From f90fd9eeb32469e41c8f4207afa44929f9ec070e Mon Sep 17 00:00:00 2001 From: Alex Ray Date: Sat, 19 Jan 2013 22:53:46 -0800 Subject: [PATCH 001/541] cutils: bitops: Add Hamming weight to bitmask Change-Id: I5c6b7adf711007edfffcb4fdf8e05b04bcffef54 --- include/cutils/bitops.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/include/cutils/bitops.h b/include/cutils/bitops.h index eb44236f5..c26dc543e 100644 --- a/include/cutils/bitops.h +++ b/include/cutils/bitops.h @@ -75,6 +75,16 @@ static inline int bitmask_ffz(unsigned int *bitmask, int num_bits) return -1; } +static inline int bitmask_weight(unsigned int *bitmask, int num_bits) +{ + int i; + int weight = 0; + + for (i = 0; i < BITS_TO_WORDS(num_bits); i++) + weight += __builtin_popcount(bitmask[i]); + return weight; +} + static inline void bitmask_set(unsigned int *bitmask, int bit) { bitmask[BIT_WORD(bit)] |= BIT_MASK(bit); From 895c1b2c97a70ac6b9837354311e6c118c31172a Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Wed, 6 Mar 2013 16:23:57 -0800 Subject: [PATCH 002/541] ueventd: allow platform devices to have just a /devices/ prefix When using device tree, platform devices may not have a /devices/platform/ path prefix, but can be rooted in /devices/. Modify the platform device tracking code to store the device path as well as the name. This way, when we create symlinks, we can correctly skip the base platform device prefix and get to the proper device node path. Change-Id: I939ef8fbcb45c5c803cd9a054e40136a912efc72 Signed-off-by: Dima Zavin --- init/devices.c | 83 ++++++++++++++++++++++++++------------------------ 1 file changed, 44 insertions(+), 39 deletions(-) diff --git a/init/devices.c b/init/devices.c index b07a1a6a1..e25034c74 100644 --- a/init/devices.c +++ b/init/devices.c @@ -83,7 +83,8 @@ struct perm_node { struct platform_node { char *name; - int name_len; + char *path; + int path_len; struct listnode list; }; @@ -215,61 +216,69 @@ static void make_device(const char *path, } } -static void add_platform_device(const char *name) +static void add_platform_device(const char *path) { - int name_len = strlen(name); + int path_len = strlen(path); struct listnode *node; struct platform_node *bus; + const char *name = path; + + if (!strncmp(path, "/devices/", 9)) { + name += 9; + if (!strncmp(name, "platform/", 9)) + name += 9; + } list_for_each_reverse(node, &platform_names) { bus = node_to_item(node, struct platform_node, list); - if ((bus->name_len < name_len) && - (name[bus->name_len] == '/') && - !strncmp(name, bus->name, bus->name_len)) + if ((bus->path_len < path_len) && + (path[bus->path_len] == '/') && + !strncmp(path, bus->path, bus->path_len)) /* subdevice of an existing platform, ignore it */ return; } - INFO("adding platform device %s\n", name); + INFO("adding platform device %s (%s)\n", name, path); bus = calloc(1, sizeof(struct platform_node)); - bus->name = strdup(name); - bus->name_len = name_len; + bus->path = strdup(path); + bus->path_len = path_len; + bus->name = bus->path + (name - path); list_add_tail(&platform_names, &bus->list); } /* - * given a name that may start with a platform device, find the length of the + * given a path that may start with a platform device, find the length of the * platform device prefix. If it doesn't start with a platform device, return * 0. */ -static const char *find_platform_device(const char *name) +static struct platform_node *find_platform_device(const char *path) { - int name_len = strlen(name); + int path_len = strlen(path); struct listnode *node; struct platform_node *bus; list_for_each_reverse(node, &platform_names) { bus = node_to_item(node, struct platform_node, list); - if ((bus->name_len < name_len) && - (name[bus->name_len] == '/') && - !strncmp(name, bus->name, bus->name_len)) - return bus->name; + if ((bus->path_len < path_len) && + (path[bus->path_len] == '/') && + !strncmp(path, bus->path, bus->path_len)) + return bus; } return NULL; } -static void remove_platform_device(const char *name) +static void remove_platform_device(const char *path) { struct listnode *node; struct platform_node *bus; list_for_each_reverse(node, &platform_names) { bus = node_to_item(node, struct platform_node, list); - if (!strcmp(name, bus->name)) { - INFO("removing platform device %s\n", name); - free(bus->name); + if (!strcmp(path, bus->path)) { + INFO("removing platform device %s\n", bus->name); + free(bus->path); list_remove(node); free(bus); return; @@ -355,8 +364,10 @@ static char **get_character_device_symlinks(struct uevent *uevent) char **links; int link_num = 0; int width; + struct platform_node *pdev; - if (strncmp(uevent->path, "/devices/platform/", 18)) + pdev = find_platform_device(uevent->path); + if (!pdev) return NULL; links = malloc(sizeof(char *) * 2); @@ -365,7 +376,7 @@ static char **get_character_device_symlinks(struct uevent *uevent) memset(links, 0, sizeof(char *) * 2); /* skip "/devices/platform/" */ - parent = strchr(uevent->path + 18, '/'); + parent = strchr(uevent->path + pdev->path_len, '/'); if (!*parent) goto err; @@ -402,7 +413,7 @@ err: static char **parse_platform_block_device(struct uevent *uevent) { const char *device; - const char *path; + struct platform_node *pdev; char *slash; int width; char buf[256]; @@ -414,18 +425,16 @@ static char **parse_platform_block_device(struct uevent *uevent) unsigned int size; struct stat info; + pdev = find_platform_device(uevent->path); + if (!pdev) + return NULL; + device = pdev->name; + char **links = malloc(sizeof(char *) * 4); if (!links) return NULL; memset(links, 0, sizeof(char *) * 4); - /* Drop "/devices/platform/" */ - path = uevent->path; - device = path + 18; - device = find_platform_device(device); - if (!device) - goto err; - INFO("found platform device %s\n", device); snprintf(link_path, sizeof(link_path), "/dev/block/platform/%s", device); @@ -447,17 +456,13 @@ static char **parse_platform_block_device(struct uevent *uevent) links[link_num] = NULL; } - slash = strrchr(path, '/'); + slash = strrchr(uevent->path, '/'); if (asprintf(&links[link_num], "%s/%s", link_path, slash + 1) > 0) link_num++; else links[link_num] = NULL; return links; - -err: - free(links); - return NULL; } static void handle_device(const char *action, const char *devpath, @@ -490,12 +495,12 @@ static void handle_device(const char *action, const char *devpath, static void handle_platform_device_event(struct uevent *uevent) { - const char *name = uevent->path + 18; /* length of /devices/platform/ */ + const char *path = uevent->path; if (!strcmp(uevent->action, "add")) - add_platform_device(name); + add_platform_device(path); else if (!strcmp(uevent->action, "remove")) - remove_platform_device(name); + remove_platform_device(path); } static const char *parse_device_name(struct uevent *uevent, unsigned int len) @@ -533,7 +538,7 @@ static void handle_block_device_event(struct uevent *uevent) snprintf(devpath, sizeof(devpath), "%s%s", base, name); make_dir(base, 0755); - if (!strncmp(uevent->path, "/devices/platform/", 18)) + if (!strncmp(uevent->path, "/devices/", 9)) links = parse_platform_block_device(uevent); handle_device(uevent->action, devpath, uevent->path, 1, From f3ae4a2c53c06cb117ed951abc58df042fe9289e Mon Sep 17 00:00:00 2001 From: Christopher Tate Date: Wed, 6 Mar 2013 16:40:52 -0800 Subject: [PATCH 003/541] Document new -obb flag for adb backup Now that adb backup handles OBB file backup/restore. Bug: 6718844 Change-Id: I5d0a1f95a52a6f15908691697ee29b077436b089 --- adb/commandline.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/adb/commandline.c b/adb/commandline.c index a927423b4..27a175498 100644 --- a/adb/commandline.c +++ b/adb/commandline.c @@ -144,12 +144,15 @@ void help() " adb bugreport - return all information from the device\n" " that should be included in a bug report.\n" "\n" - " adb backup [-f ] [-apk|-noapk] [-shared|-noshared] [-all] [-system|-nosystem] []\n" + " adb backup [-f ] [-apk|-noapk] [-obb|-noobb] [-shared|-noshared] [-all] [-system|-nosystem] []\n" " - write an archive of the device's data to .\n" " If no -f option is supplied then the data is written\n" " to \"backup.ab\" in the current directory.\n" " (-apk|-noapk enable/disable backup of the .apks themselves\n" " in the archive; the default is noapk.)\n" + " (-obb|-noobb enable/disable backup of any installed apk expansion\n" + " (aka .obb) files associated with each application; the default\n" + " is noobb.)\n" " (-shared|-noshared enable/disable backup of the device's\n" " shared storage / SD card contents; the default is noshared.)\n" " (-all means to back up all installed applications)\n" From 391f365c6d64d3223adc4cbd6b2fb558416ccceb Mon Sep 17 00:00:00 2001 From: Benoit Goby Date: Tue, 15 Jan 2013 18:43:01 -0800 Subject: [PATCH 004/541] toolbox: Fix rm -f with multiple files Only check errno if unlink returns -1. Continue instead of exiting if one file does not exist. Change-Id: Iaf01b8523b84e87fcb0d732b89b7be6e24279c0b --- toolbox/rm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/toolbox/rm.c b/toolbox/rm.c index 3a24becb6..127cbc450 100644 --- a/toolbox/rm.c +++ b/toolbox/rm.c @@ -103,8 +103,8 @@ int rm_main(int argc, char *argv[]) ret = unlink_recursive(argv[i], flags); } else { ret = unlink(argv[i]); - if (errno == ENOENT && (flags & OPT_FORCE)) { - return 0; + if (ret < 0 && errno == ENOENT && (flags & OPT_FORCE)) { + continue; } } From c6d7e200eddd620d8ac55259ab3aa5f8bfa2aadb Mon Sep 17 00:00:00 2001 From: Benoit Goby Date: Fri, 22 Mar 2013 16:23:48 -0700 Subject: [PATCH 005/541] toolbox: Make reboot a separate command from toolbox Set the CAP_SYS_BOOT filesystem capability on the new reboot command and keep CAP_SYS_BOOT in adb bounding set so that the shell user can run it. Change-Id: I1dd6143445ee2a952254f0452ab6e544318431dd --- CleanSpec.mk | 1 + adb/adb.c | 3 ++- include/private/android_filesystem_config.h | 3 ++- reboot/Android.mk | 12 +++++++++++ {toolbox => reboot}/reboot.c | 22 ++++++++++++++++++--- toolbox/Android.mk | 1 - 6 files changed, 36 insertions(+), 6 deletions(-) create mode 100644 reboot/Android.mk rename {toolbox => reboot}/reboot.c (66%) diff --git a/CleanSpec.mk b/CleanSpec.mk index 8611d3bc2..74ec29d2d 100644 --- a/CleanSpec.mk +++ b/CleanSpec.mk @@ -50,3 +50,4 @@ $(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/init.rc) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/init.rc) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/reboot) diff --git a/adb/adb.c b/adb/adb.c index d2a2d27eb..93a93343b 100644 --- a/adb/adb.c +++ b/adb/adb.c @@ -1195,8 +1195,9 @@ static void drop_capabilities_bounding_set_if_needed() { #endif int i; for (i = 0; prctl(PR_CAPBSET_READ, i, 0, 0, 0) >= 0; i++) { - if ((i == CAP_SETUID) || (i == CAP_SETGID)) { + if (i == CAP_SETUID || i == CAP_SETGID || i == CAP_SYS_BOOT) { // CAP_SETUID CAP_SETGID needed by /system/bin/run-as + // CAP_SYS_BOOT needed by /system/bin/reboot continue; } int err = prctl(PR_CAPBSET_DROP, i, 0, 0, 0); diff --git a/include/private/android_filesystem_config.h b/include/private/android_filesystem_config.h index 540318fd5..850e0bd75 100644 --- a/include/private/android_filesystem_config.h +++ b/include/private/android_filesystem_config.h @@ -228,8 +228,9 @@ static const struct fs_path_config android_files[] = { { 06755, AID_ROOT, AID_ROOT, 0, "system/xbin/tcpdump" }, { 04770, AID_ROOT, AID_RADIO, 0, "system/bin/pppd-ril" }, - /* the following file has enhanced capabilities and IS included in user builds. */ + /* the following files have enhanced capabilities and ARE included in user builds. */ { 00750, AID_ROOT, AID_SHELL, (1 << CAP_SETUID) | (1 << CAP_SETGID), "system/bin/run-as" }, + { 00750, AID_ROOT, AID_SHELL, 1 << CAP_SYS_BOOT, "system/bin/reboot" }, { 00755, AID_ROOT, AID_SHELL, 0, "system/bin/*" }, { 00755, AID_ROOT, AID_ROOT, 0, "system/lib/valgrind/*" }, diff --git a/reboot/Android.mk b/reboot/Android.mk new file mode 100644 index 000000000..4db0c1e4d --- /dev/null +++ b/reboot/Android.mk @@ -0,0 +1,12 @@ +# Copyright 2013 The Android Open Source Project + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= reboot.c + +LOCAL_SHARED_LIBRARIES:= libcutils + +LOCAL_MODULE:= reboot + +include $(BUILD_EXECUTABLE) diff --git a/toolbox/reboot.c b/reboot/reboot.c similarity index 66% rename from toolbox/reboot.c rename to reboot/reboot.c index f8546de2d..45d8a8ef5 100644 --- a/toolbox/reboot.c +++ b/reboot/reboot.c @@ -1,10 +1,26 @@ +/* + * Copyright (C) 2013 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 #include #include #include #include -int reboot_main(int argc, char *argv[]) +int main(int argc, char *argv[]) { int ret; int nosync = 0; @@ -16,11 +32,11 @@ int reboot_main(int argc, char *argv[]) int c; c = getopt(argc, argv, "np"); - + if (c == EOF) { break; } - + switch (c) { case 'n': nosync = 1; diff --git a/toolbox/Android.mk b/toolbox/Android.mk index 2ecb62644..677539ffd 100644 --- a/toolbox/Android.mk +++ b/toolbox/Android.mk @@ -16,7 +16,6 @@ TOOLS := \ rm \ mkdir \ rmdir \ - reboot \ getevent \ sendevent \ date \ From ca203fc23ebf6a64ffaa4a552a5e71f414d92f13 Mon Sep 17 00:00:00 2001 From: Geremy Condra Date: Sat, 30 Mar 2013 17:27:43 -0700 Subject: [PATCH 006/541] Give system ownership of selinux load and enforce files. This is necessary to enable remote updates. Change-Id: I05fb979c0360eca4cc6e4add48bb42f712a1ba17 --- rootdir/init.rc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/rootdir/init.rc b/rootdir/init.rc index e6583e352..89ec18a36 100644 --- a/rootdir/init.rc +++ b/rootdir/init.rc @@ -348,6 +348,10 @@ on boot chown system system /sys/kernel/ipv4/tcp_rmem_max chown root radio /proc/cmdline +# Set these so we can remotely update SELinux policy + chown system system /sys/fs/selinux/load + chown system system /sys/fs/selinux/enforce + # Define TCP buffer sizes for various networks # ReadMin, ReadInitial, ReadMax, WriteMin, WriteInitial, WriteMax, setprop net.tcp.buffersize.default 4096,87380,110208,4096,16384,110208 From 78c8f3577ec0c7814365566fd6f977ac20e353ce Mon Sep 17 00:00:00 2001 From: Geremy Condra Date: Thu, 28 Feb 2013 17:29:58 -0800 Subject: [PATCH 007/541] Apply the correct SELinux label for the properties workspace. Change-Id: Ibb9c8044caa9d39ee6ec9fe06d54bb9dc4b56ff3 --- init/init.c | 1 + 1 file changed, 1 insertion(+) diff --git a/init/init.c b/init/init.c index b28b0ab23..93b59974f 100755 --- a/init/init.c +++ b/init/init.c @@ -879,6 +879,7 @@ int main(int argc, char **argv) */ restorecon("/dev"); restorecon("/dev/socket"); + restorecon("/dev/__properties__"); is_charger = !strcmp(bootmode, "charger"); From 515e1639ef0ab5e3149fafeffce826cf654d616f Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Wed, 10 Apr 2013 09:22:02 -0700 Subject: [PATCH 008/541] mincrypt: support SHA-256 hash algorithm - adds a library to compute the SHA-256 hash - updates the RSA verifier to take an argument specifying either SHA-1 or SHA-256 - updates DumpPublicKey to with new "key" version numbers for specifying SHA-256 - adds new argument to adb auth code to maintain existing behavior Change-Id: I5b1406cf57c2b8993f6032eda3e29139f7740839 --- adb/adb_auth_client.c | 3 +- include/mincrypt/hash-internal.h | 40 +++++ include/mincrypt/rsa.h | 3 +- include/mincrypt/sha.h | 59 ++----- include/mincrypt/sha256.h | 29 ++++ libmincrypt/Android.mk | 4 +- libmincrypt/rsa.c | 13 +- libmincrypt/rsa_e_3.c | 50 +++++- libmincrypt/rsa_e_f4.c | 79 +++++++++- libmincrypt/sha.c | 228 +++++---------------------- libmincrypt/sha256.c | 184 +++++++++++++++++++++ libmincrypt/tools/DumpPublicKey.java | 41 +++-- 12 files changed, 465 insertions(+), 268 deletions(-) create mode 100644 include/mincrypt/hash-internal.h create mode 100644 include/mincrypt/sha256.h create mode 100644 libmincrypt/sha256.c diff --git a/adb/adb_auth_client.c b/adb/adb_auth_client.c index 763b448d7..f8d730645 100644 --- a/adb/adb_auth_client.c +++ b/adb/adb_auth_client.c @@ -25,6 +25,7 @@ #include "adb_auth.h" #include "fdevent.h" #include "mincrypt/rsa.h" +#include "mincrypt/sha.h" #define TRACE_TAG TRACE_AUTH @@ -149,7 +150,7 @@ int adb_auth_verify(void *token, void *sig, int siglen) list_for_each(item, &key_list) { key = node_to_item(item, struct adb_public_key, node); - ret = RSA_verify(&key->key, sig, siglen, token); + ret = RSA_verify(&key->key, sig, siglen, token, SHA_DIGEST_SIZE); if (ret) break; } diff --git a/include/mincrypt/hash-internal.h b/include/mincrypt/hash-internal.h new file mode 100644 index 000000000..96806f7c6 --- /dev/null +++ b/include/mincrypt/hash-internal.h @@ -0,0 +1,40 @@ +// Copyright 2007 Google Inc. All Rights Reserved. +// Author: mschilder@google.com (Marius Schilder) + +#ifndef SECURITY_UTIL_LITE_HASH_INTERNAL_H__ +#define SECURITY_UTIL_LITE_HASH_INTERNAL_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +struct HASH_CTX; // forward decl + +typedef struct HASH_VTAB { + void (* const init)(struct HASH_CTX*); + void (* const update)(struct HASH_CTX*, const void*, int); + const uint8_t* (* const final)(struct HASH_CTX*); + const uint8_t* (* const hash)(const void*, int, uint8_t*); + int size; +} HASH_VTAB; + +typedef struct HASH_CTX { + const HASH_VTAB * f; + uint64_t count; + uint8_t buf[64]; + uint32_t state[8]; // upto SHA2 +} HASH_CTX; + +#define HASH_init(ctx) (ctx)->f->init(ctx) +#define HASH_update(ctx, data, len) (ctx)->f->update(ctx, data, len) +#define HASH_final(ctx) (ctx)->f->final(ctx) +#define HASH_hash(data, len, digest) (ctx)->f->hash(data, len, digest) +#define HASH_size(ctx) (ctx)->f->size + +#ifdef __cplusplus +} +#endif // __cplusplus + +#endif // SECURITY_UTIL_LITE_HASH_INTERNAL_H__ diff --git a/include/mincrypt/rsa.h b/include/mincrypt/rsa.h index d7429fc70..cc0e80024 100644 --- a/include/mincrypt/rsa.h +++ b/include/mincrypt/rsa.h @@ -48,7 +48,8 @@ typedef struct RSAPublicKey { int RSA_verify(const RSAPublicKey *key, const uint8_t* signature, const int len, - const uint8_t* sha); + const uint8_t* hash, + const int hash_len); #ifdef __cplusplus } diff --git a/include/mincrypt/sha.h b/include/mincrypt/sha.h index af63e8775..120ddcb4f 100644 --- a/include/mincrypt/sha.h +++ b/include/mincrypt/sha.h @@ -1,63 +1,30 @@ -/* sha.h -** -** Copyright 2008, The Android Open Source Project -** -** Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in the -** documentation and/or other materials provided with the distribution. -** * Neither the name of Google Inc. nor the names of its contributors may -** be used to endorse or promote products derived from this software -** without specific prior written permission. -** -** THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR -** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO -** EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; -** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR -** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ +// Copyright 2005 Google Inc. All Rights Reserved. +// Author: mschilder@google.com (Marius Schilder) -#ifndef _EMBEDDED_SHA_H_ -#define _EMBEDDED_SHA_H_ +#ifndef SECURITY_UTIL_LITE_SHA1_H__ +#define SECURITY_UTIL_LITE_SHA1_H__ -#include +#include +#include "hash-internal.h" #ifdef __cplusplus extern "C" { -#endif +#endif // __cplusplus -typedef struct SHA_CTX { - uint64_t count; - uint32_t state[5]; -#if defined(HAVE_ENDIAN_H) && defined(HAVE_LITTLE_ENDIAN) - union { - uint8_t b[64]; - uint32_t w[16]; - } buf; -#else - uint8_t buf[64]; -#endif -} SHA_CTX; +typedef HASH_CTX SHA_CTX; void SHA_init(SHA_CTX* ctx); void SHA_update(SHA_CTX* ctx, const void* data, int len); const uint8_t* SHA_final(SHA_CTX* ctx); -/* Convenience method. Returns digest parameter value. */ -const uint8_t* SHA(const void* data, int len, uint8_t* digest); +// Convenience method. Returns digest address. +// NOTE: *digest needs to hold SHA_DIGEST_SIZE bytes. +const uint8_t* SHA_hash(const void* data, int len, uint8_t* digest); #define SHA_DIGEST_SIZE 20 #ifdef __cplusplus } -#endif +#endif // __cplusplus -#endif +#endif // SECURITY_UTIL_LITE_SHA1_H__ diff --git a/include/mincrypt/sha256.h b/include/mincrypt/sha256.h new file mode 100644 index 000000000..0f3efb7f0 --- /dev/null +++ b/include/mincrypt/sha256.h @@ -0,0 +1,29 @@ +// Copyright 2011 Google Inc. All Rights Reserved. +// Author: mschilder@google.com (Marius Schilder) + +#ifndef SECURITY_UTIL_LITE_SHA256_H__ +#define SECURITY_UTIL_LITE_SHA256_H__ + +#include +#include "hash-internal.h" + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +typedef HASH_CTX SHA256_CTX; + +void SHA256_init(SHA256_CTX* ctx); +void SHA256_update(SHA256_CTX* ctx, const void* data, int len); +const uint8_t* SHA256_final(SHA256_CTX* ctx); + +// Convenience method. Returns digest address. +const uint8_t* SHA256_hash(const void* data, int len, uint8_t* digest); + +#define SHA256_DIGEST_SIZE 32 + +#ifdef __cplusplus +} +#endif // __cplusplus + +#endif // SECURITY_UTIL_LITE_SHA256_H__ diff --git a/libmincrypt/Android.mk b/libmincrypt/Android.mk index f58eab9b3..addbed8b2 100644 --- a/libmincrypt/Android.mk +++ b/libmincrypt/Android.mk @@ -4,13 +4,13 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := libmincrypt -LOCAL_SRC_FILES := rsa.c rsa_e_3.c rsa_e_f4.c sha.c +LOCAL_SRC_FILES := rsa.c rsa_e_3.c rsa_e_f4.c sha.c sha256.c include $(BUILD_STATIC_LIBRARY) include $(CLEAR_VARS) LOCAL_MODULE := libmincrypt -LOCAL_SRC_FILES := rsa.c rsa_e_3.c rsa_e_f4.c sha.c +LOCAL_SRC_FILES := rsa.c rsa_e_3.c rsa_e_f4.c sha.c sha256.c include $(BUILD_HOST_STATIC_LIBRARY) diff --git a/libmincrypt/rsa.c b/libmincrypt/rsa.c index b4ee6af60..0cdbaa24b 100644 --- a/libmincrypt/rsa.c +++ b/libmincrypt/rsa.c @@ -30,23 +30,26 @@ int RSA_e_f4_verify(const RSAPublicKey* key, const uint8_t* signature, const int len, - const uint8_t* sha); + const uint8_t* hash, + const int hash_len); int RSA_e_3_verify(const RSAPublicKey *key, const uint8_t *signature, const int len, - const uint8_t *sha); + const uint8_t *hash, + const int hash_len); int RSA_verify(const RSAPublicKey *key, const uint8_t *signature, const int len, - const uint8_t *sha) { + const uint8_t *hash, + const int hash_len) { switch (key->exponent) { case 3: - return RSA_e_3_verify(key, signature, len, sha); + return RSA_e_3_verify(key, signature, len, hash, hash_len); break; case 65537: - return RSA_e_f4_verify(key, signature, len, sha); + return RSA_e_f4_verify(key, signature, len, hash, hash_len); break; default: return 0; diff --git a/libmincrypt/rsa_e_3.c b/libmincrypt/rsa_e_3.c index c8c02c41e..012a35717 100644 --- a/libmincrypt/rsa_e_3.c +++ b/libmincrypt/rsa_e_3.c @@ -27,6 +27,7 @@ #include "mincrypt/rsa.h" #include "mincrypt/sha.h" +#include "mincrypt/sha256.h" /* a[] -= mod */ static void subM(const RSAPublicKey *key, uint32_t *a) { @@ -134,7 +135,7 @@ static void modpow3(const RSAPublicKey *key, ** other flavor which omits the optional parameter entirely). This code does not ** accept signatures without the optional parameter. */ -static const uint8_t padding[RSANUMBYTES - SHA_DIGEST_SIZE] = { +static const uint8_t sha_padding[RSANUMBYTES - SHA_DIGEST_SIZE] = { 0x00,0x01,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, @@ -156,15 +157,56 @@ static const uint8_t padding[RSANUMBYTES - SHA_DIGEST_SIZE] = { 0x04,0x14 }; +static const uint8_t sha256_padding[RSANUMBYTES - SHA256_DIGEST_SIZE] = { + 0x00,0x01,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x30,0x31,0x30, + 0x0d,0x06,0x09,0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x01,0x05, + 0x00,0x04,0x20 +}; + + /* Verify a 2048 bit RSA e=3 PKCS1.5 signature against an expected SHA-1 hash. ** Returns 0 on failure, 1 on success. */ int RSA_e_3_verify(const RSAPublicKey *key, const uint8_t *signature, const int len, - const uint8_t *sha) { + const uint8_t *hash, + const int hash_len) { uint8_t buf[RSANUMBYTES]; int i; + int padding_size; + const uint8_t* padding; + + switch (hash_len) { + case SHA_DIGEST_SIZE: + padding = sha_padding; + padding_size = sizeof(sha_padding); + break; + case SHA256_DIGEST_SIZE: + padding = sha256_padding; + padding_size = sizeof(sha256_padding); + break; + default: + return 0; // unsupported hash + } if (key->len != RSANUMWORDS) { return 0; /* Wrong key passed in. */ @@ -185,7 +227,7 @@ int RSA_e_3_verify(const RSAPublicKey *key, modpow3(key, buf); /* Check pkcs1.5 padding bytes. */ - for (i = 0; i < (int) sizeof(padding); ++i) { + for (i = 0; i < padding_size; ++i) { if (buf[i] != padding[i]) { return 0; } @@ -193,7 +235,7 @@ int RSA_e_3_verify(const RSAPublicKey *key, /* Check sha digest matches. */ for (; i < len; ++i) { - if (buf[i] != *sha++) { + if (buf[i] != *hash++) { return 0; } } diff --git a/libmincrypt/rsa_e_f4.c b/libmincrypt/rsa_e_f4.c index 6701bcc20..17a2de2c7 100644 --- a/libmincrypt/rsa_e_f4.c +++ b/libmincrypt/rsa_e_f4.c @@ -27,6 +27,7 @@ #include "mincrypt/rsa.h" #include "mincrypt/sha.h" +#include "mincrypt/sha256.h" // a[] -= mod static void subM(const RSAPublicKey* key, @@ -138,26 +139,79 @@ static void modpowF4(const RSAPublicKey* key, // other flavor which omits the optional parameter entirely). This code does not // accept signatures without the optional parameter. /* -static const uint8_t padding[RSANUMBYTES] = { +static const uint8_t sha_padding[RSANUMBYTES] = { 0x00,0x01,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x30,0x21,0x30,0x09,0x06,0x05,0x2b,0x0e,0x03,0x02,0x1a,0x05,0x00,0x04,0x14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }; + +static const uint8_t sha256_padding[RSANUMBYTES] = { + 0x00,0x01,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x30,0x31,0x30, + 0x0d,0x06,0x09,0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x01,0x05, + 0x00,0x04,0x20, + + // 32 bytes of hash go here. + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, +}; + */ -// SHA-1 of PKCS1.5 signature padding for 2048 bit, as above. +// SHA-1 of PKCS1.5 signature sha_padding for 2048 bit, as above. // At the location of the bytes of the hash all 00 are hashed. static const uint8_t kExpectedPadShaRsa2048[SHA_DIGEST_SIZE] = { 0xdc, 0xbd, 0xbe, 0x42, 0xd5, 0xf5, 0xa7, 0x2e, 0x6e, 0xfc, 0xf5, 0x5d, 0xaf, 0x9d, 0xea, 0x68, 0x7c, 0xfb, 0xf1, 0x67 }; +// SHA-256 of PKCS1.5 signature sha256_padding for 2048 bit, as above. +// At the location of the bytes of the hash all 00 are hashed. +static const uint8_t kExpectedPadSha256Rsa2048[SHA256_DIGEST_SIZE] = { + 0xab, 0x28, 0x8d, 0x8a, 0xd7, 0xd9, 0x59, 0x92, + 0xba, 0xcc, 0xf8, 0x67, 0x20, 0xe1, 0x15, 0x2e, + 0x39, 0x8d, 0x80, 0x36, 0xd6, 0x6f, 0xf0, 0xfd, + 0x90, 0xe8, 0x7d, 0x8b, 0xe1, 0x7c, 0x87, 0x59, +}; + // Verify a 2048 bit RSA e=65537 PKCS1.5 signature against an expected // SHA-1 hash. Returns 0 on failure, 1 on success. int RSA_e_f4_verify(const RSAPublicKey* key, const uint8_t* signature, const int len, - const uint8_t* sha) { + const uint8_t* hash, + const int hash_len) { uint8_t buf[RSANUMBYTES]; int i; + const uint8_t* padding_hash; + + switch (hash_len) { + case SHA_DIGEST_SIZE: + padding_hash = kExpectedPadShaRsa2048; + break; + case SHA256_DIGEST_SIZE: + padding_hash = kExpectedPadSha256Rsa2048; + break; + default: + return 0; // unsupported hash + } if (key->len != RSANUMWORDS) { return 0; // Wrong key passed in. @@ -178,16 +232,25 @@ int RSA_e_f4_verify(const RSAPublicKey* key, modpowF4(key, buf); // In-place exponentiation. // Xor sha portion, so it all becomes 00 iff equal. - for (i = len - SHA_DIGEST_SIZE; i < len; ++i) { - buf[i] ^= *sha++; + for (i = len - hash_len; i < len; ++i) { + buf[i] ^= *hash++; } // Hash resulting buf, in-place. - SHA(buf, len, buf); + switch (hash_len) { + case SHA_DIGEST_SIZE: + SHA_hash(buf, len, buf); + break; + case SHA256_DIGEST_SIZE: + SHA256_hash(buf, len, buf); + break; + default: + return 0; + } // Compare against expected hash value. - for (i = 0; i < SHA_DIGEST_SIZE; ++i) { - if (buf[i] != kExpectedPadShaRsa2048[i]) { + for (i = 0; i < hash_len; ++i) { + if (buf[i] != padding_hash[i]) { return 0; } } diff --git a/libmincrypt/sha.c b/libmincrypt/sha.c index e089d791f..5bef32e42 100644 --- a/libmincrypt/sha.c +++ b/libmincrypt/sha.c @@ -1,6 +1,6 @@ /* sha.c ** -** Copyright 2008, The Android Open Source Project +** Copyright 2013, The Android Open Source Project ** ** Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are met: @@ -25,177 +25,20 @@ ** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +// Optimized for minimal code size. + #include "mincrypt/sha.h" -// Some machines lack byteswap.h and endian.h. These have to use the -// slower code, even if they're little-endian. - -#if defined(HAVE_ENDIAN_H) && defined(HAVE_LITTLE_ENDIAN) - -#include -#include - -// This version is about 28% faster than the generic version below, -// but assumes little-endianness. - -static inline uint32_t ror27(uint32_t val) { - return (val >> 27) | (val << 5); -} -static inline uint32_t ror2(uint32_t val) { - return (val >> 2) | (val << 30); -} -static inline uint32_t ror31(uint32_t val) { - return (val >> 31) | (val << 1); -} - -static void SHA1_Transform(SHA_CTX* ctx) { - uint32_t W[80]; - register uint32_t A, B, C, D, E; - int t; - - A = ctx->state[0]; - B = ctx->state[1]; - C = ctx->state[2]; - D = ctx->state[3]; - E = ctx->state[4]; - -#define SHA_F1(A,B,C,D,E,t) \ - E += ror27(A) + \ - (W[t] = bswap_32(ctx->buf.w[t])) + \ - (D^(B&(C^D))) + 0x5A827999; \ - B = ror2(B); - - for (t = 0; t < 15; t += 5) { - SHA_F1(A,B,C,D,E,t + 0); - SHA_F1(E,A,B,C,D,t + 1); - SHA_F1(D,E,A,B,C,t + 2); - SHA_F1(C,D,E,A,B,t + 3); - SHA_F1(B,C,D,E,A,t + 4); - } - SHA_F1(A,B,C,D,E,t + 0); // 16th one, t == 15 - -#undef SHA_F1 - -#define SHA_F1(A,B,C,D,E,t) \ - E += ror27(A) + \ - (W[t] = ror31(W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16])) + \ - (D^(B&(C^D))) + 0x5A827999; \ - B = ror2(B); - - SHA_F1(E,A,B,C,D,t + 1); - SHA_F1(D,E,A,B,C,t + 2); - SHA_F1(C,D,E,A,B,t + 3); - SHA_F1(B,C,D,E,A,t + 4); - -#undef SHA_F1 - -#define SHA_F2(A,B,C,D,E,t) \ - E += ror27(A) + \ - (W[t] = ror31(W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16])) + \ - (B^C^D) + 0x6ED9EBA1; \ - B = ror2(B); - - for (t = 20; t < 40; t += 5) { - SHA_F2(A,B,C,D,E,t + 0); - SHA_F2(E,A,B,C,D,t + 1); - SHA_F2(D,E,A,B,C,t + 2); - SHA_F2(C,D,E,A,B,t + 3); - SHA_F2(B,C,D,E,A,t + 4); - } - -#undef SHA_F2 - -#define SHA_F3(A,B,C,D,E,t) \ - E += ror27(A) + \ - (W[t] = ror31(W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16])) + \ - ((B&C)|(D&(B|C))) + 0x8F1BBCDC; \ - B = ror2(B); - - for (; t < 60; t += 5) { - SHA_F3(A,B,C,D,E,t + 0); - SHA_F3(E,A,B,C,D,t + 1); - SHA_F3(D,E,A,B,C,t + 2); - SHA_F3(C,D,E,A,B,t + 3); - SHA_F3(B,C,D,E,A,t + 4); - } - -#undef SHA_F3 - -#define SHA_F4(A,B,C,D,E,t) \ - E += ror27(A) + \ - (W[t] = ror31(W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16])) + \ - (B^C^D) + 0xCA62C1D6; \ - B = ror2(B); - - for (; t < 80; t += 5) { - SHA_F4(A,B,C,D,E,t + 0); - SHA_F4(E,A,B,C,D,t + 1); - SHA_F4(D,E,A,B,C,t + 2); - SHA_F4(C,D,E,A,B,t + 3); - SHA_F4(B,C,D,E,A,t + 4); - } - -#undef SHA_F4 - - ctx->state[0] += A; - ctx->state[1] += B; - ctx->state[2] += C; - ctx->state[3] += D; - ctx->state[4] += E; -} - -void SHA_update(SHA_CTX* ctx, const void* data, int len) { - int i = ctx->count % sizeof(ctx->buf); - const uint8_t* p = (const uint8_t*)data; - - ctx->count += len; - - while (len > sizeof(ctx->buf) - i) { - memcpy(&ctx->buf.b[i], p, sizeof(ctx->buf) - i); - len -= sizeof(ctx->buf) - i; - p += sizeof(ctx->buf) - i; - SHA1_Transform(ctx); - i = 0; - } - - while (len--) { - ctx->buf.b[i++] = *p++; - if (i == sizeof(ctx->buf)) { - SHA1_Transform(ctx); - i = 0; - } - } -} - - -const uint8_t* SHA_final(SHA_CTX* ctx) { - uint64_t cnt = ctx->count * 8; - int i; - - SHA_update(ctx, (uint8_t*)"\x80", 1); - while ((ctx->count % sizeof(ctx->buf)) != (sizeof(ctx->buf) - 8)) { - SHA_update(ctx, (uint8_t*)"\0", 1); - } - for (i = 0; i < 8; ++i) { - uint8_t tmp = cnt >> ((7 - i) * 8); - SHA_update(ctx, &tmp, 1); - } - - for (i = 0; i < 5; i++) { - ctx->buf.w[i] = bswap_32(ctx->state[i]); - } - - return ctx->buf.b; -} - -#else // #if defined(HAVE_ENDIAN_H) && defined(HAVE_LITTLE_ENDIAN) +#include +#include +#include #define rol(bits, value) (((value) << (bits)) | ((value) >> (32 - (bits)))) -static void SHA1_transform(SHA_CTX *ctx) { +static void SHA1_Transform(SHA_CTX* ctx) { uint32_t W[80]; uint32_t A, B, C, D, E; - uint8_t *p = ctx->buf; + uint8_t* p = ctx->buf; int t; for(t = 0; t < 16; ++t) { @@ -242,31 +85,52 @@ static void SHA1_transform(SHA_CTX *ctx) { ctx->state[4] += E; } -void SHA_update(SHA_CTX *ctx, const void *data, int len) { - int i = ctx->count % sizeof(ctx->buf); +static const HASH_VTAB SHA_VTAB = { + SHA_init, + SHA_update, + SHA_final, + SHA_hash, + SHA_DIGEST_SIZE +}; + +void SHA_init(SHA_CTX* ctx) { + ctx->f = &SHA_VTAB; + ctx->state[0] = 0x67452301; + ctx->state[1] = 0xEFCDAB89; + ctx->state[2] = 0x98BADCFE; + ctx->state[3] = 0x10325476; + ctx->state[4] = 0xC3D2E1F0; + ctx->count = 0; +} + + +void SHA_update(SHA_CTX* ctx, const void* data, int len) { + int i = (int) (ctx->count & 63); const uint8_t* p = (const uint8_t*)data; ctx->count += len; while (len--) { ctx->buf[i++] = *p++; - if (i == sizeof(ctx->buf)) { - SHA1_transform(ctx); + if (i == 64) { + SHA1_Transform(ctx); i = 0; } } } -const uint8_t *SHA_final(SHA_CTX *ctx) { + + +const uint8_t* SHA_final(SHA_CTX* ctx) { uint8_t *p = ctx->buf; uint64_t cnt = ctx->count * 8; int i; SHA_update(ctx, (uint8_t*)"\x80", 1); - while ((ctx->count % sizeof(ctx->buf)) != (sizeof(ctx->buf) - 8)) { + while ((ctx->count & 63) != 56) { SHA_update(ctx, (uint8_t*)"\0", 1); } for (i = 0; i < 8; ++i) { - uint8_t tmp = cnt >> ((7 - i) * 8); + uint8_t tmp = (uint8_t) (cnt >> ((7 - i) * 8)); SHA_update(ctx, &tmp, 1); } @@ -281,27 +145,11 @@ const uint8_t *SHA_final(SHA_CTX *ctx) { return ctx->buf; } -#endif // endianness - -void SHA_init(SHA_CTX* ctx) { - ctx->state[0] = 0x67452301; - ctx->state[1] = 0xEFCDAB89; - ctx->state[2] = 0x98BADCFE; - ctx->state[3] = 0x10325476; - ctx->state[4] = 0xC3D2E1F0; - ctx->count = 0; -} - /* Convenience function */ -const uint8_t* SHA(const void *data, int len, uint8_t *digest) { - const uint8_t *p; - int i; +const uint8_t* SHA_hash(const void* data, int len, uint8_t* digest) { SHA_CTX ctx; SHA_init(&ctx); SHA_update(&ctx, data, len); - p = SHA_final(&ctx); - for (i = 0; i < SHA_DIGEST_SIZE; ++i) { - digest[i] = *p++; - } + memcpy(digest, SHA_final(&ctx), SHA_DIGEST_SIZE); return digest; } diff --git a/libmincrypt/sha256.c b/libmincrypt/sha256.c new file mode 100644 index 000000000..eb6e308e8 --- /dev/null +++ b/libmincrypt/sha256.c @@ -0,0 +1,184 @@ +/* sha256.c +** +** Copyright 2013, The Android Open Source Project +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** * Neither the name of Google Inc. nor the names of its contributors may +** be used to endorse or promote products derived from this software +** without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +** EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// Optimized for minimal code size. + +#include "mincrypt/sha256.h" + +#include +#include +#include + +#define ror(value, bits) (((value) >> (bits)) | ((value) << (32 - (bits)))) +#define shr(value, bits) ((value) >> (bits)) + +static const uint32_t K[64] = { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, + 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, + 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, + 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, + 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, + 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, + 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, + 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, + 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 }; + +static void SHA256_Transform(SHA256_CTX* ctx) { + uint32_t W[64]; + uint32_t A, B, C, D, E, F, G, H; + uint8_t* p = ctx->buf; + int t; + + for(t = 0; t < 16; ++t) { + uint32_t tmp = *p++ << 24; + tmp |= *p++ << 16; + tmp |= *p++ << 8; + tmp |= *p++; + W[t] = tmp; + } + + for(; t < 64; t++) { + uint32_t s0 = ror(W[t-15], 7) ^ ror(W[t-15], 18) ^ shr(W[t-15], 3); + uint32_t s1 = ror(W[t-2], 17) ^ ror(W[t-2], 19) ^ shr(W[t-2], 10); + W[t] = W[t-16] + s0 + W[t-7] + s1; + } + + A = ctx->state[0]; + B = ctx->state[1]; + C = ctx->state[2]; + D = ctx->state[3]; + E = ctx->state[4]; + F = ctx->state[5]; + G = ctx->state[6]; + H = ctx->state[7]; + + for(t = 0; t < 64; t++) { + uint32_t s0 = ror(A, 2) ^ ror(A, 13) ^ ror(A, 22); + uint32_t maj = (A & B) ^ (A & C) ^ (B & C); + uint32_t t2 = s0 + maj; + uint32_t s1 = ror(E, 6) ^ ror(E, 11) ^ ror(E, 25); + uint32_t ch = (E & F) ^ ((~E) & G); + uint32_t t1 = H + s1 + ch + K[t] + W[t]; + + H = G; + G = F; + F = E; + E = D + t1; + D = C; + C = B; + B = A; + A = t1 + t2; + } + + ctx->state[0] += A; + ctx->state[1] += B; + ctx->state[2] += C; + ctx->state[3] += D; + ctx->state[4] += E; + ctx->state[5] += F; + ctx->state[6] += G; + ctx->state[7] += H; +} + +static const HASH_VTAB SHA256_VTAB = { + SHA256_init, + SHA256_update, + SHA256_final, + SHA256_hash, + SHA256_DIGEST_SIZE +}; + +void SHA256_init(SHA256_CTX* ctx) { + ctx->f = &SHA256_VTAB; + ctx->state[0] = 0x6a09e667; + ctx->state[1] = 0xbb67ae85; + ctx->state[2] = 0x3c6ef372; + ctx->state[3] = 0xa54ff53a; + ctx->state[4] = 0x510e527f; + ctx->state[5] = 0x9b05688c; + ctx->state[6] = 0x1f83d9ab; + ctx->state[7] = 0x5be0cd19; + ctx->count = 0; +} + + +void SHA256_update(SHA256_CTX* ctx, const void* data, int len) { + int i = (int) (ctx->count & 63); + const uint8_t* p = (const uint8_t*)data; + + ctx->count += len; + + while (len--) { + ctx->buf[i++] = *p++; + if (i == 64) { + SHA256_Transform(ctx); + i = 0; + } + } +} + + +const uint8_t* SHA256_final(SHA256_CTX* ctx) { + uint8_t *p = ctx->buf; + uint64_t cnt = ctx->count * 8; + int i; + + SHA256_update(ctx, (uint8_t*)"\x80", 1); + while ((ctx->count & 63) != 56) { + SHA256_update(ctx, (uint8_t*)"\0", 1); + } + for (i = 0; i < 8; ++i) { + uint8_t tmp = (uint8_t) (cnt >> ((7 - i) * 8)); + SHA256_update(ctx, &tmp, 1); + } + + for (i = 0; i < 8; i++) { + uint32_t tmp = ctx->state[i]; + *p++ = tmp >> 24; + *p++ = tmp >> 16; + *p++ = tmp >> 8; + *p++ = tmp >> 0; + } + + return ctx->buf; +} + +/* Convenience function */ +const uint8_t* SHA256_hash(const void* data, int len, uint8_t* digest) { + SHA256_CTX ctx; + SHA256_init(&ctx); + SHA256_update(&ctx, data, len); + memcpy(digest, SHA256_final(&ctx), SHA256_DIGEST_SIZE); + return digest; +} diff --git a/libmincrypt/tools/DumpPublicKey.java b/libmincrypt/tools/DumpPublicKey.java index 12b4f5629..718911627 100644 --- a/libmincrypt/tools/DumpPublicKey.java +++ b/libmincrypt/tools/DumpPublicKey.java @@ -19,7 +19,7 @@ package com.android.dumpkey; import java.io.FileInputStream; import java.math.BigInteger; import java.security.cert.CertificateFactory; -import java.security.cert.Certificate; +import java.security.cert.X509Certificate; import java.security.KeyStore; import java.security.Key; import java.security.PublicKey; @@ -34,20 +34,22 @@ class DumpPublicKey { /** * @param key to perform sanity checks on * @return version number of key. Supported versions are: - * 1: 2048-bit key with e=3 - * 2: 2048-bit key with e=65537 + * 1: 2048-bit RSA key with e=3 and SHA-1 hash + * 2: 2048-bit RSA key with e=65537 and SHA-1 hash + * 3: 2048-bit RSA key with e=3 and SHA-256 hash + * 4: 2048-bit RSA key with e=65537 and SHA-256 hash * @throws Exception if the key has the wrong size or public exponent */ - static int check(RSAPublicKey key) throws Exception { + static int check(RSAPublicKey key, boolean useSHA256) throws Exception { BigInteger pubexp = key.getPublicExponent(); BigInteger modulus = key.getModulus(); int version; if (pubexp.equals(BigInteger.valueOf(3))) { - version = 1; + version = useSHA256 ? 3 : 1; } else if (pubexp.equals(BigInteger.valueOf(65537))) { - version = 2; + version = useSHA256 ? 4 : 2; } else { throw new Exception("Public exponent should be 3 or 65537 but is " + pubexp.toString(10) + "."); @@ -67,8 +69,8 @@ class DumpPublicKey { * version 1 key, the string will be a C initializer; this is * not true for newer key versions. */ - static String print(RSAPublicKey key) throws Exception { - int version = check(key); + static String print(RSAPublicKey key, boolean useSHA256) throws Exception { + int version = check(key, useSHA256); BigInteger N = key.getModulus(); @@ -135,10 +137,27 @@ class DumpPublicKey { for (int i = 0; i < args.length; i++) { FileInputStream input = new FileInputStream(args[i]); CertificateFactory cf = CertificateFactory.getInstance("X.509"); - Certificate cert = cf.generateCertificate(input); + X509Certificate cert = (X509Certificate) cf.generateCertificate(input); + + boolean useSHA256 = false; + String sigAlg = cert.getSigAlgName(); + if ("SHA1withRSA".equals(sigAlg) || "MD5withRSA".equals(sigAlg)) { + // SignApk has historically accepted "MD5withRSA" + // certificates, but treated them as "SHA1withRSA" + // anyway. Continue to do so for backwards + // compatibility. + useSHA256 = false; + } else if ("SHA256withRSA".equals(sigAlg)) { + useSHA256 = true; + } else { + System.err.println(args[i] + ": unsupported signature algorithm \"" + + sigAlg + "\""); + System.exit(1); + } + RSAPublicKey key = (RSAPublicKey) (cert.getPublicKey()); - check(key); - System.out.print(print(key)); + check(key, useSHA256); + System.out.print(print(key, useSHA256)); System.out.println(i < args.length - 1 ? "," : ""); } } catch (Exception e) { From 8fdbf97652b76b19b4faff522ddcf4a9da543a86 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Wed, 10 Apr 2013 15:08:28 -0700 Subject: [PATCH 009/541] mincrypt: merge the two RSA verifiers The e=3 and e=65537 implementations are nearly identical, refactor them down into one. Change-Id: I537a368a6cc195f373b9354d7472957fd683beea --- libmincrypt/Android.mk | 8 +- libmincrypt/rsa.c | 281 +++++++++++- libmincrypt/rsa_e_3.c | 244 ----------- libmincrypt/rsa_e_f4.c | 259 ----------- libmincrypt/test/Android.mk | 10 + libmincrypt/test/rsa_test.c | 838 ++++++++++++++++++++++++++++++++++++ 6 files changed, 1118 insertions(+), 522 deletions(-) delete mode 100644 libmincrypt/rsa_e_3.c delete mode 100644 libmincrypt/rsa_e_f4.c create mode 100644 libmincrypt/test/Android.mk create mode 100644 libmincrypt/test/rsa_test.c diff --git a/libmincrypt/Android.mk b/libmincrypt/Android.mk index addbed8b2..090d0e591 100644 --- a/libmincrypt/Android.mk +++ b/libmincrypt/Android.mk @@ -4,15 +4,15 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := libmincrypt -LOCAL_SRC_FILES := rsa.c rsa_e_3.c rsa_e_f4.c sha.c sha256.c +LOCAL_SRC_FILES := rsa.c sha.c sha256.c include $(BUILD_STATIC_LIBRARY) include $(CLEAR_VARS) LOCAL_MODULE := libmincrypt -LOCAL_SRC_FILES := rsa.c rsa_e_3.c rsa_e_f4.c sha.c sha256.c +LOCAL_SRC_FILES := rsa.c sha.c sha256.c include $(BUILD_HOST_STATIC_LIBRARY) -# TODO: drop the hyphen once these are checked in -include $(LOCAL_PATH)/tools/Android.mk +include $(LOCAL_PATH)/tools/Android.mk \ + $(LOCAL_PATH)/test/Android.mk diff --git a/libmincrypt/rsa.c b/libmincrypt/rsa.c index 0cdbaa24b..9061b3ae3 100644 --- a/libmincrypt/rsa.c +++ b/libmincrypt/rsa.c @@ -26,32 +26,283 @@ */ #include "mincrypt/rsa.h" +#include "mincrypt/sha.h" +#include "mincrypt/sha256.h" -int RSA_e_f4_verify(const RSAPublicKey* key, - const uint8_t* signature, - const int len, - const uint8_t* hash, - const int hash_len); +// a[] -= mod +static void subM(const RSAPublicKey* key, + uint32_t* a) { + int64_t A = 0; + int i; + for (i = 0; i < key->len; ++i) { + A += (uint64_t)a[i] - key->n[i]; + a[i] = (uint32_t)A; + A >>= 32; + } +} -int RSA_e_3_verify(const RSAPublicKey *key, - const uint8_t *signature, - const int len, - const uint8_t *hash, - const int hash_len); +// return a[] >= mod +static int geM(const RSAPublicKey* key, + const uint32_t* a) { + int i; + for (i = key->len; i;) { + --i; + if (a[i] < key->n[i]) return 0; + if (a[i] > key->n[i]) return 1; + } + return 1; // equal +} +// montgomery c[] += a * b[] / R % mod +static void montMulAdd(const RSAPublicKey* key, + uint32_t* c, + const uint32_t a, + const uint32_t* b) { + uint64_t A = (uint64_t)a * b[0] + c[0]; + uint32_t d0 = (uint32_t)A * key->n0inv; + uint64_t B = (uint64_t)d0 * key->n[0] + (uint32_t)A; + int i; + + for (i = 1; i < key->len; ++i) { + A = (A >> 32) + (uint64_t)a * b[i] + c[i]; + B = (B >> 32) + (uint64_t)d0 * key->n[i] + (uint32_t)A; + c[i - 1] = (uint32_t)B; + } + + A = (A >> 32) + (B >> 32); + + c[i - 1] = (uint32_t)A; + + if (A >> 32) { + subM(key, c); + } +} + +// montgomery c[] = a[] * b[] / R % mod +static void montMul(const RSAPublicKey* key, + uint32_t* c, + const uint32_t* a, + const uint32_t* b) { + int i; + for (i = 0; i < key->len; ++i) { + c[i] = 0; + } + for (i = 0; i < key->len; ++i) { + montMulAdd(key, c, a[i], b); + } +} + +// In-place public exponentiation. +// Input and output big-endian byte array in inout. +static void modpow(const RSAPublicKey* key, + uint8_t* inout) { + uint32_t a[RSANUMWORDS]; + uint32_t aR[RSANUMWORDS]; + uint32_t aaR[RSANUMWORDS]; + uint32_t* aaa = 0; + int i; + + // Convert from big endian byte array to little endian word array. + for (i = 0; i < key->len; ++i) { + uint32_t tmp = + (inout[((key->len - 1 - i) * 4) + 0] << 24) | + (inout[((key->len - 1 - i) * 4) + 1] << 16) | + (inout[((key->len - 1 - i) * 4) + 2] << 8) | + (inout[((key->len - 1 - i) * 4) + 3] << 0); + a[i] = tmp; + } + + if (key->exponent == 65537) { + aaa = aaR; // Re-use location. + montMul(key, aR, a, key->rr); // aR = a * RR / R mod M + for (i = 0; i < 16; i += 2) { + montMul(key, aaR, aR, aR); // aaR = aR * aR / R mod M + montMul(key, aR, aaR, aaR); // aR = aaR * aaR / R mod M + } + montMul(key, aaa, aR, a); // aaa = aR * a / R mod M + } else if (key->exponent == 3) { + aaa = aR; // Re-use location. + montMul(key, aR, a, key->rr); /* aR = a * RR / R mod M */ + montMul(key, aaR, aR, aR); /* aaR = aR * aR / R mod M */ + montMul(key, aaa, aaR, a); /* aaa = aaR * a / R mod M */ + } + + // Make sure aaa < mod; aaa is at most 1x mod too large. + if (geM(key, aaa)) { + subM(key, aaa); + } + + // Convert to bigendian byte array + for (i = key->len - 1; i >= 0; --i) { + uint32_t tmp = aaa[i]; + *inout++ = tmp >> 24; + *inout++ = tmp >> 16; + *inout++ = tmp >> 8; + *inout++ = tmp >> 0; + } +} + +// Expected PKCS1.5 signature padding bytes, for a keytool RSA signature. +// Has the 0-length optional parameter encoded in the ASN1 (as opposed to the +// other flavor which omits the optional parameter entirely). This code does not +// accept signatures without the optional parameter. + +/* +static const uint8_t sha_padding[RSANUMBYTES] = { + 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x30, 0x21, 0x30, + 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, + 0x05, 0x00, 0x04, 0x14, + + // 20 bytes of hash go here. + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +}; +*/ + +// SHA-1 of PKCS1.5 signature sha_padding for 2048 bit, as above. +// At the location of the bytes of the hash all 00 are hashed. +static const uint8_t kExpectedPadShaRsa2048[SHA_DIGEST_SIZE] = { + 0xdc, 0xbd, 0xbe, 0x42, 0xd5, 0xf5, 0xa7, 0x2e, + 0x6e, 0xfc, 0xf5, 0x5d, 0xaf, 0x9d, 0xea, 0x68, + 0x7c, 0xfb, 0xf1, 0x67 +}; + +/* +static const uint8_t sha256_padding[RSANUMBYTES] = { + 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x30, 0x31, 0x30, + 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, + 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20, + + // 32 bytes of hash go here. + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +}; +*/ + +// SHA-256 of PKCS1.5 signature sha256_padding for 2048 bit, as above. +// At the location of the bytes of the hash all 00 are hashed. +static const uint8_t kExpectedPadSha256Rsa2048[SHA256_DIGEST_SIZE] = { + 0xab, 0x28, 0x8d, 0x8a, 0xd7, 0xd9, 0x59, 0x92, + 0xba, 0xcc, 0xf8, 0x67, 0x20, 0xe1, 0x15, 0x2e, + 0x39, 0x8d, 0x80, 0x36, 0xd6, 0x6f, 0xf0, 0xfd, + 0x90, 0xe8, 0x7d, 0x8b, 0xe1, 0x7c, 0x87, 0x59, +}; + +// Verify a 2048-bit RSA PKCS1.5 signature against an expected hash. +// Both e=3 and e=65537 are supported. hash_len may be +// SHA_DIGEST_SIZE (== 20) to indicate a SHA-1 hash, or +// SHA256_DIGEST_SIZE (== 32) to indicate a SHA-256 hash. No other +// values are supported. +// +// Returns 1 on successful verification, 0 on failure. int RSA_verify(const RSAPublicKey *key, const uint8_t *signature, const int len, const uint8_t *hash, const int hash_len) { - switch (key->exponent) { - case 3: - return RSA_e_3_verify(key, signature, len, hash, hash_len); + uint8_t buf[RSANUMBYTES]; + int i; + const uint8_t* padding_hash; + + if (key->len != RSANUMWORDS) { + return 0; // Wrong key passed in. + } + + if (len != sizeof(buf)) { + return 0; // Wrong input length. + } + + if (hash_len != SHA_DIGEST_SIZE && + hash_len != SHA256_DIGEST_SIZE) { + return 0; // Unsupported hash. + } + + if (key->exponent != 3 && key->exponent != 65537) { + return 0; // Unsupported exponent. + } + + for (i = 0; i < len; ++i) { // Copy input to local workspace. + buf[i] = signature[i]; + } + + modpow(key, buf); // In-place exponentiation. + + // Xor sha portion, so it all becomes 00 iff equal. + for (i = len - hash_len; i < len; ++i) { + buf[i] ^= *hash++; + } + + // Hash resulting buf, in-place. + switch (hash_len) { + case SHA_DIGEST_SIZE: + padding_hash = kExpectedPadShaRsa2048; + SHA_hash(buf, len, buf); break; - case 65537: - return RSA_e_f4_verify(key, signature, len, hash, hash_len); + case SHA256_DIGEST_SIZE: + padding_hash = kExpectedPadSha256Rsa2048; + SHA256_hash(buf, len, buf); break; default: return 0; } + + // Compare against expected hash value. + for (i = 0; i < hash_len; ++i) { + if (buf[i] != padding_hash[i]) { + return 0; + } + } + + return 1; // All checked out OK. } diff --git a/libmincrypt/rsa_e_3.c b/libmincrypt/rsa_e_3.c deleted file mode 100644 index 012a35717..000000000 --- a/libmincrypt/rsa_e_3.c +++ /dev/null @@ -1,244 +0,0 @@ -/* rsa_e_3.c -** -** Copyright 2008, The Android Open Source Project -** -** Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in the -** documentation and/or other materials provided with the distribution. -** * Neither the name of Google Inc. nor the names of its contributors may -** be used to endorse or promote products derived from this software -** without specific prior written permission. -** -** THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR -** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO -** EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; -** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR -** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#include "mincrypt/rsa.h" -#include "mincrypt/sha.h" -#include "mincrypt/sha256.h" - -/* a[] -= mod */ -static void subM(const RSAPublicKey *key, uint32_t *a) { - int64_t A = 0; - int i; - for (i = 0; i < key->len; ++i) { - A += (uint64_t)a[i] - key->n[i]; - a[i] = (uint32_t)A; - A >>= 32; - } -} - -/* return a[] >= mod */ -static int geM(const RSAPublicKey *key, const uint32_t *a) { - int i; - for (i = key->len; i;) { - --i; - if (a[i] < key->n[i]) return 0; - if (a[i] > key->n[i]) return 1; - } - return 1; /* equal */ -} - -/* montgomery c[] += a * b[] / R % mod */ -static void montMulAdd(const RSAPublicKey *key, - uint32_t* c, - const uint32_t a, - const uint32_t* b) { - uint64_t A = (uint64_t)a * b[0] + c[0]; - uint32_t d0 = (uint32_t)A * key->n0inv; - uint64_t B = (uint64_t)d0 * key->n[0] + (uint32_t)A; - int i; - - for (i = 1; i < key->len; ++i) { - A = (A >> 32) + (uint64_t)a * b[i] + c[i]; - B = (B >> 32) + (uint64_t)d0 * key->n[i] + (uint32_t)A; - c[i - 1] = (uint32_t)B; - } - - A = (A >> 32) + (B >> 32); - - c[i - 1] = (uint32_t)A; - - if (A >> 32) { - subM(key, c); - } -} - -/* montgomery c[] = a[] * b[] / R % mod */ -static void montMul(const RSAPublicKey *key, - uint32_t* c, - const uint32_t* a, - const uint32_t* b) { - int i; - for (i = 0; i < key->len; ++i) { - c[i] = 0; - } - for (i = 0; i < key->len; ++i) { - montMulAdd(key, c, a[i], b); - } -} - -/* In-place public exponentiation. -** Input and output big-endian byte array in inout. -*/ -static void modpow3(const RSAPublicKey *key, - uint8_t* inout) { - uint32_t a[RSANUMWORDS]; - uint32_t aR[RSANUMWORDS]; - uint32_t aaR[RSANUMWORDS]; - uint32_t *aaa = aR; /* Re-use location. */ - int i; - - /* Convert from big endian byte array to little endian word array. */ - for (i = 0; i < key->len; ++i) { - uint32_t tmp = - (inout[((key->len - 1 - i) * 4) + 0] << 24) | - (inout[((key->len - 1 - i) * 4) + 1] << 16) | - (inout[((key->len - 1 - i) * 4) + 2] << 8) | - (inout[((key->len - 1 - i) * 4) + 3] << 0); - a[i] = tmp; - } - - montMul(key, aR, a, key->rr); /* aR = a * RR / R mod M */ - montMul(key, aaR, aR, aR); /* aaR = aR * aR / R mod M */ - montMul(key, aaa, aaR, a); /* aaa = aaR * a / R mod M */ - - /* Make sure aaa < mod; aaa is at most 1x mod too large. */ - if (geM(key, aaa)) { - subM(key, aaa); - } - - /* Convert to bigendian byte array */ - for (i = key->len - 1; i >= 0; --i) { - uint32_t tmp = aaa[i]; - *inout++ = tmp >> 24; - *inout++ = tmp >> 16; - *inout++ = tmp >> 8; - *inout++ = tmp >> 0; - } -} - -/* Expected PKCS1.5 signature padding bytes, for a keytool RSA signature. -** Has the 0-length optional parameter encoded in the ASN1 (as opposed to the -** other flavor which omits the optional parameter entirely). This code does not -** accept signatures without the optional parameter. -*/ -static const uint8_t sha_padding[RSANUMBYTES - SHA_DIGEST_SIZE] = { - 0x00,0x01,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00, - 0x30,0x21,0x30,0x09,0x06,0x05,0x2b,0x0e,0x03,0x02,0x1a,0x05,0x00, - 0x04,0x14 -}; - -static const uint8_t sha256_padding[RSANUMBYTES - SHA256_DIGEST_SIZE] = { - 0x00,0x01,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x30,0x31,0x30, - 0x0d,0x06,0x09,0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x01,0x05, - 0x00,0x04,0x20 -}; - - -/* Verify a 2048 bit RSA e=3 PKCS1.5 signature against an expected SHA-1 hash. -** Returns 0 on failure, 1 on success. -*/ -int RSA_e_3_verify(const RSAPublicKey *key, - const uint8_t *signature, - const int len, - const uint8_t *hash, - const int hash_len) { - uint8_t buf[RSANUMBYTES]; - int i; - int padding_size; - const uint8_t* padding; - - switch (hash_len) { - case SHA_DIGEST_SIZE: - padding = sha_padding; - padding_size = sizeof(sha_padding); - break; - case SHA256_DIGEST_SIZE: - padding = sha256_padding; - padding_size = sizeof(sha256_padding); - break; - default: - return 0; // unsupported hash - } - - if (key->len != RSANUMWORDS) { - return 0; /* Wrong key passed in. */ - } - - if (len != sizeof(buf)) { - return 0; /* Wrong input length. */ - } - - if (key->exponent != 3) { - return 0; // Wrong exponent. - } - - for (i = 0; i < len; ++i) { - buf[i] = signature[i]; - } - - modpow3(key, buf); - - /* Check pkcs1.5 padding bytes. */ - for (i = 0; i < padding_size; ++i) { - if (buf[i] != padding[i]) { - return 0; - } - } - - /* Check sha digest matches. */ - for (; i < len; ++i) { - if (buf[i] != *hash++) { - return 0; - } - } - - return 1; -} diff --git a/libmincrypt/rsa_e_f4.c b/libmincrypt/rsa_e_f4.c deleted file mode 100644 index 17a2de2c7..000000000 --- a/libmincrypt/rsa_e_f4.c +++ /dev/null @@ -1,259 +0,0 @@ -/* rsa_e_f4.c -** -** Copyright 2012, The Android Open Source Project -** -** Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in the -** documentation and/or other materials provided with the distribution. -** * Neither the name of Google Inc. nor the names of its contributors may -** be used to endorse or promote products derived from this software -** without specific prior written permission. -** -** THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR -** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO -** EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; -** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR -** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#include "mincrypt/rsa.h" -#include "mincrypt/sha.h" -#include "mincrypt/sha256.h" - -// a[] -= mod -static void subM(const RSAPublicKey* key, - uint32_t* a) { - int64_t A = 0; - int i; - for (i = 0; i < key->len; ++i) { - A += (uint64_t)a[i] - key->n[i]; - a[i] = (uint32_t)A; - A >>= 32; - } -} - -// return a[] >= mod -static int geM(const RSAPublicKey* key, - const uint32_t* a) { - int i; - for (i = key->len; i;) { - --i; - if (a[i] < key->n[i]) return 0; - if (a[i] > key->n[i]) return 1; - } - return 1; // equal -} - -// montgomery c[] += a * b[] / R % mod -static void montMulAdd(const RSAPublicKey* key, - uint32_t* c, - const uint32_t a, - const uint32_t* b) { - uint64_t A = (uint64_t)a * b[0] + c[0]; - uint32_t d0 = (uint32_t)A * key->n0inv; - uint64_t B = (uint64_t)d0 * key->n[0] + (uint32_t)A; - int i; - - for (i = 1; i < key->len; ++i) { - A = (A >> 32) + (uint64_t)a * b[i] + c[i]; - B = (B >> 32) + (uint64_t)d0 * key->n[i] + (uint32_t)A; - c[i - 1] = (uint32_t)B; - } - - A = (A >> 32) + (B >> 32); - - c[i - 1] = (uint32_t)A; - - if (A >> 32) { - subM(key, c); - } -} - -// montgomery c[] = a[] * b[] / R % mod -static void montMul(const RSAPublicKey* key, - uint32_t* c, - const uint32_t* a, - const uint32_t* b) { - int i; - for (i = 0; i < key->len; ++i) { - c[i] = 0; - } - for (i = 0; i < key->len; ++i) { - montMulAdd(key, c, a[i], b); - } -} - -// In-place public exponentiation. -// Input and output big-endian byte array in inout. -static void modpowF4(const RSAPublicKey* key, - uint8_t* inout) { - uint32_t a[RSANUMWORDS]; - uint32_t aR[RSANUMWORDS]; - uint32_t aaR[RSANUMWORDS]; - uint32_t* aaa = aaR; // Re-use location. - int i; - - // Convert from big endian byte array to little endian word array. - for (i = 0; i < key->len; ++i) { - uint32_t tmp = - (inout[((key->len - 1 - i) * 4) + 0] << 24) | - (inout[((key->len - 1 - i) * 4) + 1] << 16) | - (inout[((key->len - 1 - i) * 4) + 2] << 8) | - (inout[((key->len - 1 - i) * 4) + 3] << 0); - a[i] = tmp; - } - - montMul(key, aR, a, key->rr); // aR = a * RR / R mod M - for (i = 0; i < 16; i += 2) { - montMul(key, aaR, aR, aR); // aaR = aR * aR / R mod M - montMul(key, aR, aaR, aaR); // aR = aaR * aaR / R mod M - } - montMul(key, aaa, aR, a); // aaa = aR * a / R mod M - - // Make sure aaa < mod; aaa is at most 1x mod too large. - if (geM(key, aaa)) { - subM(key, aaa); - } - - // Convert to bigendian byte array - for (i = key->len - 1; i >= 0; --i) { - uint32_t tmp = aaa[i]; - *inout++ = tmp >> 24; - *inout++ = tmp >> 16; - *inout++ = tmp >> 8; - *inout++ = tmp >> 0; - } -} - -// Expected PKCS1.5 signature padding bytes, for a keytool RSA signature. -// Has the 0-length optional parameter encoded in the ASN1 (as opposed to the -// other flavor which omits the optional parameter entirely). This code does not -// accept signatures without the optional parameter. -/* -static const uint8_t sha_padding[RSANUMBYTES] = { -0x00,0x01,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x30,0x21,0x30,0x09,0x06,0x05,0x2b,0x0e,0x03,0x02,0x1a,0x05,0x00,0x04,0x14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 -}; - -static const uint8_t sha256_padding[RSANUMBYTES] = { - 0x00,0x01,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, - - 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x30,0x31,0x30, - 0x0d,0x06,0x09,0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x01,0x05, - 0x00,0x04,0x20, - - // 32 bytes of hash go here. - 0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0, -}; - -*/ - -// SHA-1 of PKCS1.5 signature sha_padding for 2048 bit, as above. -// At the location of the bytes of the hash all 00 are hashed. -static const uint8_t kExpectedPadShaRsa2048[SHA_DIGEST_SIZE] = { - 0xdc, 0xbd, 0xbe, 0x42, 0xd5, 0xf5, 0xa7, 0x2e, 0x6e, 0xfc, - 0xf5, 0x5d, 0xaf, 0x9d, 0xea, 0x68, 0x7c, 0xfb, 0xf1, 0x67 -}; - -// SHA-256 of PKCS1.5 signature sha256_padding for 2048 bit, as above. -// At the location of the bytes of the hash all 00 are hashed. -static const uint8_t kExpectedPadSha256Rsa2048[SHA256_DIGEST_SIZE] = { - 0xab, 0x28, 0x8d, 0x8a, 0xd7, 0xd9, 0x59, 0x92, - 0xba, 0xcc, 0xf8, 0x67, 0x20, 0xe1, 0x15, 0x2e, - 0x39, 0x8d, 0x80, 0x36, 0xd6, 0x6f, 0xf0, 0xfd, - 0x90, 0xe8, 0x7d, 0x8b, 0xe1, 0x7c, 0x87, 0x59, -}; - -// Verify a 2048 bit RSA e=65537 PKCS1.5 signature against an expected -// SHA-1 hash. Returns 0 on failure, 1 on success. -int RSA_e_f4_verify(const RSAPublicKey* key, - const uint8_t* signature, - const int len, - const uint8_t* hash, - const int hash_len) { - uint8_t buf[RSANUMBYTES]; - int i; - const uint8_t* padding_hash; - - switch (hash_len) { - case SHA_DIGEST_SIZE: - padding_hash = kExpectedPadShaRsa2048; - break; - case SHA256_DIGEST_SIZE: - padding_hash = kExpectedPadSha256Rsa2048; - break; - default: - return 0; // unsupported hash - } - - if (key->len != RSANUMWORDS) { - return 0; // Wrong key passed in. - } - - if (len != sizeof(buf)) { - return 0; // Wrong input length. - } - - if (key->exponent != 65537) { - return 0; // Wrong exponent. - } - - for (i = 0; i < len; ++i) { // Copy input to local workspace. - buf[i] = signature[i]; - } - - modpowF4(key, buf); // In-place exponentiation. - - // Xor sha portion, so it all becomes 00 iff equal. - for (i = len - hash_len; i < len; ++i) { - buf[i] ^= *hash++; - } - - // Hash resulting buf, in-place. - switch (hash_len) { - case SHA_DIGEST_SIZE: - SHA_hash(buf, len, buf); - break; - case SHA256_DIGEST_SIZE: - SHA256_hash(buf, len, buf); - break; - default: - return 0; - } - - // Compare against expected hash value. - for (i = 0; i < hash_len; ++i) { - if (buf[i] != padding_hash[i]) { - return 0; - } - } - - return 1; // All checked out OK. -} diff --git a/libmincrypt/test/Android.mk b/libmincrypt/test/Android.mk new file mode 100644 index 000000000..a28ccd89b --- /dev/null +++ b/libmincrypt/test/Android.mk @@ -0,0 +1,10 @@ +# Copyright 2013 The Android Open Source Project + +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE := rsa_test +LOCAL_SRC_FILES := rsa_test.c +LOCAL_STATIC_LIBRARIES := libmincrypt +include $(BUILD_HOST_EXECUTABLE) + diff --git a/libmincrypt/test/rsa_test.c b/libmincrypt/test/rsa_test.c new file mode 100644 index 000000000..17862dcc9 --- /dev/null +++ b/libmincrypt/test/rsa_test.c @@ -0,0 +1,838 @@ +/* rsa_test.c +** +** Copyright 2013, The Android Open Source Project +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** * Neither the name of Google Inc. nor the names of its contributors may +** be used to endorse or promote products derived from this software +** without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +** EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include +#include + +#include "mincrypt/rsa.h" +#include "mincrypt/sha.h" + +// RSA test data taken from: +// +// ftp://ftp.rsa.com/pub/rsalabs/tmp/pkcs1v15sign-vectors.txt + +// This is the result (reformatted) of running DumpPublicKey on: +// +// # Example 15: A 2048-bit RSA key pair +// # ----------------------------------- +// +// +// # Public key +// # ---------- +// +// # Modulus: +// df 27 1f d2 5f 86 44 49 6b 0c 81 be 4b d5 02 97 +// ef 09 9b 00 2a 6f d6 77 27 eb 44 9c ea 56 6e d6 +// a3 98 1a 71 31 2a 14 1c ab c9 81 5c 12 09 e3 20 +// a2 5b 32 46 4e 99 99 f1 8c a1 3a 9f d3 89 25 58 +// f9 e0 ad ef dd 36 50 dd 23 a3 f0 36 d6 0f e3 98 +// 84 37 06 a4 0b 0b 84 62 c8 be e3 bc e1 2f 1f 28 +// 60 c2 44 4c dc 6a 44 47 6a 75 ff 4a a2 42 73 cc +// be 3b f8 02 48 46 5f 8f f8 c3 a7 f3 36 7d fc 0d +// f5 b6 50 9a 4f 82 81 1c ed d8 1c da aa 73 c4 91 +// da 41 21 70 d5 44 d4 ba 96 b9 7f 0a fc 80 65 49 +// 8d 3a 49 fd 91 09 92 a1 f0 72 5b e2 4f 46 5c fe +// 7e 0e ab f6 78 99 6c 50 bc 5e 75 24 ab f7 3f 15 +// e5 be f7 d5 18 39 4e 31 38 ce 49 44 50 6a aa af +// 3f 9b 23 6d ca b8 fc 00 f8 7a f5 96 fd c3 d9 d6 +// c7 5c d5 08 36 2f ae 2c be dd cc 4c 74 50 b1 7b +// 77 6c 07 9e cc a1 f2 56 35 1a 43 b9 7d be 21 53 +// +// # Exponent: +// 01 00 01 + +RSAPublicKey key_15 = { + .len = 64, + .n0inv = 0xf0053525, + .n = {2109612371u,890913721u,3433165398u,2003568542u, + 1951445371u,3202206796u,909094444u,3344749832u, + 4257470934u,4168807830u,3401120768u,1067131757u, + 1349167791u,953043268u,406408753u,3854497749u, + 2885107477u,3160306980u,2023320656u,2114890742u, + 1330011390u,4034026466u,2433323681u,2369407485u, + 4236272969u,2528739082u,3578057914u,3661701488u, + 2859713681u,3990363354u,1333952796u,4122366106u, + 914226189u,4173572083u,1212571535u,3191601154u, + 2722264012u,1786117962u,3697951815u,1623344204u, + 3777961768u,3367953340u,185304162u,2218198692u, + 3591365528u,597946422u,3711324381u,4192251375u, + 3548980568u,2359376543u,1318689265u,2723885638u, + 302637856u,2882109788u,824841244u,2744654449u, + 3931533014u,669729948u,711972471u,4010384128u, + 1272251031u,1795981758u,1602634825u,3743883218u}, + .rr = {820482522u,2494434288u,1082168230u,731376296u, + 1306039452u,3139792975u,2575869288u,3874938710u, + 3198185181u,153506080u,1236489694u,1061859740u, + 1174461268u,115279508u,1782749185u,238124145u, + 3587596076u,2259236093u,1112265915u,4048059865u, + 3890381098u,999426242u,794481771u,3804065613u, + 2786019148u,461403875u,3072256692u,4079652654u, + 3056719901u,1871565394u,212974856u,3359008174u, + 1397773937u,3796256698u,914342841u,1097174457u, + 3322220191u,3170814748u,2439215020u,618719336u, + 3629353460u,496817177u,317052742u,380264245u, + 1976007217u,2697736152u,312540864u,4291855337u, + 697006561u,4234182488u,3904590917u,2609582216u, + 451424084u,1805773827u,776344974u,1064489733u, + 2633377036u,1954826648u,3202815814u,2240368662u, + 2618582484u,2211196815u,4107362845u,3640258615u}, + .exponent = 65537, +}; + +// PKCS#1 v1.5 Signature Example 15.1 + +char* message_1 = + "f4 5d 55 f3 55 51 e9 75 d6 a8 dc 7e a9 f4 88 59" + "39 40 cc 75 69 4a 27 8f 27 e5 78 a1 63 d8 39 b3" + "40 40 84 18 08 cf 9c 58 c9 b8 72 8b f5 f9 ce 8e" + "e8 11 ea 91 71 4f 47 ba b9 2d 0f 6d 5a 26 fc fe" + "ea 6c d9 3b 91 0c 0a 2c 96 3e 64 eb 18 23 f1 02" + "75 3d 41 f0 33 59 10 ad 3a 97 71 04 f1 aa f6 c3" + "74 27 16 a9 75 5d 11 b8 ee d6 90 47 7f 44 5c 5d" + "27 20 8b 2e 28 43 30 fa 3d 30 14 23 fa 7f 2d 08" + "6e 0a d0 b8 92 b9 db 54 4e 45 6d 3f 0d ab 85 d9" + "53 c1 2d 34 0a a8 73 ed a7 27 c8 a6 49 db 7f a6" + "37 40 e2 5e 9a f1 53 3b 30 7e 61 32 99 93 11 0e" + "95 19 4e 03 93 99 c3 82 4d 24 c5 1f 22 b2 6b de" + "10 24 cd 39 59 58 a2 df eb 48 16 a6 e8 ad ed b5" + "0b 1f 6b 56 d0 b3 06 0f f0 f1 c4 cb 0d 0e 00 1d" + "d5 9d 73 be 12"; + +char* signature_1 = + "b7 5a 54 66 b6 5d 0f 30 0e f5 38 33 f2 17 5c 8a" + "34 7a 38 04 fc 63 45 1d c9 02 f0 b7 1f 90 83 45" + "9e d3 7a 51 79 a3 b7 23 a5 3f 10 51 64 2d 77 37" + "4c 4c 6c 8d bb 1c a2 05 25 f5 c9 f3 2d b7 76 95" + "35 56 da 31 29 0e 22 19 74 82 ce b6 99 06 c4 6a" + "75 8f b0 e7 40 9b a8 01 07 7d 2a 0a 20 ea e7 d1" + "d6 d3 92 ab 49 57 e8 6b 76 f0 65 2d 68 b8 39 88" + "a7 8f 26 e1 11 72 ea 60 9b f8 49 fb bd 78 ad 7e" + "dc e2 1d e6 62 a0 81 36 8c 04 06 07 ce e2 9d b0" + "62 72 27 f4 49 63 ad 17 1d 22 93 b6 33 a3 92 e3" + "31 dc a5 4f e3 08 27 52 f4 3f 63 c1 61 b4 47 a4" + "c6 5a 68 75 67 0d 5f 66 00 fc c8 60 a1 ca eb 0a" + "88 f8 fd ec 4e 56 43 98 a5 c4 6c 87 f6 8c e0 70" + "01 f6 21 3a be 0a b5 62 5f 87 d1 90 25 f0 8d 81" + "da c7 bd 45 86 bc 93 82 19 1f 6d 28 80 f6 22 7e" + "5d f3 ee d2 1e 77 92 d2 49 48 04 87 f3 65 52 61"; + +// PKCS#1 v1.5 Signature Example 15.2 + +char *message_2 = + "c1 4b 4c 60 75 b2 f9 aa d6 61 de f4 ec fd 3c b9 " + "33 c6 23 f4 e6 3b f5 34 10 d2 f0 16 d1 ab 98 e2 " + "72 9e cc f8 00 6c d8 e0 80 50 73 7d 95 fd bf 29 " + "6b 66 f5 b9 79 2a 90 29 36 c4 f7 ac 69 f5 14 53 " + "ce 43 69 45 2d c2 2d 96 f0 37 74 81 14 66 20 00 " + "dd 9c d3 a5 e1 79 f4 e0 f8 1f a6 a0 31 1c a1 ae " + "e6 51 9a 0f 63 ce c7 8d 27 bb 72 63 93 fb 7f 1f " + "88 cd e7 c9 7f 8a 66 cd 66 30 12 81 da c3 f3 a4 " + "33 24 8c 75 d6 c2 dc d7 08 b6 a9 7b 0a 3f 32 5e " + "0b 29 64 f8 a5 81 9e 47 9b "; + +char* signature_2 = + "af a7 34 34 62 be a1 22 cc 14 9f ca 70 ab da e7" + "94 46 67 7d b5 37 36 66 af 7d c3 13 01 5f 4d e7" + "86 e6 e3 94 94 6f ad 3c c0 e2 b0 2b ed ba 50 47" + "fe 9e 2d 7d 09 97 05 e4 a3 9f 28 68 32 79 cf 0a" + "c8 5c 15 30 41 22 42 c0 e9 18 95 3b e0 00 e9 39" + "cf 3b f1 82 52 5e 19 93 70 fa 79 07 eb a6 9d 5d" + "b4 63 10 17 c0 e3 6d f7 03 79 b5 db 8d 4c 69 5a" + "97 9a 8e 61 73 22 40 65 d7 dc 15 13 2e f2 8c d8" + "22 79 51 63 06 3b 54 c6 51 14 1b e8 6d 36 e3 67" + "35 bc 61 f3 1f ca 57 4e 53 09 f3 a3 bb df 91 ef" + "f1 2b 99 e9 cc 17 44 f1 ee 9a 1b d2 2c 5b ad 96" + "ad 48 19 29 25 1f 03 43 fd 36 bc f0 ac de 7f 11" + "e5 ad 60 97 77 21 20 27 96 fe 06 1f 9a da 1f c4" + "c8 e0 0d 60 22 a8 35 75 85 ff e9 fd d5 93 31 a2" + "8c 4a a3 12 15 88 fb 6c f6 83 96 d8 ac 05 46 59" + "95 00 c9 70 85 00 a5 97 2b d5 4f 72 cf 8d b0 c8"; + +// PKCS#1 v1.5 Signature Example 15.3 + +char* message_3 = + "d0 23 71 ad 7e e4 8b bf db 27 63 de 7a 84 3b 94 " + "08 ce 5e b5 ab f8 47 ca 3d 73 59 86 df 84 e9 06 " + "0b db cd d3 a5 5b a5 5d de 20 d4 76 1e 1a 21 d2 " + "25 c1 a1 86 f4 ac 4b 30 19 d3 ad f7 8f e6 33 46 " + "67 f5 6f 70 c9 01 a0 a2 70 0c 6f 0d 56 ad d7 19 " + "59 2d c8 8f 6d 23 06 c7 00 9f 6e 7a 63 5b 4c b3 " + "a5 02 df e6 8d dc 58 d0 3b e1 0a 11 70 00 4f e7 " + "4d d3 e4 6b 82 59 1f f7 54 14 f0 c4 a0 3e 60 5e " + "20 52 4f 24 16 f1 2e ca 58 9f 11 1b 75 d6 39 c6 " + "1b aa 80 ca fd 05 cf 35 00 24 4a 21 9e d9 ce d9 " + "f0 b1 02 97 18 2b 65 3b 52 6f 40 0f 29 53 ba 21 " + "4d 5b cd 47 88 41 32 87 2a e9 0d 4d 6b 1f 42 15 " + "39 f9 f3 46 62 a5 6d c0 e7 b4 b9 23 b6 23 1e 30 " + "d2 67 67 97 81 7f 7c 33 7b 5a c8 24 ba 93 14 3b " + "33 81 fa 3d ce 0e 6a eb d3 8e 67 73 51 87 b1 eb " + "d9 5c 02 "; + +char* signature_3 = + "3b ac 63 f8 6e 3b 70 27 12 03 10 6b 9c 79 aa bd" + "9f 47 7c 56 e4 ee 58 a4 fc e5 ba f2 ca b4 96 0f" + "88 39 1c 9c 23 69 8b e7 5c 99 ae df 9e 1a bf 17" + "05 be 1d ac 33 14 0a db 48 eb 31 f4 50 bb 9e fe" + "83 b7 b9 0d b7 f1 57 6d 33 f4 0c 1c ba 4b 8d 6b" + "1d 33 23 56 4b 0f 17 74 11 4f a7 c0 8e 6d 1e 20" + "dd 8f bb a9 b6 ac 7a d4 1e 26 b4 56 8f 4a 8a ac" + "bf d1 78 a8 f8 d2 c9 d5 f5 b8 81 12 93 5a 8b c9" + "ae 32 cd a4 0b 8d 20 37 55 10 73 50 96 53 68 18" + "ce 2b 2d b7 1a 97 72 c9 b0 dd a0 9a e1 01 52 fa" + "11 46 62 18 d0 91 b5 3d 92 54 30 61 b7 29 4a 55" + "be 82 ff 35 d5 c3 2f a2 33 f0 5a aa c7 58 50 30" + "7e cf 81 38 3c 11 16 74 39 7b 1a 1b 9d 3b f7 61" + "2c cb e5 ba cd 2b 38 f0 a9 83 97 b2 4c 83 65 8f" + "b6 c0 b4 14 0e f1 19 70 c4 63 0d 44 34 4e 76 ea" + "ed 74 dc be e8 11 db f6 57 59 41 f0 8a 65 23 b8"; + +// PKCS#1 v1.5 Signature Example 15.4 + +char* message_4 = + "29 03 55 84 ab 7e 02 26 a9 ec 4b 02 e8 dc f1 27 " + "2d c9 a4 1d 73 e2 82 00 07 b0 f6 e2 1f ec cd 5b " + "d9 db b9 ef 88 cd 67 58 76 9e e1 f9 56 da 7a d1 " + "84 41 de 6f ab 83 86 db c6 93 "; + +char* signature_4 = + "28 d8 e3 fc d5 dd db 21 ff bd 8d f1 63 0d 73 77" + "aa 26 51 e1 4c ad 1c 0e 43 cc c5 2f 90 7f 94 6d" + "66 de 72 54 e2 7a 6c 19 0e b0 22 ee 89 ec f6 22" + "4b 09 7b 71 06 8c d6 07 28 a1 ae d6 4b 80 e5 45" + "7b d3 10 6d d9 17 06 c9 37 c9 79 5f 2b 36 36 7f" + "f1 53 dc 25 19 a8 db 9b df 2c 80 74 30 c4 51 de" + "17 bb cd 0c e7 82 b3 e8 f1 02 4d 90 62 4d ea 7f" + "1e ed c7 42 0b 7e 7c aa 65 77 ce f4 31 41 a7 26" + "42 06 58 0e 44 a1 67 df 5e 41 ee a0 e6 9a 80 54" + "54 c4 0e ef c1 3f 48 e4 23 d7 a3 2d 02 ed 42 c0" + "ab 03 d0 a7 cf 70 c5 86 0a c9 2e 03 ee 00 5b 60" + "ff 35 03 42 4b 98 cc 89 45 68 c7 c5 6a 02 33 55" + "1c eb e5 88 cf 8b 01 67 b7 df 13 ad ca d8 28 67" + "68 10 49 9c 70 4d a7 ae 23 41 4d 69 e3 c0 d2 db" + "5d cb c2 61 3b c1 20 42 1f 9e 36 53 c5 a8 76 72" + "97 64 3c 7e 07 40 de 01 63 55 45 3d 6c 95 ae 72"; + +// PKCS#1 v1.5 Signature Example 15.5 + +char* message_5 = + "bd a3 a1 c7 90 59 ea e5 98 30 8d 3d f6 09 "; + +char* signature_5 = + "a1 56 17 6c b9 67 77 c7 fb 96 10 5d bd 91 3b c4" + "f7 40 54 f6 80 7c 60 08 a1 a9 56 ea 92 c1 f8 1c" + "b8 97 dc 4b 92 ef 9f 4e 40 66 8d c7 c5 56 90 1a" + "cb 6c f2 69 fe 61 5b 0f b7 2b 30 a5 13 38 69 23" + "14 b0 e5 87 8a 88 c2 c7 77 4b d1 69 39 b5 ab d8" + "2b 44 29 d6 7b d7 ac 8e 5e a7 fe 92 4e 20 a6 ec" + "66 22 91 f2 54 8d 73 4f 66 34 86 8b 03 9a a5 f9" + "d4 d9 06 b2 d0 cb 85 85 bf 42 85 47 af c9 1c 6e" + "20 52 dd cd 00 1c 3e f8 c8 ee fc 3b 6b 2a 82 b6" + "f9 c8 8c 56 f2 e2 c3 cb 0b e4 b8 0d a9 5e ba 37" + "1d 8b 5f 60 f9 25 38 74 3d db b5 da 29 72 c7 1f" + "e7 b9 f1 b7 90 26 8a 0e 77 0f c5 eb 4d 5d d8 52" + "47 d4 8a e2 ec 3f 26 25 5a 39 85 52 02 06 a1 f2" + "68 e4 83 e9 db b1 d5 ca b1 90 91 76 06 de 31 e7" + "c5 18 2d 8f 15 1b f4 1d fe cc ae d7 cd e6 90 b2" + "16 47 10 6b 49 0c 72 9d 54 a8 fe 28 02 a6 d1 26"; + +// PKCS#1 v1.5 Signature Example 15.6 + +char* message_6 = + "c1 87 91 5e 4e 87 da 81 c0 8e d4 35 6a 0c ce ac " + "1c 4f b5 c0 46 b4 52 81 b3 87 ec 28 f1 ab fd 56 " + "7e 54 6b 23 6b 37 d0 1a e7 1d 3b 28 34 36 5d 3d " + "f3 80 b7 50 61 b7 36 b0 13 0b 07 0b e5 8a e8 a4 " + "6d 12 16 63 61 b6 13 db c4 7d fa eb 4c a7 46 45 " + "6c 2e 88 83 85 52 5c ca 9d d1 c3 c7 a9 ad a7 6d " + "6c ";; + +char* signature_6 = + "9c ab 74 16 36 08 66 9f 75 55 a3 33 cf 19 6f e3" + "a0 e9 e5 eb 1a 32 d3 4b b5 c8 5f f6 89 aa ab 0e" + "3e 65 66 8e d3 b1 15 3f 94 eb 3d 8b e3 79 b8 ee" + "f0 07 c4 a0 2c 70 71 ce 30 d8 bb 34 1e 58 c6 20" + "f7 3d 37 b4 ec bf 48 be 29 4f 6c 9e 0e cb 5e 63" + "fe c4 1f 12 0e 55 53 df a0 eb eb bb 72 64 0a 95" + "37 ba dc b4 51 33 02 29 d9 f7 10 f6 2e 3e d8 ec" + "78 4e 50 ee 1d 92 62 b4 26 71 34 00 11 d7 d0 98" + "c6 f2 55 7b 21 31 fa 9b d0 25 46 36 59 7e 88 ec" + "b3 5a 24 0e f0 fd 85 95 71 24 df 80 80 fe e1 e1" + "49 af 93 99 89 e8 6b 26 c8 5a 58 81 fa e8 67 3d" + "9f d4 08 00 dd 13 4e b9 bd b6 41 0f 42 0b 0a a9" + "7b 20 ef cf 2e b0 c8 07 fa eb 83 a3 cc d9 b5 1d" + "45 53 e4 1d fc 0d f6 ca 80 a1 e8 1d c2 34 bb 83" + "89 dd 19 5a 38 b4 2d e4 ed c4 9d 34 64 78 b9 f1" + "1f 05 57 20 5f 5b 0b d7 ff e9 c8 50 f3 96 d7 c4";; + +// PKCS#1 v1.5 Signature Example 15.7 + +char* message_7 = + "ab fa 2e cb 7d 29 bd 5b cb 99 31 ce 2b ad 2f 74 " + "38 3e 95 68 3c ee 11 02 2f 08 e8 e7 d0 b8 fa 05 " + "8b f9 eb 7e b5 f9 88 68 b5 bb 1f b5 c3 1c ed a3 " + "a6 4f 1a 12 cd f2 0f cd 0e 5a 24 6d 7a 17 73 d8 " + "db a0 e3 b2 77 54 5b ab e5 8f 2b 96 e3 f4 ed c1 " + "8e ab f5 cd 2a 56 0f ca 75 fe 96 e0 7d 85 9d ef " + "b2 56 4f 3a 34 f1 6f 11 e9 1b 3a 71 7b 41 af 53 " + "f6 60 53 23 00 1a a4 06 c6 "; + +char* signature_7 = + "c4 b4 37 bc f7 03 f3 52 e1 fa f7 4e b9 62 20 39" + "42 6b 56 72 ca f2 a7 b3 81 c6 c4 f0 19 1e 7e 4a" + "98 f0 ee bc d6 f4 17 84 c2 53 7f f0 f9 9e 74 98" + "2c 87 20 1b fb c6 5e ae 83 2d b7 1d 16 da ca db" + "09 77 e5 c5 04 67 9e 40 be 0f 9d b0 6f fd 84 8d" + "d2 e5 c3 8a 7e c0 21 e7 f6 8c 47 df d3 8c c3 54" + "49 3d 53 39 b4 59 5a 5b f3 1e 3f 8f 13 81 68 07" + "37 3d f6 ad 0d c7 e7 31 e5 1a d1 9e b4 75 4b 13" + "44 85 84 2f e7 09 d3 78 44 4d 8e 36 b1 72 4a 4f" + "da 21 ca fe e6 53 ab 80 74 7f 79 52 ee 80 4d ea" + "b1 03 9d 84 13 99 45 bb f4 be 82 00 87 53 f3 c5" + "4c 78 21 a1 d2 41 f4 21 79 c7 94 ef 70 42 bb f9" + "95 56 56 22 2e 45 c3 43 69 a3 84 69 7b 6a e7 42" + "e1 8f a5 ca 7a ba d2 7d 9f e7 10 52 e3 31 0d 0f" + "52 c8 d1 2e a3 3b f0 53 a3 00 f4 af c4 f0 98 df" + "4e 6d 88 67 79 d6 45 94 d3 69 15 8f db c1 f6 94"; + +// PKCS#1 v1.5 Signature Example 15.8 + +char* message_8 = + "df 40 44 a8 9a 83 e9 fc bf 12 62 54 0a e3 03 8b " + "bc 90 f2 b2 62 8b f2 a4 46 7a c6 77 22 d8 54 6b " + "3a 71 cb 0e a4 16 69 d5 b4 d6 18 59 c1 b4 e4 7c " + "ec c5 93 3f 75 7e c8 6d b0 64 4e 31 18 12 d0 0f " + "b8 02 f0 34 00 63 9c 0e 36 4d ae 5a eb c5 79 1b " + "c6 55 76 23 61 bc 43 c5 3d 3c 78 86 76 8f 79 68 " + "c1 c5 44 c6 f7 9f 7b e8 20 c7 e2 bd 2f 9d 73 e6 " + "2d ed 6d 2e 93 7e 6a 6d ae f9 0e e3 7a 1a 52 a5 " + "4f 00 e3 1a dd d6 48 94 cf 4c 02 e1 60 99 e2 9f " + "9e b7 f1 a7 bb 7f 84 c4 7a 2b 59 48 13 be 02 a1 " + "7b 7f c4 3b 34 c2 2c 91 92 52 64 12 6c 89 f8 6b " + "b4 d8 7f 3e f1 31 29 6c 53 a3 08 e0 33 1d ac 8b " + "af 3b 63 42 22 66 ec ef 2b 90 78 15 35 db da 41 " + "cb d0 cf 22 a8 cb fb 53 2e c6 8f c6 af b2 ac 06 "; + +char* signature_8 = + "14 14 b3 85 67 ae 6d 97 3e de 4a 06 84 2d cc 0e" + "05 59 b1 9e 65 a4 88 9b db ab d0 fd 02 80 68 29" + "13 ba cd 5d c2 f0 1b 30 bb 19 eb 81 0b 7d 9d ed" + "32 b2 84 f1 47 bb e7 71 c9 30 c6 05 2a a7 34 13" + "90 a8 49 f8 1d a9 cd 11 e5 ec cf 24 6d ba e9 5f" + "a9 58 28 e9 ae 0c a3 55 03 25 32 6d ee f9 f4 95" + "30 ba 44 1b ed 4a c2 9c 02 9c 9a 27 36 b1 a4 19" + "0b 85 08 4a d1 50 42 6b 46 d7 f8 5b d7 02 f4 8d" + "ac 5f 71 33 0b c4 23 a7 66 c6 5c c1 dc ab 20 d3" + "d3 bb a7 2b 63 b3 ef 82 44 d4 2f 15 7c b7 e3 a8" + "ba 5c 05 27 2c 64 cc 1a d2 1a 13 49 3c 39 11 f6" + "0b 4e 9f 4e cc 99 00 eb 05 6e e5 9d 6f e4 b8 ff" + "6e 80 48 cc c0 f3 8f 28 36 fd 3d fe 91 bf 4a 38" + "6e 1e cc 2c 32 83 9f 0c a4 d1 b2 7a 56 8f a9 40" + "dd 64 ad 16 bd 01 25 d0 34 8e 38 30 85 f0 88 94" + "86 1c a1 89 87 22 7d 37 b4 2b 58 4a 83 57 cb 04"; + +// PKCS#1 v1.5 Signature Example 15.9 + +char* message_9 = + "ea 94 1f f0 6f 86 c2 26 92 7f cf 0e 3b 11 b0 87 " + "26 76 17 0c 1b fc 33 bd a8 e2 65 c7 77 71 f9 d0 " + "85 01 64 a5 ee cb cc 5c e8 27 fb fa 07 c8 52 14 " + "79 6d 81 27 e8 ca a8 18 94 ea 61 ce b1 44 9e 72 " + "fe a0 a4 c9 43 b2 da 6d 9b 10 5f e0 53 b9 03 9a " + "9c c5 3d 42 0b 75 39 fa b2 23 9c 6b 51 d1 7e 69 " + "4c 95 7d 4b 0f 09 84 46 18 79 a0 75 9c 44 01 be " + "ec d4 c6 06 a0 af bd 7a 07 6f 50 a2 df c2 80 7f " + "24 f1 91 9b aa 77 46 d3 a6 4e 26 8e d3 f5 f8 e6 " + "da 83 a2 a5 c9 15 2f 83 7c b0 78 12 bd 5b a7 d3 " + "a0 79 85 de 88 11 3c 17 96 e9 b4 66 ec 29 9c 5a " + "c1 05 9e 27 f0 94 15 "; + +char* signature_9 = + "ce eb 84 cc b4 e9 09 92 65 65 07 21 ee a0 e8 ec" + "89 ca 25 bd 35 4d 4f 64 56 49 67 be 9d 4b 08 b3" + "f1 c0 18 53 9c 9d 37 1c f8 96 1f 22 91 fb e0 dc" + "2f 2f 95 fe a4 7b 63 9f 1e 12 f4 bc 38 1c ef 0c" + "2b 7a 7b 95 c3 ad f2 76 05 b7 f6 39 98 c3 cb ad" + "54 28 08 c3 82 2e 06 4d 4a d1 40 93 67 9e 6e 01" + "41 8a 6d 5c 05 96 84 cd 56 e3 4e d6 5a b6 05 b8" + "de 4f cf a6 40 47 4a 54 a8 25 1b bb 73 26 a4 2d" + "08 58 5c fc fc 95 67 69 b1 5b 6d 7f df 7d a8 4f" + "81 97 6e aa 41 d6 92 38 0f f1 0e ae cf e0 a5 79" + "68 29 09 b5 52 1f ad e8 54 d7 97 b8 a0 34 5b 9a" + "86 4e 05 88 f6 ca dd bf 65 f1 77 99 8e 18 0d 1f" + "10 24 43 e6 dc a5 3a 94 82 3c aa 9c 3b 35 f3 22" + "58 3c 70 3a f6 74 76 15 9e c7 ec 93 d1 76 9b 30" + "0a f0 e7 15 7d c2 98 c6 cd 2d ee 22 62 f8 cd dc" + "10 f1 1e 01 74 14 71 bb fd 65 18 a1 75 73 45 75"; + +// PKCS#1 v1.5 Signature Example 15.10 + +char* message_10 = + "d8 b8 16 45 c1 3c d7 ec f5 d0 0e d2 c9 1b 9a cd " + "46 c1 55 68 e5 30 3c 4a 97 75 ed e7 6b 48 40 3d " + "6b e5 6c 05 b6 b1 cf 77 c6 e7 5d e0 96 c5 cb 35 " + "51 cb 6f a9 64 f3 c8 79 cf 58 9d 28 e1 da 2f 9d " + "ec "; + +char* signature_10 = + "27 45 07 4c a9 71 75 d9 92 e2 b4 47 91 c3 23 c5" + "71 67 16 5c dd 8d a5 79 cd ef 46 86 b9 bb 40 4b" + "d3 6a 56 50 4e b1 fd 77 0f 60 bf a1 88 a7 b2 4b" + "0c 91 e8 81 c2 4e 35 b0 4d c4 dd 4c e3 85 66 bc" + "c9 ce 54 f4 9a 17 5f c9 d0 b2 25 22 d9 57 90 47" + "f9 ed 42 ec a8 3f 76 4a 10 16 39 97 94 7e 7d 2b" + "52 ff 08 98 0e 7e 7c 22 57 93 7b 23 f3 d2 79 d4" + "cd 17 d6 f4 95 54 63 73 d9 83 d5 36 ef d7 d1 b6" + "71 81 ca 2c b5 0a c6 16 c5 c7 ab fb b9 26 0b 91" + "b1 a3 8e 47 24 20 01 ff 45 2f 8d e1 0c a6 ea ea" + "dc af 9e dc 28 95 6f 28 a7 11 29 1f c9 a8 08 78" + "b8 ba 4c fe 25 b8 28 1c b8 0b c9 cd 6d 2b d1 82" + "52 46 ee be 25 2d 99 57 ef 93 70 73 52 08 4e 6d" + "36 d4 23 55 1b f2 66 a8 53 40 fb 4a 6a f3 70 88" + "0a ab 07 15 3d 01 f4 8d 08 6d f0 bf be c0 5e 7b" + "44 3b 97 e7 17 18 97 0e 2f 4b f6 20 23 e9 5b 67"; + +// PKCS#1 v1.5 Signature Example 15.11 + +char* message_11 = + "e5 73 9b 6c 14 c9 2d 51 0d 95 b8 26 93 33 37 ff " + "0d 24 ef 72 1a c4 ef 64 c2 ba d2 64 be 8b 44 ef " + "a1 51 6e 08 a2 7e b6 b6 11 d3 30 1d f0 06 2d ae " + "fc 73 a8 c0 d9 2e 2c 52 1f ac bc 7b 26 47 38 76 " + "7e a6 fc 97 d5 88 a0 ba f6 ce 50 ad f7 9e 60 0b " + "d2 9e 34 5f cb 1d ba 71 ac 5c 02 89 02 3f e4 a8 " + "2b 46 a5 40 77 19 19 7d 2e 95 8e 35 31 fd 54 ae " + "f9 03 aa bb 43 55 f8 83 18 99 4e d3 c3 dd 62 f4 " + "20 a7 "; + +char* signature_11 = + "be 40 a5 fb 94 f1 13 e1 b3 ef f6 b6 a3 39 86 f2" + "02 e3 63 f0 74 83 b7 92 e6 8d fa 55 54 df 04 66" + "cc 32 15 09 50 78 3b 4d 96 8b 63 9a 04 fd 2f b9" + "7f 6e b9 67 02 1f 5a dc cb 9f ca 95 ac c8 f2 cd" + "88 5a 38 0b 0a 4e 82 bc 76 07 64 db ab 88 c1 e6" + "c0 25 5c aa 94 f2 32 19 9d 6f 59 7c c9 14 5b 00" + "e3 d4 ba 34 6b 55 9a 88 33 ad 15 16 ad 51 63 f0" + "16 af 6a 59 83 1c 82 ea 13 c8 22 4d 84 d0 76 5a" + "9d 12 38 4d a4 60 a8 53 1b 4c 40 7e 04 f4 f3 50" + "70 9e b9 f0 8f 5b 22 0f fb 45 ab f6 b7 5d 15 79" + "fd 3f 1e b5 5f c7 5b 00 af 8b a3 b0 87 82 7f e9" + "ae 9f b4 f6 c5 fa 63 03 1f e5 82 85 2f e2 83 4f" + "9c 89 bf f5 3e 25 52 21 6b c7 c1 d4 a3 d5 dc 2b" + "a6 95 5c d9 b1 7d 13 63 e7 fe e8 ed 76 29 75 3f" + "f3 12 5e dd 48 52 1a e3 b9 b0 32 17 f4 49 6d 0d" + "8e de 57 ac bc 5b d4 de ae 74 a5 6f 86 67 1d e2"; + +// PKCS#1 v1.5 Signature Example 15.12 + +char* message_12 = + "7a f4 28 35 91 7a 88 d6 b3 c6 71 6b a2 f5 b0 d5 " + "b2 0b d4 e2 e6 e5 74 e0 6a f1 ee f7 c8 11 31 be " + "22 bf 81 28 b9 cb c6 ec 00 27 5b a8 02 94 a5 d1 " + "17 2d 08 24 a7 9e 8f dd 83 01 83 e4 c0 0b 96 78 " + "28 67 b1 22 7f ea 24 9a ad 32 ff c5 fe 00 7b c5 " + "1f 21 79 2f 72 8d ed a8 b5 70 8a a9 9c ab ab 20 " + "a4 aa 78 3e d8 6f 0f 27 b5 d5 63 f4 2e 07 15 8c " + "ea 72 d0 97 aa 68 87 ec 41 1d d0 12 91 2a 5e 03 " + "2b bf a6 78 50 71 44 bc c9 5f 39 b5 8b e7 bf d1 " + "75 9a db 9a 91 fa 1d 6d 82 26 a8 34 3a 8b 84 9d " + "ae 76 f7 b9 82 24 d5 9e 28 f7 81 f1 3e ce 60 5f " + "84 f6 c9 0b ae 5f 8c f3 78 81 6f 40 20 a7 dd a1 " + "be d9 0c 92 a2 36 34 d2 03 fa c3 fc d8 6d 68 d3 " + "18 2a 7d 9c ca be 7b 07 95 f5 c6 55 e9 ac c4 e3 " + "ec 18 51 40 d1 0c ef 05 34 64 ab 17 5c 83 bd 83 " + "93 5e 3d ab af 34 62 ee be 63 d1 5f 57 3d 26 9a "; + +char* signature_12 = + "4e 78 c5 90 2b 80 79 14 d1 2f a5 37 ae 68 71 c8" + "6d b8 02 1e 55 d1 ad b8 eb 0c cf 1b 8f 36 ab 7d" + "ad 1f 68 2e 94 7a 62 70 72 f0 3e 62 73 71 78 1d" + "33 22 1d 17 4a be 46 0d bd 88 56 0c 22 f6 90 11" + "6e 2f bb e6 e9 64 36 3a 3e 52 83 bb 5d 94 6e f1" + "c0 04 7e ba 03 8c 75 6c 40 be 79 23 05 58 09 b0" + "e9 f3 4a 03 a5 88 15 eb dd e7 67 93 1f 01 8f 6f" + "18 78 f2 ef 4f 47 dd 37 40 51 dd 48 68 5d ed 6e" + "fb 3e a8 02 1f 44 be 1d 7d 14 93 98 f9 8e a9 c0" + "8d 62 88 8e bb 56 19 2d 17 74 7b 6b 8e 17 09 54" + "31 f1 25 a8 a8 e9 96 2a a3 1c 28 52 64 e0 8f b2" + "1a ac 33 6c e6 c3 8a a3 75 e4 2b c9 2a b0 ab 91" + "03 84 31 e1 f9 2c 39 d2 af 5d ed 7e 43 bc 15 1e" + "6e be a4 c3 e2 58 3a f3 43 7e 82 c4 3c 5e 3b 5b" + "07 cf 03 59 68 3d 22 98 e3 59 48 ed 80 6c 06 3c" + "60 6e a1 78 15 0b 1e fc 15 85 69 34 c7 25 5c fe"; + +// PKCS#1 v1.5 Signature Example 15.13 + +char* message_13 = + "eb ae f3 f9 f2 3b df e5 fa 6b 8a f4 c2 08 c1 89 " + "f2 25 1b f3 2f 5f 13 7b 9d e4 40 63 78 68 6b 3f " + "07 21 f6 2d 24 cb 86 88 d6 fc 41 a2 7c ba e2 1d " + "30 e4 29 fe ac c7 11 19 41 c2 77 "; + +char* signature_13 = + "c4 8d be f5 07 11 4f 03 c9 5f af be b4 df 1b fa" + "88 e0 18 4a 33 cc 4f 8a 9a 10 35 ff 7f 82 2a 5e" + "38 cd a1 87 23 91 5f f0 78 24 44 29 e0 f6 08 1c" + "14 fd 83 33 1f a6 5c 6b a7 bb 9a 12 db f6 62 23" + "74 cd 0c a5 7d e3 77 4e 2b d7 ae 82 36 77 d0 61" + "d5 3a e9 c4 04 0d 2d a7 ef 70 14 f3 bb dc 95 a3" + "61 a4 38 55 c8 ce 9b 97 ec ab ce 17 4d 92 62 85" + "14 2b 53 4a 30 87 f9 f4 ef 74 51 1e c7 42 b0 d5" + "68 56 03 fa f4 03 b5 07 2b 98 5d f4 6a df 2d 25" + "29 a0 2d 40 71 1e 21 90 91 70 52 37 1b 79 b7 49" + "b8 3a bf 0a e2 94 86 c3 f2 f6 24 77 b2 bd 36 2b" + "03 9c 01 3c 0c 50 76 ef 52 0d bb 40 5f 42 ce e9" + "54 25 c3 73 a9 75 e1 cd d0 32 c4 96 22 c8 50 79" + "b0 9e 88 da b2 b1 39 69 ef 7a 72 39 73 78 10 40" + "45 9f 57 d5 01 36 38 48 3d e2 d9 1c b3 c4 90 da" + "81 c4 6d e6 cd 76 ea 8a 0c 8f 6f e3 31 71 2d 24"; + +// PKCS#1 v1.5 Signature Example 15.14 + +char* message_14 = + "c5 a2 71 12 78 76 1d fc dd 4f 0c 99 e6 f5 61 9d " + "6c 48 b5 d4 c1 a8 09 82 fa a6 b4 cf 1c f7 a6 0f " + "f3 27 ab ef 93 c8 01 42 9e fd e0 86 40 85 81 46 " + "10 56 ac c3 3f 3d 04 f5 ad a2 12 16 ca cd 5f d1 " + "f9 ed 83 20 3e 0e 2f e6 13 8e 3e ae 84 24 e5 91 " + "5a 08 3f 3f 7a b7 60 52 c8 be 55 ae 88 2d 6e c1 " + "48 2b 1e 45 c5 da e9 f4 10 15 40 53 27 02 2e c3 " + "2f 0e a2 42 97 63 b2 55 04 3b 19 58 ee 3c f6 d6 " + "39 83 59 6e b3 85 84 4f 85 28 cc 9a 98 65 83 5d " + "c5 11 3c 02 b8 0d 0f ca 68 aa 25 e7 2b ca ae b3 " + "cf 9d 79 d8 4f 98 4f d4 17 "; + +char* signature_14 = + "6b d5 25 7a a0 66 11 fb 46 60 08 7c b4 bc 4a 9e" + "44 91 59 d3 16 52 bd 98 08 44 da f3 b1 c7 b3 53" + "f8 e5 61 42 f7 ea 98 57 43 3b 18 57 3b 4d ee de" + "81 8a 93 b0 29 02 97 78 3f 1a 2f 23 cb c7 27 97" + "a6 72 53 7f 01 f6 24 84 cd 41 62 c3 21 4b 9a c6" + "28 22 4c 5d e0 1f 32 bb 9b 76 b2 73 54 f2 b1 51" + "d0 e8 c4 21 3e 46 15 ad 0b c7 1f 51 5e 30 0d 6a" + "64 c6 74 34 11 ff fd e8 e5 ff 19 0e 54 92 30 43" + "12 6e cf c4 c4 53 90 22 66 8f b6 75 f2 5c 07 e2" + "00 99 ee 31 5b 98 d6 af ec 4b 1a 9a 93 dc 33 49" + "6a 15 bd 6f de 16 63 a7 d4 9b 9f 1e 63 9d 38 66" + "4b 37 a0 10 b1 f3 5e 65 86 82 d9 cd 63 e5 7d e0" + "f1 5e 8b dd 09 65 58 f0 7e c0 ca a2 18 a8 c0 6f" + "47 88 45 39 40 28 7c 9d 34 b6 d4 0a 3f 09 bf 77" + "99 fe 98 ae 4e b4 9f 3f f4 1c 50 40 a5 0c ef c9" + "bd f2 39 4b 74 9c f1 64 48 0d f1 ab 68 80 27 3b"; + +// PKCS#1 v1.5 Signature Example 15.15 + +char* message_15 = + "9b f8 aa 25 3b 87 2e a7 7a 7e 23 47 6b e2 6b 23 " + "29 57 8c f6 ac 9e a2 80 5b 35 7f 6f c3 ad 13 0d " + "ba eb 3d 86 9a 13 cc e7 a8 08 bb bb c9 69 85 7e " + "03 94 5c 7b b6 1d f1 b5 c2 58 9b 8e 04 6c 2a 5d " + "7e 40 57 b1 a7 4f 24 c7 11 21 63 64 28 85 29 ec " + "95 70 f2 51 97 21 3b e1 f5 c2 e5 96 f8 bf 8b 2c " + "f3 cb 38 aa 56 ff e5 e3 1d f7 39 58 20 e9 4e cf " + "3b 11 89 a9 65 dc f9 a9 cb 42 98 d3 c8 8b 29 23 " + "c1 9f c6 bc 34 aa ce ca d4 e0 93 1a 7c 4e 5d 73 " + "dc 86 df a7 98 a8 47 6d 82 46 3e ef aa 90 a8 a9 " + "19 2a b0 8b 23 08 8d d5 8e 12 80 f7 d7 2e 45 48 " + "39 6b aa c1 12 25 2d d5 c5 34 6a db 20 04 a2 f7 " + "10 1c cc 89 9c c7 fa fa e8 bb e2 95 73 88 96 a5 " + "b2 01 22 85 01 4e f6 "; + +char* signature_15 = + "27 f7 f4 da 9b d6 10 10 6e f5 7d 32 38 3a 44 8a" + "8a 62 45 c8 3d c1 30 9c 6d 77 0d 35 7b a8 9e 73" + "f2 ad 08 32 06 2e b0 fe 0a c9 15 57 5b cd 6b 8b" + "ca db 4e 2b a6 fa 9d a7 3a 59 17 51 52 b2 d4 fe" + "72 b0 70 c9 b7 37 9e 50 00 0e 55 e6 c2 69 f6 65" + "8c 93 79 72 79 7d 3a dd 69 f1 30 e3 4b 85 bd ec" + "9f 3a 9b 39 22 02 d6 f3 e4 30 d0 9c ac a8 22 77" + "59 ab 82 5f 70 12 d2 ff 4b 5b 62 c8 50 4d ba d8" + "55 c0 5e dd 5c ab 5a 4c cc dc 67 f0 1d d6 51 7c" + "7d 41 c4 3e 2a 49 57 af f1 9d b6 f1 8b 17 85 9a" + "f0 bc 84 ab 67 14 6e c1 a4 a6 0a 17 d7 e0 5f 8b" + "4f 9c ed 6a d1 09 08 d8 d7 8f 7f c8 8b 76 ad c8" + "29 0f 87 da f2 a7 be 10 ae 40 85 21 39 5d 54 ed" + "25 56 fb 76 61 85 4a 73 0c e3 d8 2c 71 a8 d4 93" + "ec 49 a3 78 ac 8a 3c 74 43 9f 7c c5 55 ba 13 f8" + "59 07 08 90 ee 18 ff 65 8f a4 d7 41 96 9d 70 a5"; + +// PKCS#1 v1.5 Signature Example 15.16 + +char* message_16 = + "32 47 48 30 e2 20 37 54 c8 bf 06 81 dc 4f 84 2a " + "fe 36 09 30 37 86 16 c1 08 e8 33 65 6e 56 40 c8 " + "68 56 88 5b b0 5d 1e b9 43 8e fe de 67 92 63 de " + "07 cb 39 55 3f 6a 25 e0 06 b0 a5 23 11 a0 63 ca " + "08 82 66 d2 56 4f f6 49 0c 46 b5 60 98 18 54 8f " + "88 76 4d ad 34 a2 5e 3a 85 d5 75 02 3f 0b 9e 66 " + "50 48 a0 3c 35 05 79 a9 d3 24 46 c7 bb 96 cc 92 " + "e0 65 ab 94 d3 c8 95 2e 8d f6 8e f0 d9 fa 45 6b " + "3a 06 bb 80 e3 bb c4 b2 8e 6a 94 b6 d0 ff 76 96 " + "a6 4e fe 05 e7 35 fe a0 25 d7 bd bc 41 39 f3 a3 " + "b5 46 07 5c ba 7e fa 94 73 74 d3 f0 ac 80 a6 8d " + "76 5f 5d f6 21 0b ca 06 9a 2d 88 64 7a f7 ea 04 " + "2d ac 69 0c b5 73 78 ec 07 77 61 4f b8 b6 5f f4 " + "53 ca 6b 7d ce 60 98 45 1a 2f 8c 0d a9 bf ec f1 " + "fd f3 91 bb aa 4e 2a 91 ca 18 a1 12 1a 75 23 a2 " + "ab d4 25 14 f4 89 e8 "; + +char* signature_16 = + "69 17 43 72 57 c2 2c cb 54 03 29 0c 3d ee 82 d9" + "cf 75 50 b3 1b d3 1c 51 bd 57 bf d3 5d 45 2a b4" + "db 7c 4b e6 b2 e2 5a c9 a5 9a 1d 2a 7f eb 62 7f" + "0a fd 49 76 b3 00 3c c9 cf fd 88 96 50 5e c3 82" + "f2 65 10 4d 4c f8 c9 32 fa 9f e8 6e 00 87 07 95" + "99 12 38 9d a4 b2 d6 b3 69 b3 6a 5e 72 e2 9d 24" + "c9 a9 8c 9d 31 a3 ab 44 e6 43 e6 94 12 66 a4 7a" + "45 e3 44 6c e8 77 6a be 24 1a 8f 5f c6 42 3b 24" + "b1 ff 25 0d c2 c3 a8 17 23 53 56 10 77 e8 50 a7" + "69 b2 5f 03 25 da c8 89 65 a3 b9 b4 72 c4 94 e9" + "5f 71 9b 4e ac 33 2c aa 7a 65 c7 df e4 6d 9a a7" + "e6 e0 0f 52 5f 30 3d d6 3a b7 91 92 18 90 18 68" + "f9 33 7f 8c d2 6a af e6 f3 3b 7f b2 c9 88 10 af" + "19 f7 fc b2 82 ba 15 77 91 2c 1d 36 89 75 fd 5d" + "44 0b 86 e1 0c 19 97 15 fa 0b 6f 42 50 b5 33 73" + "2d 0b ef e1 54 51 50 fc 47 b8 76 de 09 b0 0a 94"; + +// PKCS#1 v1.5 Signature Example 15.17 + +char* message_17 = + "00 8e 59 50 5e af b5 50 aa e5 e8 45 58 4c eb b0 " + "0b 6d e1 73 3e 9f 95 d4 2c 88 2a 5b be b5 ce 1c " + "57 e1 19 e7 c0 d4 da ca 9f 1f f7 87 02 17 f7 cf " + "d8 a6 b3 73 97 7c ac 9c ab 8e 71 e4 20 "; + +char* signature_17 = + "92 25 03 b6 73 ee 5f 3e 69 1e 1c a8 5e 9f f4 17" + "3c f7 2b 05 ac 2c 13 1d a5 60 35 93 e3 bc 25 9c" + "94 c1 f7 d3 a0 6a 5b 98 91 bf 11 3f a3 9e 59 ff" + "7c 1e d6 46 5e 90 80 49 cb 89 e4 e1 25 cd 37 d2" + "ff d9 22 7a 41 b4 a0 a1 9c 0a 44 fb bf 3d e5 5b" + "ab 80 20 87 a3 bb 8d 4f f6 68 ee 6b bb 8a d8 9e" + "68 57 a7 9a 9c 72 78 19 90 df cf 92 cd 51 94 04" + "c9 50 f1 3d 11 43 c3 18 4f 1d 25 0c 90 e1 7a c6" + "ce 36 16 3b 98 95 62 7a d6 ff ec 14 22 44 1f 55" + "e4 49 9d ba 9b e8 95 46 ae 8b c6 3c ca 01 dd 08" + "46 3a e7 f1 fc e3 d8 93 99 69 38 77 8c 18 12 e6" + "74 ad 9c 30 9c 5a cc a3 fd e4 4e 7d d8 69 59 93" + "e9 c1 fa 87 ac da 99 ec e5 c8 49 9e 46 89 57 ad" + "66 35 9b f1 2a 51 ad be 78 d3 a2 13 b4 49 bf 0b" + "5f 8d 4d 49 6a cf 03 d3 03 3b 7c cd 19 6b c2 2f" + "68 fb 7b ef 4f 69 7c 5e a2 b3 50 62 f4 8a 36 dd"; + +// PKCS#1 v1.5 Signature Example 15.18 + +char* message_18 = + "6a bc 54 cf 8d 1d ff 1f 53 b1 7d 81 60 36 88 78 " + "a8 78 8c c6 d2 2f a5 c2 25 8c 88 e6 60 b0 9a 89 " + "33 f9 f2 c0 50 4d da dc 21 f6 e7 5e 0b 83 3b eb " + "55 52 29 de e6 56 b9 04 7b 92 f6 2e 76 b8 ff cc " + "60 da b0 6b 80 "; + +char* signature_18 = + "0b 6d af 42 f7 a8 62 14 7e 41 74 93 c2 c4 01 ef" + "ae 32 63 6a b4 cb d4 41 92 bb f5 f1 95 b5 0a e0" + "96 a4 75 a1 61 4f 0a 9f a8 f7 a0 26 cb 46 c6 50" + "6e 51 8e 33 d8 3e 56 47 7a 87 5a ca 8c 7e 71 4c" + "e1 bd bd 61 ef 5d 53 52 39 b3 3f 2b fd d6 17 71" + "ba b6 27 76 d7 81 71 a1 42 3c ea 87 31 f8 2e 60" + "76 6d 64 54 26 56 20 b1 5f 5c 5a 58 4f 55 f9 5b" + "80 2f e7 8c 57 4e d5 da cf c8 31 f3 cf 2b 05 02" + "c0 b2 98 f2 5c cf 11 f9 73 b3 1f 85 e4 74 42 19" + "85 f3 cf f7 02 df 39 46 ef 0a 66 05 68 21 11 b2" + "f5 5b 1f 8a b0 d2 ea 3a 68 3c 69 98 5e ad 93 ed" + "44 9e a4 8f 03 58 dd f7 08 02 cb 41 de 2f d8 3f" + "3c 80 80 82 d8 49 36 94 8e 0c 84 a1 31 b4 92 78" + "27 46 05 27 bb 5c d2 4b fa b7 b4 8e 07 1b 24 17" + "19 30 f9 97 63 27 2f 97 97 bc b7 6f 1d 24 81 57" + "55 58 fc f2 60 b1 f0 e5 54 eb b3 df 3c fc b9 58"; + +// PKCS#1 v1.5 Signature Example 15.19 + +char* message_19 = + "af 2d 78 15 2c f1 0e fe 01 d2 74 f2 17 b1 77 f6 " + "b0 1b 5e 74 9f 15 67 71 5d a3 24 85 9c d3 dd 88 " + "db 84 8e c7 9f 48 db ba 7b 6f 1d 33 11 1e f3 1b " + "64 89 9e 73 91 c2 bf fd 69 f4 90 25 cf 20 1f c5 " + "85 db d1 54 2c 1c 77 8a 2c e7 a7 ee 10 8a 30 9f " + "ec a2 6d 13 3a 5f fe dc 4e 86 9d cd 76 56 59 6a " + "c8 42 7e a3 ef 6e 3f d7 8f e9 9d 8d dc 71 d8 39 " + "f6 78 6e 0d a6 e7 86 bd 62 b3 a4 f1 9b 89 1a 56 " + "15 7a 55 4e c2 a2 b3 9e 25 a1 d7 c7 d3 73 21 c7 " + "a1 d9 46 cf 4f be 75 8d 92 76 f0 85 63 44 9d 67 " + "41 4a 2c 03 0f 42 51 cf e2 21 3d 04 a5 41 06 37 " + "87 "; + +char* signature_19 = + "20 9c 61 15 78 57 38 7b 71 e2 4b f3 dd 56 41 45" + "50 50 3b ec 18 0f f5 3b dd 9b ac 06 2a 2d 49 95" + "09 bf 99 12 81 b7 95 27 df 91 36 61 5b 7a 6d 9d" + "b3 a1 03 b5 35 e0 20 2a 2c ac a1 97 a7 b7 4e 53" + "56 f3 dd 59 5b 49 ac fd 9d 30 04 9a 98 ca 88 f6" + "25 bc a1 d5 f2 2a 39 2d 8a 74 9e fb 6e ed 9b 78" + "21 d3 11 0a c0 d2 44 19 9e cb 4a a3 d7 35 a8 3a" + "2e 88 93 c6 bf 85 81 38 3c ca ee 83 46 35 b7 fa" + "1f af fa 45 b1 3d 15 c1 da 33 af 71 e8 93 03 d6" + "80 90 ff 62 ee 61 5f df 5a 84 d1 20 71 1d a5 3c" + "28 89 19 8a b3 83 17 a9 73 4a b2 7d 67 92 4c ea" + "74 15 6f f9 9b ef 98 76 bb 5c 33 9e 93 74 52 83" + "e1 b3 4e 07 22 26 b8 80 45 e0 17 e9 f0 5b 2a 8c" + "41 67 40 25 8e 22 3b 26 90 02 74 91 73 22 73 f3" + "22 9d 9e f2 b1 b3 80 7e 32 10 18 92 0a d3 e5 3d" + "ae 47 e6 d9 39 5c 18 4b 93 a3 74 c6 71 fa a2 ce"; + +// PKCS#1 v1.5 Signature Example 15.20 + +char* message_20 = + "40 ee 99 24 58 d6 f6 14 86 d2 56 76 a9 6d d2 cb " + "93 a3 7f 04 b1 78 48 2f 2b 18 6c f8 82 15 27 0d " + "ba 29 d7 86 d7 74 b0 c5 e7 8c 7f 6e 56 a9 56 e7 " + "f7 39 50 a2 b0 c0 c1 0a 08 db cd 67 e5 b2 10 bb " + "21 c5 8e 27 67 d4 4f 7d d4 01 4e 39 66 14 3b f7 " + "e3 d6 6f f0 c0 9b e4 c5 5f 93 b3 99 94 b8 51 8d " + "9c 1d 76 d5 b4 73 74 de a0 8f 15 7d 57 d7 06 34 " + "97 8f 38 56 e0 e5 b4 81 af bb db 5a 3a c4 8d 48 " + "4b e9 2c 93 de 22 91 78 35 4c 2d e5 26 e9 c6 5a " + "31 ed e1 ef 68 cb 63 98 d7 91 16 84 fe c0 ba bc " + "3a 78 1a 66 66 07 83 50 69 74 d0 e1 48 25 10 1c " + "3b fa ea "; + +char* signature_20 = + "92 75 02 b8 24 af c4 25 13 ca 65 70 de 33 8b 8a" + "64 c3 a8 5e b8 28 d3 19 36 24 f2 7e 8b 10 29 c5" + "5c 11 9c 97 33 b1 8f 58 49 b3 50 09 18 bc c0 05" + "51 d9 a8 fd f5 3a 97 74 9f a8 dc 48 0d 6f e9 74" + "2a 58 71 f9 73 92 65 28 97 2a 1a f4 9e 39 25 b0" + "ad f1 4a 84 27 19 b4 a5 a2 d8 9f a9 c0 b6 60 5d" + "21 2b ed 1e 67 23 b9 34 06 ad 30 e8 68 29 a5 c7" + "19 b8 90 b3 89 30 6d c5 50 64 86 ee 2f 36 a8 df" + "e0 a9 6a f6 78 c9 cb d6 af f3 97 ca 20 0e 3e dc" + "1e 36 bd 2f 08 b3 1d 54 0c 0c b2 82 a9 55 9e 4a" + "dd 4f c9 e6 49 2e ed 0c cb d3 a6 98 2e 5f aa 2d" + "dd 17 be 47 41 7c 80 b4 e5 45 2d 31 f7 24 01 a0" + "42 32 51 09 54 4d 95 4c 01 93 90 79 d4 09 a5 c3" + "78 d7 51 2d fc 2d 2a 71 ef cc 34 32 a7 65 d1 c6" + "a5 2c fc e8 99 cd 79 b1 5b 4f c3 72 36 41 ef 6b" + "d0 0a cc 10 40 7e 5d f5 8d d1 c3 c5 c5 59 a5 06"; + + +unsigned char* parsehex(char* str, int* len) { + // result can't be longer than input + unsigned char* result = malloc(strlen(str)); + + unsigned char* p = result; + *len = 0; + + while (*str) { + int b; + + while (isspace(*str)) str++; + + switch (*str) { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + b = (*str - '0') << 4; break; + case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': + b = (*str - 'a' + 10) << 4; break; + case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': + b = (*str - 'A' + 10) << 4; break; + case '\0': + return result; + default: + return NULL; + } + str++; + + while (isspace(*str)) str++; + + switch (*str) { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + b |= *str - '0'; break; + case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': + b |= *str - 'a' + 10; break; + case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': + b |= *str - 'A' + 10; break; + default: + return NULL; + } + str++; + + *p++ = b; + ++*len; + } + + return result; +} + + +int main(int arg, char** argv) { + + unsigned char hash[SHA_DIGEST_SIZE]; + + unsigned char* message; + int mlen; + unsigned char* signature; + int slen; + +#define TEST_MESSAGE(n) do {\ + message = parsehex(message_##n, &mlen); \ + SHA_hash(message, mlen, hash); \ + signature = parsehex(signature_##n, &slen); \ + int result = RSA_verify(&key_15, signature, slen, hash, sizeof(hash)); \ + printf("message %d: %s\n", n, result ? "verified" : "not verified"); \ + success = success && result; \ + } while(0) + + int success = 1; + + TEST_MESSAGE(1); + TEST_MESSAGE(2); + TEST_MESSAGE(3); + TEST_MESSAGE(4); + TEST_MESSAGE(5); + TEST_MESSAGE(6); + TEST_MESSAGE(7); + TEST_MESSAGE(8); + TEST_MESSAGE(9); + TEST_MESSAGE(10); + TEST_MESSAGE(11); + TEST_MESSAGE(12); + TEST_MESSAGE(13); + TEST_MESSAGE(14); + TEST_MESSAGE(15); + TEST_MESSAGE(16); + TEST_MESSAGE(17); + TEST_MESSAGE(18); + TEST_MESSAGE(19); + TEST_MESSAGE(20); + + printf("\n%s\n\n", success ? "PASS" : "FAIL"); + + return !success; +} From 47677a506febfe386d186f8f854d967f165a342f Mon Sep 17 00:00:00 2001 From: Geremy Condra Date: Wed, 10 Apr 2013 17:51:53 -0700 Subject: [PATCH 010/541] Add logic to fixup file contexts after a policy update. Bug: 8116902 Change-Id: Ifa1785c75a24a9b3cfcb700f22ded7d16a917f79 --- init/init.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/init/init.c b/init/init.c index f8b21e64a..3e67192b2 100755 --- a/init/init.c +++ b/init/init.c @@ -745,7 +745,7 @@ static int bootchart_init_action(int nargs, char **args) #endif static const struct selinux_opt seopts_prop[] = { - { SELABEL_OPT_PATH, "/data/security/property_contexts" }, + { SELABEL_OPT_PATH, "/data/security/current/property_contexts" }, { SELABEL_OPT_PATH, "/property_contexts" }, { 0, NULL } }; @@ -793,6 +793,11 @@ int selinux_reload_policy(void) selabel_close(sehandle_prop); selinux_init_all_handles(); + + selinux_android_fixcon("/data"); + selinux_android_fixcon("/system"); + selinux_android_fixcon("/dev"); + return 0; } From 451a6ba829ddfdf2c1d8605611342397340534df Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Fri, 19 Apr 2013 12:48:19 -0700 Subject: [PATCH 011/541] klog: error handling fixups Change-Id: I9014e62010c589ec30d5e99eef420353e35418b5 --- libcutils/klog.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libcutils/klog.c b/libcutils/klog.c index 812af3bbf..d69fb1005 100644 --- a/libcutils/klog.c +++ b/libcutils/klog.c @@ -40,6 +40,8 @@ void klog_init(void) if (mknod(name, S_IFCHR | 0600, (1 << 8) | 11) == 0) { klog_fd = open(name, O_WRONLY); + if (klog_fd < 0) + return; fcntl(klog_fd, F_SETFD, FD_CLOEXEC); unlink(name); } @@ -54,6 +56,7 @@ void klog_write(int level, const char *fmt, ...) if (level > klog_level) return; if (klog_fd < 0) klog_init(); + if (klog_fd < 0) return; va_start(ap, fmt); vsnprintf(buf, LOG_BUF_MAX, fmt, ap); From ca8e66a8b0f843812014a28d49208f9f6f64ecbc Mon Sep 17 00:00:00 2001 From: Nick Kralevich Date: Thu, 18 Apr 2013 12:20:02 -0700 Subject: [PATCH 012/541] Make init handle reboots Move the responsibility for rebooting the system from the reboot command to init. Init is in a better position to take actions to bring the system down cleanly, including making sure filesystems are mounted read-only. The only UIDs which can perform an init triggered reboot are root, system, and shell. Modify the reboot command so that it calls into init to perform the reboot. The reboot command no longer requires CAP_SYS_BOOT. Remove the -n reboot option and code which supports it. Anyone needing to do an unclean shutdown can just do a 'echo c > /proc/sysrq-trigger'. Modify adb so that it calls into init to perform a shutdown. Bug: 8646621 Change-Id: I84c0513acb549720cb0e8c9fcbda0050f5c396f5 --- adb/adb.c | 20 +---------- adb/services.c | 13 +++++-- include/cutils/android_reboot.h | 5 ++- include/private/android_filesystem_config.h | 1 - init/builtins.c | 38 +++++++++++++++++++++ init/init_parser.c | 2 ++ init/keywords.h | 2 ++ init/property_service.c | 1 + libcutils/android_reboot.c | 7 ++-- reboot/reboot.c | 37 ++++++++++---------- rootdir/init.rc | 3 ++ 11 files changed, 80 insertions(+), 49 deletions(-) mode change 100755 => 100644 init/property_service.c diff --git a/adb/adb.c b/adb/adb.c index 9d2b86ef9..6580c6e95 100644 --- a/adb/adb.c +++ b/adb/adb.c @@ -1199,9 +1199,8 @@ static void drop_capabilities_bounding_set_if_needed() { #endif int i; for (i = 0; prctl(PR_CAPBSET_READ, i, 0, 0, 0) >= 0; i++) { - if (i == CAP_SETUID || i == CAP_SETGID || i == CAP_SYS_BOOT) { + if (i == CAP_SETUID || i == CAP_SETGID) { // CAP_SETUID CAP_SETGID needed by /system/bin/run-as - // CAP_SYS_BOOT needed by /system/bin/reboot continue; } int err = prctl(PR_CAPBSET_DROP, i, 0, 0, 0); @@ -1302,13 +1301,6 @@ int adb_main(int is_daemon, int server_port) /* don't listen on a port (default 5037) if running in secure mode */ /* don't run as root if we are running in secure mode */ if (should_drop_privileges()) { - struct __user_cap_header_struct header; - struct __user_cap_data_struct cap[2]; - - if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) != 0) { - exit(1); - } - drop_capabilities_bounding_set_if_needed(); /* add extra groups: @@ -1338,16 +1330,6 @@ int adb_main(int is_daemon, int server_port) exit(1); } - memset(&header, 0, sizeof(header)); - memset(cap, 0, sizeof(cap)); - - /* set CAP_SYS_BOOT capability, so "adb reboot" will succeed */ - header.version = _LINUX_CAPABILITY_VERSION_3; - header.pid = 0; - cap[CAP_TO_INDEX(CAP_SYS_BOOT)].effective |= CAP_TO_MASK(CAP_SYS_BOOT); - cap[CAP_TO_INDEX(CAP_SYS_BOOT)].permitted |= CAP_TO_MASK(CAP_SYS_BOOT); - capset(&header, cap); - D("Local port disabled\n"); } else { char local_name[30]; diff --git a/adb/services.c b/adb/services.c index 54d21a8b7..e82a0ea5a 100644 --- a/adb/services.c +++ b/adb/services.c @@ -165,6 +165,7 @@ void restart_usb_service(int fd, void *cookie) void reboot_service(int fd, void *arg) { char buf[100]; + char property_val[PROPERTY_VALUE_MAX]; int pid, ret; sync(); @@ -182,11 +183,19 @@ void reboot_service(int fd, void *arg) waitpid(pid, &ret, 0); } - ret = android_reboot(ANDROID_RB_RESTART2, 0, (char *) arg); + ret = snprintf(property_val, sizeof(property_val), "reboot,%s", (char *) arg); + if (ret >= (int) sizeof(property_val)) { + snprintf(buf, sizeof(buf), "reboot string too long. length=%d\n", ret); + writex(fd, buf, strlen(buf)); + goto cleanup; + } + + ret = property_set(ANDROID_RB_PROPERTY, property_val); if (ret < 0) { - snprintf(buf, sizeof(buf), "reboot failed: %s\n", strerror(errno)); + snprintf(buf, sizeof(buf), "reboot failed: %d\n", ret); writex(fd, buf, strlen(buf)); } +cleanup: free(arg); adb_close(fd); } diff --git a/include/cutils/android_reboot.h b/include/cutils/android_reboot.h index 0c79be7e3..8c30e8e3f 100644 --- a/include/cutils/android_reboot.h +++ b/include/cutils/android_reboot.h @@ -24,9 +24,8 @@ __BEGIN_DECLS #define ANDROID_RB_POWEROFF 0xDEAD0002 #define ANDROID_RB_RESTART2 0xDEAD0003 -/* Flags */ -#define ANDROID_RB_FLAG_NO_SYNC 0x1 -#define ANDROID_RB_FLAG_NO_REMOUNT_RO 0x2 +/* Properties */ +#define ANDROID_RB_PROPERTY "sys.powerctl" int android_reboot(int cmd, int flags, char *arg); diff --git a/include/private/android_filesystem_config.h b/include/private/android_filesystem_config.h index 850e0bd75..d69b33225 100644 --- a/include/private/android_filesystem_config.h +++ b/include/private/android_filesystem_config.h @@ -230,7 +230,6 @@ static const struct fs_path_config android_files[] = { /* the following files have enhanced capabilities and ARE included in user builds. */ { 00750, AID_ROOT, AID_SHELL, (1 << CAP_SETUID) | (1 << CAP_SETGID), "system/bin/run-as" }, - { 00750, AID_ROOT, AID_SHELL, 1 << CAP_SYS_BOOT, "system/bin/reboot" }, { 00755, AID_ROOT, AID_SHELL, 0, "system/bin/*" }, { 00755, AID_ROOT, AID_ROOT, 0, "system/lib/valgrind/*" }, diff --git a/init/builtins.c b/init/builtins.c index 0f9f13136..9ae9ba3f7 100644 --- a/init/builtins.c +++ b/init/builtins.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -599,6 +600,43 @@ int do_restart(int nargs, char **args) return 0; } +int do_powerctl(int nargs, char **args) +{ + char command[PROP_VALUE_MAX]; + int res; + int len = 0; + int cmd = 0; + char *reboot_target; + + res = expand_props(command, args[1], sizeof(command)); + if (res) { + ERROR("powerctl: cannot expand '%s'\n", args[1]); + return -EINVAL; + } + + if (strncmp(command, "shutdown", 8) == 0) { + cmd = ANDROID_RB_POWEROFF; + len = 8; + } else if (strncmp(command, "reboot", 6) == 0) { + cmd = ANDROID_RB_RESTART2; + len = 6; + } else { + ERROR("powerctl: unrecognized command '%s'\n", command); + return -EINVAL; + } + + if (command[len] == ',') { + reboot_target = &command[len + 1]; + } else if (command[len] == '\0') { + reboot_target = ""; + } else { + ERROR("powerctl: unrecognized reboot target '%s'\n", &command[len]); + return -EINVAL; + } + + return android_reboot(cmd, 0, reboot_target); +} + int do_trigger(int nargs, char **args) { action_for_each_trigger(args[1], action_add_queue_tail); diff --git a/init/init_parser.c b/init/init_parser.c index 686640ed4..a1d242355 100644 --- a/init/init_parser.c +++ b/init/init_parser.c @@ -130,6 +130,8 @@ int lookup_keyword(const char *s) if (!strcmp(s, "neshot")) return K_oneshot; if (!strcmp(s, "nrestart")) return K_onrestart; break; + case 'p': + if (!strcmp(s, "owerctl")) return K_powerctl; case 'r': if (!strcmp(s, "estart")) return K_restart; if (!strcmp(s, "estorecon")) return K_restorecon; diff --git a/init/keywords.h b/init/keywords.h index f188db5d7..f14750647 100644 --- a/init/keywords.h +++ b/init/keywords.h @@ -14,6 +14,7 @@ int do_insmod(int nargs, char **args); int do_mkdir(int nargs, char **args); int do_mount_all(int nargs, char **args); int do_mount(int nargs, char **args); +int do_powerctl(int nargs, char **args); int do_restart(int nargs, char **args); int do_restorecon(int nargs, char **args); int do_rm(int nargs, char **args); @@ -66,6 +67,7 @@ enum { KEYWORD(on, SECTION, 0, 0) KEYWORD(oneshot, OPTION, 0, 0) KEYWORD(onrestart, OPTION, 0, 0) + KEYWORD(powerctl, COMMAND, 1, do_powerctl) KEYWORD(restart, COMMAND, 1, do_restart) KEYWORD(restorecon, COMMAND, 1, do_restorecon) KEYWORD(rm, COMMAND, 1, do_rm) diff --git a/init/property_service.c b/init/property_service.c old mode 100755 new mode 100644 index 578000154..6bf06b4f7 --- a/init/property_service.c +++ b/init/property_service.c @@ -77,6 +77,7 @@ struct { { "runtime.", AID_SYSTEM, 0 }, { "hw.", AID_SYSTEM, 0 }, { "sys.", AID_SYSTEM, 0 }, + { "sys.powerctl", AID_SHELL, 0 }, { "service.", AID_SYSTEM, 0 }, { "wlan.", AID_SYSTEM, 0 }, { "bluetooth.", AID_BLUETOOTH, 0 }, diff --git a/libcutils/android_reboot.c b/libcutils/android_reboot.c index 33a7358ec..16f82bbd5 100644 --- a/libcutils/android_reboot.c +++ b/libcutils/android_reboot.c @@ -105,11 +105,8 @@ int android_reboot(int cmd, int flags, char *arg) { int ret; - if (!(flags & ANDROID_RB_FLAG_NO_SYNC)) - sync(); - - if (!(flags & ANDROID_RB_FLAG_NO_REMOUNT_RO)) - remount_ro(); + sync(); + remount_ro(); switch (cmd) { case ANDROID_RB_RESTART: diff --git a/reboot/reboot.c b/reboot/reboot.c index 45d8a8ef5..0e5170d42 100644 --- a/reboot/reboot.c +++ b/reboot/reboot.c @@ -17,35 +17,34 @@ #include #include #include +#include #include #include int main(int argc, char *argv[]) { int ret; - int nosync = 0; - int poweroff = 0; - int flags = 0; + size_t prop_len; + char property_val[PROPERTY_VALUE_MAX]; + const char *cmd = "reboot"; + char *optarg = ""; opterr = 0; do { int c; - c = getopt(argc, argv, "np"); + c = getopt(argc, argv, "p"); if (c == EOF) { break; } switch (c) { - case 'n': - nosync = 1; - break; case 'p': - poweroff = 1; + cmd = "shutdown"; break; case '?': - fprintf(stderr, "usage: %s [-n] [-p] [rebootcommand]\n", argv[0]); + fprintf(stderr, "usage: %s [-p] [rebootcommand]\n", argv[0]); exit(EXIT_FAILURE); } } while (1); @@ -55,20 +54,20 @@ int main(int argc, char *argv[]) exit(EXIT_FAILURE); } - if(nosync) - /* also set NO_REMOUNT_RO as remount ro includes an implicit sync */ - flags = ANDROID_RB_FLAG_NO_SYNC | ANDROID_RB_FLAG_NO_REMOUNT_RO; + if (argc > optind) + optarg = argv[optind]; - if(poweroff) - ret = android_reboot(ANDROID_RB_POWEROFF, flags, 0); - else if(argc > optind) - ret = android_reboot(ANDROID_RB_RESTART2, flags, argv[optind]); - else - ret = android_reboot(ANDROID_RB_RESTART, flags, 0); + prop_len = snprintf(property_val, sizeof(property_val), "%s,%s", cmd, optarg); + if (prop_len >= sizeof(property_val)) { + fprintf(stderr, "reboot command too long: %s\n", optarg); + exit(EXIT_FAILURE); + } + + ret = property_set(ANDROID_RB_PROPERTY, property_val); if(ret < 0) { perror("reboot"); exit(EXIT_FAILURE); } - fprintf(stderr, "reboot returned\n"); + fprintf(stderr, "Done\n"); return 0; } diff --git a/rootdir/init.rc b/rootdir/init.rc index c3ef50329..f7837687d 100644 --- a/rootdir/init.rc +++ b/rootdir/init.rc @@ -398,6 +398,9 @@ on property:vold.decrypt=trigger_shutdown_framework class_reset late_start class_reset main +on property:sys.powerctl=* + powerctl ${sys.powerctl} + ## Daemon processes to be run by init. ## service ueventd /sbin/ueventd From e18c0d508a6d8b4376c6f0b8c22600e5aca37f69 Mon Sep 17 00:00:00 2001 From: Nick Kralevich Date: Tue, 16 Apr 2013 16:41:32 -0700 Subject: [PATCH 013/541] fs_mgr: make block devices read-only When a filesystem is mounted read-only, make the underlying block device read-only too. This helps prevent an attacker who is able to change permissions on the files in /dev (for example, symlink attack) from modifying the block device. In particular, this change would have stopped the LG Thrill / Optimus 3D rooting exploit (http://vulnfactory.org/blog/2012/02/26/rooting-the-lg-thrill-optimus-3d/) as that exploit modified the raw block device corresponding to /system. This change also makes UID=0 less powerful. Block devices cannot be made writable again without CAP_SYS_ADMIN, so an escalation to UID=0 by itself doesn't give full root access. adb/mount: Prior to mounting something read-write, remove the read-only restrictions on the underlying block device. This avoids messing up developer workflows. Change-Id: I135098a8fe06f327336f045aab0d48ed9de33807 --- adb/remount_service.c | 9 +++++++++ fs_mgr/fs_mgr.c | 47 ++++++++++++++++++++++++++++++++++++++----- toolbox/mount.c | 22 ++++++++++++++++++++ 3 files changed, 73 insertions(+), 5 deletions(-) diff --git a/adb/remount_service.c b/adb/remount_service.c index 4cb41e7d1..ad61284b6 100644 --- a/adb/remount_service.c +++ b/adb/remount_service.c @@ -72,6 +72,8 @@ static char *find_mount(const char *dir) static int remount_system() { char *dev; + int fd; + int OFF = 0; if (system_ro == 0) { return 0; @@ -82,6 +84,13 @@ static int remount_system() if (!dev) return -1; + fd = unix_open(dev, O_RDONLY); + if (fd < 0) + return -1; + + ioctl(fd, BLKROSET, &OFF); + adb_close(fd); + system_ro = mount(dev, "/system", "none", MS_REMOUNT, NULL); free(dev); diff --git a/fs_mgr/fs_mgr.c b/fs_mgr/fs_mgr.c index fecc556a3..08483426f 100644 --- a/fs_mgr/fs_mgr.c +++ b/fs_mgr/fs_mgr.c @@ -487,6 +487,43 @@ static void remove_trailing_slashes(char *n) } } +/* + * Mark the given block device as read-only, using the BLKROSET ioctl. + * Return 0 on success, and -1 on error. + */ +static void fs_set_blk_ro(const char *blockdev) +{ + int fd; + int ON = 1; + + fd = open(blockdev, O_RDONLY); + if (fd < 0) { + // should never happen + return; + } + + ioctl(fd, BLKROSET, &ON); + close(fd); +} + +/* + * __mount(): wrapper around the mount() system call which also + * sets the underlying block device to read-only if the mount is read-only. + * See "man 2 mount" for return values. + */ +static int __mount(const char *source, const char *target, + const char *filesystemtype, unsigned long mountflags, + const void *data) +{ + int ret = mount(source, target, filesystemtype, mountflags, data); + + if ((ret == 0) && (mountflags & MS_RDONLY) != 0) { + fs_set_blk_ro(source); + } + + return ret; +} + static int fs_match(char *in1, char *in2) { char *n1; @@ -539,9 +576,9 @@ int fs_mgr_mount_all(struct fstab *fstab) fstab->recs[i].mount_point); } - mret = mount(fstab->recs[i].blk_device, fstab->recs[i].mount_point, - fstab->recs[i].fs_type, fstab->recs[i].flags, - fstab->recs[i].fs_options); + mret = __mount(fstab->recs[i].blk_device, fstab->recs[i].mount_point, + fstab->recs[i].fs_type, fstab->recs[i].flags, + fstab->recs[i].fs_options); if (!mret) { /* Success! Go get the next one */ continue; @@ -621,8 +658,8 @@ int fs_mgr_do_mount(struct fstab *fstab, char *n_name, char *n_blk_device, } else { m = fstab->recs[i].mount_point; } - if (mount(n_blk_device, m, fstab->recs[i].fs_type, - fstab->recs[i].flags, fstab->recs[i].fs_options)) { + if (__mount(n_blk_device, m, fstab->recs[i].fs_type, + fstab->recs[i].flags, fstab->recs[i].fs_options)) { ERROR("Cannot mount filesystem on %s at %s\n", n_blk_device, m); goto out; diff --git a/toolbox/mount.c b/toolbox/mount.c index b7adce2d9..164efcd1a 100644 --- a/toolbox/mount.c +++ b/toolbox/mount.c @@ -137,6 +137,24 @@ parse_mount_options(char *arg, unsigned long rwflag, struct extra_opts *extra, i return rwflag; } +/* + * Mark the given block device as read-write, using the BLKROSET ioctl. + */ +static void fs_set_blk_rw(const char *blockdev) +{ + int fd; + int OFF = 0; + + fd = open(blockdev, O_RDONLY); + if (fd < 0) { + // should never happen + return; + } + + ioctl(fd, BLKROSET, &OFF); + close(fd); +} + static char *progname; static struct extra_opts extra; @@ -178,6 +196,10 @@ do_mount(char *dev, char *dir, char *type, unsigned long rwflag, void *data, int dev = loopdev; } + if ((rwflag & MS_RDONLY) == 0) { + fs_set_blk_rw(dev); + } + while ((s = strsep(&type, ",")) != NULL) { retry: if (mount(dev, dir, s, rwflag, data) == -1) { From 9470c2f1ab555311633d52e5ed8303a813061cdf Mon Sep 17 00:00:00 2001 From: Benoit Goby Date: Wed, 20 Feb 2013 15:04:53 -0800 Subject: [PATCH 014/541] adb: Cleanup dead code dns_service is unused and recover_service has been replaced by adb sideload Change-Id: Ie90000d7f672e8299ee1622a9690c7371b214dc1 --- adb/Android.mk | 5 +-- adb/SERVICES.TXT | 21 ---------- adb/adb.h | 5 +-- adb/mutex_list.h | 1 - adb/services.c | 103 +-------------------------------------------- adb/sockets.c | 12 ++---- adb/utils.c | 106 ----------------------------------------------- adb/utils.h | 68 ------------------------------ 8 files changed, 7 insertions(+), 314 deletions(-) delete mode 100644 adb/utils.c delete mode 100644 adb/utils.h diff --git a/adb/Android.mk b/adb/Android.mk index a80397875..36f595b50 100644 --- a/adb/Android.mk +++ b/adb/Android.mk @@ -64,7 +64,6 @@ LOCAL_SRC_FILES := \ file_sync_client.c \ $(EXTRA_SRCS) \ $(USB_SRCS) \ - utils.c \ usb_vendors.c LOCAL_C_INCLUDES += external/openssl/include @@ -116,8 +115,7 @@ LOCAL_SRC_FILES := \ framebuffer_service.c \ remount_service.c \ usb_linux_client.c \ - log_service.c \ - utils.c + log_service.c LOCAL_CFLAGS := -O2 -g -DADB_HOST=0 -Wall -Wno-unused-parameter LOCAL_CFLAGS += -D_XOPEN_SOURCE -D_GNU_SOURCE @@ -157,7 +155,6 @@ LOCAL_SRC_FILES := \ file_sync_client.c \ get_my_path_linux.c \ usb_linux.c \ - utils.c \ usb_vendors.c \ fdevent.c diff --git a/adb/SERVICES.TXT b/adb/SERVICES.TXT index b53bc444c..5966686de 100644 --- a/adb/SERVICES.TXT +++ b/adb/SERVICES.TXT @@ -225,27 +225,6 @@ framebuffer: If the adbd daemon doesn't have sufficient privileges to open the framebuffer device, the connection is simply closed immediately. -dns: - This service is an exception because it only runs within the ADB server. - It is used to implement USB networking, i.e. to provide a network connection - to the device through the host machine (note: this is the exact opposite of - network tethering). - - It is used to perform a gethostbyname(
) on the host and return - the corresponding IP address as a 4-byte string. - -recover: - This service is used to upload a recovery image to the device. - must be a number corresponding to the size of the file. The service works - by: - - - creating a file named /tmp/update - - reading 'size' bytes from the client and writing them to /tmp/update - - when everything is read successfully, create a file named /tmp/update.start - - This service can only work when the device is in recovery mode. Otherwise, - the /tmp directory doesn't exist and the connection will be closed immediately. - jdwp: Connects to the JDWP thread running in the VM of process . diff --git a/adb/adb.h b/adb/adb.h index a01d460e4..38767e57f 100644 --- a/adb/adb.h +++ b/adb/adb.h @@ -128,10 +128,7 @@ struct asocket { */ void (*close)(asocket *s); - /* socket-type-specific extradata */ - void *extra; - - /* A socket is bound to atransport */ + /* A socket is bound to atransport */ atransport *transport; }; diff --git a/adb/mutex_list.h b/adb/mutex_list.h index 652dd7341..ff7275129 100644 --- a/adb/mutex_list.h +++ b/adb/mutex_list.h @@ -6,7 +6,6 @@ #ifndef ADB_MUTEX #error ADB_MUTEX not defined when including this file #endif -ADB_MUTEX(dns_lock) ADB_MUTEX(socket_list_lock) ADB_MUTEX(transport_lock) #if ADB_HOST diff --git a/adb/services.c b/adb/services.c index e82a0ea5a..619199f61 100644 --- a/adb/services.c +++ b/adb/services.c @@ -53,59 +53,7 @@ void *service_bootstrap_func(void *x) return 0; } -#if ADB_HOST -ADB_MUTEX_DEFINE( dns_lock ); - -static void dns_service(int fd, void *cookie) -{ - char *hostname = cookie; - struct hostent *hp; - unsigned zero = 0; - - adb_mutex_lock(&dns_lock); - hp = gethostbyname(hostname); - free(cookie); - if(hp == 0) { - writex(fd, &zero, 4); - } else { - writex(fd, hp->h_addr, 4); - } - adb_mutex_unlock(&dns_lock); - adb_close(fd); -} -#else -extern int recovery_mode; - -static void recover_service(int s, void *cookie) -{ - unsigned char buf[4096]; - unsigned count = (unsigned) cookie; - int fd; - - fd = adb_creat("/tmp/update", 0644); - if(fd < 0) { - adb_close(s); - return; - } - - while(count > 0) { - unsigned xfer = (count > 4096) ? 4096 : count; - if(readx(s, buf, xfer)) break; - if(writex(fd, buf, xfer)) break; - count -= xfer; - } - - if(count == 0) { - writex(s, "OKAY", 4); - } else { - writex(s, "FAIL", 4); - } - adb_close(fd); - adb_close(s); - - fd = adb_creat("/tmp/update.begin", 0644); - adb_close(fd); -} +#if !ADB_HOST void restart_root_service(int fd, void *cookie) { @@ -202,40 +150,6 @@ cleanup: #endif -#if 0 -static void echo_service(int fd, void *cookie) -{ - char buf[4096]; - int r; - char *p; - int c; - - for(;;) { - r = adb_read(fd, buf, 4096); - if(r == 0) goto done; - if(r < 0) { - if(errno == EINTR) continue; - else goto done; - } - - c = r; - p = buf; - while(c > 0) { - r = write(fd, p, c); - if(r > 0) { - c -= r; - p += r; - continue; - } - if((r < 0) && (errno == EINTR)) continue; - goto done; - } - } -done: - close(fd); -} -#endif - static int create_service_thread(void (*func)(int, void *), void *cookie) { stinfo *sti; @@ -422,9 +336,7 @@ int service_to_fd(const char *name) disable_tcp_nagle(ret); } else { #if ADB_HOST - adb_mutex_lock(&dns_lock); ret = socket_network_client(name + 1, port, SOCK_STREAM); - adb_mutex_unlock(&dns_lock); #else return -1; #endif @@ -443,18 +355,11 @@ int service_to_fd(const char *name) ret = socket_local_client(name + 16, ANDROID_SOCKET_NAMESPACE_FILESYSTEM, SOCK_STREAM); #endif -#if ADB_HOST - } else if(!strncmp("dns:", name, 4)){ - char *n = strdup(name + 4); - if(n == 0) return -1; - ret = create_service_thread(dns_service, n); -#else /* !ADB_HOST */ +#if !ADB_HOST } else if(!strncmp("dev:", name, 4)) { ret = unix_open(name + 4, O_RDWR); } else if(!strncmp(name, "framebuffer:", 12)) { ret = create_service_thread(framebuffer_service, 0); - } else if(recovery_mode && !strncmp(name, "recover:", 8)) { - ret = create_service_thread(recover_service, (void*) atoi(name + 8)); } else if (!strncmp(name, "jdwp:", 5)) { ret = create_jdwp_connection_fd(atoi(name+5)); } else if (!strncmp(name, "log:", 4)) { @@ -489,10 +394,6 @@ int service_to_fd(const char *name) ret = create_service_thread(restart_tcp_service, (void *)port); } else if(!strncmp(name, "usb:", 4)) { ret = create_service_thread(restart_usb_service, NULL); -#endif -#if 0 - } else if(!strncmp(name, "echo:", 5)){ - ret = create_service_thread(echo_service, 0); #endif } if (ret >= 0) { diff --git a/adb/sockets.c b/adb/sockets.c index 305cb4429..f17608b62 100644 --- a/adb/sockets.c +++ b/adb/sockets.c @@ -844,7 +844,7 @@ static void smart_socket_close(asocket *s) free(s); } -asocket *create_smart_socket(void (*action_cb)(asocket *s, const char *act)) +static asocket *create_smart_socket(void) { D("Creating smart socket \n"); asocket *s = calloc(1, sizeof(asocket)); @@ -852,21 +852,15 @@ asocket *create_smart_socket(void (*action_cb)(asocket *s, const char *act)) s->enqueue = smart_socket_enqueue; s->ready = smart_socket_ready; s->close = smart_socket_close; - s->extra = action_cb; - D("SS(%d): created %p\n", s->id, action_cb); + D("SS(%d)\n", s->id); return s; } -void smart_socket_action(asocket *s, const char *act) -{ - -} - void connect_to_smartsocket(asocket *s) { D("Connecting to smart socket \n"); - asocket *ss = create_smart_socket(smart_socket_action); + asocket *ss = create_smart_socket(); s->peer = ss; ss->peer = s; s->ready(s); diff --git a/adb/utils.c b/adb/utils.c deleted file mode 100644 index 91518bab6..000000000 --- a/adb/utils.c +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "utils.h" -#include -#include -#include - -char* -buff_addc (char* buff, char* buffEnd, int c) -{ - int avail = buffEnd - buff; - - if (avail <= 0) /* already in overflow mode */ - return buff; - - if (avail == 1) { /* overflowing, the last byte is reserved for zero */ - buff[0] = 0; - return buff + 1; - } - - buff[0] = (char) c; /* add char and terminating zero */ - buff[1] = 0; - return buff + 1; -} - -char* -buff_adds (char* buff, char* buffEnd, const char* s) -{ - int slen = strlen(s); - - return buff_addb(buff, buffEnd, s, slen); -} - -char* -buff_addb (char* buff, char* buffEnd, const void* data, int len) -{ - int avail = (buffEnd - buff); - - if (avail <= 0 || len <= 0) /* already overflowing */ - return buff; - - if (len > avail) - len = avail; - - memcpy(buff, data, len); - - buff += len; - - /* ensure there is a terminating zero */ - if (buff >= buffEnd) { /* overflow */ - buff[-1] = 0; - } else - buff[0] = 0; - - return buff; -} - -char* -buff_add (char* buff, char* buffEnd, const char* format, ... ) -{ - int avail; - - avail = (buffEnd - buff); - - if (avail > 0) { - va_list args; - int nn; - - va_start(args, format); - nn = vsnprintf( buff, avail, format, args); - va_end(args); - - if (nn < 0) { - /* some C libraries return -1 in case of overflow, - * but they will also do that if the format spec is - * invalid. We assume ADB is not buggy enough to - * trigger that last case. */ - nn = avail; - } - else if (nn > avail) { - nn = avail; - } - - buff += nn; - - /* ensure that there is a terminating zero */ - if (buff >= buffEnd) - buff[-1] = 0; - else - buff[0] = 0; - } - return buff; -} diff --git a/adb/utils.h b/adb/utils.h deleted file mode 100644 index f70ecd24d..000000000 --- a/adb/utils.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef _ADB_UTILS_H -#define _ADB_UTILS_H - -/* bounded buffer functions */ - -/* all these functions are used to append data to a bounded buffer. - * - * after each operation, the buffer is guaranteed to be zero-terminated, - * even in the case of an overflow. they all return the new buffer position - * which allows one to use them in succession, only checking for overflows - * at the end. For example: - * - * BUFF_DECL(temp,p,end,1024); - * char* p; - * - * p = buff_addc(temp, end, '"'); - * p = buff_adds(temp, end, string); - * p = buff_addc(temp, end, '"'); - * - * if (p >= end) { - * overflow detected. note that 'temp' is - * zero-terminated for safety. - * } - * return strdup(temp); - */ - -/* tries to add a character to the buffer, in case of overflow - * this will only write a terminating zero and return buffEnd. - */ -char* buff_addc (char* buff, char* buffEnd, int c); - -/* tries to add a string to the buffer */ -char* buff_adds (char* buff, char* buffEnd, const char* s); - -/* tries to add a bytes to the buffer. the input can contain zero bytes, - * but a terminating zero will always be appended at the end anyway - */ -char* buff_addb (char* buff, char* buffEnd, const void* data, int len); - -/* tries to add a formatted string to a bounded buffer */ -char* buff_add (char* buff, char* buffEnd, const char* format, ... ); - -/* convenience macro used to define a bounded buffer, as well as - * a 'cursor' and 'end' variables all in one go. - * - * note: this doesn't place an initial terminating zero in the buffer, - * you need to use one of the buff_ functions for this. or simply - * do _cursor[0] = 0 manually. - */ -#define BUFF_DECL(_buff,_cursor,_end,_size) \ - char _buff[_size], *_cursor=_buff, *_end = _cursor + (_size) - -#endif /* _ADB_UTILS_H */ From 1c45ee92e2372f3c552744823143fb093fdbda9d Mon Sep 17 00:00:00 2001 From: Benoit Goby Date: Fri, 29 Mar 2013 18:22:36 -0700 Subject: [PATCH 015/541] adb: Handle adb connect in a thread adb connect calls connect() in the event loop. If you pass a wrong ip address or the server is slow to respond, this will block the event loop and you can't even kill the adb server with adb kill-server. Handle connect requests in a service thread instead. Change-Id: I2ee732869a3dc22a6d3b87cf8ac80acaa7790037 --- adb/adb.c | 114 -------------------------------------------- adb/adb.h | 2 +- adb/services.c | 123 ++++++++++++++++++++++++++++++++++++++++++++++++ adb/transport.c | 53 ++++++++++++++++++--- 4 files changed, 170 insertions(+), 122 deletions(-) diff --git a/adb/adb.c b/adb/adb.c index 6580c6e95..187f51a8d 100644 --- a/adb/adb.c +++ b/adb/adb.c @@ -1387,105 +1387,6 @@ int adb_main(int is_daemon, int server_port) return 0; } -#if ADB_HOST -void connect_device(char* host, char* buffer, int buffer_size) -{ - int port, fd; - char* portstr = strchr(host, ':'); - char hostbuf[100]; - char serial[100]; - - strncpy(hostbuf, host, sizeof(hostbuf) - 1); - if (portstr) { - if (portstr - host >= (ptrdiff_t)sizeof(hostbuf)) { - snprintf(buffer, buffer_size, "bad host name %s", host); - return; - } - // zero terminate the host at the point we found the colon - hostbuf[portstr - host] = 0; - if (sscanf(portstr + 1, "%d", &port) == 0) { - snprintf(buffer, buffer_size, "bad port number %s", portstr); - return; - } - } else { - port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT; - } - - snprintf(serial, sizeof(serial), "%s:%d", hostbuf, port); - if (find_transport(serial)) { - snprintf(buffer, buffer_size, "already connected to %s", serial); - return; - } - - fd = socket_network_client(hostbuf, port, SOCK_STREAM); - if (fd < 0) { - snprintf(buffer, buffer_size, "unable to connect to %s:%d", host, port); - return; - } - - D("client: connected on remote on fd %d\n", fd); - close_on_exec(fd); - disable_tcp_nagle(fd); - register_socket_transport(fd, serial, port, 0); - snprintf(buffer, buffer_size, "connected to %s", serial); -} - -void connect_emulator(char* port_spec, char* buffer, int buffer_size) -{ - char* port_separator = strchr(port_spec, ','); - if (!port_separator) { - snprintf(buffer, buffer_size, - "unable to parse '%s' as ,", - port_spec); - return; - } - - // Zero-terminate console port and make port_separator point to 2nd port. - *port_separator++ = 0; - int console_port = strtol(port_spec, NULL, 0); - int adb_port = strtol(port_separator, NULL, 0); - if (!(console_port > 0 && adb_port > 0)) { - *(port_separator - 1) = ','; - snprintf(buffer, buffer_size, - "Invalid port numbers: Expected positive numbers, got '%s'", - port_spec); - return; - } - - /* Check if the emulator is already known. - * Note: There's a small but harmless race condition here: An emulator not - * present just yet could be registered by another invocation right - * after doing this check here. However, local_connect protects - * against double-registration too. From here, a better error message - * can be produced. In the case of the race condition, the very specific - * error message won't be shown, but the data doesn't get corrupted. */ - atransport* known_emulator = find_emulator_transport_by_adb_port(adb_port); - if (known_emulator != NULL) { - snprintf(buffer, buffer_size, - "Emulator on port %d already registered.", adb_port); - return; - } - - /* Check if more emulators can be registered. Similar unproblematic - * race condition as above. */ - int candidate_slot = get_available_local_transport_index(); - if (candidate_slot < 0) { - snprintf(buffer, buffer_size, "Cannot accept more emulators."); - return; - } - - /* Preconditions met, try to connect to the emulator. */ - if (!local_connect_arbitrary_ports(console_port, adb_port)) { - snprintf(buffer, buffer_size, - "Connected to emulator on ports %d,%d", console_port, adb_port); - } else { - snprintf(buffer, buffer_size, - "Could not connect to emulator on ports %d,%d", - console_port, adb_port); - } -} -#endif - int handle_host_request(char *service, transport_type ttype, char* serial, int reply_fd, asocket *s) { atransport *transport = NULL; @@ -1546,21 +1447,6 @@ int handle_host_request(char *service, transport_type ttype, char* serial, int r } } - // add a new TCP transport, device or emulator - if (!strncmp(service, "connect:", 8)) { - char buffer[4096]; - char* host = service + 8; - if (!strncmp(host, "emu:", 4)) { - connect_emulator(host + 4, buffer, sizeof(buffer)); - } else { - connect_device(host, buffer, sizeof(buffer)); - } - // Send response for emulator and device - snprintf(buf, sizeof(buf), "OKAY%04x%s",(unsigned)strlen(buffer), buffer); - writex(reply_fd, buf, strlen(buf)); - return 0; - } - // remove TCP transport if (!strncmp(service, "disconnect:", 11)) { char buffer[4096]; diff --git a/adb/adb.h b/adb/adb.h index 38767e57f..622ca7099 100644 --- a/adb/adb.h +++ b/adb/adb.h @@ -289,7 +289,7 @@ void init_usb_transport(atransport *t, usb_handle *usb, int state); void close_usb_devices(); /* cause new transports to be init'd and added to the list */ -void register_socket_transport(int s, const char *serial, int port, int local); +int register_socket_transport(int s, const char *serial, int port, int local); /* these should only be used for the "adb disconnect" command */ void unregister_transport(atransport *t); diff --git a/adb/services.c b/adb/services.c index 619199f61..d2d428a09 100644 --- a/adb/services.c +++ b/adb/services.c @@ -14,6 +14,7 @@ * limitations under the License. */ +#include #include #include #include @@ -429,6 +430,124 @@ static void wait_for_state(int fd, void* cookie) adb_close(fd); D("wait_for_state is done\n"); } + +static void connect_device(char* host, char* buffer, int buffer_size) +{ + int port, fd; + char* portstr = strchr(host, ':'); + char hostbuf[100]; + char serial[100]; + int ret; + + strncpy(hostbuf, host, sizeof(hostbuf) - 1); + if (portstr) { + if (portstr - host >= (ptrdiff_t)sizeof(hostbuf)) { + snprintf(buffer, buffer_size, "bad host name %s", host); + return; + } + // zero terminate the host at the point we found the colon + hostbuf[portstr - host] = 0; + if (sscanf(portstr + 1, "%d", &port) == 0) { + snprintf(buffer, buffer_size, "bad port number %s", portstr); + return; + } + } else { + port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT; + } + + snprintf(serial, sizeof(serial), "%s:%d", hostbuf, port); + + fd = socket_network_client(hostbuf, port, SOCK_STREAM); + if (fd < 0) { + snprintf(buffer, buffer_size, "unable to connect to %s:%d", host, port); + return; + } + + D("client: connected on remote on fd %d\n", fd); + close_on_exec(fd); + disable_tcp_nagle(fd); + + ret = register_socket_transport(fd, serial, port, 0); + if (ret < 0) { + adb_close(fd); + snprintf(buffer, buffer_size, "already connected to %s", serial); + } else { + snprintf(buffer, buffer_size, "connected to %s", serial); + } +} + +void connect_emulator(char* port_spec, char* buffer, int buffer_size) +{ + char* port_separator = strchr(port_spec, ','); + if (!port_separator) { + snprintf(buffer, buffer_size, + "unable to parse '%s' as ,", + port_spec); + return; + } + + // Zero-terminate console port and make port_separator point to 2nd port. + *port_separator++ = 0; + int console_port = strtol(port_spec, NULL, 0); + int adb_port = strtol(port_separator, NULL, 0); + if (!(console_port > 0 && adb_port > 0)) { + *(port_separator - 1) = ','; + snprintf(buffer, buffer_size, + "Invalid port numbers: Expected positive numbers, got '%s'", + port_spec); + return; + } + + /* Check if the emulator is already known. + * Note: There's a small but harmless race condition here: An emulator not + * present just yet could be registered by another invocation right + * after doing this check here. However, local_connect protects + * against double-registration too. From here, a better error message + * can be produced. In the case of the race condition, the very specific + * error message won't be shown, but the data doesn't get corrupted. */ + atransport* known_emulator = find_emulator_transport_by_adb_port(adb_port); + if (known_emulator != NULL) { + snprintf(buffer, buffer_size, + "Emulator on port %d already registered.", adb_port); + return; + } + + /* Check if more emulators can be registered. Similar unproblematic + * race condition as above. */ + int candidate_slot = get_available_local_transport_index(); + if (candidate_slot < 0) { + snprintf(buffer, buffer_size, "Cannot accept more emulators."); + return; + } + + /* Preconditions met, try to connect to the emulator. */ + if (!local_connect_arbitrary_ports(console_port, adb_port)) { + snprintf(buffer, buffer_size, + "Connected to emulator on ports %d,%d", console_port, adb_port); + } else { + snprintf(buffer, buffer_size, + "Could not connect to emulator on ports %d,%d", + console_port, adb_port); + } +} + +static void connect_service(int fd, void* cookie) +{ + char buf[4096]; + char resp[4096]; + char *host = cookie; + + if (!strncmp(host, "emu:", 4)) { + connect_emulator(host + 4, buf, sizeof(buf)); + } else { + connect_device(host, buf, sizeof(buf)); + } + + // Send response for emulator and device + snprintf(resp, sizeof(resp), "%04x%s",(unsigned)strlen(buf), buf); + writex(fd, resp, strlen(resp)); + adb_close(fd); +} #endif #if ADB_HOST @@ -462,6 +581,10 @@ asocket* host_service_to_socket(const char* name, const char *serial) int fd = create_service_thread(wait_for_state, sinfo); return create_local_socket(fd); + } else if (!strncmp(name, "connect:", 8)) { + const char *host = name + 8; + int fd = create_service_thread(connect_service, (void *)host); + return create_local_socket(fd); } return NULL; } diff --git a/adb/transport.c b/adb/transport.c index b4abb66ec..224fe556b 100644 --- a/adb/transport.c +++ b/adb/transport.c @@ -32,6 +32,11 @@ static atransport transport_list = { .prev = &transport_list, }; +static atransport pending_list = { + .next = &pending_list, + .prev = &pending_list, +}; + ADB_MUTEX_DEFINE( transport_lock ); #if ADB_TRACE @@ -645,8 +650,11 @@ static void transport_registration_func(int _fd, unsigned ev, void *data) } } - /* put us on the master device list */ adb_mutex_lock(&transport_lock); + /* remove from pending list */ + t->next->prev = t->prev; + t->prev->next = t->next; + /* put us on the master device list */ t->next = &transport_list; t->prev = transport_list.prev; t->next->prev = t; @@ -989,9 +997,10 @@ void close_usb_devices() } #endif // ADB_HOST -void register_socket_transport(int s, const char *serial, int port, int local) +int register_socket_transport(int s, const char *serial, int port, int local) { atransport *t = calloc(1, sizeof(atransport)); + atransport *n; char buff[32]; if (!serial) { @@ -999,15 +1008,37 @@ void register_socket_transport(int s, const char *serial, int port, int local) serial = buff; } D("transport: %s init'ing for socket %d, on port %d\n", serial, s, port); - if ( init_socket_transport(t, s, port, local) < 0 ) { - adb_close(s); + if (init_socket_transport(t, s, port, local) < 0) { free(t); - return; + return -1; } - if(serial) { - t->serial = strdup(serial); + + adb_mutex_lock(&transport_lock); + for (n = pending_list.next; n != &pending_list; n = n->next) { + if (n->serial && !strcmp(serial, n->serial)) { + adb_mutex_unlock(&transport_lock); + free(t); + return -1; + } } + + for (n = transport_list.next; n != &transport_list; n = n->next) { + if (n->serial && !strcmp(serial, n->serial)) { + adb_mutex_unlock(&transport_lock); + free(t); + return -1; + } + } + + t->next = &pending_list; + t->prev = pending_list.prev; + t->next->prev = t; + t->prev->next = t; + t->serial = strdup(serial); + adb_mutex_unlock(&transport_lock); + register_transport(t); + return 0; } #if ADB_HOST @@ -1077,6 +1108,14 @@ void register_usb_transport(usb_handle *usb, const char *serial, const char *dev if(devpath) { t->devpath = strdup(devpath); } + + adb_mutex_lock(&transport_lock); + t->next = &pending_list; + t->prev = pending_list.prev; + t->next->prev = t; + t->prev->next = t; + adb_mutex_unlock(&transport_lock); + register_transport(t); } From e00a12bf8a99eb6c4f278efa503488aa21dd8d4d Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Tue, 7 May 2013 00:05:53 -0700 Subject: [PATCH 016/541] cutils: first pass at cleaning up legacy/obsolete code in cutils Removed unused code and moved libraries with single clients near their respective users. Change-Id: I65f90f8659f27bd0f44ca5ddf33da2bce14674c1 Signed-off-by: Dima Zavin --- include/cutils/abort_socket.h | 103 --- include/cutils/array.h | 67 -- include/cutils/mq.h | 124 --- include/cutils/qsort_r_compat.h | 39 - include/cutils/record_stream.h | 43 - include/cutils/selector.h | 130 --- include/cutils/zygote.h | 31 - libcutils/Android.mk | 10 +- libcutils/abort_socket.c | 293 ------- libcutils/array.c | 170 ---- libcutils/buffer.c | 116 --- libcutils/buffer.h | 112 --- libcutils/mq.c | 1357 ------------------------------- libcutils/qsort_r_compat.c | 84 -- libcutils/record_stream.c | 186 ----- libcutils/selector.c | 263 ------ libcutils/zygote.c | 229 ------ 17 files changed, 1 insertion(+), 3356 deletions(-) delete mode 100644 include/cutils/abort_socket.h delete mode 100644 include/cutils/array.h delete mode 100644 include/cutils/mq.h delete mode 100644 include/cutils/qsort_r_compat.h delete mode 100644 include/cutils/record_stream.h delete mode 100644 include/cutils/selector.h delete mode 100644 include/cutils/zygote.h delete mode 100644 libcutils/abort_socket.c delete mode 100644 libcutils/array.c delete mode 100644 libcutils/buffer.c delete mode 100644 libcutils/buffer.h delete mode 100644 libcutils/mq.c delete mode 100644 libcutils/qsort_r_compat.c delete mode 100644 libcutils/record_stream.c delete mode 100644 libcutils/selector.c delete mode 100644 libcutils/zygote.c diff --git a/include/cutils/abort_socket.h b/include/cutils/abort_socket.h deleted file mode 100644 index fbb1112fa..000000000 --- a/include/cutils/abort_socket.h +++ /dev/null @@ -1,103 +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. - */ - -/* Helper to perform abortable blocking operations on a socket: - * asocket_connect() - * asocket_accept() - * asocket_read() - * asocket_write() - * These calls are similar to the regular syscalls, but can be aborted with: - * asocket_abort() - * - * Calling close() on a regular POSIX socket does not abort blocked syscalls on - * that socket in other threads. - * - * After calling asocket_abort() the socket cannot be reused. - * - * Call asocket_destory() *after* all threads have finished with the socket to - * finish closing the socket and free the asocket structure. - * - * The helper is implemented by setting the socket non-blocking to initiate - * syscalls connect(), accept(), read(), write(), then using a blocking poll() - * on both the primary socket and a local pipe. This makes the poll() abortable - * by writing a byte to the local pipe in asocket_abort(). - * - * asocket_create() sets the fd to non-blocking mode. It must not be changed to - * blocking mode. - * - * Using asocket will triple the number of file descriptors required per - * socket, due to the local pipe. It may be possible to use a global pipe per - * process rather than per socket, but we have not been able to come up with a - * race-free implementation yet. - * - * All functions except asocket_init() and asocket_destroy() are thread safe. - */ - -#include -#include - -#ifndef __CUTILS_ABORT_SOCKET_H__ -#define __CUTILS_ABORT_SOCKET_H__ -#ifdef __cplusplus -extern "C" { -#endif - -struct asocket { - int fd; /* primary socket fd */ - int abort_fd[2]; /* pipe used to abort */ -}; - -/* Create an asocket from fd. - * Sets the socket to non-blocking mode. - * Returns NULL on error with errno set. - */ -struct asocket *asocket_init(int fd); - -/* Blocking socket I/O with timeout. - * Calling asocket_abort() from another thread will cause each of these - * functions to immediately return with value -1 and errno ECANCELED. - * timeout is in ms, use -1 to indicate no timeout. On timeout -1 is returned - * with errno ETIMEDOUT. - * EINTR is handled in-call. - * Other semantics are identical to the regular syscalls. - */ -int asocket_connect(struct asocket *s, const struct sockaddr *addr, - socklen_t addrlen, int timeout); - -int asocket_accept(struct asocket *s, struct sockaddr *addr, - socklen_t *addrlen, int timeout); - -int asocket_read(struct asocket *s, void *buf, size_t count, int timeout); - -int asocket_write(struct asocket *s, const void *buf, size_t count, - int timeout); - -/* Abort above calls and shutdown socket. - * Further I/O operations on this socket will immediately fail after this call. - * asocket_destroy() should be used to release resources once all threads - * have returned from blocking calls on the socket. - */ -void asocket_abort(struct asocket *s); - -/* Close socket and free asocket structure. - * Must not be called until all calls on this structure have completed. - */ -void asocket_destroy(struct asocket *s); - -#ifdef __cplusplus -} -#endif -#endif //__CUTILS_ABORT_SOCKET__H__ diff --git a/include/cutils/array.h b/include/cutils/array.h deleted file mode 100644 index c97ff34cb..000000000 --- a/include/cutils/array.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * 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. - */ - -/** - * A pointer array which intelligently expands its capacity ad needed. - */ - -#ifndef __ARRAY_H -#define __ARRAY_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include - -/** An array. */ -typedef struct Array Array; - -/** Constructs a new array. Returns NULL if we ran out of memory. */ -Array* arrayCreate(); - -/** Frees an array. Does not free elements themselves. */ -void arrayFree(Array* array); - -/** Adds a pointer. Returns 0 is successful, < 0 otherwise. */ -int arrayAdd(Array* array, void* pointer); - -/** Gets the pointer at the specified index. */ -void* arrayGet(Array* array, int index); - -/** Removes the pointer at the given index and returns it. */ -void* arrayRemove(Array* array, int index); - -/** Sets pointer at the given index. Returns old pointer. */ -void* arraySet(Array* array, int index, void* pointer); - -/** Sets the array size. Sets new pointers to NULL. Returns 0 if successful, < 0 otherwise . */ -int arraySetSize(Array* array, int size); - -/** Returns the size of the given array. */ -int arraySize(Array* array); - -/** - * Returns a pointer to a C-style array which will be valid until this array - * changes. - */ -const void** arrayUnwrap(Array* array); - -#ifdef __cplusplus -} -#endif - -#endif /* __ARRAY_H */ diff --git a/include/cutils/mq.h b/include/cutils/mq.h deleted file mode 100644 index b27456d4f..000000000 --- a/include/cutils/mq.h +++ /dev/null @@ -1,124 +0,0 @@ -/* - * 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. - */ - -/** - * IPC messaging library. - */ - -#ifndef __MQ_H -#define __MQ_H - -#ifdef __cplusplus -extern "C" { -#endif - -/** A message. */ -typedef struct MqMessage MqMessage; - -/** A destination to which messages can be sent. */ -typedef struct MqDestination MqDestination; - -/* Array of bytes. */ -typedef struct MqBytes MqBytes; - -/** - * Hears messages. - * - * @param destination to which the message was sent - * @param message the message to hear - */ -typedef void MqMessageListener(MqDestination* destination, MqMessage* message); - -/** - * Hears a destination close. - * - * @param destination that closed - */ -typedef void MqCloseListener(MqDestination* destination); - -/** Message functions. */ - -/** - * Creates a new Message. - * - * @param header as defined by user - * @param body as defined by user - * @param replyTo destination to which replies should be sent, NULL if none - */ -MqMessage* mqCreateMessage(MqBytes header, MqBytes body, - MqDestination* replyTo); - -/** Sends a message to a destination. */ -void mqSendMessage(MqMessage* message, MqDestination* destination); - -/** Destination functions. */ - -/** - * Creates a new destination. Acquires a reference implicitly. - * - * @param messageListener function to call when a message is recieved - * @param closeListener function to call when the destination closes - * @param userData user-specific data to associate with the destination. - * Retrieve using mqGetDestinationUserData(). - */ -MqDestination* mqCreateDestination(MqMessageListener* messageListener, - MqCloseListener* closeListener, void* userData); - -/** - * Gets user data which was associated with the given destination at - * construction time. - * - * It is only valid to call this function in the same process that the - * given destination was created in. - * This function returns a null pointer if you call it on a destination - * created in a remote process. - */ -void* mqGetUserData(MqDestination* destination); - -/** - * Returns 1 if the destination was created in this process, or 0 if - * the destination was created in a different process, in which case you have - * a remote stub. - */ -int mqIsDestinationLocal(MqDestination* destination); - -/** - * Increments the destination's reference count. - */ -void mqKeepDestination(MqDesintation* destination); - -/** - * Decrements the destination's reference count. - */ -void mqFreeDestination(MqDestination* desintation); - -/** Registry API. */ - -/** - * Gets the destination bound to a name. - */ -MqDestination* mqGetDestination(char* name); - -/** - * Binds a destination to a name. - */ -void mqPutDestination(char* name, MqDestination* desintation); - -#ifdef __cplusplus -} -#endif - -#endif /* __MQ_H */ diff --git a/include/cutils/qsort_r_compat.h b/include/cutils/qsort_r_compat.h deleted file mode 100644 index 479a1ab38..000000000 --- a/include/cutils/qsort_r_compat.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2012 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. - */ - -/* - * Provides a portable version of qsort_r, called qsort_r_compat, which is a - * reentrant variant of qsort that passes a user data pointer to its comparator. - * This implementation follows the BSD parameter convention. - */ - -#ifndef _LIBS_CUTILS_QSORT_R_COMPAT_H -#define _LIBS_CUTILS_QSORT_R_COMPAT_H - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -void qsort_r_compat(void* base, size_t nel, size_t width, void* thunk, - int (*compar)(void*, const void* , const void* )); - -#ifdef __cplusplus -} -#endif - -#endif // _LIBS_CUTILS_QSORT_R_COMPAT_H diff --git a/include/cutils/record_stream.h b/include/cutils/record_stream.h deleted file mode 100644 index bfac87a53..000000000 --- a/include/cutils/record_stream.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2006 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. - */ - -/* - * A simple utility for reading fixed records out of a stream fd - */ - -#ifndef _CUTILS_RECORD_STREAM_H -#define _CUTILS_RECORD_STREAM_H - -#ifdef __cplusplus -extern "C" { -#endif - - -typedef struct RecordStream RecordStream; - -extern RecordStream *record_stream_new(int fd, size_t maxRecordLen); -extern void record_stream_free(RecordStream *p_rs); - -extern int record_stream_get_next (RecordStream *p_rs, void ** p_outRecord, - size_t *p_outRecordLen); - -#ifdef __cplusplus -} -#endif - - -#endif /*_CUTILS_RECORD_STREAM_H*/ - diff --git a/include/cutils/selector.h b/include/cutils/selector.h deleted file mode 100644 index dfc2a9d34..000000000 --- a/include/cutils/selector.h +++ /dev/null @@ -1,130 +0,0 @@ -/* - * 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. - */ - -/** - * Framework for multiplexing I/O. A selector manages a set of file - * descriptors and calls out to user-provided callback functions to read and - * write data and handle errors. - */ - -#ifndef __SELECTOR_H -#define __SELECTOR_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include - -/** - * Manages SelectableFds and invokes their callbacks at appropriate times. - */ -typedef struct Selector Selector; - -/** - * A selectable descriptor. Contains callbacks which the selector can invoke - * before calling select(), when the descriptor is readable or writable, and - * when the descriptor contains out-of-band data. Simply set a callback to - * NULL if you're not interested in that particular event. - * - * A selectable descriptor can indicate that it needs to be removed from the - * selector by setting the 'remove' flag. The selector will remove the - * descriptor at a later time and invoke the onRemove() callback. - * - * SelectableFd fields should only be modified from the selector loop. - */ -typedef struct SelectableFd SelectableFd; -struct SelectableFd { - - /** The file descriptor itself. */ - int fd; - - /** Pointer to user-specific data. Can be NULL. */ - void* data; - - /** - * Set this flag when you no longer wish to be selected. The selector - * will invoke onRemove() when the descriptor is actually removed. - */ - bool remove; - - /** - * Invoked by the selector before calling select. You can set up other - * callbacks from here as necessary. - */ - void (*beforeSelect)(SelectableFd* self); - - /** - * Invoked by the selector when the descriptor has data available. Set to - * NULL to indicate that you're not interested in reading. - */ - void (*onReadable)(SelectableFd* self); - - /** - * Invoked by the selector when the descriptor can accept data. Set to - * NULL to indicate that you're not interested in writing. - */ - void (*onWritable)(SelectableFd* self); - - /** - * Invoked by the selector when out-of-band (OOB) data is available. Set to - * NULL to indicate that you're not interested in OOB data. - */ - void (*onExcept)(SelectableFd* self); - - /** - * Invoked by the selector after the descriptor is removed from the - * selector but before the selector frees the SelectableFd memory. - */ - void (*onRemove)(SelectableFd* self); - - /** - * The selector which selected this fd. Set by the selector itself. - */ - Selector* selector; -}; - -/** - * Creates a new selector. - */ -Selector* selectorCreate(void); - -/** - * Creates a new selectable fd, adds it to the given selector and returns a - * pointer. Outside of 'selector' and 'fd', all fields are set to 0 or NULL - * by default. - * - * The selectable fd should only be modified from the selector loop thread. - */ -SelectableFd* selectorAdd(Selector* selector, int fd); - -/** - * Wakes up the selector even though no I/O events occurred. Use this - * to indicate that you're ready to write to a descriptor. - */ -void selectorWakeUp(Selector* selector); - -/** - * Loops continuously selecting file descriptors and firing events. - * Does not return. - */ -void selectorLoop(Selector* selector); - -#ifdef __cplusplus -} -#endif - -#endif /* __SELECTOR_H */ diff --git a/include/cutils/zygote.h b/include/cutils/zygote.h deleted file mode 100644 index a7480d320..000000000 --- a/include/cutils/zygote.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * 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. - */ - -#ifndef __CUTILS_ZYGOTE_H -#define __CUTILS_ZYGOTE_H - -#ifdef __cplusplus -extern "C" { -#endif - -int zygote_run_oneshot(int sendStdio, int argc, const char **argv); -int zygote_run(int argc, const char **argv); - -#ifdef __cplusplus -} -#endif - -#endif /* __CUTILS_ZYGOTE_H */ diff --git a/libcutils/Android.mk b/libcutils/Android.mk index 503770563..afbc758a2 100644 --- a/libcutils/Android.mk +++ b/libcutils/Android.mk @@ -24,11 +24,9 @@ endif hostSmpFlag := -DANDROID_SMP=0 commonSources := \ - array.c \ hashmap.c \ atomic.c.arm \ native_handle.c \ - buffer.c \ socket_inaddr_any_server.c \ socket_local_client.c \ socket_local_server.c \ @@ -43,10 +41,8 @@ commonSources := \ open_memstream.c \ strdup16to8.c \ strdup8to16.c \ - record_stream.c \ process_name.c \ properties.c \ - qsort_r_compat.c \ threads.c \ sched_policy.c \ iosched_policy.c \ @@ -74,11 +70,8 @@ ifeq ($(WINDOWS_HOST_ONLY),1) uio.c else commonSources += \ - abort_socket.c \ fs.c \ - selector.c \ - multiuser.c \ - zygote.c + multiuser.c endif @@ -118,7 +111,6 @@ LOCAL_SRC_FILES := $(commonSources) \ ashmem-dev.c \ debugger.c \ klog.c \ - mq.c \ partition_utils.c \ qtaguid.c \ trace.c \ diff --git a/libcutils/abort_socket.c b/libcutils/abort_socket.c deleted file mode 100644 index 6a5e5e461..000000000 --- a/libcutils/abort_socket.c +++ /dev/null @@ -1,293 +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 -#include -#include -#include -#include -#include - -#include "cutils/abort_socket.h" - -struct asocket *asocket_init(int fd) { - int abort_fd[2]; - int flags; - struct asocket *s; - - /* set primary socket to non-blocking */ - flags = fcntl(fd, F_GETFL); - if (flags == -1) - return NULL; - if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) - return NULL; - - /* create pipe with non-blocking write, so that asocket_close() cannot - block */ - if (pipe(abort_fd)) - return NULL; - flags = fcntl(abort_fd[1], F_GETFL); - if (flags == -1) - return NULL; - if (fcntl(abort_fd[1], F_SETFL, flags | O_NONBLOCK)) - return NULL; - - s = malloc(sizeof(struct asocket)); - if (!s) - return NULL; - - s->fd = fd; - s->abort_fd[0] = abort_fd[0]; - s->abort_fd[1] = abort_fd[1]; - - return s; -} - -int asocket_connect(struct asocket *s, const struct sockaddr *addr, - socklen_t addrlen, int timeout) { - - int ret; - - do { - ret = connect(s->fd, addr, addrlen); - } while (ret && errno == EINTR); - - if (ret && errno == EINPROGRESS) { - /* ready to poll() */ - socklen_t retlen; - struct pollfd pfd[2]; - - pfd[0].fd = s->fd; - pfd[0].events = POLLOUT; - pfd[0].revents = 0; - pfd[1].fd = s->abort_fd[0]; - pfd[1].events = POLLIN; - pfd[1].revents = 0; - - do { - ret = poll(pfd, 2, timeout); - } while (ret < 0 && errno == EINTR); - - if (ret < 0) - return -1; - else if (ret == 0) { - /* timeout */ - errno = ETIMEDOUT; - return -1; - } - - if (pfd[1].revents) { - /* abort due to asocket_abort() */ - errno = ECANCELED; - return -1; - } - - if (pfd[0].revents) { - if (pfd[0].revents & POLLOUT) { - /* connect call complete, read return code */ - retlen = sizeof(ret); - if (getsockopt(s->fd, SOL_SOCKET, SO_ERROR, &ret, &retlen)) - return -1; - /* got connect() return code */ - if (ret) { - errno = ret; - } - } else { - /* some error event on this fd */ - errno = ECONNABORTED; - return -1; - } - } - } - - return ret; -} - -int asocket_accept(struct asocket *s, struct sockaddr *addr, - socklen_t *addrlen, int timeout) { - - int ret; - struct pollfd pfd[2]; - - pfd[0].fd = s->fd; - pfd[0].events = POLLIN; - pfd[0].revents = 0; - pfd[1].fd = s->abort_fd[0]; - pfd[1].events = POLLIN; - pfd[1].revents = 0; - - do { - ret = poll(pfd, 2, timeout); - } while (ret < 0 && errno == EINTR); - - if (ret < 0) - return -1; - else if (ret == 0) { - /* timeout */ - errno = ETIMEDOUT; - return -1; - } - - if (pfd[1].revents) { - /* abort due to asocket_abort() */ - errno = ECANCELED; - return -1; - } - - if (pfd[0].revents) { - if (pfd[0].revents & POLLIN) { - /* ready to accept() without blocking */ - do { - ret = accept(s->fd, addr, addrlen); - } while (ret < 0 && errno == EINTR); - } else { - /* some error event on this fd */ - errno = ECONNABORTED; - return -1; - } - } - - return ret; -} - -int asocket_read(struct asocket *s, void *buf, size_t count, int timeout) { - int ret; - struct pollfd pfd[2]; - - pfd[0].fd = s->fd; - pfd[0].events = POLLIN; - pfd[0].revents = 0; - pfd[1].fd = s->abort_fd[0]; - pfd[1].events = POLLIN; - pfd[1].revents = 0; - - do { - ret = poll(pfd, 2, timeout); - } while (ret < 0 && errno == EINTR); - - if (ret < 0) - return -1; - else if (ret == 0) { - /* timeout */ - errno = ETIMEDOUT; - return -1; - } - - if (pfd[1].revents) { - /* abort due to asocket_abort() */ - errno = ECANCELED; - return -1; - } - - if (pfd[0].revents) { - if (pfd[0].revents & POLLIN) { - /* ready to read() without blocking */ - do { - ret = read(s->fd, buf, count); - } while (ret < 0 && errno == EINTR); - } else { - /* some error event on this fd */ - errno = ECONNABORTED; - return -1; - } - } - - return ret; -} - -int asocket_write(struct asocket *s, const void *buf, size_t count, - int timeout) { - int ret; - struct pollfd pfd[2]; - - pfd[0].fd = s->fd; - pfd[0].events = POLLOUT; - pfd[0].revents = 0; - pfd[1].fd = s->abort_fd[0]; - pfd[1].events = POLLIN; - pfd[1].revents = 0; - - do { - ret = poll(pfd, 2, timeout); - } while (ret < 0 && errno == EINTR); - - if (ret < 0) - return -1; - else if (ret == 0) { - /* timeout */ - errno = ETIMEDOUT; - return -1; - } - - if (pfd[1].revents) { - /* abort due to asocket_abort() */ - errno = ECANCELED; - return -1; - } - - if (pfd[0].revents) { - if (pfd[0].revents & POLLOUT) { - /* ready to write() without blocking */ - do { - ret = write(s->fd, buf, count); - } while (ret < 0 && errno == EINTR); - } else { - /* some error event on this fd */ - errno = ECONNABORTED; - return -1; - } - } - - return ret; -} - -void asocket_abort(struct asocket *s) { - int ret; - char buf = 0; - - /* Prevent further use of fd, without yet releasing the fd */ - shutdown(s->fd, SHUT_RDWR); - - /* wake up calls blocked at poll() */ - do { - ret = write(s->abort_fd[1], &buf, 1); - } while (ret < 0 && errno == EINTR); -} - -void asocket_destroy(struct asocket *s) { - struct asocket s_copy = *s; - - /* Clients should *not* be using these fd's after calling - asocket_destroy(), but in case they do, set to -1 so they cannot use a - stale fd */ - s->fd = -1; - s->abort_fd[0] = -1; - s->abort_fd[1] = -1; - - /* Call asocket_abort() in case there are still threads blocked on this - socket. Clients should not rely on this behavior - it is racy because we - are about to close() these sockets - clients should instead make sure - all threads are done with the socket before calling asocket_destory(). - */ - asocket_abort(&s_copy); - - /* enough safety checks, close and release memory */ - close(s_copy.abort_fd[1]); - close(s_copy.abort_fd[0]); - close(s_copy.fd); - - free(s); -} diff --git a/libcutils/array.c b/libcutils/array.c deleted file mode 100644 index 55ec055ff..000000000 --- a/libcutils/array.c +++ /dev/null @@ -1,170 +0,0 @@ -/* - * 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. - */ - -#include -#include -#include -#include -#include - -#define INITIAL_CAPACITY (4) -#define MAX_CAPACITY ((int)(UINT_MAX/sizeof(void*))) - -struct Array { - void** contents; - int size; - int capacity; -}; - -Array* arrayCreate() { - return calloc(1, sizeof(struct Array)); -} - -void arrayFree(Array* array) { - assert(array != NULL); - - // Free internal array. - free(array->contents); - - // Free the Array itself. - free(array); -} - -/** Returns 0 if successful, < 0 otherwise.. */ -static int ensureCapacity(Array* array, int capacity) { - int oldCapacity = array->capacity; - if (capacity > oldCapacity) { - int newCapacity = (oldCapacity == 0) ? INITIAL_CAPACITY : oldCapacity; - - // Ensure we're not doing something nasty - if (capacity > MAX_CAPACITY) - return -1; - - // Keep doubling capacity until we surpass necessary capacity. - while (newCapacity < capacity) { - int newCap = newCapacity*2; - // Handle integer overflows - if (newCap < newCapacity || newCap > MAX_CAPACITY) { - newCap = MAX_CAPACITY; - } - newCapacity = newCap; - } - - // Should not happen, but better be safe than sorry - if (newCapacity < 0 || newCapacity > MAX_CAPACITY) - return -1; - - void** newContents; - if (array->contents == NULL) { - // Allocate new array. - newContents = malloc(newCapacity * sizeof(void*)); - if (newContents == NULL) { - return -1; - } - } else { - // Expand existing array. - newContents = realloc(array->contents, sizeof(void*) * newCapacity); - if (newContents == NULL) { - return -1; - } - } - - array->capacity = newCapacity; - array->contents = newContents; - } - - return 0; -} - -int arrayAdd(Array* array, void* pointer) { - assert(array != NULL); - int size = array->size; - int result = ensureCapacity(array, size + 1); - if (result < 0) { - return result; - } - array->contents[size] = pointer; - array->size++; - return 0; -} - -static inline void checkBounds(Array* array, int index) { - assert(array != NULL); - assert(index < array->size); - assert(index >= 0); -} - -void* arrayGet(Array* array, int index) { - checkBounds(array, index); - return array->contents[index]; -} - -void* arrayRemove(Array* array, int index) { - checkBounds(array, index); - - void* pointer = array->contents[index]; - - int newSize = array->size - 1; - - // Shift entries left. - if (index != newSize) { - memmove(array->contents + index, array->contents + index + 1, - (sizeof(void*)) * (newSize - index)); - } - - array->size = newSize; - - return pointer; -} - -void* arraySet(Array* array, int index, void* pointer) { - checkBounds(array, index); - void* old = array->contents[index]; - array->contents[index] = pointer; - return old; -} - -int arraySetSize(Array* array, int newSize) { - assert(array != NULL); - assert(newSize >= 0); - - int oldSize = array->size; - - if (newSize > oldSize) { - // Expand. - int result = ensureCapacity(array, newSize); - if (result < 0) { - return result; - } - - // Zero out new entries. - memset(array->contents + sizeof(void*) * oldSize, 0, - sizeof(void*) * (newSize - oldSize)); - } - - array->size = newSize; - - return 0; -} - -int arraySize(Array* array) { - assert(array != NULL); - return array->size; -} - -const void** arrayUnwrap(Array* array) { - return (const void**)array->contents; -} diff --git a/libcutils/buffer.c b/libcutils/buffer.c deleted file mode 100644 index af99bd728..000000000 --- a/libcutils/buffer.c +++ /dev/null @@ -1,116 +0,0 @@ -/* - * 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. - */ - -#define LOG_TAG "buffer" - -#include -#include -#include -#include - -#include "buffer.h" -#include "loghack.h" - -Buffer* bufferCreate(size_t capacity) { - Buffer* buffer = malloc(sizeof(Buffer)); - if (buffer == NULL) { - return NULL; - } - buffer->capacity = capacity; - buffer->expected = 0; - buffer->data = malloc(capacity); - if (buffer->data == NULL) { - free(buffer); - return NULL; - } - return buffer; -} - -void bufferFree(Buffer* buffer) { - free(buffer->data); - free(buffer); -} - -Buffer* bufferWrap(char* data, size_t capacity, size_t size) { - Buffer* buffer = malloc(sizeof(Buffer)); - if (buffer == NULL) { - return NULL; - } - - buffer->data = data; - buffer->capacity = capacity; - buffer->size = size; - buffer->expected = 0; - return buffer; -} - -int bufferPrepareForRead(Buffer* buffer, size_t expected) { - if (expected > buffer->capacity) { - // Expand buffer. - char* expanded = realloc(buffer->data, expected); - if (expanded == NULL) { - errno = ENOMEM; - return -1; - } - buffer->capacity = expected; - buffer->data = expanded; - } - - buffer->size = 0; - buffer->expected = expected; - return 0; -} - -ssize_t bufferRead(Buffer* buffer, int fd) { - assert(buffer->size < buffer->expected); - - ssize_t bytesRead = read(fd, - buffer->data + buffer->size, - buffer->expected - buffer->size); - - if (bytesRead > 0) { - buffer->size += bytesRead; - return buffer->size; - } - - return bytesRead; -} - -void bufferPrepareForWrite(Buffer* buffer) { - buffer->remaining = buffer->size; -} - -ssize_t bufferWrite(Buffer* buffer, int fd) { - assert(buffer->remaining > 0); - assert(buffer->remaining <= buffer->size); - - ssize_t bytesWritten = write(fd, - buffer->data + buffer->size - buffer->remaining, - buffer->remaining); - - if (bytesWritten >= 0) { - buffer->remaining -= bytesWritten; - - ALOGD("Buffer bytes written: %d", (int) bytesWritten); - ALOGD("Buffer size: %d", (int) buffer->size); - ALOGD("Buffer remaining: %d", (int) buffer->remaining); - - return buffer->remaining; - } - - return bytesWritten; -} - diff --git a/libcutils/buffer.h b/libcutils/buffer.h deleted file mode 100644 index d8bc108ee..000000000 --- a/libcutils/buffer.h +++ /dev/null @@ -1,112 +0,0 @@ -/* - * 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. - */ - -/** - * Byte buffer utilities. - */ - -#ifndef __BUFFER_H -#define __BUFFER_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include - -/** - * Byte buffer of known size. Keeps track of how much data has been read - * into or written out of the buffer. - */ -typedef struct { - /** Buffered data. */ - char* data; - - union { - /** For reading. # of bytes we expect. */ - size_t expected; - - /** For writing. # of bytes to write. */ - size_t remaining; - }; - - /** Actual # of bytes in the buffer. */ - size_t size; - - /** Amount of memory allocated for this buffer. */ - size_t capacity; -} Buffer; - -/** - * Returns true if all data has been read into the buffer. - */ -#define bufferReadComplete(buffer) (buffer->expected == buffer->size) - -/** - * Returns true if the buffer has been completely written. - */ -#define bufferWriteComplete(buffer) (buffer->remaining == 0) - -/** - * Creates a new buffer with the given initial capacity. - */ -Buffer* bufferCreate(size_t initialCapacity); - -/** - * Wraps an existing byte array. - */ -Buffer* bufferWrap(char* data, size_t capacity, size_t size); - -/** - * Frees and its data. - */ -void bufferFree(Buffer* buffer); - -/** - * Prepares buffer to read 'expected' number of bytes. Expands capacity if - * necessary. Returns 0 if successful or -1 if an error occurs allocating - * memory. - */ -int bufferPrepareForRead(Buffer* buffer, size_t expected); - -/** - * Reads some data into a buffer. Returns -1 in case of an error and sets - * errno (see read()). Returns 0 for EOF. Updates buffer->size and returns - * the new size after a succesful read. - * - * Precondition: buffer->size < buffer->expected - */ -ssize_t bufferRead(Buffer* buffer, int fd); - -/** - * Prepares a buffer to be written out. - */ -void bufferPrepareForWrite(Buffer* buffer); - -/** - * Writes data from buffer to the given fd. Returns -1 and sets errno in case - * of an error. Updates buffer->remaining and returns the number of remaining - * bytes to be written after a successful write. - * - * Precondition: buffer->remaining > 0 - */ -ssize_t bufferWrite(Buffer* buffer, int fd); - -#ifdef __cplusplus -} -#endif - -#endif /* __BUFFER_H */ diff --git a/libcutils/mq.c b/libcutils/mq.c deleted file mode 100644 index 6f6740ebb..000000000 --- a/libcutils/mq.c +++ /dev/null @@ -1,1357 +0,0 @@ -/* - * 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. - */ - -#define LOG_TAG "mq" - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include - -#include "loghack.h" -#include "buffer.h" - -/** Number of dead peers to remember. */ -#define PEER_HISTORY (16) - -typedef struct sockaddr SocketAddress; -typedef struct sockaddr_un UnixAddress; - -/** - * Process/user/group ID. We don't use ucred directly because it's only - * available on Linux. - */ -typedef struct { - pid_t pid; - uid_t uid; - gid_t gid; -} Credentials; - -/** Listens for bytes coming from remote peers. */ -typedef void BytesListener(Credentials credentials, char* bytes, size_t size); - -/** Listens for the deaths of remote peers. */ -typedef void DeathListener(pid_t pid); - -/** Types of packets. */ -typedef enum { - /** Request for a connection to another peer. */ - CONNECTION_REQUEST, - - /** A connection to another peer. */ - CONNECTION, - - /** Reports a failed connection attempt. */ - CONNECTION_ERROR, - - /** A generic packet of bytes. */ - BYTES, -} PacketType; - -typedef enum { - /** Reading a packet header. */ - READING_HEADER, - - /** Waiting for a connection from the master. */ - ACCEPTING_CONNECTION, - - /** Reading bytes. */ - READING_BYTES, -} InputState; - -/** A packet header. */ -// TODO: Use custom headers for master->peer, peer->master, peer->peer. -typedef struct { - PacketType type; - union { - /** Packet size. Used for BYTES. */ - size_t size; - - /** Credentials. Used for CONNECTION and CONNECTION_REQUEST. */ - Credentials credentials; - }; -} Header; - -/** A packet which will be sent to a peer. */ -typedef struct OutgoingPacket OutgoingPacket; -struct OutgoingPacket { - /** Packet header. */ - Header header; - - union { - /** Connection to peer. Used with CONNECTION. */ - int socket; - - /** Buffer of bytes. Used with BYTES. */ - Buffer* bytes; - }; - - /** Frees all resources associated with this packet. */ - void (*free)(OutgoingPacket* packet); - - /** Optional context. */ - void* context; - - /** Next packet in the queue. */ - OutgoingPacket* nextPacket; -}; - -/** Represents a remote peer. */ -typedef struct PeerProxy PeerProxy; - -/** Local peer state. You typically have one peer per process. */ -typedef struct { - /** This peer's PID. */ - pid_t pid; - - /** - * Map from pid to peer proxy. The peer has a peer proxy for each remote - * peer it's connected to. - * - * Acquire mutex before use. - */ - Hashmap* peerProxies; - - /** Manages I/O. */ - Selector* selector; - - /** Used to synchronize operations with the selector thread. */ - pthread_mutex_t mutex; - - /** Is this peer the master? */ - bool master; - - /** Peer proxy for the master. */ - PeerProxy* masterProxy; - - /** Listens for packets from remote peers. */ - BytesListener* onBytes; - - /** Listens for deaths of remote peers. */ - DeathListener* onDeath; - - /** Keeps track of recently dead peers. Requires mutex. */ - pid_t deadPeers[PEER_HISTORY]; - size_t deadPeerCursor; -} Peer; - -struct PeerProxy { - /** Credentials of the remote process. */ - Credentials credentials; - - /** Keeps track of data coming in from the remote peer. */ - InputState inputState; - Buffer* inputBuffer; - PeerProxy* connecting; - - /** File descriptor for this peer. */ - SelectableFd* fd; - - /** - * Queue of packets to be written out to the remote peer. - * - * Requires mutex. - */ - // TODO: Limit queue length. - OutgoingPacket* currentPacket; - OutgoingPacket* lastPacket; - - /** Used to write outgoing header. */ - Buffer outgoingHeader; - - /** True if this is the master's proxy. */ - bool master; - - /** Reference back to the local peer. */ - Peer* peer; - - /** - * Used in master only. Maps this peer proxy to other peer proxies to - * which the peer has been connected to. Maps pid to PeerProxy. Helps - * keep track of which connections we've sent to whom. - */ - Hashmap* connections; -}; - -/** Server socket path. */ -static const char* MASTER_PATH = "/master.peer"; - -/** Credentials of the master peer. */ -static const Credentials MASTER_CREDENTIALS = {0, 0, 0}; - -/** Creates a peer proxy and adds it to the peer proxy map. */ -static PeerProxy* peerProxyCreate(Peer* peer, Credentials credentials); - -/** Sets the non-blocking flag on a descriptor. */ -static void setNonBlocking(int fd) { - int flags; - if ((flags = fcntl(fd, F_GETFL, 0)) < 0) { - LOG_ALWAYS_FATAL("fcntl() error: %s", strerror(errno)); - } - if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) { - LOG_ALWAYS_FATAL("fcntl() error: %s", strerror(errno)); - } -} - -/** Closes a fd and logs a warning if the close fails. */ -static void closeWithWarning(int fd) { - int result = close(fd); - if (result == -1) { - ALOGW("close() error: %s", strerror(errno)); - } -} - -/** Hashes pid_t keys. */ -static int pidHash(void* key) { - pid_t* pid = (pid_t*) key; - return (int) (*pid); -} - -/** Compares pid_t keys. */ -static bool pidEquals(void* keyA, void* keyB) { - pid_t* a = (pid_t*) keyA; - pid_t* b = (pid_t*) keyB; - return *a == *b; -} - -/** Gets the master address. Not thread safe. */ -static UnixAddress* getMasterAddress() { - static UnixAddress masterAddress; - static bool initialized = false; - if (initialized == false) { - masterAddress.sun_family = AF_LOCAL; - strcpy(masterAddress.sun_path, MASTER_PATH); - initialized = true; - } - return &masterAddress; -} - -/** Gets exclusive access to the peer for this thread. */ -static void peerLock(Peer* peer) { - pthread_mutex_lock(&peer->mutex); -} - -/** Releases exclusive access to the peer. */ -static void peerUnlock(Peer* peer) { - pthread_mutex_unlock(&peer->mutex); -} - -/** Frees a simple, i.e. header-only, outgoing packet. */ -static void outgoingPacketFree(OutgoingPacket* packet) { - ALOGD("Freeing outgoing packet."); - free(packet); -} - -/** - * Prepare to read a new packet from the peer. - */ -static void peerProxyExpectHeader(PeerProxy* peerProxy) { - peerProxy->inputState = READING_HEADER; - bufferPrepareForRead(peerProxy->inputBuffer, sizeof(Header)); -} - -/** Sets up the buffer for the outgoing header. */ -static void peerProxyPrepareOutgoingHeader(PeerProxy* peerProxy) { - peerProxy->outgoingHeader.data - = (char*) &(peerProxy->currentPacket->header); - peerProxy->outgoingHeader.size = sizeof(Header); - bufferPrepareForWrite(&peerProxy->outgoingHeader); -} - -/** Adds a packet to the end of the queue. Callers must have the mutex. */ -static void peerProxyEnqueueOutgoingPacket(PeerProxy* peerProxy, - OutgoingPacket* newPacket) { - newPacket->nextPacket = NULL; // Just in case. - if (peerProxy->currentPacket == NULL) { - // The queue is empty. - peerProxy->currentPacket = newPacket; - peerProxy->lastPacket = newPacket; - - peerProxyPrepareOutgoingHeader(peerProxy); - } else { - peerProxy->lastPacket->nextPacket = newPacket; - } -} - -/** Takes the peer lock and enqueues the given packet. */ -static void peerProxyLockAndEnqueueOutgoingPacket(PeerProxy* peerProxy, - OutgoingPacket* newPacket) { - Peer* peer = peerProxy->peer; - peerLock(peer); - peerProxyEnqueueOutgoingPacket(peerProxy, newPacket); - peerUnlock(peer); -} - -/** - * Frees current packet and moves to the next one. Returns true if there is - * a next packet or false if the queue is empty. - */ -static bool peerProxyNextPacket(PeerProxy* peerProxy) { - Peer* peer = peerProxy->peer; - peerLock(peer); - - OutgoingPacket* current = peerProxy->currentPacket; - - if (current == NULL) { - // The queue is already empty. - peerUnlock(peer); - return false; - } - - OutgoingPacket* next = current->nextPacket; - peerProxy->currentPacket = next; - current->nextPacket = NULL; - current->free(current); - if (next == NULL) { - // The queue is empty. - peerProxy->lastPacket = NULL; - peerUnlock(peer); - return false; - } else { - peerUnlock(peer); - peerProxyPrepareOutgoingHeader(peerProxy); - - // TODO: Start writing next packet? It would reduce the number of - // system calls, but we could also starve other peers. - return true; - } -} - -/** - * Checks whether a peer died recently. - */ -static bool peerIsDead(Peer* peer, pid_t pid) { - size_t i; - for (i = 0; i < PEER_HISTORY; i++) { - pid_t deadPeer = peer->deadPeers[i]; - if (deadPeer == 0) { - return false; - } - if (deadPeer == pid) { - return true; - } - } - return false; -} - -/** - * Cleans up connection information. - */ -static bool peerProxyRemoveConnection(void* key, void* value, void* context) { - PeerProxy* deadPeer = (PeerProxy*) context; - PeerProxy* otherPeer = (PeerProxy*) value; - hashmapRemove(otherPeer->connections, &(deadPeer->credentials.pid)); - return true; -} - -/** - * Called when the peer dies. - */ -static void peerProxyKill(PeerProxy* peerProxy, bool errnoIsSet) { - if (errnoIsSet) { - ALOGI("Peer %d died. errno: %s", peerProxy->credentials.pid, - strerror(errno)); - } else { - ALOGI("Peer %d died.", peerProxy->credentials.pid); - } - - // If we lost the master, we're up a creek. We can't let this happen. - if (peerProxy->master) { - LOG_ALWAYS_FATAL("Lost connection to master."); - } - - Peer* localPeer = peerProxy->peer; - pid_t pid = peerProxy->credentials.pid; - - peerLock(localPeer); - - // Remember for awhile that the peer died. - localPeer->deadPeers[localPeer->deadPeerCursor] - = peerProxy->credentials.pid; - localPeer->deadPeerCursor++; - if (localPeer->deadPeerCursor == PEER_HISTORY) { - localPeer->deadPeerCursor = 0; - } - - // Remove from peer map. - hashmapRemove(localPeer->peerProxies, &pid); - - // External threads can no longer get to this peer proxy, so we don't - // need the lock anymore. - peerUnlock(localPeer); - - // Remove the fd from the selector. - if (peerProxy->fd != NULL) { - peerProxy->fd->remove = true; - } - - // Clear outgoing packet queue. - while (peerProxyNextPacket(peerProxy)) {} - - bufferFree(peerProxy->inputBuffer); - - // This only applies to the master. - if (peerProxy->connections != NULL) { - // We can't leave these other maps pointing to freed memory. - hashmapForEach(peerProxy->connections, &peerProxyRemoveConnection, - peerProxy); - hashmapFree(peerProxy->connections); - } - - // Invoke death listener. - localPeer->onDeath(pid); - - // Free the peer proxy itself. - free(peerProxy); -} - -static void peerProxyHandleError(PeerProxy* peerProxy, char* functionName) { - if (errno == EINTR) { - // Log interruptions but otherwise ignore them. - ALOGW("%s() interrupted.", functionName); - } else if (errno == EAGAIN) { - ALOGD("EWOULDBLOCK"); - // Ignore. - } else { - ALOGW("Error returned by %s().", functionName); - peerProxyKill(peerProxy, true); - } -} - -/** - * Buffers output sent to a peer. May be called multiple times until the entire - * buffer is filled. Returns true when the buffer is empty. - */ -static bool peerProxyWriteFromBuffer(PeerProxy* peerProxy, Buffer* outgoing) { - ssize_t size = bufferWrite(outgoing, peerProxy->fd->fd); - if (size < 0) { - peerProxyHandleError(peerProxy, "write"); - return false; - } else { - return bufferWriteComplete(outgoing); - } -} - -/** Writes packet bytes to peer. */ -static void peerProxyWriteBytes(PeerProxy* peerProxy) { - Buffer* buffer = peerProxy->currentPacket->bytes; - if (peerProxyWriteFromBuffer(peerProxy, buffer)) { - ALOGD("Bytes written."); - peerProxyNextPacket(peerProxy); - } -} - -/** Sends a socket to the peer. */ -static void peerProxyWriteConnection(PeerProxy* peerProxy) { - int socket = peerProxy->currentPacket->socket; - - // Why does sending and receiving fds have to be such a PITA? - struct msghdr msg; - struct iovec iov[1]; - - union { - struct cmsghdr cm; - char control[CMSG_SPACE(sizeof(int))]; - } control_un; - - struct cmsghdr *cmptr; - - msg.msg_control = control_un.control; - msg.msg_controllen = sizeof(control_un.control); - cmptr = CMSG_FIRSTHDR(&msg); - cmptr->cmsg_len = CMSG_LEN(sizeof(int)); - cmptr->cmsg_level = SOL_SOCKET; - cmptr->cmsg_type = SCM_RIGHTS; - - // Store the socket in the message. - *((int *) CMSG_DATA(cmptr)) = peerProxy->currentPacket->socket; - - msg.msg_name = NULL; - msg.msg_namelen = 0; - iov[0].iov_base = ""; - iov[0].iov_len = 1; - msg.msg_iov = iov; - msg.msg_iovlen = 1; - - ssize_t result = sendmsg(peerProxy->fd->fd, &msg, 0); - - if (result < 0) { - peerProxyHandleError(peerProxy, "sendmsg"); - } else { - // Success. Queue up the next packet. - peerProxyNextPacket(peerProxy); - - } -} - -/** - * Writes some outgoing data. - */ -static void peerProxyWrite(SelectableFd* fd) { - // TODO: Try to write header and body with one system call. - - PeerProxy* peerProxy = (PeerProxy*) fd->data; - OutgoingPacket* current = peerProxy->currentPacket; - - if (current == NULL) { - // We have nothing left to write. - return; - } - - // Write the header. - Buffer* outgoingHeader = &peerProxy->outgoingHeader; - bool headerWritten = bufferWriteComplete(outgoingHeader); - if (!headerWritten) { - ALOGD("Writing header..."); - headerWritten = peerProxyWriteFromBuffer(peerProxy, outgoingHeader); - if (headerWritten) { - ALOGD("Header written."); - } - } - - // Write body. - if (headerWritten) { - PacketType type = current->header.type; - switch (type) { - case CONNECTION: - peerProxyWriteConnection(peerProxy); - break; - case BYTES: - peerProxyWriteBytes(peerProxy); - break; - case CONNECTION_REQUEST: - case CONNECTION_ERROR: - // These packets consist solely of a header. - peerProxyNextPacket(peerProxy); - break; - default: - LOG_ALWAYS_FATAL("Unknown packet type: %d", type); - } - } -} - -/** - * Sets up a peer proxy's fd before we try to select() it. - */ -static void peerProxyBeforeSelect(SelectableFd* fd) { - ALOGD("Before select..."); - - PeerProxy* peerProxy = (PeerProxy*) fd->data; - - peerLock(peerProxy->peer); - bool hasPackets = peerProxy->currentPacket != NULL; - peerUnlock(peerProxy->peer); - - if (hasPackets) { - ALOGD("Packets found. Setting onWritable()."); - - fd->onWritable = &peerProxyWrite; - } else { - // We have nothing to write. - fd->onWritable = NULL; - } -} - -/** Prepare to read bytes from the peer. */ -static void peerProxyExpectBytes(PeerProxy* peerProxy, Header* header) { - ALOGD("Expecting %d bytes.", header->size); - - peerProxy->inputState = READING_BYTES; - if (bufferPrepareForRead(peerProxy->inputBuffer, header->size) == -1) { - ALOGW("Couldn't allocate memory for incoming data. Size: %u", - (unsigned int) header->size); - - // TODO: Ignore the packet and log a warning? - peerProxyKill(peerProxy, false); - } -} - -/** - * Gets a peer proxy for the given ID. Creates a peer proxy if necessary. - * Sends a connection request to the master if desired. - * - * Returns NULL if an error occurs. Sets errno to EHOSTDOWN if the peer died - * or ENOMEM if memory couldn't be allocated. - */ -static PeerProxy* peerProxyGetOrCreate(Peer* peer, pid_t pid, - bool requestConnection) { - if (pid == peer->pid) { - errno = EINVAL; - return NULL; - } - - if (peerIsDead(peer, pid)) { - errno = EHOSTDOWN; - return NULL; - } - - PeerProxy* peerProxy = hashmapGet(peer->peerProxies, &pid); - if (peerProxy != NULL) { - return peerProxy; - } - - // If this is the master peer, we already know about all peers. - if (peer->master) { - errno = EHOSTDOWN; - return NULL; - } - - // Try to create a peer proxy. - Credentials credentials; - credentials.pid = pid; - - // Fake gid and uid until we have the real thing. The real creds are - // filled in by masterProxyExpectConnection(). These fake creds will - // never be exposed to the user. - credentials.uid = 0; - credentials.gid = 0; - - // Make sure we can allocate the connection request packet. - OutgoingPacket* packet = NULL; - if (requestConnection) { - packet = calloc(1, sizeof(OutgoingPacket)); - if (packet == NULL) { - errno = ENOMEM; - return NULL; - } - - packet->header.type = CONNECTION_REQUEST; - packet->header.credentials = credentials; - packet->free = &outgoingPacketFree; - } - - peerProxy = peerProxyCreate(peer, credentials); - if (peerProxy == NULL) { - free(packet); - errno = ENOMEM; - return NULL; - } else { - // Send a connection request to the master. - if (requestConnection) { - PeerProxy* masterProxy = peer->masterProxy; - peerProxyEnqueueOutgoingPacket(masterProxy, packet); - } - - return peerProxy; - } -} - -/** - * Switches the master peer proxy into a state where it's waiting for a - * connection from the master. - */ -static void masterProxyExpectConnection(PeerProxy* masterProxy, - Header* header) { - // TODO: Restructure things so we don't need this check. - // Verify that this really is the master. - if (!masterProxy->master) { - ALOGW("Non-master process %d tried to send us a connection.", - masterProxy->credentials.pid); - // Kill off the evil peer. - peerProxyKill(masterProxy, false); - return; - } - - masterProxy->inputState = ACCEPTING_CONNECTION; - Peer* localPeer = masterProxy->peer; - - // Create a peer proxy so we have somewhere to stash the creds. - // See if we already have a proxy set up. - pid_t pid = header->credentials.pid; - peerLock(localPeer); - PeerProxy* peerProxy = peerProxyGetOrCreate(localPeer, pid, false); - if (peerProxy == NULL) { - ALOGW("Peer proxy creation failed: %s", strerror(errno)); - } else { - // Fill in full credentials. - peerProxy->credentials = header->credentials; - } - peerUnlock(localPeer); - - // Keep track of which peer proxy we're accepting a connection for. - masterProxy->connecting = peerProxy; -} - -/** - * Reads input from a peer process. - */ -static void peerProxyRead(SelectableFd* fd); - -/** Sets up fd callbacks. */ -static void peerProxySetFd(PeerProxy* peerProxy, SelectableFd* fd) { - peerProxy->fd = fd; - fd->data = peerProxy; - fd->onReadable = &peerProxyRead; - fd->beforeSelect = &peerProxyBeforeSelect; - - // Make the socket non-blocking. - setNonBlocking(fd->fd); -} - -/** - * Accepts a connection sent by the master proxy. - */ -static void masterProxyAcceptConnection(PeerProxy* masterProxy) { - struct msghdr msg; - struct iovec iov[1]; - ssize_t size; - char ignored; - int incomingFd; - - // TODO: Reuse code which writes the connection. Who the heck designed - // this API anyway? - union { - struct cmsghdr cm; - char control[CMSG_SPACE(sizeof(int))]; - } control_un; - struct cmsghdr *cmptr; - msg.msg_control = control_un.control; - msg.msg_controllen = sizeof(control_un.control); - - msg.msg_name = NULL; - msg.msg_namelen = 0; - - // We sent 1 byte of data so we can detect EOF. - iov[0].iov_base = &ignored; - iov[0].iov_len = 1; - msg.msg_iov = iov; - msg.msg_iovlen = 1; - - size = recvmsg(masterProxy->fd->fd, &msg, 0); - if (size < 0) { - if (errno == EINTR) { - // Log interruptions but otherwise ignore them. - ALOGW("recvmsg() interrupted."); - return; - } else if (errno == EAGAIN) { - // Keep waiting for the connection. - return; - } else { - LOG_ALWAYS_FATAL("Error reading connection from master: %s", - strerror(errno)); - } - } else if (size == 0) { - // EOF. - LOG_ALWAYS_FATAL("Received EOF from master."); - } - - // Extract fd from message. - if ((cmptr = CMSG_FIRSTHDR(&msg)) != NULL - && cmptr->cmsg_len == CMSG_LEN(sizeof(int))) { - if (cmptr->cmsg_level != SOL_SOCKET) { - LOG_ALWAYS_FATAL("Expected SOL_SOCKET."); - } - if (cmptr->cmsg_type != SCM_RIGHTS) { - LOG_ALWAYS_FATAL("Expected SCM_RIGHTS."); - } - incomingFd = *((int*) CMSG_DATA(cmptr)); - } else { - LOG_ALWAYS_FATAL("Expected fd."); - } - - // The peer proxy this connection is for. - PeerProxy* peerProxy = masterProxy->connecting; - if (peerProxy == NULL) { - ALOGW("Received connection for unknown peer."); - closeWithWarning(incomingFd); - } else { - Peer* peer = masterProxy->peer; - - SelectableFd* selectableFd = selectorAdd(peer->selector, incomingFd); - if (selectableFd == NULL) { - ALOGW("Error adding fd to selector for %d.", - peerProxy->credentials.pid); - closeWithWarning(incomingFd); - peerProxyKill(peerProxy, false); - } - - peerProxySetFd(peerProxy, selectableFd); - } - - peerProxyExpectHeader(masterProxy); -} - -/** - * Frees an outgoing packet containing a connection. - */ -static void outgoingPacketFreeSocket(OutgoingPacket* packet) { - closeWithWarning(packet->socket); - outgoingPacketFree(packet); -} - -/** - * Connects two known peers. - */ -static void masterConnectPeers(PeerProxy* peerA, PeerProxy* peerB) { - int sockets[2]; - int result = socketpair(AF_LOCAL, SOCK_STREAM, 0, sockets); - if (result == -1) { - ALOGW("socketpair() error: %s", strerror(errno)); - // TODO: Send CONNECTION_FAILED packets to peers. - return; - } - - OutgoingPacket* packetA = calloc(1, sizeof(OutgoingPacket)); - OutgoingPacket* packetB = calloc(1, sizeof(OutgoingPacket)); - if (packetA == NULL || packetB == NULL) { - free(packetA); - free(packetB); - ALOGW("malloc() error. Failed to tell process %d that process %d is" - " dead.", peerA->credentials.pid, peerB->credentials.pid); - return; - } - - packetA->header.type = CONNECTION; - packetB->header.type = CONNECTION; - - packetA->header.credentials = peerB->credentials; - packetB->header.credentials = peerA->credentials; - - packetA->socket = sockets[0]; - packetB->socket = sockets[1]; - - packetA->free = &outgoingPacketFreeSocket; - packetB->free = &outgoingPacketFreeSocket; - - peerLock(peerA->peer); - peerProxyEnqueueOutgoingPacket(peerA, packetA); - peerProxyEnqueueOutgoingPacket(peerB, packetB); - peerUnlock(peerA->peer); -} - -/** - * Informs a peer that the peer they're trying to connect to couldn't be - * found. - */ -static void masterReportConnectionError(PeerProxy* peerProxy, - Credentials credentials) { - OutgoingPacket* packet = calloc(1, sizeof(OutgoingPacket)); - if (packet == NULL) { - ALOGW("malloc() error. Failed to tell process %d that process %d is" - " dead.", peerProxy->credentials.pid, credentials.pid); - return; - } - - packet->header.type = CONNECTION_ERROR; - packet->header.credentials = credentials; - packet->free = &outgoingPacketFree; - - peerProxyLockAndEnqueueOutgoingPacket(peerProxy, packet); -} - -/** - * Handles a request to be connected to another peer. - */ -static void masterHandleConnectionRequest(PeerProxy* peerProxy, - Header* header) { - Peer* master = peerProxy->peer; - pid_t targetPid = header->credentials.pid; - if (!hashmapContainsKey(peerProxy->connections, &targetPid)) { - // We haven't connected these peers yet. - PeerProxy* targetPeer - = (PeerProxy*) hashmapGet(master->peerProxies, &targetPid); - if (targetPeer == NULL) { - // Unknown process. - masterReportConnectionError(peerProxy, header->credentials); - } else { - masterConnectPeers(peerProxy, targetPeer); - } - } - - // This packet is complete. Get ready for the next one. - peerProxyExpectHeader(peerProxy); -} - -/** - * The master told us this peer is dead. - */ -static void masterProxyHandleConnectionError(PeerProxy* masterProxy, - Header* header) { - Peer* peer = masterProxy->peer; - - // Look up the peer proxy. - pid_t pid = header->credentials.pid; - PeerProxy* peerProxy = NULL; - peerLock(peer); - peerProxy = hashmapGet(peer->peerProxies, &pid); - peerUnlock(peer); - - if (peerProxy != NULL) { - ALOGI("Couldn't connect to %d.", pid); - peerProxyKill(peerProxy, false); - } else { - ALOGW("Peer proxy for %d not found. This shouldn't happen.", pid); - } - - peerProxyExpectHeader(masterProxy); -} - -/** - * Handles a packet header. - */ -static void peerProxyHandleHeader(PeerProxy* peerProxy, Header* header) { - switch (header->type) { - case CONNECTION_REQUEST: - masterHandleConnectionRequest(peerProxy, header); - break; - case CONNECTION: - masterProxyExpectConnection(peerProxy, header); - break; - case CONNECTION_ERROR: - masterProxyHandleConnectionError(peerProxy, header); - break; - case BYTES: - peerProxyExpectBytes(peerProxy, header); - break; - default: - ALOGW("Invalid packet type from %d: %d", peerProxy->credentials.pid, - header->type); - peerProxyKill(peerProxy, false); - } -} - -/** - * Buffers input sent by peer. May be called multiple times until the entire - * buffer is filled. Returns true when the buffer is full. - */ -static bool peerProxyBufferInput(PeerProxy* peerProxy) { - Buffer* in = peerProxy->inputBuffer; - ssize_t size = bufferRead(in, peerProxy->fd->fd); - if (size < 0) { - peerProxyHandleError(peerProxy, "read"); - return false; - } else if (size == 0) { - // EOF. - ALOGI("EOF"); - peerProxyKill(peerProxy, false); - return false; - } else if (bufferReadComplete(in)) { - // We're done! - return true; - } else { - // Continue reading. - return false; - } -} - -/** - * Reads input from a peer process. - */ -static void peerProxyRead(SelectableFd* fd) { - ALOGD("Reading..."); - PeerProxy* peerProxy = (PeerProxy*) fd->data; - int state = peerProxy->inputState; - Buffer* in = peerProxy->inputBuffer; - switch (state) { - case READING_HEADER: - if (peerProxyBufferInput(peerProxy)) { - ALOGD("Header read."); - // We've read the complete header. - Header* header = (Header*) in->data; - peerProxyHandleHeader(peerProxy, header); - } - break; - case READING_BYTES: - ALOGD("Reading bytes..."); - if (peerProxyBufferInput(peerProxy)) { - ALOGD("Bytes read."); - // We have the complete packet. Notify bytes listener. - peerProxy->peer->onBytes(peerProxy->credentials, - in->data, in->size); - - // Get ready for the next packet. - peerProxyExpectHeader(peerProxy); - } - break; - case ACCEPTING_CONNECTION: - masterProxyAcceptConnection(peerProxy); - break; - default: - LOG_ALWAYS_FATAL("Unknown state: %d", state); - } -} - -static PeerProxy* peerProxyCreate(Peer* peer, Credentials credentials) { - PeerProxy* peerProxy = calloc(1, sizeof(PeerProxy)); - if (peerProxy == NULL) { - return NULL; - } - - peerProxy->inputBuffer = bufferCreate(sizeof(Header)); - if (peerProxy->inputBuffer == NULL) { - free(peerProxy); - return NULL; - } - - peerProxy->peer = peer; - peerProxy->credentials = credentials; - - // Initial state == expecting a header. - peerProxyExpectHeader(peerProxy); - - // Add this proxy to the map. Make sure the key points to the stable memory - // inside of the peer proxy itself. - pid_t* pid = &(peerProxy->credentials.pid); - hashmapPut(peer->peerProxies, pid, peerProxy); - return peerProxy; -} - -/** Accepts a connection to the master peer. */ -static void masterAcceptConnection(SelectableFd* listenerFd) { - // Accept connection. - int socket = accept(listenerFd->fd, NULL, NULL); - if (socket == -1) { - ALOGW("accept() error: %s", strerror(errno)); - return; - } - - ALOGD("Accepted connection as fd %d.", socket); - - // Get credentials. - Credentials credentials; - struct ucred ucredentials; - socklen_t credentialsSize = sizeof(struct ucred); - int result = getsockopt(socket, SOL_SOCKET, SO_PEERCRED, - &ucredentials, &credentialsSize); - // We might want to verify credentialsSize. - if (result == -1) { - ALOGW("getsockopt() error: %s", strerror(errno)); - closeWithWarning(socket); - return; - } - - // Copy values into our own structure so we know we have the types right. - credentials.pid = ucredentials.pid; - credentials.uid = ucredentials.uid; - credentials.gid = ucredentials.gid; - - ALOGI("Accepted connection from process %d.", credentials.pid); - - Peer* masterPeer = (Peer*) listenerFd->data; - - peerLock(masterPeer); - - // Make sure we don't already have a connection from that process. - PeerProxy* peerProxy - = hashmapGet(masterPeer->peerProxies, &credentials.pid); - if (peerProxy != NULL) { - peerUnlock(masterPeer); - ALOGW("Alread connected to process %d.", credentials.pid); - closeWithWarning(socket); - return; - } - - // Add connection to the selector. - SelectableFd* socketFd = selectorAdd(masterPeer->selector, socket); - if (socketFd == NULL) { - peerUnlock(masterPeer); - ALOGW("malloc() failed."); - closeWithWarning(socket); - return; - } - - // Create a peer proxy. - peerProxy = peerProxyCreate(masterPeer, credentials); - peerUnlock(masterPeer); - if (peerProxy == NULL) { - ALOGW("malloc() failed."); - socketFd->remove = true; - closeWithWarning(socket); - } - peerProxy->connections = hashmapCreate(10, &pidHash, &pidEquals); - peerProxySetFd(peerProxy, socketFd); -} - -/** - * Creates the local peer. - */ -static Peer* peerCreate() { - Peer* peer = calloc(1, sizeof(Peer)); - if (peer == NULL) { - LOG_ALWAYS_FATAL("malloc() error."); - } - peer->peerProxies = hashmapCreate(10, &pidHash, &pidEquals); - peer->selector = selectorCreate(); - - pthread_mutexattr_t attributes; - if (pthread_mutexattr_init(&attributes) != 0) { - LOG_ALWAYS_FATAL("pthread_mutexattr_init() error."); - } - if (pthread_mutexattr_settype(&attributes, PTHREAD_MUTEX_RECURSIVE) != 0) { - LOG_ALWAYS_FATAL("pthread_mutexattr_settype() error."); - } - if (pthread_mutex_init(&peer->mutex, &attributes) != 0) { - LOG_ALWAYS_FATAL("pthread_mutex_init() error."); - } - - peer->pid = getpid(); - return peer; -} - -/** The local peer. */ -static Peer* localPeer; - -/** Frees a packet of bytes. */ -static void outgoingPacketFreeBytes(OutgoingPacket* packet) { - ALOGD("Freeing outgoing packet."); - bufferFree(packet->bytes); - free(packet); -} - -/** - * Sends a packet of bytes to a remote peer. Returns 0 on success. - * - * Returns -1 if an error occurs. Sets errno to ENOMEM if memory couldn't be - * allocated. Sets errno to EHOSTDOWN if the peer died recently. Sets errno - * to EINVAL if pid is the same as the local pid. - */ -int peerSendBytes(pid_t pid, const char* bytes, size_t size) { - Peer* peer = localPeer; - assert(peer != NULL); - - OutgoingPacket* packet = calloc(1, sizeof(OutgoingPacket)); - if (packet == NULL) { - errno = ENOMEM; - return -1; - } - - Buffer* copy = bufferCreate(size); - if (copy == NULL) { - free(packet); - errno = ENOMEM; - return -1; - } - - // Copy data. - memcpy(copy->data, bytes, size); - copy->size = size; - - packet->bytes = copy; - packet->header.type = BYTES; - packet->header.size = size; - packet->free = outgoingPacketFreeBytes; - bufferPrepareForWrite(packet->bytes); - - peerLock(peer); - - PeerProxy* peerProxy = peerProxyGetOrCreate(peer, pid, true); - if (peerProxy == NULL) { - // The peer is already dead or we couldn't alloc memory. Either way, - // errno is set. - peerUnlock(peer); - packet->free(packet); - return -1; - } else { - peerProxyEnqueueOutgoingPacket(peerProxy, packet); - peerUnlock(peer); - selectorWakeUp(peer->selector); - return 0; - } -} - -/** Keeps track of how to free shared bytes. */ -typedef struct { - void (*free)(void* context); - void* context; -} SharedBytesFreer; - -/** Frees shared bytes. */ -static void outgoingPacketFreeSharedBytes(OutgoingPacket* packet) { - SharedBytesFreer* sharedBytesFreer - = (SharedBytesFreer*) packet->context; - sharedBytesFreer->free(sharedBytesFreer->context); - free(sharedBytesFreer); - free(packet); -} - -/** - * Sends a packet of bytes to a remote peer without copying the bytes. Calls - * free() with context after the bytes have been sent. - * - * Returns -1 if an error occurs. Sets errno to ENOMEM if memory couldn't be - * allocated. Sets errno to EHOSTDOWN if the peer died recently. Sets errno - * to EINVAL if pid is the same as the local pid. - */ -int peerSendSharedBytes(pid_t pid, char* bytes, size_t size, - void (*free)(void* context), void* context) { - Peer* peer = localPeer; - assert(peer != NULL); - - OutgoingPacket* packet = calloc(1, sizeof(OutgoingPacket)); - if (packet == NULL) { - errno = ENOMEM; - return -1; - } - - Buffer* wrapper = bufferWrap(bytes, size, size); - if (wrapper == NULL) { - free(packet); - errno = ENOMEM; - return -1; - } - - SharedBytesFreer* sharedBytesFreer = malloc(sizeof(SharedBytesFreer)); - if (sharedBytesFreer == NULL) { - free(packet); - free(wrapper); - errno = ENOMEM; - return -1; - } - sharedBytesFreer->free = free; - sharedBytesFreer->context = context; - - packet->bytes = wrapper; - packet->context = sharedBytesFreer; - packet->header.type = BYTES; - packet->header.size = size; - packet->free = &outgoingPacketFreeSharedBytes; - bufferPrepareForWrite(packet->bytes); - - peerLock(peer); - - PeerProxy* peerProxy = peerProxyGetOrCreate(peer, pid, true); - if (peerProxy == NULL) { - // The peer is already dead or we couldn't alloc memory. Either way, - // errno is set. - peerUnlock(peer); - packet->free(packet); - return -1; - } else { - peerProxyEnqueueOutgoingPacket(peerProxy, packet); - peerUnlock(peer); - selectorWakeUp(peer->selector); - return 0; - } -} - -/** - * Starts the master peer. The master peer differs from other peers in that - * it is responsible for connecting the other peers. You can only have one - * master peer. - * - * Goes into an I/O loop and does not return. - */ -void masterPeerInitialize(BytesListener* bytesListener, - DeathListener* deathListener) { - // Create and bind socket. - int listenerSocket = socket(AF_LOCAL, SOCK_STREAM, 0); - if (listenerSocket == -1) { - LOG_ALWAYS_FATAL("socket() error: %s", strerror(errno)); - } - unlink(MASTER_PATH); - int result = bind(listenerSocket, (SocketAddress*) getMasterAddress(), - sizeof(UnixAddress)); - if (result == -1) { - LOG_ALWAYS_FATAL("bind() error: %s", strerror(errno)); - } - - ALOGD("Listener socket: %d", listenerSocket); - - // Queue up to 16 connections. - result = listen(listenerSocket, 16); - if (result != 0) { - LOG_ALWAYS_FATAL("listen() error: %s", strerror(errno)); - } - - // Make socket non-blocking. - setNonBlocking(listenerSocket); - - // Create the peer for this process. Fail if we already have one. - if (localPeer != NULL) { - LOG_ALWAYS_FATAL("Peer is already initialized."); - } - localPeer = peerCreate(); - if (localPeer == NULL) { - LOG_ALWAYS_FATAL("malloc() failed."); - } - localPeer->master = true; - localPeer->onBytes = bytesListener; - localPeer->onDeath = deathListener; - - // Make listener socket selectable. - SelectableFd* listenerFd = selectorAdd(localPeer->selector, listenerSocket); - if (listenerFd == NULL) { - LOG_ALWAYS_FATAL("malloc() error."); - } - listenerFd->data = localPeer; - listenerFd->onReadable = &masterAcceptConnection; -} - -/** - * Starts a local peer. - * - * Goes into an I/O loop and does not return. - */ -void peerInitialize(BytesListener* bytesListener, - DeathListener* deathListener) { - // Connect to master peer. - int masterSocket = socket(AF_LOCAL, SOCK_STREAM, 0); - if (masterSocket == -1) { - LOG_ALWAYS_FATAL("socket() error: %s", strerror(errno)); - } - int result = connect(masterSocket, (SocketAddress*) getMasterAddress(), - sizeof(UnixAddress)); - if (result != 0) { - LOG_ALWAYS_FATAL("connect() error: %s", strerror(errno)); - } - - // Create the peer for this process. Fail if we already have one. - if (localPeer != NULL) { - LOG_ALWAYS_FATAL("Peer is already initialized."); - } - localPeer = peerCreate(); - if (localPeer == NULL) { - LOG_ALWAYS_FATAL("malloc() failed."); - } - localPeer->onBytes = bytesListener; - localPeer->onDeath = deathListener; - - // Make connection selectable. - SelectableFd* masterFd = selectorAdd(localPeer->selector, masterSocket); - if (masterFd == NULL) { - LOG_ALWAYS_FATAL("malloc() error."); - } - - // Create a peer proxy for the master peer. - PeerProxy* masterProxy = peerProxyCreate(localPeer, MASTER_CREDENTIALS); - if (masterProxy == NULL) { - LOG_ALWAYS_FATAL("malloc() error."); - } - peerProxySetFd(masterProxy, masterFd); - masterProxy->master = true; - localPeer->masterProxy = masterProxy; -} - -/** Starts the master peer I/O loop. Doesn't return. */ -void peerLoop() { - assert(localPeer != NULL); - - // Start selector. - selectorLoop(localPeer->selector); -} - diff --git a/libcutils/qsort_r_compat.c b/libcutils/qsort_r_compat.c deleted file mode 100644 index 8971cb5bc..000000000 --- a/libcutils/qsort_r_compat.c +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (C) 2012 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 -#include - -#if HAVE_BSD_QSORT_R - -/* - * BSD qsort_r parameter order is as we have defined here. - */ - -void qsort_r_compat(void* base, size_t nel, size_t width, void* thunk, - int (*compar)(void*, const void* , const void*)) { - qsort_r(base, nel, width, thunk, compar); -} - -#elif HAVE_GNU_QSORT_R - -/* - * GNU qsort_r parameter order places the thunk parameter last. - */ - -struct compar_data { - void* thunk; - int (*compar)(void*, const void* , const void*); -}; - -static int compar_wrapper(const void* a, const void* b, void* data) { - struct compar_data* compar_data = (struct compar_data*)data; - return compar_data->compar(compar_data->thunk, a, b); -} - -void qsort_r_compat(void* base, size_t nel, size_t width, void* thunk, - int (*compar)(void*, const void* , const void*)) { - struct compar_data compar_data; - compar_data.thunk = thunk; - compar_data.compar = compar; - qsort_r(base, nel, width, compar_wrapper, &compar_data); -} - -#else - -/* - * Emulate qsort_r using thread local storage to access the thunk data. - */ - -#include - -static thread_store_t compar_data_key = THREAD_STORE_INITIALIZER; - -struct compar_data { - void* thunk; - int (*compar)(void*, const void* , const void*); -}; - -static int compar_wrapper(const void* a, const void* b) { - struct compar_data* compar_data = (struct compar_data*)thread_store_get(&compar_data_key); - return compar_data->compar(compar_data->thunk, a, b); -} - -void qsort_r_compat(void* base, size_t nel, size_t width, void* thunk, - int (*compar)(void*, const void* , const void*)) { - struct compar_data compar_data; - compar_data.thunk = thunk; - compar_data.compar = compar; - thread_store_set(&compar_data_key, &compar_data, NULL); - qsort(base, nel, width, compar_wrapper); -} - -#endif diff --git a/libcutils/record_stream.c b/libcutils/record_stream.c deleted file mode 100644 index 69949047c..000000000 --- a/libcutils/record_stream.c +++ /dev/null @@ -1,186 +0,0 @@ -/* libs/cutils/record_stream.c -** -** Copyright 2006, 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 -#include -#include -#include -#include -#include -#include -#ifdef HAVE_WINSOCK -#include /* for ntohl */ -#else -#include -#endif - -#define HEADER_SIZE 4 - -struct RecordStream { - int fd; - size_t maxRecordLen; - - unsigned char *buffer; - - unsigned char *unconsumed; - unsigned char *read_end; - unsigned char *buffer_end; -}; - - -extern RecordStream *record_stream_new(int fd, size_t maxRecordLen) -{ - RecordStream *ret; - - assert (maxRecordLen <= 0xffff); - - ret = (RecordStream *)calloc(1, sizeof(RecordStream)); - - ret->fd = fd; - ret->maxRecordLen = maxRecordLen; - ret->buffer = (unsigned char *)malloc (maxRecordLen + HEADER_SIZE); - - ret->unconsumed = ret->buffer; - ret->read_end = ret->buffer; - ret->buffer_end = ret->buffer + maxRecordLen + HEADER_SIZE; - - return ret; -} - - -extern void record_stream_free(RecordStream *rs) -{ - free(rs->buffer); - free(rs); -} - - -/* returns NULL; if there isn't a full record in the buffer */ -static unsigned char * getEndOfRecord (unsigned char *p_begin, - unsigned char *p_end) -{ - size_t len; - unsigned char * p_ret; - - if (p_end < p_begin + HEADER_SIZE) { - return NULL; - } - - //First four bytes are length - len = ntohl(*((uint32_t *)p_begin)); - - p_ret = p_begin + HEADER_SIZE + len; - - if (p_end < p_ret) { - return NULL; - } - - return p_ret; -} - -static void *getNextRecord (RecordStream *p_rs, size_t *p_outRecordLen) -{ - unsigned char *record_start, *record_end; - - record_end = getEndOfRecord (p_rs->unconsumed, p_rs->read_end); - - if (record_end != NULL) { - /* one full line in the buffer */ - record_start = p_rs->unconsumed + HEADER_SIZE; - p_rs->unconsumed = record_end; - - *p_outRecordLen = record_end - record_start; - - return record_start; - } - - return NULL; -} - -/** - * Reads the next record from stream fd - * Records are prefixed by a 16-bit big endian length value - * Records may not be larger than maxRecordLen - * - * Doesn't guard against EINTR - * - * p_outRecord and p_outRecordLen may not be NULL - * - * Return 0 on success, -1 on fail - * Returns 0 with *p_outRecord set to NULL on end of stream - * Returns -1 / errno = EAGAIN if it needs to read again - */ -int record_stream_get_next (RecordStream *p_rs, void ** p_outRecord, - size_t *p_outRecordLen) -{ - void *ret; - - ssize_t countRead; - - /* is there one record already in the buffer? */ - ret = getNextRecord (p_rs, p_outRecordLen); - - if (ret != NULL) { - *p_outRecord = ret; - return 0; - } - - // if the buffer is full and we don't have a full record - if (p_rs->unconsumed == p_rs->buffer - && p_rs->read_end == p_rs->buffer_end - ) { - // this should never happen - //ALOGE("max record length exceeded\n"); - assert (0); - errno = EFBIG; - return -1; - } - - if (p_rs->unconsumed != p_rs->buffer) { - // move remainder to the beginning of the buffer - size_t toMove; - - toMove = p_rs->read_end - p_rs->unconsumed; - if (toMove) { - memmove(p_rs->buffer, p_rs->unconsumed, toMove); - } - - p_rs->read_end = p_rs->buffer + toMove; - p_rs->unconsumed = p_rs->buffer; - } - - countRead = read (p_rs->fd, p_rs->read_end, p_rs->buffer_end - p_rs->read_end); - - if (countRead <= 0) { - /* note: end-of-stream drops through here too */ - *p_outRecord = NULL; - return countRead; - } - - p_rs->read_end += countRead; - - ret = getNextRecord (p_rs, p_outRecordLen); - - if (ret == NULL) { - /* not enough of a buffer to for a whole command */ - errno = EAGAIN; - return -1; - } - - *p_outRecord = ret; - return 0; -} diff --git a/libcutils/selector.c b/libcutils/selector.c deleted file mode 100644 index 3776bbbc5..000000000 --- a/libcutils/selector.c +++ /dev/null @@ -1,263 +0,0 @@ -/* - * 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. - */ - -#define LOG_TAG "selector" - -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "loghack.h" - -struct Selector { - Array* selectableFds; - bool looping; - fd_set readFds; - fd_set writeFds; - fd_set exceptFds; - int maxFd; - int wakeupPipe[2]; - SelectableFd* wakeupFd; - - bool inSelect; - pthread_mutex_t inSelectLock; -}; - -/** Reads and ignores wake up data. */ -static void eatWakeupData(SelectableFd* wakeupFd) { - static char garbage[64]; - if (read(wakeupFd->fd, garbage, sizeof(garbage)) < 0) { - if (errno == EINTR) { - ALOGI("read() interrupted."); - } else { - LOG_ALWAYS_FATAL("This should never happen: %s", strerror(errno)); - } - } -} - -static void setInSelect(Selector* selector, bool inSelect) { - pthread_mutex_lock(&selector->inSelectLock); - selector->inSelect = inSelect; - pthread_mutex_unlock(&selector->inSelectLock); -} - -static bool isInSelect(Selector* selector) { - pthread_mutex_lock(&selector->inSelectLock); - bool inSelect = selector->inSelect; - pthread_mutex_unlock(&selector->inSelectLock); - return inSelect; -} - -void selectorWakeUp(Selector* selector) { - if (!isInSelect(selector)) { - // We only need to write wake-up data if we're blocked in select(). - return; - } - - static char garbage[1]; - if (write(selector->wakeupPipe[1], garbage, sizeof(garbage)) < 0) { - if (errno == EINTR) { - ALOGI("read() interrupted."); - } else { - LOG_ALWAYS_FATAL("This should never happen: %s", strerror(errno)); - } - } -} - -Selector* selectorCreate(void) { - Selector* selector = calloc(1, sizeof(Selector)); - if (selector == NULL) { - LOG_ALWAYS_FATAL("malloc() error."); - } - selector->selectableFds = arrayCreate(); - - // Set up wake-up pipe. - if (pipe(selector->wakeupPipe) < 0) { - LOG_ALWAYS_FATAL("pipe() error: %s", strerror(errno)); - } - - ALOGD("Wakeup fd: %d", selector->wakeupPipe[0]); - - SelectableFd* wakeupFd = selectorAdd(selector, selector->wakeupPipe[0]); - if (wakeupFd == NULL) { - LOG_ALWAYS_FATAL("malloc() error."); - } - wakeupFd->onReadable = &eatWakeupData; - - pthread_mutex_init(&selector->inSelectLock, NULL); - - return selector; -} - -SelectableFd* selectorAdd(Selector* selector, int fd) { - assert(selector != NULL); - - SelectableFd* selectableFd = calloc(1, sizeof(SelectableFd)); - if (selectableFd != NULL) { - selectableFd->selector = selector; - selectableFd->fd = fd; - - arrayAdd(selector->selectableFds, selectableFd); - } - - return selectableFd; -} - -/** - * Adds an fd to the given set if the callback is non-null. Returns true - * if the fd was added. - */ -static inline bool maybeAdd(SelectableFd* selectableFd, - void (*callback)(SelectableFd*), fd_set* fdSet) { - if (callback != NULL) { - FD_SET(selectableFd->fd, fdSet); - return true; - } - return false; -} - -/** - * Removes stale file descriptors and initializes file descriptor sets. - */ -static void prepareForSelect(Selector* selector) { - fd_set* exceptFds = &selector->exceptFds; - fd_set* readFds = &selector->readFds; - fd_set* writeFds = &selector->writeFds; - - FD_ZERO(exceptFds); - FD_ZERO(readFds); - FD_ZERO(writeFds); - - Array* selectableFds = selector->selectableFds; - int i = 0; - selector->maxFd = 0; - int size = arraySize(selectableFds); - while (i < size) { - SelectableFd* selectableFd = arrayGet(selectableFds, i); - if (selectableFd->remove) { - // This descriptor should be removed. - arrayRemove(selectableFds, i); - size--; - if (selectableFd->onRemove != NULL) { - selectableFd->onRemove(selectableFd); - } - free(selectableFd); - } else { - if (selectableFd->beforeSelect != NULL) { - selectableFd->beforeSelect(selectableFd); - } - - bool inSet = false; - if (maybeAdd(selectableFd, selectableFd->onExcept, exceptFds)) { - ALOGD("Selecting fd %d for writing...", selectableFd->fd); - inSet = true; - } - if (maybeAdd(selectableFd, selectableFd->onReadable, readFds)) { - ALOGD("Selecting fd %d for reading...", selectableFd->fd); - inSet = true; - } - if (maybeAdd(selectableFd, selectableFd->onWritable, writeFds)) { - inSet = true; - } - - if (inSet) { - // If the fd is in a set, check it against max. - int fd = selectableFd->fd; - if (fd > selector->maxFd) { - selector->maxFd = fd; - } - } - - // Move to next descriptor. - i++; - } - } -} - -/** - * Invokes a callback if the callback is non-null and the fd is in the given - * set. - */ -static inline void maybeInvoke(SelectableFd* selectableFd, - void (*callback)(SelectableFd*), fd_set* fdSet) { - if (callback != NULL && !selectableFd->remove && - FD_ISSET(selectableFd->fd, fdSet)) { - ALOGD("Selected fd %d.", selectableFd->fd); - callback(selectableFd); - } -} - -/** - * Notifies user if file descriptors are readable or writable, or if - * out-of-band data is present. - */ -static void fireEvents(Selector* selector) { - Array* selectableFds = selector->selectableFds; - int size = arraySize(selectableFds); - int i; - for (i = 0; i < size; i++) { - SelectableFd* selectableFd = arrayGet(selectableFds, i); - maybeInvoke(selectableFd, selectableFd->onExcept, - &selector->exceptFds); - maybeInvoke(selectableFd, selectableFd->onReadable, - &selector->readFds); - maybeInvoke(selectableFd, selectableFd->onWritable, - &selector->writeFds); - } -} - -void selectorLoop(Selector* selector) { - // Make sure we're not already looping. - if (selector->looping) { - LOG_ALWAYS_FATAL("Already looping."); - } - selector->looping = true; - - while (true) { - setInSelect(selector, true); - - prepareForSelect(selector); - - ALOGD("Entering select()."); - - // Select file descriptors. - int result = select(selector->maxFd + 1, &selector->readFds, - &selector->writeFds, &selector->exceptFds, NULL); - - ALOGD("Exiting select()."); - - setInSelect(selector, false); - - if (result == -1) { - // Abort on everything except EINTR. - if (errno == EINTR) { - ALOGI("select() interrupted."); - } else { - LOG_ALWAYS_FATAL("select() error: %s", - strerror(errno)); - } - } else if (result > 0) { - fireEvents(selector); - } - } -} diff --git a/libcutils/zygote.c b/libcutils/zygote.c deleted file mode 100644 index 37236e8a6..000000000 --- a/libcutils/zygote.c +++ /dev/null @@ -1,229 +0,0 @@ -/* - * 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. - */ - -#define LOG_TAG "Zygote" - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define ZYGOTE_SOCKET "zygote" - -#define ZYGOTE_RETRY_COUNT 1000 -#define ZYGOTE_RETRY_MILLIS 500 - -static void replace_nl(char *str); - -/* - * If sendStdio is non-zero, the current process's stdio file descriptors - * will be sent and inherited by the spawned process. - */ -static int send_request(int fd, int sendStdio, int argc, const char **argv) -{ -#ifndef HAVE_ANDROID_OS - // not supported on simulator targets - //ALOGE("zygote_* not supported on simulator targets"); - return -1; -#else /* HAVE_ANDROID_OS */ - uint32_t pid; - int i; - struct iovec ivs[2]; - struct msghdr msg; - char argc_buffer[12]; - const char *newline_string = "\n"; - struct cmsghdr *cmsg; - char msgbuf[CMSG_SPACE(sizeof(int) * 3)]; - int *cmsg_payload; - ssize_t ret; - - memset(&msg, 0, sizeof(msg)); - memset(&ivs, 0, sizeof(ivs)); - - // First line is arg count - snprintf(argc_buffer, sizeof(argc_buffer), "%d\n", argc); - - ivs[0].iov_base = argc_buffer; - ivs[0].iov_len = strlen(argc_buffer); - - msg.msg_iov = ivs; - msg.msg_iovlen = 1; - - if (sendStdio != 0) { - // Pass the file descriptors with the first write - msg.msg_control = msgbuf; - msg.msg_controllen = sizeof msgbuf; - - cmsg = CMSG_FIRSTHDR(&msg); - - cmsg->cmsg_len = CMSG_LEN(3 * sizeof(int)); - cmsg->cmsg_level = SOL_SOCKET; - cmsg->cmsg_type = SCM_RIGHTS; - - cmsg_payload = (int *)CMSG_DATA(cmsg); - cmsg_payload[0] = STDIN_FILENO; - cmsg_payload[1] = STDOUT_FILENO; - cmsg_payload[2] = STDERR_FILENO; - } - - do { - ret = sendmsg(fd, &msg, MSG_NOSIGNAL); - } while (ret < 0 && errno == EINTR); - - if (ret < 0) { - return -1; - } - - // Only send the fd's once - msg.msg_control = NULL; - msg.msg_controllen = 0; - - // replace any newlines with spaces and send the args - for (i = 0; i < argc; i++) { - char *tofree = NULL; - const char *toprint; - - toprint = argv[i]; - - if (strchr(toprint, '\n') != NULL) { - tofree = strdup(toprint); - toprint = tofree; - replace_nl(tofree); - } - - ivs[0].iov_base = (char *)toprint; - ivs[0].iov_len = strlen(toprint); - ivs[1].iov_base = (char *)newline_string; - ivs[1].iov_len = 1; - - msg.msg_iovlen = 2; - - do { - ret = sendmsg(fd, &msg, MSG_NOSIGNAL); - } while (ret < 0 && errno == EINTR); - - if (tofree != NULL) { - free(tofree); - } - - if (ret < 0) { - return -1; - } - } - - // Read the pid, as a 4-byte network-order integer - - ivs[0].iov_base = &pid; - ivs[0].iov_len = sizeof(pid); - msg.msg_iovlen = 1; - - do { - do { - ret = recvmsg(fd, &msg, MSG_NOSIGNAL | MSG_WAITALL); - } while (ret < 0 && errno == EINTR); - - if (ret < 0) { - return -1; - } - - ivs[0].iov_len -= ret; - ivs[0].iov_base += ret; - } while (ivs[0].iov_len > 0); - - pid = ntohl(pid); - - return pid; -#endif /* HAVE_ANDROID_OS */ -} - -/** - * Spawns a new dalvik instance via the Zygote process. The non-zygote - * arguments are passed to com.android.internal.os.RuntimeInit(). The - * first non-option argument should be a class name in the system class path. - * - * The arg list may start with zygote params such as --set-uid. - * - * If sendStdio is non-zero, the current process's stdio file descriptors - * will be sent and inherited by the spawned process. - * - * The pid of the child process is returned, or -1 if an error was - * encountered. - * - * zygote_run_oneshot waits up to ZYGOTE_RETRY_COUNT * - * ZYGOTE_RETRY_MILLIS for the zygote socket to be available. - */ -int zygote_run_oneshot(int sendStdio, int argc, const char **argv) -{ - int fd = -1; - int err; - int i; - int retries; - int pid; - const char **newargv = argv; - const int newargc = argc; - - for (retries = 0; (fd < 0) && (retries < ZYGOTE_RETRY_COUNT); retries++) { - if (retries > 0) { - struct timespec ts; - - memset(&ts, 0, sizeof(ts)); - ts.tv_nsec = ZYGOTE_RETRY_MILLIS * 1000 * 1000; - - do { - err = nanosleep (&ts, &ts); - } while (err < 0 && errno == EINTR); - } - fd = socket_local_client(ZYGOTE_SOCKET, AF_LOCAL, - ANDROID_SOCKET_NAMESPACE_RESERVED); - } - - if (fd < 0) { - return -1; - } - - pid = send_request(fd, 0, newargc, newargv); - - do { - err = close(fd); - } while (err < 0 && errno == EINTR); - - return pid; -} - -/** - * Replaces all occurrances of newline with space. - */ -static void replace_nl(char *str) -{ - for(; *str; str++) { - if (*str == '\n') { - *str = ' '; - } - } -} - - - From 8577e8649062953a59e0ac652b5ac16c96c1c1f4 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Thu, 9 May 2013 14:50:42 -0700 Subject: [PATCH 017/541] surfaceflinger is now always running in its own process Change-Id: I1ae63347a8b6d406306ed894665674dd726b7d51 --- rootdir/init.rc | 3 --- 1 file changed, 3 deletions(-) diff --git a/rootdir/init.rc b/rootdir/init.rc index 654fd3db8..03877b434 100644 --- a/rootdir/init.rc +++ b/rootdir/init.rc @@ -373,9 +373,6 @@ on boot setprop net.tcp.buffersize.gprs 4092,8760,11680,4096,8760,11680 setprop net.tcp.buffersize.evdo 4094,87380,262144,4096,16384,262144 -# Set this property so surfaceflinger is not started by system_init - setprop system_init.startsurfaceflinger 0 - class_start core class_start main From ddc50e67e2ef33f20e76fca1c3fc04474cd9df9d Mon Sep 17 00:00:00 2001 From: Ben Cheng Date: Tue, 21 May 2013 10:55:03 -0700 Subject: [PATCH 018/541] Format stack trace PC offset to 8 digits with leading zeros. BUG: 9067999 Change-Id: Ib5bbf2d5c166dc27e35386d589536eeb6b078c6a --- libcorkscrew/backtrace.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/libcorkscrew/backtrace.c b/libcorkscrew/backtrace.c index b365e5bdd..f1dd61d74 100644 --- a/libcorkscrew/backtrace.c +++ b/libcorkscrew/backtrace.c @@ -319,16 +319,17 @@ void format_backtrace_line(unsigned frameNumber, const backtrace_frame_t* frame if (symbolName) { uint32_t pc_offset = symbol->relative_pc - symbol->relative_symbol_addr; if (pc_offset) { - snprintf(buffer, bufferSize, "#%02u pc %p %.*s (%.*s+%u)", - frameNumber, (void*) symbol->relative_pc, fieldWidth, mapName, - fieldWidth, symbolName, pc_offset); + snprintf(buffer, bufferSize, "#%02u pc %08x %.*s (%.*s+%u)", + frameNumber, (unsigned int) symbol->relative_pc, + fieldWidth, mapName, fieldWidth, symbolName, pc_offset); } else { - snprintf(buffer, bufferSize, "#%02u pc %p %.*s (%.*s)", - frameNumber, (void*) symbol->relative_pc, fieldWidth, mapName, - fieldWidth, symbolName); + snprintf(buffer, bufferSize, "#%02u pc %08x %.*s (%.*s)", + frameNumber, (unsigned int) symbol->relative_pc, + fieldWidth, mapName, fieldWidth, symbolName); } } else { - snprintf(buffer, bufferSize, "#%02u pc %p %.*s", - frameNumber, (void*) symbol->relative_pc, fieldWidth, mapName); + snprintf(buffer, bufferSize, "#%02u pc %08x %.*s", + frameNumber, (unsigned int) symbol->relative_pc, + fieldWidth, mapName); } } From 893a4a47e8c9290128b9254af0246e36f821c260 Mon Sep 17 00:00:00 2001 From: Nick Kralevich Date: Thu, 23 May 2013 09:54:13 -0700 Subject: [PATCH 019/541] adb: Only use properties on device builds When building for the host, don't make reference to property_get / property_set. I'm in the process of removing host side support for properties. Change-Id: I691c5872b5fd538e78bc38a3fe72574cdc7f43c3 --- adb/adb.c | 1 + adb/services.c | 1 + adb/sysdeps.h | 1 - adb/transport_local.c | 3 +++ 4 files changed, 5 insertions(+), 1 deletion(-) diff --git a/adb/adb.c b/adb/adb.c index 187f51a8d..72b7484af 100644 --- a/adb/adb.c +++ b/adb/adb.c @@ -34,6 +34,7 @@ #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) #if !ADB_HOST +#include #include #include #include diff --git a/adb/services.c b/adb/services.c index d2d428a09..f0d587817 100644 --- a/adb/services.c +++ b/adb/services.c @@ -35,6 +35,7 @@ # endif #else # include +# include #endif typedef struct stinfo stinfo; diff --git a/adb/sysdeps.h b/adb/sysdeps.h index 0252ef3b8..4033b7220 100644 --- a/adb/sysdeps.h +++ b/adb/sysdeps.h @@ -261,7 +261,6 @@ extern char* adb_strtok_r(char *str, const char *delim, char **saveptr); #include "fdevent.h" #include -#include #include #include #include diff --git a/adb/transport_local.c b/adb/transport_local.c index 96a24ba50..1cfa24d7f 100644 --- a/adb/transport_local.c +++ b/adb/transport_local.c @@ -21,6 +21,9 @@ #include "sysdeps.h" #include +#if !ADB_HOST +#include +#endif #define TRACE_TAG TRACE_TRANSPORT #include "adb.h" From b39e3a8a4fa9b4121a9ceb45cacce843b32b1a65 Mon Sep 17 00:00:00 2001 From: Nick Kralevich Date: Thu, 23 May 2013 09:54:47 -0700 Subject: [PATCH 020/541] libcutils: Don't build host property support Only build property support for the device, not for the host. Host side property support is being removed, as it was only really used for the simulator. process_name.c: When building this for the host, don't reference properties. Change-Id: Idcea5ad52a85e47eef17a381cb0601657efbdf13 --- libcutils/Android.mk | 2 +- libcutils/process_name.c | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/libcutils/Android.mk b/libcutils/Android.mk index afbc758a2..1c3185e9d 100644 --- a/libcutils/Android.mk +++ b/libcutils/Android.mk @@ -42,7 +42,6 @@ commonSources := \ strdup16to8.c \ strdup8to16.c \ process_name.c \ - properties.c \ threads.c \ sched_policy.c \ iosched_policy.c \ @@ -112,6 +111,7 @@ LOCAL_SRC_FILES := $(commonSources) \ debugger.c \ klog.c \ partition_utils.c \ + properties.c \ qtaguid.c \ trace.c \ uevent.c diff --git a/libcutils/process_name.c b/libcutils/process_name.c index bda9d0827..a6ab951b7 100644 --- a/libcutils/process_name.c +++ b/libcutils/process_name.c @@ -17,7 +17,9 @@ #include #include #include +#ifdef HAVE_ANDROID_OS #include +#endif #include #include #include @@ -33,7 +35,9 @@ static const char* process_name = "unknown"; static int running_in_emulator = -1; void set_process_name(const char* new_name) { +#ifdef HAVE_ANDROID_OS char propBuf[PROPERTY_VALUE_MAX]; +#endif if (new_name == NULL) { return; @@ -53,6 +57,7 @@ void set_process_name(const char* new_name) { } #endif +#ifdef HAVE_ANDROID_OS // If we know we are not running in the emulator, then return. if (running_in_emulator == 0) { return; @@ -82,6 +87,7 @@ void set_process_name(const char* new_name) { return; write(fd, process_name, strlen(process_name) + 1); close(fd); +#endif } const char* get_process_name(void) { From 53df3ade9bacce57099bb9eeb2a34bb5945d463e Mon Sep 17 00:00:00 2001 From: Nick Kralevich Date: Thu, 23 May 2013 09:55:41 -0700 Subject: [PATCH 021/541] properties.h: avoid duplicating constant values Use the constant value from sys/system_properties.h, rather than having two copies of this constant in two different places. This partially addresses the TODO in the header file. Change-Id: Ic0b098cb8caf562ed2a25a0cf8cd412ba2e06884 --- include/cutils/properties.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/include/cutils/properties.h b/include/cutils/properties.h index 25fd67ae0..7e4ed9316 100644 --- a/include/cutils/properties.h +++ b/include/cutils/properties.h @@ -17,6 +17,8 @@ #ifndef __CUTILS_PROPERTIES_H #define __CUTILS_PROPERTIES_H +#include + #ifdef __cplusplus extern "C" { #endif @@ -28,8 +30,8 @@ extern "C" { ** WARNING: system/bionic/include/sys/system_properties.h also defines ** these, but with different names. (TODO: fix that) */ -#define PROPERTY_KEY_MAX 32 -#define PROPERTY_VALUE_MAX 92 +#define PROPERTY_KEY_MAX PROP_NAME_MAX +#define PROPERTY_VALUE_MAX PROP_VALUE_MAX /* property_get: returns the length of the value which will never be ** greater than PROPERTY_VALUE_MAX - 1 and will always be zero terminated. From 0e54ec825e6eae234a486c2049039fb6986a39b8 Mon Sep 17 00:00:00 2001 From: Nick Kralevich Date: Thu, 23 May 2013 10:01:42 -0700 Subject: [PATCH 022/541] property_get: do argument checking. Try to verify, at compile time, that the supplied value buffer is large enough. Change-Id: I91fa560d3ceff87609194269356ac634bdbf2ede --- include/cutils/properties.h | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/include/cutils/properties.h b/include/cutils/properties.h index 7e4ed9316..228b73ec3 100644 --- a/include/cutils/properties.h +++ b/include/cutils/properties.h @@ -17,6 +17,8 @@ #ifndef __CUTILS_PROPERTIES_H #define __CUTILS_PROPERTIES_H +#include +#include #include #ifdef __cplusplus @@ -48,6 +50,23 @@ int property_set(const char *key, const char *value); int property_list(void (*propfn)(const char *key, const char *value, void *cookie), void *cookie); +#if defined(__BIONIC_FORTIFY) + +extern int __property_get_real(const char *, char *, const char *) + __asm__(__USER_LABEL_PREFIX__ "property_get"); +extern void __property_get_too_small_error() + __attribute__((__error__("property_get() called with too small of a buffer"))); + +__BIONIC_FORTIFY_INLINE +int property_get(const char *key, char *value, const char *default_value) { + size_t bos = __bos(value); + if (bos < PROPERTY_VALUE_MAX) { + __property_get_too_small_error(); + } + return __property_get_real(key, value, default_value); +} + +#endif #ifdef HAVE_SYSTEM_PROPERTY_SERVER /* From d8b11c1ddd071e91f2ff8121be4f91e84cb9729a Mon Sep 17 00:00:00 2001 From: Tim Murray Date: Thu, 23 May 2013 13:46:12 -0700 Subject: [PATCH 023/541] Add ATRACE_TAG_RS. Change-Id: Iaff497a1cb04a797441b5bb47b30ad6f09db8e95 --- include/cutils/trace.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/cutils/trace.h b/include/cutils/trace.h index 29034cab5..fbb607780 100644 --- a/include/cutils/trace.h +++ b/include/cutils/trace.h @@ -66,7 +66,8 @@ __BEGIN_DECLS #define ATRACE_TAG_APP (1<<12) #define ATRACE_TAG_RESOURCES (1<<13) #define ATRACE_TAG_DALVIK (1<<14) -#define ATRACE_TAG_LAST ATRACE_TAG_DALVIK +#define ATRACE_TAG_RS (1<<15) +#define ATRACE_TAG_LAST ATRACE_TAG_RS // Reserved for initialization. #define ATRACE_TAG_NOT_READY (1LL<<63) From 960ac434a4960b0524993544949ef892c08339ed Mon Sep 17 00:00:00 2001 From: Nick Kralevich Date: Mon, 3 Jun 2013 12:10:30 -0700 Subject: [PATCH 024/541] fix strncat misuse The third argument of strncat() tells how much data to append from src, not the total size of the buffer. Change uses of strncat() to strlcat(), which has the buffer overflow protection behavior intended by the original author. This fixes the following compiler warning: In function 'strncat', inlined from 'print_type' at system/core/toolbox/lsof.c:76:12: bionic/libc/include/string.h:142:5: warning: call to __builtin___strncat_chk might overflow destination buffer [enabled by default] Change-Id: Id69edc641de3cb87d1867a409cd57b04b12f90a7 --- toolbox/lsof.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/toolbox/lsof.c b/toolbox/lsof.c index 376a642e7..113c120fc 100644 --- a/toolbox/lsof.c +++ b/toolbox/lsof.c @@ -54,7 +54,7 @@ struct pid_info_t { ssize_t parent_length; }; -void print_header() +static void print_header() { printf("%-9s %5s %10s %4s %9s %18s %9s %10s %s\n", "COMMAND", @@ -68,12 +68,12 @@ void print_header() "NAME"); } -void print_type(char *type, struct pid_info_t* info) +static void print_type(char *type, struct pid_info_t* info) { static ssize_t link_dest_size; static char link_dest[PATH_MAX]; - strncat(info->path, type, sizeof(info->path)); + strlcat(info->path, type, sizeof(info->path)); if ((link_dest_size = readlink(info->path, link_dest, sizeof(link_dest)-1)) < 0) { if (errno == ENOENT) goto out; @@ -96,7 +96,7 @@ out: } // Prints out all file that have been memory mapped -void print_maps(struct pid_info_t* info) +static void print_maps(struct pid_info_t* info) { FILE *maps; char buffer[PATH_MAX + 100]; @@ -107,7 +107,7 @@ void print_maps(struct pid_info_t* info) long int inode; char file[PATH_MAX]; - strncat(info->path, "maps", sizeof(info->path)); + strlcat(info->path, "maps", sizeof(info->path)); maps = fopen(info->path, "r"); if (!maps) @@ -131,10 +131,10 @@ out: } // Prints out all open file descriptors -void print_fds(struct pid_info_t* info) +static void print_fds(struct pid_info_t* info) { static char* fd_path = "fd/"; - strncat(info->path, fd_path, sizeof(info->path)); + strlcat(info->path, fd_path, sizeof(info->path)); int previous_length = info->parent_length; info->parent_length += strlen(fd_path); @@ -163,7 +163,7 @@ out: info->path[info->parent_length] = '\0'; } -void lsof_dumpinfo(pid_t pid) +static void lsof_dumpinfo(pid_t pid) { int fd; struct pid_info_t info; @@ -187,7 +187,7 @@ void lsof_dumpinfo(pid_t pid) } // Read the command line information; each argument is terminated with NULL. - strncat(info.path, "cmdline", sizeof(info.path)); + strlcat(info.path, "cmdline", sizeof(info.path)); fd = open(info.path, O_RDONLY); if (fd < 0) { fprintf(stderr, "Couldn't read %s\n", info.path); From 29393d69b3baed12ebe1530c29f59f4fe18ad029 Mon Sep 17 00:00:00 2001 From: Nick Kralevich Date: Mon, 3 Jun 2013 13:38:14 -0700 Subject: [PATCH 025/541] fix another misuse of strncat This has the potential to overflow "buffer" if the command line is more than 4K. Change-Id: Icdfed0d9d21804f290b75787ef3809e0475b14f0 --- toolbox/log.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/toolbox/log.c b/toolbox/log.c index f30e6a782..0bee19e90 100644 --- a/toolbox/log.c +++ b/toolbox/log.c @@ -130,8 +130,8 @@ int log_main(int argc, char *argv[]) buffer[0] = '\0'; for (i = optind ; i < argc ; i++) { - strncat(buffer, argv[i], sizeof(buffer)-1); - strncat(buffer, " ", sizeof(buffer)-1); + strlcat(buffer, argv[i], sizeof(buffer)-1); + strlcat(buffer, " ", sizeof(buffer)-1); } if(buffer[0] == 0) { From abb7d58010da1fcd466e6e032c5f7bf8810ed4dd Mon Sep 17 00:00:00 2001 From: Dima Zavin Date: Tue, 4 Jun 2013 10:34:49 -0700 Subject: [PATCH 026/541] HACK: cutils: restore record_stream temporarily Restore the record_stream code in cutils that was removed in e00a12bf8a99eb6c4f278efa503488aa21dd8d4d as part of cutils cleanup. Unfortunately, there are some prebuilt vendor libs that rely on this functionality that we cannot currently rebuild. Remove at the earliest possible convenience once the vendor libs have been fixed. Bug: 9189218 Change-Id: I5d44126756f1e3ed194d1b5873d64d9154183199 Signed-off-by: Dima Zavin --- include/cutils/record_stream.h | 43 ++++++++ libcutils/Android.mk | 1 + libcutils/record_stream.c | 186 +++++++++++++++++++++++++++++++++ 3 files changed, 230 insertions(+) create mode 100644 include/cutils/record_stream.h create mode 100644 libcutils/record_stream.c diff --git a/include/cutils/record_stream.h b/include/cutils/record_stream.h new file mode 100644 index 000000000..bfac87a53 --- /dev/null +++ b/include/cutils/record_stream.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2006 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. + */ + +/* + * A simple utility for reading fixed records out of a stream fd + */ + +#ifndef _CUTILS_RECORD_STREAM_H +#define _CUTILS_RECORD_STREAM_H + +#ifdef __cplusplus +extern "C" { +#endif + + +typedef struct RecordStream RecordStream; + +extern RecordStream *record_stream_new(int fd, size_t maxRecordLen); +extern void record_stream_free(RecordStream *p_rs); + +extern int record_stream_get_next (RecordStream *p_rs, void ** p_outRecord, + size_t *p_outRecordLen); + +#ifdef __cplusplus +} +#endif + + +#endif /*_CUTILS_RECORD_STREAM_H*/ + diff --git a/libcutils/Android.mk b/libcutils/Android.mk index 1c3185e9d..e46216eec 100644 --- a/libcutils/Android.mk +++ b/libcutils/Android.mk @@ -41,6 +41,7 @@ commonSources := \ open_memstream.c \ strdup16to8.c \ strdup8to16.c \ + record_stream.c \ process_name.c \ threads.c \ sched_policy.c \ diff --git a/libcutils/record_stream.c b/libcutils/record_stream.c new file mode 100644 index 000000000..69949047c --- /dev/null +++ b/libcutils/record_stream.c @@ -0,0 +1,186 @@ +/* libs/cutils/record_stream.c +** +** Copyright 2006, 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 +#include +#include +#include +#include +#include +#include +#ifdef HAVE_WINSOCK +#include /* for ntohl */ +#else +#include +#endif + +#define HEADER_SIZE 4 + +struct RecordStream { + int fd; + size_t maxRecordLen; + + unsigned char *buffer; + + unsigned char *unconsumed; + unsigned char *read_end; + unsigned char *buffer_end; +}; + + +extern RecordStream *record_stream_new(int fd, size_t maxRecordLen) +{ + RecordStream *ret; + + assert (maxRecordLen <= 0xffff); + + ret = (RecordStream *)calloc(1, sizeof(RecordStream)); + + ret->fd = fd; + ret->maxRecordLen = maxRecordLen; + ret->buffer = (unsigned char *)malloc (maxRecordLen + HEADER_SIZE); + + ret->unconsumed = ret->buffer; + ret->read_end = ret->buffer; + ret->buffer_end = ret->buffer + maxRecordLen + HEADER_SIZE; + + return ret; +} + + +extern void record_stream_free(RecordStream *rs) +{ + free(rs->buffer); + free(rs); +} + + +/* returns NULL; if there isn't a full record in the buffer */ +static unsigned char * getEndOfRecord (unsigned char *p_begin, + unsigned char *p_end) +{ + size_t len; + unsigned char * p_ret; + + if (p_end < p_begin + HEADER_SIZE) { + return NULL; + } + + //First four bytes are length + len = ntohl(*((uint32_t *)p_begin)); + + p_ret = p_begin + HEADER_SIZE + len; + + if (p_end < p_ret) { + return NULL; + } + + return p_ret; +} + +static void *getNextRecord (RecordStream *p_rs, size_t *p_outRecordLen) +{ + unsigned char *record_start, *record_end; + + record_end = getEndOfRecord (p_rs->unconsumed, p_rs->read_end); + + if (record_end != NULL) { + /* one full line in the buffer */ + record_start = p_rs->unconsumed + HEADER_SIZE; + p_rs->unconsumed = record_end; + + *p_outRecordLen = record_end - record_start; + + return record_start; + } + + return NULL; +} + +/** + * Reads the next record from stream fd + * Records are prefixed by a 16-bit big endian length value + * Records may not be larger than maxRecordLen + * + * Doesn't guard against EINTR + * + * p_outRecord and p_outRecordLen may not be NULL + * + * Return 0 on success, -1 on fail + * Returns 0 with *p_outRecord set to NULL on end of stream + * Returns -1 / errno = EAGAIN if it needs to read again + */ +int record_stream_get_next (RecordStream *p_rs, void ** p_outRecord, + size_t *p_outRecordLen) +{ + void *ret; + + ssize_t countRead; + + /* is there one record already in the buffer? */ + ret = getNextRecord (p_rs, p_outRecordLen); + + if (ret != NULL) { + *p_outRecord = ret; + return 0; + } + + // if the buffer is full and we don't have a full record + if (p_rs->unconsumed == p_rs->buffer + && p_rs->read_end == p_rs->buffer_end + ) { + // this should never happen + //ALOGE("max record length exceeded\n"); + assert (0); + errno = EFBIG; + return -1; + } + + if (p_rs->unconsumed != p_rs->buffer) { + // move remainder to the beginning of the buffer + size_t toMove; + + toMove = p_rs->read_end - p_rs->unconsumed; + if (toMove) { + memmove(p_rs->buffer, p_rs->unconsumed, toMove); + } + + p_rs->read_end = p_rs->buffer + toMove; + p_rs->unconsumed = p_rs->buffer; + } + + countRead = read (p_rs->fd, p_rs->read_end, p_rs->buffer_end - p_rs->read_end); + + if (countRead <= 0) { + /* note: end-of-stream drops through here too */ + *p_outRecord = NULL; + return countRead; + } + + p_rs->read_end += countRead; + + ret = getNextRecord (p_rs, p_outRecordLen); + + if (ret == NULL) { + /* not enough of a buffer to for a whole command */ + errno = EAGAIN; + return -1; + } + + *p_outRecord = ret; + return 0; +} From 32a9d696c4d8172789a19a4f7de43d96d0c4d3e6 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Tue, 4 Jun 2013 18:47:31 -0700 Subject: [PATCH 027/541] remove unused SF event log tags Change-Id: I1271875c575aac31568017a17622f4dc9ff40032 --- logcat/event.logtags | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/logcat/event.logtags b/logcat/event.logtags index 6040bd9c9..3a1b28140 100644 --- a/logcat/event.logtags +++ b/logcat/event.logtags @@ -110,22 +110,7 @@ 60003 view_use_drawing_cache (View drawn using bitmap cache|1|5) # graphics timestamp -60100 sf_app_dequeue_before (buffer|1),(identity|1),(time|2) -60101 sf_app_dequeue_after (buffer|1),(identity|1),(time|2) -60102 sf_app_lock_before (buffer|1),(identity|1),(time|2) -60103 sf_app_lock_after (buffer|1),(identity|1),(time|2) -60104 sf_app_queue (buffer|1),(identity|1),(time|2) -60105 sf_repaint (buffer|1),(time|2) -60106 sf_composition_complete (buffer|1),(time|2) -60107 sf_unlock_clients (buffer|1),(time|2) -60108 sf_swapbuffers (buffer|1),(time|2) -60109 sf_repaint_done (buffer|1),(time|2) -60110 sf_fb_post_before (buffer|1),(time|2) -60111 sf_fb_post_after (buffer|1),(time|2) -60112 sf_fb_dequeue_before (buffer|1),(time|2) -60113 sf_fb_dequeue_after (buffer|1),(time|2) -60114 sf_fb_lock_before (buffer|1),(time|2) -60115 sf_fb_lock_after (buffer|1),(time|2) +# 60100 - 60199 reserved for surfaceflinger # 0 for screen off, 1 for screen on, 2 for key-guard done 70000 screen_toggled (screen_state|1|5) From 317ec9246b28c8be373d2915be5fbfd85b2854e2 Mon Sep 17 00:00:00 2001 From: "Torne (Richard Coles)" Date: Tue, 4 Jun 2013 16:31:15 +0100 Subject: [PATCH 028/541] Add webviewchromium to BOOTCLASSPATH. Add /system/framework/webviewchromium.jar to BOOTCLASSPATH. This jar contains the implementation classes for the new WebView. It has been processed with jarjar to ensure that it doesn't define any classes outside of com.android. Change-Id: If65913638df0088f4dd7d62a087750b90038a7fb --- rootdir/init.rc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rootdir/init.rc b/rootdir/init.rc index a68e9af78..5eab0c3c9 100644 --- a/rootdir/init.rc +++ b/rootdir/init.rc @@ -37,7 +37,7 @@ loglevel 3 export ANDROID_STORAGE /storage export ASEC_MOUNTPOINT /mnt/asec export LOOP_MOUNTPOINT /mnt/obb - export BOOTCLASSPATH /system/framework/core.jar:/system/framework/conscrypt.jar:/system/framework/okhttp.jar:/system/framework/core-junit.jar:/system/framework/bouncycastle.jar:/system/framework/ext.jar:/system/framework/framework.jar:/system/framework/telephony-common.jar:/system/framework/voip-common.jar:/system/framework/mms-common.jar:/system/framework/android.policy.jar:/system/framework/services.jar:/system/framework/apache-xml.jar + export BOOTCLASSPATH /system/framework/core.jar:/system/framework/conscrypt.jar:/system/framework/okhttp.jar:/system/framework/core-junit.jar:/system/framework/bouncycastle.jar:/system/framework/ext.jar:/system/framework/framework.jar:/system/framework/telephony-common.jar:/system/framework/voip-common.jar:/system/framework/mms-common.jar:/system/framework/android.policy.jar:/system/framework/services.jar:/system/framework/apache-xml.jar:/system/framework/webviewchromium.jar # Backward compatibility symlink /system/etc /etc From 2deedfe0b1ac86ebd62d19cf7da9e7dcb508ab09 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Mon, 28 Jan 2013 17:13:35 -0800 Subject: [PATCH 029/541] init: switch property_get to use __system_property_get Change-Id: I4fc0502a1a5b331087618a4d2e3d90948743d7bd --- init/init.c | 22 ++++++++++++---------- init/init_parser.c | 16 ++++++++-------- init/keychords.c | 7 +++---- init/property_service.c | 21 +++++++-------------- init/property_service.h | 3 ++- 5 files changed, 32 insertions(+), 37 deletions(-) diff --git a/init/init.c b/init/init.c index f8b21e64a..94ffe1b7e 100755 --- a/init/init.c +++ b/init/init.c @@ -625,7 +625,7 @@ static void import_kernel_nv(char *name, int for_emulator) static void export_kernel_boot_props(void) { char tmp[PROP_VALUE_MAX]; - const char *pval; + int ret; unsigned i; struct { const char *src_prop; @@ -639,22 +639,24 @@ static void export_kernel_boot_props(void) }; for (i = 0; i < ARRAY_SIZE(prop_map); i++) { - pval = property_get(prop_map[i].src_prop); - property_set(prop_map[i].dest_prop, pval ?: prop_map[i].def_val); + ret = property_get(prop_map[i].src_prop, tmp); + if (ret == 0) + property_set(prop_map[i].dest_prop, prop_map[i].def_val); } - pval = property_get("ro.boot.console"); - if (pval) - strlcpy(console, pval, sizeof(console)); + ret = property_get("ro.boot.console", tmp); + if (ret) + strlcpy(console, tmp, sizeof(console)); /* save a copy for init's usage during boot */ - strlcpy(bootmode, property_get("ro.bootmode"), sizeof(bootmode)); + property_get("ro.bootmode", tmp); + strlcpy(bootmode, tmp, sizeof(bootmode)); /* if this was given on kernel command line, override what we read * before (e.g. from /proc/cpuinfo), if anything */ - pval = property_get("ro.boot.hardware"); - if (pval) - strlcpy(hardware, pval, sizeof(hardware)); + ret = property_get("ro.boot.hardware", tmp); + if (ret) + strlcpy(hardware, tmp, sizeof(hardware)); property_set("ro.hardware", hardware); snprintf(tmp, PROP_VALUE_MAX, "%d", revision); diff --git a/init/init_parser.c b/init/init_parser.c index a1d242355..28bf30c14 100644 --- a/init/init_parser.c +++ b/init/init_parser.c @@ -208,8 +208,9 @@ int expand_props(char *dst, const char *src, int dst_size) while (*src_ptr && left > 0) { char *c; char prop[PROP_NAME_MAX + 1]; - const char *prop_val; + char prop_val[PROP_VALUE_MAX]; int prop_len = 0; + int prop_val_len; c = strchr(src_ptr, '$'); if (!c) { @@ -267,14 +268,14 @@ int expand_props(char *dst, const char *src, int dst_size) goto err; } - prop_val = property_get(prop); - if (!prop_val) { + prop_val_len = property_get(prop, prop_val); + if (!prop_val_len) { ERROR("property '%s' doesn't exist while expanding '%s'\n", prop, src); goto err; } - ret = push_chars(&dst_ptr, &left, prop_val, strlen(prop_val)); + ret = push_chars(&dst_ptr, &left, prop_val, prop_val_len); if (ret < 0) goto err_nospace; src_ptr = c; @@ -545,7 +546,7 @@ void queue_all_property_triggers() const char* equals = strchr(name, '='); if (equals) { char prop_name[PROP_NAME_MAX + 1]; - const char* value; + char value[PROP_VALUE_MAX]; int length = equals - name; if (length > PROP_NAME_MAX) { ERROR("property name too long in trigger %s", act->name); @@ -554,9 +555,8 @@ void queue_all_property_triggers() prop_name[length] = 0; /* does the property exist, and match the trigger value? */ - value = property_get(prop_name); - if (value && (!strcmp(equals + 1, value) || - !strcmp(equals + 1, "*"))) { + property_get(prop_name, value); + if (!strcmp(equals + 1, value) ||!strcmp(equals + 1, "*")) { action_add_queue_tail(act); } } diff --git a/init/keychords.c b/init/keychords.c index 061d15701..4a6404261 100644 --- a/init/keychords.c +++ b/init/keychords.c @@ -95,20 +95,19 @@ void keychord_init() void handle_keychord() { struct service *svc; - const char* debuggable; - const char* adb_enabled; + char adb_enabled[PROP_VALUE_MAX]; int ret; __u16 id; // Only handle keychords if adb is enabled. - adb_enabled = property_get("init.svc.adbd"); + property_get("init.svc.adbd", adb_enabled); ret = read(keychord_fd, &id, sizeof(id)); if (ret != sizeof(id)) { ERROR("could not read keychord id\n"); return; } - if ((adb_enabled && !strcmp(adb_enabled, "running"))) { + if (!strcmp(adb_enabled, "running")) { svc = service_find_by_keychord(id); if (svc) { INFO("starting service %s from keychord\n", svc->name); diff --git a/init/property_service.c b/init/property_service.c index 33ec14633..cd4717192 100644 --- a/init/property_service.c +++ b/init/property_service.c @@ -299,19 +299,9 @@ static int check_perms(const char *name, unsigned int uid, unsigned int gid, cha return 0; } -const char* property_get(const char *name) +int property_get(const char *name, char value[PROP_VALUE_MAX]) { - prop_info *pi; - - if(strlen(name) >= PROP_NAME_MAX) return 0; - - pi = (prop_info*) __system_property_find(name); - - if(pi != 0) { - return pi->value; - } else { - return 0; - } + return __system_property_get(name, value); } static void write_persistent_property(const char *name, const char *value) @@ -598,8 +588,11 @@ int properties_inited(void) static void load_override_properties() { #ifdef ALLOW_LOCAL_PROP_OVERRIDE - const char *debuggable = property_get("ro.debuggable"); - if (debuggable && (strcmp(debuggable, "1") == 0)) { + char debuggable[PROP_VALUE_MAX]; + int ret; + + ret = property_get("ro.debuggable", debuggable); + if (ret && (strcmp(debuggable, "1") == 0)) { load_properties_from_file(PROP_PATH_LOCAL_OVERRIDE); } #endif /* ALLOW_LOCAL_PROP_OVERRIDE */ diff --git a/init/property_service.h b/init/property_service.h index b9d1bf610..b08c11854 100644 --- a/init/property_service.h +++ b/init/property_service.h @@ -18,6 +18,7 @@ #define _INIT_PROPERTY_H #include +#include extern void handle_property_set_fd(void); extern void property_init(void); @@ -25,7 +26,7 @@ extern void property_load_boot_defaults(void); extern void load_persist_props(void); extern void start_property_service(void); void get_property_workspace(int *fd, int *sz); -extern const char* property_get(const char *name); +extern int property_get(const char *name, char value[PROP_VALUE_MAX]); extern int property_set(const char *name, const char *value); extern int properties_inited(); int get_property_set_fd(void); From 9f5af635010a7ba92edf1fca543f7271cc9d75c8 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Wed, 23 Jan 2013 23:09:48 -0800 Subject: [PATCH 030/541] init: move the system property writer implementation Move the system property writer implementation into bionic to keep it next to the reader implementation and allow for better testing. Change-Id: I9026e604109e30546b2849b60cab2e7e5ff00ba5 --- init/property_service.c | 59 ++++++----------------------------------- 1 file changed, 8 insertions(+), 51 deletions(-) diff --git a/init/property_service.c b/init/property_service.c index cd4717192..79ff6c088 100644 --- a/init/property_service.c +++ b/init/property_service.c @@ -152,23 +152,11 @@ out: return -1; } -/* (8 header words + 247 toc words) = 1020 bytes */ -/* 1024 bytes header and toc + 247 prop_infos @ 128 bytes = 32640 bytes */ - -#define PA_COUNT_MAX 247 -#define PA_INFO_START 1024 -#define PA_SIZE 32768 - static workspace pa_workspace; -static prop_info *pa_info_array; - -extern prop_area *__system_property_area__; static int init_property_area(void) { - prop_area *pa; - - if(pa_info_array) + if (property_area_inited) return -1; if(init_workspace(&pa_workspace, PA_SIZE)) @@ -176,27 +164,12 @@ static int init_property_area(void) fcntl(pa_workspace.fd, F_SETFD, FD_CLOEXEC); - pa_info_array = (void*) (((char*) pa_workspace.data) + PA_INFO_START); + __system_property_area_init(pa_workspace.data); - pa = pa_workspace.data; - memset(pa, 0, PA_SIZE); - pa->magic = PROP_AREA_MAGIC; - pa->version = PROP_AREA_VERSION; - - /* plug into the lib property services */ - __system_property_area__ = pa; property_area_inited = 1; return 0; } -static void update_prop_info(prop_info *pi, const char *value, unsigned len) -{ - pi->serial = pi->serial | 1; - memcpy(pi->value, value, len + 1); - pi->serial = (len << 24) | ((pi->serial + 1) & 0xffffff); - __futex_wake(&pi->serial, INT32_MAX); -} - static int check_mac_perms(const char *name, char *sctx) { if (is_selinux_enabled() <= 0) @@ -328,8 +301,8 @@ static void write_persistent_property(const char *name, const char *value) int property_set(const char *name, const char *value) { - prop_area *pa; prop_info *pi; + int ret; size_t namelen = strlen(name); size_t valuelen = strlen(value); @@ -344,29 +317,13 @@ int property_set(const char *name, const char *value) /* ro.* properties may NEVER be modified once set */ if(!strncmp(name, "ro.", 3)) return -1; - pa = __system_property_area__; - update_prop_info(pi, value, valuelen); - pa->serial++; - __futex_wake(&pa->serial, INT32_MAX); + __system_property_update(pi, value, valuelen); } else { - pa = __system_property_area__; - if(pa->count == PA_COUNT_MAX) { - ERROR("Failed to set '%s'='%s', property pool is exhausted at %d entries", - name, value, PA_COUNT_MAX); - return -1; + ret = __system_property_add(name, namelen, value, valuelen); + if (ret < 0) { + ERROR("Failed to set '%s'='%s'", name, value); + return ret; } - - pi = pa_info_array + pa->count; - pi->serial = (valuelen << 24); - memcpy(pi->name, name, namelen + 1); - memcpy(pi->value, value, valuelen + 1); - - pa->toc[pa->count] = - (namelen << 24) | (((unsigned) pi) - ((unsigned) pa)); - - pa->count++; - pa->serial++; - __futex_wake(&pa->serial, INT32_MAX); } /* If name starts with "net." treat as a DNS property. */ if (strncmp("net.", name, strlen("net.")) == 0) { From 88ac54a4e8d2a63e4fd9c465e115795ace316776 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Tue, 29 Jan 2013 14:58:57 -0800 Subject: [PATCH 031/541] init: verify size of property buffers passed to property_get Verify that the buffer passed as the value parameter to property_get is always big enough. Change-Id: Ie5b6fcd94bb908215cfd55d0c9b07f717ddb70b1 --- init/property_service.c | 2 +- init/property_service.h | 18 +++++++++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/init/property_service.c b/init/property_service.c index 79ff6c088..846a0a312 100644 --- a/init/property_service.c +++ b/init/property_service.c @@ -272,7 +272,7 @@ static int check_perms(const char *name, unsigned int uid, unsigned int gid, cha return 0; } -int property_get(const char *name, char value[PROP_VALUE_MAX]) +int __property_get(const char *name, char *value) { return __system_property_get(name, value); } diff --git a/init/property_service.h b/init/property_service.h index b08c11854..46cbd8ff5 100644 --- a/init/property_service.h +++ b/init/property_service.h @@ -26,9 +26,25 @@ extern void property_load_boot_defaults(void); extern void load_persist_props(void); extern void start_property_service(void); void get_property_workspace(int *fd, int *sz); -extern int property_get(const char *name, char value[PROP_VALUE_MAX]); +extern int __property_get(const char *name, char *value); extern int property_set(const char *name, const char *value); extern int properties_inited(); int get_property_set_fd(void); +extern void __property_get_size_error() + __attribute__((__error__("property_get called with too small buffer"))); + +static inline +__attribute__ ((always_inline)) +__attribute__ ((gnu_inline)) +__attribute__ ((artificial)) +int property_get(const char *name, char *value) +{ + size_t value_len = __builtin_object_size(value, 0); + if (value_len != PROP_VALUE_MAX) + __property_get_size_error(); + + return __property_get(name, value); +} + #endif /* _INIT_PROPERTY_H */ From 91779634debc79bc75d3df4e0f59d964ad4f5f78 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Mon, 28 Jan 2013 17:14:04 -0800 Subject: [PATCH 032/541] toolbox: hide property implementation from watchprops Change-Id: Ia6609116d641d3354971ca40a16ffcab38484150 --- toolbox/watchprops.c | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/toolbox/watchprops.c b/toolbox/watchprops.c index d3119924b..3418d6293 100644 --- a/toolbox/watchprops.c +++ b/toolbox/watchprops.c @@ -9,9 +9,6 @@ #define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_ #include - -extern prop_area *__system_property_area__; - typedef struct pwatch pwatch; struct pwatch @@ -39,33 +36,35 @@ static void announce(const prop_info *pi) int watchprops_main(int argc, char *argv[]) { - prop_area *pa = __system_property_area__; - unsigned serial = pa->serial; - unsigned count = pa->count; + unsigned serial = 0; + unsigned count; unsigned n; - if(count >= 1024) exit(1); - - for(n = 0; n < count; n++) { + for(n = 0; n < 1024; n++) { watchlist[n].pi = __system_property_find_nth(n); - watchlist[n].serial = watchlist[n].pi->serial; + if (watchlist[n].pi == 0) + break; + watchlist[n].serial = __system_property_serial(watchlist[n].pi); } - for(;;) { - do { - __futex_wait(&pa->serial, serial, 0); - } while(pa->serial == serial); + count = n; + if (count == 1024) + exit(1); - while(count < pa->count){ + for(;;) { + serial = __system_property_wait_any(serial); + while(count < 1024){ watchlist[count].pi = __system_property_find_nth(count); - watchlist[count].serial = watchlist[n].pi->serial; + if (watchlist[count].pi == 0) + break; + watchlist[count].serial = __system_property_serial(watchlist[n].pi); announce(watchlist[count].pi); count++; if(count == 1024) exit(1); } for(n = 0; n < count; n++){ - unsigned tmp = watchlist[n].pi->serial; + unsigned tmp = __system_property_serial(watchlist[n].pi); if(watchlist[n].serial != tmp) { announce(watchlist[n].pi); watchlist[n].serial = tmp; From 67e3663fc93c65b69b5d121db05b0833b98d97f1 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Mon, 17 Jun 2013 16:20:08 -0700 Subject: [PATCH 033/541] init: fix copying boot properties The previous patch "init: verify size of property buffers passed to property_get" incorrectly modified one of the callers, resulting in ro.serialno, ro.bootmode, ro.baseband, and ro.bootloader always being set to their default values. Bug: 9469860 Change-Id: Id45bd8dd657e8d61f4cfaf7e6b2559d2bfd05181 --- init/init.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/init/init.c b/init/init.c index 94ffe1b7e..fd428b01f 100755 --- a/init/init.c +++ b/init/init.c @@ -640,7 +640,9 @@ static void export_kernel_boot_props(void) for (i = 0; i < ARRAY_SIZE(prop_map); i++) { ret = property_get(prop_map[i].src_prop, tmp); - if (ret == 0) + if (ret > 0) + property_set(prop_map[i].dest_prop, tmp); + else property_set(prop_map[i].dest_prop, prop_map[i].def_val); } From 9ede33233f97da5c9fb079ac4b373fb7eeb6a3ee Mon Sep 17 00:00:00 2001 From: Tobias Grosser Date: Mon, 17 Jun 2013 18:35:46 -0700 Subject: [PATCH 034/541] Set ATRACE_ENABLE() to '0' if not availble Change-Id: Iec61d275cd7b2be65f828c282f97cc59adf4cc0b --- include/cutils/trace.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/cutils/trace.h b/include/cutils/trace.h index fbb607780..a0dd1e034 100644 --- a/include/cutils/trace.h +++ b/include/cutils/trace.h @@ -263,7 +263,7 @@ static inline void atrace_int(uint64_t tag, const char* name, int32_t value) #define ATRACE_INIT() #define ATRACE_GET_ENABLED_TAGS() -#define ATRACE_ENABLED() +#define ATRACE_ENABLED() 0 #define ATRACE_BEGIN(name) #define ATRACE_END() #define ATRACE_ASYNC_BEGIN(name, cookie) From bda25e4ed5f2fdd65cf32e7454774f5937c4ede4 Mon Sep 17 00:00:00 2001 From: Nick Kralevich Date: Tue, 18 Jun 2013 10:57:25 -0700 Subject: [PATCH 035/541] Don't use __attribute__(__error__) __attribute(__error__) isn't supported on clang, and generates compiler warnings. Use __errordecl instead, which does the right thing for different compilers. Change-Id: Ifb0797a5de703cc5b3c39dcd97bcfaf404f1cafa --- include/cutils/properties.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/include/cutils/properties.h b/include/cutils/properties.h index 228b73ec3..2c70165b0 100644 --- a/include/cutils/properties.h +++ b/include/cutils/properties.h @@ -54,8 +54,7 @@ int property_list(void (*propfn)(const char *key, const char *value, void *cooki extern int __property_get_real(const char *, char *, const char *) __asm__(__USER_LABEL_PREFIX__ "property_get"); -extern void __property_get_too_small_error() - __attribute__((__error__("property_get() called with too small of a buffer"))); +__errordecl(__property_get_too_small_error, "property_get() called with too small of a buffer"); __BIONIC_FORTIFY_INLINE int property_get(const char *key, char *value, const char *default_value) { From e7bb159d16f9e60850a3c79cc388587959015a65 Mon Sep 17 00:00:00 2001 From: Greg Hackmann Date: Tue, 12 Feb 2013 14:41:59 -0800 Subject: [PATCH 036/541] libcutils: reimplement property_list() using __system_property_foreach() Change-Id: I273fbf7151908ee57b9b951e7d8c5f4925bbc109 Signed-off-by: Greg Hackmann --- libcutils/properties.c | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/libcutils/properties.c b/libcutils/properties.c index f732ec067..28d8b2f59 100644 --- a/libcutils/properties.c +++ b/libcutils/properties.c @@ -52,19 +52,28 @@ int property_get(const char *key, char *value, const char *default_value) return len; } -int property_list(void (*propfn)(const char *key, const char *value, void *cookie), - void *cookie) +struct property_list_callback_data +{ + void (*propfn)(const char *key, const char *value, void *cookie); + void *cookie; +}; + +static void property_list_callback(const prop_info *pi, void *cookie) { char name[PROP_NAME_MAX]; char value[PROP_VALUE_MAX]; - const prop_info *pi; - unsigned n; - - for(n = 0; (pi = __system_property_find_nth(n)); n++) { - __system_property_read(pi, name, value); - propfn(name, value, cookie); - } - return 0; + struct property_list_callback_data *data = cookie; + + __system_property_read(pi, name, value); + data->propfn(name, value, data->cookie); +} + +int property_list( + void (*propfn)(const char *key, const char *value, void *cookie), + void *cookie) +{ + struct property_list_callback_data data = { propfn, cookie }; + return __system_property_foreach(property_list_callback, &data); } #elif defined(HAVE_SYSTEM_PROPERTY_SERVER) From 389e358017ad15485f2bd9120feebfffa489131b Mon Sep 17 00:00:00 2001 From: Greg Hackmann Date: Wed, 13 Feb 2013 10:49:50 -0800 Subject: [PATCH 037/541] toolbox: reimplement watchprops using __system_property_foreach() Internally, replace the watchlist array with a hashmap since the array assumes properties are enumerated in a consistent order and foreach() probably won't. (find_nth() never guaranteed this either but it usually worked in practice.) Change-Id: I83843facdd671edd09652edf472e88ec3d1edd3b Signed-off-by: Greg Hackmann --- toolbox/watchprops.c | 100 ++++++++++++++++++++++++++----------------- 1 file changed, 60 insertions(+), 40 deletions(-) diff --git a/toolbox/watchprops.c b/toolbox/watchprops.c index 3418d6293..bf82882f1 100644 --- a/toolbox/watchprops.c +++ b/toolbox/watchprops.c @@ -1,32 +1,30 @@ #include #include #include +#include #include +#include #include #define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_ #include -typedef struct pwatch pwatch; - -struct pwatch +static int str_hash(void *key) { - const prop_info *pi; - unsigned serial; -}; + return hashmapHash(key, strlen(key)); +} -static pwatch watchlist[1024]; - -static void announce(const prop_info *pi) +static bool str_equals(void *keyA, void *keyB) +{ + return strcmp(keyA, keyB) == 0; +} + +static void announce(char *name, char *value) { - char name[PROP_NAME_MAX]; - char value[PROP_VALUE_MAX]; char *x; - __system_property_read(pi, name, value); - for(x = value; *x; x++) { if((*x < 32) || (*x > 127)) *x = '.'; } @@ -34,42 +32,64 @@ static void announce(const prop_info *pi) fprintf(stderr,"%10d %s = '%s'\n", (int) time(0), name, value); } +static void add_to_watchlist(Hashmap *watchlist, const char *name, + const prop_info *pi) +{ + char *key = strdup(name); + unsigned *value = malloc(sizeof(unsigned)); + if (!key || !value) + exit(1); + + *value = __system_property_serial(pi); + hashmapPut(watchlist, key, value); +} + +static void populate_watchlist(const prop_info *pi, void *cookie) +{ + Hashmap *watchlist = cookie; + char name[PROP_NAME_MAX]; + char value_unused[PROP_VALUE_MAX]; + + __system_property_read(pi, name, value_unused); + add_to_watchlist(watchlist, name, pi); +} + +static void update_watchlist(const prop_info *pi, void *cookie) +{ + Hashmap *watchlist = cookie; + char name[PROP_NAME_MAX]; + char value[PROP_VALUE_MAX]; + unsigned *serial; + + __system_property_read(pi, name, value); + serial = hashmapGet(watchlist, name); + if (!serial) { + add_to_watchlist(watchlist, name, pi); + announce(name, value); + } else { + unsigned tmp = __system_property_serial(pi); + if (*serial != tmp) { + *serial = tmp; + announce(name, value); + } + } +} + int watchprops_main(int argc, char *argv[]) { unsigned serial = 0; - unsigned count; + unsigned count = 0; unsigned n; - for(n = 0; n < 1024; n++) { - watchlist[n].pi = __system_property_find_nth(n); - if (watchlist[n].pi == 0) - break; - watchlist[n].serial = __system_property_serial(watchlist[n].pi); - } - - count = n; - if (count == 1024) + Hashmap *watchlist = hashmapCreate(1024, str_hash, str_equals); + if (!watchlist) exit(1); + __system_property_foreach(populate_watchlist, watchlist); + for(;;) { serial = __system_property_wait_any(serial); - while(count < 1024){ - watchlist[count].pi = __system_property_find_nth(count); - if (watchlist[count].pi == 0) - break; - watchlist[count].serial = __system_property_serial(watchlist[n].pi); - announce(watchlist[count].pi); - count++; - if(count == 1024) exit(1); - } - - for(n = 0; n < count; n++){ - unsigned tmp = __system_property_serial(watchlist[n].pi); - if(watchlist[n].serial != tmp) { - announce(watchlist[n].pi); - watchlist[n].serial = tmp; - } - } + __system_property_foreach(update_watchlist, watchlist); } return 0; } From f14eef0c3c456bfe39f7e9d57c8f7ae4ec775972 Mon Sep 17 00:00:00 2001 From: Greg Hackmann Date: Tue, 12 Feb 2013 14:39:31 -0800 Subject: [PATCH 038/541] init: move initial property area allocation into bionic bionic's __system_property_add() now expands the property area as needed by mapping in more pages. Rather than duplicate the mapping code, move it inside bionic and have bionic's __system_property_area_init() set up the first page. Change-Id: If9917d5f775c1a82eb89be55b84635395145ca49 Signed-off-by: Greg Hackmann --- init/property_service.c | 33 +++++---------------------------- 1 file changed, 5 insertions(+), 28 deletions(-) diff --git a/init/property_service.c b/init/property_service.c index 846a0a312..9afc7569a 100644 --- a/init/property_service.c +++ b/init/property_service.c @@ -112,7 +112,6 @@ struct { }; typedef struct { - void *data; size_t size; int fd; } workspace; @@ -120,36 +119,13 @@ typedef struct { static int init_workspace(workspace *w, size_t size) { void *data; - int fd; - - /* dev is a tmpfs that we can use to carve a shared workspace - * out of, so let's do that... - */ - fd = open(PROP_FILENAME, O_RDWR | O_CREAT | O_NOFOLLOW, 0644); + int fd = open(PROP_FILENAME, O_RDONLY | O_NOFOLLOW); if (fd < 0) return -1; - if (ftruncate(fd, size) < 0) - goto out; - - data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); - if(data == MAP_FAILED) - goto out; - - close(fd); - - fd = open(PROP_FILENAME, O_RDONLY | O_NOFOLLOW); - if (fd < 0) - return -1; - - w->data = data; w->size = size; w->fd = fd; return 0; - -out: - close(fd); - return -1; } static workspace pa_workspace; @@ -159,13 +135,14 @@ static int init_property_area(void) if (property_area_inited) return -1; - if(init_workspace(&pa_workspace, PA_SIZE)) + if(__system_property_area_init()) + return -1; + + if(init_workspace(&pa_workspace, 0)) return -1; fcntl(pa_workspace.fd, F_SETFD, FD_CLOEXEC); - __system_property_area_init(pa_workspace.data); - property_area_inited = 1; return 0; } From 96bcd488c286ebc131e6caf6132b77c66343e5e2 Mon Sep 17 00:00:00 2001 From: Nick Kralevich Date: Tue, 18 Jun 2013 17:57:08 -0700 Subject: [PATCH 039/541] debuggerd: Ignore SIGPIPE The system server may prematurely close the connection to /data/system/ndebugsocket if it's not interested in the data from debuggerd. If it does so, we don't want to die due to a SIGPIPE. Change-Id: Iaef1f497bcd630144e6df6a06644a3293b85b6e0 --- debuggerd/debuggerd.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/debuggerd/debuggerd.c b/debuggerd/debuggerd.c index da2e9b0e1..0028bda50 100644 --- a/debuggerd/debuggerd.c +++ b/debuggerd/debuggerd.c @@ -435,11 +435,13 @@ static int do_server() { signal(SIGBUS, SIG_DFL); signal(SIGFPE, SIG_DFL); signal(SIGSEGV, SIG_DFL); - signal(SIGPIPE, SIG_DFL); #ifdef SIGSTKFLT signal(SIGSTKFLT, SIG_DFL); #endif + // Ignore failed writes to closed sockets + signal(SIGPIPE, SIG_IGN); + logsocket = socket_local_client("logd", ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_DGRAM); if(logsocket < 0) { From b710ed21dec88c0dde8209264df054c842561589 Mon Sep 17 00:00:00 2001 From: Nick Kralevich Date: Mon, 24 Jun 2013 17:41:40 -0700 Subject: [PATCH 040/541] init: move SELinux into enforcing mode. When init starts up, immediately put SELinux into enforcing mode. This is currently a no-op. We currently have everything in the unconfined domain, so this should not break anything. (if it does, I'll roll it back immediately) If the kernel doesn't have SELinux support compiled in, then don't try loading a policy and continue without SELinux protections. Change-Id: Id0279cf82c545ea0f7090137b7566a5bc3ddd641 --- init/init.c | 41 ++++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 21 deletions(-) mode change 100755 => 100644 init/init.c diff --git a/init/init.c b/init/init.c old mode 100755 new mode 100644 index fd428b01f..4a335ca77 --- a/init/init.c +++ b/init/init.c @@ -39,6 +39,7 @@ #include #include +#include #include #include #include @@ -73,8 +74,6 @@ static char hardware[32]; static unsigned revision = 0; static char qemu[32]; -static int selinux_enabled = 1; - static struct action *cur_action = NULL; static struct command *cur_command = NULL; static struct listnode *command_queue = NULL; @@ -594,10 +593,6 @@ static void import_kernel_nv(char *name, int for_emulator) *value++ = 0; if (name_len == 0) return; - if (!strcmp(name,"selinux")) { - selinux_enabled = atoi(value); - } - if (for_emulator) { /* in the emulator, export any kernel option with the * ro.kernel. prefix */ @@ -780,10 +775,6 @@ void selinux_init_all_handles(void) int selinux_reload_policy(void) { - if (!selinux_enabled) { - return -1; - } - INFO("SELinux: Attempting to reload policy files\n"); if (selinux_android_reload_policy() == -1) { @@ -806,6 +797,24 @@ int audit_callback(void *data, security_class_t cls, char *buf, size_t len) return 0; } +static void selinux_initialize(void) +{ + if (access("/sys/fs/selinux", F_OK) != 0) { + // SELinux is not compiled into this kernel. Fail gracefully. + return; + } + + INFO("loading selinux policy\n"); + if (selinux_android_load_policy() < 0) { + ERROR("SELinux: Failed to load policy; rebooting into recovery mode\n"); + android_reboot(ANDROID_RB_RESTART2, 0, "recovery"); + while (1) { pause(); } // never reached + } + + selinux_init_all_handles(); + security_setenforce(1); +} + int main(int argc, char **argv) { int fd_count = 0; @@ -866,17 +875,7 @@ int main(int argc, char **argv) cb.func_audit = audit_callback; selinux_set_callback(SELINUX_CB_AUDIT, cb); - INFO("loading selinux policy\n"); - if (selinux_enabled) { - if (selinux_android_load_policy() < 0) { - selinux_enabled = 0; - INFO("SELinux: Disabled due to failed policy load\n"); - } else { - selinux_init_all_handles(); - } - } else { - INFO("SELinux: Disabled by command line option\n"); - } + selinux_initialize(); /* These directories were necessarily created before initial policy load * and therefore need their security context restored to the proper value. * This must happen before /dev is populated by ueventd. From 6f1cd0b2ad7a16d4ec0b5324f992cae33dc34f34 Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Tue, 25 Jun 2013 11:52:05 -0700 Subject: [PATCH 041/541] fastboot: add preflash command to help with large downloads+flash Without the partition name, the bootloader would have to store all of the data until the next flash command, which potentially wastes time. To help the bootloader deal with the data more efficiently, we now issue a flashing hint ("preflash:%s", partname). "preflash:" is used to indicate that the following download should ignore any previous "preflash:%s". Bug: 6045918 Change-Id: I4e9708f64f28a4781bde14eb3714d36b281ec5be --- fastboot/engine.c | 13 ++++++++++++- fastboot/fastboot_protocol.txt | 18 +++++++++--------- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/fastboot/engine.c b/fastboot/engine.c index 8d469911a..4045759bc 100644 --- a/fastboot/engine.c +++ b/fastboot/engine.c @@ -76,6 +76,7 @@ char *mkmsg(const char *fmt, ...) #define OP_NOTICE 4 #define OP_FORMAT 5 #define OP_DOWNLOAD_SPARSE 6 +#define OP_COMMAND_IGNORE_FAIL 7 typedef struct Action Action; @@ -374,6 +375,8 @@ int fb_format(Action *a, usb_handle *usb, int skip_if_not_supported) // Following piece of code is similar to fb_queue_flash() but executes // actions directly without queuing + snprintf(cmd, sizeof(cmd), "preflash:%s", partition); + fb_command(usb, cmd); /* Ignore status */ fprintf(stderr, "sending '%s' (%lli KB)...\n", partition, image.image_size/1024); status = fb_download_data(usb, image.buffer, image.image_size); if (status) goto cleanup; @@ -402,6 +405,8 @@ void fb_queue_flash(const char *ptn, void *data, unsigned sz) { Action *a; + a = queue_action(OP_COMMAND_IGNORE_FAIL, "preflash:%s", ptn); + a->msg = mkmsg("prep for '%s' (%d KB)", ptn, sz / 1024); a = queue_action(OP_DOWNLOAD, ""); a->data = data; a->size = sz; @@ -415,6 +420,8 @@ void fb_queue_flash_sparse(const char *ptn, struct sparse_file *s, unsigned sz) { Action *a; + a = queue_action(OP_COMMAND_IGNORE_FAIL, "preflash:%s", ptn); + a->msg = mkmsg("prep for sparse '%s' (%d KB)", ptn, sz / 1024); a = queue_action(OP_DOWNLOAD_SPARSE, ""); a->data = s; a->size = 0; @@ -575,7 +582,9 @@ void fb_queue_command(const char *cmd, const char *msg) void fb_queue_download(const char *name, void *data, unsigned size) { - Action *a = queue_action(OP_DOWNLOAD, ""); + Action *a; + a = queue_action(OP_COMMAND_IGNORE_FAIL, "preflash:"); + a = queue_action(OP_DOWNLOAD, ""); a->data = data; a->size = size; a->msg = mkmsg("downloading '%s'", name); @@ -614,6 +623,8 @@ int fb_execute_queue(usb_handle *usb) status = fb_command(usb, a->cmd); status = a->func(a, status, status ? fb_get_error() : ""); if (status) break; + } else if (a->op == OP_COMMAND_IGNORE_FAIL) { + fb_command(usb, a->cmd); /* Ignore status */ } else if (a->op == OP_QUERY) { status = fb_command_response(usb, a->cmd, resp); status = a->func(a, status, status ? fb_get_error() : resp); diff --git a/fastboot/fastboot_protocol.txt b/fastboot/fastboot_protocol.txt index 2248992d2..eede5d1ea 100644 --- a/fastboot/fastboot_protocol.txt +++ b/fastboot/fastboot_protocol.txt @@ -1,5 +1,5 @@ -FastBoot Version 0.4 +FastBoot Version 0.4++ ---------------------- The fastboot protocol is a mechanism for communicating with bootloaders @@ -67,10 +67,6 @@ Transport and Framing Example Session --------------- -Host: "getvar:version" request version variable - -Client: "OKAY0.4" return version "0.4" - Host: "getvar:nonexistant" request some undefined variable Client: "OKAY" return value "" @@ -123,6 +119,11 @@ Command Reference "flash:%s" Write the previously downloaded image to the named partition (if possible). + "preflash:%s" Optionally prepare for a download + flash. + E.g. flash directly during download. To deal with a + failed "flash" followed by a "boot", we send + "preflash:". + "erase:%s" Erase the indicated partition (clear to 0xFFs) "boot" The previously downloaded data is a boot.img @@ -140,7 +141,9 @@ Command Reference "powerdown" Power off the device. - +* Note about sparse files + Large files can be split up using libsparse, and sent to the bootloader + as repeated chunks of "download:%08x" + "flash:%s". Client Variables ---------------- @@ -151,9 +154,6 @@ on it. The various currently defined names are: - version Version of FastBoot protocol supported. - It should be "0.3" for this document. - version-bootloader Version string for the Bootloader. version-baseband Version string of the Baseband Software From 367297c3d764eaf6e60880964e1739df13f0b703 Mon Sep 17 00:00:00 2001 From: Rom Lemarchand Date: Wed, 5 Jun 2013 13:25:12 -0700 Subject: [PATCH 042/541] toolbox: swap utils Add swap utilities (mkswap, swapon, swapoff) to the toolbox Change-Id: If5ed6981670a1cdda6b528b587dbc1be7ccdf832 Signed-off-by: Rom Lemarchand --- toolbox/Android.mk | 5 ++- toolbox/mkswap.c | 94 ++++++++++++++++++++++++++++++++++++++++++++++ toolbox/swapoff.c | 21 +++++++++++ toolbox/swapon.c | 73 +++++++++++++++++++++++++++++++++++ 4 files changed, 192 insertions(+), 1 deletion(-) create mode 100644 toolbox/mkswap.c create mode 100644 toolbox/swapoff.c create mode 100644 toolbox/swapon.c diff --git a/toolbox/Android.mk b/toolbox/Android.mk index 565ec2aa5..f60037dae 100644 --- a/toolbox/Android.mk +++ b/toolbox/Android.mk @@ -65,7 +65,10 @@ TOOLS := \ runcon \ getsebool \ setsebool \ - load_policy + load_policy \ + swapon \ + swapoff \ + mkswap ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT))) TOOLS += r diff --git a/toolbox/mkswap.c b/toolbox/mkswap.c new file mode 100644 index 000000000..1710ef6f8 --- /dev/null +++ b/toolbox/mkswap.c @@ -0,0 +1,94 @@ +#include +#include +#include +#include +#include +#include +#include + +/* XXX This needs to be obtained from kernel headers. See b/9336527 */ +struct linux_swap_header { + char bootbits[1024]; /* Space for disklabel etc. */ + uint32_t version; + uint32_t last_page; + uint32_t nr_badpages; + unsigned char sws_uuid[16]; + unsigned char sws_volume[16]; + uint32_t padding[117]; + uint32_t badpages[1]; +}; + +#define MAGIC_SWAP_HEADER "SWAPSPACE2" +#define MAGIC_SWAP_HEADER_LEN 10 +#define MIN_PAGES 10 + +int mkswap_main(int argc, char **argv) +{ + int err = 0; + int fd; + ssize_t len; + off_t swap_size; + int pagesize; + struct linux_swap_header sw_hdr; + + if (argc != 2) { + fprintf(stderr, "Usage: %s \n", argv[0]); + return -EINVAL; + } + + fd = open(argv[1], O_WRONLY); + if (fd < 0) { + err = errno; + fprintf(stderr, "Cannot open %s\n", argv[1]); + return err; + } + + pagesize = getpagesize(); + /* Determine the length of the swap file */ + swap_size = lseek(fd, 0, SEEK_END); + if (swap_size < MIN_PAGES * pagesize) { + fprintf(stderr, "Swap file needs to be at least %dkB\n", + (MIN_PAGES * pagesize) >> 10); + err = -ENOSPC; + goto err; + } + if (lseek(fd, 0, SEEK_SET)) { + err = errno; + fprintf(stderr, "Can't seek to the beginning of the file\n"); + goto err; + } + + memset(&sw_hdr, 0, sizeof(sw_hdr)); + sw_hdr.version = 1; + sw_hdr.last_page = (swap_size / pagesize) - 1; + + len = write(fd, &sw_hdr, sizeof(sw_hdr)); + if (len != sizeof(sw_hdr)) { + err = errno; + fprintf(stderr, "Failed to write swap header into %s\n", argv[1]); + goto err; + } + + /* Write the magic header */ + if (lseek(fd, pagesize - MAGIC_SWAP_HEADER_LEN, SEEK_SET) < 0) { + err = errno; + fprintf(stderr, "Failed to seek into %s\n", argv[1]); + goto err; + } + + len = write(fd, MAGIC_SWAP_HEADER, MAGIC_SWAP_HEADER_LEN); + if (len != MAGIC_SWAP_HEADER_LEN) { + err = errno; + fprintf(stderr, "Failed to write magic swap header into %s\n", argv[1]); + goto err; + } + + if (fsync(fd) < 0) { + err = errno; + fprintf(stderr, "Failed to sync %s\n", argv[1]); + goto err; + } +err: + close(fd); + return err; +} diff --git a/toolbox/swapoff.c b/toolbox/swapoff.c new file mode 100644 index 000000000..8f1415850 --- /dev/null +++ b/toolbox/swapoff.c @@ -0,0 +1,21 @@ +#include +#include +#include +#include + +int swapoff_main(int argc, char **argv) +{ + int err = 0; + + if (argc != 2) { + fprintf(stderr, "Usage: %s \n", argv[0]); + return -EINVAL; + } + + err = swapoff(argv[1]); + if (err) { + fprintf(stderr, "swapoff failed for %s\n", argv[1]); + } + + return err; +} diff --git a/toolbox/swapon.c b/toolbox/swapon.c new file mode 100644 index 000000000..afa686852 --- /dev/null +++ b/toolbox/swapon.c @@ -0,0 +1,73 @@ +#include +#include +#include +#include +#include +#include + +/* XXX These need to be obtained from kernel headers. See b/9336527 */ +#define SWAP_FLAG_PREFER 0x8000 +#define SWAP_FLAG_PRIO_MASK 0x7fff +#define SWAP_FLAG_PRIO_SHIFT 0 +#define SWAP_FLAG_DISCARD 0x10000 + +void usage(char *name) +{ + fprintf(stderr, "Usage: %s [-p prio] \n" + " prio must be between 0 and %d\n", name, SWAP_FLAG_PRIO_MASK); +} + +int parse_prio(char *prio_str) +{ + unsigned long p = strtoul(prio_str, NULL, 10); + + return (p > SWAP_FLAG_PRIO_MASK)? -1 : (int)p; +} + +int swapon_main(int argc, char **argv) +{ + int err = 0; + int flags = 0; + int prio; + + opterr = 0; + do { + int c = getopt(argc, argv, "hp:"); + if (c == -1) + break; + + switch (c) { + case 'p': + if (optarg != NULL) + prio = parse_prio(optarg); + else + prio = -1; + + if (prio < 0) { + usage(argv[0]); + return -EINVAL; + } + flags |= SWAP_FLAG_PREFER; + flags |= (prio << SWAP_FLAG_PRIO_SHIFT) & SWAP_FLAG_PRIO_MASK; + break; + case 'h': + usage(argv[0]); + return 0; + case '?': + fprintf(stderr, "unknown option: %c\n", optopt); + return -EINVAL; + } + } while (1); + + if (optind != argc - 1) { + usage(argv[0]); + return -EINVAL; + } + + err = swapon(argv[argc - 1], flags); + if (err) { + fprintf(stderr, "swapon failed for %s\n", argv[argc - 1]); + } + + return err; +} From 88e8f61a9ef5cda24932f669d3b224e0216deba9 Mon Sep 17 00:00:00 2001 From: jp abgrall Date: Wed, 26 Jun 2013 03:51:29 +0000 Subject: [PATCH 043/541] Revert "fastboot: add preflash command to help with large downloads+flash" This reverts commit 6f1cd0b2ad7a16d4ec0b5324f992cae33dc34f34. It causes some devices to fail (b/9581613). Revert for now. Change-Id: I076158c704c2b4f0459322aa157c19ebd0c4615f --- fastboot/engine.c | 13 +------------ fastboot/fastboot_protocol.txt | 18 +++++++++--------- 2 files changed, 10 insertions(+), 21 deletions(-) diff --git a/fastboot/engine.c b/fastboot/engine.c index 4045759bc..8d469911a 100644 --- a/fastboot/engine.c +++ b/fastboot/engine.c @@ -76,7 +76,6 @@ char *mkmsg(const char *fmt, ...) #define OP_NOTICE 4 #define OP_FORMAT 5 #define OP_DOWNLOAD_SPARSE 6 -#define OP_COMMAND_IGNORE_FAIL 7 typedef struct Action Action; @@ -375,8 +374,6 @@ int fb_format(Action *a, usb_handle *usb, int skip_if_not_supported) // Following piece of code is similar to fb_queue_flash() but executes // actions directly without queuing - snprintf(cmd, sizeof(cmd), "preflash:%s", partition); - fb_command(usb, cmd); /* Ignore status */ fprintf(stderr, "sending '%s' (%lli KB)...\n", partition, image.image_size/1024); status = fb_download_data(usb, image.buffer, image.image_size); if (status) goto cleanup; @@ -405,8 +402,6 @@ void fb_queue_flash(const char *ptn, void *data, unsigned sz) { Action *a; - a = queue_action(OP_COMMAND_IGNORE_FAIL, "preflash:%s", ptn); - a->msg = mkmsg("prep for '%s' (%d KB)", ptn, sz / 1024); a = queue_action(OP_DOWNLOAD, ""); a->data = data; a->size = sz; @@ -420,8 +415,6 @@ void fb_queue_flash_sparse(const char *ptn, struct sparse_file *s, unsigned sz) { Action *a; - a = queue_action(OP_COMMAND_IGNORE_FAIL, "preflash:%s", ptn); - a->msg = mkmsg("prep for sparse '%s' (%d KB)", ptn, sz / 1024); a = queue_action(OP_DOWNLOAD_SPARSE, ""); a->data = s; a->size = 0; @@ -582,9 +575,7 @@ void fb_queue_command(const char *cmd, const char *msg) void fb_queue_download(const char *name, void *data, unsigned size) { - Action *a; - a = queue_action(OP_COMMAND_IGNORE_FAIL, "preflash:"); - a = queue_action(OP_DOWNLOAD, ""); + Action *a = queue_action(OP_DOWNLOAD, ""); a->data = data; a->size = size; a->msg = mkmsg("downloading '%s'", name); @@ -623,8 +614,6 @@ int fb_execute_queue(usb_handle *usb) status = fb_command(usb, a->cmd); status = a->func(a, status, status ? fb_get_error() : ""); if (status) break; - } else if (a->op == OP_COMMAND_IGNORE_FAIL) { - fb_command(usb, a->cmd); /* Ignore status */ } else if (a->op == OP_QUERY) { status = fb_command_response(usb, a->cmd, resp); status = a->func(a, status, status ? fb_get_error() : resp); diff --git a/fastboot/fastboot_protocol.txt b/fastboot/fastboot_protocol.txt index eede5d1ea..2248992d2 100644 --- a/fastboot/fastboot_protocol.txt +++ b/fastboot/fastboot_protocol.txt @@ -1,5 +1,5 @@ -FastBoot Version 0.4++ +FastBoot Version 0.4 ---------------------- The fastboot protocol is a mechanism for communicating with bootloaders @@ -67,6 +67,10 @@ Transport and Framing Example Session --------------- +Host: "getvar:version" request version variable + +Client: "OKAY0.4" return version "0.4" + Host: "getvar:nonexistant" request some undefined variable Client: "OKAY" return value "" @@ -119,11 +123,6 @@ Command Reference "flash:%s" Write the previously downloaded image to the named partition (if possible). - "preflash:%s" Optionally prepare for a download + flash. - E.g. flash directly during download. To deal with a - failed "flash" followed by a "boot", we send - "preflash:". - "erase:%s" Erase the indicated partition (clear to 0xFFs) "boot" The previously downloaded data is a boot.img @@ -141,9 +140,7 @@ Command Reference "powerdown" Power off the device. -* Note about sparse files - Large files can be split up using libsparse, and sent to the bootloader - as repeated chunks of "download:%08x" + "flash:%s". + Client Variables ---------------- @@ -154,6 +151,9 @@ on it. The various currently defined names are: + version Version of FastBoot protocol supported. + It should be "0.3" for this document. + version-bootloader Version string for the Bootloader. version-baseband Version string of the Baseband Software From 13495a1cbb6c6b7ec617488f272bc02f2107a63c Mon Sep 17 00:00:00 2001 From: Ken Sumrall Date: Tue, 25 Jun 2013 21:42:23 -0700 Subject: [PATCH 044/541] Update the touch command to take a human readable timestamp Now the -t option to the toolbox touch command takes a timestamp in the form of YYYYMMDD.hhmmss Change-Id: I3812700edaa1a06590a07c15b050721b49e9b7e0 --- toolbox/touch.c | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/toolbox/touch.c b/toolbox/touch.c index b8ab310d0..52ddf2a55 100644 --- a/toolbox/touch.c +++ b/toolbox/touch.c @@ -5,13 +5,40 @@ #include #include #include +#include static void usage(void) { - fprintf(stderr, "touch: usage: touch [-alm] [-t time_t] \n"); + fprintf(stderr, "touch: usage: touch [-alm] [-t YYYYMMDD[.hhmmss]] \n"); exit(1); } +static time_t parse_time(char *s) +{ + struct tm tm; + int day = atoi(s); + int hour = 0; + + while (*s && *s != '.') { + s++; + } + + if (*s) { + s++; + hour = atoi(s); + } + + tm.tm_year = day / 10000 - 1900; + tm.tm_mon = (day % 10000) / 100 - 1; + tm.tm_mday = day % 100; + tm.tm_hour = hour / 10000; + tm.tm_min = (hour % 10000) / 100; + tm.tm_sec = hour % 100; + tm.tm_isdst = -1; + + return mktime(&tm); +} + int touch_main(int argc, char *argv[]) { int i, fd, aflag = 0, mflag = 0, debug = 0, flags = 0; @@ -31,9 +58,9 @@ int touch_main(int argc, char *argv[]) case 't': if ((i+1) >= argc) usage(); - specified_time.tv_sec = atol(argv[++i]); - if (specified_time.tv_sec == 0) { - fprintf(stderr, "touch: invalid time_t\n"); + specified_time.tv_sec = parse_time(argv[++i]); + if (specified_time.tv_sec == -1) { + fprintf(stderr, "touch: invalid timestamp specified\n"); exit(1); } specified_time.tv_nsec = 0; From 035482976368dea4ddbda62230a6ec69025a65c3 Mon Sep 17 00:00:00 2001 From: Ken Sumrall Date: Tue, 25 Jun 2013 22:29:06 -0700 Subject: [PATCH 045/541] Do not exit early on errors when -f is specified When running with the -f option, do not stop recursion or proccessing command line args if an error occurs. Continue trying to remove all the items specified on the command line. However, still return an error status if some files could not be removed. Change-Id: I83d66babe833da8a68aad68248647ba0601c5d32 --- toolbox/rm.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/toolbox/rm.c b/toolbox/rm.c index 127cbc450..957b5867f 100644 --- a/toolbox/rm.c +++ b/toolbox/rm.c @@ -45,8 +45,10 @@ static int unlink_recursive(const char* name, int flags) continue; sprintf(dn, "%s/%s", name, de->d_name); if (unlink_recursive(dn, flags) < 0) { - fail = 1; - break; + if (!(flags & OPT_FORCE)) { + fail = 1; + break; + } } errno = 0; } @@ -71,6 +73,7 @@ int rm_main(int argc, char *argv[]) int ret; int i, c; int flags = 0; + int something_failed = 0; if (argc < 2) return usage(); @@ -110,10 +113,14 @@ int rm_main(int argc, char *argv[]) if (ret < 0) { fprintf(stderr, "rm failed for %s, %s\n", argv[i], strerror(errno)); - return -1; + if (!(flags & OPT_FORCE)) { + return -1; + } else { + something_failed = 1; + } } } - return 0; + return something_failed; } From 05529a1a10711d93745a9fcf07520f65a3eb4f3d Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Mon, 25 Mar 2013 16:07:43 +0000 Subject: [PATCH 046/541] Add defines for audio offload and non-blocking write - Defines audio output flags for offloaded playback of compressed streams, and non-blocking HAL write()/drain() - Adds audio_offload_info_t to pass additional information about a compressed stream when offloading Change-Id: I4592d0877edd39e68d0c1035d0652adbd397bf3a Signed-off-by: Richard Fitzgerald Signed-off-by: Eric Laurent --- include/system/audio.h | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/include/system/audio.h b/include/system/audio.h index da235dd1b..c49b0eec4 100644 --- a/include/system/audio.h +++ b/include/system/audio.h @@ -383,9 +383,41 @@ typedef enum { // controls related to voice calls. AUDIO_OUTPUT_FLAG_FAST = 0x4, // output supports "fast tracks", // defined elsewhere - AUDIO_OUTPUT_FLAG_DEEP_BUFFER = 0x8 // use deep audio buffers + AUDIO_OUTPUT_FLAG_DEEP_BUFFER = 0x8, // use deep audio buffers + AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD = 0x10, // offload playback of compressed + // streams to hardware codec + AUDIO_OUTPUT_FLAG_NON_BLOCKING = 0x20 // use non-blocking write } audio_output_flags_t; +/* Additional information about compressed streams offloaded to + * hardware playback + * The version and size fields must be initialized by the caller by using + * one of the constants defined here. + */ +typedef struct { + uint16_t version; // version of the info structure + uint16_t size; // total size of the structure including version and size + uint32_t sample_rate; // sample rate in Hz + audio_channel_mask_t channel_mask; // channel mask + audio_format_t format; // audio format + audio_stream_type_t stream_type; // stream type + uint32_t bit_rate; // bit rate in bits per second + int64_t duration_us; // duration in microseconds, -1 if unknown + bool has_video; // true if stream is tied to a video stream + bool is_streaming; // true if streaming, false if local playback +} audio_offload_info_t; + +#define AUDIO_MAKE_OFFLOAD_INFO_VERSION(maj,min) \ + ((((maj) & 0xff) << 8) | ((min) & 0xff)) + +#define AUDIO_OFFLOAD_INFO_VERSION_0_1 AUDIO_MAKE_OFFLOAD_INFO_VERSION(0, 1) +#define AUDIO_OFFLOAD_INFO_VERSION_CURRENT AUDIO_OFFLOAD_INFO_VERSION_0_1 + +static const audio_offload_info_t AUDIO_INFO_INITIALIZER = { + version: AUDIO_OFFLOAD_INFO_VERSION_CURRENT, + size: sizeof(audio_offload_info_t), +}; + static inline bool audio_is_output_device(audio_devices_t device) { if (((device & AUDIO_DEVICE_BIT_IN) == 0) && From 4838aa1b7b0f279ae6194ab1606922e2d1540f90 Mon Sep 17 00:00:00 2001 From: Nick Kralevich Date: Wed, 26 Jun 2013 15:37:26 -0700 Subject: [PATCH 047/541] init: allow disabling selinux via a kernel command line Create a new "androidboot.selinux" option, to control how userspace handles SELinux. This kernel command line can have three options: * disabled * permissive * enforcing "disabled" completely disables userspace support for SELinux. No policy is ever loaded, nor is the SELinux filesystem /sys/fs/selinux ever mounted. "permissive" loads the SELinux policy, but puts SELinux into permissive mode. SELinux policy violations are logged, but not rejected. "enforcing", the default, loads the SELinux policy, and places SELinux into enforcing mode. Policy violations are rejected. This change addresses post review comments for change b710ed21dec88c0dde8209264df054c842561589 . Change-Id: I912583db8e6a0e9c63380de32ad8ffc47a8a440f --- init/init.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 48 insertions(+), 3 deletions(-) diff --git a/init/init.c b/init/init.c index 419662053..d75adca5a 100644 --- a/init/init.c +++ b/init/init.c @@ -793,8 +793,52 @@ void selinux_init_all_handles(void) sehandle_prop = selinux_android_prop_context_handle(); } +static bool selinux_is_disabled(void) +{ + char tmp[PROP_VALUE_MAX]; + + if (access("/sys/fs/selinux", F_OK) != 0) { + /* SELinux is not compiled into the kernel, or has been disabled + * via the kernel command line "selinux=0". + */ + return true; + } + + if ((property_get("ro.boot.selinux", tmp) != 0) && (strcmp(tmp, "disabled") == 0)) { + /* SELinux is compiled into the kernel, but we've been told to disable it. */ + return true; + } + + return false; +} + +static bool selinux_is_enforcing(void) +{ + char tmp[PROP_VALUE_MAX]; + + if (property_get("ro.boot.selinux", tmp) == 0) { + /* Property is not set. Assume enforcing */ + return true; + } + + if (strcmp(tmp, "permissive") == 0) { + /* SELinux is in the kernel, but we've been told to go into permissive mode */ + return false; + } + + if (strcmp(tmp, "enforcing") != 0) { + ERROR("SELinux: Unknown value of ro.boot.selinux. Got: \"%s\". Assuming enforcing.\n", tmp); + } + + return true; +} + int selinux_reload_policy(void) { + if (selinux_is_disabled()) { + return -1; + } + INFO("SELinux: Attempting to reload policy files\n"); if (selinux_android_reload_policy() == -1) { @@ -819,8 +863,7 @@ int audit_callback(void *data, security_class_t cls, char *buf, size_t len) static void selinux_initialize(void) { - if (access("/sys/fs/selinux", F_OK) != 0) { - // SELinux is not compiled into this kernel. Fail gracefully. + if (selinux_is_disabled()) { return; } @@ -832,7 +875,9 @@ static void selinux_initialize(void) } selinux_init_all_handles(); - security_setenforce(1); + bool is_enforcing = selinux_is_enforcing(); + INFO("SELinux: security_setenforce(%d)\n", is_enforcing); + security_setenforce(is_enforcing); } int main(int argc, char **argv) From c9cce4b981c79d543d2d10d2365e81fb39ad3da9 Mon Sep 17 00:00:00 2001 From: Rom Lemarchand Date: Fri, 28 Jun 2013 09:45:08 -0700 Subject: [PATCH 048/541] fastboot: Remove legacy MINGW workarounds The version of MINGW we compile with has more advanced POSIX support. Removing legacy MINGW workarounds as those are not needed anymore. Change-Id: Id5d67176b719db6c3667be6d63c41432e0ba9f30 Signed-off-by: Rom Lemarchand --- fastboot/engine.c | 14 --------- fastboot/fastboot.c | 13 +++----- fastboot/util_windows.c | 67 ----------------------------------------- 3 files changed, 4 insertions(+), 90 deletions(-) diff --git a/fastboot/engine.c b/fastboot/engine.c index 8d469911a..b07e74251 100644 --- a/fastboot/engine.c +++ b/fastboot/engine.c @@ -286,21 +286,7 @@ void generate_ext4_image(struct image_data *image) int fd; struct stat st; -#ifdef USE_MINGW - /* Ideally we should use tmpfile() here, the same as with unix version. - * But unfortunately it is not portable as it is not clear whether this - * function opens file in TEXT or BINARY mode. - * - * There are also some reports it is buggy: - * http://pdplab.it.uom.gr/teaching/gcc_manuals/gnulib.html#tmpfile - * http://www.mega-nerd.com/erikd/Blog/Windiots/tmpfile.html - */ - char *filename = tempnam(getenv("TEMP"), "fastboot-format.img"); - fd = open(filename, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, 0644); - unlink(filename); -#else fd = fileno(tmpfile()); -#endif make_ext4fs_sparse_fd(fd, image->partition_size, NULL, NULL); fstat(fd, &st); diff --git a/fastboot/fastboot.c b/fastboot/fastboot.c index 447b2572b..e469d97f0 100644 --- a/fastboot/fastboot.c +++ b/fastboot/fastboot.c @@ -135,21 +135,17 @@ char *find_item(const char *item, const char *product) return strdup(path); } -#ifdef _WIN32 -void *load_file(const char *fn, unsigned *_sz); -int64_t file_size(const char *fn); -#else #if defined(__APPLE__) && defined(__MACH__) #define lseek64 lseek #define off64_t off_t #endif -int64_t file_size(const char *fn) +static int64_t file_size(const char *fn) { off64_t off; int fd; - fd = open(fn, O_RDONLY); + fd = open(fn, O_RDONLY | O_BINARY); if (fd < 0) return -1; off = lseek64(fd, 0, SEEK_END); @@ -158,7 +154,7 @@ int64_t file_size(const char *fn) return off; } -void *load_file(const char *fn, unsigned *_sz) +static void *load_file(const char *fn, unsigned *_sz) { char *data; int sz; @@ -166,7 +162,7 @@ void *load_file(const char *fn, unsigned *_sz) int errno_tmp; data = 0; - fd = open(fn, O_RDONLY); + fd = open(fn, O_RDONLY | O_BINARY); if(fd < 0) return 0; sz = lseek(fd, 0, SEEK_END); @@ -190,7 +186,6 @@ oops: errno = errno_tmp; return 0; } -#endif int match_fastboot_with_serial(usb_ifc_info *info, const char *local_serial) { diff --git a/fastboot/util_windows.c b/fastboot/util_windows.c index 9e029fdb8..74a5c27b4 100644 --- a/fastboot/util_windows.c +++ b/fastboot/util_windows.c @@ -36,29 +36,6 @@ #include -int64_t file_size(const char *fn) -{ - HANDLE file; - char *data; - DWORD sz; - - file = CreateFile( fn, - GENERIC_READ, - FILE_SHARE_READ, - NULL, - OPEN_EXISTING, - 0, - NULL ); - - if (file == INVALID_HANDLE_VALUE) - return -1; - - sz = GetFileSize( file, NULL ); - CloseHandle( file ); - - return sz; -} - void get_my_path(char exe[PATH_MAX]) { char* r; @@ -70,47 +47,3 @@ void get_my_path(char exe[PATH_MAX]) *r = 0; } - -void *load_file(const char *fn, unsigned *_sz) -{ - HANDLE file; - char *data; - DWORD sz; - - file = CreateFile( fn, - GENERIC_READ, - FILE_SHARE_READ, - NULL, - OPEN_EXISTING, - 0, - NULL ); - - if (file == INVALID_HANDLE_VALUE) - return NULL; - - sz = GetFileSize( file, NULL ); - data = NULL; - - if (sz > 0) { - data = (char*) malloc( sz ); - if (data == NULL) { - fprintf(stderr, "load_file: could not allocate %ld bytes\n", sz ); - sz = 0; - } else { - DWORD out_bytes; - - if ( !ReadFile( file, data, sz, &out_bytes, NULL ) || - out_bytes != sz ) - { - fprintf(stderr, "load_file: could not read %ld bytes from '%s'\n", sz, fn); - free(data); - data = NULL; - sz = 0; - } - } - } - CloseHandle( file ); - - *_sz = (unsigned) sz; - return data; -} From 622810ceff6d98779171c68391465c7434adeb1d Mon Sep 17 00:00:00 2001 From: Rom Lemarchand Date: Fri, 28 Jun 2013 09:54:59 -0700 Subject: [PATCH 049/541] fastboot: add support for sparse images in flashall and update Change-Id: I66a73b16a988a65fc91fb22a26d11986025089de Signed-off-by: Rom Lemarchand --- fastboot/fastboot.c | 268 ++++++++++++++++++++++++++++---------------- 1 file changed, 172 insertions(+), 96 deletions(-) diff --git a/fastboot/fastboot.c b/fastboot/fastboot.c index e469d97f0..f186c93b8 100644 --- a/fastboot/fastboot.c +++ b/fastboot/fastboot.c @@ -43,6 +43,7 @@ #include #include +#include #include #include @@ -54,6 +55,8 @@ #define O_BINARY 0 #endif +#define ARRAY_SIZE(a) (sizeof(a)/sizeof(*(a))) + char cur_product[FB_RESPONSE_SZ + 1]; void bootimg_set_cmdline(boot_img_hdr *h, const char *cmdline); @@ -81,6 +84,27 @@ unsigned ramdisk_offset = 0x01000000; unsigned second_offset = 0x00f00000; unsigned tags_offset = 0x00000100; +enum fb_buffer_type { + FB_BUFFER, + FB_BUFFER_SPARSE, +}; + +struct fastboot_buffer { + enum fb_buffer_type type; + void *data; + unsigned int sz; +}; + +static struct { + char img_name[13]; + char sig_name[13]; + char part_name[9]; + bool is_optional; +} images[3] = { + {"boot.img", "boot.sig", "boot", false}, + {"recovery.img", "recovery.sig", "recovery", true}, + {"system.img", "system.sig", "system", false}, +}; void die(const char *fmt, ...) { @@ -135,40 +159,28 @@ char *find_item(const char *item, const char *product) return strdup(path); } -#if defined(__APPLE__) && defined(__MACH__) -#define lseek64 lseek -#define off64_t off_t -#endif - -static int64_t file_size(const char *fn) +static int64_t file_size(int fd) { - off64_t off; - int fd; + struct stat st; + int ret; - fd = open(fn, O_RDONLY | O_BINARY); - if (fd < 0) return -1; + ret = fstat(fd, &st); - off = lseek64(fd, 0, SEEK_END); - close(fd); - - return off; + return ret ? -1 : st.st_size; } -static void *load_file(const char *fn, unsigned *_sz) +static void *load_fd(int fd, unsigned *_sz) { char *data; int sz; - int fd; int errno_tmp; data = 0; - fd = open(fn, O_RDONLY | O_BINARY); - if(fd < 0) return 0; - sz = lseek(fd, 0, SEEK_END); - if(sz < 0) goto oops; - - if(lseek(fd, 0, SEEK_SET) != 0) goto oops; + sz = file_size(fd); + if (sz < 0) { + goto oops; + } data = (char*) malloc(sz); if(data == 0) goto oops; @@ -187,6 +199,16 @@ oops: return 0; } +static void *load_file(const char *fn, unsigned *_sz) +{ + int fd; + + fd = open(fn, O_RDONLY | O_BINARY); + if(fd < 0) return 0; + + return load_fd(fd, _sz); +} + int match_fastboot_with_serial(usb_ifc_info *info, const char *local_serial) { if(!(vendor_id && (info->dev_vendor == vendor_id)) && @@ -392,6 +414,31 @@ void *unzip_file(zipfile_t zip, const char *name, unsigned *sz) return data; } +static int unzip_to_file(zipfile_t zip, char *name) +{ + int fd; + char *data; + unsigned sz; + + fd = fileno(tmpfile()); + if (fd < 0) { + return -1; + } + + data = unzip_file(zip, name, &sz); + if (data == 0) { + return -1; + } + + if (write(fd, data, sz) != sz) { + fd = -1; + } + + free(data); + lseek(fd, 0, SEEK_SET); + return fd; +} + static char *strip(char *s) { int n; @@ -490,27 +537,20 @@ void queue_info_dump(void) fb_queue_notice("--------------------------------------------"); } - -struct sparse_file **load_sparse_files(const char *fname, int max_size) +static struct sparse_file **load_sparse_files(int fd, int max_size) { - int fd; struct sparse_file *s; int files; struct sparse_file **out_s; - fd = open(fname, O_RDONLY | O_BINARY); - if (fd < 0) { - die("cannot open '%s'\n", fname); - } - s = sparse_file_import_auto(fd, false); if (!s) { - die("cannot sparse read file '%s'\n", fname); + die("cannot sparse read file\n"); } files = sparse_file_resparse(s, max_size, NULL, 0); if (files < 0) { - die("Failed to resparse '%s'\n", fname); + die("Failed to resparse\n"); } out_s = calloc(sizeof(struct sparse_file *), files + 1); @@ -520,7 +560,7 @@ struct sparse_file **load_sparse_files(const char *fname, int max_size) files = sparse_file_resparse(s, max_size, out_s, files); if (files < 0) { - die("Failed to resparse '%s'\n", fname); + die("Failed to resparse\n"); } return out_s; @@ -581,29 +621,78 @@ static int needs_erase(const char *part) return fb_format_supported(usb, part); } -void do_flash(usb_handle *usb, const char *pname, const char *fname) +static int load_buf_fd(usb_handle *usb, int fd, + struct fastboot_buffer *buf) { int64_t sz64; void *data; int64_t limit; - sz64 = file_size(fname); + sz64 = file_size(fd); + if (sz64 < 0) { + return -1; + } limit = get_sparse_limit(usb, sz64); if (limit) { - struct sparse_file **s = load_sparse_files(fname, limit); + struct sparse_file **s = load_sparse_files(fd, limit); if (s == NULL) { - die("cannot sparse load '%s'\n", fname); - } - while (*s) { - sz64 = sparse_file_len(*s, true, false); - fb_queue_flash_sparse(pname, *s++, sz64); + return -1; } + buf->type = FB_BUFFER_SPARSE; + buf->data = s; } else { unsigned int sz; - data = load_file(fname, &sz); - if (data == 0) die("cannot load '%s': %s\n", fname, strerror(errno)); - fb_queue_flash(pname, data, sz); + data = load_fd(fd, &sz); + if (data == 0) return -1; + buf->type = FB_BUFFER; + buf->data = data; + buf->sz = sz; } + + return 0; +} + +static int load_buf(usb_handle *usb, const char *fname, + struct fastboot_buffer *buf) +{ + int fd; + + fd = open(fname, O_RDONLY | O_BINARY); + if (fd < 0) { + die("cannot open '%s'\n", fname); + } + + return load_buf_fd(usb, fd, buf); +} + +static void flash_buf(const char *pname, struct fastboot_buffer *buf) +{ + struct sparse_file **s; + + switch (buf->type) { + case FB_BUFFER_SPARSE: + s = buf->data; + while (*s) { + int64_t sz64 = sparse_file_len(*s, true, false); + fb_queue_flash_sparse(pname, *s++, sz64); + } + break; + case FB_BUFFER: + fb_queue_flash(pname, buf->data, buf->sz); + break; + default: + die("unknown buffer type: %d", buf->type); + } +} + +void do_flash(usb_handle *usb, const char *pname, const char *fname) +{ + struct fastboot_buffer buf; + + if (load_buf(usb, fname, &buf)) { + die("cannot load '%s'", fname); + } + flash_buf(pname, &buf); } void do_update_signature(zipfile_t zip, char *fn) @@ -616,13 +705,17 @@ void do_update_signature(zipfile_t zip, char *fn) fb_queue_command("signature", "installing signature"); } -void do_update(char *fn, int erase_first) +void do_update(usb_handle *usb, char *fn, int erase_first) { void *zdata; unsigned zsize; void *data; unsigned sz; zipfile_t zip; + int fd; + int rc; + struct fastboot_buffer buf; + int i; queue_info_dump(); @@ -651,30 +744,25 @@ void do_update(char *fn, int erase_first) setup_requirements(data, sz); - data = unzip_file(zip, "boot.img", &sz); - if (data == 0) die("update package missing boot.img"); - do_update_signature(zip, "boot.sig"); - if (erase_first && needs_erase("boot")) { - fb_queue_erase("boot"); - } - fb_queue_flash("boot", data, sz); - - data = unzip_file(zip, "recovery.img", &sz); - if (data != 0) { - do_update_signature(zip, "recovery.sig"); - if (erase_first && needs_erase("recovery")) { - fb_queue_erase("recovery"); + for (i = 0; i < ARRAY_SIZE(images); i++) { + fd = unzip_to_file(zip, images[i].img_name); + if (fd < 0) { + if (images[i].is_optional) + continue; + die("update package missing %s", images[i].img_name); } - fb_queue_flash("recovery", data, sz); + rc = load_buf_fd(usb, fd, &buf); + if (rc) die("cannot load %s from flash", images[i].img_name); + do_update_signature(zip, images[i].sig_name); + if (erase_first && needs_erase(images[i].part_name)) { + fb_queue_erase(images[i].part_name); + } + flash_buf(images[i].part_name, &buf); + /* not closing the fd here since the sparse code keeps the fd around + * but hasn't mmaped data yet. The tmpfile will get cleaned up when the + * program exits. + */ } - - data = unzip_file(zip, "system.img", &sz); - if (data == 0) die("update package missing system.img"); - do_update_signature(zip, "system.sig"); - if (erase_first && needs_erase("system")) { - fb_queue_erase("system"); - } - fb_queue_flash("system", data, sz); } void do_send_signature(char *fn) @@ -695,11 +783,13 @@ void do_send_signature(char *fn) fb_queue_command("signature", "installing signature"); } -void do_flashall(int erase_first) +void do_flashall(usb_handle *usb, int erase_first) { char *fname; void *data; unsigned sz; + struct fastboot_buffer buf; + int i; queue_info_dump(); @@ -711,33 +801,19 @@ void do_flashall(int erase_first) if (data == 0) die("could not load android-info.txt: %s", strerror(errno)); setup_requirements(data, sz); - fname = find_item("boot", product); - data = load_file(fname, &sz); - if (data == 0) die("could not load boot.img: %s", strerror(errno)); - do_send_signature(fname); - if (erase_first && needs_erase("boot")) { - fb_queue_erase("boot"); - } - fb_queue_flash("boot", data, sz); - - fname = find_item("recovery", product); - data = load_file(fname, &sz); - if (data != 0) { - do_send_signature(fname); - if (erase_first && needs_erase("recovery")) { - fb_queue_erase("recovery"); + for (i = 0; i < ARRAY_SIZE(images); i++) { + fname = find_item(images[i].part_name, product); + if (load_buf(usb, fname, &buf)) { + if (images[i].is_optional) + continue; + die("could not load %s\n", images[i].img_name); } - fb_queue_flash("recovery", data, sz); + do_send_signature(fname); + if (erase_first && needs_erase(images[i].part_name)) { + fb_queue_erase(images[i].part_name); + } + flash_buf(images[i].part_name, &buf); } - - fname = find_item("system", product); - data = load_file(fname, &sz); - if (data == 0) die("could not load system.img: %s", strerror(errno)); - do_send_signature(fname); - if (erase_first && needs_erase("system")) { - fb_queue_erase("system"); - } - fb_queue_flash("system", data, sz); } #define skip(n) do { argc -= (n); argv += (n); } while (0) @@ -997,14 +1073,14 @@ int main(int argc, char **argv) fb_queue_flash(pname, data, sz); } else if(!strcmp(*argv, "flashall")) { skip(1); - do_flashall(erase_first); + do_flashall(usb, erase_first); wants_reboot = 1; } else if(!strcmp(*argv, "update")) { if (argc > 1) { - do_update(argv[1], erase_first); + do_update(usb, argv[1], erase_first); skip(2); } else { - do_update("update.zip", erase_first); + do_update(usb, "update.zip", erase_first); skip(1); } wants_reboot = 1; From 5bc31a2632f453e03edac714b865773970bba608 Mon Sep 17 00:00:00 2001 From: Ken Sumrall Date: Mon, 8 Jul 2013 19:11:55 -0700 Subject: [PATCH 050/541] Add support for swap entries in fstab Swap entries can optionally specify a swapprio= or zramsize= flag in the fs_mgr flags field. Change-Id: I30530501efd4112af4e158898a9f65f6443c4fdb --- fs_mgr/fs_mgr.c | 174 ++++++++++++++++++++++++++++++---------- fs_mgr/fs_mgr_priv.h | 2 + fs_mgr/include/fs_mgr.h | 3 + 3 files changed, 137 insertions(+), 42 deletions(-) diff --git a/fs_mgr/fs_mgr.c b/fs_mgr/fs_mgr.c index 08483426f..2761545bb 100644 --- a/fs_mgr/fs_mgr.c +++ b/fs_mgr/fs_mgr.c @@ -27,6 +27,12 @@ #include #include #include +#include +/* XXX These need to be obtained from kernel headers. See b/9336527 */ +#define SWAP_FLAG_PREFER 0x8000 +#define SWAP_FLAG_PRIO_MASK 0x7fff +#define SWAP_FLAG_PRIO_SHIFT 0 +#define SWAP_FLAG_DISCARD 0x10000 #include #include @@ -39,6 +45,9 @@ #define KEY_IN_FOOTER "footer" #define E2FSCK_BIN "/system/bin/e2fsck" +#define MKSWAP_BIN "/system/bin/mkswap" + +#define ZRAM_CONF_DEV "/sys/block/zram0/disksize" #define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a))) @@ -74,10 +83,21 @@ static struct flag_list fs_mgr_flags[] = { { "voldmanaged=",MF_VOLDMANAGED}, { "length=", MF_LENGTH }, { "recoveryonly",MF_RECOVERYONLY }, + { "swapprio=", MF_SWAPPRIO }, + { "zramsize=", MF_ZRAMSIZE }, { "defaults", 0 }, { 0, 0 }, }; +struct fs_mgr_flag_values { + char *key_loc; + long long part_length; + char *label; + int partnum; + int swap_prio; + unsigned int zram_size; +}; + /* * gettime() - returns the time in seconds of the system's monotonic clock or * zero on error. @@ -109,7 +129,7 @@ static int wait_for_file(const char *filename, int timeout) } static int parse_flags(char *flags, struct flag_list *fl, - char **key_loc, long long *part_length, char **label, int *partnum, + struct fs_mgr_flag_values *flag_vals, char *fs_options, int fs_options_len) { int f = 0; @@ -117,21 +137,12 @@ static int parse_flags(char *flags, struct flag_list *fl, char *p; char *savep; - /* initialize key_loc to null, if we find an MF_CRYPT flag, - * then we'll set key_loc to the proper value */ - if (key_loc) { - *key_loc = NULL; - } - /* initialize part_length to 0, if we find an MF_LENGTH flag, - * then we'll set part_length to the proper value */ - if (part_length) { - *part_length = 0; - } - if (partnum) { - *partnum = -1; - } - if (label) { - *label = NULL; + /* initialize flag values. If we find a relevant flag, we'll + * update the value */ + if (flag_vals) { + memset(flag_vals, 0, sizeof(*flag_vals)); + flag_vals->partnum = -1; + flag_vals->swap_prio = -1; /* negative means it wasn't specified. */ } /* initialize fs_options to the null string */ @@ -147,17 +158,17 @@ static int parse_flags(char *flags, struct flag_list *fl, for (i = 0; fl[i].name; i++) { if (!strncmp(p, fl[i].name, strlen(fl[i].name))) { f |= fl[i].flag; - if ((fl[i].flag == MF_CRYPT) && key_loc) { + if ((fl[i].flag == MF_CRYPT) && flag_vals) { /* The encryptable flag is followed by an = and the * location of the keys. Get it and return it. */ - *key_loc = strdup(strchr(p, '=') + 1); - } else if ((fl[i].flag == MF_LENGTH) && part_length) { + flag_vals->key_loc = strdup(strchr(p, '=') + 1); + } else if ((fl[i].flag == MF_LENGTH) && flag_vals) { /* The length flag is followed by an = and the * size of the partition. Get it and return it. */ - *part_length = strtoll(strchr(p, '=') + 1, NULL, 0); - } else if ((fl[i].flag == MF_VOLDMANAGED) && label && partnum) { + flag_vals->part_length = strtoll(strchr(p, '=') + 1, NULL, 0); + } else if ((fl[i].flag == MF_VOLDMANAGED) && flag_vals) { /* The voldmanaged flag is followed by an = and the * label, a colon and the partition number or the * word "auto", e.g. @@ -171,17 +182,21 @@ static int parse_flags(char *flags, struct flag_list *fl, label_start = strchr(p, '=') + 1; label_end = strchr(p, ':'); if (label_end) { - *label = strndup(label_start, - (int) (label_end - label_start)); + flag_vals->label = strndup(label_start, + (int) (label_end - label_start)); part_start = strchr(p, ':') + 1; if (!strcmp(part_start, "auto")) { - *partnum = -1; + flag_vals->partnum = -1; } else { - *partnum = strtol(part_start, NULL, 0); + flag_vals->partnum = strtol(part_start, NULL, 0); } } else { ERROR("Warning: voldmanaged= flag malformed\n"); } + } else if ((fl[i].flag == MF_SWAPPRIO) && flag_vals) { + flag_vals->swap_prio = strtoll(strchr(p, '=') + 1, NULL, 0); + } else if ((fl[i].flag == MF_ZRAMSIZE) && flag_vals) { + flag_vals->zram_size = strtoll(strchr(p, '=') + 1, NULL, 0); } break; } @@ -282,10 +297,7 @@ struct fstab *fs_mgr_read_fstab(const char *fstab_path) char *save_ptr, *p; struct fstab *fstab; struct fstab_rec *recs; - char *key_loc; - long long part_length; - char *label; - int partnum; + struct fs_mgr_flag_values flag_vals; #define FS_OPTIONS_LEN 1024 char tmp_fs_options[FS_OPTIONS_LEN]; @@ -375,8 +387,7 @@ struct fstab *fs_mgr_read_fstab(const char *fstab_path) return 0; } tmp_fs_options[0] = '\0'; - fstab->recs[cnt].flags = parse_flags(p, mount_flags, - NULL, NULL, NULL, NULL, + fstab->recs[cnt].flags = parse_flags(p, mount_flags, NULL, tmp_fs_options, FS_OPTIONS_LEN); /* fs_options are optional */ @@ -391,13 +402,13 @@ struct fstab *fs_mgr_read_fstab(const char *fstab_path) return 0; } fstab->recs[cnt].fs_mgr_flags = parse_flags(p, fs_mgr_flags, - &key_loc, &part_length, - &label, &partnum, - NULL, 0); - fstab->recs[cnt].key_loc = key_loc; - fstab->recs[cnt].length = part_length; - fstab->recs[cnt].label = label; - fstab->recs[cnt].partnum = partnum; + &flag_vals, NULL, 0); + fstab->recs[cnt].key_loc = flag_vals.key_loc; + fstab->recs[cnt].length = flag_vals.part_length; + fstab->recs[cnt].label = flag_vals.label; + fstab->recs[cnt].partnum = flag_vals.partnum; + fstab->recs[cnt].swap_prio = flag_vals.swap_prio; + fstab->recs[cnt].zram_size = flag_vals.zram_size; cnt++; } fclose(fstab_file); @@ -561,8 +572,9 @@ int fs_mgr_mount_all(struct fstab *fstab) continue; } - /* Skip raw partition entries such as boot, recovery, etc */ - if (!strcmp(fstab->recs[i].fs_type, "emmc") || + /* Skip swap and raw partition entries such as boot, recovery, etc */ + if (!strcmp(fstab->recs[i].fs_type, "swap") || + !strcmp(fstab->recs[i].fs_type, "emmc") || !strcmp(fstab->recs[i].fs_type, "mtd")) { continue; } @@ -634,8 +646,9 @@ int fs_mgr_do_mount(struct fstab *fstab, char *n_name, char *n_blk_device, } /* We found our match */ - /* If this is a raw partition, report an error */ - if (!strcmp(fstab->recs[i].fs_type, "emmc") || + /* If this swap or a raw partition, report an error */ + if (!strcmp(fstab->recs[i].fs_type, "swap") || + !strcmp(fstab->recs[i].fs_type, "emmc") || !strcmp(fstab->recs[i].fs_type, "mtd")) { ERROR("Cannot mount filesystem of type %s on %s\n", fstab->recs[i].fs_type, n_blk_device); @@ -714,6 +727,83 @@ int fs_mgr_unmount_all(struct fstab *fstab) return ret; } + +/* This must be called after mount_all, because the mkswap command needs to be + * available. + */ +int fs_mgr_swapon_all(struct fstab *fstab) +{ + int i = 0; + int flags = 0; + int err = 0; + int ret = 0; + int status; + char *mkswap_argv[2] = { + MKSWAP_BIN, + NULL + }; + + if (!fstab) { + return -1; + } + + for (i = 0; i < fstab->num_entries; i++) { + /* Skip non-swap entries */ + if (strcmp(fstab->recs[i].fs_type, "swap")) { + continue; + } + + if (fstab->recs[i].zram_size > 0) { + /* A zram_size was specified, so we need to configure the + * device. There is no point in having multiple zram devices + * on a system (all the memory comes from the same pool) so + * we can assume the device number is 0. + */ + FILE *zram_fp; + + zram_fp = fopen(ZRAM_CONF_DEV, "r+"); + if (zram_fp == NULL) { + ERROR("Unable to open zram conf device " ZRAM_CONF_DEV); + ret = -1; + continue; + } + fprintf(zram_fp, "%d\n", fstab->recs[i].zram_size); + fclose(zram_fp); + } + + if (fstab->recs[i].fs_mgr_flags & MF_WAIT) { + wait_for_file(fstab->recs[i].blk_device, WAIT_TIMEOUT); + } + + /* Initialize the swap area */ + mkswap_argv[1] = fstab->recs[i].blk_device; + err = android_fork_execvp_ext(ARRAY_SIZE(mkswap_argv), mkswap_argv, + &status, true, LOG_KLOG, false); + if (err) { + ERROR("mkswap failed for %s\n", fstab->recs[i].blk_device); + ret = -1; + continue; + } + + /* If -1, then no priority was specified in fstab, so don't set + * SWAP_FLAG_PREFER or encode the priority */ + if (fstab->recs[i].swap_prio >= 0) { + flags = (fstab->recs[i].swap_prio << SWAP_FLAG_PRIO_SHIFT) & + SWAP_FLAG_PRIO_MASK; + flags |= SWAP_FLAG_PREFER; + } else { + flags = 0; + } + err = swapon(fstab->recs[i].blk_device, flags); + if (err) { + ERROR("swapon failed for %s\n", fstab->recs[i].blk_device); + ret = -1; + } + } + + return ret; +} + /* * key_loc must be at least PROPERTY_VALUE_MAX bytes long * diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h index 75dad498d..217181502 100644 --- a/fs_mgr/fs_mgr_priv.h +++ b/fs_mgr/fs_mgr_priv.h @@ -69,6 +69,8 @@ #define MF_VOLDMANAGED 0x10 #define MF_LENGTH 0x20 #define MF_RECOVERYONLY 0x40 +#define MF_SWAPPRIO 0x80 +#define MF_ZRAMSIZE 0x100 #endif /* __CORE_FS_MGR_PRIV_H */ diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h index 05bcc1bdf..110e73830 100644 --- a/fs_mgr/include/fs_mgr.h +++ b/fs_mgr/include/fs_mgr.h @@ -38,6 +38,8 @@ struct fstab_rec { long long length; char *label; int partnum; + int swap_prio; + unsigned int zram_size; }; struct fstab *fs_mgr_read_fstab(const char *fstab_path); @@ -56,6 +58,7 @@ struct fstab_rec *fs_mgr_get_entry_for_mount_point(struct fstab *fstab, const ch int fs_mgr_is_voldmanaged(struct fstab_rec *fstab); int fs_mgr_is_nonremovable(struct fstab_rec *fstab); int fs_mgr_is_encryptable(struct fstab_rec *fstab); +int fs_mgr_swapon_all(struct fstab *fstab); #ifdef __cplusplus } #endif From a76baaabe56b98e23861e89722bd1425f92dbc2c Mon Sep 17 00:00:00 2001 From: Ken Sumrall Date: Tue, 9 Jul 2013 18:42:09 -0700 Subject: [PATCH 051/541] Add swapon_all to init's builtin commands For enabling swap devices found in the fstab file Change-Id: Iec4fd4d208df3d7a161a90d95f8606e8b516334a --- init/builtins.c | 12 ++++++++++++ init/init_parser.c | 1 + init/keywords.h | 2 ++ 3 files changed, 15 insertions(+) diff --git a/init/builtins.c b/init/builtins.c index 6e37d0867..bfc0ddb15 100644 --- a/init/builtins.c +++ b/init/builtins.c @@ -516,6 +516,18 @@ int do_mount_all(int nargs, char **args) return ret; } +int do_swapon_all(int nargs, char **args) +{ + struct fstab *fstab; + int ret; + + fstab = fs_mgr_read_fstab(args[1]); + ret = fs_mgr_swapon_all(fstab); + fs_mgr_free_fstab(fstab); + + return ret; +} + int do_setcon(int nargs, char **args) { if (is_selinux_enabled() <= 0) return 0; diff --git a/init/init_parser.c b/init/init_parser.c index 28bf30c14..776c699aa 100644 --- a/init/init_parser.c +++ b/init/init_parser.c @@ -151,6 +151,7 @@ int lookup_keyword(const char *s) if (!strcmp(s, "ocket")) return K_socket; if (!strcmp(s, "tart")) return K_start; if (!strcmp(s, "top")) return K_stop; + if (!strcmp(s, "wapon_all")) return K_swapon_all; if (!strcmp(s, "ymlink")) return K_symlink; if (!strcmp(s, "ysclktz")) return K_sysclktz; break; diff --git a/init/keywords.h b/init/keywords.h index f14750647..5a44df356 100644 --- a/init/keywords.h +++ b/init/keywords.h @@ -27,6 +27,7 @@ int do_setrlimit(int nargs, char **args); int do_setsebool(int nargs, char **args); int do_start(int nargs, char **args); int do_stop(int nargs, char **args); +int do_swapon_all(int nargs, char **args); int do_trigger(int nargs, char **args); int do_symlink(int nargs, char **args); int do_sysclktz(int nargs, char **args); @@ -84,6 +85,7 @@ enum { KEYWORD(socket, OPTION, 0, 0) KEYWORD(start, COMMAND, 1, do_start) KEYWORD(stop, COMMAND, 1, do_stop) + KEYWORD(swapon_all, COMMAND, 1, do_swapon_all) KEYWORD(trigger, COMMAND, 1, do_trigger) KEYWORD(symlink, COMMAND, 1, do_symlink) KEYWORD(sysclktz, COMMAND, 1, do_sysclktz) From 435a52ef07a5c97bdd717f20cb3115a585eb6d3c Mon Sep 17 00:00:00 2001 From: Rom Lemarchand Date: Wed, 10 Jul 2013 13:00:42 -0700 Subject: [PATCH 052/541] init.rc: configure memory cgroups Create the memory cgroups folders and configure them Change-Id: I06c4bb486b204f7e98b0bc6757deec8bd7f6349e --- rootdir/init.rc | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/rootdir/init.rc b/rootdir/init.rc index 5eab0c3c9..8557af1a6 100644 --- a/rootdir/init.rc +++ b/rootdir/init.rc @@ -52,6 +52,19 @@ loglevel 3 mount cgroup none /acct cpuacct mkdir /acct/uid +# Create cgroup mount point for memory + mount tmpfs none /sys/fs/cgroup + mkdir /sys/fs/cgroup/memory + mount cgroup none /sys/fs/cgroup/memory memory + write /sys/fs/cgroup/memory/memory.move_charge_at_immigrate 1 + chown root system /sys/fs/cgroup/memory/tasks + chmod 0660 /sys/fs/cgroup/memory/tasks + mkdir /sys/fs/cgroup/memory/sw + write /sys/fs/cgroup/memory/sw/memory.swappiness 100 + write /sys/fs/cgroup/memory/sw/memory.move_charge_at_immigrate 1 + chown root system /sys/fs/cgroup/memory/sw/tasks + chmod 0660 /sys/fs/cgroup/memory/sw/tasks + mkdir /system mkdir /data 0771 system system mkdir /cache 0770 system cache From f29c533c49ab1c90eae612b1c454f2c6879a6658 Mon Sep 17 00:00:00 2001 From: Nick Kralevich Date: Thu, 11 Jul 2013 15:38:26 -0700 Subject: [PATCH 053/541] init: call restorecon on /sys Not all files on /sys are not getting labeled properly. Fix them. Change-Id: I9dcff76354e7f50d41f1b6e702836cfbbc149278 --- init/init.c | 1 + init/util.c | 15 +++++++++++++++ init/util.h | 1 + 3 files changed, 17 insertions(+) mode change 100755 => 100644 init/util.c diff --git a/init/init.c b/init/init.c index d75adca5a..b7e34d04c 100644 --- a/init/init.c +++ b/init/init.c @@ -948,6 +948,7 @@ int main(int argc, char **argv) restorecon("/dev"); restorecon("/dev/socket"); restorecon("/dev/__properties__"); + restorecon_recursive("/sys"); is_charger = !strcmp(bootmode, "charger"); diff --git a/init/util.c b/init/util.c old mode 100755 new mode 100644 index 918bc057e..fffc8a478 --- a/init/util.c +++ b/init/util.c @@ -22,6 +22,7 @@ #include #include #include +#include #include @@ -499,3 +500,17 @@ int restorecon(const char *pathname) freecon(secontext); return 0; } + +static int nftw_restorecon(const char* filename, const struct stat* statptr, + int fileflags, struct FTW* pftw) +{ + restorecon(filename); + return 0; +} + +int restorecon_recursive(const char* pathname) +{ + int fd_limit = 20; + int flags = FTW_DEPTH | FTW_MOUNT | FTW_PHYS; + return nftw(pathname, nftw_restorecon, fd_limit, flags); +} diff --git a/init/util.h b/init/util.h index 45905b618..6bca4e689 100644 --- a/init/util.h +++ b/init/util.h @@ -41,4 +41,5 @@ void get_hardware_name(char *hardware, unsigned int *revision); void import_kernel_cmdline(int in_qemu, void (*import_kernel_nv)(char *name, int in_qemu)); int make_dir(const char *path, mode_t mode); int restorecon(const char *pathname); +int restorecon_recursive(const char *pathname); #endif From 3884a9b252c1852e88a2ad75c22b042d25a633f4 Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Fri, 12 Jul 2013 16:52:03 -0700 Subject: [PATCH 054/541] init.rc: Add input flinger. Change-Id: Ibcb1116bdcd9189272db620910b9958c8ffb4552 --- rootdir/init.rc | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/rootdir/init.rc b/rootdir/init.rc index af164f7a8..14519533f 100644 --- a/rootdir/init.rc +++ b/rootdir/init.rc @@ -441,6 +441,7 @@ service servicemanager /system/bin/servicemanager onrestart restart zygote onrestart restart media onrestart restart surfaceflinger + onrestart restart inputflinger onrestart restart drm service vold /system/bin/vold @@ -470,6 +471,12 @@ service surfaceflinger /system/bin/surfaceflinger group graphics drmrpc onrestart restart zygote +service inputflinger /system/bin/inputflinger + class main + user system + group input + onrestart restart zygote + service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server class main socket zygote stream 660 root system From 57fc1ceebc6a8c84fa6e9cbf268b5ffca3052c2d Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Wed, 17 Jul 2013 20:30:12 -0700 Subject: [PATCH 055/541] Split framework jar. Change-Id: I590de05f23669cf625030b479c786423f21c6159 --- rootdir/init.rc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rootdir/init.rc b/rootdir/init.rc index 14519533f..dc0b8a79a 100644 --- a/rootdir/init.rc +++ b/rootdir/init.rc @@ -37,7 +37,7 @@ loglevel 3 export ANDROID_STORAGE /storage export ASEC_MOUNTPOINT /mnt/asec export LOOP_MOUNTPOINT /mnt/obb - export BOOTCLASSPATH /system/framework/core.jar:/system/framework/conscrypt.jar:/system/framework/okhttp.jar:/system/framework/core-junit.jar:/system/framework/bouncycastle.jar:/system/framework/ext.jar:/system/framework/framework.jar:/system/framework/telephony-common.jar:/system/framework/voip-common.jar:/system/framework/mms-common.jar:/system/framework/android.policy.jar:/system/framework/services.jar:/system/framework/apache-xml.jar:/system/framework/webviewchromium.jar + export BOOTCLASSPATH /system/framework/core.jar:/system/framework/conscrypt.jar:/system/framework/okhttp.jar:/system/framework/core-junit.jar:/system/framework/bouncycastle.jar:/system/framework/ext.jar:/system/framework/framework.jar:/system/framework/framework2.jar:/system/framework/telephony-common.jar:/system/framework/voip-common.jar:/system/framework/mms-common.jar:/system/framework/android.policy.jar:/system/framework/services.jar:/system/framework/apache-xml.jar:/system/framework/webviewchromium.jar # Backward compatibility symlink /system/etc /etc From f7326dc0aa2da2df6bf1fa1ae7ef259a0ebdbd7b Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Fri, 19 Jul 2013 14:50:21 -0700 Subject: [PATCH 056/541] Valid channel mask must have at least one channel Also: - Use correct type name audio_channel_mask_t - Use the parameter name 'channelMask' instead of the more ambiguous 'channels' Change-Id: I22bc1821a1bded0e69fe7d99e7b981ff60d77cee --- include/system/audio.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/system/audio.h b/include/system/audio.h index c49b0eec4..6c260dd7f 100644 --- a/include/system/audio.h +++ b/include/system/audio.h @@ -477,18 +477,18 @@ static inline bool audio_is_remote_submix_device(audio_devices_t device) return false; } -static inline bool audio_is_input_channel(uint32_t channel) +static inline bool audio_is_input_channel(audio_channel_mask_t channel) { if ((channel & ~AUDIO_CHANNEL_IN_ALL) == 0) - return true; + return channel != 0; else return false; } -static inline bool audio_is_output_channel(uint32_t channel) +static inline bool audio_is_output_channel(audio_channel_mask_t channel) { if ((channel & ~AUDIO_CHANNEL_OUT_ALL) == 0) - return true; + return channel != 0; else return false; } From 8a84c71acbc84138a685e2482b4c53d4973abf9a Mon Sep 17 00:00:00 2001 From: Alex Ray Date: Tue, 23 Jul 2013 17:04:24 -0700 Subject: [PATCH 057/541] cutils: add thread_defs header Change-Id: I3b70054d9f9740a5980df514221de07df55e7176 --- include/system/thread_defs.h | 74 ++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 include/system/thread_defs.h diff --git a/include/system/thread_defs.h b/include/system/thread_defs.h new file mode 100644 index 000000000..30fe5649c --- /dev/null +++ b/include/system/thread_defs.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2013 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_THREAD_DEFS_H +#define ANDROID_THREAD_DEFS_H + +#include +#include "graphics.h" + +__BEGIN_DECLS + +enum { + /* + * *********************************************** + * ** Keep in sync with android.os.Process.java ** + * *********************************************** + * + * This maps directly to the "nice" priorities we use in Android. + * A thread priority should be chosen inverse-proportionally to + * the amount of work the thread is expected to do. The more work + * a thread will do, the less favorable priority it should get so that + * it doesn't starve the system. Threads not behaving properly might + * be "punished" by the kernel. + * Use the levels below when appropriate. Intermediate values are + * acceptable, preferably use the {MORE|LESS}_FAVORABLE constants below. + */ + ANDROID_PRIORITY_LOWEST = 19, + + /* use for background tasks */ + ANDROID_PRIORITY_BACKGROUND = 10, + + /* most threads run at normal priority */ + ANDROID_PRIORITY_NORMAL = 0, + + /* threads currently running a UI that the user is interacting with */ + ANDROID_PRIORITY_FOREGROUND = -2, + + /* the main UI thread has a slightly more favorable priority */ + ANDROID_PRIORITY_DISPLAY = -4, + + /* ui service treads might want to run at a urgent display (uncommon) */ + ANDROID_PRIORITY_URGENT_DISPLAY = HAL_PRIORITY_URGENT_DISPLAY, + + /* all normal audio threads */ + ANDROID_PRIORITY_AUDIO = -16, + + /* service audio threads (uncommon) */ + ANDROID_PRIORITY_URGENT_AUDIO = -19, + + /* should never be used in practice. regular process might not + * be allowed to use this level */ + ANDROID_PRIORITY_HIGHEST = -20, + + ANDROID_PRIORITY_DEFAULT = ANDROID_PRIORITY_NORMAL, + ANDROID_PRIORITY_MORE_FAVORABLE = -1, + ANDROID_PRIORITY_LESS_FAVORABLE = +1, +}; + +__END_DECLS + +#endif /* ANDROID_THREAD_DEFS_H */ From 42cf2b5945c6e7cefc84b1146ba5057faae91322 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Tue, 23 Jul 2013 19:58:03 -0700 Subject: [PATCH 058/541] fix windows build thread_defs.h gets included by the windows host libandroidfw build, which does not have sys/cdefs.h or BEGIN_DECLS. Switch to using extern "C" manually. Change-Id: I363e6f2d3a64c5efeff54049a2e5dab080fd4715 --- include/system/thread_defs.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/include/system/thread_defs.h b/include/system/thread_defs.h index 30fe5649c..f4d6a57f6 100644 --- a/include/system/thread_defs.h +++ b/include/system/thread_defs.h @@ -17,10 +17,11 @@ #ifndef ANDROID_THREAD_DEFS_H #define ANDROID_THREAD_DEFS_H -#include #include "graphics.h" -__BEGIN_DECLS +#if defined(__cplusplus) +extern "C" { +#endif enum { /* @@ -69,6 +70,8 @@ enum { ANDROID_PRIORITY_LESS_FAVORABLE = +1, }; -__END_DECLS +#if defined(__cplusplus) +} +#endif #endif /* ANDROID_THREAD_DEFS_H */ From 000b1d0c5058d9da4dfec3a6f16b58fd50c34374 Mon Sep 17 00:00:00 2001 From: Alex Ray Date: Tue, 23 Jul 2013 22:20:45 -0700 Subject: [PATCH 059/541] thread_defs: remove trailing whitespace Change-Id: Idba19c64dfde67c40a98c29f68d5e8d4e555a4cf --- include/system/thread_defs.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/include/system/thread_defs.h b/include/system/thread_defs.h index f4d6a57f6..377a48ce9 100644 --- a/include/system/thread_defs.h +++ b/include/system/thread_defs.h @@ -28,11 +28,11 @@ enum { * *********************************************** * ** Keep in sync with android.os.Process.java ** * *********************************************** - * + * * This maps directly to the "nice" priorities we use in Android. * A thread priority should be chosen inverse-proportionally to * the amount of work the thread is expected to do. The more work - * a thread will do, the less favorable priority it should get so that + * a thread will do, the less favorable priority it should get so that * it doesn't starve the system. Threads not behaving properly might * be "punished" by the kernel. * Use the levels below when appropriate. Intermediate values are @@ -42,26 +42,26 @@ enum { /* use for background tasks */ ANDROID_PRIORITY_BACKGROUND = 10, - + /* most threads run at normal priority */ ANDROID_PRIORITY_NORMAL = 0, - + /* threads currently running a UI that the user is interacting with */ ANDROID_PRIORITY_FOREGROUND = -2, /* the main UI thread has a slightly more favorable priority */ ANDROID_PRIORITY_DISPLAY = -4, - + /* ui service treads might want to run at a urgent display (uncommon) */ ANDROID_PRIORITY_URGENT_DISPLAY = HAL_PRIORITY_URGENT_DISPLAY, - + /* all normal audio threads */ ANDROID_PRIORITY_AUDIO = -16, - + /* service audio threads (uncommon) */ ANDROID_PRIORITY_URGENT_AUDIO = -19, - /* should never be used in practice. regular process might not + /* should never be used in practice. regular process might not * be allowed to use this level */ ANDROID_PRIORITY_HIGHEST = -20, From a3d386ea56ef53bb070b87ea7c28e103c5a53e45 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Wed, 6 Feb 2013 21:03:34 -0800 Subject: [PATCH 060/541] fastbootd: userspace implementation of the fastboot device-side protocol Initial commit of fastbootd. A few commands work, but not fully functional yet. Change-Id: I589dee7b327b4460e94b4434aaf9bcf780faa839 --- fastbootd/Android.mk | 38 ++++ fastbootd/bootimg.h | 97 ++++++++++ fastbootd/commands.c | 206 ++++++++++++++++++++ fastbootd/config.c | 161 ++++++++++++++++ fastbootd/debug.h | 42 +++++ fastbootd/fastbootd.c | 45 +++++ fastbootd/protocol.c | 195 +++++++++++++++++++ fastbootd/protocol.h | 61 ++++++ fastbootd/transport.c | 149 +++++++++++++++ fastbootd/transport.h | 41 ++++ fastbootd/usb_linux_client.c | 353 +++++++++++++++++++++++++++++++++++ 11 files changed, 1388 insertions(+) create mode 100644 fastbootd/Android.mk create mode 100644 fastbootd/bootimg.h create mode 100644 fastbootd/commands.c create mode 100644 fastbootd/config.c create mode 100644 fastbootd/debug.h create mode 100644 fastbootd/fastbootd.c create mode 100644 fastbootd/protocol.c create mode 100644 fastbootd/protocol.h create mode 100644 fastbootd/transport.c create mode 100644 fastbootd/transport.h create mode 100644 fastbootd/usb_linux_client.c diff --git a/fastbootd/Android.mk b/fastbootd/Android.mk new file mode 100644 index 000000000..76b28e25b --- /dev/null +++ b/fastbootd/Android.mk @@ -0,0 +1,38 @@ +# Copyright (C) 2013 Google Inc. +# +# 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. + +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + config.c \ + commands.c \ + fastbootd.c \ + protocol.c \ + transport.c \ + usb_linux_client.c + +LOCAL_MODULE := fastbootd +LOCAL_MODULE_TAGS := optional +LOCAL_CFLAGS := -Wall -Werror -Wno-unused-parameter + +LOCAL_STATIC_LIBRARIES := \ + libsparse_static \ + libc \ + libcutils + +LOCAL_FORCE_STATIC_EXECUTABLE := true + +include $(BUILD_EXECUTABLE) diff --git a/fastbootd/bootimg.h b/fastbootd/bootimg.h new file mode 100644 index 000000000..44fde9277 --- /dev/null +++ b/fastbootd/bootimg.h @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _BOOT_IMAGE_H_ +#define _BOOT_IMAGE_H_ + +typedef struct boot_img_hdr boot_img_hdr; + +#define BOOT_MAGIC "ANDROID!" +#define BOOT_MAGIC_SIZE 8 +#define BOOT_NAME_SIZE 16 +#define BOOT_ARGS_SIZE 512 + +struct boot_img_hdr +{ + unsigned char magic[BOOT_MAGIC_SIZE]; + + unsigned kernel_size; /* size in bytes */ + unsigned kernel_addr; /* physical load addr */ + + unsigned ramdisk_size; /* size in bytes */ + unsigned ramdisk_addr; /* physical load addr */ + + unsigned second_size; /* size in bytes */ + unsigned second_addr; /* physical load addr */ + + unsigned tags_addr; /* physical addr for kernel tags */ + unsigned page_size; /* flash page size we assume */ + unsigned unused[2]; /* future expansion: should be 0 */ + + unsigned char name[BOOT_NAME_SIZE]; /* asciiz product name */ + + unsigned char cmdline[BOOT_ARGS_SIZE]; + + unsigned id[8]; /* timestamp / checksum / sha1 / etc */ +}; + +/* +** +-----------------+ +** | boot header | 1 page +** +-----------------+ +** | kernel | n pages +** +-----------------+ +** | ramdisk | m pages +** +-----------------+ +** | second stage | o pages +** +-----------------+ +** +** n = (kernel_size + page_size - 1) / page_size +** m = (ramdisk_size + page_size - 1) / page_size +** o = (second_size + page_size - 1) / page_size +** +** 0. all entities are page_size aligned in flash +** 1. kernel and ramdisk are required (size != 0) +** 2. second is optional (second_size == 0 -> no second) +** 3. load each element (kernel, ramdisk, second) at +** the specified physical address (kernel_addr, etc) +** 4. prepare tags at tag_addr. kernel_args[] is +** appended to the kernel commandline in the tags. +** 5. r0 = 0, r1 = MACHINE_TYPE, r2 = tags_addr +** 6. if second_size != 0: jump to second_addr +** else: jump to kernel_addr +*/ + +boot_img_hdr *mkbootimg(void *kernel, unsigned kernel_size, + void *ramdisk, unsigned ramdisk_size, + void *second, unsigned second_size, + unsigned page_size, + unsigned *bootimg_size); + +void bootimg_set_cmdline(boot_img_hdr *hdr, const char *cmdline); +#endif diff --git a/fastbootd/commands.c b/fastbootd/commands.c new file mode 100644 index 000000000..252f655e8 --- /dev/null +++ b/fastbootd/commands.c @@ -0,0 +1,206 @@ +/* + * Copyright (c) 2009-2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google, Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include + +#include "bootimg.h" +#include "debug.h" +#include "protocol.h" + +static void cmd_boot(struct protocol_handle *phandle, const char *arg) +{ +#if 0 + unsigned kernel_actual; + unsigned ramdisk_actual; + static struct boot_img_hdr hdr; + char *ptr = ((char*) data); + + if (sz < sizeof(hdr)) { + fastboot_fail(phandle, "invalid bootimage header"); + return; + } + + memcpy(&hdr, data, sizeof(hdr)); + + /* ensure commandline is terminated */ + hdr.cmdline[BOOT_ARGS_SIZE-1] = 0; + + kernel_actual = ROUND_TO_PAGE(hdr.kernel_size); + ramdisk_actual = ROUND_TO_PAGE(hdr.ramdisk_size); + + if (2048 + kernel_actual + ramdisk_actual < sz) { + fastboot_fail(phandle, "incomplete bootimage"); + return; + } + + /*memmove((void*) KERNEL_ADDR, ptr + 2048, hdr.kernel_size); + memmove((void*) RAMDISK_ADDR, ptr + 2048 + kernel_actual, hdr.ramdisk_size);*/ + + fastboot_okay(phandle, ""); + udc_stop(); + + + /*boot_linux((void*) KERNEL_ADDR, (void*) TAGS_ADDR, + (const char*) hdr.cmdline, LINUX_MACHTYPE, + (void*) RAMDISK_ADDR, hdr.ramdisk_size);*/ +#endif +} + +static void cmd_erase(struct protocol_handle *phandle, const char *arg) +{ +#if 0 + struct ptentry *ptn; + struct ptable *ptable; + + ptable = flash_get_ptable(); + if (ptable == NULL) { + fastboot_fail(phandle, "partition table doesn't exist"); + return; + } + + ptn = ptable_find(ptable, arg); + if (ptn == NULL) { + fastboot_fail(phandle, "unknown partition name"); + return; + } + + if (flash_erase(ptn)) { + fastboot_fail(phandle, "failed to erase partition"); + return; + } + fastboot_okay(phandle, ""); +#endif +} + +static void cmd_flash(struct protocol_handle *phandle, const char *arg) +{ +#if 0 + struct ptentry *ptn; + struct ptable *ptable; + unsigned extra = 0; + + ptable = flash_get_ptable(); + if (ptable == NULL) { + fastboot_fail(phandle, "partition table doesn't exist"); + return; + } + + ptn = ptable_find(ptable, arg); + if (ptn == NULL) { + fastboot_fail(phandle, "unknown partition name"); + return; + } + + if (!strcmp(ptn->name, "boot") || !strcmp(ptn->name, "recovery")) { + if (memcmp((void *)data, BOOT_MAGIC, BOOT_MAGIC_SIZE)) { + fastboot_fail(phandle, "image is not a boot image"); + return; + } + } + + if (!strcmp(ptn->name, "system") || !strcmp(ptn->name, "userdata")) + extra = 64; + else + sz = ROUND_TO_PAGE(sz); + + D(INFO, "writing %d bytes to '%s'\n", sz, ptn->name); + if (flash_write(ptn, extra, data, sz)) { + fastboot_fail(phandle, "flash write failure"); + return; + } + D(INFO, "partition '%s' updated\n", ptn->name); +#endif + fastboot_okay(phandle, ""); +} + +static void cmd_continue(struct protocol_handle *phandle, const char *arg) +{ + fastboot_okay(phandle, ""); +#if 0 + udc_stop(); + + boot_linux_from_flash(); +#endif +} + +static void cmd_getvar(struct protocol_handle *phandle, const char *arg) +{ + const char *value; + D(DEBUG, "cmd_getvar %s\n", arg); + + value = fastboot_getvar(arg); + + fastboot_okay(phandle, value); +} + +static void cmd_download(struct protocol_handle *phandle, const char *arg) +{ + unsigned len = strtoul(arg, NULL, 16); + int old_fd; + + if (len > 256 * 1024 * 1024) { + fastboot_fail(phandle, "data too large"); + return; + } + + fastboot_data(phandle, len); + + old_fd = protocol_get_download(phandle); + if (old_fd >= 0) { + off_t len = lseek(old_fd, 0, SEEK_END); + D(INFO, "disposing of unused fd %d, size %ld", old_fd, len); + close(old_fd); + } + + phandle->download_fd = protocol_handle_download(phandle, len); + if (phandle->download_fd < 0) { + //handle->state = STATE_ERROR; + fastboot_fail(phandle, "download failed"); + return; + } + + fastboot_okay(phandle, ""); +} + +void commands_init() +{ + fastboot_register("boot", cmd_boot); + fastboot_register("erase:", cmd_erase); + fastboot_register("flash:", cmd_flash); + fastboot_register("continue", cmd_continue); + fastboot_register("getvar:", cmd_getvar); + fastboot_register("download:", cmd_download); + //fastboot_publish("version", "0.5"); + //fastboot_publish("product", "swordfish"); + //fastboot_publish("kernel", "lk"); +} diff --git a/fastbootd/config.c b/fastbootd/config.c new file mode 100644 index 000000000..b8503fd8e --- /dev/null +++ b/fastbootd/config.c @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google, Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include + +#include "protocol.h" + +#include "debug.h" + +// TODO: change config path +#define CONFIG_PATH "/data/fastboot.cfg" + +static char *strip(char *str) +{ + int n; + + n = strspn(str, " \t"); + str += n; + + for (n = strlen(str) - 1; n >= 0; n--) { + if (str[n] == ' ' || str[n] == '\t') + str[n] = '\0'; + else + break; + } + + return str; +} + +static int config_parse_line(char *line) +{ + char *c; + char *key; + char *value; + + c = strchr(line, '#'); + if (c) + *c = '\0'; + + if (strspn(line, " \t") == strlen(line)) + return 0; + + c = strchr(line, '='); + if (c == NULL) + return -1; + + key = line; + *c = '\0'; + value = c + 1; + + key = strip(key); + value = strip(value); + + key = strdup(key); + value = strdup(value); + + fastboot_publish(key, value); + + return 0; +} + +static void config_parse(char *buffer) +{ + char *saveptr; + char *str = buffer; + char *line = buffer; + int c; + int ret; + + for (c = 1; line != NULL; c++) { + line = strtok_r(str, "\r\n", &saveptr); + if (line != NULL) { + D(VERBOSE, "'%s'", line); + ret = config_parse_line(line); + if (ret < 0) { + D(WARN, "error parsing " CONFIG_PATH " line %d", c); + } + } + str = NULL; + } +} + +void config_init() +{ + int fd; + off_t len; + ssize_t ret; + size_t count = 0; + char *buffer; + + fd = open(CONFIG_PATH, O_RDONLY); + if (fd < 0) { + D(ERR, "failed to open " CONFIG_PATH); + return; + } + + len = lseek(fd, 0, SEEK_END); + if (len < 0) { + D(ERR, "failed to seek to end of " CONFIG_PATH); + return; + } + + lseek(fd, 0, SEEK_SET); + + buffer = malloc(len + 1); + if (buffer == NULL) { + D(ERR, "failed to allocate %ld bytes", len); + return; + } + + while (count < (size_t)len) { + ret = read(fd, buffer + count, len - count); + if (ret < 0 && errno != EINTR) { + D(ERR, "failed to read " CONFIG_PATH ": %d %s", errno, strerror(errno)); + return; + } + if (ret == 0) { + D(ERR, "early EOF reading " CONFIG_PATH); + return; + } + + count += ret; + } + + buffer[len] = '\0'; + + config_parse(buffer); + + free(buffer); +} diff --git a/fastbootd/debug.h b/fastbootd/debug.h new file mode 100644 index 000000000..74620b829 --- /dev/null +++ b/fastbootd/debug.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2013 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 _FASTBOOTD_DEBUG_H_ +#define _FASTBOOTD_DEBUG_H_ + +#include + +#include + +#define ERR 0 +#define WARN 1 +#define INFO 2 +#define VERBOSE 3 +#define DEBUG 4 + +extern unsigned int debug_level; + +//#define DLOG(fmt, ...) printf(fmt, ##__VA_ARGS__) +#define DLOG(fmt, ...) KLOG_INFO("fastbootd", fmt, ##__VA_ARGS__) + +#define D(level, fmt, ...) \ + do { \ + if (debug_level == level || debug_level > level) { \ + DLOG("%s:%d " fmt "\n", __BASE_FILE__, __LINE__, ##__VA_ARGS__); \ + } \ + } while (0) + +#endif diff --git a/fastbootd/fastbootd.c b/fastbootd/fastbootd.c new file mode 100644 index 000000000..98df0db3d --- /dev/null +++ b/fastbootd/fastbootd.c @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2013 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 +#include + +#include + +#include "debug.h" + +unsigned int debug_level = DEBUG; + +void commands_init(); +void usb_init(); +void config_init(); + +int main(int argc, char **argv) +{ + (void)argc; + (void)argv; + + klog_init(); + klog_set_level(6); + + config_init(); + commands_init(); + usb_init(); + while (1) { + sleep(1); + } + return 0; +} diff --git a/fastbootd/protocol.c b/fastbootd/protocol.c new file mode 100644 index 000000000..0086b4a96 --- /dev/null +++ b/fastbootd/protocol.c @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2009-2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google, Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include + +#include "debug.h" +#include "protocol.h" +#include "transport.h" + +#define STATE_OFFLINE 0 +#define STATE_COMMAND 1 +#define STATE_COMPLETE 2 +#define STATE_ERROR 3 + +struct fastboot_cmd { + struct fastboot_cmd *next; + const char *prefix; + unsigned prefix_len; + void (*execute)(struct protocol_handle *phandle, const char *arg); +}; + +struct fastboot_var { + struct fastboot_var *next; + const char *name; + const char *value; +}; + +static struct fastboot_cmd *cmdlist; + +void fastboot_register(const char *prefix, + void (*phandle)(struct protocol_handle *phandle, const char *arg)) +{ + struct fastboot_cmd *cmd; + cmd = malloc(sizeof(*cmd)); + if (cmd) { + cmd->prefix = prefix; + cmd->prefix_len = strlen(prefix); + cmd->execute = phandle; + cmd->next = cmdlist; + cmdlist = cmd; + } +} + +static struct fastboot_var *varlist; + +void fastboot_publish(const char *name, const char *value) +{ + struct fastboot_var *var; + var = malloc(sizeof(*var)); + if (var) { + var->name = name; + var->value = value; + var->next = varlist; + varlist = var; + } +} + +const char *fastboot_getvar(const char *name) +{ + struct fastboot_var *var; + + for (var = varlist; var; var = var->next) { + if (!strcmp(var->name, name)) { + return var->value; + } + } + + return ""; +} + +int protocol_handle_download(struct protocol_handle *phandle, size_t len) +{ + return transport_handle_download(phandle->transport_handle, len); +} + +static ssize_t protocol_handle_write(struct protocol_handle *phandle, + char *buffer, size_t len) +{ + return transport_handle_write(phandle->transport_handle, buffer, len); +} + +static void fastboot_ack(struct protocol_handle *phandle, const char *code, + const char *reason) +{ + char response[64]; + + if (phandle->state != STATE_COMMAND) + return; + + if (reason == 0) + reason = ""; + + snprintf(response, 64, "%s%s", code, reason); + phandle->state = STATE_COMPLETE; + + protocol_handle_write(phandle, response, strlen(response)); +} + +void fastboot_fail(struct protocol_handle *phandle, const char *reason) +{ + fastboot_ack(phandle, "FAIL", reason); +} + +void fastboot_okay(struct protocol_handle *phandle, const char *info) +{ + fastboot_ack(phandle, "OKAY", info); +} + +void fastboot_data(struct protocol_handle *phandle, size_t len) +{ + char response[64]; + ssize_t ret; + + snprintf(response, 64, "DATA%08x", len); + ret = protocol_handle_write(phandle, response, strlen(response)); + if (ret < 0) + return; +} + +void protocol_handle_command(struct protocol_handle *phandle, char *buffer) +{ + D(INFO,"fastboot: %s\n", buffer); + + struct fastboot_cmd *cmd; + + for (cmd = cmdlist; cmd; cmd = cmd->next) { + if (memcmp(buffer, cmd->prefix, cmd->prefix_len)) + continue; + phandle->state = STATE_COMMAND; + cmd->execute(phandle, buffer + cmd->prefix_len); + if (phandle->state == STATE_COMMAND) + fastboot_fail(phandle, "unknown reason"); + return; + } + + fastboot_fail(phandle, "unknown command"); +} + +struct protocol_handle *create_protocol_handle(struct transport_handle *thandle) +{ + struct protocol_handle *phandle; + + phandle = calloc(sizeof(struct protocol_handle), 1); + + phandle->transport_handle = thandle; + phandle->state = STATE_OFFLINE; + phandle->download_fd = -1; + + pthread_mutex_init(&phandle->lock, NULL); + + return phandle; +} + +int protocol_get_download(struct protocol_handle *phandle) +{ + int fd; + + pthread_mutex_lock(&phandle->lock); + fd = phandle->download_fd; + phandle->download_fd = -1; + pthread_mutex_unlock(&phandle->lock); + + return fd; +} diff --git a/fastbootd/protocol.h b/fastbootd/protocol.h new file mode 100644 index 000000000..ea2a8dfb2 --- /dev/null +++ b/fastbootd/protocol.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2013, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google, Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _FASTBOOTD_PROTOCOL_H_ +#define _FASTBOOTD_PROTOCOL_H_ + +#include +#include + +struct protocol_handle { + struct transport_handle *transport_handle; + unsigned int state; + int download_fd; + + pthread_mutex_t lock; +}; + +void fastboot_register(const char *prefix, + void (*handle)(struct protocol_handle *handle, const char *arg)); + +void fastboot_publish(const char *name, const char *value); +const char *fastboot_getvar(const char *name); + +struct protocol_handle *create_protocol_handle(struct transport_handle *t); +void protocol_handle_command(struct protocol_handle *handle, char *buffer); +int protocol_handle_download(struct protocol_handle *phandle, size_t len); +int protocol_get_download(struct protocol_handle *phandle); + +void fastboot_fail(struct protocol_handle *handle, const char *reason); +void fastboot_okay(struct protocol_handle *handle, const char *reason); +void fastboot_data(struct protocol_handle *handle, size_t len); + +#endif diff --git a/fastbootd/transport.c b/fastbootd/transport.c new file mode 100644 index 000000000..01a5a8ae4 --- /dev/null +++ b/fastbootd/transport.c @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2013 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 +#include +#include +#include +#include +#include + +#include "debug.h" +#include "protocol.h" +#include "transport.h" + +#define COMMAND_BUF_SIZE 64 + +ssize_t transport_handle_write(struct transport_handle *thandle, char *buffer, size_t len) +{ + return thandle->transport->write(thandle, buffer, len); +} + +void transport_handle_close(struct transport_handle *thandle) +{ + thandle->transport->close(thandle); +} + +int transport_handle_download(struct transport_handle *thandle, size_t len) +{ + ssize_t ret; + size_t n = 0; + int fd; + // TODO: move out of /dev + char tempname[] = "/dev/fastboot_download_XXXXXX"; + char *buffer; + + fd = mkstemp(tempname); + if (fd < 0) + return -1; + + unlink(tempname); + + ftruncate(fd, len); + + buffer = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (buffer == NULL) { + D(ERR, "mmap(%u) failed: %d %s", len, errno, strerror(errno)); + goto err; + } + + while (n < len) { + ret = thandle->transport->read(thandle, buffer + n, len - n); + if (ret <= 0) { + D(WARN, "transport read failed, ret=%d %s", ret, strerror(-ret)); + break; + } + n += ret; + } + + munmap(buffer, len); + + if (n != len) + goto err; + + return fd; + +err: + close(fd); + transport_handle_close(thandle); + return -1; +} + +static void *transport_data_thread(void *arg) +{ + struct transport_handle *thandle = arg; + struct protocol_handle *phandle = create_protocol_handle(thandle); + + while (!thandle->stopped) { + int ret; + char buffer[COMMAND_BUF_SIZE + 1]; + D(VERBOSE, "transport_data_thread\n"); + + ret = thandle->transport->read(thandle, buffer, COMMAND_BUF_SIZE); + if (ret <= 0) { + D(DEBUG, "ret = %d\n", ret); + break; + } + if (ret > 0) { + buffer[ret] = 0; + protocol_handle_command(phandle, buffer); + } + } + + transport_handle_close(thandle); + free(thandle); + + return NULL; +} + +static void *transport_connect_thread(void *arg) +{ + struct transport *transport = arg; + while (!transport->stopped) { + struct transport_handle *thandle; + pthread_t thread; + pthread_attr_t attr; + + D(VERBOSE, "transport_connect_thread\n"); + thandle = transport->connect(transport); + if (thandle == NULL) { + D(ERR, "transport connect failed\n"); + sleep(1); + continue; + } + thandle->transport = transport; + + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + + pthread_create(&thread, &attr, transport_data_thread, thandle); + + sleep(1); + } + + return NULL; +} + +void transport_register(struct transport *transport) +{ + pthread_t thread; + pthread_attr_t attr; + + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + + pthread_create(&thread, &attr, transport_connect_thread, transport); +} diff --git a/fastbootd/transport.h b/fastbootd/transport.h new file mode 100644 index 000000000..209340d46 --- /dev/null +++ b/fastbootd/transport.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2013 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 _FASTBOOTD_TRANSPORT_H_ +#define _FASTBOOTD_TRANSPORT_H_ + +#include + +struct transport_handle { + struct transport *transport; + + bool stopped; +}; + +struct transport { + void (*init)(); + void (*close)(struct transport_handle *thandle); + ssize_t (*read)(struct transport_handle *thandle, void *data, size_t len); + ssize_t (*write)(struct transport_handle *thandle, const void *data, size_t len); + struct transport_handle *(*connect)(struct transport *transport); + bool stopped; +}; + +void transport_register(struct transport *transport); +ssize_t transport_handle_write(struct transport_handle *handle, char *buffer, size_t len); +int transport_handle_download(struct transport_handle *handle, size_t len); + +#endif diff --git a/fastbootd/usb_linux_client.c b/fastbootd/usb_linux_client.c new file mode 100644 index 000000000..111cf3596 --- /dev/null +++ b/fastbootd/usb_linux_client.c @@ -0,0 +1,353 @@ +/* + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include "debug.h" +#include "transport.h" + +#define TRACE_TAG TRACE_USB + +#define MAX_PACKET_SIZE_FS 64 +#define MAX_PACKET_SIZE_HS 512 + +#define cpu_to_le16(x) htole16(x) +#define cpu_to_le32(x) htole32(x) + +#define FASTBOOT_CLASS 0xff +#define FASTBOOT_SUBCLASS 0x42 +#define FASTBOOT_PROTOCOL 0x3 + +#define USB_FFS_FASTBOOT_PATH "/dev/usb-ffs/adb/" +#define USB_FFS_FASTBOOT_EP(x) USB_FFS_FASTBOOT_PATH#x + +#define USB_FFS_FASTBOOT_EP0 USB_FFS_FASTBOOT_EP(ep0) +#define USB_FFS_FASTBOOT_OUT USB_FFS_FASTBOOT_EP(ep1) +#define USB_FFS_FASTBOOT_IN USB_FFS_FASTBOOT_EP(ep2) + +#define READ_BUF_SIZE (16*1024) + +#define container_of(ptr, type, member) \ + ((type*)((char*)(ptr) - offsetof(type, member))) + +struct usb_transport { + struct transport transport; + + pthread_cond_t notify; + pthread_mutex_t lock; + + int control; + int bulk_out; /* "out" from the host's perspective => source for fastbootd */ + int bulk_in; /* "in" from the host's perspective => sink for fastbootd */ +}; + +struct usb_handle { + struct transport_handle handle; +}; + +static const struct { + struct usb_functionfs_descs_head header; + struct { + struct usb_interface_descriptor intf; + struct usb_endpoint_descriptor_no_audio source; + struct usb_endpoint_descriptor_no_audio sink; + } __attribute__((packed)) fs_descs, hs_descs; +} __attribute__((packed)) descriptors = { + .header = { + .magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC), + .length = cpu_to_le32(sizeof(descriptors)), + .fs_count = 3, + .hs_count = 3, + }, + .fs_descs = { + .intf = { + .bLength = sizeof(descriptors.fs_descs.intf), + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0, + .bNumEndpoints = 2, + .bInterfaceClass = FASTBOOT_CLASS, + .bInterfaceSubClass = FASTBOOT_SUBCLASS, + .bInterfaceProtocol = FASTBOOT_PROTOCOL, + .iInterface = 1, /* first string from the provided table */ + }, + .source = { + .bLength = sizeof(descriptors.fs_descs.source), + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 1 | USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = MAX_PACKET_SIZE_FS, + }, + .sink = { + .bLength = sizeof(descriptors.fs_descs.sink), + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 2 | USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = MAX_PACKET_SIZE_FS, + }, + }, + .hs_descs = { + .intf = { + .bLength = sizeof(descriptors.hs_descs.intf), + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0, + .bNumEndpoints = 2, + .bInterfaceClass = FASTBOOT_CLASS, + .bInterfaceSubClass = FASTBOOT_SUBCLASS, + .bInterfaceProtocol = FASTBOOT_PROTOCOL, + .iInterface = 1, /* first string from the provided table */ + }, + .source = { + .bLength = sizeof(descriptors.hs_descs.source), + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 1 | USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = MAX_PACKET_SIZE_HS, + }, + .sink = { + .bLength = sizeof(descriptors.hs_descs.sink), + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 2 | USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = MAX_PACKET_SIZE_HS, + }, + }, +}; + +#define STR_INTERFACE_ "Fastboot Interface" + +static const struct { + struct usb_functionfs_strings_head header; + struct { + __le16 code; + const char str1[sizeof(STR_INTERFACE_)]; + } __attribute__((packed)) lang0; +} __attribute__((packed)) strings = { + .header = { + .magic = cpu_to_le32(FUNCTIONFS_STRINGS_MAGIC), + .length = cpu_to_le32(sizeof(strings)), + .str_count = cpu_to_le32(1), + .lang_count = cpu_to_le32(1), + }, + .lang0 = { + cpu_to_le16(0x0409), /* en-us */ + STR_INTERFACE_, + }, +}; + +static int init_functionfs(struct usb_transport *usb_transport) +{ + ssize_t ret; + + D(VERBOSE, "OPENING %s", USB_FFS_FASTBOOT_EP0); + usb_transport->control = open(USB_FFS_FASTBOOT_EP0, O_RDWR); + if (usb_transport->control < 0) { + D(ERR, "[ %s: cannot open control endpoint: errno=%d]", USB_FFS_FASTBOOT_EP0, errno); + goto err; + } + + ret = write(usb_transport->control, &descriptors, sizeof(descriptors)); + if (ret < 0) { + D(ERR, "[ %s: write descriptors failed: errno=%d ]", USB_FFS_FASTBOOT_EP0, errno); + goto err; + } + + ret = write(usb_transport->control, &strings, sizeof(strings)); + if (ret < 0) { + D(ERR, "[ %s: writing strings failed: errno=%d]", USB_FFS_FASTBOOT_EP0, errno); + goto err; + } + + usb_transport->bulk_out = open(USB_FFS_FASTBOOT_OUT, O_RDWR); + if (usb_transport->bulk_out < 0) { + D(ERR, "[ %s: cannot open bulk-out ep: errno=%d ]", USB_FFS_FASTBOOT_OUT, errno); + goto err; + } + + usb_transport->bulk_in = open(USB_FFS_FASTBOOT_IN, O_RDWR); + if (usb_transport->bulk_in < 0) { + D(ERR, "[ %s: cannot open bulk-in ep: errno=%d ]", USB_FFS_FASTBOOT_IN, errno); + goto err; + } + + return 0; + +err: + if (usb_transport->bulk_in > 0) { + close(usb_transport->bulk_in); + usb_transport->bulk_in = -1; + } + if (usb_transport->bulk_out > 0) { + close(usb_transport->bulk_out); + usb_transport->bulk_out = -1; + } + if (usb_transport->control > 0) { + close(usb_transport->control); + usb_transport->control = -1; + } + return -1; +} + +static ssize_t bulk_write(int bulk_in, const char *buf, size_t length) +{ + size_t count = 0; + ssize_t ret; + + do { + ret = TEMP_FAILURE_RETRY(write(bulk_in, buf + count, length - count)); + if (ret < 0) { + D(WARN, "[ bulk_read failed fd=%d length=%d errno=%d %s ]", + bulk_in, length, errno, strerror(errno)); + return -1; + } else { + count += ret; + } + } while (count < length); + + D(VERBOSE, "[ bulk_write done fd=%d ]", bulk_in); + return count; +} + +static ssize_t usb_write(struct transport_handle *thandle, const void *data, size_t len) +{ + ssize_t ret; + struct transport *t = thandle->transport; + struct usb_transport *usb_transport = container_of(t, struct usb_transport, transport); + + D(DEBUG, "about to write (fd=%d, len=%d)", usb_transport->bulk_in, len); + ret = bulk_write(usb_transport->bulk_in, data, len); + if (ret < 0) { + D(ERR, "ERROR: fd = %d, ret = %zd", usb_transport->bulk_in, ret); + return -1; + } + D(DEBUG, "[ usb_write done fd=%d ]", usb_transport->bulk_in); + return ret; +} + +static ssize_t bulk_read(int bulk_out, char *buf, size_t length) +{ + ssize_t ret; + size_t n = 0; + + while (n < length) { + size_t to_read = (length - n > READ_BUF_SIZE) ? READ_BUF_SIZE : length - n; + ret = TEMP_FAILURE_RETRY(read(bulk_out, buf + n, to_read)); + if (ret < 0) { + D(WARN, "[ bulk_read failed fd=%d length=%d errno=%d %s ]", + bulk_out, length, errno, strerror(errno)); + return ret; + } + n += ret; + if (ret < (ssize_t)to_read) { + D(VERBOSE, "bulk_read short read, ret=%zd to_read=%u n=%u length=%u", + ret, to_read, n, length); + break; + } + } + + return n; +} + +ssize_t usb_read(struct transport_handle *thandle, void *data, size_t len) +{ + ssize_t ret; + struct transport *t = thandle->transport; + struct usb_transport *usb_transport = container_of(t, struct usb_transport, transport); + + D(DEBUG, "about to read (fd=%d, len=%d)", usb_transport->bulk_out, len); + ret = bulk_read(usb_transport->bulk_out, data, len); + if (ret < 0) { + D(ERR, "ERROR: fd = %d, ret = %zd", usb_transport->bulk_out, ret); + return -1; + } + D(DEBUG, "[ usb_read done fd=%d ret=%zd]", usb_transport->bulk_out, ret); + return ret; +} + +void usb_close(struct transport_handle *thandle) +{ + int err; + struct transport *t = thandle->transport; + struct usb_transport *usb_transport = container_of(t, struct usb_transport, transport); + + err = ioctl(usb_transport->bulk_in, FUNCTIONFS_CLEAR_HALT); + if (err < 0) + D(WARN, "[ kick: source (fd=%d) clear halt failed (%d) ]", usb_transport->bulk_in, errno); + + err = ioctl(usb_transport->bulk_out, FUNCTIONFS_CLEAR_HALT); + if (err < 0) + D(WARN, "[ kick: sink (fd=%d) clear halt failed (%d) ]", usb_transport->bulk_out, errno); + + pthread_mutex_lock(&usb_transport->lock); + close(usb_transport->control); + close(usb_transport->bulk_out); + close(usb_transport->bulk_in); + usb_transport->control = usb_transport->bulk_out = usb_transport->bulk_in = -1; + + pthread_cond_signal(&usb_transport->notify); + pthread_mutex_unlock(&usb_transport->lock); +} + +struct transport_handle *usb_connect(struct transport *transport) +{ + int ret; + struct usb_handle *usb_handle = calloc(sizeof(struct usb_handle), 1); + struct usb_transport *usb_transport = container_of(transport, struct usb_transport, transport); + + pthread_mutex_lock(&usb_transport->lock); + while (usb_transport->control != -1) + pthread_cond_wait(&usb_transport->notify, &usb_transport->lock); + pthread_mutex_unlock(&usb_transport->lock); + + ret = init_functionfs(usb_transport); + if (ret < 0) { + D(ERR, "usb connect: failed to initialize usb transport"); + return NULL; + } + + D(DEBUG, "[ usb_thread - registering device ]"); + return &usb_handle->handle; +} + +void usb_init() +{ + struct usb_transport *usb_transport = calloc(1, sizeof(struct usb_transport)); + + usb_transport->transport.connect = usb_connect; + usb_transport->transport.close = usb_close; + usb_transport->transport.read = usb_read; + usb_transport->transport.write = usb_write; + usb_transport->control = -1; + usb_transport->bulk_out = -1; + usb_transport->bulk_out = -1; + + pthread_cond_init(&usb_transport->notify, NULL); + pthread_mutex_init(&usb_transport->lock, NULL); + + transport_register(&usb_transport->transport); +} + From 5748ee972de22e3a53bd3ef7d998ed15ea726a69 Mon Sep 17 00:00:00 2001 From: Ying Wang Date: Tue, 23 Jul 2013 18:03:37 -0700 Subject: [PATCH 061/541] Generate init.environ.rc by populating BOOTCLASSPATH - BOOTCLASSPATH now is derived from PRODUCT_BOOT_JARS, which is a product configuration variable set up by the core build system. - Moved files from the legacy ALL_PREBUILT to PRODUCT_COPY_FILES in build/target/product/embedded.mk. Bug: 9990214 Change-Id: I98bac36c1ca8c779dda572a0a5e0a22b7e4c4a7a --- rootdir/Android.mk | 73 +++++++++++++++----------------------- rootdir/init.environ.rc.in | 12 +++++++ rootdir/init.rc | 13 +------ 3 files changed, 41 insertions(+), 57 deletions(-) create mode 100644 rootdir/init.environ.rc.in diff --git a/rootdir/Android.mk b/rootdir/Android.mk index 3417f54f3..2c1608442 100644 --- a/rootdir/Android.mk +++ b/rootdir/Android.mk @@ -1,55 +1,38 @@ LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -# files that live under /system/etc/... - -copy_from := \ - etc/hosts - - -copy_to := $(addprefix $(TARGET_OUT)/,$(copy_from)) -copy_from := $(addprefix $(LOCAL_PATH)/,$(copy_from)) - -$(copy_to) : PRIVATE_MODULE := system_etcdir -$(copy_to) : $(TARGET_OUT)/% : $(LOCAL_PATH)/% | $(ACP) - $(transform-prebuilt-to-target) - -ALL_PREBUILT += $(copy_to) - - -# files that live under /... +####################################### +# init.rc # Only copy init.rc if the target doesn't have its own. ifneq ($(TARGET_PROVIDES_INIT_RC),true) -file := $(TARGET_ROOT_OUT)/init.rc -$(file) : $(LOCAL_PATH)/init.rc | $(ACP) - $(transform-prebuilt-to-target) -ALL_PREBUILT += $(file) -$(INSTALLED_RAMDISK_TARGET): $(file) +include $(CLEAR_VARS) + +LOCAL_MODULE := init.rc +LOCAL_SRC_FILES := $(LOCAL_MODULE) +LOCAL_MODULE_CLASS := ETC +LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT) + +include $(BUILD_PREBUILT) endif +####################################### +# init.environ.rc -file := $(TARGET_ROOT_OUT)/ueventd.rc -$(file) : $(LOCAL_PATH)/ueventd.rc | $(ACP) - $(transform-prebuilt-to-target) -ALL_PREBUILT += $(file) -$(INSTALLED_RAMDISK_TARGET): $(file) - -# init.usb.rc is handled by build/target/product/core.rc - +include $(CLEAR_VARS) +LOCAL_MODULE_CLASS := ETC +LOCAL_MODULE := init.environ.rc +LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT) +# Put it here instead of in init.rc module definition, +# because init.rc is conditionally included. +# # create some directories (some are mount points) -DIRS := $(addprefix $(TARGET_ROOT_OUT)/, \ - sbin \ - dev \ - proc \ - sys \ - system \ - data \ - ) \ - $(TARGET_OUT_DATA) +LOCAL_POST_INSTALL_CMD := mkdir -p $(addprefix $(TARGET_ROOT_OUT)/, \ + sbin dev proc sys system data) -$(DIRS): - @echo Directory: $@ - @mkdir -p $@ +include $(BUILD_SYSTEM)/base_rules.mk -ALL_PREBUILT += $(DIRS) +$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/init.environ.rc.in + @echo "Generate: $< -> $@" + @mkdir -p $(dir $@) + $(hide) sed -e 's?%BOOTCLASSPATH%?$(PRODUCT_BOOTCLASSPATH)?g' $< >$@ + +####################################### diff --git a/rootdir/init.environ.rc.in b/rootdir/init.environ.rc.in new file mode 100644 index 000000000..d2f74c081 --- /dev/null +++ b/rootdir/init.environ.rc.in @@ -0,0 +1,12 @@ +# set up the global environment +on init + export PATH /sbin:/vendor/bin:/system/sbin:/system/bin:/system/xbin + export LD_LIBRARY_PATH /vendor/lib:/system/lib + export ANDROID_BOOTLOGO 1 + export ANDROID_ROOT /system + export ANDROID_ASSETS /system/app + export ANDROID_DATA /data + export ANDROID_STORAGE /storage + export ASEC_MOUNTPOINT /mnt/asec + export LOOP_MOUNTPOINT /mnt/obb + export BOOTCLASSPATH %BOOTCLASSPATH% diff --git a/rootdir/init.rc b/rootdir/init.rc index 399028732..d4d74877c 100644 --- a/rootdir/init.rc +++ b/rootdir/init.rc @@ -4,6 +4,7 @@ # This is a common source of Android security bugs. # +import /init.environ.rc import /init.usb.rc import /init.${ro.hardware}.rc import /init.trace.rc @@ -27,18 +28,6 @@ sysclktz 0 loglevel 3 -# setup the global environment - export PATH /sbin:/vendor/bin:/system/sbin:/system/bin:/system/xbin - export LD_LIBRARY_PATH /vendor/lib:/system/lib - export ANDROID_BOOTLOGO 1 - export ANDROID_ROOT /system - export ANDROID_ASSETS /system/app - export ANDROID_DATA /data - export ANDROID_STORAGE /storage - export ASEC_MOUNTPOINT /mnt/asec - export LOOP_MOUNTPOINT /mnt/obb - export BOOTCLASSPATH /system/framework/core.jar:/system/framework/conscrypt.jar:/system/framework/okhttp.jar:/system/framework/core-junit.jar:/system/framework/bouncycastle.jar:/system/framework/ext.jar:/system/framework/framework.jar:/system/framework/framework2.jar:/system/framework/telephony-common.jar:/system/framework/voip-common.jar:/system/framework/mms-common.jar:/system/framework/android.policy.jar:/system/framework/services.jar:/system/framework/apache-xml.jar:/system/framework/webviewchromium.jar - # Backward compatibility symlink /system/etc /etc symlink /sys/kernel/debug /d From 9227bd385504ace739d4451a6c5dc3d777b5bf21 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Tue, 23 Jul 2013 16:59:20 -0700 Subject: [PATCH 062/541] Move liblog headers to system/core/include/log Move the liblog headers to log/ instead of cutils/ to complete the separation of libcutils and liblog. cutils/log.h still exists and includes log/log.h in order to support the many existing modules that use cutils/log.h. Change-Id: I2758c9f4aedcb809ca7ba8383d0f55041dd44345 --- adb/Android.mk | 2 +- adb/log_service.c | 2 +- debuggerd/debuggerd.c | 5 +- debuggerd/tombstone.c | 2 +- debuggerd/utility.c | 2 +- include/cutils/log.h | 564 +----------------------- include/{cutils => log}/event_tag_map.h | 0 include/log/log.h | 563 +++++++++++++++++++++++ include/{cutils => log}/logd.h | 2 +- include/{cutils => log}/logger.h | 0 include/{cutils => log}/logprint.h | 6 +- include/{cutils => log}/uio.h | 0 libcutils/Android.mk | 5 +- liblog/Android.mk | 3 + liblog/event_tag_map.c | 4 +- liblog/fake_log_device.c | 2 +- liblog/logd_write.c | 6 +- liblog/logprint.c | 4 +- {libcutils => liblog}/uio.c | 2 +- logcat/logcat.cpp | 8 +- toolbox/log.c | 2 +- 21 files changed, 593 insertions(+), 591 deletions(-) rename include/{cutils => log}/event_tag_map.h (100%) create mode 100644 include/log/log.h rename include/{cutils => log}/logd.h (98%) rename include/{cutils => log}/logger.h (100%) rename include/{cutils => log}/logprint.h (97%) rename include/{cutils => log}/uio.h (100%) rename {libcutils => liblog}/uio.c (98%) diff --git a/adb/Android.mk b/adb/Android.mk index 36f595b50..721b48d5a 100644 --- a/adb/Android.mk +++ b/adb/Android.mk @@ -130,7 +130,7 @@ LOCAL_FORCE_STATIC_EXECUTABLE := true LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT_SBIN) LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_SBIN_UNSTRIPPED) -LOCAL_STATIC_LIBRARIES := libcutils libc libmincrypt +LOCAL_STATIC_LIBRARIES := liblog libcutils libc libmincrypt include $(BUILD_EXECUTABLE) diff --git a/adb/log_service.c b/adb/log_service.c index 6e9bdeee6..af2435679 100644 --- a/adb/log_service.c +++ b/adb/log_service.c @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include "sysdeps.h" #include "adb.h" diff --git a/debuggerd/debuggerd.c b/debuggerd/debuggerd.c index 0028bda50..756f7bb10 100644 --- a/debuggerd/debuggerd.c +++ b/debuggerd/debuggerd.c @@ -31,9 +31,10 @@ #include #include +#include +#include + #include -#include -#include #include #include diff --git a/debuggerd/tombstone.c b/debuggerd/tombstone.c index 77f6ef1c0..7009a8e02 100644 --- a/debuggerd/tombstone.c +++ b/debuggerd/tombstone.c @@ -29,7 +29,7 @@ #include -#include +#include #include #include diff --git a/debuggerd/utility.c b/debuggerd/utility.c index 9bf3c18dd..41be9821e 100644 --- a/debuggerd/utility.c +++ b/debuggerd/utility.c @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/include/cutils/log.h b/include/cutils/log.h index 8b045c75d..0e0248e50 100644 --- a/include/cutils/log.h +++ b/include/cutils/log.h @@ -1,563 +1 @@ -/* - * Copyright (C) 2005 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. - */ - -// -// C/C++ logging functions. See the logging documentation for API details. -// -// We'd like these to be available from C code (in case we import some from -// somewhere), so this has a C interface. -// -// The output will be correct when the log file is shared between multiple -// threads and/or multiple processes so long as the operating system -// supports O_APPEND. These calls have mutex-protected data structures -// and so are NOT reentrant. Do not use LOG in a signal handler. -// -#ifndef _LIBS_CUTILS_LOG_H -#define _LIBS_CUTILS_LOG_H - -#include -#include -#include -#include -#ifdef HAVE_PTHREADS -#include -#endif -#include - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -// --------------------------------------------------------------------- - -/* - * Normally we strip ALOGV (VERBOSE messages) from release builds. - * You can modify this (for example with "#define LOG_NDEBUG 0" - * at the top of your source file) to change that behavior. - */ -#ifndef LOG_NDEBUG -#ifdef NDEBUG -#define LOG_NDEBUG 1 -#else -#define LOG_NDEBUG 0 -#endif -#endif - -/* - * This is the local tag used for the following simplified - * logging macros. You can change this preprocessor definition - * before using the other macros to change the tag. - */ -#ifndef LOG_TAG -#define LOG_TAG NULL -#endif - -// --------------------------------------------------------------------- - -/* - * Simplified macro to send a verbose log message using the current LOG_TAG. - */ -#ifndef ALOGV -#if LOG_NDEBUG -#define ALOGV(...) ((void)0) -#else -#define ALOGV(...) ((void)ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) -#endif -#endif - -#define CONDITION(cond) (__builtin_expect((cond)!=0, 0)) - -#ifndef ALOGV_IF -#if LOG_NDEBUG -#define ALOGV_IF(cond, ...) ((void)0) -#else -#define ALOGV_IF(cond, ...) \ - ( (CONDITION(cond)) \ - ? ((void)ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) \ - : (void)0 ) -#endif -#endif - -/* - * Simplified macro to send a debug log message using the current LOG_TAG. - */ -#ifndef ALOGD -#define ALOGD(...) ((void)ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)) -#endif - -#ifndef ALOGD_IF -#define ALOGD_IF(cond, ...) \ - ( (CONDITION(cond)) \ - ? ((void)ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)) \ - : (void)0 ) -#endif - -/* - * Simplified macro to send an info log message using the current LOG_TAG. - */ -#ifndef ALOGI -#define ALOGI(...) ((void)ALOG(LOG_INFO, LOG_TAG, __VA_ARGS__)) -#endif - -#ifndef ALOGI_IF -#define ALOGI_IF(cond, ...) \ - ( (CONDITION(cond)) \ - ? ((void)ALOG(LOG_INFO, LOG_TAG, __VA_ARGS__)) \ - : (void)0 ) -#endif - -/* - * Simplified macro to send a warning log message using the current LOG_TAG. - */ -#ifndef ALOGW -#define ALOGW(...) ((void)ALOG(LOG_WARN, LOG_TAG, __VA_ARGS__)) -#endif - -#ifndef ALOGW_IF -#define ALOGW_IF(cond, ...) \ - ( (CONDITION(cond)) \ - ? ((void)ALOG(LOG_WARN, LOG_TAG, __VA_ARGS__)) \ - : (void)0 ) -#endif - -/* - * Simplified macro to send an error log message using the current LOG_TAG. - */ -#ifndef ALOGE -#define ALOGE(...) ((void)ALOG(LOG_ERROR, LOG_TAG, __VA_ARGS__)) -#endif - -#ifndef ALOGE_IF -#define ALOGE_IF(cond, ...) \ - ( (CONDITION(cond)) \ - ? ((void)ALOG(LOG_ERROR, LOG_TAG, __VA_ARGS__)) \ - : (void)0 ) -#endif - -// --------------------------------------------------------------------- - -/* - * Conditional based on whether the current LOG_TAG is enabled at - * verbose priority. - */ -#ifndef IF_ALOGV -#if LOG_NDEBUG -#define IF_ALOGV() if (false) -#else -#define IF_ALOGV() IF_ALOG(LOG_VERBOSE, LOG_TAG) -#endif -#endif - -/* - * Conditional based on whether the current LOG_TAG is enabled at - * debug priority. - */ -#ifndef IF_ALOGD -#define IF_ALOGD() IF_ALOG(LOG_DEBUG, LOG_TAG) -#endif - -/* - * Conditional based on whether the current LOG_TAG is enabled at - * info priority. - */ -#ifndef IF_ALOGI -#define IF_ALOGI() IF_ALOG(LOG_INFO, LOG_TAG) -#endif - -/* - * Conditional based on whether the current LOG_TAG is enabled at - * warn priority. - */ -#ifndef IF_ALOGW -#define IF_ALOGW() IF_ALOG(LOG_WARN, LOG_TAG) -#endif - -/* - * Conditional based on whether the current LOG_TAG is enabled at - * error priority. - */ -#ifndef IF_ALOGE -#define IF_ALOGE() IF_ALOG(LOG_ERROR, LOG_TAG) -#endif - - -// --------------------------------------------------------------------- - -/* - * Simplified macro to send a verbose system log message using the current LOG_TAG. - */ -#ifndef SLOGV -#if LOG_NDEBUG -#define SLOGV(...) ((void)0) -#else -#define SLOGV(...) ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) -#endif -#endif - -#define CONDITION(cond) (__builtin_expect((cond)!=0, 0)) - -#ifndef SLOGV_IF -#if LOG_NDEBUG -#define SLOGV_IF(cond, ...) ((void)0) -#else -#define SLOGV_IF(cond, ...) \ - ( (CONDITION(cond)) \ - ? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) \ - : (void)0 ) -#endif -#endif - -/* - * Simplified macro to send a debug system log message using the current LOG_TAG. - */ -#ifndef SLOGD -#define SLOGD(...) ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)) -#endif - -#ifndef SLOGD_IF -#define SLOGD_IF(cond, ...) \ - ( (CONDITION(cond)) \ - ? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)) \ - : (void)0 ) -#endif - -/* - * Simplified macro to send an info system log message using the current LOG_TAG. - */ -#ifndef SLOGI -#define SLOGI(...) ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)) -#endif - -#ifndef SLOGI_IF -#define SLOGI_IF(cond, ...) \ - ( (CONDITION(cond)) \ - ? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)) \ - : (void)0 ) -#endif - -/* - * Simplified macro to send a warning system log message using the current LOG_TAG. - */ -#ifndef SLOGW -#define SLOGW(...) ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)) -#endif - -#ifndef SLOGW_IF -#define SLOGW_IF(cond, ...) \ - ( (CONDITION(cond)) \ - ? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)) \ - : (void)0 ) -#endif - -/* - * Simplified macro to send an error system log message using the current LOG_TAG. - */ -#ifndef SLOGE -#define SLOGE(...) ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)) -#endif - -#ifndef SLOGE_IF -#define SLOGE_IF(cond, ...) \ - ( (CONDITION(cond)) \ - ? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)) \ - : (void)0 ) -#endif - -// --------------------------------------------------------------------- - -/* - * Simplified macro to send a verbose radio log message using the current LOG_TAG. - */ -#ifndef RLOGV -#if LOG_NDEBUG -#define RLOGV(...) ((void)0) -#else -#define RLOGV(...) ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) -#endif -#endif - -#define CONDITION(cond) (__builtin_expect((cond)!=0, 0)) - -#ifndef RLOGV_IF -#if LOG_NDEBUG -#define RLOGV_IF(cond, ...) ((void)0) -#else -#define RLOGV_IF(cond, ...) \ - ( (CONDITION(cond)) \ - ? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) \ - : (void)0 ) -#endif -#endif - -/* - * Simplified macro to send a debug radio log message using the current LOG_TAG. - */ -#ifndef RLOGD -#define RLOGD(...) ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)) -#endif - -#ifndef RLOGD_IF -#define RLOGD_IF(cond, ...) \ - ( (CONDITION(cond)) \ - ? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)) \ - : (void)0 ) -#endif - -/* - * Simplified macro to send an info radio log message using the current LOG_TAG. - */ -#ifndef RLOGI -#define RLOGI(...) ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)) -#endif - -#ifndef RLOGI_IF -#define RLOGI_IF(cond, ...) \ - ( (CONDITION(cond)) \ - ? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)) \ - : (void)0 ) -#endif - -/* - * Simplified macro to send a warning radio log message using the current LOG_TAG. - */ -#ifndef RLOGW -#define RLOGW(...) ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)) -#endif - -#ifndef RLOGW_IF -#define RLOGW_IF(cond, ...) \ - ( (CONDITION(cond)) \ - ? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)) \ - : (void)0 ) -#endif - -/* - * Simplified macro to send an error radio log message using the current LOG_TAG. - */ -#ifndef RLOGE -#define RLOGE(...) ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)) -#endif - -#ifndef RLOGE_IF -#define RLOGE_IF(cond, ...) \ - ( (CONDITION(cond)) \ - ? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)) \ - : (void)0 ) -#endif - - -// --------------------------------------------------------------------- - -/* - * Log a fatal error. If the given condition fails, this stops program - * execution like a normal assertion, but also generating the given message. - * It is NOT stripped from release builds. Note that the condition test - * is -inverted- from the normal assert() semantics. - */ -#ifndef LOG_ALWAYS_FATAL_IF -#define LOG_ALWAYS_FATAL_IF(cond, ...) \ - ( (CONDITION(cond)) \ - ? ((void)android_printAssert(#cond, LOG_TAG, ## __VA_ARGS__)) \ - : (void)0 ) -#endif - -#ifndef LOG_ALWAYS_FATAL -#define LOG_ALWAYS_FATAL(...) \ - ( ((void)android_printAssert(NULL, LOG_TAG, ## __VA_ARGS__)) ) -#endif - -/* - * Versions of LOG_ALWAYS_FATAL_IF and LOG_ALWAYS_FATAL that - * are stripped out of release builds. - */ -#if LOG_NDEBUG - -#ifndef LOG_FATAL_IF -#define LOG_FATAL_IF(cond, ...) ((void)0) -#endif -#ifndef LOG_FATAL -#define LOG_FATAL(...) ((void)0) -#endif - -#else - -#ifndef LOG_FATAL_IF -#define LOG_FATAL_IF(cond, ...) LOG_ALWAYS_FATAL_IF(cond, ## __VA_ARGS__) -#endif -#ifndef LOG_FATAL -#define LOG_FATAL(...) LOG_ALWAYS_FATAL(__VA_ARGS__) -#endif - -#endif - -/* - * Assertion that generates a log message when the assertion fails. - * Stripped out of release builds. Uses the current LOG_TAG. - */ -#ifndef ALOG_ASSERT -#define ALOG_ASSERT(cond, ...) LOG_FATAL_IF(!(cond), ## __VA_ARGS__) -//#define ALOG_ASSERT(cond) LOG_FATAL_IF(!(cond), "Assertion failed: " #cond) -#endif - -// --------------------------------------------------------------------- - -/* - * Basic log message macro. - * - * Example: - * ALOG(LOG_WARN, NULL, "Failed with error %d", errno); - * - * The second argument may be NULL or "" to indicate the "global" tag. - */ -#ifndef ALOG -#define ALOG(priority, tag, ...) \ - LOG_PRI(ANDROID_##priority, tag, __VA_ARGS__) -#endif - -/* - * Log macro that allows you to specify a number for the priority. - */ -#ifndef LOG_PRI -#define LOG_PRI(priority, tag, ...) \ - android_printLog(priority, tag, __VA_ARGS__) -#endif - -/* - * Log macro that allows you to pass in a varargs ("args" is a va_list). - */ -#ifndef LOG_PRI_VA -#define LOG_PRI_VA(priority, tag, fmt, args) \ - android_vprintLog(priority, NULL, tag, fmt, args) -#endif - -/* - * Conditional given a desired logging priority and tag. - */ -#ifndef IF_ALOG -#define IF_ALOG(priority, tag) \ - if (android_testLog(ANDROID_##priority, tag)) -#endif - -// --------------------------------------------------------------------- - -/* - * Event logging. - */ - -/* - * Event log entry types. These must match up with the declarations in - * java/android/android/util/EventLog.java. - */ -typedef enum { - EVENT_TYPE_INT = 0, - EVENT_TYPE_LONG = 1, - EVENT_TYPE_STRING = 2, - EVENT_TYPE_LIST = 3, -} AndroidEventLogType; - - -#ifndef LOG_EVENT_INT -#define LOG_EVENT_INT(_tag, _value) { \ - int intBuf = _value; \ - (void) android_btWriteLog(_tag, EVENT_TYPE_INT, &intBuf, \ - sizeof(intBuf)); \ - } -#endif -#ifndef LOG_EVENT_LONG -#define LOG_EVENT_LONG(_tag, _value) { \ - long long longBuf = _value; \ - (void) android_btWriteLog(_tag, EVENT_TYPE_LONG, &longBuf, \ - sizeof(longBuf)); \ - } -#endif -#ifndef LOG_EVENT_STRING -#define LOG_EVENT_STRING(_tag, _value) \ - ((void) 0) /* not implemented -- must combine len with string */ -#endif -/* TODO: something for LIST */ - -/* - * =========================================================================== - * - * The stuff in the rest of this file should not be used directly. - */ - -#define android_printLog(prio, tag, fmt...) \ - __android_log_print(prio, tag, fmt) - -#define android_vprintLog(prio, cond, tag, fmt...) \ - __android_log_vprint(prio, tag, fmt) - -/* XXX Macros to work around syntax errors in places where format string - * arg is not passed to ALOG_ASSERT, LOG_ALWAYS_FATAL or LOG_ALWAYS_FATAL_IF - * (happens only in debug builds). - */ - -/* Returns 2nd arg. Used to substitute default value if caller's vararg list - * is empty. - */ -#define __android_second(dummy, second, ...) second - -/* If passed multiple args, returns ',' followed by all but 1st arg, otherwise - * returns nothing. - */ -#define __android_rest(first, ...) , ## __VA_ARGS__ - -#define android_printAssert(cond, tag, fmt...) \ - __android_log_assert(cond, tag, \ - __android_second(0, ## fmt, NULL) __android_rest(fmt)) - -#define android_writeLog(prio, tag, text) \ - __android_log_write(prio, tag, text) - -#define android_bWriteLog(tag, payload, len) \ - __android_log_bwrite(tag, payload, len) -#define android_btWriteLog(tag, type, payload, len) \ - __android_log_btwrite(tag, type, payload, len) - -// TODO: remove these prototypes and their users -#define android_testLog(prio, tag) (1) -#define android_writevLog(vec,num) do{}while(0) -#define android_write1Log(str,len) do{}while (0) -#define android_setMinPriority(tag, prio) do{}while(0) -//#define android_logToCallback(func) do{}while(0) -#define android_logToFile(tag, file) (0) -#define android_logToFd(tag, fd) (0) - -typedef enum { - LOG_ID_MAIN = 0, - LOG_ID_RADIO = 1, - LOG_ID_EVENTS = 2, - LOG_ID_SYSTEM = 3, - - LOG_ID_MAX -} log_id_t; - -/* - * Send a simple string to the log. - */ -int __android_log_buf_write(int bufID, int prio, const char *tag, const char *text); -int __android_log_buf_print(int bufID, int prio, const char *tag, const char *fmt, ...); - - -#ifdef __cplusplus -} -#endif - -#endif // _LIBS_CUTILS_LOG_H +#include diff --git a/include/cutils/event_tag_map.h b/include/log/event_tag_map.h similarity index 100% rename from include/cutils/event_tag_map.h rename to include/log/event_tag_map.h diff --git a/include/log/log.h b/include/log/log.h new file mode 100644 index 000000000..7faddea91 --- /dev/null +++ b/include/log/log.h @@ -0,0 +1,563 @@ +/* + * Copyright (C) 2005 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. + */ + +// +// C/C++ logging functions. See the logging documentation for API details. +// +// We'd like these to be available from C code (in case we import some from +// somewhere), so this has a C interface. +// +// The output will be correct when the log file is shared between multiple +// threads and/or multiple processes so long as the operating system +// supports O_APPEND. These calls have mutex-protected data structures +// and so are NOT reentrant. Do not use LOG in a signal handler. +// +#ifndef _LIBS_LOG_LOG_H +#define _LIBS_LOG_LOG_H + +#include +#include +#include +#include +#ifdef HAVE_PTHREADS +#include +#endif +#include + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// --------------------------------------------------------------------- + +/* + * Normally we strip ALOGV (VERBOSE messages) from release builds. + * You can modify this (for example with "#define LOG_NDEBUG 0" + * at the top of your source file) to change that behavior. + */ +#ifndef LOG_NDEBUG +#ifdef NDEBUG +#define LOG_NDEBUG 1 +#else +#define LOG_NDEBUG 0 +#endif +#endif + +/* + * This is the local tag used for the following simplified + * logging macros. You can change this preprocessor definition + * before using the other macros to change the tag. + */ +#ifndef LOG_TAG +#define LOG_TAG NULL +#endif + +// --------------------------------------------------------------------- + +/* + * Simplified macro to send a verbose log message using the current LOG_TAG. + */ +#ifndef ALOGV +#if LOG_NDEBUG +#define ALOGV(...) ((void)0) +#else +#define ALOGV(...) ((void)ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) +#endif +#endif + +#define CONDITION(cond) (__builtin_expect((cond)!=0, 0)) + +#ifndef ALOGV_IF +#if LOG_NDEBUG +#define ALOGV_IF(cond, ...) ((void)0) +#else +#define ALOGV_IF(cond, ...) \ + ( (CONDITION(cond)) \ + ? ((void)ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) \ + : (void)0 ) +#endif +#endif + +/* + * Simplified macro to send a debug log message using the current LOG_TAG. + */ +#ifndef ALOGD +#define ALOGD(...) ((void)ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)) +#endif + +#ifndef ALOGD_IF +#define ALOGD_IF(cond, ...) \ + ( (CONDITION(cond)) \ + ? ((void)ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)) \ + : (void)0 ) +#endif + +/* + * Simplified macro to send an info log message using the current LOG_TAG. + */ +#ifndef ALOGI +#define ALOGI(...) ((void)ALOG(LOG_INFO, LOG_TAG, __VA_ARGS__)) +#endif + +#ifndef ALOGI_IF +#define ALOGI_IF(cond, ...) \ + ( (CONDITION(cond)) \ + ? ((void)ALOG(LOG_INFO, LOG_TAG, __VA_ARGS__)) \ + : (void)0 ) +#endif + +/* + * Simplified macro to send a warning log message using the current LOG_TAG. + */ +#ifndef ALOGW +#define ALOGW(...) ((void)ALOG(LOG_WARN, LOG_TAG, __VA_ARGS__)) +#endif + +#ifndef ALOGW_IF +#define ALOGW_IF(cond, ...) \ + ( (CONDITION(cond)) \ + ? ((void)ALOG(LOG_WARN, LOG_TAG, __VA_ARGS__)) \ + : (void)0 ) +#endif + +/* + * Simplified macro to send an error log message using the current LOG_TAG. + */ +#ifndef ALOGE +#define ALOGE(...) ((void)ALOG(LOG_ERROR, LOG_TAG, __VA_ARGS__)) +#endif + +#ifndef ALOGE_IF +#define ALOGE_IF(cond, ...) \ + ( (CONDITION(cond)) \ + ? ((void)ALOG(LOG_ERROR, LOG_TAG, __VA_ARGS__)) \ + : (void)0 ) +#endif + +// --------------------------------------------------------------------- + +/* + * Conditional based on whether the current LOG_TAG is enabled at + * verbose priority. + */ +#ifndef IF_ALOGV +#if LOG_NDEBUG +#define IF_ALOGV() if (false) +#else +#define IF_ALOGV() IF_ALOG(LOG_VERBOSE, LOG_TAG) +#endif +#endif + +/* + * Conditional based on whether the current LOG_TAG is enabled at + * debug priority. + */ +#ifndef IF_ALOGD +#define IF_ALOGD() IF_ALOG(LOG_DEBUG, LOG_TAG) +#endif + +/* + * Conditional based on whether the current LOG_TAG is enabled at + * info priority. + */ +#ifndef IF_ALOGI +#define IF_ALOGI() IF_ALOG(LOG_INFO, LOG_TAG) +#endif + +/* + * Conditional based on whether the current LOG_TAG is enabled at + * warn priority. + */ +#ifndef IF_ALOGW +#define IF_ALOGW() IF_ALOG(LOG_WARN, LOG_TAG) +#endif + +/* + * Conditional based on whether the current LOG_TAG is enabled at + * error priority. + */ +#ifndef IF_ALOGE +#define IF_ALOGE() IF_ALOG(LOG_ERROR, LOG_TAG) +#endif + + +// --------------------------------------------------------------------- + +/* + * Simplified macro to send a verbose system log message using the current LOG_TAG. + */ +#ifndef SLOGV +#if LOG_NDEBUG +#define SLOGV(...) ((void)0) +#else +#define SLOGV(...) ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) +#endif +#endif + +#define CONDITION(cond) (__builtin_expect((cond)!=0, 0)) + +#ifndef SLOGV_IF +#if LOG_NDEBUG +#define SLOGV_IF(cond, ...) ((void)0) +#else +#define SLOGV_IF(cond, ...) \ + ( (CONDITION(cond)) \ + ? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) \ + : (void)0 ) +#endif +#endif + +/* + * Simplified macro to send a debug system log message using the current LOG_TAG. + */ +#ifndef SLOGD +#define SLOGD(...) ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)) +#endif + +#ifndef SLOGD_IF +#define SLOGD_IF(cond, ...) \ + ( (CONDITION(cond)) \ + ? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)) \ + : (void)0 ) +#endif + +/* + * Simplified macro to send an info system log message using the current LOG_TAG. + */ +#ifndef SLOGI +#define SLOGI(...) ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)) +#endif + +#ifndef SLOGI_IF +#define SLOGI_IF(cond, ...) \ + ( (CONDITION(cond)) \ + ? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)) \ + : (void)0 ) +#endif + +/* + * Simplified macro to send a warning system log message using the current LOG_TAG. + */ +#ifndef SLOGW +#define SLOGW(...) ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)) +#endif + +#ifndef SLOGW_IF +#define SLOGW_IF(cond, ...) \ + ( (CONDITION(cond)) \ + ? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)) \ + : (void)0 ) +#endif + +/* + * Simplified macro to send an error system log message using the current LOG_TAG. + */ +#ifndef SLOGE +#define SLOGE(...) ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)) +#endif + +#ifndef SLOGE_IF +#define SLOGE_IF(cond, ...) \ + ( (CONDITION(cond)) \ + ? ((void)__android_log_buf_print(LOG_ID_SYSTEM, ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)) \ + : (void)0 ) +#endif + +// --------------------------------------------------------------------- + +/* + * Simplified macro to send a verbose radio log message using the current LOG_TAG. + */ +#ifndef RLOGV +#if LOG_NDEBUG +#define RLOGV(...) ((void)0) +#else +#define RLOGV(...) ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) +#endif +#endif + +#define CONDITION(cond) (__builtin_expect((cond)!=0, 0)) + +#ifndef RLOGV_IF +#if LOG_NDEBUG +#define RLOGV_IF(cond, ...) ((void)0) +#else +#define RLOGV_IF(cond, ...) \ + ( (CONDITION(cond)) \ + ? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) \ + : (void)0 ) +#endif +#endif + +/* + * Simplified macro to send a debug radio log message using the current LOG_TAG. + */ +#ifndef RLOGD +#define RLOGD(...) ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)) +#endif + +#ifndef RLOGD_IF +#define RLOGD_IF(cond, ...) \ + ( (CONDITION(cond)) \ + ? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)) \ + : (void)0 ) +#endif + +/* + * Simplified macro to send an info radio log message using the current LOG_TAG. + */ +#ifndef RLOGI +#define RLOGI(...) ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)) +#endif + +#ifndef RLOGI_IF +#define RLOGI_IF(cond, ...) \ + ( (CONDITION(cond)) \ + ? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)) \ + : (void)0 ) +#endif + +/* + * Simplified macro to send a warning radio log message using the current LOG_TAG. + */ +#ifndef RLOGW +#define RLOGW(...) ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)) +#endif + +#ifndef RLOGW_IF +#define RLOGW_IF(cond, ...) \ + ( (CONDITION(cond)) \ + ? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)) \ + : (void)0 ) +#endif + +/* + * Simplified macro to send an error radio log message using the current LOG_TAG. + */ +#ifndef RLOGE +#define RLOGE(...) ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)) +#endif + +#ifndef RLOGE_IF +#define RLOGE_IF(cond, ...) \ + ( (CONDITION(cond)) \ + ? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)) \ + : (void)0 ) +#endif + + +// --------------------------------------------------------------------- + +/* + * Log a fatal error. If the given condition fails, this stops program + * execution like a normal assertion, but also generating the given message. + * It is NOT stripped from release builds. Note that the condition test + * is -inverted- from the normal assert() semantics. + */ +#ifndef LOG_ALWAYS_FATAL_IF +#define LOG_ALWAYS_FATAL_IF(cond, ...) \ + ( (CONDITION(cond)) \ + ? ((void)android_printAssert(#cond, LOG_TAG, ## __VA_ARGS__)) \ + : (void)0 ) +#endif + +#ifndef LOG_ALWAYS_FATAL +#define LOG_ALWAYS_FATAL(...) \ + ( ((void)android_printAssert(NULL, LOG_TAG, ## __VA_ARGS__)) ) +#endif + +/* + * Versions of LOG_ALWAYS_FATAL_IF and LOG_ALWAYS_FATAL that + * are stripped out of release builds. + */ +#if LOG_NDEBUG + +#ifndef LOG_FATAL_IF +#define LOG_FATAL_IF(cond, ...) ((void)0) +#endif +#ifndef LOG_FATAL +#define LOG_FATAL(...) ((void)0) +#endif + +#else + +#ifndef LOG_FATAL_IF +#define LOG_FATAL_IF(cond, ...) LOG_ALWAYS_FATAL_IF(cond, ## __VA_ARGS__) +#endif +#ifndef LOG_FATAL +#define LOG_FATAL(...) LOG_ALWAYS_FATAL(__VA_ARGS__) +#endif + +#endif + +/* + * Assertion that generates a log message when the assertion fails. + * Stripped out of release builds. Uses the current LOG_TAG. + */ +#ifndef ALOG_ASSERT +#define ALOG_ASSERT(cond, ...) LOG_FATAL_IF(!(cond), ## __VA_ARGS__) +//#define ALOG_ASSERT(cond) LOG_FATAL_IF(!(cond), "Assertion failed: " #cond) +#endif + +// --------------------------------------------------------------------- + +/* + * Basic log message macro. + * + * Example: + * ALOG(LOG_WARN, NULL, "Failed with error %d", errno); + * + * The second argument may be NULL or "" to indicate the "global" tag. + */ +#ifndef ALOG +#define ALOG(priority, tag, ...) \ + LOG_PRI(ANDROID_##priority, tag, __VA_ARGS__) +#endif + +/* + * Log macro that allows you to specify a number for the priority. + */ +#ifndef LOG_PRI +#define LOG_PRI(priority, tag, ...) \ + android_printLog(priority, tag, __VA_ARGS__) +#endif + +/* + * Log macro that allows you to pass in a varargs ("args" is a va_list). + */ +#ifndef LOG_PRI_VA +#define LOG_PRI_VA(priority, tag, fmt, args) \ + android_vprintLog(priority, NULL, tag, fmt, args) +#endif + +/* + * Conditional given a desired logging priority and tag. + */ +#ifndef IF_ALOG +#define IF_ALOG(priority, tag) \ + if (android_testLog(ANDROID_##priority, tag)) +#endif + +// --------------------------------------------------------------------- + +/* + * Event logging. + */ + +/* + * Event log entry types. These must match up with the declarations in + * java/android/android/util/EventLog.java. + */ +typedef enum { + EVENT_TYPE_INT = 0, + EVENT_TYPE_LONG = 1, + EVENT_TYPE_STRING = 2, + EVENT_TYPE_LIST = 3, +} AndroidEventLogType; + + +#ifndef LOG_EVENT_INT +#define LOG_EVENT_INT(_tag, _value) { \ + int intBuf = _value; \ + (void) android_btWriteLog(_tag, EVENT_TYPE_INT, &intBuf, \ + sizeof(intBuf)); \ + } +#endif +#ifndef LOG_EVENT_LONG +#define LOG_EVENT_LONG(_tag, _value) { \ + long long longBuf = _value; \ + (void) android_btWriteLog(_tag, EVENT_TYPE_LONG, &longBuf, \ + sizeof(longBuf)); \ + } +#endif +#ifndef LOG_EVENT_STRING +#define LOG_EVENT_STRING(_tag, _value) \ + ((void) 0) /* not implemented -- must combine len with string */ +#endif +/* TODO: something for LIST */ + +/* + * =========================================================================== + * + * The stuff in the rest of this file should not be used directly. + */ + +#define android_printLog(prio, tag, fmt...) \ + __android_log_print(prio, tag, fmt) + +#define android_vprintLog(prio, cond, tag, fmt...) \ + __android_log_vprint(prio, tag, fmt) + +/* XXX Macros to work around syntax errors in places where format string + * arg is not passed to ALOG_ASSERT, LOG_ALWAYS_FATAL or LOG_ALWAYS_FATAL_IF + * (happens only in debug builds). + */ + +/* Returns 2nd arg. Used to substitute default value if caller's vararg list + * is empty. + */ +#define __android_second(dummy, second, ...) second + +/* If passed multiple args, returns ',' followed by all but 1st arg, otherwise + * returns nothing. + */ +#define __android_rest(first, ...) , ## __VA_ARGS__ + +#define android_printAssert(cond, tag, fmt...) \ + __android_log_assert(cond, tag, \ + __android_second(0, ## fmt, NULL) __android_rest(fmt)) + +#define android_writeLog(prio, tag, text) \ + __android_log_write(prio, tag, text) + +#define android_bWriteLog(tag, payload, len) \ + __android_log_bwrite(tag, payload, len) +#define android_btWriteLog(tag, type, payload, len) \ + __android_log_btwrite(tag, type, payload, len) + +// TODO: remove these prototypes and their users +#define android_testLog(prio, tag) (1) +#define android_writevLog(vec,num) do{}while(0) +#define android_write1Log(str,len) do{}while (0) +#define android_setMinPriority(tag, prio) do{}while(0) +//#define android_logToCallback(func) do{}while(0) +#define android_logToFile(tag, file) (0) +#define android_logToFd(tag, fd) (0) + +typedef enum { + LOG_ID_MAIN = 0, + LOG_ID_RADIO = 1, + LOG_ID_EVENTS = 2, + LOG_ID_SYSTEM = 3, + + LOG_ID_MAX +} log_id_t; + +/* + * Send a simple string to the log. + */ +int __android_log_buf_write(int bufID, int prio, const char *tag, const char *text); +int __android_log_buf_print(int bufID, int prio, const char *tag, const char *fmt, ...); + + +#ifdef __cplusplus +} +#endif + +#endif // _LIBS_CUTILS_LOG_H diff --git a/include/cutils/logd.h b/include/log/logd.h similarity index 98% rename from include/cutils/logd.h rename to include/log/logd.h index 8737639cc..379c37334 100644 --- a/include/cutils/logd.h +++ b/include/log/logd.h @@ -31,7 +31,7 @@ #ifdef HAVE_PTHREADS #include #endif -#include +#include #include #ifdef __cplusplus diff --git a/include/cutils/logger.h b/include/log/logger.h similarity index 100% rename from include/cutils/logger.h rename to include/log/logger.h diff --git a/include/cutils/logprint.h b/include/log/logprint.h similarity index 97% rename from include/cutils/logprint.h rename to include/log/logprint.h index 2b1e1c515..481c96e2e 100644 --- a/include/cutils/logprint.h +++ b/include/log/logprint.h @@ -17,9 +17,9 @@ #ifndef _LOGPRINT_H #define _LOGPRINT_H -#include -#include -#include +#include +#include +#include #include #ifdef __cplusplus diff --git a/include/cutils/uio.h b/include/log/uio.h similarity index 100% rename from include/cutils/uio.h rename to include/log/uio.h diff --git a/libcutils/Android.mk b/libcutils/Android.mk index e46216eec..0fd5a57e0 100644 --- a/libcutils/Android.mk +++ b/libcutils/Android.mk @@ -65,10 +65,7 @@ ifneq ($(strip $(USE_MINGW)),) WINDOWS_HOST_ONLY := 1 endif -ifeq ($(WINDOWS_HOST_ONLY),1) - commonSources += \ - uio.c -else +ifneq ($(WINDOWS_HOST_ONLY),1) commonSources += \ fs.c \ multiuser.c diff --git a/liblog/Android.mk b/liblog/Android.mk index be5cec261..6bfb11966 100644 --- a/liblog/Android.mk +++ b/liblog/Android.mk @@ -36,6 +36,9 @@ ifndef WITH_MINGW liblog_sources += \ logprint.c \ event_tag_map.c +else + liblog_sources += \ + uio.c endif liblog_host_sources := $(liblog_sources) fake_log_device.c diff --git a/liblog/event_tag_map.c b/liblog/event_tag_map.c index e70754e17..f3d1e2fe2 100644 --- a/liblog/event_tag_map.c +++ b/liblog/event_tag_map.c @@ -13,8 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include "cutils/event_tag_map.h" -#include "cutils/log.h" +#include +#include #include #include diff --git a/liblog/fake_log_device.c b/liblog/fake_log_device.c index df4329921..528361986 100644 --- a/liblog/fake_log_device.c +++ b/liblog/fake_log_device.c @@ -19,7 +19,7 @@ * passed on to the underlying (fake) log device. When not in the * simulator, messages are printed to stderr. */ -#include "cutils/logd.h" +#include #include #include diff --git a/liblog/logd_write.c b/liblog/logd_write.c index 3613d25f2..fff7cc4dd 100644 --- a/liblog/logd_write.c +++ b/liblog/logd_write.c @@ -27,9 +27,9 @@ #include #include -#include -#include -#include +#include +#include +#include #define LOG_BUF_SIZE 1024 diff --git a/liblog/logprint.c b/liblog/logprint.c index 6fac84b46..508c82565 100644 --- a/liblog/logprint.c +++ b/liblog/logprint.c @@ -26,8 +26,8 @@ #include #include -#include -#include +#include +#include typedef struct FilterInfo_t { char *mTag; diff --git a/libcutils/uio.c b/liblog/uio.c similarity index 98% rename from libcutils/uio.c rename to liblog/uio.c index baa8051bc..cfa4cb122 100644 --- a/libcutils/uio.c +++ b/liblog/uio.c @@ -16,7 +16,7 @@ #ifndef HAVE_SYS_UIO_H -#include +#include #include int readv( int fd, struct iovec* vecs, int count ) diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp index d3b5ed0b9..d44c6796b 100644 --- a/logcat/logcat.cpp +++ b/logcat/logcat.cpp @@ -1,10 +1,10 @@ // Copyright 2006 The Android Open Source Project -#include -#include +#include +#include +#include +#include #include -#include -#include #include #include diff --git a/toolbox/log.c b/toolbox/log.c index 0bee19e90..2f020a8a4 100644 --- a/toolbox/log.c +++ b/toolbox/log.c @@ -30,7 +30,7 @@ */ #include -#include +#include #include #include #include From 8d9da28ab5f32e3cd83858a5b31f45bb5d03a1fd Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Thu, 25 Jul 2013 17:07:11 -0700 Subject: [PATCH 063/541] add sRGB pixel formats to the HALs Change-Id: I1d5f9dd14a6485dd3a29fb5960edfa79aa86da42 --- include/system/graphics.h | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/include/system/graphics.h b/include/system/graphics.h index ed493f5ae..2ddcc08ce 100644 --- a/include/system/graphics.h +++ b/include/system/graphics.h @@ -42,6 +42,13 @@ extern "C" { */ enum { + /* + * "linear" color pixel formats: + * + * The pixel formats below contain sRGB data but are otherwise treated + * as linear formats, i.e.: no special operation is performed when + * reading or writing into a buffer in one of these formats + */ HAL_PIXEL_FORMAT_RGBA_8888 = 1, HAL_PIXEL_FORMAT_RGBX_8888 = 2, HAL_PIXEL_FORMAT_RGB_888 = 3, @@ -50,7 +57,27 @@ enum { HAL_PIXEL_FORMAT_RGBA_5551 = 6, HAL_PIXEL_FORMAT_RGBA_4444 = 7, - /* 0x8 - 0xFF range unavailable */ + /* + * sRGB color pixel formats: + * + * The red, green and blue components are stored in sRGB space, and converted + * to linear space when read, using the standard sRGB to linear equation: + * + * Clinear = Csrgb / 12.92 for Csrgb <= 0.04045 + * = (Csrgb + 0.055 / 1.055)^2.4 for Csrgb > 0.04045 + * + * When written the inverse transformation is performed: + * + * Csrgb = 12.92 * Clinear for Clinear <= 0.0031308 + * = 1.055 * Clinear^(1/2.4) - 0.055 for Clinear > 0.0031308 + * + * + * The alpha component, if present, is always stored in linear space and + * is left unmodified when read or written. + * + */ + HAL_PIXEL_FORMAT_sRGB_A_8888 = 0xC, + HAL_PIXEL_FORMAT_sRGB_888 = 0xD, /* * 0x100 - 0x1FF From 57fdb5cfd2b20f47299a12c2050ff8bb2707c788 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Thu, 25 Jul 2013 10:34:30 -0700 Subject: [PATCH 064/541] add property to adjust extra_free_kbytes kernel vm tunable ActivityManager can't directly write to extra_free_kbytes because /proc/sys rejects all chown and chmod syscalls. Proxy the writes through init by using the sys.sysctl.extra_free_kbytes property. Bug: 10024467 Change-Id: I441e00478421254355fcafb252bc878166483d4c --- rootdir/init.rc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/rootdir/init.rc b/rootdir/init.rc index 399028732..c7e96903e 100644 --- a/rootdir/init.rc +++ b/rootdir/init.rc @@ -402,6 +402,10 @@ on property:vold.decrypt=trigger_shutdown_framework on property:sys.powerctl=* powerctl ${sys.powerctl} +# system server cannot write to /proc/sys files, so proxy it through init +on property:sys.sysctl.extra_free_kbytes=* + write /proc/sys/vm/extra_free_kbytes ${sys.sysctl.extra_free_kbytes} + ## Daemon processes to be run by init. ## service ueventd /sbin/ueventd From 752faf2c18407fd79127fb3f3773910b4ddf669d Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Wed, 12 Jun 2013 13:25:59 -0700 Subject: [PATCH 065/541] healthd: Add system health monitoring daemon Initially moving battery health monitoring here. Command line flag -n tells healthd not to use (or wait for) servicemanager in this execution, for charger and recovery modes. Change-Id: I1720594724af0c068497b359f9c6ad65aeaa1519 --- healthd/Android.mk | 23 ++ healthd/BatteryMonitor.cpp | 354 +++++++++++++++++++++++++ healthd/BatteryMonitor.h | 67 +++++ healthd/BatteryPropertiesRegistrar.cpp | 81 ++++++ healthd/BatteryPropertiesRegistrar.h | 52 ++++ healthd/healthd.cpp | 258 ++++++++++++++++++ 6 files changed, 835 insertions(+) create mode 100644 healthd/Android.mk create mode 100644 healthd/BatteryMonitor.cpp create mode 100644 healthd/BatteryMonitor.h create mode 100644 healthd/BatteryPropertiesRegistrar.cpp create mode 100644 healthd/BatteryPropertiesRegistrar.h create mode 100644 healthd/healthd.cpp diff --git a/healthd/Android.mk b/healthd/Android.mk new file mode 100644 index 000000000..910afb2ec --- /dev/null +++ b/healthd/Android.mk @@ -0,0 +1,23 @@ +# Copyright 2013 The Android Open Source Project + +ifneq ($(BUILD_TINY_ANDROID),true) + +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + healthd.cpp \ + BatteryMonitor.cpp \ + BatteryPropertiesRegistrar.cpp + +LOCAL_MODULE := healthd +LOCAL_MODULE_TAGS := optional +LOCAL_FORCE_STATIC_EXECUTABLE := true +LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT_SBIN) +LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_SBIN_UNSTRIPPED) + +LOCAL_STATIC_LIBRARIES := libbatteryservice libbinder libz libutils libstdc++ libcutils liblog libm libc + +include $(BUILD_EXECUTABLE) + +endif diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp new file mode 100644 index 000000000..22f77136f --- /dev/null +++ b/healthd/BatteryMonitor.cpp @@ -0,0 +1,354 @@ +/* + * Copyright (C) 2013 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 "healthd" + +#include "BatteryMonitor.h" +#include "BatteryPropertiesRegistrar.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define POWER_SUPPLY_SUBSYSTEM "power_supply" +#define POWER_SUPPLY_SYSFS_PATH "/sys/class/" POWER_SUPPLY_SUBSYSTEM + +namespace android { + +struct sysfsStringEnumMap { + char* s; + int val; +}; + +static int mapSysfsString(const char* str, + struct sysfsStringEnumMap map[]) { + for (int i = 0; map[i].s; i++) + if (!strcmp(str, map[i].s)) + return map[i].val; + + return -1; +} + +int BatteryMonitor::getBatteryStatus(const char* status) { + int ret; + struct sysfsStringEnumMap batteryStatusMap[] = { + { "Unknown", BATTERY_STATUS_UNKNOWN }, + { "Charging", BATTERY_STATUS_CHARGING }, + { "Discharging", BATTERY_STATUS_DISCHARGING }, + { "Not charging", BATTERY_STATUS_NOT_CHARGING }, + { "Full", BATTERY_STATUS_FULL }, + { NULL, 0 }, + }; + + ret = mapSysfsString(status, batteryStatusMap); + if (ret < 0) { + KLOG_WARNING(LOG_TAG, "Unknown battery status '%s'\n", status); + ret = BATTERY_STATUS_UNKNOWN; + } + + return ret; +} + +int BatteryMonitor::getBatteryHealth(const char* status) { + int ret; + struct sysfsStringEnumMap batteryHealthMap[] = { + { "Unknown", BATTERY_HEALTH_UNKNOWN }, + { "Good", BATTERY_HEALTH_GOOD }, + { "Overheat", BATTERY_HEALTH_OVERHEAT }, + { "Dead", BATTERY_HEALTH_DEAD }, + { "Over voltage", BATTERY_HEALTH_OVER_VOLTAGE }, + { "Unspecified failure", BATTERY_HEALTH_UNSPECIFIED_FAILURE }, + { "Cold", BATTERY_HEALTH_COLD }, + { NULL, 0 }, + }; + + ret = mapSysfsString(status, batteryHealthMap); + if (ret < 0) { + KLOG_WARNING(LOG_TAG, "Unknown battery health '%s'\n", status); + ret = BATTERY_HEALTH_UNKNOWN; + } + + return ret; +} + +int BatteryMonitor::readFromFile(const String8& path, char* buf, size_t size) { + char *cp = NULL; + + if (path.isEmpty()) + return -1; + int fd = open(path.string(), O_RDONLY, 0); + if (fd == -1) { + KLOG_ERROR(LOG_TAG, "Could not open '%s'\n", path.string()); + return -1; + } + + ssize_t count = TEMP_FAILURE_RETRY(read(fd, buf, size)); + if (count > 0) + cp = (char *)memrchr(buf, '\n', count); + + if (cp) + *cp = '\0'; + else + buf[0] = '\0'; + + close(fd); + return count; +} + +BatteryMonitor::PowerSupplyType BatteryMonitor::readPowerSupplyType(const String8& path) { + const int SIZE = 128; + char buf[SIZE]; + int length = readFromFile(path, buf, SIZE); + BatteryMonitor::PowerSupplyType ret; + struct sysfsStringEnumMap supplyTypeMap[] = { + { "Unknown", ANDROID_POWER_SUPPLY_TYPE_UNKNOWN }, + { "Battery", ANDROID_POWER_SUPPLY_TYPE_BATTERY }, + { "UPS", ANDROID_POWER_SUPPLY_TYPE_AC }, + { "Mains", ANDROID_POWER_SUPPLY_TYPE_AC }, + { "USB", ANDROID_POWER_SUPPLY_TYPE_USB }, + { "USB_DCP", ANDROID_POWER_SUPPLY_TYPE_AC }, + { "USB_CDP", ANDROID_POWER_SUPPLY_TYPE_AC }, + { "USB_ACA", ANDROID_POWER_SUPPLY_TYPE_AC }, + { "Wireless", ANDROID_POWER_SUPPLY_TYPE_WIRELESS }, + { NULL, 0 }, + }; + + if (length <= 0) + return ANDROID_POWER_SUPPLY_TYPE_UNKNOWN; + + ret = (BatteryMonitor::PowerSupplyType)mapSysfsString(buf, supplyTypeMap); + if (ret < 0) + ret = ANDROID_POWER_SUPPLY_TYPE_UNKNOWN; + + return ret; +} + +bool BatteryMonitor::getBooleanField(const String8& path) { + const int SIZE = 16; + char buf[SIZE]; + + bool value = false; + if (readFromFile(path, buf, SIZE) > 0) { + if (buf[0] != '0') { + value = true; + } + } + + return value; +} + +int BatteryMonitor::getIntField(const String8& path) { + const int SIZE = 128; + char buf[SIZE]; + + int value = 0; + if (readFromFile(path, buf, SIZE) > 0) { + value = strtol(buf, NULL, 0); + } + return value; +} + +bool BatteryMonitor::update(void) { + struct BatteryProperties props; + + props.chargerAcOnline = false; + props.chargerUsbOnline = false; + props.chargerWirelessOnline = false; + props.batteryStatus = BATTERY_STATUS_UNKNOWN; + props.batteryHealth = BATTERY_HEALTH_UNKNOWN; + + if (!mBatteryPresentPath.isEmpty()) + props.batteryPresent = getBooleanField(mBatteryPresentPath); + else + props.batteryPresent = true; + + props.batteryLevel = getIntField(mBatteryCapacityPath); + props.batteryVoltage = getIntField(mBatteryVoltagePath) / 1000; + props.batteryTemperature = getIntField(mBatteryTemperaturePath); + + const int SIZE = 128; + char buf[SIZE]; + String8 btech; + + if (readFromFile(mBatteryStatusPath, buf, SIZE) > 0) + props.batteryStatus = getBatteryStatus(buf); + + if (readFromFile(mBatteryHealthPath, buf, SIZE) > 0) + props.batteryHealth = getBatteryHealth(buf); + + if (readFromFile(mBatteryTechnologyPath, buf, SIZE) > 0) + props.batteryTechnology = String8(buf); + + unsigned int i; + + for (i = 0; i < mChargerNames.size(); i++) { + String8 path; + path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH, + mChargerNames[i].string()); + + if (readFromFile(path, buf, SIZE) > 0) { + if (buf[0] != '0') { + path.clear(); + path.appendFormat("%s/%s/type", POWER_SUPPLY_SYSFS_PATH, + mChargerNames[i].string()); + switch(readPowerSupplyType(path)) { + case ANDROID_POWER_SUPPLY_TYPE_AC: + props.chargerAcOnline = true; + break; + case ANDROID_POWER_SUPPLY_TYPE_USB: + props.chargerUsbOnline = true; + break; + case ANDROID_POWER_SUPPLY_TYPE_WIRELESS: + props.chargerWirelessOnline = true; + break; + default: + KLOG_WARNING(LOG_TAG, "%s: Unknown power supply type\n", + mChargerNames[i].string()); + } + } + } + } + + KLOG_INFO(LOG_TAG, "battery l=%d v=%d t=%s%d.%d h=%d st=%d chg=%s%s%s\n", + props.batteryLevel, props.batteryVoltage, + props.batteryTemperature < 0 ? "-" : "", + abs(props.batteryTemperature / 10), + abs(props.batteryTemperature % 10), props.batteryHealth, + props.batteryStatus, + props.chargerAcOnline ? "a" : "", + props.chargerUsbOnline ? "u" : "", + props.chargerWirelessOnline ? "w" : ""); + + if (mBatteryPropertiesRegistrar != NULL) + mBatteryPropertiesRegistrar->notifyListeners(props); + + return props.chargerAcOnline | props.chargerUsbOnline | + props.chargerWirelessOnline; +} + +void BatteryMonitor::init(bool nosvcmgr) { + String8 path; + + DIR* dir = opendir(POWER_SUPPLY_SYSFS_PATH); + if (dir == NULL) { + KLOG_ERROR(LOG_TAG, "Could not open %s\n", POWER_SUPPLY_SYSFS_PATH); + } else { + struct dirent* entry; + + while ((entry = readdir(dir))) { + const char* name = entry->d_name; + + if (!strcmp(name, ".") || !strcmp(name, "..")) + continue; + + char buf[20]; + // Look for "type" file in each subdirectory + path.clear(); + path.appendFormat("%s/%s/type", POWER_SUPPLY_SYSFS_PATH, name); + switch(readPowerSupplyType(path)) { + case ANDROID_POWER_SUPPLY_TYPE_AC: + case ANDROID_POWER_SUPPLY_TYPE_USB: + case ANDROID_POWER_SUPPLY_TYPE_WIRELESS: + path.clear(); + path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH, name); + if (access(path.string(), R_OK) == 0) + mChargerNames.add(String8(name)); + break; + + case ANDROID_POWER_SUPPLY_TYPE_BATTERY: + path.clear(); + path.appendFormat("%s/%s/status", POWER_SUPPLY_SYSFS_PATH, name); + if (access(path, R_OK) == 0) + mBatteryStatusPath = path; + path.clear(); + path.appendFormat("%s/%s/health", POWER_SUPPLY_SYSFS_PATH, name); + if (access(path, R_OK) == 0) + mBatteryHealthPath = path; + path.clear(); + path.appendFormat("%s/%s/present", POWER_SUPPLY_SYSFS_PATH, name); + if (access(path, R_OK) == 0) + mBatteryPresentPath = path; + path.clear(); + path.appendFormat("%s/%s/capacity", POWER_SUPPLY_SYSFS_PATH, name); + if (access(path, R_OK) == 0) + mBatteryCapacityPath = path; + + path.clear(); + path.appendFormat("%s/%s/voltage_now", POWER_SUPPLY_SYSFS_PATH, name); + if (access(path, R_OK) == 0) { + mBatteryVoltagePath = path; + } else { + path.clear(); + path.appendFormat("%s/%s/batt_vol", POWER_SUPPLY_SYSFS_PATH, name); + if (access(path, R_OK) == 0) + mBatteryVoltagePath = path; + } + + path.clear(); + path.appendFormat("%s/%s/temp", POWER_SUPPLY_SYSFS_PATH, name); + if (access(path, R_OK) == 0) { + mBatteryTemperaturePath = path; + } else { + path.clear(); + path.appendFormat("%s/%s/batt_temp", POWER_SUPPLY_SYSFS_PATH, name); + if (access(path, R_OK) == 0) + mBatteryTemperaturePath = path; + } + + path.clear(); + path.appendFormat("%s/%s/technology", POWER_SUPPLY_SYSFS_PATH, name); + if (access(path, R_OK) == 0) + mBatteryTechnologyPath = path; + break; + + case ANDROID_POWER_SUPPLY_TYPE_UNKNOWN: + break; + } + } + closedir(dir); + } + + if (!mChargerNames.size()) + KLOG_ERROR(LOG_TAG, "No charger supplies found\n"); + if (mBatteryStatusPath.isEmpty()) + KLOG_WARNING(LOG_TAG, "BatteryStatusPath not found\n"); + if (mBatteryHealthPath.isEmpty()) + KLOG_WARNING("BatteryHealthPath not found\n"); + if (mBatteryPresentPath.isEmpty()) + KLOG_WARNING(LOG_TAG, "BatteryPresentPath not found\n"); + if (mBatteryCapacityPath.isEmpty()) + KLOG_WARNING(LOG_TAG, "BatteryCapacityPath not found\n"); + if (mBatteryVoltagePath.isEmpty()) + KLOG_WARNING(LOG_TAG, "BatteryVoltagePath not found\n"); + if (mBatteryTemperaturePath.isEmpty()) + KLOG_WARNING(LOG_TAG, "BatteryTemperaturePath not found\n"); + if (mBatteryTechnologyPath.isEmpty()) + KLOG_WARNING(LOG_TAG, "BatteryTechnologyPath not found\n"); + + if (nosvcmgr == false) { + mBatteryPropertiesRegistrar = new BatteryPropertiesRegistrar(this); + mBatteryPropertiesRegistrar->publish(); + } +} + +}; // namespace android diff --git a/healthd/BatteryMonitor.h b/healthd/BatteryMonitor.h new file mode 100644 index 000000000..df0920b88 --- /dev/null +++ b/healthd/BatteryMonitor.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2013 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 HEALTHD_BATTERYMONITOR_H +#define HEALTHD_BATTERYMONITOR_H + +#include +#include +#include + +#include "BatteryPropertiesRegistrar.h" + +namespace android { + +class BatteryPropertiesRegistrar; + +class BatteryMonitor { + public: + + enum PowerSupplyType { + ANDROID_POWER_SUPPLY_TYPE_UNKNOWN = 0, + ANDROID_POWER_SUPPLY_TYPE_AC, + ANDROID_POWER_SUPPLY_TYPE_USB, + ANDROID_POWER_SUPPLY_TYPE_WIRELESS, + ANDROID_POWER_SUPPLY_TYPE_BATTERY + }; + + void init(bool nosvcmgr); + bool update(void); + + private: + String8 mBatteryStatusPath; + String8 mBatteryHealthPath; + String8 mBatteryPresentPath; + String8 mBatteryCapacityPath; + String8 mBatteryVoltagePath; + String8 mBatteryTemperaturePath; + String8 mBatteryTechnologyPath; + + Vector mChargerNames; + + sp mBatteryPropertiesRegistrar; + + int getBatteryStatus(const char* status); + int getBatteryHealth(const char* status); + int readFromFile(const String8& path, char* buf, size_t size); + PowerSupplyType readPowerSupplyType(const String8& path); + bool getBooleanField(const String8& path); + int getIntField(const String8& path); +}; + +}; // namespace android + +#endif // HEALTHD_BATTERY_MONTIOR_H diff --git a/healthd/BatteryPropertiesRegistrar.cpp b/healthd/BatteryPropertiesRegistrar.cpp new file mode 100644 index 000000000..6a33ad8e0 --- /dev/null +++ b/healthd/BatteryPropertiesRegistrar.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2013 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 "BatteryPropertiesRegistrar.h" +#include +#include +#include +#include +#include +#include +#include + +namespace android { + +BatteryPropertiesRegistrar::BatteryPropertiesRegistrar(BatteryMonitor* monitor) { + mBatteryMonitor = monitor; +} + +void BatteryPropertiesRegistrar::publish() { + defaultServiceManager()->addService(String16("batterypropreg"), this); +} + +void BatteryPropertiesRegistrar::notifyListeners(struct BatteryProperties props) { + Mutex::Autolock _l(mRegistrationLock); + for (size_t i = 0; i < mListeners.size(); i++) { + mListeners[i]->batteryPropertiesChanged(props); + } +} + +void BatteryPropertiesRegistrar::registerListener(const sp& listener) { + { + Mutex::Autolock _l(mRegistrationLock); + // check whether this is a duplicate + for (size_t i = 0; i < mListeners.size(); i++) { + if (mListeners[i]->asBinder() == listener->asBinder()) { + return; + } + } + + mListeners.add(listener); + listener->asBinder()->linkToDeath(this); + } + mBatteryMonitor->update(); +} + +void BatteryPropertiesRegistrar::unregisterListener(const sp& listener) { + Mutex::Autolock _l(mRegistrationLock); + for (size_t i = 0; i < mListeners.size(); i++) { + if (mListeners[i]->asBinder() == listener->asBinder()) { + mListeners[i]->asBinder()->unlinkToDeath(this); + mListeners.removeAt(i); + break; + } + } +} + +void BatteryPropertiesRegistrar::binderDied(const wp& who) { + Mutex::Autolock _l(mRegistrationLock); + + for (size_t i = 0; i < mListeners.size(); i++) { + if (mListeners[i]->asBinder() == who) { + mListeners.removeAt(i); + break; + } + } +} + +} // namespace android diff --git a/healthd/BatteryPropertiesRegistrar.h b/healthd/BatteryPropertiesRegistrar.h new file mode 100644 index 000000000..793ddad82 --- /dev/null +++ b/healthd/BatteryPropertiesRegistrar.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2013 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 HEALTHD_BATTERYPROPERTIES_REGISTRAR_H +#define HEALTHD_BATTERYPROPERTIES_REGISTRAR_H + +#include "BatteryMonitor.h" + +#include +#include +#include +#include +#include +#include + +namespace android { + +class BatteryMonitor; + +class BatteryPropertiesRegistrar : public BnBatteryPropertiesRegistrar, + public IBinder::DeathRecipient { +public: + BatteryPropertiesRegistrar(BatteryMonitor* monitor); + void publish(); + void notifyListeners(struct BatteryProperties props); + +private: + BatteryMonitor* mBatteryMonitor; + Mutex mRegistrationLock; + Vector > mListeners; + + void registerListener(const sp& listener); + void unregisterListener(const sp& listener); + void binderDied(const wp& who); +}; + +}; // namespace android + +#endif // HEALTHD_BATTERYPROPERTIES_REGISTRAR_H diff --git a/healthd/healthd.cpp b/healthd/healthd.cpp new file mode 100644 index 000000000..1d79097a7 --- /dev/null +++ b/healthd/healthd.cpp @@ -0,0 +1,258 @@ +/* + * Copyright (C) 2013 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 "healthd" +#define KLOG_LEVEL 6 + +#include "BatteryMonitor.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace android; + +// Periodic chores intervals in seconds +#define PERIODIC_CHORES_INTERVAL_FAST (60 * 1) +#define PERIODIC_CHORES_INTERVAL_SLOW (60 * 10) + +#define POWER_SUPPLY_SUBSYSTEM "power_supply" + +// epoll events: uevent, wakealarm, binder +#define MAX_EPOLL_EVENTS 3 +static int uevent_fd; +static int wakealarm_fd; +static int binder_fd; + +// -1 for no epoll timeout +static int awake_poll_interval = -1; + +static int wakealarm_wake_interval = PERIODIC_CHORES_INTERVAL_FAST; + +static BatteryMonitor* gBatteryMonitor; + +static bool nosvcmgr; + +static void wakealarm_set_interval(int interval) { + struct itimerspec itval; + + if (wakealarm_fd == -1) + return; + + wakealarm_wake_interval = interval; + itval.it_interval.tv_sec = interval; + itval.it_interval.tv_nsec = 0; + itval.it_value.tv_sec = interval; + itval.it_value.tv_nsec = 0; + + if (timerfd_settime(wakealarm_fd, 0, &itval, NULL) == -1) + KLOG_ERROR(LOG_TAG, "wakealarm_set_interval: timerfd_settime failed\n"); +} + +static void battery_update(void) { + // Fast wake interval when on charger (watch for overheat); + // slow wake interval when on battery (watch for drained battery). + + int new_wake_interval = gBatteryMonitor->update() ? + PERIODIC_CHORES_INTERVAL_FAST : PERIODIC_CHORES_INTERVAL_SLOW; + + if (new_wake_interval != wakealarm_wake_interval) + wakealarm_set_interval(new_wake_interval); + + // During awake periods poll at fast rate. If wake alarm is set at fast + // rate then just use the alarm; if wake alarm is set at slow rate then + // poll at fast rate while awake and let alarm wake up at slow rate when + // asleep. + + awake_poll_interval = + new_wake_interval == PERIODIC_CHORES_INTERVAL_FAST ? + -1 : PERIODIC_CHORES_INTERVAL_FAST * 1000; +} + +static void periodic_chores() { + battery_update(); +} + +static void uevent_init(void) { + uevent_fd = uevent_open_socket(64*1024, true); + + if (uevent_fd >= 0) + fcntl(uevent_fd, F_SETFL, O_NONBLOCK); + else + KLOG_ERROR(LOG_TAG, "uevent_init: uevent_open_socket failed\n"); +} + +#define UEVENT_MSG_LEN 1024 +static void uevent_event(void) { + char msg[UEVENT_MSG_LEN+2]; + char *cp; + int n; + + n = uevent_kernel_multicast_recv(uevent_fd, msg, UEVENT_MSG_LEN); + if (n <= 0) + return; + if (n >= UEVENT_MSG_LEN) /* overflow -- discard */ + return; + + msg[n] = '\0'; + msg[n+1] = '\0'; + cp = msg; + + while (*cp) { + if (!strcmp(cp, "SUBSYSTEM=" POWER_SUPPLY_SUBSYSTEM)) { + battery_update(); + break; + } + + /* advance to after the next \0 */ + while (*cp++) + ; + } +} + +static void wakealarm_init(void) { + wakealarm_fd = timerfd_create(CLOCK_BOOTTIME_ALARM, TFD_NONBLOCK); + if (wakealarm_fd == -1) { + KLOG_ERROR(LOG_TAG, "wakealarm_init: timerfd_create failed\n"); + return; + } + + wakealarm_set_interval(PERIODIC_CHORES_INTERVAL_FAST); +} + +static void wakealarm_event(void) { + unsigned long long wakeups; + + if (read(wakealarm_fd, &wakeups, sizeof(wakeups)) == -1) { + KLOG_ERROR(LOG_TAG, "wakealarm_event: read wakealarm_fd failed\n"); + return; + } + + periodic_chores(); +} + +static void binder_init(void) { + ProcessState::self()->setThreadPoolMaxThreadCount(0); + IPCThreadState::self()->disableBackgroundScheduling(true); + IPCThreadState::self()->setupPolling(&binder_fd); +} + +static void binder_event(void) { + IPCThreadState::self()->handlePolledCommands(); +} + +static void healthd_mainloop(void) { + struct epoll_event ev; + int epollfd; + int maxevents = 0; + + epollfd = epoll_create(MAX_EPOLL_EVENTS); + if (epollfd == -1) { + KLOG_ERROR(LOG_TAG, + "healthd_mainloop: epoll_create failed; errno=%d\n", + errno); + return; + } + + if (uevent_fd >= 0) { + ev.events = EPOLLIN | EPOLLWAKEUP; + ev.data.ptr = (void *)uevent_event; + if (epoll_ctl(epollfd, EPOLL_CTL_ADD, uevent_fd, &ev) == -1) + KLOG_ERROR(LOG_TAG, + "healthd_mainloop: epoll_ctl for uevent_fd failed; errno=%d\n", + errno); + else + maxevents++; + } + + if (wakealarm_fd >= 0) { + ev.events = EPOLLIN | EPOLLWAKEUP; + ev.data.ptr = (void *)wakealarm_event; + if (epoll_ctl(epollfd, EPOLL_CTL_ADD, wakealarm_fd, &ev) == -1) + KLOG_ERROR(LOG_TAG, + "healthd_mainloop: epoll_ctl for wakealarm_fd failed; errno=%d\n", + errno); + else + maxevents++; + } + + if (binder_fd >= 0) { + ev.events = EPOLLIN | EPOLLWAKEUP; + ev.data.ptr= (void *)binder_event; + if (epoll_ctl(epollfd, EPOLL_CTL_ADD, binder_fd, &ev) == -1) + KLOG_ERROR(LOG_TAG, + "healthd_mainloop: epoll_ctl for binder_fd failed; errno=%d\n", + errno); + else + maxevents++; + } + + while (1) { + struct epoll_event events[maxevents]; + int nevents; + + IPCThreadState::self()->flushCommands(); + nevents = epoll_wait(epollfd, events, maxevents, awake_poll_interval); + + if (nevents == -1) { + if (errno == EINTR) + continue; + KLOG_ERROR(LOG_TAG, "healthd_mainloop: epoll_wait failed\n"); + break; + } + + for (int n = 0; n < nevents; ++n) { + if (events[n].data.ptr) + (*(void (*)())events[n].data.ptr)(); + } + } + + return; +} + +int main(int argc, char **argv) { + int ch; + + klog_set_level(KLOG_LEVEL); + + while ((ch = getopt(argc, argv, "n")) != -1) { + switch (ch) { + case 'n': + nosvcmgr = true; + break; + case '?': + default: + KLOG_WARNING(LOG_TAG, "Unrecognized healthd option: %c\n", ch); + } + } + + wakealarm_init(); + uevent_init(); + binder_init(); + gBatteryMonitor = new BatteryMonitor(); + gBatteryMonitor->init(nosvcmgr); + + healthd_mainloop(); + return 0; +} From 6bac41f1bf9b651523a2f9f119c2adb0836eb6b7 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Fri, 26 Jul 2013 14:50:26 -0700 Subject: [PATCH 066/541] get rid of HAL pixelformats 5551 and 4444 Change-Id: I047d948f2f3b5c681a0b187589775f92b4f01541 --- include/system/graphics.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/system/graphics.h b/include/system/graphics.h index 2ddcc08ce..5d883be26 100644 --- a/include/system/graphics.h +++ b/include/system/graphics.h @@ -54,8 +54,6 @@ enum { HAL_PIXEL_FORMAT_RGB_888 = 3, HAL_PIXEL_FORMAT_RGB_565 = 4, HAL_PIXEL_FORMAT_BGRA_8888 = 5, - HAL_PIXEL_FORMAT_RGBA_5551 = 6, - HAL_PIXEL_FORMAT_RGBA_4444 = 7, /* * sRGB color pixel formats: From 13f4c9fb279f1b34a3b47a6eac80b8a09d2d3a79 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Wed, 19 Jun 2013 15:09:35 -0700 Subject: [PATCH 067/541] healthd: normal and charger mode startup Command line flag -n tells healthd not to use (or wait for) servicemanager in charger mode. Change-Id: I255f9597dff6fc904f5ed20fd02f52c0531d14f8 --- rootdir/init.rc | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/rootdir/init.rc b/rootdir/init.rc index d93ad28c4..729879a75 100644 --- a/rootdir/init.rc +++ b/rootdir/init.rc @@ -402,6 +402,16 @@ service ueventd /sbin/ueventd critical seclabel u:r:ueventd:s0 +service healthd /sbin/healthd + class core + critical + seclabel u:r:healthd:s0 + +service healthd-charger /sbin/healthd -n + class charger + critical + seclabel u:r:healthd:s0 + on property:selinux.reload_policy=1 restart ueventd restart installd @@ -432,6 +442,7 @@ service servicemanager /system/bin/servicemanager user system group system critical + onrestart restart healthd onrestart restart zygote onrestart restart media onrestart restart surfaceflinger From d245d1d09731d6a19e8a25559d3907f54441ac3d Mon Sep 17 00:00:00 2001 From: The Android Open Source Project Date: Tue, 21 Oct 2008 07:00:00 -0700 Subject: [PATCH 068/541] Initial Contribution --- MODULE_LICENSE_APACHE2 | 0 NOTICE | 222 ++ include/utils.h | 33 + include/utils/AndroidUnicode.h | 255 ++ include/utils/Asset.h | 311 ++ include/utils/AssetDir.h | 145 + include/utils/AssetManager.h | 323 ++ include/utils/Atomic.h | 22 + include/utils/Binder.h | 103 + include/utils/BpBinder.h | 122 + include/utils/Buffer.h | 107 + include/utils/BufferedTextOutput.h | 67 + include/utils/ByteOrder.h | 69 + include/utils/CallStack.h | 76 + include/utils/Debug.h | 45 + include/utils/Endian.h | 40 + include/utils/Errors.h | 87 + include/utils/FileMap.h | 134 + include/utils/IBinder.h | 159 + include/utils/IInterface.h | 135 + include/utils/IMemory.h | 94 + include/utils/IPCThreadState.h | 107 + include/utils/IPermissionController.h | 56 + include/utils/IServiceManager.h | 98 + include/utils/KeyedVector.h | 201 ++ include/utils/List.h | 280 ++ include/utils/Log.h | 33 + include/utils/LogSocket.h | 20 + include/utils/MemoryBase.h | 51 + include/utils/MemoryDealer.h | 238 ++ include/utils/MemoryHeapBase.h | 95 + include/utils/MemoryHeapPmem.h | 65 + include/utils/Parcel.h | 196 ++ include/utils/Pipe.h | 108 + include/utils/ProcessState.h | 117 + include/utils/RefBase.h | 526 ++++ include/utils/ResourceTypes.h | 1685 +++++++++++ include/utils/SharedBuffer.h | 146 + include/utils/Socket.h | 80 + include/utils/SortedVector.h | 282 ++ include/utils/StopWatch.h | 62 + include/utils/String16.h | 260 ++ include/utils/String8.h | 353 +++ include/utils/SystemClock.h | 32 + include/utils/TextOutput.h | 190 ++ include/utils/TimeUtils.h | 88 + include/utils/TimerProbe.h | 72 + include/utils/Timers.h | 137 + include/utils/TypeHelpers.h | 254 ++ include/utils/Vector.h | 359 +++ include/utils/VectorImpl.h | 199 ++ include/utils/ZipEntry.h | 345 +++ include/utils/ZipFile.h | 269 ++ include/utils/ZipFileCRO.h | 59 + include/utils/ZipFileRO.h | 222 ++ include/utils/ZipUtils.h | 67 + include/utils/ashmem.h | 41 + include/utils/executablepath.h | 28 + include/utils/inet_address.h | 103 + include/utils/logger.h | 46 + include/utils/misc.h | 93 + include/utils/ported.h | 50 + include/utils/string_array.h | 122 + include/utils/threads.h | 347 +++ libs/utils/Android.mk | 148 + libs/utils/Asset.cpp | 813 +++++ libs/utils/AssetDir.cpp | 66 + libs/utils/AssetManager.cpp | 1637 ++++++++++ libs/utils/Binder.cpp | 242 ++ libs/utils/BpBinder.cpp | 348 +++ libs/utils/BufferedTextOutput.cpp | 279 ++ libs/utils/CallStack.cpp | 335 +++ libs/utils/Debug.cpp | 318 ++ libs/utils/FileMap.cpp | 222 ++ libs/utils/IDataConnection.cpp | 89 + libs/utils/IInterface.cpp | 35 + libs/utils/IMemory.cpp | 486 +++ libs/utils/IPCThreadState.cpp | 1007 +++++++ libs/utils/IPermissionController.cpp | 86 + libs/utils/IServiceManager.cpp | 230 ++ libs/utils/InetAddress.cpp | 236 ++ libs/utils/LogSocket.cpp | 129 + libs/utils/MODULE_LICENSE_APACHE2 | 0 libs/utils/MemoryBase.cpp | 46 + libs/utils/MemoryDealer.cpp | 407 +++ libs/utils/MemoryHeapBase.cpp | 178 ++ libs/utils/MemoryHeapPmem.cpp | 226 ++ libs/utils/NOTICE | 190 ++ libs/utils/Parcel.cpp | 1311 ++++++++ libs/utils/Pipe.cpp | 465 +++ libs/utils/ProcessState.cpp | 398 +++ libs/utils/README | 14 + libs/utils/RefBase.cpp | 534 ++++ libs/utils/ResourceTypes.cpp | 3969 +++++++++++++++++++++++++ libs/utils/SharedBuffer.cpp | 113 + libs/utils/Socket.cpp | 388 +++ libs/utils/Static.cpp | 120 + libs/utils/StopWatch.cpp | 79 + libs/utils/String16.cpp | 609 ++++ libs/utils/String8.cpp | 602 ++++ libs/utils/SystemClock.cpp | 139 + libs/utils/TextOutput.cpp | 146 + libs/utils/Threads.cpp | 1126 +++++++ libs/utils/TimerProbe.cpp | 131 + libs/utils/Timers.cpp | 240 ++ libs/utils/Unicode.cpp | 193 ++ libs/utils/VectorImpl.cpp | 611 ++++ libs/utils/ZipEntry.cpp | 696 +++++ libs/utils/ZipFile.cpp | 1296 ++++++++ libs/utils/ZipFileCRO.cpp | 54 + libs/utils/ZipFileRO.cpp | 724 +++++ libs/utils/ZipUtils.cpp | 344 +++ libs/utils/characterData.h | 730 +++++ libs/utils/executablepath_darwin.cpp | 31 + libs/utils/executablepath_linux.cpp | 30 + libs/utils/futex_synchro.c | 175 ++ libs/utils/misc.cpp | 185 ++ libs/utils/ported.cpp | 106 + 118 files changed, 34043 insertions(+) create mode 100644 MODULE_LICENSE_APACHE2 create mode 100644 NOTICE create mode 100644 include/utils.h create mode 100644 include/utils/AndroidUnicode.h create mode 100644 include/utils/Asset.h create mode 100644 include/utils/AssetDir.h create mode 100644 include/utils/AssetManager.h create mode 100644 include/utils/Atomic.h create mode 100644 include/utils/Binder.h create mode 100644 include/utils/BpBinder.h create mode 100644 include/utils/Buffer.h create mode 100644 include/utils/BufferedTextOutput.h create mode 100644 include/utils/ByteOrder.h create mode 100644 include/utils/CallStack.h create mode 100644 include/utils/Debug.h create mode 100644 include/utils/Endian.h create mode 100644 include/utils/Errors.h create mode 100644 include/utils/FileMap.h create mode 100644 include/utils/IBinder.h create mode 100644 include/utils/IInterface.h create mode 100644 include/utils/IMemory.h create mode 100644 include/utils/IPCThreadState.h create mode 100644 include/utils/IPermissionController.h create mode 100644 include/utils/IServiceManager.h create mode 100644 include/utils/KeyedVector.h create mode 100644 include/utils/List.h create mode 100644 include/utils/Log.h create mode 100644 include/utils/LogSocket.h create mode 100644 include/utils/MemoryBase.h create mode 100644 include/utils/MemoryDealer.h create mode 100644 include/utils/MemoryHeapBase.h create mode 100644 include/utils/MemoryHeapPmem.h create mode 100644 include/utils/Parcel.h create mode 100644 include/utils/Pipe.h create mode 100644 include/utils/ProcessState.h create mode 100644 include/utils/RefBase.h create mode 100644 include/utils/ResourceTypes.h create mode 100644 include/utils/SharedBuffer.h create mode 100644 include/utils/Socket.h create mode 100644 include/utils/SortedVector.h create mode 100644 include/utils/StopWatch.h create mode 100644 include/utils/String16.h create mode 100644 include/utils/String8.h create mode 100644 include/utils/SystemClock.h create mode 100644 include/utils/TextOutput.h create mode 100644 include/utils/TimeUtils.h create mode 100644 include/utils/TimerProbe.h create mode 100644 include/utils/Timers.h create mode 100644 include/utils/TypeHelpers.h create mode 100644 include/utils/Vector.h create mode 100644 include/utils/VectorImpl.h create mode 100644 include/utils/ZipEntry.h create mode 100644 include/utils/ZipFile.h create mode 100644 include/utils/ZipFileCRO.h create mode 100644 include/utils/ZipFileRO.h create mode 100644 include/utils/ZipUtils.h create mode 100644 include/utils/ashmem.h create mode 100644 include/utils/executablepath.h create mode 100644 include/utils/inet_address.h create mode 100644 include/utils/logger.h create mode 100644 include/utils/misc.h create mode 100644 include/utils/ported.h create mode 100644 include/utils/string_array.h create mode 100644 include/utils/threads.h create mode 100644 libs/utils/Android.mk create mode 100644 libs/utils/Asset.cpp create mode 100644 libs/utils/AssetDir.cpp create mode 100644 libs/utils/AssetManager.cpp create mode 100644 libs/utils/Binder.cpp create mode 100644 libs/utils/BpBinder.cpp create mode 100644 libs/utils/BufferedTextOutput.cpp create mode 100644 libs/utils/CallStack.cpp create mode 100644 libs/utils/Debug.cpp create mode 100644 libs/utils/FileMap.cpp create mode 100644 libs/utils/IDataConnection.cpp create mode 100644 libs/utils/IInterface.cpp create mode 100644 libs/utils/IMemory.cpp create mode 100644 libs/utils/IPCThreadState.cpp create mode 100644 libs/utils/IPermissionController.cpp create mode 100644 libs/utils/IServiceManager.cpp create mode 100644 libs/utils/InetAddress.cpp create mode 100644 libs/utils/LogSocket.cpp create mode 100644 libs/utils/MODULE_LICENSE_APACHE2 create mode 100644 libs/utils/MemoryBase.cpp create mode 100644 libs/utils/MemoryDealer.cpp create mode 100644 libs/utils/MemoryHeapBase.cpp create mode 100644 libs/utils/MemoryHeapPmem.cpp create mode 100644 libs/utils/NOTICE create mode 100644 libs/utils/Parcel.cpp create mode 100644 libs/utils/Pipe.cpp create mode 100644 libs/utils/ProcessState.cpp create mode 100644 libs/utils/README create mode 100644 libs/utils/RefBase.cpp create mode 100644 libs/utils/ResourceTypes.cpp create mode 100644 libs/utils/SharedBuffer.cpp create mode 100644 libs/utils/Socket.cpp create mode 100644 libs/utils/Static.cpp create mode 100644 libs/utils/StopWatch.cpp create mode 100644 libs/utils/String16.cpp create mode 100644 libs/utils/String8.cpp create mode 100644 libs/utils/SystemClock.cpp create mode 100644 libs/utils/TextOutput.cpp create mode 100644 libs/utils/Threads.cpp create mode 100644 libs/utils/TimerProbe.cpp create mode 100644 libs/utils/Timers.cpp create mode 100644 libs/utils/Unicode.cpp create mode 100644 libs/utils/VectorImpl.cpp create mode 100644 libs/utils/ZipEntry.cpp create mode 100644 libs/utils/ZipFile.cpp create mode 100644 libs/utils/ZipFileCRO.cpp create mode 100644 libs/utils/ZipFileRO.cpp create mode 100644 libs/utils/ZipUtils.cpp create mode 100644 libs/utils/characterData.h create mode 100644 libs/utils/executablepath_darwin.cpp create mode 100644 libs/utils/executablepath_linux.cpp create mode 100644 libs/utils/futex_synchro.c create mode 100644 libs/utils/misc.cpp create mode 100644 libs/utils/ported.cpp diff --git a/MODULE_LICENSE_APACHE2 b/MODULE_LICENSE_APACHE2 new file mode 100644 index 000000000..e69de29bb diff --git a/NOTICE b/NOTICE new file mode 100644 index 000000000..267a6aafd --- /dev/null +++ b/NOTICE @@ -0,0 +1,222 @@ + ========================================================================= + == NOTICE file corresponding to the section 4 d of == + == the Apache License, Version 2.0, == + == in this case for the Android-specific code. == + ========================================================================= + +Android Code +Copyright 2005-2008 The Android Open Source Project + +This product includes software developed as part of +The Android Open Source Project (http://source.android.com). + + ========================================================================= + == NOTICE file corresponding to the section 4 d of == + == the Apache License, Version 2.0, == + == in this case for Apache Commons code. == + ========================================================================= + +Apache Commons +Copyright 1999-2004 The Apache Software Foundation + +This product includes software developed at +The Apache Software Foundation (http://www.apache.org/). + + ========================================================================= + == NOTICE file corresponding to the section 4 d of == + == the Apache License, Version 2.0, == + == in this case for Jakarta Commons Logging. == + ========================================================================= + +Jakarta Commons Logging (JCL) +Copyright 2005,2006 The Apache Software Foundation. + +This product includes software developed at +The Apache Software Foundation (http://www.apache.org/). + + ========================================================================= + == NOTICE file corresponding to the section 4 d of == + == the Apache License, Version 2.0, == + == in this case for the Nuance code. == + ========================================================================= + +These files are Copyright 2007 Nuance Communications, but released under +the Apache2 License. + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + diff --git a/include/utils.h b/include/utils.h new file mode 100644 index 000000000..30648b185 --- /dev/null +++ b/include/utils.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2005 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. + */ + +// +// Handy utility functions and portability code. This file includes all +// of the generally-useful headers in the "utils" directory. +// +#ifndef _LIBS_UTILS_H +#define _LIBS_UTILS_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#endif // _LIBS_UTILS_H diff --git a/include/utils/AndroidUnicode.h b/include/utils/AndroidUnicode.h new file mode 100644 index 000000000..563fcd019 --- /dev/null +++ b/include/utils/AndroidUnicode.h @@ -0,0 +1,255 @@ +/* + * Copyright (C) 2006 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_UNICODE_H +#define ANDROID_UNICODE_H + +#include +#include + +#define REPLACEMENT_CHAR (0xFFFD) + +// this part of code is copied from umachine.h under ICU +/** + * Define UChar32 as a type for single Unicode code points. + * UChar32 is a signed 32-bit integer (same as int32_t). + * + * The Unicode code point range is 0..0x10ffff. + * All other values (negative or >=0x110000) are illegal as Unicode code points. + * They may be used as sentinel values to indicate "done", "error" + * or similar non-code point conditions. + * + * @stable ICU 2.4 + */ +typedef int32_t UChar32; + +namespace android { + + class Encoding; + /** + * \class Unicode + * + * Helper class for getting properties of Unicode characters. Characters + * can have one of the types listed in CharType and each character can have the + * directionality of Direction. + */ + class Unicode + { + public: + /** + * Directions specified in the Unicode standard. These directions map directly + * to java.lang.Character. + */ + enum Direction { + DIRECTIONALITY_UNDEFINED = -1, + DIRECTIONALITY_LEFT_TO_RIGHT, + DIRECTIONALITY_RIGHT_TO_LEFT, + DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC, + DIRECTIONALITY_EUROPEAN_NUMBER, + DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR, + DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR, + DIRECTIONALITY_ARABIC_NUMBER, + DIRECTIONALITY_COMMON_NUMBER_SEPARATOR, + DIRECTIONALITY_NONSPACING_MARK, + DIRECTIONALITY_BOUNDARY_NEUTRAL, + DIRECTIONALITY_PARAGRAPH_SEPARATOR, + DIRECTIONALITY_SEGMENT_SEPARATOR, + DIRECTIONALITY_WHITESPACE, + DIRECTIONALITY_OTHER_NEUTRALS, + DIRECTIONALITY_LEFT_TO_RIGHT_EMBEDDING, + DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE, + DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING, + DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE, + DIRECTIONALITY_POP_DIRECTIONAL_FORMAT + }; + + /** + * Character types as specified in the Unicode standard. These map directly to + * java.lang.Character. + */ + enum CharType { + CHARTYPE_UNASSIGNED = 0, + CHARTYPE_UPPERCASE_LETTER, + CHARTYPE_LOWERCASE_LETTER, + CHARTYPE_TITLECASE_LETTER, + CHARTYPE_MODIFIER_LETTER, + CHARTYPE_OTHER_LETTER, + CHARTYPE_NON_SPACING_MARK, + CHARTYPE_ENCLOSING_MARK, + CHARTYPE_COMBINING_SPACING_MARK, + CHARTYPE_DECIMAL_DIGIT_NUMBER, + CHARTYPE_LETTER_NUMBER, + CHARTYPE_OTHER_NUMBER, + CHARTYPE_SPACE_SEPARATOR, + CHARTYPE_LINE_SEPARATOR, + CHARTYPE_PARAGRAPH_SEPARATOR, + CHARTYPE_CONTROL, + CHARTYPE_FORMAT, + CHARTYPE_MISSING_VALUE_FOR_JAVA, /* This is the mysterious missing 17 value from the java constants */ + CHARTYPE_PRIVATE_USE, + CHARTYPE_SURROGATE, + CHARTYPE_DASH_PUNCTUATION, + CHARTYPE_START_PUNCTUATION, + CHARTYPE_END_PUNCTUATION, + CHARTYPE_CONNECTOR_PUNCTUATION, + CHARTYPE_OTHER_PUNCTUATION, + CHARTYPE_MATH_SYMBOL, + CHARTYPE_CURRENCY_SYMBOL, + CHARTYPE_MODIFIER_SYMBOL, + CHARTYPE_OTHER_SYMBOL, + CHARTYPE_INITIAL_QUOTE_PUNCTUATION, + CHARTYPE_FINAL_QUOTE_PUNCTUATION + }; + + /** + * Decomposition types as described by the unicode standard. These values map to + * the same values in uchar.h in ICU. + */ + enum DecompositionType { + DECOMPOSITION_NONE = 0, + DECOMPOSITION_CANONICAL, + DECOMPOSITION_COMPAT, + DECOMPOSITION_CIRCLE, + DECOMPOSITION_FINAL, + DECOMPOSITION_FONT, + DECOMPOSITION_FRACTION, + DECOMPOSITION_INITIAL, + DECOMPOSITION_ISOLATED, + DECOMPOSITION_MEDIAL, + DECOMPOSITION_NARROW, + DECOMPOSITION_NOBREAK, + DECOMPOSITION_SMALL, + DECOMPOSITION_SQUARE, + DECOMPOSITION_SUB, + DECOMPOSITION_SUPER, + DECOMPOSITION_VERTICAL, + DECOMPOSITION_WIDE + }; + + /** + * Returns the packed data for java calls + * @param c The unicode character. + * @return The packed data for the character. + * + * Copied from java.lang.Character implementation: + * 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 + * F E D C B A 9 8 7 6 5 4 3 2 1 0 F E D C B A 9 8 7 6 5 4 3 2 1 0 + * + * 31 types --------- + * 18 directionalities --------- + * 2 mirroreds - + * ----------- 56 toupper diffs + * ----------- 48 tolower diffs + * --- 4 totitlecase diffs + * ------------- 84 numeric values + * --------- 24 mirror char diffs + */ + static uint32_t getPackedData(UChar32 c); + + /** + * Get the Character type. + * @param c The unicode character. + * @return The character's type or CHARTYPE_UNASSIGNED if the character is invalid + * or has an unassigned class. + */ + static CharType getType(UChar32 c); + + /** + * Get the Character's decomposition type. + * @param c The unicode character. + * @return The character's decomposition type or DECOMPOSITION_NONE is there + * is no decomposition. + */ + static DecompositionType getDecompositionType(UChar32 c); + + /** + * Returns the digit value of a character or -1 if the character + * is not within the specified radix. + * + * The digit value is computed for integer characters and letters + * within the given radix. This function does not handle Roman Numerals, + * fractions, or any other characters that may represent numbers. + * + * @param c The unicode character + * @param radix The intended radix. + * @return The digit value or -1 if there is no digit value or if the value is outside the radix. + */ + static int getDigitValue(UChar32 c, int radix = 10); + + /** + * Return the numeric value of a character + * + * @param c The unicode character. + * @return The numeric value of the character. -1 if the character has no numeric value, + * -2 if the character has a numeric value that is not representable by an integer. + */ + static int getNumericValue(UChar32 c); + + /** + * Convert the character to lowercase + * @param c The unicode character. + * @return The lowercase character equivalent of c. If c does not have a lowercase equivalent, + * the original character is returned. + */ + static UChar32 toLower(UChar32 c); + + /** + * Convert the character to uppercase + * @param c The unicode character. + * @return The uppercase character equivalent of c. If c does not have an uppercase equivalent, + * the original character is returned. + */ + static UChar32 toUpper(UChar32 c); + + /** + * Get the directionality of the character. + * @param c The unicode character. + * @return The direction of the character or DIRECTIONALITY_UNDEFINED. + */ + static Direction getDirectionality(UChar32 c); + + /** + * Check if the character is a mirrored character. This means that the character + * has an equivalent character that is the mirror image of itself. + * @param c The unicode character. + * @return True iff c has a mirror equivalent. + */ + static bool isMirrored(UChar32 c); + + /** + * Return the mirror of the given character. + * @param c The unicode character. + * @return The mirror equivalent of c. If c does not have a mirror equivalent, + * the original character is returned. + * @see isMirrored + */ + static UChar32 toMirror(UChar32 c); + + /** + * Convert the character to title case. + * @param c The unicode character. + * @return The titlecase equivalent of c. If c does not have a titlecase equivalent, + * the original character is returned. + */ + static UChar32 toTitle(UChar32 c); + + }; + +} + +#endif diff --git a/include/utils/Asset.h b/include/utils/Asset.h new file mode 100644 index 000000000..d8351f57c --- /dev/null +++ b/include/utils/Asset.h @@ -0,0 +1,311 @@ +/* + * Copyright (C) 2006 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. + */ + +// +// Class providing access to a read-only asset. Asset objects are NOT +// thread-safe, and should not be shared across threads. +// +#ifndef __LIBS_ASSET_H +#define __LIBS_ASSET_H + +#include +#include +#include "FileMap.h" +#include "String8.h" +#include "Errors.h" + +namespace android { + +/* + * Instances of this class provide read-only operations on a byte stream. + * + * Access may be optimized for streaming, random, or whole buffer modes. All + * operations are supported regardless of how the file was opened, but some + * things will be less efficient. [pass that in??] + * + * "Asset" is the base class for all types of assets. The classes below + * provide most of the implementation. The AssetManager uses one of the + * static "create" functions defined here to create a new instance. + */ +class Asset { +public: + virtual ~Asset(void); + + static int32_t getGlobalCount(); + + /* used when opening an asset */ + typedef enum AccessMode { + ACCESS_UNKNOWN = 0, + + /* read chunks, and seek forward and backward */ + ACCESS_RANDOM, + + /* read sequentially, with an occasional forward seek */ + ACCESS_STREAMING, + + /* caller plans to ask for a read-only buffer with all data */ + ACCESS_BUFFER, + } AccessMode; + + enum { + /* data larger than this does not get uncompressed into a buffer */ + UNCOMPRESS_DATA_MAX = 1 * 1024 * 1024 + }; + + /* + * Read data from the current offset. Returns the actual number of + * bytes read, 0 on EOF, or -1 on error. + */ + virtual ssize_t read(void* buf, size_t count) = 0; + + /* + * Seek to the specified offset. "whence" uses the same values as + * lseek/fseek. Returns the new position on success, or (off_t) -1 + * on failure. + */ + virtual off_t seek(off_t offset, int whence) = 0; + + /* + * Close the asset, freeing all associated resources. + */ + virtual void close(void) = 0; + + /* + * Get a pointer to a buffer with the entire contents of the file. + */ + virtual const void* getBuffer(bool wordAligned) = 0; + + /* + * Get the total amount of data that can be read. + */ + virtual off_t getLength(void) const = 0; + + /* + * Get the total amount of data that can be read from the current position. + */ + virtual off_t getRemainingLength(void) const = 0; + + /* + * Open a new file descriptor that can be used to read this asset. + * Returns -1 if you can not use the file descriptor (for example if the + * asset is compressed). + */ + virtual int openFileDescriptor(off_t* outStart, off_t* outLength) const = 0; + + /* + * Get a string identifying the asset's source. This might be a full + * path, it might be a colon-separated list of identifiers. + * + * This is NOT intended to be used for anything except debug output. + * DO NOT try to parse this or use it to open a file. + */ + const char* getAssetSource(void) const { return mAssetSource.string(); } + +protected: + Asset(void); // constructor; only invoked indirectly + + /* handle common seek() housekeeping */ + off_t handleSeek(off_t offset, int whence, off_t curPosn, off_t maxPosn); + + /* set the asset source string */ + void setAssetSource(const String8& path) { mAssetSource = path; } + + AccessMode getAccessMode(void) const { return mAccessMode; } + +private: + /* these operations are not implemented */ + Asset(const Asset& src); + Asset& operator=(const Asset& src); + + /* AssetManager needs access to our "create" functions */ + friend class AssetManager; + + /* + * Create the asset from a named file on disk. + */ + static Asset* createFromFile(const char* fileName, AccessMode mode); + + /* + * Create the asset from a named, compressed file on disk (e.g. ".gz"). + */ + static Asset* createFromCompressedFile(const char* fileName, + AccessMode mode); + +#if 0 + /* + * Create the asset from a segment of an open file. This will fail + * if "offset" and "length" don't fit within the bounds of the file. + * + * The asset takes ownership of the file descriptor. + */ + static Asset* createFromFileSegment(int fd, off_t offset, size_t length, + AccessMode mode); + + /* + * Create from compressed data. "fd" should be seeked to the start of + * the compressed data. This could be inside a gzip file or part of a + * Zip archive. + * + * The asset takes ownership of the file descriptor. + * + * This may not verify the validity of the compressed data until first + * use. + */ + static Asset* createFromCompressedData(int fd, off_t offset, + int compressionMethod, size_t compressedLength, + size_t uncompressedLength, AccessMode mode); +#endif + + /* + * Create the asset from a memory-mapped file segment. + * + * The asset takes ownership of the FileMap. + */ + static Asset* createFromUncompressedMap(FileMap* dataMap, AccessMode mode); + + /* + * Create the asset from a memory-mapped file segment with compressed + * data. "method" is a Zip archive compression method constant. + * + * The asset takes ownership of the FileMap. + */ + static Asset* createFromCompressedMap(FileMap* dataMap, int method, + size_t uncompressedLen, AccessMode mode); + + + /* + * Create from a reference-counted chunk of shared memory. + */ + // TODO + + AccessMode mAccessMode; // how the asset was opened + String8 mAssetSource; // debug string +}; + + +/* + * =========================================================================== + * + * Innards follow. Do not use these classes directly. + */ + +/* + * An asset based on an uncompressed file on disk. It may encompass the + * entire file or just a piece of it. Access is through fread/fseek. + */ +class _FileAsset : public Asset { +public: + _FileAsset(void); + virtual ~_FileAsset(void); + + /* + * Use a piece of an already-open file. + * + * On success, the object takes ownership of "fd". + */ + status_t openChunk(const char* fileName, int fd, off_t offset, size_t length); + + /* + * Use a memory-mapped region. + * + * On success, the object takes ownership of "dataMap". + */ + status_t openChunk(FileMap* dataMap); + + /* + * Standard Asset interfaces. + */ + virtual ssize_t read(void* buf, size_t count); + virtual off_t seek(off_t offset, int whence); + virtual void close(void); + virtual const void* getBuffer(bool wordAligned); + virtual off_t getLength(void) const { return mLength; } + virtual off_t getRemainingLength(void) const { return mLength-mOffset; } + virtual int openFileDescriptor(off_t* outStart, off_t* outLength) const; + +private: + off_t mStart; // absolute file offset of start of chunk + off_t mLength; // length of the chunk + off_t mOffset; // current local offset, 0 == mStart + FILE* mFp; // for read/seek + char* mFileName; // for opening + + /* + * To support getBuffer() we either need to read the entire thing into + * a buffer or memory-map it. For small files it's probably best to + * just read them in. + */ + enum { kReadVsMapThreshold = 4096 }; + + FileMap* mMap; // for memory map + unsigned char* mBuf; // for read + + const void* ensureAlignment(FileMap* map); +}; + + +/* + * An asset based on compressed data in a file. + */ +class _CompressedAsset : public Asset { +public: + _CompressedAsset(void); + virtual ~_CompressedAsset(void); + + /* + * Use a piece of an already-open file. + * + * On success, the object takes ownership of "fd". + */ + status_t openChunk(int fd, off_t offset, int compressionMethod, + size_t uncompressedLen, size_t compressedLen); + + /* + * Use a memory-mapped region. + * + * On success, the object takes ownership of "fd". + */ + status_t openChunk(FileMap* dataMap, int compressionMethod, + size_t uncompressedLen); + + /* + * Standard Asset interfaces. + */ + virtual ssize_t read(void* buf, size_t count); + virtual off_t seek(off_t offset, int whence); + virtual void close(void); + virtual const void* getBuffer(bool wordAligned); + virtual off_t getLength(void) const { return mUncompressedLen; } + virtual off_t getRemainingLength(void) const { return mUncompressedLen-mOffset; } + virtual int openFileDescriptor(off_t* outStart, off_t* outLength) const { return -1; } + +private: + off_t mStart; // offset to start of compressed data + off_t mCompressedLen; // length of the compressed data + off_t mUncompressedLen; // length of the uncompressed data + off_t mOffset; // current offset, 0 == start of uncomp data + + FileMap* mMap; // for memory-mapped input + int mFd; // for file input + + unsigned char* mBuf; // for getBuffer() +}; + +// need: shared mmap version? + +}; // namespace android + +#endif // __LIBS_ASSET_H diff --git a/include/utils/AssetDir.h b/include/utils/AssetDir.h new file mode 100644 index 000000000..abf8a3595 --- /dev/null +++ b/include/utils/AssetDir.h @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2006 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. + */ + +// +// Access a chunk of the asset hierarchy as if it were a single directory. +// +#ifndef __LIBS_ASSETDIR_H +#define __LIBS_ASSETDIR_H + +#include +#include +#include +#include +#include + +namespace android { + +/* + * This provides vector-style access to a directory. We do this rather + * than modeling opendir/readdir access because it's simpler and the + * nature of the operation requires us to have all data on hand anyway. + * + * The list of files will be sorted in ascending order by ASCII value. + * + * The contents are populated by our friend, the AssetManager. + */ +class AssetDir { +public: + AssetDir(void) + : mFileInfo(NULL) + {} + virtual ~AssetDir(void) { + delete mFileInfo; + } + + /* + * Vector-style access. + */ + size_t getFileCount(void) { return mFileInfo->size(); } + const String8& getFileName(int idx) { + return mFileInfo->itemAt(idx).getFileName(); + } + const String8& getSourceName(int idx) { + return mFileInfo->itemAt(idx).getSourceName(); + } + + /* + * Get the type of a file (usually regular or directory). + */ + FileType getFileType(int idx) { + return mFileInfo->itemAt(idx).getFileType(); + } + +private: + /* these operations are not implemented */ + AssetDir(const AssetDir& src); + const AssetDir& operator=(const AssetDir& src); + + friend class AssetManager; + + /* + * This holds information about files in the asset hierarchy. + */ + class FileInfo { + public: + FileInfo(void) {} + FileInfo(const String8& path) // useful for e.g. svect.indexOf + : mFileName(path), mFileType(kFileTypeUnknown) + {} + ~FileInfo(void) {} + FileInfo(const FileInfo& src) { + copyMembers(src); + } + const FileInfo& operator= (const FileInfo& src) { + if (this != &src) + copyMembers(src); + return *this; + } + + void copyMembers(const FileInfo& src) { + mFileName = src.mFileName; + mFileType = src.mFileType; + mSourceName = src.mSourceName; + } + + /* need this for SortedVector; must compare only on file name */ + bool operator< (const FileInfo& rhs) const { + return mFileName < rhs.mFileName; + } + + /* used by AssetManager */ + bool operator== (const FileInfo& rhs) const { + return mFileName == rhs.mFileName; + } + + void set(const String8& path, FileType type) { + mFileName = path; + mFileType = type; + } + + const String8& getFileName(void) const { return mFileName; } + void setFileName(const String8& path) { mFileName = path; } + + FileType getFileType(void) const { return mFileType; } + void setFileType(FileType type) { mFileType = type; } + + const String8& getSourceName(void) const { return mSourceName; } + void setSourceName(const String8& path) { mSourceName = path; } + + /* + * Handy utility for finding an entry in a sorted vector of FileInfo. + * Returns the index of the matching entry, or -1 if none found. + */ + static int findEntry(const SortedVector* pVector, + const String8& fileName); + + private: + String8 mFileName; // filename only + FileType mFileType; // regular, directory, etc + + String8 mSourceName; // currently debug-only + }; + + /* AssetManager uses this to initialize us */ + void setFileList(SortedVector* list) { mFileInfo = list; } + + SortedVector* mFileInfo; +}; + +}; // namespace android + +#endif // __LIBS_ASSETDIR_H diff --git a/include/utils/AssetManager.h b/include/utils/AssetManager.h new file mode 100644 index 000000000..e94c0e8fe --- /dev/null +++ b/include/utils/AssetManager.h @@ -0,0 +1,323 @@ +/* + * Copyright (C) 2006 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. + */ + +// +// Asset management class. AssetManager objects are thread-safe. +// +#ifndef __LIBS_ASSETMANAGER_H +#define __LIBS_ASSETMANAGER_H + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace android { + +class Asset; // fwd decl for things that include Asset.h first +class ResTable; +struct ResTable_config; + +/* + * Every application that uses assets needs one instance of this. A + * single instance may be shared across multiple threads, and a single + * thread may have more than one instance (the latter is discouraged). + * + * The purpose of the AssetManager is to create Asset objects. To do + * this efficiently it may cache information about the locations of + * files it has seen. This can be controlled with the "cacheMode" + * argument. + * + * The asset hierarchy may be examined like a filesystem, using + * AssetDir objects to peruse a single directory. + */ +class AssetManager { +public: + typedef enum CacheMode { + CACHE_UNKNOWN = 0, + CACHE_OFF, // don't try to cache file locations + CACHE_DEFER, // construct cache as pieces are needed + //CACHE_SCAN, // scan full(!) asset hierarchy at init() time + } CacheMode; + + AssetManager(CacheMode cacheMode = CACHE_OFF); + virtual ~AssetManager(void); + + static int32_t getGlobalCount(); + + /* + * Add a new source for assets. This can be called multiple times to + * look in multiple places for assets. It can be either a directory (for + * finding assets as raw files on the disk) or a ZIP file. This newly + * added asset path will be examined first when searching for assets, + * before any that were previously added. + * + * Returns "true" on success, "false" on failure. If 'cookie' is non-NULL, + * then on success, *cookie is set to the value corresponding to the + * newly-added asset source. + */ + bool addAssetPath(const String8& path, void** cookie); + + /* + * Convenience for adding the standard system assets. Uses the + * ANDROID_ROOT environment variable to find them. + */ + bool addDefaultAssets(); + + /* + * Iterate over the asset paths in this manager. (Previously + * added via addAssetPath() and addDefaultAssets().) On first call, + * 'cookie' must be NULL, resulting in the first cookie being returned. + * Each next cookie will be returned there-after, until NULL indicating + * the end has been reached. + */ + void* nextAssetPath(void* cookie) const; + + /* + * Return an asset path in the manager. 'which' must be between 0 and + * countAssetPaths(). + */ + String8 getAssetPath(void* cookie) const; + + /* + * Set the current locale and vendor. The locale can change during + * the lifetime of an AssetManager if the user updates the device's + * language setting. The vendor is less likely to change. + * + * Pass in NULL to indicate no preference. + */ + void setLocale(const char* locale); + void setVendor(const char* vendor); + + /* + * Choose screen orientation for resources values returned. + */ + void setConfiguration(const ResTable_config& config, const char* locale = NULL); + + typedef Asset::AccessMode AccessMode; // typing shortcut + + /* + * Open an asset. + * + * This will search through locale-specific and vendor-specific + * directories and packages to find the file. + * + * The object returned does not depend on the AssetManager. It should + * be freed by calling Asset::close(). + */ + Asset* open(const char* fileName, AccessMode mode); + + /* + * Open a non-asset file as an asset. + * + * This is for opening files that are included in an asset package + * but aren't assets. These sit outside the usual "locale/vendor" + * path hierarchy, and will not be seen by "AssetDir" or included + * in our filename cache. + */ + Asset* openNonAsset(const char* fileName, AccessMode mode); + + /* + * Explicit non-asset file. The file explicitly named by the cookie (the + * resource set to look in) and fileName will be opened and returned. + */ + Asset* openNonAsset(void* cookie, const char* fileName, AccessMode mode); + + /* + * Open a directory within the asset hierarchy. + * + * The contents of the directory are an amalgam of vendor-specific, + * locale-specific, and generic assets stored loosely or in asset + * packages. Depending on the cache setting and previous accesses, + * this call may incur significant disk overhead. + * + * To open the top-level directory, pass in "". + */ + AssetDir* openDir(const char* dirName); + + /* + * Get the type of a file in the asset hierarchy. They will either + * be "regular" or "directory". [Currently only works for "regular".] + * + * Can also be used as a quick test for existence of a file. + */ + FileType getFileType(const char* fileName); + + /* + * Return the complete resource table to find things in the package. + */ + const ResTable& getResources(bool required = true) const; + + /* + * Discard cached filename information. This only needs to be called + * if somebody has updated the set of "loose" files, and we want to + * discard our cached notion of what's where. + */ + void purge(void) { purgeFileNameCacheLocked(); } + + /* + * Return true if the files this AssetManager references are all + * up-to-date (have not been changed since it was created). If false + * is returned, you will need to create a new AssetManager to get + * the current data. + */ + bool isUpToDate(); + + /** + * Get the known locales for this asset manager object. + */ + void getLocales(Vector* locales) const; + +private: + struct asset_path + { + String8 path; + FileType type; + }; + + Asset* openInPathLocked(const char* fileName, AccessMode mode, + const asset_path& path); + Asset* openNonAssetInPathLocked(const char* fileName, AccessMode mode, + const asset_path& path); + Asset* openInLocaleVendorLocked(const char* fileName, AccessMode mode, + const asset_path& path, const char* locale, const char* vendor); + String8 createPathNameLocked(const asset_path& path, const char* locale, + const char* vendor); + String8 createPathNameLocked(const asset_path& path, const char* rootDir); + String8 createZipSourceNameLocked(const String8& zipFileName, + const String8& dirName, const String8& fileName); + + ZipFileRO* getZipFileLocked(const asset_path& path); + Asset* openAssetFromFileLocked(const String8& fileName, AccessMode mode); + Asset* openAssetFromZipLocked(const ZipFileRO* pZipFile, + const ZipEntryRO entry, AccessMode mode, const String8& entryName); + + bool scanAndMergeDirLocked(SortedVector* pMergedInfo, + const asset_path& path, const char* rootDir, const char* dirName); + SortedVector* scanDirLocked(const String8& path); + bool scanAndMergeZipLocked(SortedVector* pMergedInfo, + const asset_path& path, const char* rootDir, const char* dirName); + void mergeInfoLocked(SortedVector* pMergedInfo, + const SortedVector* pContents); + + void loadFileNameCacheLocked(void); + void fncScanLocked(SortedVector* pMergedInfo, + const char* dirName); + bool fncScanAndMergeDirLocked( + SortedVector* pMergedInfo, + const asset_path& path, const char* locale, const char* vendor, + const char* dirName); + void purgeFileNameCacheLocked(void); + + const ResTable* getResTable(bool required = true) const; + void setLocaleLocked(const char* locale); + void updateResourceParamsLocked() const; + + class SharedZip : public RefBase { + public: + static sp get(const String8& path); + + ZipFileRO* getZip(); + + Asset* getResourceTableAsset(); + Asset* setResourceTableAsset(Asset* asset); + + bool isUpToDate(); + + protected: + ~SharedZip(); + + private: + SharedZip(const String8& path, time_t modWhen); + SharedZip(); // <-- not implemented + + String8 mPath; + ZipFileRO* mZipFile; + time_t mModWhen; + + Asset* mResourceTableAsset; + + static Mutex gLock; + static DefaultKeyedVector > gOpen; + }; + + /* + * Manage a set of Zip files. For each file we need a pointer to the + * ZipFile and a time_t with the file's modification date. + * + * We currently only have two zip files (current app, "common" app). + * (This was originally written for 8, based on app/locale/vendor.) + */ + class ZipSet { + public: + ZipSet(void); + ~ZipSet(void); + + /* + * Return a ZipFileRO structure for a ZipFileRO with the specified + * parameters. + */ + ZipFileRO* getZip(const String8& path); + + Asset* getZipResourceTable(const String8& path); + Asset* setZipResourceTable(const String8& path, Asset* asset); + + // generate path, e.g. "common/en-US-noogle.zip" + static String8 getPathName(const char* path); + + bool isUpToDate(); + + private: + void closeZip(int idx); + + int getIndex(const String8& zip) const; + mutable Vector mZipPath; + mutable Vector > mZipFile; + }; + + // Protect all internal state. + mutable Mutex mLock; + + ZipSet mZipSet; + + Vector mAssetPaths; + char* mLocale; + char* mVendor; + + mutable ResTable* mResources; + ResTable_config* mConfig; + + /* + * Cached data for "loose" files. This lets us avoid poking at the + * filesystem when searching for loose assets. Each entry is the + * "extended partial" path, e.g. "default/default/foo/bar.txt". The + * full set of files is present, including ".EXCLUDE" entries. + * + * We do not cache directory names. We don't retain the ".gz", + * because to our clients "foo" and "foo.gz" both look like "foo". + */ + CacheMode mCacheMode; // is the cache enabled? + bool mCacheValid; // clear when locale or vendor changes + SortedVector mCache; +}; + +}; // namespace android + +#endif // __LIBS_ASSETMANAGER_H diff --git a/include/utils/Atomic.h b/include/utils/Atomic.h new file mode 100644 index 000000000..7eb476c94 --- /dev/null +++ b/include/utils/Atomic.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2005 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_UTILS_ATOMIC_H +#define ANDROID_UTILS_ATOMIC_H + +#include + +#endif // ANDROID_UTILS_ATOMIC_H diff --git a/include/utils/Binder.h b/include/utils/Binder.h new file mode 100644 index 000000000..b5b8d9851 --- /dev/null +++ b/include/utils/Binder.h @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_BINDER_H +#define ANDROID_BINDER_H + +#include + +// --------------------------------------------------------------------------- +namespace android { + +class BBinder : public IBinder +{ +public: + BBinder(); + + virtual String16 getInterfaceDescriptor() const; + virtual bool isBinderAlive() const; + virtual status_t pingBinder(); + virtual status_t dump(int fd, const Vector& args); + + virtual status_t transact( uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags = 0); + + virtual status_t linkToDeath(const sp& recipient, + void* cookie = NULL, + uint32_t flags = 0); + + virtual status_t unlinkToDeath( const wp& recipient, + void* cookie = NULL, + uint32_t flags = 0, + wp* outRecipient = NULL); + + virtual void attachObject( const void* objectID, + void* object, + void* cleanupCookie, + object_cleanup_func func); + virtual void* findObject(const void* objectID) const; + virtual void detachObject(const void* objectID); + + virtual BBinder* localBinder(); + +protected: + virtual ~BBinder(); + + virtual status_t onTransact( uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags = 0); + +private: + BBinder(const BBinder& o); + BBinder& operator=(const BBinder& o); + + class Extras; + + Extras* mExtras; + void* mReserved0; +}; + +// --------------------------------------------------------------------------- + +class BpRefBase : public virtual RefBase +{ +protected: + BpRefBase(const sp& o); + virtual ~BpRefBase(); + virtual void onFirstRef(); + virtual void onLastStrongRef(const void* id); + virtual bool onIncStrongAttempted(uint32_t flags, const void* id); + + inline IBinder* remote() { return mRemote; } + inline IBinder* remote() const { return mRemote; } + +private: + BpRefBase(const BpRefBase& o); + BpRefBase& operator=(const BpRefBase& o); + + IBinder* const mRemote; + RefBase::weakref_type* mRefs; + volatile int32_t mState; +}; + +}; // namespace android + +// --------------------------------------------------------------------------- + +#endif // ANDROID_BINDER_H diff --git a/include/utils/BpBinder.h b/include/utils/BpBinder.h new file mode 100644 index 000000000..7b96e296f --- /dev/null +++ b/include/utils/BpBinder.h @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2005 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_BPBINDER_H +#define ANDROID_BPBINDER_H + +#include +#include +#include + +// --------------------------------------------------------------------------- +namespace android { + +class BpBinder : public IBinder +{ +public: + BpBinder(int32_t handle); + + inline int32_t handle() const { return mHandle; } + + virtual String16 getInterfaceDescriptor() const; + virtual bool isBinderAlive() const; + virtual status_t pingBinder(); + virtual status_t dump(int fd, const Vector& args); + + virtual status_t transact( uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags = 0); + + virtual status_t linkToDeath(const sp& recipient, + void* cookie = NULL, + uint32_t flags = 0); + virtual status_t unlinkToDeath( const wp& recipient, + void* cookie = NULL, + uint32_t flags = 0, + wp* outRecipient = NULL); + + virtual void attachObject( const void* objectID, + void* object, + void* cleanupCookie, + object_cleanup_func func); + virtual void* findObject(const void* objectID) const; + virtual void detachObject(const void* objectID); + + virtual BpBinder* remoteBinder(); + + status_t setConstantData(const void* data, size_t size); + void sendObituary(); + + class ObjectManager + { + public: + ObjectManager(); + ~ObjectManager(); + + void attach( const void* objectID, + void* object, + void* cleanupCookie, + IBinder::object_cleanup_func func); + void* find(const void* objectID) const; + void detach(const void* objectID); + + void kill(); + + private: + ObjectManager(const ObjectManager&); + ObjectManager& operator=(const ObjectManager&); + + struct entry_t + { + void* object; + void* cleanupCookie; + IBinder::object_cleanup_func func; + }; + + KeyedVector mObjects; + }; + +protected: + virtual ~BpBinder(); + virtual void onFirstRef(); + virtual void onLastStrongRef(const void* id); + virtual bool onIncStrongAttempted(uint32_t flags, const void* id); + +private: + const int32_t mHandle; + + struct Obituary { + wp recipient; + void* cookie; + uint32_t flags; + }; + + void reportOneDeath(const Obituary& obit); + + mutable Mutex mLock; + volatile int32_t mAlive; + volatile int32_t mObitsSent; + Vector* mObituaries; + ObjectManager mObjects; + Parcel* mConstantData; +}; + +}; // namespace android + +// --------------------------------------------------------------------------- + +#endif // ANDROID_BPBINDER_H diff --git a/include/utils/Buffer.h b/include/utils/Buffer.h new file mode 100644 index 000000000..8e22b0f21 --- /dev/null +++ b/include/utils/Buffer.h @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __UTILS_BUFFER_H__ +#define __UTILS_BUFFER_H__ 1 + +#include +#include +#include + +namespace android { + +class Buffer +{ +private: + char *buf; + int bufsiz; + int used; + void ensureCapacity(int len); + + void + makeRoomFor(int len) + { + if (len + used >= bufsiz) { + bufsiz = (len + used) * 3/2 + 2; + char *blah = new char[bufsiz]; + + memcpy(blah, buf, used); + delete[] buf; + buf = blah; + } + } + +public: + Buffer() + { + bufsiz = 16; + buf = new char[bufsiz]; + clear(); + } + + ~Buffer() + { + delete[] buf; + } + + void + clear() + { + buf[0] = '\0'; + used = 0; + } + + int + length() + { + return used; + } + + void + append(const char c) + { + makeRoomFor(1); + buf[used] = c; + used++; + buf[used] = '\0'; + } + + void + append(const char *s, int len) + { + makeRoomFor(len); + + memcpy(buf + used, s, len); + used += len; + buf[used] = '\0'; + } + + void + append(const char *s) + { + append(s, strlen(s)); + } + + char * + getBytes() + { + return buf; + } +}; + +}; // namespace android + +#endif diff --git a/include/utils/BufferedTextOutput.h b/include/utils/BufferedTextOutput.h new file mode 100644 index 000000000..69c624037 --- /dev/null +++ b/include/utils/BufferedTextOutput.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2006 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_BUFFEREDTEXTOUTPUT_H +#define ANDROID_BUFFEREDTEXTOUTPUT_H + +#include +#include +#include + +// --------------------------------------------------------------------------- +namespace android { + +class BufferedTextOutput : public TextOutput +{ +public: + //** Flags for constructor */ + enum { + MULTITHREADED = 0x0001 + }; + + BufferedTextOutput(uint32_t flags = 0); + virtual ~BufferedTextOutput(); + + virtual status_t print(const char* txt, size_t len); + virtual void moveIndent(int delta); + + virtual void pushBundle(); + virtual void popBundle(); + +protected: + virtual status_t writeLines(const struct iovec& vec, size_t N) = 0; + +private: + struct BufferState; + struct ThreadState; + + static ThreadState*getThreadState(); + static void threadDestructor(void *st); + + BufferState*getBuffer() const; + + uint32_t mFlags; + const int32_t mSeq; + const int32_t mIndex; + + Mutex mLock; + BufferState* mGlobalState; +}; + +// --------------------------------------------------------------------------- +}; // namespace android + +#endif // ANDROID_BUFFEREDTEXTOUTPUT_H diff --git a/include/utils/ByteOrder.h b/include/utils/ByteOrder.h new file mode 100644 index 000000000..4c0606763 --- /dev/null +++ b/include/utils/ByteOrder.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2006 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 _LIBS_UTILS_BYTE_ORDER_H +#define _LIBS_UTILS_BYTE_ORDER_H + +#include +#include +#ifdef HAVE_WINSOCK +#include +#else +#include +#endif + +/* + * These macros are like the hton/ntoh byte swapping macros, + * except they allow you to swap to and from the "device" byte + * order. The device byte order is the endianness of the target + * device -- for the ARM CPUs we use today, this is little endian. + * + * Note that the byte swapping functions have not been optimized + * much; performance is currently not an issue for them since the + * intent is to allow us to avoid byte swapping on the device. + */ + +#define DEVICE_BYTE_ORDER LITTLE_ENDIAN + +#if BYTE_ORDER == DEVICE_BYTE_ORDER + +#define dtohl(x) (x) +#define dtohs(x) (x) +#define htodl(x) (x) +#define htods(x) (x) + +#else + +static inline uint32_t android_swap_long(uint32_t v) +{ + return (v<<24) | ((v<<8)&0x00FF0000) | ((v>>8)&0x0000FF00) | (v>>24); +} + +static inline uint16_t android_swap_short(uint16_t v) +{ + return (v<<8) | (v>>8); +} + +#define dtohl(x) (android_swap_long(x)) +#define dtohs(x) (android_swap_short(x)) +#define htodl(x) (android_swap_long(x)) +#define htods(x) (android_swap_short(x)) + +#endif + +#endif // _LIBS_UTILS_BYTE_ORDER_H diff --git a/include/utils/CallStack.h b/include/utils/CallStack.h new file mode 100644 index 000000000..c2c8ce514 --- /dev/null +++ b/include/utils/CallStack.h @@ -0,0 +1,76 @@ +/* + * 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. + */ + +#ifndef ANDROID_CALLSTACK_H +#define ANDROID_CALLSTACK_H + +#include +#include + +#include + +// --------------------------------------------------------------------------- + +namespace android { + +class CallStack +{ +public: + enum { + MAX_DEPTH = 31 + }; + + CallStack(); + CallStack(const CallStack& rhs); + ~CallStack(); + + CallStack& operator = (const CallStack& rhs); + + bool operator == (const CallStack& rhs) const; + bool operator != (const CallStack& rhs) const; + bool operator < (const CallStack& rhs) const; + bool operator >= (const CallStack& rhs) const; + bool operator > (const CallStack& rhs) const; + bool operator <= (const CallStack& rhs) const; + + const void* operator [] (int index) const; + + void clear(); + + void update(int32_t ignoreDepth=0, int32_t maxDepth=MAX_DEPTH); + + // Dump a stack trace to the log + void dump(const char* prefix = 0) const; + + // Return a string (possibly very long) containing the complete stack trace + String8 toString(const char* prefix = 0) const; + + size_t size() const { return mCount; } + +private: + // Internal helper function + String8 toStringSingleLevel(const char* prefix, int32_t level) const; + + size_t mCount; + const void* mStack[MAX_DEPTH]; +}; + +}; // namespace android + + +// --------------------------------------------------------------------------- + +#endif // ANDROID_CALLSTACK_H diff --git a/include/utils/Debug.h b/include/utils/Debug.h new file mode 100644 index 000000000..a662b9cc2 --- /dev/null +++ b/include/utils/Debug.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2005 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. + */ + +// +// Debugging tools. These should be able to be stripped +// in release builds. +// +#ifndef ANDROID_DEBUG_H +#define ANDROID_DEBUG_H + +#include +#include + +namespace android { + +template struct CompileTimeAssert; +template<> struct CompileTimeAssert {}; + +const char* stringForIndent(int32_t indentLevel); + +typedef void (*debugPrintFunc)(void* cookie, const char* txt); + +void printTypeCode(uint32_t typeCode, + debugPrintFunc func = 0, void* cookie = 0); +void printHexData(int32_t indent, const void *buf, size_t length, + size_t bytesPerLine=16, int32_t singleLineBytesCutoff=16, + size_t alignment=0, bool cArrayStyle=false, + debugPrintFunc func = 0, void* cookie = 0); + +}; // namespace android + +#endif // ANDROID_DEBUG_H diff --git a/include/utils/Endian.h b/include/utils/Endian.h new file mode 100644 index 000000000..19f250494 --- /dev/null +++ b/include/utils/Endian.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2005 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. + */ + +// +// Android endian-ness defines. +// +#ifndef _LIBS_UTILS_ENDIAN_H +#define _LIBS_UTILS_ENDIAN_H + +#if defined(HAVE_ENDIAN_H) + +#include + +#else /*not HAVE_ENDIAN_H*/ + +#define __BIG_ENDIAN 0x1000 +#define __LITTLE_ENDIAN 0x0001 + +#if defined(HAVE_LITTLE_ENDIAN) +# define __BYTE_ORDER __LITTLE_ENDIAN +#else +# define __BYTE_ORDER __BIG_ENDIAN +#endif + +#endif /*not HAVE_ENDIAN_H*/ + +#endif /*_LIBS_UTILS_ENDIAN_H*/ diff --git a/include/utils/Errors.h b/include/utils/Errors.h new file mode 100644 index 000000000..1bf9e6f2b --- /dev/null +++ b/include/utils/Errors.h @@ -0,0 +1,87 @@ +/* + * 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. + */ + +#ifndef ANDROID_ERRORS_H +#define ANDROID_ERRORS_H + +#include +#include + +namespace android { + +// use this type to return error codes +#ifdef HAVE_MS_C_RUNTIME +typedef int status_t; +#else +typedef int32_t status_t; +#endif + +/* the MS C runtime lacks a few error codes */ + +/* + * Error codes. + * All error codes are negative values. + */ + +// Win32 #defines NO_ERROR as well. It has the same value, so there's no +// real conflict, though it's a bit awkward. +#ifdef _WIN32 +# undef NO_ERROR +#endif + +enum { + OK = 0, // Everything's swell. + NO_ERROR = 0, // No errors. + + UNKNOWN_ERROR = 0x80000000, + + NO_MEMORY = -ENOMEM, + INVALID_OPERATION = -ENOSYS, + BAD_VALUE = -EINVAL, + BAD_TYPE = 0x80000001, + NAME_NOT_FOUND = -ENOENT, + PERMISSION_DENIED = -EPERM, + NO_INIT = -ENODEV, + ALREADY_EXISTS = -EEXIST, + DEAD_OBJECT = -EPIPE, + FAILED_TRANSACTION = 0x80000002, + JPARKS_BROKE_IT = -EPIPE, +#if !defined(HAVE_MS_C_RUNTIME) + BAD_INDEX = -EOVERFLOW, + NOT_ENOUGH_DATA = -ENODATA, + WOULD_BLOCK = -EWOULDBLOCK, + TIMED_OUT = -ETIME, + UNKNOWN_TRANSACTION = -EBADMSG, +#else + BAD_INDEX = -E2BIG, + NOT_ENOUGH_DATA = 0x80000003, + WOULD_BLOCK = 0x80000004, + TIMED_OUT = 0x80000005, + UNKNOWN_TRANSACTION = 0x80000006, +#endif +}; + +// Restore define; enumeration is in "android" namespace, so the value defined +// there won't work for Win32 code in a different namespace. +#ifdef _WIN32 +# define NO_ERROR 0L +#endif + +}; // namespace android + +// --------------------------------------------------------------------------- + +#endif // ANDROID_ERRORS_H diff --git a/include/utils/FileMap.h b/include/utils/FileMap.h new file mode 100644 index 000000000..8dfd3bea6 --- /dev/null +++ b/include/utils/FileMap.h @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2006 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. + */ + +// +// Encapsulate a shared file mapping. +// +#ifndef __LIBS_FILE_MAP_H +#define __LIBS_FILE_MAP_H + +#include + +#ifdef HAVE_WIN32_FILEMAP +#include +#endif + +namespace android { + +/* + * This represents a memory-mapped file. It might be the entire file or + * only part of it. This requires a little bookkeeping because the mapping + * needs to be aligned on page boundaries, and in some cases we'd like to + * have multiple references to the mapped area without creating additional + * maps. + * + * This always uses MAP_SHARED. + * + * TODO: we should be able to create a new FileMap that is a subset of + * an existing FileMap and shares the underlying mapped pages. Requires + * completing the refcounting stuff and possibly introducing the notion + * of a FileMap hierarchy. + */ +class FileMap { +public: + FileMap(void); + + /* + * Create a new mapping on an open file. + * + * Closing the file descriptor does not unmap the pages, so we don't + * claim ownership of the fd. + * + * Returns "false" on failure. + */ + bool create(const char* origFileName, int fd, + off_t offset, size_t length, bool readOnly); + + /* + * Return the name of the file this map came from, if known. + */ + const char* getFileName(void) const { return mFileName; } + + /* + * Get a pointer to the piece of the file we requested. + */ + void* getDataPtr(void) const { return mDataPtr; } + + /* + * Get the length we requested. + */ + size_t getDataLength(void) const { return mDataLength; } + + /* + * Get the data offset used to create this map. + */ + off_t getDataOffset(void) const { return mDataOffset; } + + /* + * Get a "copy" of the object. + */ + FileMap* acquire(void) { mRefCount++; return this; } + + /* + * Call this when mapping is no longer needed. + */ + void release(void) { + if (--mRefCount <= 0) + delete this; + } + + /* + * This maps directly to madvise() values, but allows us to avoid + * including everywhere. + */ + enum MapAdvice { + NORMAL, RANDOM, SEQUENTIAL, WILLNEED, DONTNEED + }; + + /* + * Apply an madvise() call to the entire file. + * + * Returns 0 on success, -1 on failure. + */ + int advise(MapAdvice advice); + +protected: + // don't delete objects; call release() + ~FileMap(void); + +private: + // these are not implemented + FileMap(const FileMap& src); + const FileMap& operator=(const FileMap& src); + + int mRefCount; // reference count + char* mFileName; // original file name, if known + void* mBasePtr; // base of mmap area; page aligned + size_t mBaseLength; // length, measured from "mBasePtr" + off_t mDataOffset; // offset used when map was created + void* mDataPtr; // start of requested data, offset from base + size_t mDataLength; // length, measured from "mDataPtr" +#ifdef HAVE_WIN32_FILEMAP + HANDLE mFileHandle; // Win32 file handle + HANDLE mFileMapping; // Win32 file mapping handle +#endif + + static long mPageSize; +}; + +}; // namespace android + +#endif // __LIBS_FILE_MAP_H diff --git a/include/utils/IBinder.h b/include/utils/IBinder.h new file mode 100644 index 000000000..737033090 --- /dev/null +++ b/include/utils/IBinder.h @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_IBINDER_H +#define ANDROID_IBINDER_H + +#include +#include +#include +#include + + +#define B_PACK_CHARS(c1, c2, c3, c4) \ + ((((c1)<<24)) | (((c2)<<16)) | (((c3)<<8)) | (c4)) + +// --------------------------------------------------------------------------- +namespace android { + +class BBinder; +class BpBinder; +class IInterface; +class Parcel; + +/** + * Base class and low-level protocol for a remotable object. + * You can derive from this class to create an object for which other + * processes can hold references to it. Communication between processes + * (method calls, property get and set) is down through a low-level + * protocol implemented on top of the transact() API. + */ +class IBinder : public virtual RefBase +{ +public: + enum { + FIRST_CALL_TRANSACTION = 0x00000001, + LAST_CALL_TRANSACTION = 0x00ffffff, + + PING_TRANSACTION = B_PACK_CHARS('_','P','N','G'), + DUMP_TRANSACTION = B_PACK_CHARS('_','D','M','P'), + INTERFACE_TRANSACTION = B_PACK_CHARS('_', 'N', 'T', 'F'), + + // Corresponds to tfOneWay -- an asynchronous call. + FLAG_ONEWAY = 0x00000001 + }; + + inline IBinder() { } + + /** + * Check if this IBinder implements the interface named by + * @a descriptor. If it does, the base pointer to it is returned, + * which you can safely static_cast<> to the concrete C++ interface. + */ + virtual sp queryLocalInterface(const String16& descriptor); + + /** + * Return the canonical name of the interface provided by this IBinder + * object. + */ + virtual String16 getInterfaceDescriptor() const = 0; + + virtual bool isBinderAlive() const = 0; + virtual status_t pingBinder() = 0; + virtual status_t dump(int fd, const Vector& args) = 0; + + virtual status_t transact( uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags = 0) = 0; + + /** + * This method allows you to add data that is transported through + * IPC along with your IBinder pointer. When implementing a Binder + * object, override it to write your desired data in to @a outData. + * You can then call getConstantData() on your IBinder to retrieve + * that data, from any process. You MUST return the number of bytes + * written in to the parcel (including padding). + */ + class DeathRecipient : public virtual RefBase + { + public: + virtual void binderDied(const wp& who) = 0; + }; + + /** + * Register the @a recipient for a notification if this binder + * goes away. If this binder object unexpectedly goes away + * (typically because its hosting process has been killed), + * then DeathRecipient::binderDied() will be called with a referene + * to this. + * + * The @a cookie is optional -- if non-NULL, it should be a + * memory address that you own (that is, you know it is unique). + * + * @note You will only receive death notifications for remote binders, + * as local binders by definition can't die without you dying as well. + * Trying to use this function on a local binder will result in an + * INVALID_OPERATION code being returned and nothing happening. + * + * @note This link always holds a weak reference to its recipient. + * + * @note You will only receive a weak reference to the dead + * binder. You should not try to promote this to a strong reference. + * (Nor should you need to, as there is nothing useful you can + * directly do with it now that it has passed on.) + */ + virtual status_t linkToDeath(const sp& recipient, + void* cookie = NULL, + uint32_t flags = 0) = 0; + + /** + * Remove a previously registered death notification. + * The @a recipient will no longer be called if this object + * dies. The @a cookie is optional. If non-NULL, you can + * supply a NULL @a recipient, and the recipient previously + * added with that cookie will be unlinked. + */ + virtual status_t unlinkToDeath( const wp& recipient, + void* cookie = NULL, + uint32_t flags = 0, + wp* outRecipient = NULL) = 0; + + virtual bool checkSubclass(const void* subclassID) const; + + typedef void (*object_cleanup_func)(const void* id, void* obj, void* cleanupCookie); + + virtual void attachObject( const void* objectID, + void* object, + void* cleanupCookie, + object_cleanup_func func) = 0; + virtual void* findObject(const void* objectID) const = 0; + virtual void detachObject(const void* objectID) = 0; + + virtual BBinder* localBinder(); + virtual BpBinder* remoteBinder(); + +protected: + inline virtual ~IBinder() { } + +private: +}; + +}; // namespace android + +// --------------------------------------------------------------------------- + +#endif // ANDROID_IBINDER_H diff --git a/include/utils/IInterface.h b/include/utils/IInterface.h new file mode 100644 index 000000000..959722a4d --- /dev/null +++ b/include/utils/IInterface.h @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2005 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_IINTERFACE_H +#define ANDROID_IINTERFACE_H + +#include + +namespace android { + +// ---------------------------------------------------------------------- + +class IInterface : public virtual RefBase +{ +public: + sp asBinder(); + sp asBinder() const; + +protected: + virtual IBinder* onAsBinder() = 0; +}; + +// ---------------------------------------------------------------------- + +template +inline sp interface_cast(const sp& obj) +{ + return INTERFACE::asInterface(obj); +} + +// ---------------------------------------------------------------------- + +template +class BnInterface : public INTERFACE, public BBinder +{ +public: + virtual sp queryLocalInterface(const String16& _descriptor); + virtual String16 getInterfaceDescriptor() const; + +protected: + virtual IBinder* onAsBinder(); +}; + +// ---------------------------------------------------------------------- + +template +class BpInterface : public INTERFACE, public BpRefBase +{ +public: + BpInterface(const sp& remote); + +protected: + virtual IBinder* onAsBinder(); +}; + +// ---------------------------------------------------------------------- + +#define DECLARE_META_INTERFACE(INTERFACE) \ + static const String16 descriptor; \ + static sp asInterface(const sp& obj); \ + virtual String16 getInterfaceDescriptor() const; \ + +#define IMPLEMENT_META_INTERFACE(INTERFACE, NAME) \ + const String16 I##INTERFACE::descriptor(NAME); \ + String16 I##INTERFACE::getInterfaceDescriptor() const { \ + return I##INTERFACE::descriptor; \ + } \ + sp I##INTERFACE::asInterface(const sp& obj) \ + { \ + sp intr; \ + if (obj != NULL) { \ + intr = static_cast( \ + obj->queryLocalInterface( \ + I##INTERFACE::descriptor).get()); \ + if (intr == NULL) { \ + intr = new Bp##INTERFACE(obj); \ + } \ + } \ + return intr; \ + } \ + +// ---------------------------------------------------------------------- +// No user-servicable parts after this... + +template +inline sp BnInterface::queryLocalInterface( + const String16& _descriptor) +{ + if (_descriptor == INTERFACE::descriptor) return this; + return NULL; +} + +template +inline String16 BnInterface::getInterfaceDescriptor() const +{ + return INTERFACE::getInterfaceDescriptor(); +} + +template +IBinder* BnInterface::onAsBinder() +{ + return this; +} + +template +inline BpInterface::BpInterface(const sp& remote) + : BpRefBase(remote) +{ +} + +template +inline IBinder* BpInterface::onAsBinder() +{ + return remote(); +} + +// ---------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_IINTERFACE_H diff --git a/include/utils/IMemory.h b/include/utils/IMemory.h new file mode 100644 index 000000000..35a3fd7da --- /dev/null +++ b/include/utils/IMemory.h @@ -0,0 +1,94 @@ +/* + * 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. + */ + +#ifndef ANDROID_IMEMORY_H +#define ANDROID_IMEMORY_H + +#include +#include +#include + +#include +#include +#include + +namespace android { + +// ---------------------------------------------------------------------------- + +class IMemoryHeap : public IInterface +{ +public: + DECLARE_META_INTERFACE(MemoryHeap); + + // flags returned by getFlags() + enum { + READ_ONLY = 0x00000001, + MAP_ONCE = 0x00000002 + }; + + virtual int getHeapID() const = 0; + virtual void* getBase() const = 0; + virtual size_t getSize() const = 0; + virtual uint32_t getFlags() const = 0; + + // these are there just for backward source compatibility + int32_t heapID() const { return getHeapID(); } + void* base() const { return getBase(); } + size_t virtualSize() const { return getSize(); } +}; + +class BnMemoryHeap : public BnInterface +{ +public: + virtual status_t onTransact( + uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags = 0); +}; + +// ---------------------------------------------------------------------------- + +class IMemory : public IInterface +{ +public: + DECLARE_META_INTERFACE(Memory); + + virtual sp getMemory(ssize_t* offset=0, size_t* size=0) const = 0; + + // helpers + void* fastPointer(const sp& heap, ssize_t offset) const; + void* pointer() const; + size_t size() const; + ssize_t offset() const; +}; + +class BnMemory : public BnInterface +{ +public: + virtual status_t onTransact( + uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags = 0); +}; + +// ---------------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_IMEMORY_H diff --git a/include/utils/IPCThreadState.h b/include/utils/IPCThreadState.h new file mode 100644 index 000000000..47043b817 --- /dev/null +++ b/include/utils/IPCThreadState.h @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2005 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_IPC_THREAD_STATE_H +#define ANDROID_IPC_THREAD_STATE_H + +#include +#include +#include + +#ifdef HAVE_WIN32_PROC +typedef int uid_t; +#endif + +// --------------------------------------------------------------------------- +namespace android { + +class IPCThreadState +{ +public: + static IPCThreadState* self(); + + sp process(); + + status_t clearLastError(); + + int getCallingPid(); + int getCallingUid(); + + int64_t clearCallingIdentity(); + void restoreCallingIdentity(int64_t token); + + void flushCommands(); + + void joinThreadPool(bool isMain = true); + + // Stop the local process. + void stopProcess(bool immediate = true); + + status_t transact(int32_t handle, + uint32_t code, const Parcel& data, + Parcel* reply, uint32_t flags); + + void incStrongHandle(int32_t handle); + void decStrongHandle(int32_t handle); + void incWeakHandle(int32_t handle); + void decWeakHandle(int32_t handle); + status_t attemptIncStrongHandle(int32_t handle); + static void expungeHandle(int32_t handle, IBinder* binder); + status_t requestDeathNotification( int32_t handle, + BpBinder* proxy); + status_t clearDeathNotification( int32_t handle, + BpBinder* proxy); + + static void shutdown(); + +private: + IPCThreadState(); + ~IPCThreadState(); + + status_t sendReply(const Parcel& reply, uint32_t flags); + status_t waitForResponse(Parcel *reply, + status_t *acquireResult=NULL); + status_t talkWithDriver(bool doReceive=true); + status_t writeTransactionData(int32_t cmd, + uint32_t binderFlags, + int32_t handle, + uint32_t code, + const Parcel& data, + status_t* statusBuffer); + status_t executeCommand(int32_t command); + + void clearCaller(); + + static void threadDestructor(void *st); + static void freeBuffer(Parcel* parcel, + const uint8_t* data, size_t dataSize, + const size_t* objects, size_t objectsSize, + void* cookie); + + const sp mProcess; + + Parcel mIn; + Parcel mOut; + status_t mLastError; + pid_t mCallingPid; + uid_t mCallingUid; +}; + +}; // namespace android + +// --------------------------------------------------------------------------- + +#endif // ANDROID_IPC_THREAD_STATE_H diff --git a/include/utils/IPermissionController.h b/include/utils/IPermissionController.h new file mode 100644 index 000000000..cb1dd345d --- /dev/null +++ b/include/utils/IPermissionController.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2005 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_IPERMISSION_CONTROLLER_H +#define ANDROID_IPERMISSION_CONTROLLER_H + +#include + +namespace android { + +// ---------------------------------------------------------------------- + +class IPermissionController : public IInterface +{ +public: + DECLARE_META_INTERFACE(PermissionController); + + virtual bool checkPermission(const String16& permission, + int32_t pid, int32_t uid) = 0; + + enum { + CHECK_PERMISSION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION + }; +}; + +// ---------------------------------------------------------------------- + +class BnPermissionController : public BnInterface +{ +public: + virtual status_t onTransact( uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags = 0); +}; + +// ---------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_IPERMISSION_CONTROLLER_H + diff --git a/include/utils/IServiceManager.h b/include/utils/IServiceManager.h new file mode 100644 index 000000000..e3d99fe7e --- /dev/null +++ b/include/utils/IServiceManager.h @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2005 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_ISERVICE_MANAGER_H +#define ANDROID_ISERVICE_MANAGER_H + +#include +#include +#include +#include + +namespace android { + +// ---------------------------------------------------------------------- + +class IServiceManager : public IInterface +{ +public: + DECLARE_META_INTERFACE(ServiceManager); + + /** + * Retrieve an existing service, blocking for a few seconds + * if it doesn't yet exist. + */ + virtual sp getService( const String16& name) const = 0; + + /** + * Retrieve an existing service, non-blocking. + */ + virtual sp checkService( const String16& name) const = 0; + + /** + * Register a service. + */ + virtual status_t addService( const String16& name, + const sp& service) = 0; + + /** + * Return list of all existing services. + */ + virtual Vector listServices() = 0; + + enum { + GET_SERVICE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION, + CHECK_SERVICE_TRANSACTION, + ADD_SERVICE_TRANSACTION, + LIST_SERVICES_TRANSACTION, + }; +}; + +sp defaultServiceManager(); + +template +status_t getService(const String16& name, sp* outService) +{ + const sp sm = defaultServiceManager(); + if (sm != NULL) { + *outService = interface_cast(sm->getService(name)); + if ((*outService) != NULL) return NO_ERROR; + } + return NAME_NOT_FOUND; +} + +bool checkCallingPermission(const String16& permission); +bool checkCallingPermission(const String16& permission, + int32_t* outPid, int32_t* outUid); + +// ---------------------------------------------------------------------- + +class BnServiceManager : public BnInterface +{ +public: + virtual status_t onTransact( uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags = 0); +}; + +// ---------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_ISERVICE_MANAGER_H + diff --git a/include/utils/KeyedVector.h b/include/utils/KeyedVector.h new file mode 100644 index 000000000..f4513ee20 --- /dev/null +++ b/include/utils/KeyedVector.h @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2005 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_KEYED_VECTOR_H +#define ANDROID_KEYED_VECTOR_H + +#include +#include +#include + +#include +#include +#include + +// --------------------------------------------------------------------------- + +namespace android { + +template +class KeyedVector +{ +public: + typedef KEY key_type; + typedef VALUE value_type; + + inline KeyedVector(); + + /* + * empty the vector + */ + + inline void clear() { mVector.clear(); } + + /*! + * vector stats + */ + + //! returns number of items in the vector + inline size_t size() const { return mVector.size(); } + //! returns wether or not the vector is empty + inline bool isEmpty() const { return mVector.isEmpty(); } + //! returns how many items can be stored without reallocating the backing store + inline size_t capacity() const { return mVector.capacity(); } + //! setst the capacity. capacity can never be reduced less than size() + inline ssize_t setCapacity(size_t size) { return mVector.setCapacity(size); } + + /*! + * accessors + */ + const VALUE& valueFor(const KEY& key) const; + const VALUE& valueAt(size_t index) const; + const KEY& keyAt(size_t index) const; + ssize_t indexOfKey(const KEY& key) const; + + /*! + * modifing the array + */ + + VALUE& editValueFor(const KEY& key); + VALUE& editValueAt(size_t index); + + /*! + * add/insert/replace items + */ + + ssize_t add(const KEY& key, const VALUE& item); + ssize_t replaceValueFor(const KEY& key, const VALUE& item); + ssize_t replaceValueAt(size_t index, const VALUE& item); + + /*! + * remove items + */ + + ssize_t removeItem(const KEY& key); + ssize_t removeItemsAt(size_t index, size_t count = 1); + +private: + SortedVector< key_value_pair_t > mVector; +}; + +// --------------------------------------------------------------------------- + +/** + * Variation of KeyedVector that holds a default value to return when + * valueFor() is called with a key that doesn't exist. + */ +template +class DefaultKeyedVector : public KeyedVector +{ +public: + inline DefaultKeyedVector(const VALUE& defValue = VALUE()); + const VALUE& valueFor(const KEY& key) const; + +private: + VALUE mDefault; +}; + +// --------------------------------------------------------------------------- + +template inline +KeyedVector::KeyedVector() +{ +} + +template inline +ssize_t KeyedVector::indexOfKey(const KEY& key) const { + return mVector.indexOf( key_value_pair_t(key) ); +} + +template inline +const VALUE& KeyedVector::valueFor(const KEY& key) const { + ssize_t i = indexOfKey(key); + assert(i>=0); + return mVector.itemAt(i).value; +} + +template inline +const VALUE& KeyedVector::valueAt(size_t index) const { + return mVector.itemAt(index).value; +} + +template inline +const KEY& KeyedVector::keyAt(size_t index) const { + return mVector.itemAt(index).key; +} + +template inline +VALUE& KeyedVector::editValueFor(const KEY& key) { + ssize_t i = indexOfKey(key); + assert(i>=0); + return mVector.editItemAt(i).value; +} + +template inline +VALUE& KeyedVector::editValueAt(size_t index) { + return mVector.editItemAt(index).value; +} + +template inline +ssize_t KeyedVector::add(const KEY& key, const VALUE& value) { + return mVector.add( key_value_pair_t(key, value) ); +} + +template inline +ssize_t KeyedVector::replaceValueFor(const KEY& key, const VALUE& value) { + key_value_pair_t pair(key, value); + mVector.remove(pair); + return mVector.add(pair); +} + +template inline +ssize_t KeyedVector::replaceValueAt(size_t index, const VALUE& item) { + if (index inline +ssize_t KeyedVector::removeItem(const KEY& key) { + return mVector.remove(key_value_pair_t(key)); +} + +template inline +ssize_t KeyedVector::removeItemsAt(size_t index, size_t count) { + return mVector.removeItemsAt(index, count); +} + +// --------------------------------------------------------------------------- + +template inline +DefaultKeyedVector::DefaultKeyedVector(const VALUE& defValue) + : mDefault(defValue) +{ +} + +template inline +const VALUE& DefaultKeyedVector::valueFor(const KEY& key) const { + ssize_t i = indexOfKey(key); + return i >= 0 ? KeyedVector::valueAt(i) : mDefault; +} + +}; // namespace android + +// --------------------------------------------------------------------------- + +#endif // ANDROID_KEYED_VECTOR_H diff --git a/include/utils/List.h b/include/utils/List.h new file mode 100644 index 000000000..1a6be9ac9 --- /dev/null +++ b/include/utils/List.h @@ -0,0 +1,280 @@ +/* + * Copyright (C) 2005 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. + */ + +// +// Templated list class. Normally we'd use STL, but we don't have that. +// This class mimics STL's interfaces. +// +// Objects are copied into the list with the '=' operator or with copy- +// construction, so if the compiler's auto-generated versions won't work for +// you, define your own. +// +// The only class you want to use from here is "List". Do not use classes +// starting with "_" directly. +// +#ifndef _LIBS_UTILS_LIST_H +#define _LIBS_UTILS_LIST_H + +namespace android { + +/* + * One element in the list. + */ +template class _ListNode { +public: + typedef _ListNode _Node; + + _ListNode(const T& val) : mVal(val) {} + ~_ListNode(void) {} + + T& getRef(void) { return mVal; } + void setVal(const T& val) { mVal = val; } + + _Node* getPrev(void) const { return mpPrev; } + void setPrev(_Node* ptr) { mpPrev = ptr; } + _Node* getNext(void) const { return mpNext; } + void setNext(_Node* ptr) { mpNext = ptr; } + +private: + T mVal; + _Node* mpPrev; + _Node* mpNext; +}; + +/* + * Iterator for walking through the list. + */ +template class _ListIterator { +public: + typedef _ListIterator _Iter; + typedef _ListNode _Node; + + _ListIterator(void) {} + _ListIterator(_Node* ptr) : mpNode(ptr) {} + ~_ListIterator(void) {} + + /* + * Dereference operator. Used to get at the juicy insides. + */ + Tref operator*() const { return mpNode->getRef(); } + + /* + * Iterator comparison. + */ + bool operator==(const _Iter& right) const { return mpNode == right.mpNode; } + bool operator!=(const _Iter& right) const { return mpNode != right.mpNode; } + + /* + * Incr/decr, used to move through the list. + */ + _Iter& operator++(void) { // pre-increment + mpNode = mpNode->getNext(); + return *this; + } + _Iter operator++(int) { // post-increment + _Iter tmp = *this; + ++*this; + return tmp; + } + _Iter& operator--(void) { // pre-increment + mpNode = mpNode->getPrev(); + return *this; + } + _Iter operator--(int) { // post-increment + _Iter tmp = *this; + --*this; + return tmp; + } + + _Node* getNode(void) const { return mpNode; } + +private: + _Node* mpNode; +}; + + +/* + * Doubly-linked list. Instantiate with "List myList". + * + * Objects added to the list are copied using the assignment operator, + * so this must be defined. + */ +template class List { +public: + typedef _ListNode _Node; + + List(void) { + prep(); + } + List(const List& src) { // copy-constructor + prep(); + insert(begin(), src.begin(), src.end()); + } + virtual ~List(void) { + clear(); + delete[] (unsigned char*) mpMiddle; + } + + typedef _ListIterator iterator; + typedef _ListIterator const_iterator; + + List& operator=(const List& right); + + /* returns true if the list is empty */ + bool empty(void) const { return mpMiddle->getNext() == mpMiddle; } + + /* return #of elements in list */ + unsigned int size(void) const { + return distance(begin(), end()); + } + + /* + * Return the first element or one past the last element. The + * _ListNode* we're returning is converted to an "iterator" by a + * constructor in _ListIterator. + */ + iterator begin() { return mpMiddle->getNext(); } + const_iterator begin() const { return mpMiddle->getNext(); } + iterator end() { return mpMiddle; } + const_iterator end() const { return mpMiddle; } + + /* add the object to the head or tail of the list */ + void push_front(const T& val) { insert(begin(), val); } + void push_back(const T& val) { insert(end(), val); } + + /* insert before the current node; returns iterator at new node */ + iterator insert(iterator posn, const T& val) { + _Node* newNode = new _Node(val); // alloc & copy-construct + newNode->setNext(posn.getNode()); + newNode->setPrev(posn.getNode()->getPrev()); + posn.getNode()->getPrev()->setNext(newNode); + posn.getNode()->setPrev(newNode); + return newNode; + } + + /* insert a range of elements before the current node */ + void insert(iterator posn, const_iterator first, const_iterator last) { + for ( ; first != last; ++first) + insert(posn, *first); + } + + /* remove one entry; returns iterator at next node */ + iterator erase(iterator posn) { + _Node* pNext = posn.getNode()->getNext(); + _Node* pPrev = posn.getNode()->getPrev(); + pPrev->setNext(pNext); + pNext->setPrev(pPrev); + delete posn.getNode(); + return pNext; + } + + /* remove a range of elements */ + iterator erase(iterator first, iterator last) { + while (first != last) + erase(first++); // don't erase than incr later! + return last; + } + + /* remove all contents of the list */ + void clear(void) { + _Node* pCurrent = mpMiddle->getNext(); + _Node* pNext; + + while (pCurrent != mpMiddle) { + pNext = pCurrent->getNext(); + delete pCurrent; + pCurrent = pNext; + } + mpMiddle->setPrev(mpMiddle); + mpMiddle->setNext(mpMiddle); + } + + /* + * Measure the distance between two iterators. On exist, "first" + * will be equal to "last". The iterators must refer to the same + * list. + * + * (This is actually a generic iterator function. It should be part + * of some other class, possibly an iterator base class. It needs to + * know the difference between a list, which has to march through, + * and a vector, which can just do pointer math.) + */ + unsigned int distance(iterator first, iterator last) { + unsigned int count = 0; + while (first != last) { + ++first; + ++count; + } + return count; + } + unsigned int distance(const_iterator first, const_iterator last) const { + unsigned int count = 0; + while (first != last) { + ++first; + ++count; + } + return count; + } + +private: + /* + * I want a _ListNode but don't need it to hold valid data. More + * to the point, I don't want T's constructor to fire, since it + * might have side-effects or require arguments. So, we do this + * slightly uncouth storage alloc. + */ + void prep(void) { + mpMiddle = (_Node*) new unsigned char[sizeof(_Node)]; + mpMiddle->setPrev(mpMiddle); + mpMiddle->setNext(mpMiddle); + } + + /* + * This node plays the role of "pointer to head" and "pointer to tail". + * It sits in the middle of a circular list of nodes. The iterator + * runs around the circle until it encounters this one. + */ + _Node* mpMiddle; +}; + +/* + * Assignment operator. + * + * The simplest way to do this would be to clear out the target list and + * fill it with the source. However, we can speed things along by + * re-using existing elements. + */ +template +List& List::operator=(const List& right) +{ + if (this == &right) + return *this; // self-assignment + iterator firstDst = begin(); + iterator lastDst = end(); + const_iterator firstSrc = right.begin(); + const_iterator lastSrc = right.end(); + while (firstSrc != lastSrc && firstDst != lastDst) + *firstDst++ = *firstSrc++; + if (firstSrc == lastSrc) // ran out of elements in source? + erase(firstDst, lastDst); // yes, erase any extras + else + insert(lastDst, firstSrc, lastSrc); // copy remaining over + return *this; +} + +}; // namespace android + +#endif // _LIBS_UTILS_LIST_H diff --git a/include/utils/Log.h b/include/utils/Log.h new file mode 100644 index 000000000..3c6cc8bdc --- /dev/null +++ b/include/utils/Log.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2005 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. + */ + +// +// C/C++ logging functions. See the logging documentation for API details. +// +// We'd like these to be available from C code (in case we import some from +// somewhere), so this has a C interface. +// +// The output will be correct when the log file is shared between multiple +// threads and/or multiple processes so long as the operating system +// supports O_APPEND. These calls have mutex-protected data structures +// and so are NOT reentrant. Do not use LOG in a signal handler. +// +#ifndef _LIBS_UTILS_LOG_H +#define _LIBS_UTILS_LOG_H + +#include + +#endif // _LIBS_UTILS_LOG_H diff --git a/include/utils/LogSocket.h b/include/utils/LogSocket.h new file mode 100644 index 000000000..01fbfb50e --- /dev/null +++ b/include/utils/LogSocket.h @@ -0,0 +1,20 @@ +/* utils/LogSocket.h +** +** Copyright 2008, The Android Open Source Project +** +** This file is dual licensed. It may be redistributed and/or modified +** under the terms of the Apache 2.0 License OR version 2 of the GNU +** General Public License. +*/ + +#ifndef _UTILS_LOGSOCKET_H +#define _UTILS_LOGSOCKET_H + +#define SOCKET_CLOSE_LOCAL 0 + +void add_send_stats(int fd, int send); +void add_recv_stats(int fd, int recv); +void log_socket_close(int fd, short reason); +void log_socket_connect(int fd, unsigned int ip, unsigned short port); + +#endif /* _UTILS_LOGSOCKET_H */ diff --git a/include/utils/MemoryBase.h b/include/utils/MemoryBase.h new file mode 100644 index 000000000..eb5a9d275 --- /dev/null +++ b/include/utils/MemoryBase.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_MEMORY_BASE_H +#define ANDROID_MEMORY_BASE_H + +#include +#include + +#include + + +namespace android { + +// --------------------------------------------------------------------------- + +class MemoryBase : public BnMemory +{ +public: + MemoryBase(const sp& heap, ssize_t offset, size_t size); + virtual ~MemoryBase(); + virtual sp getMemory(ssize_t* offset, size_t* size) const; + +protected: + size_t getSize() const { return mSize; } + ssize_t getOffset() const { return mOffset; } + const sp& getHeap() const { return mHeap; } + +private: + size_t mSize; + ssize_t mOffset; + sp mHeap; +}; + +// --------------------------------------------------------------------------- +}; // namespace android + +#endif // ANDROID_MEMORY_BASE_H diff --git a/include/utils/MemoryDealer.h b/include/utils/MemoryDealer.h new file mode 100644 index 000000000..454b6270e --- /dev/null +++ b/include/utils/MemoryDealer.h @@ -0,0 +1,238 @@ +/* + * 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. + */ + +#ifndef ANDROID_MEMORY_DEALER_H +#define ANDROID_MEMORY_DEALER_H + + +#include +#include + +#include +#include +#include + +namespace android { +// ---------------------------------------------------------------------------- +class String8; + +/* + * interface for implementing a "heap". A heap basically provides + * the IMemoryHeap interface for cross-process sharing and the + * ability to map/unmap pages within the heap. + */ +class HeapInterface : public virtual BnMemoryHeap +{ +public: + // all values must be page-aligned + virtual sp mapMemory(size_t offset, size_t size) = 0; +}; + +// ---------------------------------------------------------------------------- + +/* + * interface for implementing an allocator. An allocator provides + * methods for allocating and freeing memory blocks and dumping + * its state. + */ +class AllocatorInterface : public RefBase +{ +public: + enum { + PAGE_ALIGNED = 0x00000001 + }; + + virtual size_t allocate(size_t size, uint32_t flags = 0) = 0; + virtual status_t deallocate(size_t offset) = 0; + virtual size_t size() const = 0; + virtual void dump(const char* what, uint32_t flags = 0) const = 0; + virtual void dump(String8& res, + const char* what, uint32_t flags = 0) const = 0; +}; + +// ---------------------------------------------------------------------------- + +/* + * concrete implementation of HeapInterface on top of mmap() + */ +class SharedHeap : public HeapInterface, public MemoryHeapBase +{ +public: + SharedHeap(size_t size, uint32_t flags = 0, char const * name = NULL); + virtual ~SharedHeap(); + virtual sp mapMemory(size_t offset, size_t size); +}; + +// ---------------------------------------------------------------------------- + +/* + * A simple templatized doubly linked-list implementation + */ + +template +class LinkedList +{ + NODE* mFirst; + NODE* mLast; + +public: + LinkedList() : mFirst(0), mLast(0) { } + bool isEmpty() const { return mFirst == 0; } + NODE const* head() const { return mFirst; } + NODE* head() { return mFirst; } + NODE const* tail() const { return mLast; } + NODE* tail() { return mLast; } + + void insertAfter(NODE* node, NODE* newNode) { + newNode->prev = node; + newNode->next = node->next; + if (node->next == 0) mLast = newNode; + else node->next->prev = newNode; + node->next = newNode; + } + + void insertBefore(NODE* node, NODE* newNode) { + newNode->prev = node->prev; + newNode->next = node; + if (node->prev == 0) mFirst = newNode; + else node->prev->next = newNode; + node->prev = newNode; + } + + void insertHead(NODE* newNode) { + if (mFirst == 0) { + mFirst = mLast = newNode; + newNode->prev = newNode->next = 0; + } else { + insertBefore(mFirst, newNode); + } + } + + void insertTail(NODE* newNode) { + if (mLast == 0) insertBeginning(newNode); + else insertAfter(mLast, newNode); + } + + NODE* remove(NODE* node) { + if (node->prev == 0) mFirst = node->next; + else node->prev->next = node->next; + if (node->next == 0) mLast = node->prev; + else node->next->prev = node->prev; + return node; + } +}; + + +/* + * concrete implementation of AllocatorInterface using a simple + * best-fit allocation scheme + */ +class SimpleBestFitAllocator : public AllocatorInterface +{ +public: + + SimpleBestFitAllocator(size_t size); + virtual ~SimpleBestFitAllocator(); + + virtual size_t allocate(size_t size, uint32_t flags = 0); + virtual status_t deallocate(size_t offset); + virtual size_t size() const; + virtual void dump(const char* what, uint32_t flags = 0) const; + virtual void dump(String8& res, + const char* what, uint32_t flags = 0) const; + +private: + + struct chunk_t { + chunk_t(size_t start, size_t size) + : start(start), size(size), free(1), prev(0), next(0) { + } + size_t start; + size_t size : 28; + int free : 4; + mutable chunk_t* prev; + mutable chunk_t* next; + }; + + ssize_t alloc(size_t size, uint32_t flags); + chunk_t* dealloc(size_t start); + void dump_l(const char* what, uint32_t flags = 0) const; + void dump_l(String8& res, const char* what, uint32_t flags = 0) const; + + static const int kMemoryAlign; + mutable Mutex mLock; + LinkedList mList; + size_t mHeapSize; +}; + +// ---------------------------------------------------------------------------- + +class MemoryDealer : public RefBase +{ +public: + + enum { + READ_ONLY = MemoryHeapBase::READ_ONLY, + PAGE_ALIGNED = AllocatorInterface::PAGE_ALIGNED + }; + + // creates a memory dealer with the SharedHeap and SimpleBestFitAllocator + MemoryDealer(size_t size, uint32_t flags = 0, const char* name = 0); + + // provide a custom heap but use the SimpleBestFitAllocator + MemoryDealer(const sp& heap); + + // provide both custom heap and allocotar + MemoryDealer( + const sp& heap, + const sp& allocator); + + virtual ~MemoryDealer(); + + virtual sp allocate(size_t size, uint32_t flags = 0); + virtual void deallocate(size_t offset); + virtual void dump(const char* what, uint32_t flags = 0) const; + + + sp getMemoryHeap() const { return heap(); } + sp getAllocator() const { return allocator(); } + +private: + const sp& heap() const; + const sp& allocator() const; + + class Allocation : public BnMemory { + public: + Allocation(const sp& dealer, + ssize_t offset, size_t size, const sp& memory); + virtual ~Allocation(); + virtual sp getMemory(ssize_t* offset, size_t* size) const; + private: + sp mDealer; + ssize_t mOffset; + size_t mSize; + sp mMemory; + }; + + sp mHeap; + sp mAllocator; +}; + + +// ---------------------------------------------------------------------------- +}; // namespace android + +#endif // ANDROID_MEMORY_DEALER_H diff --git a/include/utils/MemoryHeapBase.h b/include/utils/MemoryHeapBase.h new file mode 100644 index 000000000..ff8973878 --- /dev/null +++ b/include/utils/MemoryHeapBase.h @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_MEMORY_HEAP_BASE_H +#define ANDROID_MEMORY_HEAP_BASE_H + +#include +#include + +#include + + +namespace android { + +// --------------------------------------------------------------------------- + +class MemoryHeapBase : public virtual BnMemoryHeap +{ +public: + enum { + READ_ONLY = IMemoryHeap::READ_ONLY, + MAP_ONCE = IMemoryHeap::MAP_ONCE + }; + + /* + * maps the memory referenced by fd. but DOESN'T take ownership + * of the filedescriptor (it makes a copy with dup() + */ + MemoryHeapBase(int fd, size_t size, uint32_t flags = 0); + + /* + * maps memory from the given device + */ + MemoryHeapBase(const char* device, size_t size = 0, uint32_t flags = 0); + + /* + * maps memory from ashmem, with the given name for debugging + */ + MemoryHeapBase(size_t size, uint32_t flags = 0, char const* name = NULL); + + virtual ~MemoryHeapBase(); + + /* implement IMemoryHeap interface */ + virtual int getHeapID() const; + virtual void* getBase() const; + virtual size_t getSize() const; + virtual uint32_t getFlags() const; + + const char* getDevice() const; + + /* this closes this heap -- use carefully */ + void dispose(); + + /* this is only needed as a workaround, use only if you know + * what you are doing */ + status_t setDevice(const char* device) { + if (mDevice == 0) + mDevice = device; + return mDevice ? NO_ERROR : ALREADY_EXISTS; + } + +protected: + MemoryHeapBase(); + // init() takes ownership of fd + status_t init(int fd, void *base, int size, + int flags = 0, const char* device = NULL); + +private: + status_t mapfd(int fd, size_t size); + + int mFD; + size_t mSize; + void* mBase; + uint32_t mFlags; + const char* mDevice; + bool mNeedUnmap; +}; + +// --------------------------------------------------------------------------- +}; // namespace android + +#endif // ANDROID_MEMORY_HEAP_BASE_H diff --git a/include/utils/MemoryHeapPmem.h b/include/utils/MemoryHeapPmem.h new file mode 100644 index 000000000..b694b202a --- /dev/null +++ b/include/utils/MemoryHeapPmem.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_MEMORY_HEAP_PMEM_H +#define ANDROID_MEMORY_HEAP_PMEM_H + +#include +#include + +#include +#include +#include +#include + +namespace android { + +class MemoryHeapBase; + +// --------------------------------------------------------------------------- + +class SubRegionMemory; + +class MemoryHeapPmem : public HeapInterface, public MemoryHeapBase +{ +public: + MemoryHeapPmem(const sp& pmemHeap, + uint32_t flags = IMemoryHeap::MAP_ONCE); + ~MemoryHeapPmem(); + + /* HeapInterface additions */ + virtual sp mapMemory(size_t offset, size_t size); + + /* make the whole heap visible (you know who you are) */ + virtual status_t slap(); + + /* hide (revoke) the whole heap (the client will see the garbage page) */ + virtual status_t unslap(); + + /* revoke all allocations made by this heap */ + virtual void revoke(); + +private: + sp mParentHeap; + mutable Mutex mLock; + Vector< wp > mAllocations; +}; + + +// --------------------------------------------------------------------------- +}; // namespace android + +#endif // ANDROID_MEMORY_HEAP_PMEM_H diff --git a/include/utils/Parcel.h b/include/utils/Parcel.h new file mode 100644 index 000000000..7c451ab21 --- /dev/null +++ b/include/utils/Parcel.h @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2005 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_PARCEL_H +#define ANDROID_PARCEL_H + +#include +#include +#include +#include + +// --------------------------------------------------------------------------- +namespace android { + +class IBinder; +class ProcessState; +class String8; +class TextOutput; + +struct flat_binder_object; // defined in support_p/binder_module.h + +class Parcel +{ +public: + Parcel(); + ~Parcel(); + + const uint8_t* data() const; + size_t dataSize() const; + size_t dataAvail() const; + size_t dataPosition() const; + size_t dataCapacity() const; + + status_t setDataSize(size_t size); + void setDataPosition(size_t pos) const; + status_t setDataCapacity(size_t size); + + status_t setData(const uint8_t* buffer, size_t len); + + status_t appendFrom(Parcel *parcel, size_t start, size_t len); + + bool hasFileDescriptors() const; + + status_t writeInterfaceToken(const String16& interface); + bool enforceInterface(const String16& interface) const; + + void freeData(); + + const size_t* objects() const; + size_t objectsCount() const; + + status_t errorCheck() const; + void setError(status_t err); + + status_t write(const void* data, size_t len); + void* writeInplace(size_t len); + status_t writeUnpadded(const void* data, size_t len); + status_t writeInt32(int32_t val); + status_t writeInt64(int64_t val); + status_t writeFloat(float val); + status_t writeDouble(double val); + status_t writeCString(const char* str); + status_t writeString8(const String8& str); + status_t writeString16(const String16& str); + status_t writeString16(const char16_t* str, size_t len); + status_t writeStrongBinder(const sp& val); + status_t writeWeakBinder(const wp& val); + + // Place a file descriptor into the parcel. The given fd must remain + // valid for the lifetime of the parcel. + status_t writeFileDescriptor(int fd); + + // Place a file descriptor into the parcel. A dup of the fd is made, which + // will be closed once the parcel is destroyed. + status_t writeDupFileDescriptor(int fd); + + status_t writeObject(const flat_binder_object& val, bool nullMetaData); + + void remove(size_t start, size_t amt); + + status_t read(void* outData, size_t len) const; + const void* readInplace(size_t len) const; + int32_t readInt32() const; + status_t readInt32(int32_t *pArg) const; + int64_t readInt64() const; + status_t readInt64(int64_t *pArg) const; + float readFloat() const; + status_t readFloat(float *pArg) const; + double readDouble() const; + status_t readDouble(double *pArg) const; + + const char* readCString() const; + String8 readString8() const; + String16 readString16() const; + const char16_t* readString16Inplace(size_t* outLen) const; + sp readStrongBinder() const; + wp readWeakBinder() const; + + // Retrieve a file descriptor from the parcel. This returns the raw fd + // in the parcel, which you do not own -- use dup() to get your own copy. + int readFileDescriptor() const; + + const flat_binder_object* readObject(bool nullMetaData) const; + + // Explicitly close all file descriptors in the parcel. + void closeFileDescriptors(); + + typedef void (*release_func)(Parcel* parcel, + const uint8_t* data, size_t dataSize, + const size_t* objects, size_t objectsSize, + void* cookie); + + const uint8_t* ipcData() const; + size_t ipcDataSize() const; + const size_t* ipcObjects() const; + size_t ipcObjectsCount() const; + void ipcSetDataReference(const uint8_t* data, size_t dataSize, + const size_t* objects, size_t objectsCount, + release_func relFunc, void* relCookie); + + void print(TextOutput& to, uint32_t flags = 0) const; + +private: + Parcel(const Parcel& o); + Parcel& operator=(const Parcel& o); + + status_t finishWrite(size_t len); + void releaseObjects(); + void acquireObjects(); + status_t growData(size_t len); + status_t restartWrite(size_t desired); + status_t continueWrite(size_t desired); + void freeDataNoInit(); + void initState(); + void scanForFds() const; + + status_t mError; + uint8_t* mData; + size_t mDataSize; + size_t mDataCapacity; + mutable size_t mDataPos; + size_t* mObjects; + size_t mObjectsSize; + size_t mObjectsCapacity; + mutable size_t mNextObjectHint; + + mutable bool mFdsKnown; + mutable bool mHasFds; + + release_func mOwner; + void* mOwnerCookie; +}; + +// --------------------------------------------------------------------------- + +inline TextOutput& operator<<(TextOutput& to, const Parcel& parcel) +{ + parcel.print(to); + return to; +} + +// --------------------------------------------------------------------------- + +// Generic acquire and release of objects. +void acquire_object(const sp& proc, + const flat_binder_object& obj, const void* who); +void release_object(const sp& proc, + const flat_binder_object& obj, const void* who); + +void flatten_binder(const sp& proc, + const sp& binder, flat_binder_object* out); +void flatten_binder(const sp& proc, + const wp& binder, flat_binder_object* out); +status_t unflatten_binder(const sp& proc, + const flat_binder_object& flat, sp* out); +status_t unflatten_binder(const sp& proc, + const flat_binder_object& flat, wp* out); + +}; // namespace android + +// --------------------------------------------------------------------------- + +#endif // ANDROID_PARCEL_H diff --git a/include/utils/Pipe.h b/include/utils/Pipe.h new file mode 100644 index 000000000..6404168a2 --- /dev/null +++ b/include/utils/Pipe.h @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2005 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. + */ + +// +// FIFO I/O. +// +#ifndef _LIBS_UTILS_PIPE_H +#define _LIBS_UTILS_PIPE_H + +#ifdef HAVE_ANDROID_OS +#error DO NOT USE THIS FILE IN THE DEVICE BUILD +#endif + +namespace android { + +/* + * Simple anonymous unidirectional pipe. + * + * The primary goal is to create an implementation with minimal overhead + * under Linux. Making Windows, Mac OS X, and Linux all work the same way + * is a secondary goal. Part of this goal is to have something that can + * be fed to a select() call, so that the application can sleep in the + * kernel until something interesting happens. + */ +class Pipe { +public: + Pipe(void); + virtual ~Pipe(void); + + /* Create the pipe */ + bool create(void); + + /* Create a read-only pipe, using the supplied handle as read handle */ + bool createReader(unsigned long handle); + /* Create a write-only pipe, using the supplied handle as write handle */ + bool createWriter(unsigned long handle); + + /* Is this object ready to go? */ + bool isCreated(void); + + /* + * Read "count" bytes from the pipe. Returns the amount of data read, + * or 0 if no data available and we're non-blocking. + * Returns -1 on error. + */ + int read(void* buf, int count); + + /* + * Write "count" bytes into the pipe. Returns number of bytes written, + * or 0 if there's no room for more data and we're non-blocking. + * Returns -1 on error. + */ + int write(const void* buf, int count); + + /* Returns "true" if data is available to read */ + bool readReady(void); + + /* Enable or disable non-blocking I/O for reads */ + bool setReadNonBlocking(bool val); + /* Enable or disable non-blocking I/O for writes. Only works on Linux. */ + bool setWriteNonBlocking(bool val); + + /* + * Get the handle. Only useful in some platform-specific situations. + */ + unsigned long getReadHandle(void); + unsigned long getWriteHandle(void); + + /* + * Modify inheritance, i.e. whether or not a child process will get + * copies of the descriptors. Systems with fork+exec allow us to close + * the descriptors before launching the child process, but Win32 + * doesn't allow it. + */ + bool disallowReadInherit(void); + bool disallowWriteInherit(void); + + /* + * Close one side or the other. Useful in the parent after launching + * a child process. + */ + bool closeRead(void); + bool closeWrite(void); + +private: + bool mReadNonBlocking; + bool mWriteNonBlocking; + + unsigned long mReadHandle; + unsigned long mWriteHandle; +}; + +}; // android + +#endif // _LIBS_UTILS_PIPE_H diff --git a/include/utils/ProcessState.h b/include/utils/ProcessState.h new file mode 100644 index 000000000..39584f42c --- /dev/null +++ b/include/utils/ProcessState.h @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2005 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_PROCESS_STATE_H +#define ANDROID_PROCESS_STATE_H + +#include +#include +#include +#include + +#include + +// --------------------------------------------------------------------------- +namespace android { + +// Global variables +extern int mArgC; +extern const char* const* mArgV; +extern int mArgLen; + +class IPCThreadState; + +class ProcessState : public virtual RefBase +{ +public: + static sp self(); + + static void setSingleProcess(bool singleProcess); + + void setContextObject(const sp& object); + sp getContextObject(const sp& caller); + + void setContextObject(const sp& object, + const String16& name); + sp getContextObject(const String16& name, + const sp& caller); + + bool supportsProcesses() const; + + void startThreadPool(); + + typedef bool (*context_check_func)(const String16& name, + const sp& caller, + void* userData); + + bool isContextManager(void) const; + bool becomeContextManager( + context_check_func checkFunc, + void* userData); + + sp getStrongProxyForHandle(int32_t handle); + wp getWeakProxyForHandle(int32_t handle); + void expungeHandle(int32_t handle, IBinder* binder); + + void setArgs(int argc, const char* const argv[]); + int getArgC() const; + const char* const* getArgV() const; + + void setArgV0(const char* txt); + + void spawnPooledThread(bool isMain); + +private: + friend class IPCThreadState; + + ProcessState(); + ~ProcessState(); + + ProcessState(const ProcessState& o); + ProcessState& operator=(const ProcessState& o); + + struct handle_entry { + IBinder* binder; + RefBase::weakref_type* refs; + }; + + handle_entry* lookupHandleLocked(int32_t handle); + + int mDriverFD; + void* mVMStart; + + mutable Mutex mLock; // protects everything below. + + VectormHandleToObject; + + bool mManagesContexts; + context_check_func mBinderContextCheckFunc; + void* mBinderContextUserData; + + KeyedVector > + mContexts; + + + String8 mRootDir; + bool mThreadPoolStarted; + volatile int32_t mThreadPoolSeq; +}; + +}; // namespace android + +// --------------------------------------------------------------------------- + +#endif // ANDROID_PROCESS_STATE_H diff --git a/include/utils/RefBase.h b/include/utils/RefBase.h new file mode 100644 index 000000000..e37b56f5b --- /dev/null +++ b/include/utils/RefBase.h @@ -0,0 +1,526 @@ +/* + * Copyright (C) 2005 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_REF_BASE_H +#define ANDROID_REF_BASE_H + +#include + +#include +#include +#include + +// --------------------------------------------------------------------------- +namespace android { + +template class wp; + +// --------------------------------------------------------------------------- + +#define COMPARE(_op_) \ +inline bool operator _op_ (const sp& o) const { \ + return m_ptr _op_ o.m_ptr; \ +} \ +inline bool operator _op_ (const wp& o) const { \ + return m_ptr _op_ o.m_ptr; \ +} \ +inline bool operator _op_ (const T* o) const { \ + return m_ptr _op_ o; \ +} \ +template \ +inline bool operator _op_ (const sp& o) const { \ + return m_ptr _op_ o.m_ptr; \ +} \ +template \ +inline bool operator _op_ (const wp& o) const { \ + return m_ptr _op_ o.m_ptr; \ +} \ +template \ +inline bool operator _op_ (const U* o) const { \ + return m_ptr _op_ o; \ +} + +// --------------------------------------------------------------------------- + +class RefBase +{ +public: + void incStrong(const void* id) const; + void decStrong(const void* id) const; + + void forceIncStrong(const void* id) const; + + //! DEBUGGING ONLY: Get current strong ref count. + int32_t getStrongCount() const; + + class weakref_type + { + public: + RefBase* refBase() const; + + void incWeak(const void* id); + void decWeak(const void* id); + + bool attemptIncStrong(const void* id); + + //! This is only safe if you have set OBJECT_LIFETIME_FOREVER. + bool attemptIncWeak(const void* id); + + //! DEBUGGING ONLY: Get current weak ref count. + int32_t getWeakCount() const; + + //! DEBUGGING ONLY: Print references held on object. + void printRefs() const; + + //! DEBUGGING ONLY: Enable tracking for this object. + // enable -- enable/disable tracking + // retain -- when tracking is enable, if true, then we save a stack trace + // for each reference and dereference; when retain == false, we + // match up references and dereferences and keep only the + // outstanding ones. + + void trackMe(bool enable, bool retain); + }; + + weakref_type* createWeak(const void* id) const; + + weakref_type* getWeakRefs() const; + + //! DEBUGGING ONLY: Print references held on object. + inline void printRefs() const { getWeakRefs()->printRefs(); } + + //! DEBUGGING ONLY: Enable tracking of object. + inline void trackMe(bool enable, bool retain) + { + getWeakRefs()->trackMe(enable, retain); + } + +protected: + RefBase(); + virtual ~RefBase(); + + //! Flags for extendObjectLifetime() + enum { + OBJECT_LIFETIME_WEAK = 0x0001, + OBJECT_LIFETIME_FOREVER = 0x0003 + }; + + void extendObjectLifetime(int32_t mode); + + //! Flags for onIncStrongAttempted() + enum { + FIRST_INC_STRONG = 0x0001 + }; + + virtual void onFirstRef(); + virtual void onLastStrongRef(const void* id); + virtual bool onIncStrongAttempted(uint32_t flags, const void* id); + virtual void onLastWeakRef(const void* id); + +private: + friend class weakref_type; + class weakref_impl; + + RefBase(const RefBase& o); + RefBase& operator=(const RefBase& o); + + weakref_impl* const mRefs; +}; + +// --------------------------------------------------------------------------- + +template +class sp +{ +public: + typedef typename RefBase::weakref_type weakref_type; + + inline sp() : m_ptr(0) { } + + sp(T* other); + sp(const sp& other); + template sp(U* other); + template sp(const sp& other); + + ~sp(); + + // Assignment + + sp& operator = (T* other); + sp& operator = (const sp& other); + + template sp& operator = (const sp& other); + template sp& operator = (U* other); + + //! Special optimization for use by ProcessState (and nobody else). + void force_set(T* other); + + // Reset + + void clear(); + + // Accessors + + inline T& operator* () const { return *m_ptr; } + inline T* operator-> () const { return m_ptr; } + inline T* get() const { return m_ptr; } + + // Operators + + COMPARE(==) + COMPARE(!=) + COMPARE(>) + COMPARE(<) + COMPARE(<=) + COMPARE(>=) + +private: + template friend class sp; + template friend class wp; + + // Optimization for wp::promote(). + sp(T* p, weakref_type* refs); + + T* m_ptr; +}; + +template +TextOutput& operator<<(TextOutput& to, const sp& val); + +// --------------------------------------------------------------------------- + +template +class wp +{ +public: + typedef typename RefBase::weakref_type weakref_type; + + inline wp() : m_ptr(0) { } + + wp(T* other); + wp(const wp& other); + wp(const sp& other); + template wp(U* other); + template wp(const sp& other); + template wp(const wp& other); + + ~wp(); + + // Assignment + + wp& operator = (T* other); + wp& operator = (const wp& other); + wp& operator = (const sp& other); + + template wp& operator = (U* other); + template wp& operator = (const wp& other); + template wp& operator = (const sp& other); + + void set_object_and_refs(T* other, weakref_type* refs); + + // promotion to sp + + sp promote() const; + + // Reset + + void clear(); + + // Accessors + + inline weakref_type* get_refs() const { return m_refs; } + + inline T* unsafe_get() const { return m_ptr; } + + // Operators + + COMPARE(==) + COMPARE(!=) + COMPARE(>) + COMPARE(<) + COMPARE(<=) + COMPARE(>=) + +private: + template friend class sp; + template friend class wp; + + T* m_ptr; + weakref_type* m_refs; +}; + +template +TextOutput& operator<<(TextOutput& to, const wp& val); + +#undef COMPARE + +// --------------------------------------------------------------------------- +// No user serviceable parts below here. + +template +sp::sp(T* other) + : m_ptr(other) +{ + if (other) other->incStrong(this); +} + +template +sp::sp(const sp& other) + : m_ptr(other.m_ptr) +{ + if (m_ptr) m_ptr->incStrong(this); +} + +template template +sp::sp(U* other) : m_ptr(other) +{ + if (other) other->incStrong(this); +} + +template template +sp::sp(const sp& other) + : m_ptr(other.m_ptr) +{ + if (m_ptr) m_ptr->incStrong(this); +} + +template +sp::~sp() +{ + if (m_ptr) m_ptr->decStrong(this); +} + +template +sp& sp::operator = (const sp& other) { + if (other.m_ptr) other.m_ptr->incStrong(this); + if (m_ptr) m_ptr->decStrong(this); + m_ptr = other.m_ptr; + return *this; +} + +template +sp& sp::operator = (T* other) +{ + if (other) other->incStrong(this); + if (m_ptr) m_ptr->decStrong(this); + m_ptr = other; + return *this; +} + +template template +sp& sp::operator = (const sp& other) +{ + if (other.m_ptr) other.m_ptr->incStrong(this); + if (m_ptr) m_ptr->decStrong(this); + m_ptr = other.m_ptr; + return *this; +} + +template template +sp& sp::operator = (U* other) +{ + if (other) other->incStrong(this); + if (m_ptr) m_ptr->decStrong(this); + m_ptr = other; + return *this; +} + +template +void sp::force_set(T* other) +{ + other->forceIncStrong(this); + m_ptr = other; +} + +template +void sp::clear() +{ + if (m_ptr) { + m_ptr->decStrong(this); + m_ptr = 0; + } +} + +template +sp::sp(T* p, weakref_type* refs) + : m_ptr((p && refs->attemptIncStrong(this)) ? p : 0) +{ +} + +template +inline TextOutput& operator<<(TextOutput& to, const sp& val) +{ + to << "sp<>(" << val.get() << ")"; + return to; +} + +// --------------------------------------------------------------------------- + +template +wp::wp(T* other) + : m_ptr(other) +{ + if (other) m_refs = other->createWeak(this); +} + +template +wp::wp(const wp& other) + : m_ptr(other.m_ptr), m_refs(other.m_refs) +{ + if (m_ptr) m_refs->incWeak(this); +} + +template +wp::wp(const sp& other) + : m_ptr(other.m_ptr) +{ + if (m_ptr) { + m_refs = m_ptr->createWeak(this); + } +} + +template template +wp::wp(U* other) + : m_ptr(other) +{ + if (other) m_refs = other->createWeak(this); +} + +template template +wp::wp(const wp& other) + : m_ptr(other.m_ptr) +{ + if (m_ptr) { + m_refs = other.m_refs; + m_refs->incWeak(this); + } +} + +template template +wp::wp(const sp& other) + : m_ptr(other.m_ptr) +{ + if (m_ptr) { + m_refs = m_ptr->createWeak(this); + } +} + +template +wp::~wp() +{ + if (m_ptr) m_refs->decWeak(this); +} + +template +wp& wp::operator = (T* other) +{ + weakref_type* newRefs = + other ? other->createWeak(this) : 0; + if (m_ptr) m_refs->decWeak(this); + m_ptr = other; + m_refs = newRefs; + return *this; +} + +template +wp& wp::operator = (const wp& other) +{ + if (other.m_ptr) other.m_refs->incWeak(this); + if (m_ptr) m_refs->decWeak(this); + m_ptr = other.m_ptr; + m_refs = other.m_refs; + return *this; +} + +template +wp& wp::operator = (const sp& other) +{ + weakref_type* newRefs = + other != NULL ? other->createWeak(this) : 0; + if (m_ptr) m_refs->decWeak(this); + m_ptr = other.get(); + m_refs = newRefs; + return *this; +} + +template template +wp& wp::operator = (U* other) +{ + weakref_type* newRefs = + other ? other->createWeak(this) : 0; + if (m_ptr) m_refs->decWeak(this); + m_ptr = other; + m_refs = newRefs; + return *this; +} + +template template +wp& wp::operator = (const wp& other) +{ + if (other.m_ptr) other.m_refs->incWeak(this); + if (m_ptr) m_refs->decWeak(this); + m_ptr = other.m_ptr; + m_refs = other.m_refs; + return *this; +} + +template template +wp& wp::operator = (const sp& other) +{ + weakref_type* newRefs = + other != NULL ? other->createWeak(this) : 0; + if (m_ptr) m_refs->decWeak(this); + m_ptr = other.get(); + m_refs = newRefs; + return *this; +} + +template +void wp::set_object_and_refs(T* other, weakref_type* refs) +{ + if (other) refs->incWeak(this); + if (m_ptr) m_refs->decWeak(this); + m_ptr = other; + m_refs = refs; +} + +template +sp wp::promote() const +{ + return sp(m_ptr, m_refs); +} + +template +void wp::clear() +{ + if (m_ptr) { + m_refs->decWeak(this); + m_ptr = 0; + } +} + +template +inline TextOutput& operator<<(TextOutput& to, const wp& val) +{ + to << "wp<>(" << val.unsafe_get() << ")"; + return to; +} + +}; // namespace android + +// --------------------------------------------------------------------------- + +#endif // ANDROID_REF_BASE_H diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h new file mode 100644 index 000000000..31b9aa84b --- /dev/null +++ b/include/utils/ResourceTypes.h @@ -0,0 +1,1685 @@ +/* + * Copyright (C) 2005 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. + */ + +// +// Definitions of resource data structures. +// +#ifndef _LIBS_UTILS_RESOURCE_TYPES_H +#define _LIBS_UTILS_RESOURCE_TYPES_H + +#include +#include +#include +#include +#include + +#include + +#include +#include + +namespace android { + +/** ******************************************************************** + * PNG Extensions + * + * New private chunks that may be placed in PNG images. + * + *********************************************************************** */ + +/** + * This chunk specifies how to split an image into segments for + * scaling. + * + * There are J horizontal and K vertical segments. These segments divide + * the image into J*K regions as follows (where J=4 and K=3): + * + * F0 S0 F1 S1 + * +-----+----+------+-------+ + * S2| 0 | 1 | 2 | 3 | + * +-----+----+------+-------+ + * | | | | | + * | | | | | + * F2| 4 | 5 | 6 | 7 | + * | | | | | + * | | | | | + * +-----+----+------+-------+ + * S3| 8 | 9 | 10 | 11 | + * +-----+----+------+-------+ + * + * Each horizontal and vertical segment is considered to by either + * stretchable (marked by the Sx labels) or fixed (marked by the Fy + * labels), in the horizontal or vertical axis, respectively. In the + * above example, the first is horizontal segment (F0) is fixed, the + * next is stretchable and then they continue to alternate. Note that + * the segment list for each axis can begin or end with a stretchable + * or fixed segment. + * + * The relative sizes of the stretchy segments indicates the relative + * amount of stretchiness of the regions bordered by the segments. For + * example, regions 3, 7 and 11 above will take up more horizontal space + * than regions 1, 5 and 9 since the horizonal segment associated with + * the first set of regions is larger than the other set of regions. The + * ratios of the amount of horizontal (or vertical) space taken by any + * two stretchable slices is exactly the ratio of their corresponding + * segment lengths. + * + * xDivs and yDivs point to arrays of horizontal and vertical pixel + * indices. The first pair of Divs (in either array) indicate the + * starting and ending points of the first stretchable segment in that + * axis. The next pair specifies the next stretchable segment, etc. So + * in the above example xDiv[0] and xDiv[1] specify the horizontal + * coordinates for the regions labeled 1, 5 and 9. xDiv[2] and + * xDiv[3] specify the coordinates for regions 3, 7 and 11. Note that + * the leftmost slices always start at x=0 and the rightmost slices + * always end at the end of the image. So, for example, the regions 0, + * 4 and 8 (which are fixed along the X axis) start at x value 0 and + * go to xDiv[0] amd slices 2, 6 and 10 start at xDiv[1] and end at + * xDiv[2]. + * + * The array pointed to by the colors field lists contains hints for + * each of the regions. They are ordered according left-to-right and + * top-to-bottom as indicated above. For each segment that is a solid + * color the array entry will contain that color value; otherwise it + * will contain NO_COLOR. Segments that are completely transparent + * will always have the value TRANSPARENT_COLOR. + * + * The PNG chunk type is "npTc". + */ +struct Res_png_9patch +{ + Res_png_9patch() : wasDeserialized(false), xDivs(NULL), + yDivs(NULL), colors(NULL) { } + + int8_t wasDeserialized; + int8_t numXDivs; + int8_t numYDivs; + int8_t numColors; + + // These tell where the next section of a patch starts. + // For example, the first patch includes the pixels from + // 0 to xDivs[0]-1 and the second patch includes the pixels + // from xDivs[0] to xDivs[1]-1. + // Note: allocation/free of these pointers is left to the caller. + int32_t* xDivs; + int32_t* yDivs; + + int32_t paddingLeft, paddingRight; + int32_t paddingTop, paddingBottom; + + enum { + // The 9 patch segment is not a solid color. + NO_COLOR = 0x00000001, + + // The 9 patch segment is completely transparent. + TRANSPARENT_COLOR = 0x00000000 + }; + // Note: allocation/free of this pointer is left to the caller. + uint32_t* colors; + + // Convert data from device representation to PNG file representation. + void deviceToFile(); + // Convert data from PNG file representation to device representation. + void fileToDevice(); + // Serialize/Marshall the patch data into a newly malloc-ed block + void* serialize(); + // Serialize/Marshall the patch data + void serialize(void* outData); + // Deserialize/Unmarshall the patch data + static Res_png_9patch* deserialize(const void* data); + // Deserialize/Unmarshall the patch data into a newly malloc-ed block + static void deserialize(const void* data, Res_png_9patch* outData); + // Compute the size of the serialized data structure + size_t serializedSize(); +}; + +/** ******************************************************************** + * Base Types + * + * These are standard types that are shared between multiple specific + * resource types. + * + *********************************************************************** */ + +/** + * Header that appears at the front of every data chunk in a resource. + */ +struct ResChunk_header +{ + // Type identifier for this chunk. The meaning of this value depends + // on the containing chunk. + uint16_t type; + + // Size of the chunk header (in bytes). Adding this value to + // the address of the chunk allows you to find its associated data + // (if any). + uint16_t headerSize; + + // Total size of this chunk (in bytes). This is the chunkSize plus + // the size of any data associated with the chunk. Adding this value + // to the chunk allows you to completely skip its contents (including + // any child chunks). If this value is the same as chunkSize, there is + // no data associated with the chunk. + uint32_t size; +}; + +enum { + RES_NULL_TYPE = 0x0000, + RES_STRING_POOL_TYPE = 0x0001, + RES_TABLE_TYPE = 0x0002, + RES_XML_TYPE = 0x0003, + + // Chunk types in RES_XML_TYPE + RES_XML_FIRST_CHUNK_TYPE = 0x0100, + RES_XML_START_NAMESPACE_TYPE= 0x0100, + RES_XML_END_NAMESPACE_TYPE = 0x0101, + RES_XML_START_ELEMENT_TYPE = 0x0102, + RES_XML_END_ELEMENT_TYPE = 0x0103, + RES_XML_CDATA_TYPE = 0x0104, + RES_XML_LAST_CHUNK_TYPE = 0x017f, + // This contains a uint32_t array mapping strings in the string + // pool back to resource identifiers. It is optional. + RES_XML_RESOURCE_MAP_TYPE = 0x0180, + + // Chunk types in RES_TABLE_TYPE + RES_TABLE_PACKAGE_TYPE = 0x0200, + RES_TABLE_TYPE_TYPE = 0x0201, + RES_TABLE_TYPE_SPEC_TYPE = 0x0202 +}; + +/** + * Macros for building/splitting resource identifiers. + */ +#define Res_VALIDID(resid) (resid != 0) +#define Res_CHECKID(resid) ((resid&0xFFFF0000) != 0) +#define Res_MAKEID(package, type, entry) \ + (((package+1)<<24) | (((type+1)&0xFF)<<16) | (entry&0xFFFF)) +#define Res_GETPACKAGE(id) ((id>>24)-1) +#define Res_GETTYPE(id) (((id>>16)&0xFF)-1) +#define Res_GETENTRY(id) (id&0xFFFF) + +#define Res_INTERNALID(resid) ((resid&0xFFFF0000) != 0 && (resid&0xFF0000) == 0) +#define Res_MAKEINTERNAL(entry) (0x01000000 | (entry&0xFFFF)) +#define Res_MAKEARRAY(entry) (0x02000000 | (entry&0xFFFF)) + +#define Res_MAXPACKAGE 255 + +/** + * Representation of a value in a resource, supplying type + * information. + */ +struct Res_value +{ + // Number of bytes in this structure. + uint16_t size; + + // Always set to 0. + uint8_t res0; + + // Type of the data value. + enum { + // Contains no data. + TYPE_NULL = 0x00, + // The 'data' holds a ResTable_ref, a reference to another resource + // table entry. + TYPE_REFERENCE = 0x01, + // The 'data' holds an attribute resource identifier. + TYPE_ATTRIBUTE = 0x02, + // The 'data' holds an index into the containing resource table's + // global value string pool. + TYPE_STRING = 0x03, + // The 'data' holds a single-precision floating point number. + TYPE_FLOAT = 0x04, + // The 'data' holds a complex number encoding a dimension value, + // such as "100in". + TYPE_DIMENSION = 0x05, + // The 'data' holds a complex number encoding a fraction of a + // container. + TYPE_FRACTION = 0x06, + + // Beginning of integer flavors... + TYPE_FIRST_INT = 0x10, + + // The 'data' is a raw integer value of the form n..n. + TYPE_INT_DEC = 0x10, + // The 'data' is a raw integer value of the form 0xn..n. + TYPE_INT_HEX = 0x11, + // The 'data' is either 0 or 1, for input "false" or "true" respectively. + TYPE_INT_BOOLEAN = 0x12, + + // Beginning of color integer flavors... + TYPE_FIRST_COLOR_INT = 0x1c, + + // The 'data' is a raw integer value of the form #aarrggbb. + TYPE_INT_COLOR_ARGB8 = 0x1c, + // The 'data' is a raw integer value of the form #rrggbb. + TYPE_INT_COLOR_RGB8 = 0x1d, + // The 'data' is a raw integer value of the form #argb. + TYPE_INT_COLOR_ARGB4 = 0x1e, + // The 'data' is a raw integer value of the form #rgb. + TYPE_INT_COLOR_RGB4 = 0x1f, + + // ...end of integer flavors. + TYPE_LAST_COLOR_INT = 0x1f, + + // ...end of integer flavors. + TYPE_LAST_INT = 0x1f + }; + uint8_t dataType; + + // Structure of complex data values (TYPE_UNIT and TYPE_FRACTION) + enum { + // Where the unit type information is. This gives us 16 possible + // types, as defined below. + COMPLEX_UNIT_SHIFT = 0, + COMPLEX_UNIT_MASK = 0xf, + + // TYPE_DIMENSION: Value is raw pixels. + COMPLEX_UNIT_PX = 0, + // TYPE_DIMENSION: Value is Device Independent Pixels. + COMPLEX_UNIT_DIP = 1, + // TYPE_DIMENSION: Value is a Scaled device independent Pixels. + COMPLEX_UNIT_SP = 2, + // TYPE_DIMENSION: Value is in points. + COMPLEX_UNIT_PT = 3, + // TYPE_DIMENSION: Value is in inches. + COMPLEX_UNIT_IN = 4, + // TYPE_DIMENSION: Value is in millimeters. + COMPLEX_UNIT_MM = 5, + + // TYPE_FRACTION: A basic fraction of the overall size. + COMPLEX_UNIT_FRACTION = 0, + // TYPE_FRACTION: A fraction of the parent size. + COMPLEX_UNIT_FRACTION_PARENT = 1, + + // Where the radix information is, telling where the decimal place + // appears in the mantissa. This give us 4 possible fixed point + // representations as defined below. + COMPLEX_RADIX_SHIFT = 4, + COMPLEX_RADIX_MASK = 0x3, + + // The mantissa is an integral number -- i.e., 0xnnnnnn.0 + COMPLEX_RADIX_23p0 = 0, + // The mantissa magnitude is 16 bits -- i.e, 0xnnnn.nn + COMPLEX_RADIX_16p7 = 1, + // The mantissa magnitude is 8 bits -- i.e, 0xnn.nnnn + COMPLEX_RADIX_8p15 = 2, + // The mantissa magnitude is 0 bits -- i.e, 0x0.nnnnnn + COMPLEX_RADIX_0p23 = 3, + + // Where the actual value is. This gives us 23 bits of + // precision. The top bit is the sign. + COMPLEX_MANTISSA_SHIFT = 8, + COMPLEX_MANTISSA_MASK = 0xffffff + }; + + // The data for this item, as interpreted according to dataType. + uint32_t data; + + void copyFrom_dtoh(const Res_value& src); +}; + +/** + * This is a reference to a unique entry (a ResTable_entry structure) + * in a resource table. The value is structured as: 0xpptteeee, + * where pp is the package index, tt is the type index in that + * package, and eeee is the entry index in that type. The package + * and type values start at 1 for the first item, to help catch cases + * where they have not been supplied. + */ +struct ResTable_ref +{ + uint32_t ident; +}; + +/** + * Reference to a string in a string pool. + */ +struct ResStringPool_ref +{ + // Index into the string pool table (uint32_t-offset from the indices + // immediately after ResStringPool_header) at which to find the location + // of the string data in the pool. + uint32_t index; +}; + +/** ******************************************************************** + * String Pool + * + * A set of strings that can be references by others through a + * ResStringPool_ref. + * + *********************************************************************** */ + +/** + * Definition for a pool of strings. The data of this chunk is an + * array of uint32_t providing indices into the pool, relative to + * stringsStart. At stringsStart are all of the UTF-16 strings + * concatenated together; each starts with a uint16_t of the string's + * length and each ends with a 0x0000 terminator. If a string is > + * 32767 characters, the high bit of the length is set meaning to take + * those 15 bits as a high word and it will be followed by another + * uint16_t containing the low word. + * + * If styleCount is not zero, then immediately following the array of + * uint32_t indices into the string table is another array of indices + * into a style table starting at stylesStart. Each entry in the + * style table is an array of ResStringPool_span structures. + */ +struct ResStringPool_header +{ + struct ResChunk_header header; + + // Number of strings in this pool (number of uint32_t indices that follow + // in the data). + uint32_t stringCount; + + // Number of style span arrays in the pool (number of uint32_t indices + // follow the string indices). + uint32_t styleCount; + + // Flags. + enum { + // If set, the string index is sorted by the string values (based + // on strcmp16()). + SORTED_FLAG = 1<<0 + }; + uint32_t flags; + + // Index from header of the string data. + uint32_t stringsStart; + + // Index from header of the style data. + uint32_t stylesStart; +}; + +/** + * This structure defines a span of style information associated with + * a string in the pool. + */ +struct ResStringPool_span +{ + enum { + END = 0xFFFFFFFF + }; + + // This is the name of the span -- that is, the name of the XML + // tag that defined it. The special value END (0xFFFFFFFF) indicates + // the end of an array of spans. + ResStringPool_ref name; + + // The range of characters in the string that this span applies to. + uint32_t firstChar, lastChar; +}; + +/** + * Convenience class for accessing data in a ResStringPool resource. + */ +class ResStringPool +{ +public: + ResStringPool(); + ResStringPool(const void* data, size_t size, bool copyData=false); + ~ResStringPool(); + + status_t setTo(const void* data, size_t size, bool copyData=false); + + status_t getError() const; + + void uninit(); + + inline const char16_t* stringAt(const ResStringPool_ref& ref, size_t* outLen) const { + return stringAt(ref.index, outLen); + } + const char16_t* stringAt(size_t idx, size_t* outLen) const; + + const ResStringPool_span* styleAt(const ResStringPool_ref& ref) const; + const ResStringPool_span* styleAt(size_t idx) const; + + ssize_t indexOfString(const char16_t* str, size_t strLen) const; + + size_t size() const; + +private: + status_t mError; + void* mOwnedData; + const ResStringPool_header* mHeader; + size_t mSize; + const uint32_t* mEntries; + const uint32_t* mEntryStyles; + const char16_t* mStrings; + uint32_t mStringPoolSize; // number of uint16_t + const uint32_t* mStyles; + uint32_t mStylePoolSize; // number of uint32_t +}; + +/** ******************************************************************** + * XML Tree + * + * Binary representation of an XML document. This is designed to + * express everything in an XML document, in a form that is much + * easier to parse on the device. + * + *********************************************************************** */ + +/** + * XML tree header. This appears at the front of an XML tree, + * describing its content. It is followed by a flat array of + * ResXMLTree_node structures; the hierarchy of the XML document + * is described by the occurrance of RES_XML_START_ELEMENT_TYPE + * and corresponding RES_XML_END_ELEMENT_TYPE nodes in the array. + */ +struct ResXMLTree_header +{ + struct ResChunk_header header; +}; + +/** + * Basic XML tree node. A single item in the XML document. Extended info + * about the node can be found after header.headerSize. + */ +struct ResXMLTree_node +{ + struct ResChunk_header header; + + // Line number in original source file at which this element appeared. + uint32_t lineNumber; + + // Optional XML comment that was associated with this element; -1 if none. + struct ResStringPool_ref comment; +}; + +/** + * Extended XML tree node for CDATA tags -- includes the CDATA string. + * Appears header.headerSize bytes after a ResXMLTree_node. + */ +struct ResXMLTree_cdataExt +{ + // The raw CDATA character data. + struct ResStringPool_ref data; + + // The typed value of the character data if this is a CDATA node. + struct Res_value typedData; +}; + +/** + * Extended XML tree node for namespace start/end nodes. + * Appears header.headerSize bytes after a ResXMLTree_node. + */ +struct ResXMLTree_namespaceExt +{ + // The prefix of the namespace. + struct ResStringPool_ref prefix; + + // The URI of the namespace. + struct ResStringPool_ref uri; +}; + +/** + * Extended XML tree node for element start/end nodes. + * Appears header.headerSize bytes after a ResXMLTree_node. + */ +struct ResXMLTree_endElementExt +{ + // String of the full namespace of this element. + struct ResStringPool_ref ns; + + // String name of this node if it is an ELEMENT; the raw + // character data if this is a CDATA node. + struct ResStringPool_ref name; +}; + +/** + * Extended XML tree node for start tags -- includes attribute + * information. + * Appears header.headerSize bytes after a ResXMLTree_node. + */ +struct ResXMLTree_attrExt +{ + // String of the full namespace of this element. + struct ResStringPool_ref ns; + + // String name of this node if it is an ELEMENT; the raw + // character data if this is a CDATA node. + struct ResStringPool_ref name; + + // Byte offset from the start of this structure where the attributes start. + uint16_t attributeStart; + + // Size of the ResXMLTree_attribute structures that follow. + uint16_t attributeSize; + + // Number of attributes associated with an ELEMENT. These are + // available as an array of ResXMLTree_attribute structures + // immediately following this node. + uint16_t attributeCount; + + // Index (1-based) of the "id" attribute. 0 if none. + uint16_t idIndex; + + // Index (1-based) of the "class" attribute. 0 if none. + uint16_t classIndex; + + // Index (1-based) of the "style" attribute. 0 if none. + uint16_t styleIndex; +}; + +struct ResXMLTree_attribute +{ + // Namespace of this attribute. + struct ResStringPool_ref ns; + + // Name of this attribute. + struct ResStringPool_ref name; + + // The original raw string value of this attribute. + struct ResStringPool_ref rawValue; + + // Processesd typed value of this attribute. + struct Res_value typedValue; +}; + +class ResXMLTree; + +class ResXMLParser +{ +public: + ResXMLParser(const ResXMLTree& tree); + + enum event_code_t { + BAD_DOCUMENT = -1, + START_DOCUMENT = 0, + END_DOCUMENT = 1, + + FIRST_CHUNK_CODE = RES_XML_FIRST_CHUNK_TYPE, + + START_NAMESPACE = RES_XML_START_NAMESPACE_TYPE, + END_NAMESPACE = RES_XML_END_NAMESPACE_TYPE, + START_TAG = RES_XML_START_ELEMENT_TYPE, + END_TAG = RES_XML_END_ELEMENT_TYPE, + TEXT = RES_XML_CDATA_TYPE + }; + + struct ResXMLPosition + { + event_code_t eventCode; + const ResXMLTree_node* curNode; + const void* curExt; + }; + + void restart(); + + event_code_t getEventType() const; + // Note, unlike XmlPullParser, the first call to next() will return + // START_TAG of the first element. + event_code_t next(); + + // These are available for all nodes: + const int32_t getCommentID() const; + const uint16_t* getComment(size_t* outLen) const; + const uint32_t getLineNumber() const; + + // This is available for TEXT: + const int32_t getTextID() const; + const uint16_t* getText(size_t* outLen) const; + ssize_t getTextValue(Res_value* outValue) const; + + // These are available for START_NAMESPACE and END_NAMESPACE: + const int32_t getNamespacePrefixID() const; + const uint16_t* getNamespacePrefix(size_t* outLen) const; + const int32_t getNamespaceUriID() const; + const uint16_t* getNamespaceUri(size_t* outLen) const; + + // These are available for START_TAG and END_TAG: + const int32_t getElementNamespaceID() const; + const uint16_t* getElementNamespace(size_t* outLen) const; + const int32_t getElementNameID() const; + const uint16_t* getElementName(size_t* outLen) const; + + // Remaining methods are for retrieving information about attributes + // associated with a START_TAG: + + size_t getAttributeCount() const; + + // Returns -1 if no namespace, -2 if idx out of range. + const int32_t getAttributeNamespaceID(size_t idx) const; + const uint16_t* getAttributeNamespace(size_t idx, size_t* outLen) const; + + const int32_t getAttributeNameID(size_t idx) const; + const uint16_t* getAttributeName(size_t idx, size_t* outLen) const; + const uint32_t getAttributeNameResID(size_t idx) const; + + const int32_t getAttributeValueStringID(size_t idx) const; + const uint16_t* getAttributeStringValue(size_t idx, size_t* outLen) const; + + int32_t getAttributeDataType(size_t idx) const; + int32_t getAttributeData(size_t idx) const; + ssize_t getAttributeValue(size_t idx, Res_value* outValue) const; + + ssize_t indexOfAttribute(const char* ns, const char* attr) const; + ssize_t indexOfAttribute(const char16_t* ns, size_t nsLen, + const char16_t* attr, size_t attrLen) const; + + ssize_t indexOfID() const; + ssize_t indexOfClass() const; + ssize_t indexOfStyle() const; + + void getPosition(ResXMLPosition* pos) const; + void setPosition(const ResXMLPosition& pos); + +private: + friend class ResXMLTree; + + event_code_t nextNode(); + + const ResXMLTree& mTree; + event_code_t mEventCode; + const ResXMLTree_node* mCurNode; + const void* mCurExt; +}; + +/** + * Convenience class for accessing data in a ResXMLTree resource. + */ +class ResXMLTree : public ResXMLParser +{ +public: + ResXMLTree(); + ResXMLTree(const void* data, size_t size, bool copyData=false); + ~ResXMLTree(); + + status_t setTo(const void* data, size_t size, bool copyData=false); + + status_t getError() const; + + void uninit(); + + const ResStringPool& getStrings() const; + +private: + friend class ResXMLParser; + + status_t validateNode(const ResXMLTree_node* node) const; + + status_t mError; + void* mOwnedData; + const ResXMLTree_header* mHeader; + size_t mSize; + const uint8_t* mDataEnd; + ResStringPool mStrings; + const uint32_t* mResIds; + size_t mNumResIds; + const ResXMLTree_node* mRootNode; + const void* mRootExt; + event_code_t mRootCode; +}; + +/** ******************************************************************** + * RESOURCE TABLE + * + *********************************************************************** */ + +/** + * Header for a resource table. Its data contains a series of + * additional chunks: + * * A ResStringPool_header containing all table values. + * * One or more ResTable_package chunks. + * + * Specific entries within a resource table can be uniquely identified + * with a single integer as defined by the ResTable_ref structure. + */ +struct ResTable_header +{ + struct ResChunk_header header; + + // The number of ResTable_package structures. + uint32_t packageCount; +}; + +/** + * A collection of resource data types within a package. Followed by + * one or more ResTable_type and ResTable_typeSpec structures containing the + * entry values for each resource type. + */ +struct ResTable_package +{ + struct ResChunk_header header; + + // If this is a base package, its ID. Package IDs start + // at 1 (corresponding to the value of the package bits in a + // resource identifier). 0 means this is not a base package. + uint32_t id; + + // Actual name of this package, \0-terminated. + char16_t name[128]; + + // Offset to a ResStringPool_header defining the resource + // type symbol table. If zero, this package is inheriting from + // another base package (overriding specific values in it). + uint32_t typeStrings; + + // Last index into typeStrings that is for public use by others. + uint32_t lastPublicType; + + // Offset to a ResStringPool_header defining the resource + // key symbol table. If zero, this package is inheriting from + // another base package (overriding specific values in it). + uint32_t keyStrings; + + // Last index into keyStrings that is for public use by others. + uint32_t lastPublicKey; +}; + +/** + * Describes a particular resource configuration. + */ +struct ResTable_config +{ + // Number of bytes in this structure. + uint32_t size; + + union { + struct { + // Mobile country code (from SIM). 0 means "any". + uint16_t mcc; + // Mobile network code (from SIM). 0 means "any". + uint16_t mnc; + }; + uint32_t imsi; + }; + + union { + struct { + // \0\0 means "any". Otherwise, en, fr, etc. + char language[2]; + + // \0\0 means "any". Otherwise, US, CA, etc. + char country[2]; + }; + uint32_t locale; + }; + + enum { + ORIENTATION_ANY = 0x0000, + ORIENTATION_PORT = 0x0001, + ORIENTATION_LAND = 0x0002, + ORIENTATION_SQUARE = 0x0002, + }; + + enum { + TOUCHSCREEN_ANY = 0x0000, + TOUCHSCREEN_NOTOUCH = 0x0001, + TOUCHSCREEN_STYLUS = 0x0002, + TOUCHSCREEN_FINGER = 0x0003, + }; + + enum { + DENSITY_ANY = 0 + }; + + union { + struct { + uint8_t orientation; + uint8_t touchscreen; + uint16_t density; + }; + uint32_t screenType; + }; + + enum { + KEYBOARD_ANY = 0x0000, + KEYBOARD_NOKEYS = 0x0001, + KEYBOARD_QWERTY = 0x0002, + KEYBOARD_12KEY = 0x0003, + }; + + enum { + NAVIGATION_ANY = 0x0000, + NAVIGATION_NONAV = 0x0001, + NAVIGATION_DPAD = 0x0002, + NAVIGATION_TRACKBALL = 0x0003, + NAVIGATION_WHEEL = 0x0004, + }; + + enum { + MASK_KEYSHIDDEN = 0x0003, + SHIFT_KEYSHIDDEN = 0, + KEYSHIDDEN_ANY = 0x0000, + KEYSHIDDEN_NO = 0x0001, + KEYSHIDDEN_YES = 0x0002, + }; + + union { + struct { + uint8_t keyboard; + uint8_t navigation; + uint8_t inputFlags; + uint8_t pad0; + }; + uint32_t input; + }; + + enum { + SCREENWIDTH_ANY = 0 + }; + + enum { + SCREENHEIGHT_ANY = 0 + }; + + union { + struct { + uint16_t screenWidth; + uint16_t screenHeight; + }; + uint32_t screenSize; + }; + + enum { + SDKVERSION_ANY = 0 + }; + + enum { + MINORVERSION_ANY = 0 + }; + + union { + struct { + uint16_t sdkVersion; + // For now minorVersion must always be 0!!! Its meaning + // is currently undefined. + uint16_t minorVersion; + }; + uint32_t version; + }; + + inline void copyFromDeviceNoSwap(const ResTable_config& o) { + const size_t size = dtohl(o.size); + if (size >= sizeof(ResTable_config)) { + *this = o; + } else { + memcpy(this, &o, size); + memset(((uint8_t*)this)+size, 0, sizeof(ResTable_config)-size); + } + } + + inline void copyFromDtoH(const ResTable_config& o) { + copyFromDeviceNoSwap(o); + size = sizeof(ResTable_config); + mcc = dtohs(mcc); + mnc = dtohs(mnc); + density = dtohs(density); + screenWidth = dtohs(screenWidth); + screenHeight = dtohs(screenHeight); + sdkVersion = dtohs(sdkVersion); + minorVersion = dtohs(minorVersion); + } + + inline void swapHtoD() { + size = htodl(size); + mcc = htods(mcc); + mnc = htods(mnc); + density = htods(density); + screenWidth = htods(screenWidth); + screenHeight = htods(screenHeight); + sdkVersion = htods(sdkVersion); + minorVersion = htods(minorVersion); + } + + inline int compare(const ResTable_config& o) const { + int32_t diff = (int32_t)(imsi - o.imsi); + if (diff != 0) return diff; + diff = (int32_t)(locale - o.locale); + if (diff != 0) return diff; + diff = (int32_t)(screenType - o.screenType); + if (diff != 0) return diff; + diff = (int32_t)(input - o.input); + if (diff != 0) return diff; + diff = (int32_t)(screenSize - o.screenSize); + if (diff != 0) return diff; + diff = (int32_t)(version - o.version); + return (int)diff; + } + + // Flags indicating a set of config values. These flag constants must + // match the corresponding ones in android.content.pm.ActivityInfo and + // attrs_manifest.xml. + enum { + CONFIG_MCC = 0x0001, + CONFIG_MNC = 0x0002, + CONFIG_LOCALE = 0x0004, + CONFIG_TOUCHSCREEN = 0x0008, + CONFIG_KEYBOARD = 0x0010, + CONFIG_KEYBOARD_HIDDEN = 0x0020, + CONFIG_NAVIGATION = 0x0040, + CONFIG_ORIENTATION = 0x0080, + CONFIG_DENSITY = 0x0100, + CONFIG_SCREEN_SIZE = 0x0200, + CONFIG_VERSION = 0x0400 + }; + + // Compare two configuration, returning CONFIG_* flags set for each value + // that is different. + inline int diff(const ResTable_config& o) const { + int diffs = 0; + if (mcc != o.mcc) diffs |= CONFIG_MCC; + if (mnc != o.mnc) diffs |= CONFIG_MNC; + if (locale != o.locale) diffs |= CONFIG_LOCALE; + if (orientation != o.orientation) diffs |= CONFIG_ORIENTATION; + if (density != o.density) diffs |= CONFIG_DENSITY; + if (touchscreen != o.touchscreen) diffs |= CONFIG_TOUCHSCREEN; + if (((inputFlags^o.inputFlags)&MASK_KEYSHIDDEN) != 0) diffs |= CONFIG_KEYBOARD_HIDDEN; + if (keyboard != o.keyboard) diffs |= CONFIG_KEYBOARD; + if (navigation != o.navigation) diffs |= CONFIG_NAVIGATION; + if (screenSize != o.screenSize) diffs |= CONFIG_SCREEN_SIZE; + if (version != o.version) diffs |= CONFIG_VERSION; + return diffs; + } + + // Return true if 'this' is more specific than 'o'. + inline bool + isBetterThan(const ResTable_config& o, const ResTable_config* requested = NULL) const { + if (imsi != 0 && (!requested || requested->imsi != 0)) { + if (mcc != 0 && (!requested || requested->mcc!= 0)) { + if (o.mcc == 0) { + return true; + } + } + if (mnc != 0 && (!requested || requested->mnc != 0)) { + if (o.mnc == 0) { + return true; + } + } + } + if (locale != 0 && (!requested || requested->locale != 0)) { + if (language[0] != 0 && (!requested || requested->language[0] != 0)) { + if (o.language[0] == 0) { + return true; + } + } + if (country[0] != 0 && (!requested || requested->country[0] != 0)) { + if (o.country[0] == 0) { + return true; + } + } + } + if (screenType != 0 && (!requested || requested->screenType != 0)) { + if (orientation != 0 && (!requested || requested->orientation != 0)) { + if (o.orientation == 0) { + return true; + } + } + if (density != 0 && (!requested || requested->density != 0)) { + if (o.density == 0) { + return true; + } + } + if (touchscreen != 0 && (!requested || requested->touchscreen != 0)) { + if (o.touchscreen == 0) { + return true; + } + } + } + if (input != 0 && (!requested || requested->input != 0)) { + if ((inputFlags&MASK_KEYSHIDDEN) != 0 && (!requested + || (requested->inputFlags&MASK_KEYSHIDDEN) != 0)) { + if ((o.inputFlags&MASK_KEYSHIDDEN) == 0) { + return true; + } + } + if (keyboard != 0 && (!requested || requested->keyboard != 0)) { + if (o.keyboard == 0) { + return true; + } + } + if (navigation != 0 && (!requested || requested->navigation != 0)) { + if (o.navigation == 0) { + return true; + } + } + } + if (screenSize != 0 && (!requested || requested->screenSize != 0)) { + if (screenWidth != 0 && (!requested || requested->screenWidth != 0)) { + if (o.screenWidth == 0) { + return true; + } + } + if (screenHeight != 0 && (!requested || requested->screenHeight != 0)) { + if (o.screenHeight == 0) { + return true; + } + } + } + if (version != 0 && (!requested || requested->version != 0)) { + if (sdkVersion != 0 && (!requested || requested->sdkVersion != 0)) { + if (o.sdkVersion == 0) { + return true; + } + } + if (minorVersion != 0 && (!requested || requested->minorVersion != 0)) { + if (o.minorVersion == 0) { + return true; + } + } + } + return false; + } + + // Return true if 'this' matches the parameters in 'settings'. + inline bool match(const ResTable_config& settings) const { + if (imsi != 0) { + if (settings.mcc != 0 && mcc != 0 + && mcc != settings.mcc) { + return false; + } + if (settings.mnc != 0 && mnc != 0 + && mnc != settings.mnc) { + return false; + } + } + if (locale != 0) { + if (settings.language[0] != 0 && language[0] != 0 + && (language[0] != settings.language[0] + || language[1] != settings.language[1])) { + return false; + } + if (settings.country[0] != 0 && country[0] != 0 + && (country[0] != settings.country[0] + || country[1] != settings.country[1])) { + return false; + } + } + if (screenType != 0) { + if (settings.orientation != 0 && orientation != 0 + && orientation != settings.orientation) { + return false; + } + if (settings.density != 0 && density != 0 + && density != settings.density) { + return false; + } + if (settings.touchscreen != 0 && touchscreen != 0 + && touchscreen != settings.touchscreen) { + return false; + } + } + if (input != 0) { + const int keysHidden = inputFlags&MASK_KEYSHIDDEN; + const int setKeysHidden = settings.inputFlags&MASK_KEYSHIDDEN; + if (setKeysHidden != 0 && keysHidden != 0 + && keysHidden != setKeysHidden) { + return false; + } + if (settings.keyboard != 0 && keyboard != 0 + && keyboard != settings.keyboard) { + return false; + } + if (settings.navigation != 0 && navigation != 0 + && navigation != settings.navigation) { + return false; + } + } + if (screenSize != 0) { + if (settings.screenWidth != 0 && screenWidth != 0 + && screenWidth != settings.screenWidth) { + return false; + } + if (settings.screenHeight != 0 && screenHeight != 0 + && screenHeight != settings.screenHeight) { + return false; + } + } + if (version != 0) { + if (settings.sdkVersion != 0 && sdkVersion != 0 + && sdkVersion != settings.sdkVersion) { + return false; + } + if (settings.minorVersion != 0 && minorVersion != 0 + && minorVersion != settings.minorVersion) { + return false; + } + } + return true; + } + + void getLocale(char str[6]) const { + memset(str, 0, 6); + if (language[0]) { + str[0] = language[0]; + str[1] = language[1]; + if (country[0]) { + str[2] = '_'; + str[3] = country[0]; + str[4] = country[1]; + } + } + } + + String8 toString() const { + char buf[200]; + sprintf(buf, "imsi=%d/%d lang=%c%c reg=%c%c orient=0x%02x touch=0x%02x dens=0x%02x " + "kbd=0x%02x nav=0x%02x input=0x%02x screenW=0x%04x screenH=0x%04x vers=%d.%d", + mcc, mnc, + language[0] ? language[0] : '-', language[1] ? language[1] : '-', + country[0] ? country[0] : '-', country[1] ? country[1] : '-', + orientation, touchscreen, density, keyboard, navigation, inputFlags, + screenWidth, screenHeight, sdkVersion, minorVersion); + return String8(buf); + } +}; + +/** + * A specification of the resources defined by a particular type. + * + * There should be one of these chunks for each resource type. + * + * This structure is followed by an array of integers providing the set of + * configuation change flags (ResTable_config::CONFIG_*) that have multiple + * resources for that configuration. In addition, the high bit is set if that + * resource has been made public. + */ +struct ResTable_typeSpec +{ + struct ResChunk_header header; + + // The type identifier this chunk is holding. Type IDs start + // at 1 (corresponding to the value of the type bits in a + // resource identifier). 0 is invalid. + uint8_t id; + + // Must be 0. + uint8_t res0; + // Must be 0. + uint16_t res1; + + // Number of uint32_t entry configuration masks that follow. + uint32_t entryCount; + + enum { + // Additional flag indicating an entry is public. + SPEC_PUBLIC = 0x40000000 + }; +}; + +/** + * A collection of resource entries for a particular resource data + * type. Followed by an array of uint32_t defining the resource + * values, corresponding to the array of type strings in the + * ResTable_package::typeStrings string block. Each of these hold an + * index from entriesStart; a value of NO_ENTRY means that entry is + * not defined. + * + * There may be multiple of these chunks for a particular resource type, + * supply different configuration variations for the resource values of + * that type. + * + * It would be nice to have an additional ordered index of entries, so + * we can do a binary search if trying to find a resource by string name. + */ +struct ResTable_type +{ + struct ResChunk_header header; + + enum { + NO_ENTRY = 0xFFFFFFFF + }; + + // The type identifier this chunk is holding. Type IDs start + // at 1 (corresponding to the value of the type bits in a + // resource identifier). 0 is invalid. + uint8_t id; + + // Must be 0. + uint8_t res0; + // Must be 0. + uint16_t res1; + + // Number of uint32_t entry indices that follow. + uint32_t entryCount; + + // Offset from header where ResTable_entry data starts. + uint32_t entriesStart; + + // Configuration this collection of entries is designed for. + ResTable_config config; +}; + +/** + * 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. + * * An array of ResTable_map structures, if FLAG_COMPLEX is set. + * These supply a set of name/value mappings of data. + */ +struct ResTable_entry +{ + // Number of bytes in this structure. + uint16_t size; + + enum { + // If set, this is a complex entry, holding a set of name/value + // mappings. It is followed by an array of ResTable_map structures. + FLAG_COMPLEX = 0x0001, + // If set, this resource has been declared public, so libraries + // are allowed to reference it. + FLAG_PUBLIC = 0x0002 + }; + uint16_t flags; + + // Reference into ResTable_package::keyStrings identifying this entry. + struct ResStringPool_ref key; +}; + +/** + * Extended form of a ResTable_entry for map entries, defining a parent map + * resource from which to inherit values. + */ +struct ResTable_map_entry : public ResTable_entry +{ + // Resource identifier of the parent mapping, or 0 if there is none. + ResTable_ref parent; + // Number of name/value pairs that follow for FLAG_COMPLEX. + uint32_t count; +}; + +/** + * A single name/value mapping that is part of a complex resource + * entry. + */ +struct ResTable_map +{ + // The resource identifier defining this mapping's name. For attribute + // resources, 'name' can be one of the following special resource types + // to supply meta-data about the attribute; for all other resource types + // it must be an attribute resource. + ResTable_ref name; + + // Special values for 'name' when defining attribute resources. + enum { + // This entry holds the attribute's type code. + ATTR_TYPE = Res_MAKEINTERNAL(0), + + // For integral attributes, this is the minimum value it can hold. + ATTR_MIN = Res_MAKEINTERNAL(1), + + // For integral attributes, this is the maximum value it can hold. + ATTR_MAX = Res_MAKEINTERNAL(2), + + // Localization of this resource is can be encouraged or required with + // an aapt flag if this is set + ATTR_L10N = Res_MAKEINTERNAL(3), + + // for plural support, see android.content.res.PluralRules#attrForQuantity(int) + ATTR_OTHER = Res_MAKEINTERNAL(4), + ATTR_ZERO = Res_MAKEINTERNAL(5), + ATTR_ONE = Res_MAKEINTERNAL(6), + ATTR_TWO = Res_MAKEINTERNAL(7), + ATTR_FEW = Res_MAKEINTERNAL(8), + ATTR_MANY = Res_MAKEINTERNAL(9) + + }; + + // Bit mask of allowed types, for use with ATTR_TYPE. + enum { + // No type has been defined for this attribute, use generic + // type handling. The low 16 bits are for types that can be + // handled generically; the upper 16 require additional information + // in the bag so can not be handled generically for TYPE_ANY. + TYPE_ANY = 0x0000FFFF, + + // Attribute holds a references to another resource. + TYPE_REFERENCE = 1<<0, + + // Attribute holds a generic string. + TYPE_STRING = 1<<1, + + // Attribute holds an integer value. ATTR_MIN and ATTR_MIN can + // optionally specify a constrained range of possible integer values. + TYPE_INTEGER = 1<<2, + + // Attribute holds a boolean integer. + TYPE_BOOLEAN = 1<<3, + + // Attribute holds a color value. + TYPE_COLOR = 1<<4, + + // Attribute holds a floating point value. + TYPE_FLOAT = 1<<5, + + // Attribute holds a dimension value, such as "20px". + TYPE_DIMENSION = 1<<6, + + // Attribute holds a fraction value, such as "20%". + TYPE_FRACTION = 1<<7, + + // Attribute holds an enumeration. The enumeration values are + // supplied as additional entries in the map. + TYPE_ENUM = 1<<16, + + // Attribute holds a bitmaks of flags. The flag bit values are + // supplied as additional entries in the map. + TYPE_FLAGS = 1<<17 + }; + + // Enum of localization modes, for use with ATTR_L10N. + enum { + L10N_NOT_REQUIRED = 0, + L10N_SUGGESTED = 1 + }; + + // This mapping's value. + Res_value value; +}; + +/** + * Convenience class for accessing data in a ResTable resource. + */ +class ResTable +{ +public: + ResTable(); + ResTable(const void* data, size_t size, void* cookie, + bool copyData=false); + ~ResTable(); + + status_t add(const void* data, size_t size, void* cookie, + bool copyData=false); + status_t add(Asset* asset, void* cookie, + bool copyData=false); + + status_t getError() const; + + void uninit(); + + struct resource_name + { + const char16_t* package; + size_t packageLen; + const char16_t* type; + size_t typeLen; + const char16_t* name; + size_t nameLen; + }; + + bool getResourceName(uint32_t resID, resource_name* outName) const; + + /** + * Retrieve the value of a resource. If the resource is found, returns a + * value >= 0 indicating the table it is in (for use with + * getTableStringBlock() and getTableCookie()) and fills in 'outValue'. If + * not found, returns a negative error code. + * + * Note that this function does not do reference traversal. If you want + * to follow references to other resources to get the "real" value to + * use, you need to call resolveReference() after this function. + * + * @param resID The desired resoruce identifier. + * @param outValue Filled in with the resource data that was found. + * + * @return ssize_t Either a >= 0 table index or a negative error code. + */ + ssize_t getResource(uint32_t resID, Res_value* outValue, bool mayBeBag=false, + uint32_t* outSpecFlags=NULL) const; + + inline ssize_t getResource(const ResTable_ref& res, Res_value* outValue, + uint32_t* outSpecFlags=NULL) const { + return getResource(res.ident, outValue, outSpecFlags); + } + + ssize_t resolveReference(Res_value* inOutValue, + ssize_t blockIndex, + uint32_t* outLastRef = NULL, + uint32_t* inoutTypeSpecFlags = NULL) const; + + enum { + TMP_BUFFER_SIZE = 16 + }; + const char16_t* valueToString(const Res_value* value, size_t stringBlock, + char16_t tmpBuffer[TMP_BUFFER_SIZE], + size_t* outLen); + + struct bag_entry { + ssize_t stringBlock; + ResTable_map map; + }; + + /** + * Retrieve the bag of a resource. If the resoruce is found, returns the + * number of bags it contains and 'outBag' points to an array of their + * values. If not found, a negative error code is returned. + * + * Note that this function -does- do reference traversal of the bag data. + * + * @param resID The desired resource identifier. + * @param outBag Filled inm with a pointer to the bag mappings. + * + * @return ssize_t Either a >= 0 bag count of negative error code. + */ + ssize_t lockBag(uint32_t resID, const bag_entry** outBag) const; + + void unlockBag(const bag_entry* bag) const; + + void lock() const; + + ssize_t getBagLocked(uint32_t resID, const bag_entry** outBag, + uint32_t* outTypeSpecFlags=NULL) const; + + void unlock() const; + + class Theme { + public: + Theme(const ResTable& table); + ~Theme(); + + inline const ResTable& getResTable() const { return mTable; } + + status_t applyStyle(uint32_t resID, bool force=false); + status_t setTo(const Theme& other); + + /** + * Retrieve a value in the theme. If the theme defines this + * value, returns a value >= 0 indicating the table it is in + * (for use with getTableStringBlock() and getTableCookie) and + * fills in 'outValue'. If not found, returns a negative error + * code. + * + * Note that this function does not do reference traversal. If you want + * to follow references to other resources to get the "real" value to + * use, you need to call resolveReference() after this function. + * + * @param resID A resource identifier naming the desired theme + * attribute. + * @param outValue Filled in with the theme value that was + * found. + * + * @return ssize_t Either a >= 0 table index or a negative error code. + */ + ssize_t getAttribute(uint32_t resID, Res_value* outValue, + uint32_t* outTypeSpecFlags = NULL) const; + + /** + * This is like ResTable::resolveReference(), but also takes + * care of resolving attribute references to the theme. + */ + ssize_t resolveAttributeReference(Res_value* inOutValue, + ssize_t blockIndex, uint32_t* outLastRef = NULL, + uint32_t* inoutTypeSpecFlags = NULL) const; + + void dumpToLog() const; + + private: + Theme(const Theme&); + Theme& operator=(const Theme&); + + struct theme_entry { + ssize_t stringBlock; + uint32_t typeSpecFlags; + Res_value value; + }; + struct type_info { + size_t numEntries; + theme_entry* entries; + }; + struct package_info { + size_t numTypes; + type_info types[]; + }; + + void free_package(package_info* pi); + package_info* copy_package(package_info* pi); + + const ResTable& mTable; + package_info* mPackages[Res_MAXPACKAGE]; + }; + + void setParameters(const ResTable_config* params); + void getParameters(ResTable_config* params) const; + + // Retrieve an identifier (which can be passed to getResource) + // for a given resource name. The 'name' can be fully qualified + // (:.) or the package or type components + // can be dropped if default values are supplied here. + // + // Returns 0 if no such resource was found, else a valid resource ID. + uint32_t identifierForName(const char16_t* name, size_t nameLen, + const char16_t* type = 0, size_t typeLen = 0, + const char16_t* defPackage = 0, + size_t defPackageLen = 0, + uint32_t* outTypeSpecFlags = NULL) const; + + static bool expandResourceRef(const uint16_t* refStr, size_t refLen, + String16* outPackage, + String16* outType, + String16* outName, + const String16* defType = NULL, + const String16* defPackage = NULL, + const char** outErrorMsg = NULL); + + static bool stringToInt(const char16_t* s, size_t len, Res_value* outValue); + static bool stringToFloat(const char16_t* s, size_t len, Res_value* outValue); + + // Used with stringToValue. + class Accessor + { + public: + inline virtual ~Accessor() { } + + virtual uint32_t getCustomResource(const String16& package, + const String16& type, + const String16& name) const = 0; + virtual uint32_t getCustomResourceWithCreation(const String16& package, + const String16& type, + const String16& name, + const bool createIfNeeded = false) = 0; + virtual uint32_t getRemappedPackage(uint32_t origPackage) const = 0; + virtual bool getAttributeType(uint32_t attrID, uint32_t* outType) = 0; + virtual bool getAttributeMin(uint32_t attrID, uint32_t* outMin) = 0; + virtual bool getAttributeMax(uint32_t attrID, uint32_t* outMax) = 0; + virtual bool getAttributeEnum(uint32_t attrID, + const char16_t* name, size_t nameLen, + Res_value* outValue) = 0; + virtual bool getAttributeFlags(uint32_t attrID, + const char16_t* name, size_t nameLen, + Res_value* outValue) = 0; + virtual uint32_t getAttributeL10N(uint32_t attrID) = 0; + virtual bool getLocalizationSetting() = 0; + virtual void reportError(void* accessorCookie, const char* fmt, ...) = 0; + }; + + // Convert a string to a resource value. Handles standard "@res", + // "#color", "123", and "0x1bd" types; performs escaping of strings. + // The resulting value is placed in 'outValue'; if it is a string type, + // 'outString' receives the string. If 'attrID' is supplied, the value is + // type checked against this attribute and it is used to perform enum + // evaluation. If 'acccessor' is supplied, it will be used to attempt to + // resolve resources that do not exist in this ResTable. If 'attrType' is + // supplied, the value will be type checked for this format if 'attrID' + // is not supplied or found. + bool stringToValue(Res_value* outValue, String16* outString, + const char16_t* s, size_t len, + bool preserveSpaces, bool coerceType, + uint32_t attrID = 0, + const String16* defType = NULL, + const String16* defPackage = NULL, + Accessor* accessor = NULL, + void* accessorCookie = NULL, + uint32_t attrType = ResTable_map::TYPE_ANY, + bool enforcePrivate = true) const; + + // Perform processing of escapes and quotes in a string. + static bool collectString(String16* outString, + const char16_t* s, size_t len, + bool preserveSpaces, + const char** outErrorMsg = NULL, + bool append = false); + + size_t getBasePackageCount() const; + const char16_t* getBasePackageName(size_t idx) const; + uint32_t getBasePackageId(size_t idx) const; + + size_t getTableCount() const; + const ResStringPool* getTableStringBlock(size_t index) const; + void* getTableCookie(size_t index) const; + + // Return the configurations (ResTable_config) that we know about + void getConfigurations(Vector* configs) const; + + void getLocales(Vector* locales) const; + +#ifndef HAVE_ANDROID_OS + void print() const; +#endif + +private: + struct Header; + struct Type; + struct Package; + struct PackageGroup; + struct bag_set; + + status_t add(const void* data, size_t size, void* cookie, + Asset* asset, bool copyData); + + ssize_t getResourcePackageIndex(uint32_t resID) const; + ssize_t getEntry( + const Package* package, int typeIndex, int entryIndex, + const ResTable_config* config, + const ResTable_type** outType, const ResTable_entry** outEntry, + const Type** outTypeClass) const; + status_t parsePackage( + const ResTable_package* const pkg, const Header* const header); + + mutable Mutex mLock; + + status_t mError; + + ResTable_config mParams; + + // Array of all resource tables. + Vector mHeaders; + + // Array of packages in all resource tables. + Vector mPackageGroups; + + // Mapping from resource package IDs to indices into the internal + // package array. + uint8_t mPackageMap[256]; +}; + +} // namespace android + +#endif // _LIBS_UTILS_RESOURCE_TYPES_H diff --git a/include/utils/SharedBuffer.h b/include/utils/SharedBuffer.h new file mode 100644 index 000000000..24508b0f7 --- /dev/null +++ b/include/utils/SharedBuffer.h @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2005 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_SHARED_BUFFER_H +#define ANDROID_SHARED_BUFFER_H + +#include +#include + +// --------------------------------------------------------------------------- + +namespace android { + +class SharedBuffer +{ +public: + + /* flags to use with release() */ + enum { + eKeepStorage = 0x00000001 + }; + + /*! allocate a buffer of size 'size' and acquire() it. + * call release() to free it. + */ + static SharedBuffer* alloc(size_t size); + + /*! free the memory associated with the SharedBuffer. + * Fails if there are any users associated with this SharedBuffer. + * In other words, the buffer must have been release by all its + * users. + */ + static ssize_t dealloc(const SharedBuffer* released); + + //! get the SharedBuffer from the data pointer + static inline const SharedBuffer* sharedBuffer(const void* data); + + //! access the data for read + inline const void* data() const; + + //! access the data for read/write + inline void* data(); + + //! get size of the buffer + inline size_t size() const; + + //! get back a SharedBuffer object from its data + static inline SharedBuffer* bufferFromData(void* data); + + //! get back a SharedBuffer object from its data + static inline const SharedBuffer* bufferFromData(const void* data); + + //! get the size of a SharedBuffer object from its data + static inline size_t sizeFromData(const void* data); + + //! edit the buffer (get a writtable, or non-const, version of it) + SharedBuffer* edit() const; + + //! edit the buffer, resizing if needed + SharedBuffer* editResize(size_t size) const; + + //! like edit() but fails if a copy is required + SharedBuffer* attemptEdit() const; + + //! resize and edit the buffer, loose it's content. + SharedBuffer* reset(size_t size) const; + + //! acquire/release a reference on this buffer + void acquire() const; + + /*! release a reference on this buffer, with the option of not + * freeing the memory associated with it if it was the last reference + * returns the previous reference count + */ + int32_t release(uint32_t flags = 0) const; + + //! returns wether or not we're the only owner + inline bool onlyOwner() const; + + +private: + inline SharedBuffer() { } + inline ~SharedBuffer() { } + inline SharedBuffer(const SharedBuffer&); + + // 16 bytes. must be sized to preserve correct alingment. + mutable int32_t mRefs; + size_t mSize; + uint32_t mReserved[2]; +}; + +// --------------------------------------------------------------------------- + +const SharedBuffer* SharedBuffer::sharedBuffer(const void* data) { + return data ? reinterpret_cast(data)-1 : 0; +} + +const void* SharedBuffer::data() const { + return this + 1; +} + +void* SharedBuffer::data() { + return this + 1; +} + +size_t SharedBuffer::size() const { + return mSize; +} + +SharedBuffer* SharedBuffer::bufferFromData(void* data) +{ + return ((SharedBuffer*)data)-1; +} + +const SharedBuffer* SharedBuffer::bufferFromData(const void* data) +{ + return ((const SharedBuffer*)data)-1; +} + +size_t SharedBuffer::sizeFromData(const void* data) +{ + return (((const SharedBuffer*)data)-1)->mSize; +} + +bool SharedBuffer::onlyOwner() const { + return (mRefs == 1); +} + +}; // namespace android + +// --------------------------------------------------------------------------- + +#endif // ANDROID_VECTOR_H diff --git a/include/utils/Socket.h b/include/utils/Socket.h new file mode 100644 index 000000000..8b7f40617 --- /dev/null +++ b/include/utils/Socket.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2005 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. + */ + +// +// Socket class. Modeled after Java classes. +// +#ifndef _RUNTIME_SOCKET_H +#define _RUNTIME_SOCKET_H + +#include +#include + +namespace android { + +/* + * Basic socket class, needed to abstract away the differences between + * BSD sockets and WinSock. This establishes a streaming network + * connection (TCP/IP) to somebody. + */ +class Socket { +public: + Socket(void); + ~Socket(void); + + // Create a connection to somewhere. + // Return 0 on success. + int connect(const char* host, int port); + int connect(const InetAddress* addr, int port); + + + // Close the socket. Don't try to use this object again after + // calling this. Returns false on failure. + bool close(void); + + // If we created the socket without an address, we can use these + // to finish the connection. Returns 0 on success. + int bind(const SocketAddress& bindPoint); + int connect(const SocketAddress& endPoint); + + // Here we deviate from the traditional object-oriented fanciness + // and just provide read/write operators instead of getters for + // objects that abstract a stream. + // + // Standard read/write semantics. + int read(void* buf, ssize_t len) const; + int write(const void* buf, ssize_t len) const; + + // This must be called once, at program startup. + static bool bootInit(void); + static void finalShutdown(void); + +private: + // Internal function that establishes a connection. + int doConnect(const InetSocketAddress& addr); + + unsigned long mSock; // holds SOCKET or int + + static bool mBootInitialized; +}; + + +// debug -- unit tests +void TestSockets(void); + +}; // namespace android + +#endif // _RUNTIME_SOCKET_H diff --git a/include/utils/SortedVector.h b/include/utils/SortedVector.h new file mode 100644 index 000000000..c8a61531f --- /dev/null +++ b/include/utils/SortedVector.h @@ -0,0 +1,282 @@ +/* + * Copyright (C) 2005 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_SORTED_VECTOR_H +#define ANDROID_SORTED_VECTOR_H + +#include +#include +#include + +#include +#include +#include + +// --------------------------------------------------------------------------- + +namespace android { + +template +class SortedVector : private SortedVectorImpl +{ +public: + typedef TYPE value_type; + + /*! + * Constructors and destructors + */ + + SortedVector(); + SortedVector(const SortedVector& rhs); + virtual ~SortedVector(); + + /*! copy operator */ + const SortedVector& operator = (const SortedVector& rhs) const; + SortedVector& operator = (const SortedVector& rhs); + + /* + * empty the vector + */ + + inline void clear() { VectorImpl::clear(); } + + /*! + * vector stats + */ + + //! returns number of items in the vector + inline size_t size() const { return VectorImpl::size(); } + //! returns wether or not the vector is empty + inline bool isEmpty() const { return VectorImpl::isEmpty(); } + //! returns how many items can be stored without reallocating the backing store + inline size_t capacity() const { return VectorImpl::capacity(); } + //! setst the capacity. capacity can never be reduced less than size() + inline ssize_t setCapacity(size_t size) { return VectorImpl::setCapacity(size); } + + /*! + * C-style array access + */ + + //! read-only C-style access + inline const TYPE* array() const; + + //! read-write C-style access. BE VERY CAREFUL when modifying the array + //! you ust keep it sorted! You usually don't use this function. + TYPE* editArray(); + + //! finds the index of an item + ssize_t indexOf(const TYPE& item) const; + + //! finds where this item should be inserted + size_t orderOf(const TYPE& item) const; + + + /*! + * accessors + */ + + //! read-only access to an item at a given index + inline const TYPE& operator [] (size_t index) const; + //! alternate name for operator [] + inline const TYPE& itemAt(size_t index) const; + //! stack-usage of the vector. returns the top of the stack (last element) + const TYPE& top() const; + //! same as operator [], but allows to access the vector backward (from the end) with a negative index + const TYPE& mirrorItemAt(ssize_t index) const; + + /*! + * modifing the array + */ + + //! add an item in the right place (and replace the one that is there) + ssize_t add(const TYPE& item); + + //! editItemAt() MUST NOT change the order of this item + TYPE& editItemAt(size_t index) { + return *( static_cast(VectorImpl::editItemLocation(index)) ); + } + + //! merges a vector into this one + ssize_t merge(const Vector& vector); + ssize_t merge(const SortedVector& vector); + + //! removes an item + ssize_t remove(const TYPE&); + + //! remove several items + inline ssize_t removeItemsAt(size_t index, size_t count = 1); + //! remove one item + inline ssize_t removeAt(size_t index) { return removeItemsAt(index); } + +protected: + virtual void do_construct(void* storage, size_t num) const; + virtual void do_destroy(void* storage, size_t num) const; + virtual void do_copy(void* dest, const void* from, size_t num) const; + virtual void do_splat(void* dest, const void* item, size_t num) const; + virtual void do_move_forward(void* dest, const void* from, size_t num) const; + virtual void do_move_backward(void* dest, const void* from, size_t num) const; + virtual int do_compare(const void* lhs, const void* rhs) const; +}; + + +// --------------------------------------------------------------------------- +// No user serviceable parts from here... +// --------------------------------------------------------------------------- + +template inline +SortedVector::SortedVector() + : SortedVectorImpl(sizeof(TYPE), + ((traits::has_trivial_ctor ? HAS_TRIVIAL_CTOR : 0) + |(traits::has_trivial_dtor ? HAS_TRIVIAL_DTOR : 0) + |(traits::has_trivial_copy ? HAS_TRIVIAL_COPY : 0) + |(traits::has_trivial_assign ? HAS_TRIVIAL_ASSIGN : 0)) + ) +{ +} + +template inline +SortedVector::SortedVector(const SortedVector& rhs) + : SortedVectorImpl(rhs) { +} + +template inline +SortedVector::~SortedVector() { + finish_vector(); +} + +template inline +SortedVector& SortedVector::operator = (const SortedVector& rhs) { + SortedVectorImpl::operator = (rhs); + return *this; +} + +template inline +const SortedVector& SortedVector::operator = (const SortedVector& rhs) const { + SortedVectorImpl::operator = (rhs); + return *this; +} + +template inline +const TYPE* SortedVector::array() const { + return static_cast(arrayImpl()); +} + +template inline +TYPE* SortedVector::editArray() { + return static_cast(editArrayImpl()); +} + + +template inline +const TYPE& SortedVector::operator[](size_t index) const { + assert( index inline +const TYPE& SortedVector::itemAt(size_t index) const { + return operator[](index); +} + +template inline +const TYPE& SortedVector::mirrorItemAt(ssize_t index) const { + assert( (index>0 ? index : -index) inline +const TYPE& SortedVector::top() const { + return *(array() + size() - 1); +} + +template inline +ssize_t SortedVector::add(const TYPE& item) { + return SortedVectorImpl::add(&item); +} + +template inline +ssize_t SortedVector::indexOf(const TYPE& item) const { + return SortedVectorImpl::indexOf(&item); +} + +template inline +size_t SortedVector::orderOf(const TYPE& item) const { + return SortedVectorImpl::orderOf(&item); +} + +template inline +ssize_t SortedVector::merge(const Vector& vector) { + return SortedVectorImpl::merge(reinterpret_cast(vector)); +} + +template inline +ssize_t SortedVector::merge(const SortedVector& vector) { + return SortedVectorImpl::merge(reinterpret_cast(vector)); +} + +template inline +ssize_t SortedVector::remove(const TYPE& item) { + return SortedVectorImpl::remove(&item); +} + +template inline +ssize_t SortedVector::removeItemsAt(size_t index, size_t count) { + return VectorImpl::removeItemsAt(index, count); +} + +// --------------------------------------------------------------------------- + +template +void SortedVector::do_construct(void* storage, size_t num) const { + construct_type( reinterpret_cast(storage), num ); +} + +template +void SortedVector::do_destroy(void* storage, size_t num) const { + destroy_type( reinterpret_cast(storage), num ); +} + +template +void SortedVector::do_copy(void* dest, const void* from, size_t num) const { + copy_type( reinterpret_cast(dest), reinterpret_cast(from), num ); +} + +template +void SortedVector::do_splat(void* dest, const void* item, size_t num) const { + splat_type( reinterpret_cast(dest), reinterpret_cast(item), num ); +} + +template +void SortedVector::do_move_forward(void* dest, const void* from, size_t num) const { + move_forward_type( reinterpret_cast(dest), reinterpret_cast(from), num ); +} + +template +void SortedVector::do_move_backward(void* dest, const void* from, size_t num) const { + move_backward_type( reinterpret_cast(dest), reinterpret_cast(from), num ); +} + +template +int SortedVector::do_compare(const void* lhs, const void* rhs) const { + return compare_type( *reinterpret_cast(lhs), *reinterpret_cast(rhs) ); +} + +}; // namespace android + + +// --------------------------------------------------------------------------- + +#endif // ANDROID_SORTED_VECTOR_H diff --git a/include/utils/StopWatch.h b/include/utils/StopWatch.h new file mode 100644 index 000000000..cc0bebc40 --- /dev/null +++ b/include/utils/StopWatch.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2005 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_STOPWATCH_H +#define ANDROID_STOPWATCH_H + +#include +#include + +#include + +// --------------------------------------------------------------------------- + +namespace android { + +class StopWatch +{ +public: + StopWatch( const char *name, + int clock = SYSTEM_TIME_MONOTONIC, + uint32_t flags = 0); + ~StopWatch(); + + const char* name() const; + nsecs_t lap(); + nsecs_t elapsedTime() const; + +private: + const char* mName; + int mClock; + uint32_t mFlags; + + struct lap_t { + nsecs_t soFar; + nsecs_t thisLap; + }; + + nsecs_t mStartTime; + lap_t mLaps[8]; + int mNumLaps; +}; + + +}; // namespace android + + +// --------------------------------------------------------------------------- + +#endif // ANDROID_STOPWATCH_H diff --git a/include/utils/String16.h b/include/utils/String16.h new file mode 100644 index 000000000..a2d22eea9 --- /dev/null +++ b/include/utils/String16.h @@ -0,0 +1,260 @@ +/* + * Copyright (C) 2005 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_STRING16_H +#define ANDROID_STRING16_H + +#include +#include + +#include +#include + +// --------------------------------------------------------------------------- + +extern "C" { + +typedef uint16_t char16_t; + +// Standard string functions on char16 strings. +int strcmp16(const char16_t *, const char16_t *); +int strncmp16(const char16_t *s1, const char16_t *s2, size_t n); +size_t strlen16(const char16_t *); +size_t strnlen16(const char16_t *, size_t); +char16_t *strcpy16(char16_t *, const char16_t *); +char16_t *strncpy16(char16_t *, const char16_t *, size_t); + +// Version of comparison that supports embedded nulls. +// This is different than strncmp() because we don't stop +// at a nul character and consider the strings to be different +// if the lengths are different (thus we need to supply the +// lengths of both strings). This can also be used when +// your string is not nul-terminated as it will have the +// equivalent result as strcmp16 (unlike strncmp16). +int strzcmp16(const char16_t *s1, size_t n1, const char16_t *s2, size_t n2); + +// Version of strzcmp16 for comparing strings in different endianness. +int strzcmp16_h_n(const char16_t *s1H, size_t n1, const char16_t *s2N, size_t n2); + +} + +// --------------------------------------------------------------------------- + +namespace android { + +class String8; +class TextOutput; + +//! This is a string holding UTF-16 characters. +class String16 +{ +public: + String16(); + String16(const String16& o); + String16(const String16& o, + size_t len, + size_t begin=0); + explicit String16(const char16_t* o); + explicit String16(const char16_t* o, size_t len); + explicit String16(const String8& o); + explicit String16(const char* o); + explicit String16(const char* o, size_t len); + + ~String16(); + + inline const char16_t* string() const; + inline size_t size() const; + + inline const SharedBuffer* sharedBuffer() const; + + void setTo(const String16& other); + status_t setTo(const char16_t* other); + status_t setTo(const char16_t* other, size_t len); + status_t setTo(const String16& other, + size_t len, + size_t begin=0); + + status_t append(const String16& other); + status_t append(const char16_t* other, size_t len); + + inline String16& operator=(const String16& other); + + inline String16& operator+=(const String16& other); + inline String16 operator+(const String16& other) const; + + status_t insert(size_t pos, const char16_t* chrs); + status_t insert(size_t pos, + const char16_t* chrs, size_t len); + + ssize_t findFirst(char16_t c) const; + ssize_t findLast(char16_t c) const; + + bool startsWith(const String16& prefix) const; + bool startsWith(const char16_t* prefix) const; + + status_t makeLower(); + + status_t replaceAll(char16_t replaceThis, + char16_t withThis); + + status_t remove(size_t len, size_t begin=0); + + inline int compare(const String16& other) const; + + inline bool operator<(const String16& other) const; + inline bool operator<=(const String16& other) const; + inline bool operator==(const String16& other) const; + inline bool operator!=(const String16& other) const; + inline bool operator>=(const String16& other) const; + inline bool operator>(const String16& other) const; + + inline bool operator<(const char16_t* other) const; + inline bool operator<=(const char16_t* other) const; + inline bool operator==(const char16_t* other) const; + inline bool operator!=(const char16_t* other) const; + inline bool operator>=(const char16_t* other) const; + inline bool operator>(const char16_t* other) const; + + inline operator const char16_t*() const; + +private: + const char16_t* mString; +}; + +TextOutput& operator<<(TextOutput& to, const String16& val); + +// --------------------------------------------------------------------------- +// No user servicable parts below. + +inline int compare_type(const String16& lhs, const String16& rhs) +{ + return lhs.compare(rhs); +} + +inline int strictly_order_type(const String16& lhs, const String16& rhs) +{ + return compare_type(lhs, rhs) < 0; +} + +inline const char16_t* String16::string() const +{ + return mString; +} + +inline size_t String16::size() const +{ + return SharedBuffer::sizeFromData(mString)/sizeof(char16_t)-1; +} + +inline const SharedBuffer* String16::sharedBuffer() const +{ + return SharedBuffer::bufferFromData(mString); +} + +inline String16& String16::operator=(const String16& other) +{ + setTo(other); + return *this; +} + +inline String16& String16::operator+=(const String16& other) +{ + append(other); + return *this; +} + +inline String16 String16::operator+(const String16& other) const +{ + String16 tmp; + tmp += other; + return tmp; +} + +inline int String16::compare(const String16& other) const +{ + return strzcmp16(mString, size(), other.mString, other.size()); +} + +inline bool String16::operator<(const String16& other) const +{ + return strzcmp16(mString, size(), other.mString, other.size()) < 0; +} + +inline bool String16::operator<=(const String16& other) const +{ + return strzcmp16(mString, size(), other.mString, other.size()) <= 0; +} + +inline bool String16::operator==(const String16& other) const +{ + return strzcmp16(mString, size(), other.mString, other.size()) == 0; +} + +inline bool String16::operator!=(const String16& other) const +{ + return strzcmp16(mString, size(), other.mString, other.size()) != 0; +} + +inline bool String16::operator>=(const String16& other) const +{ + return strzcmp16(mString, size(), other.mString, other.size()) >= 0; +} + +inline bool String16::operator>(const String16& other) const +{ + return strzcmp16(mString, size(), other.mString, other.size()) > 0; +} + +inline bool String16::operator<(const char16_t* other) const +{ + return strcmp16(mString, other) < 0; +} + +inline bool String16::operator<=(const char16_t* other) const +{ + return strcmp16(mString, other) <= 0; +} + +inline bool String16::operator==(const char16_t* other) const +{ + return strcmp16(mString, other) == 0; +} + +inline bool String16::operator!=(const char16_t* other) const +{ + return strcmp16(mString, other) != 0; +} + +inline bool String16::operator>=(const char16_t* other) const +{ + return strcmp16(mString, other) >= 0; +} + +inline bool String16::operator>(const char16_t* other) const +{ + return strcmp16(mString, other) > 0; +} + +inline String16::operator const char16_t*() const +{ + return mString; +} + +}; // namespace android + +// --------------------------------------------------------------------------- + +#endif // ANDROID_STRING16_H diff --git a/include/utils/String8.h b/include/utils/String8.h new file mode 100644 index 000000000..c49faf6fe --- /dev/null +++ b/include/utils/String8.h @@ -0,0 +1,353 @@ +/* + * Copyright (C) 2005 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_STRING8_H +#define ANDROID_STRING8_H + +#include + +// Need this for the char16_t type; String8.h should not +// be depedent on the String16 class. +#include + +#include +#include +#include + +// --------------------------------------------------------------------------- + +namespace android { + +class TextOutput; + +//! This is a string holding UTF-8 characters. +class String8 +{ +public: + String8(); + String8(const String8& o); + explicit String8(const char* o); + explicit String8(const char* o, size_t numChars); + + explicit String8(const String16& o); + explicit String8(const char16_t* o); + explicit String8(const char16_t* o, size_t numChars); + + ~String8(); + + inline const char* string() const; + inline size_t size() const; + inline size_t length() const; + inline size_t bytes() const; + + inline const SharedBuffer* sharedBuffer() const; + + void setTo(const String8& other); + status_t setTo(const char* other); + status_t setTo(const char* other, size_t numChars); + status_t setTo(const char16_t* other, size_t numChars); + + status_t append(const String8& other); + status_t append(const char* other); + status_t append(const char* other, size_t numChars); + + inline String8& operator=(const String8& other); + inline String8& operator=(const char* other); + + inline String8& operator+=(const String8& other); + inline String8 operator+(const String8& other) const; + + inline String8& operator+=(const char* other); + inline String8 operator+(const char* other) const; + + inline int compare(const String8& other) const; + + inline bool operator<(const String8& other) const; + inline bool operator<=(const String8& other) const; + inline bool operator==(const String8& other) const; + inline bool operator!=(const String8& other) const; + inline bool operator>=(const String8& other) const; + inline bool operator>(const String8& other) const; + + inline bool operator<(const char* other) const; + inline bool operator<=(const char* other) const; + inline bool operator==(const char* other) const; + inline bool operator!=(const char* other) const; + inline bool operator>=(const char* other) const; + inline bool operator>(const char* other) const; + + inline operator const char*() const; + + char* lockBuffer(size_t size); + void unlockBuffer(); + status_t unlockBuffer(size_t size); + + // return the index of the first byte of other in this at or after + // start, or -1 if not found + ssize_t find(const char* other, size_t start = 0) const; + + void toLower(); + void toLower(size_t start, size_t numChars); + void toUpper(); + void toUpper(size_t start, size_t numChars); + + /* + * These methods operate on the string as if it were a path name. + */ + + /* + * Set the filename field to a specific value. + * + * Normalizes the filename, removing a trailing '/' if present. + */ + void setPathName(const char* name); + void setPathName(const char* name, size_t numChars); + + /* + * Get just the filename component. + * + * "/tmp/foo/bar.c" --> "bar.c" + */ + String8 getPathLeaf(void) const; + + /* + * Remove the last (file name) component, leaving just the directory + * name. + * + * "/tmp/foo/bar.c" --> "/tmp/foo" + * "/tmp" --> "" // ????? shouldn't this be "/" ???? XXX + * "bar.c" --> "" + */ + String8 getPathDir(void) const; + + /* + * Retrieve the front (root dir) component. Optionally also return the + * remaining components. + * + * "/tmp/foo/bar.c" --> "tmp" (remain = "foo/bar.c") + * "/tmp" --> "tmp" (remain = "") + * "bar.c" --> "bar.c" (remain = "") + */ + String8 walkPath(String8* outRemains = NULL) const; + + /* + * Return the filename extension. This is the last '.' and up to + * four characters that follow it. The '.' is included in case we + * decide to expand our definition of what constitutes an extension. + * + * "/tmp/foo/bar.c" --> ".c" + * "/tmp" --> "" + * "/tmp/foo.bar/baz" --> "" + * "foo.jpeg" --> ".jpeg" + * "foo." --> "" + */ + String8 getPathExtension(void) const; + + /* + * Return the path without the extension. Rules for what constitutes + * an extension are described in the comment for getPathExtension(). + * + * "/tmp/foo/bar.c" --> "/tmp/foo/bar" + */ + String8 getBasePath(void) const; + + /* + * Add a component to the pathname. We guarantee that there is + * exactly one path separator between the old path and the new. + * If there is no existing name, we just copy the new name in. + * + * If leaf is a fully qualified path (i.e. starts with '/', it + * replaces whatever was there before. + */ + String8& appendPath(const char* leaf); + String8& appendPath(const String8& leaf) { return appendPath(leaf.string()); } + + /* + * Like appendPath(), but does not affect this string. Returns a new one instead. + */ + String8 appendPathCopy(const char* leaf) const + { String8 p(*this); p.appendPath(leaf); return p; } + String8 appendPathCopy(const String8& leaf) const { return appendPathCopy(leaf.string()); } + + /* + * Converts all separators in this string to /, the default path separator. + * + * If the default OS separator is backslash, this converts all + * backslashes to slashes, in-place. Otherwise it does nothing. + * Returns self. + */ + String8& convertToResPath(); + +private: + status_t real_append(const char* other, size_t numChars); + char* find_extension(void) const; + + const char* mString; +}; + +TextOutput& operator<<(TextOutput& to, const String16& val); + +// --------------------------------------------------------------------------- +// No user servicable parts below. + +inline int compare_type(const String8& lhs, const String8& rhs) +{ + return lhs.compare(rhs); +} + +inline int strictly_order_type(const String8& lhs, const String8& rhs) +{ + return compare_type(lhs, rhs) < 0; +} + +inline const char* String8::string() const +{ + return mString; +} + +inline size_t String8::length() const +{ + return SharedBuffer::sizeFromData(mString)-1; +} + +inline size_t String8::size() const +{ + return length(); +} + +inline size_t String8::bytes() const +{ + return SharedBuffer::sizeFromData(mString)-1; +} + +inline const SharedBuffer* String8::sharedBuffer() const +{ + return SharedBuffer::bufferFromData(mString); +} + +inline String8& String8::operator=(const String8& other) +{ + setTo(other); + return *this; +} + +inline String8& String8::operator=(const char* other) +{ + setTo(other); + return *this; +} + +inline String8& String8::operator+=(const String8& other) +{ + append(other); + return *this; +} + +inline String8 String8::operator+(const String8& other) const +{ + String8 tmp; + tmp += other; + return tmp; +} + +inline String8& String8::operator+=(const char* other) +{ + append(other); + return *this; +} + +inline String8 String8::operator+(const char* other) const +{ + String8 tmp; + tmp += other; + return tmp; +} + +inline int String8::compare(const String8& other) const +{ + return strcmp(mString, other.mString); +} + +inline bool String8::operator<(const String8& other) const +{ + return strcmp(mString, other.mString) < 0; +} + +inline bool String8::operator<=(const String8& other) const +{ + return strcmp(mString, other.mString) <= 0; +} + +inline bool String8::operator==(const String8& other) const +{ + return strcmp(mString, other.mString) == 0; +} + +inline bool String8::operator!=(const String8& other) const +{ + return strcmp(mString, other.mString) != 0; +} + +inline bool String8::operator>=(const String8& other) const +{ + return strcmp(mString, other.mString) >= 0; +} + +inline bool String8::operator>(const String8& other) const +{ + return strcmp(mString, other.mString) > 0; +} + +inline bool String8::operator<(const char* other) const +{ + return strcmp(mString, other) < 0; +} + +inline bool String8::operator<=(const char* other) const +{ + return strcmp(mString, other) <= 0; +} + +inline bool String8::operator==(const char* other) const +{ + return strcmp(mString, other) == 0; +} + +inline bool String8::operator!=(const char* other) const +{ + return strcmp(mString, other) != 0; +} + +inline bool String8::operator>=(const char* other) const +{ + return strcmp(mString, other) >= 0; +} + +inline bool String8::operator>(const char* other) const +{ + return strcmp(mString, other) > 0; +} + +inline String8::operator const char*() const +{ + return mString; +} + +}; // namespace android + +// --------------------------------------------------------------------------- + +#endif // ANDROID_STRING8_H diff --git a/include/utils/SystemClock.h b/include/utils/SystemClock.h new file mode 100644 index 000000000..7c319be13 --- /dev/null +++ b/include/utils/SystemClock.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_UTILS_SYSTEMCLOCK_H +#define ANDROID_UTILS_SYSTEMCLOCK_H + +#include +#include + +namespace android { + +int setCurrentTimeMillis(int64_t millis); +int64_t uptimeMillis(); +int64_t elapsedRealtime(); + +}; // namespace android + +#endif // ANDROID_UTILS_SYSTEMCLOCK_H + diff --git a/include/utils/TextOutput.h b/include/utils/TextOutput.h new file mode 100644 index 000000000..d8d86ba82 --- /dev/null +++ b/include/utils/TextOutput.h @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2006 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_TEXTOUTPUT_H +#define ANDROID_TEXTOUTPUT_H + +#include + +#include +#include + +// --------------------------------------------------------------------------- +namespace android { + +class TextOutput +{ +public: + TextOutput() { } + virtual ~TextOutput() { } + + virtual status_t print(const char* txt, size_t len) = 0; + virtual void moveIndent(int delta) = 0; + + class Bundle { + public: + inline Bundle(TextOutput& to) : mTO(to) { to.pushBundle(); } + inline ~Bundle() { mTO.popBundle(); } + private: + TextOutput& mTO; + }; + + virtual void pushBundle() = 0; + virtual void popBundle() = 0; +}; + +// --------------------------------------------------------------------------- + +// Text output stream for printing to the log (via utils/Log.h). +extern TextOutput& alog; + +// Text output stream for printing to stdout. +extern TextOutput& aout; + +// Text output stream for printing to stderr. +extern TextOutput& aerr; + +typedef TextOutput& (*TextOutputManipFunc)(TextOutput&); + +TextOutput& endl(TextOutput& to); +TextOutput& indent(TextOutput& to); +TextOutput& dedent(TextOutput& to); + +TextOutput& operator<<(TextOutput& to, const char* str); +TextOutput& operator<<(TextOutput& to, char); // writes raw character +TextOutput& operator<<(TextOutput& to, bool); +TextOutput& operator<<(TextOutput& to, int); +TextOutput& operator<<(TextOutput& to, long); +TextOutput& operator<<(TextOutput& to, unsigned int); +TextOutput& operator<<(TextOutput& to, unsigned long); +TextOutput& operator<<(TextOutput& to, long long); +TextOutput& operator<<(TextOutput& to, unsigned long long); +TextOutput& operator<<(TextOutput& to, float); +TextOutput& operator<<(TextOutput& to, double); +TextOutput& operator<<(TextOutput& to, TextOutputManipFunc func); +TextOutput& operator<<(TextOutput& to, const void*); + +class TypeCode +{ +public: + inline TypeCode(uint32_t code); + inline ~TypeCode(); + + inline uint32_t typeCode() const; + +private: + uint32_t mCode; +}; + +TextOutput& operator<<(TextOutput& to, const TypeCode& val); + +class HexDump +{ +public: + HexDump(const void *buf, size_t size, size_t bytesPerLine=16); + inline ~HexDump(); + + inline HexDump& setBytesPerLine(size_t bytesPerLine); + inline HexDump& setSingleLineCutoff(int32_t bytes); + inline HexDump& setAlignment(size_t alignment); + inline HexDump& setCArrayStyle(bool enabled); + + inline const void* buffer() const; + inline size_t size() const; + inline size_t bytesPerLine() const; + inline int32_t singleLineCutoff() const; + inline size_t alignment() const; + inline bool carrayStyle() const; + +private: + const void* mBuffer; + size_t mSize; + size_t mBytesPerLine; + int32_t mSingleLineCutoff; + size_t mAlignment; + bool mCArrayStyle; +}; + +TextOutput& operator<<(TextOutput& to, const HexDump& val); + +// --------------------------------------------------------------------------- +// No user servicable parts below. + +inline TextOutput& endl(TextOutput& to) +{ + to.print("\n", 1); + return to; +} + +inline TextOutput& indent(TextOutput& to) +{ + to.moveIndent(1); + return to; +} + +inline TextOutput& dedent(TextOutput& to) +{ + to.moveIndent(-1); + return to; +} + +inline TextOutput& operator<<(TextOutput& to, const char* str) +{ + to.print(str, strlen(str)); + return to; +} + +inline TextOutput& operator<<(TextOutput& to, char c) +{ + to.print(&c, 1); + return to; +} + +inline TextOutput& operator<<(TextOutput& to, TextOutputManipFunc func) +{ + return (*func)(to); +} + +inline TypeCode::TypeCode(uint32_t code) : mCode(code) { } +inline TypeCode::~TypeCode() { } +inline uint32_t TypeCode::typeCode() const { return mCode; } + +inline HexDump::~HexDump() { } + +inline HexDump& HexDump::setBytesPerLine(size_t bytesPerLine) { + mBytesPerLine = bytesPerLine; return *this; +} +inline HexDump& HexDump::setSingleLineCutoff(int32_t bytes) { + mSingleLineCutoff = bytes; return *this; +} +inline HexDump& HexDump::setAlignment(size_t alignment) { + mAlignment = alignment; return *this; +} +inline HexDump& HexDump::setCArrayStyle(bool enabled) { + mCArrayStyle = enabled; return *this; +} + +inline const void* HexDump::buffer() const { return mBuffer; } +inline size_t HexDump::size() const { return mSize; } +inline size_t HexDump::bytesPerLine() const { return mBytesPerLine; } +inline int32_t HexDump::singleLineCutoff() const { return mSingleLineCutoff; } +inline size_t HexDump::alignment() const { return mAlignment; } +inline bool HexDump::carrayStyle() const { return mCArrayStyle; } + +// --------------------------------------------------------------------------- +}; // namespace android + +#endif // ANDROID_TEXTOUTPUT_H diff --git a/include/utils/TimeUtils.h b/include/utils/TimeUtils.h new file mode 100644 index 000000000..30e533036 --- /dev/null +++ b/include/utils/TimeUtils.h @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2005 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_TIME_H +#define ANDROID_TIME_H + +#include +#include +#include +#include +#include +#include + +namespace android { + +/* + * This class is the core implementation of the android.util.Time java + * class. It doesn't implement some of the methods that are implemented + * in Java. They could be done here, but it's not expected that this class + * will be used. If that assumption is incorrect, feel free to update this + * file. The reason to do it here is to not mix the implementation of this + * class and the jni glue code. + */ +class Time +{ +public: + struct tm t; + + // this object doesn't own this string + const char *timezone; + + enum { + SEC = 1, + MIN = 2, + HOUR = 3, + MDAY = 4, + MON = 5, + YEAR = 6, + WDAY = 7, + YDAY = 8 + }; + + static int compare(Time& a, Time& b); + + Time(); + + void switchTimezone(const char *timezone); + String8 format(const char *format) const; + void format2445(short* buf, bool hasTime) const; + String8 toString() const; + void setToNow(); + int64_t toMillis(bool ignoreDst); + void set(int64_t millis); + + inline void set(int sec, int min, int hour, int mday, int mon, int year, + int isdst) + { + this->t.tm_sec = sec; + this->t.tm_min = min; + this->t.tm_hour = hour; + this->t.tm_mday = mday; + this->t.tm_mon = mon; + this->t.tm_year = year; + this->t.tm_isdst = isdst; +#ifdef HAVE_TM_GMTOFF + this->t.tm_gmtoff = 0; +#endif + this->t.tm_wday = 0; + this->t.tm_yday = 0; + } +}; + +}; // namespace android + +#endif // ANDROID_TIME_H diff --git a/include/utils/TimerProbe.h b/include/utils/TimerProbe.h new file mode 100644 index 000000000..f2e32b21a --- /dev/null +++ b/include/utils/TimerProbe.h @@ -0,0 +1,72 @@ +/* + * 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. + */ + +#ifndef ANDROID_TIMER_PROBE_H +#define ANDROID_TIMER_PROBE_H + +#if 0 && defined(HAVE_POSIX_CLOCKS) +#define ENABLE_TIMER_PROBE 1 +#else +#define ENABLE_TIMER_PROBE 0 +#endif + +#if ENABLE_TIMER_PROBE + +#include +#include +#include + +#define TIMER_PROBE(tag) \ + static int _timer_slot_; \ + android::TimerProbe probe(tag, &_timer_slot_) +#define TIMER_PROBE_END() probe.end() +#else +#define TIMER_PROBE(tag) +#define TIMER_PROBE_END() +#endif + +#if ENABLE_TIMER_PROBE +namespace android { + +class TimerProbe { +public: + TimerProbe(const char tag[], int* slot); + void end(); + ~TimerProbe(); +private: + struct Bucket { + int mStart, mReal, mProcess, mThread, mCount; + const char* mTag; + int* mSlotPtr; + int mIndent; + }; + static Vector gBuckets; + static TimerProbe* gExecuteChain; + static int gIndent; + static timespec gRealBase; + TimerProbe* mNext; + static uint32_t ElapsedTime(const timespec& start, const timespec& end); + void print(const timespec& r, const timespec& p, const timespec& t) const; + timespec mRealStart, mPStart, mTStart; + const char* mTag; + int mIndent; + int mBucket; +}; + +}; // namespace android + +#endif +#endif diff --git a/include/utils/Timers.h b/include/utils/Timers.h new file mode 100644 index 000000000..96103995b --- /dev/null +++ b/include/utils/Timers.h @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2005 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. + */ + +// +// Timer functions. +// +#ifndef _LIBS_UTILS_TIMERS_H +#define _LIBS_UTILS_TIMERS_H + +#include +#include +#include + +// ------------------------------------------------------------------ +// C API + +#ifdef __cplusplus +extern "C" { +#endif + +typedef int64_t nsecs_t; // nano-seconds + +static inline nsecs_t seconds_to_nanoseconds(nsecs_t secs) +{ + return secs*1000000000; +} + +static inline nsecs_t milliseconds_to_nanoseconds(nsecs_t secs) +{ + return secs*1000000; +} + +static inline nsecs_t microseconds_to_nanoseconds(nsecs_t secs) +{ + return secs*1000; +} + +static inline nsecs_t nanoseconds_to_seconds(nsecs_t secs) +{ + return secs/1000000000; +} + +static inline nsecs_t nanoseconds_to_milliseconds(nsecs_t secs) +{ + return secs/1000000; +} + +static inline nsecs_t nanoseconds_to_microseconds(nsecs_t secs) +{ + return secs/1000; +} + +static inline nsecs_t s2ns(nsecs_t v) {return seconds_to_nanoseconds(v);} +static inline nsecs_t ms2ns(nsecs_t v) {return milliseconds_to_nanoseconds(v);} +static inline nsecs_t us2ns(nsecs_t v) {return microseconds_to_nanoseconds(v);} +static inline nsecs_t ns2s(nsecs_t v) {return nanoseconds_to_seconds(v);} +static inline nsecs_t ns2ms(nsecs_t v) {return nanoseconds_to_milliseconds(v);} +static inline nsecs_t ns2us(nsecs_t v) {return nanoseconds_to_microseconds(v);} + +static inline nsecs_t seconds(nsecs_t v) { return s2ns(v); } +static inline nsecs_t milliseconds(nsecs_t v) { return ms2ns(v); } +static inline nsecs_t microseconds(nsecs_t v) { return us2ns(v); } + +enum { + SYSTEM_TIME_REALTIME = 0, // system-wide realtime clock + SYSTEM_TIME_MONOTONIC = 1, // monotonic time since unspecified starting point + SYSTEM_TIME_PROCESS = 2, // high-resolution per-process clock + SYSTEM_TIME_THREAD = 3 // high-resolution per-thread clock +}; + +// return the system-time according to the specified clock +#ifdef __cplusplus +nsecs_t systemTime(int clock = SYSTEM_TIME_MONOTONIC); +#else +nsecs_t systemTime(int clock); +#endif // def __cplusplus + +// return the system-time according to the specified clock +int sleepForInterval(long interval, struct timeval* pNextTick); + +#ifdef __cplusplus +} // extern "C" +#endif + +// ------------------------------------------------------------------ +// C++ API + +#ifdef __cplusplus + +namespace android { +/* + * Time the duration of something. + * + * Includes some timeval manipulation functions. + */ +class DurationTimer { +public: + DurationTimer(void) {} + ~DurationTimer(void) {} + + // Start the timer. + void start(void); + // Stop the timer. + void stop(void); + // Get the duration in microseconds. + long long durationUsecs(void) const; + + // Subtract two timevals. Returns the difference (ptv1-ptv2) in + // microseconds. + static long long subtractTimevals(const struct timeval* ptv1, + const struct timeval* ptv2); + + // Add the specified amount of time to the timeval. + static void addToTimeval(struct timeval* ptv, long usec); + +private: + struct timeval mStartWhen; + struct timeval mStopWhen; +}; + +}; // android +#endif // def __cplusplus + +#endif // _LIBS_UTILS_TIMERS_H diff --git a/include/utils/TypeHelpers.h b/include/utils/TypeHelpers.h new file mode 100644 index 000000000..c04c37fa9 --- /dev/null +++ b/include/utils/TypeHelpers.h @@ -0,0 +1,254 @@ +/* + * Copyright (C) 2005 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_TYPE_HELPERS_H +#define ANDROID_TYPE_HELPERS_H + +#include +#include +#include +#include + +// --------------------------------------------------------------------------- + +namespace android { + +/* + * Types traits + */ + +template struct trait_trivial_ctor { enum { value = false }; }; +template struct trait_trivial_dtor { enum { value = false }; }; +template struct trait_trivial_copy { enum { value = false }; }; +template struct trait_trivial_assign{ enum { value = false }; }; + +template struct trait_pointer { enum { value = false }; }; +template struct trait_pointer { enum { value = true }; }; + +#define ANDROID_BASIC_TYPES_TRAITS( T ) \ + template<> struct trait_trivial_ctor< T > { enum { value = true }; }; \ + template<> struct trait_trivial_dtor< T > { enum { value = true }; }; \ + template<> struct trait_trivial_copy< T > { enum { value = true }; }; \ + template<> struct trait_trivial_assign< T >{ enum { value = true }; }; + +#define ANDROID_TYPE_TRAITS( T, ctor, dtor, copy, assign ) \ + template<> struct trait_trivial_ctor< T > { enum { value = ctor }; }; \ + template<> struct trait_trivial_dtor< T > { enum { value = dtor }; }; \ + template<> struct trait_trivial_copy< T > { enum { value = copy }; }; \ + template<> struct trait_trivial_assign< T >{ enum { value = assign }; }; + +template +struct traits { + enum { + is_pointer = trait_pointer::value, + has_trivial_ctor = is_pointer || trait_trivial_ctor::value, + has_trivial_dtor = is_pointer || trait_trivial_dtor::value, + has_trivial_copy = is_pointer || trait_trivial_copy::value, + has_trivial_assign = is_pointer || trait_trivial_assign::value + }; +}; + +template +struct aggregate_traits { + enum { + is_pointer = false, + has_trivial_ctor = traits::has_trivial_ctor && traits::has_trivial_ctor, + has_trivial_dtor = traits::has_trivial_dtor && traits::has_trivial_dtor, + has_trivial_copy = traits::has_trivial_copy && traits::has_trivial_copy, + has_trivial_assign = traits::has_trivial_assign && traits::has_trivial_assign + }; +}; + +// --------------------------------------------------------------------------- + +/* + * basic types traits + */ + +ANDROID_BASIC_TYPES_TRAITS( void ); +ANDROID_BASIC_TYPES_TRAITS( bool ); +ANDROID_BASIC_TYPES_TRAITS( char ); +ANDROID_BASIC_TYPES_TRAITS( unsigned char ); +ANDROID_BASIC_TYPES_TRAITS( short ); +ANDROID_BASIC_TYPES_TRAITS( unsigned short ); +ANDROID_BASIC_TYPES_TRAITS( int ); +ANDROID_BASIC_TYPES_TRAITS( unsigned int ); +ANDROID_BASIC_TYPES_TRAITS( long ); +ANDROID_BASIC_TYPES_TRAITS( unsigned long ); +ANDROID_BASIC_TYPES_TRAITS( long long ); +ANDROID_BASIC_TYPES_TRAITS( unsigned long long ); +ANDROID_BASIC_TYPES_TRAITS( float ); +ANDROID_BASIC_TYPES_TRAITS( double ); + +// --------------------------------------------------------------------------- + + +/* + * compare and order types + */ + +template inline +int strictly_order_type(const TYPE& lhs, const TYPE& rhs) { + return (lhs < rhs) ? 1 : 0; +} + +template inline +int compare_type(const TYPE& lhs, const TYPE& rhs) { + return strictly_order_type(rhs, lhs) - strictly_order_type(lhs, rhs); +} + +/* + * create, destroy, copy and assign types... + */ + +template inline +void construct_type(TYPE* p, size_t n) { + if (!traits::has_trivial_ctor) { + while (n--) { + new(p++) TYPE; + } + } +} + +template inline +void destroy_type(TYPE* p, size_t n) { + if (!traits::has_trivial_dtor) { + while (n--) { + p->~TYPE(); + p++; + } + } +} + +template inline +void copy_type(TYPE* d, const TYPE* s, size_t n) { + if (!traits::has_trivial_copy) { + while (n--) { + new(d) TYPE(*s); + d++, s++; + } + } else { + memcpy(d,s,n*sizeof(TYPE)); + } +} + +template inline +void assign_type(TYPE* d, const TYPE* s, size_t n) { + if (!traits::has_trivial_assign) { + while (n--) { + *d++ = *s++; + } + } else { + memcpy(d,s,n*sizeof(TYPE)); + } +} + +template inline +void splat_type(TYPE* where, const TYPE* what, size_t n) { + if (!traits::has_trivial_copy) { + while (n--) { + new(where) TYPE(*what); + where++; + } + } else { + while (n--) { + *where++ = *what; + } + } +} + +template inline +void move_forward_type(TYPE* d, const TYPE* s, size_t n = 1) { + if (!traits::has_trivial_copy || !traits::has_trivial_dtor) { + d += n; + s += n; + while (n--) { + --d, --s; + if (!traits::has_trivial_copy) { + new(d) TYPE(*s); + } else { + *d = *s; + } + if (!traits::has_trivial_dtor) { + s->~TYPE(); + } + } + } else { + memmove(d,s,n*sizeof(TYPE)); + } +} + +template inline +void move_backward_type(TYPE* d, const TYPE* s, size_t n = 1) { + if (!traits::has_trivial_copy || !traits::has_trivial_dtor) { + while (n--) { + if (!traits::has_trivial_copy) { + new(d) TYPE(*s); + } else { + *d = *s; + } + if (!traits::has_trivial_dtor) { + s->~TYPE(); + } + d++, s++; + } + } else { + memmove(d,s,n*sizeof(TYPE)); + } +} +// --------------------------------------------------------------------------- + +/* + * a key/value pair + */ + +template +struct key_value_pair_t { + KEY key; + VALUE value; + key_value_pair_t() { } + key_value_pair_t(const key_value_pair_t& o) : key(o.key), value(o.value) { } + key_value_pair_t(const KEY& k, const VALUE& v) : key(k), value(v) { } + key_value_pair_t(const KEY& k) : key(k) { } + inline bool operator < (const key_value_pair_t& o) const { + return strictly_order_type(key, o.key); + } +}; + +template<> +template +struct trait_trivial_ctor< key_value_pair_t > +{ enum { value = aggregate_traits::has_trivial_ctor }; }; +template<> +template +struct trait_trivial_dtor< key_value_pair_t > +{ enum { value = aggregate_traits::has_trivial_dtor }; }; +template<> +template +struct trait_trivial_copy< key_value_pair_t > +{ enum { value = aggregate_traits::has_trivial_copy }; }; +template<> +template +struct trait_trivial_assign< key_value_pair_t > +{ enum { value = aggregate_traits::has_trivial_assign};}; + +// --------------------------------------------------------------------------- + +}; // namespace android + +// --------------------------------------------------------------------------- + +#endif // ANDROID_TYPE_HELPERS_H diff --git a/include/utils/Vector.h b/include/utils/Vector.h new file mode 100644 index 000000000..be365d83e --- /dev/null +++ b/include/utils/Vector.h @@ -0,0 +1,359 @@ +/* + * Copyright (C) 2005 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_VECTOR_H +#define ANDROID_VECTOR_H + +#include +#include +#include + +#include +#include +#include + +// --------------------------------------------------------------------------- + +namespace android { + +/*! + * The main templated vector class ensuring type safety + * while making use of VectorImpl. + * This is the class users want to use. + */ + +template +class Vector : private VectorImpl +{ +public: + typedef TYPE value_type; + + /*! + * Constructors and destructors + */ + + Vector(); + Vector(const Vector& rhs); + virtual ~Vector(); + + /*! copy operator */ + const Vector& operator = (const Vector& rhs) const; + Vector& operator = (const Vector& rhs); + + /* + * empty the vector + */ + + inline void clear() { VectorImpl::clear(); } + + /*! + * vector stats + */ + + //! returns number of items in the vector + inline size_t size() const { return VectorImpl::size(); } + //! returns wether or not the vector is empty + inline bool isEmpty() const { return VectorImpl::isEmpty(); } + //! returns how many items can be stored without reallocating the backing store + inline size_t capacity() const { return VectorImpl::capacity(); } + //! setst the capacity. capacity can never be reduced less than size() + inline ssize_t setCapacity(size_t size) { return VectorImpl::setCapacity(size); } + + /*! + * C-style array access + */ + + //! read-only C-style access + inline const TYPE* array() const; + //! read-write C-style access + TYPE* editArray(); + + /*! + * accessors + */ + + //! read-only access to an item at a given index + inline const TYPE& operator [] (size_t index) const; + //! alternate name for operator [] + inline const TYPE& itemAt(size_t index) const; + //! stack-usage of the vector. returns the top of the stack (last element) + const TYPE& top() const; + //! same as operator [], but allows to access the vector backward (from the end) with a negative index + const TYPE& mirrorItemAt(ssize_t index) const; + + /*! + * modifing the array + */ + + //! copy-on write support, grants write access to an item + TYPE& editItemAt(size_t index); + //! grants right acces to the top of the stack (last element) + TYPE& editTop(); + + /*! + * append/insert another vector + */ + + //! insert another vector at a given index + ssize_t insertVectorAt(const Vector& vector, size_t index); + + //! append another vector at the end of this one + ssize_t appendVector(const Vector& vector); + + + /*! + * add/insert/replace items + */ + + //! insert one or several items initialized with their default constructor + inline ssize_t insertAt(size_t index, size_t numItems = 1); + //! insert on onr several items initialized from a prototype item + ssize_t insertAt(const TYPE& prototype_item, size_t index, size_t numItems = 1); + //! pop the top of the stack (removes the last element). No-op if the stack's empty + inline void pop(); + //! pushes an item initialized with its default constructor + inline void push(); + //! pushes an item on the top of the stack + void push(const TYPE& item); + //! same as push() but returns the index the item was added at (or an error) + inline ssize_t add(); + //! same as push() but returns the index the item was added at (or an error) + ssize_t add(const TYPE& item); + //! replace an item with a new one initialized with its default constructor + inline ssize_t replaceAt(size_t index); + //! replace an item with a new one + ssize_t replaceAt(const TYPE& item, size_t index); + + /*! + * remove items + */ + + //! remove several items + inline ssize_t removeItemsAt(size_t index, size_t count = 1); + //! remove one item + inline ssize_t removeAt(size_t index) { return removeItemsAt(index); } + + /*! + * sort (stable) the array + */ + + typedef int (*compar_t)(const TYPE* lhs, const TYPE* rhs); + typedef int (*compar_r_t)(const TYPE* lhs, const TYPE* rhs, void* state); + + inline status_t sort(compar_t cmp); + inline status_t sort(compar_r_t cmp, void* state); + +protected: + virtual void do_construct(void* storage, size_t num) const; + virtual void do_destroy(void* storage, size_t num) const; + virtual void do_copy(void* dest, const void* from, size_t num) const; + virtual void do_splat(void* dest, const void* item, size_t num) const; + virtual void do_move_forward(void* dest, const void* from, size_t num) const; + virtual void do_move_backward(void* dest, const void* from, size_t num) const; +}; + + +// --------------------------------------------------------------------------- +// No user serviceable parts from here... +// --------------------------------------------------------------------------- + +template inline +Vector::Vector() + : VectorImpl(sizeof(TYPE), + ((traits::has_trivial_ctor ? HAS_TRIVIAL_CTOR : 0) + |(traits::has_trivial_dtor ? HAS_TRIVIAL_DTOR : 0) + |(traits::has_trivial_copy ? HAS_TRIVIAL_COPY : 0) + |(traits::has_trivial_assign ? HAS_TRIVIAL_ASSIGN : 0)) + ) +{ +} + +template inline +Vector::Vector(const Vector& rhs) + : VectorImpl(rhs) { +} + +template inline +Vector::~Vector() { + finish_vector(); +} + +template inline +Vector& Vector::operator = (const Vector& rhs) { + VectorImpl::operator = (rhs); + return *this; +} + +template inline +const Vector& Vector::operator = (const Vector& rhs) const { + VectorImpl::operator = (rhs); + return *this; +} + +template inline +const TYPE* Vector::array() const { + return static_cast(arrayImpl()); +} + +template inline +TYPE* Vector::editArray() { + return static_cast(editArrayImpl()); +} + + +template inline +const TYPE& Vector::operator[](size_t index) const { + LOG_FATAL_IF( index>=size(), + "itemAt: index %d is past size %d", (int)index, (int)size() ); + return *(array() + index); +} + +template inline +const TYPE& Vector::itemAt(size_t index) const { + return operator[](index); +} + +template inline +const TYPE& Vector::mirrorItemAt(ssize_t index) const { + LOG_FATAL_IF( (index>0 ? index : -index)>=size(), + "mirrorItemAt: index %d is past size %d", + (int)index, (int)size() ); + return *(array() + ((index<0) ? (size()-index) : index)); +} + +template inline +const TYPE& Vector::top() const { + return *(array() + size() - 1); +} + +template inline +TYPE& Vector::editItemAt(size_t index) { + return *( static_cast(editItemLocation(index)) ); +} + +template inline +TYPE& Vector::editTop() { + return *( static_cast(editItemLocation(size()-1)) ); +} + +template inline +ssize_t Vector::insertVectorAt(const Vector& vector, size_t index) { + return VectorImpl::insertVectorAt(reinterpret_cast(vector), index); +} + +template inline +ssize_t Vector::appendVector(const Vector& vector) { + return VectorImpl::appendVector(reinterpret_cast(vector)); +} + +template inline +ssize_t Vector::insertAt(const TYPE& item, size_t index, size_t numItems) { + return VectorImpl::insertAt(&item, index, numItems); +} + +template inline +void Vector::push(const TYPE& item) { + return VectorImpl::push(&item); +} + +template inline +ssize_t Vector::add(const TYPE& item) { + return VectorImpl::add(&item); +} + +template inline +ssize_t Vector::replaceAt(const TYPE& item, size_t index) { + return VectorImpl::replaceAt(&item, index); +} + +template inline +ssize_t Vector::insertAt(size_t index, size_t numItems) { + return VectorImpl::insertAt(index, numItems); +} + +template inline +void Vector::pop() { + VectorImpl::pop(); +} + +template inline +void Vector::push() { + VectorImpl::push(); +} + +template inline +ssize_t Vector::add() { + return VectorImpl::add(); +} + +template inline +ssize_t Vector::replaceAt(size_t index) { + return VectorImpl::replaceAt(index); +} + +template inline +ssize_t Vector::removeItemsAt(size_t index, size_t count) { + return VectorImpl::removeItemsAt(index, count); +} + +template inline +status_t Vector::sort(Vector::compar_t cmp) { + return VectorImpl::sort((VectorImpl::compar_t)cmp); +} + +template inline +status_t Vector::sort(Vector::compar_r_t cmp, void* state) { + return VectorImpl::sort((VectorImpl::compar_r_t)cmp, state); +} + +// --------------------------------------------------------------------------- + +template +void Vector::do_construct(void* storage, size_t num) const { + construct_type( reinterpret_cast(storage), num ); +} + +template +void Vector::do_destroy(void* storage, size_t num) const { + destroy_type( reinterpret_cast(storage), num ); +} + +template +void Vector::do_copy(void* dest, const void* from, size_t num) const { + copy_type( reinterpret_cast(dest), reinterpret_cast(from), num ); +} + +template +void Vector::do_splat(void* dest, const void* item, size_t num) const { + splat_type( reinterpret_cast(dest), reinterpret_cast(item), num ); +} + +template +void Vector::do_move_forward(void* dest, const void* from, size_t num) const { + move_forward_type( reinterpret_cast(dest), reinterpret_cast(from), num ); +} + +template +void Vector::do_move_backward(void* dest, const void* from, size_t num) const { + move_backward_type( reinterpret_cast(dest), reinterpret_cast(from), num ); +} + +}; // namespace android + + +// --------------------------------------------------------------------------- + +#endif // ANDROID_VECTOR_H diff --git a/include/utils/VectorImpl.h b/include/utils/VectorImpl.h new file mode 100644 index 000000000..2525229be --- /dev/null +++ b/include/utils/VectorImpl.h @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2005 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_VECTOR_IMPL_H +#define ANDROID_VECTOR_IMPL_H + +#include +#include +#include +#include + +// --------------------------------------------------------------------------- +// No user serviceable parts in here... +// --------------------------------------------------------------------------- + +namespace android { + +/*! + * Implementation of the guts of the vector<> class + * this ensures backward binary compatibility and + * reduces code size. + * For performance reasons, we expose mStorage and mCount + * so these fields are set in stone. + * + */ + +class VectorImpl +{ +public: + enum { // flags passed to the ctor + HAS_TRIVIAL_CTOR = 0x00000001, + HAS_TRIVIAL_DTOR = 0x00000002, + HAS_TRIVIAL_COPY = 0x00000004, + HAS_TRIVIAL_ASSIGN = 0x00000008 + }; + + VectorImpl(size_t itemSize, uint32_t flags); + VectorImpl(const VectorImpl& rhs); + virtual ~VectorImpl(); + + /*! must be called from subclasses destructor */ + void finish_vector(); + + VectorImpl& operator = (const VectorImpl& rhs); + + /*! C-style array access */ + inline const void* arrayImpl() const { return mStorage; } + void* editArrayImpl(); + + /*! vector stats */ + inline size_t size() const { return mCount; } + inline bool isEmpty() const { return mCount == 0; } + size_t capacity() const; + ssize_t setCapacity(size_t size); + + /*! append/insert another vector */ + ssize_t insertVectorAt(const VectorImpl& vector, size_t index); + ssize_t appendVector(const VectorImpl& vector); + + /*! add/insert/replace items */ + ssize_t insertAt(size_t where, size_t numItems = 1); + ssize_t insertAt(const void* item, size_t where, size_t numItems = 1); + void pop(); + void push(); + void push(const void* item); + ssize_t add(); + ssize_t add(const void* item); + ssize_t replaceAt(size_t index); + ssize_t replaceAt(const void* item, size_t index); + + /*! remove items */ + ssize_t removeItemsAt(size_t index, size_t count = 1); + void clear(); + + const void* itemLocation(size_t index) const; + void* editItemLocation(size_t index); + + typedef int (*compar_t)(const void* lhs, const void* rhs); + typedef int (*compar_r_t)(const void* lhs, const void* rhs, void* state); + status_t sort(compar_t cmp); + status_t sort(compar_r_t cmp, void* state); + +protected: + size_t itemSize() const; + void release_storage(); + + virtual void do_construct(void* storage, size_t num) const = 0; + virtual void do_destroy(void* storage, size_t num) const = 0; + virtual void do_copy(void* dest, const void* from, size_t num) const = 0; + virtual void do_splat(void* dest, const void* item, size_t num) const = 0; + virtual void do_move_forward(void* dest, const void* from, size_t num) const = 0; + virtual void do_move_backward(void* dest, const void* from, size_t num) const = 0; + + // take care of FBC... + virtual void reservedVectorImpl1(); + virtual void reservedVectorImpl2(); + virtual void reservedVectorImpl3(); + virtual void reservedVectorImpl4(); + virtual void reservedVectorImpl5(); + virtual void reservedVectorImpl6(); + virtual void reservedVectorImpl7(); + virtual void reservedVectorImpl8(); + +private: + void* _grow(size_t where, size_t amount); + void _shrink(size_t where, size_t amount); + + inline void _do_construct(void* storage, size_t num) const; + inline void _do_destroy(void* storage, size_t num) const; + inline void _do_copy(void* dest, const void* from, size_t num) const; + inline void _do_splat(void* dest, const void* item, size_t num) const; + inline void _do_move_forward(void* dest, const void* from, size_t num) const; + inline void _do_move_backward(void* dest, const void* from, size_t num) const; + + // These 2 fields are exposed in the inlines below, + // so they're set in stone. + void * mStorage; // base address of the vector + size_t mCount; // number of items + + const uint32_t mFlags; + const size_t mItemSize; +}; + + + +class SortedVectorImpl : public VectorImpl +{ +public: + SortedVectorImpl(size_t itemSize, uint32_t flags); + SortedVectorImpl(const VectorImpl& rhs); + virtual ~SortedVectorImpl(); + + SortedVectorImpl& operator = (const SortedVectorImpl& rhs); + + //! finds the index of an item + ssize_t indexOf(const void* item) const; + + //! finds where this item should be inserted + size_t orderOf(const void* item) const; + + //! add an item in the right place (or replaces it if there is one) + ssize_t add(const void* item); + + //! merges a vector into this one + ssize_t merge(const VectorImpl& vector); + ssize_t merge(const SortedVectorImpl& vector); + + //! removes an item + ssize_t remove(const void* item); + +protected: + virtual int do_compare(const void* lhs, const void* rhs) const = 0; + + // take care of FBC... + virtual void reservedSortedVectorImpl1(); + virtual void reservedSortedVectorImpl2(); + virtual void reservedSortedVectorImpl3(); + virtual void reservedSortedVectorImpl4(); + virtual void reservedSortedVectorImpl5(); + virtual void reservedSortedVectorImpl6(); + virtual void reservedSortedVectorImpl7(); + virtual void reservedSortedVectorImpl8(); + +private: + ssize_t _indexOrderOf(const void* item, size_t* order = 0) const; + + // these are made private, because they can't be used on a SortedVector + // (they don't have an implementation either) + ssize_t add(); + void pop(); + void push(); + void push(const void* item); + ssize_t insertVectorAt(const VectorImpl& vector, size_t index); + ssize_t appendVector(const VectorImpl& vector); + ssize_t insertAt(size_t where, size_t numItems = 1); + ssize_t insertAt(const void* item, size_t where, size_t numItems = 1); + ssize_t replaceAt(size_t index); + ssize_t replaceAt(const void* item, size_t index); +}; + +}; // namespace android + + +// --------------------------------------------------------------------------- + +#endif // ANDROID_VECTOR_IMPL_H diff --git a/include/utils/ZipEntry.h b/include/utils/ZipEntry.h new file mode 100644 index 000000000..e4698dfbb --- /dev/null +++ b/include/utils/ZipEntry.h @@ -0,0 +1,345 @@ +/* + * Copyright (C) 2006 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. + */ + +// +// Zip archive entries. +// +// The ZipEntry class is tightly meshed with the ZipFile class. +// +#ifndef __LIBS_ZIPENTRY_H +#define __LIBS_ZIPENTRY_H + +#include "Errors.h" + +#include +#include + +namespace android { + +class ZipFile; + +/* + * ZipEntry objects represent a single entry in a Zip archive. + * + * You can use one of these to get or set information about an entry, but + * there are no functions here for accessing the data itself. (We could + * tuck a pointer to the ZipFile in here for convenience, but that raises + * the likelihood of using ZipEntry objects after discarding the ZipFile.) + * + * File information is stored in two places: next to the file data (the Local + * File Header, and possibly a Data Descriptor), and at the end of the file + * (the Central Directory Entry). The two must be kept in sync. + */ +class ZipEntry { +public: + friend class ZipFile; + + ZipEntry(void) + : mDeleted(false), mMarked(false) + {} + ~ZipEntry(void) {} + + /* + * Returns "true" if the data is compressed. + */ + bool isCompressed(void) const { + return mCDE.mCompressionMethod != kCompressStored; + } + int getCompressionMethod(void) const { return mCDE.mCompressionMethod; } + + /* + * Return the uncompressed length. + */ + off_t getUncompressedLen(void) const { return mCDE.mUncompressedSize; } + + /* + * Return the compressed length. For uncompressed data, this returns + * the same thing as getUncompresesdLen(). + */ + off_t getCompressedLen(void) const { return mCDE.mCompressedSize; } + + /* + * Return the absolute file offset of the start of the compressed or + * uncompressed data. + */ + off_t getFileOffset(void) const { + return mCDE.mLocalHeaderRelOffset + + LocalFileHeader::kLFHLen + + mLFH.mFileNameLength + + mLFH.mExtraFieldLength; + } + + /* + * Return the data CRC. + */ + unsigned long getCRC32(void) const { return mCDE.mCRC32; } + + /* + * Return file modification time in UNIX seconds-since-epoch. + */ + time_t getModWhen(void) const; + + /* + * Return the archived file name. + */ + const char* getFileName(void) const { return (const char*) mCDE.mFileName; } + + /* + * Application-defined "mark". Can be useful when synchronizing the + * contents of an archive with contents on disk. + */ + bool getMarked(void) const { return mMarked; } + void setMarked(bool val) { mMarked = val; } + + /* + * Some basic functions for raw data manipulation. "LE" means + * Little Endian. + */ + static inline unsigned short getShortLE(const unsigned char* buf) { + return buf[0] | (buf[1] << 8); + } + static inline unsigned long getLongLE(const unsigned char* buf) { + return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); + } + static inline void putShortLE(unsigned char* buf, short val) { + buf[0] = (unsigned char) val; + buf[1] = (unsigned char) (val >> 8); + } + static inline void putLongLE(unsigned char* buf, long val) { + buf[0] = (unsigned char) val; + buf[1] = (unsigned char) (val >> 8); + buf[2] = (unsigned char) (val >> 16); + buf[3] = (unsigned char) (val >> 24); + } + + /* defined for Zip archives */ + enum { + kCompressStored = 0, // no compression + // shrunk = 1, + // reduced 1 = 2, + // reduced 2 = 3, + // reduced 3 = 4, + // reduced 4 = 5, + // imploded = 6, + // tokenized = 7, + kCompressDeflated = 8, // standard deflate + // Deflate64 = 9, + // lib imploded = 10, + // reserved = 11, + // bzip2 = 12, + }; + + /* + * Deletion flag. If set, the entry will be removed on the next + * call to "flush". + */ + bool getDeleted(void) const { return mDeleted; } + +protected: + /* + * Initialize the structure from the file, which is pointing at + * our Central Directory entry. + */ + status_t initFromCDE(FILE* fp); + + /* + * Initialize the structure for a new file. We need the filename + * and comment so that we can properly size the LFH area. The + * filename is mandatory, the comment is optional. + */ + void initNew(const char* fileName, const char* comment); + + /* + * Initialize the structure with the contents of a ZipEntry from + * another file. + */ + status_t initFromExternal(const ZipFile* pZipFile, const ZipEntry* pEntry); + + /* + * Add some pad bytes to the LFH. We do this by adding or resizing + * the "extra" field. + */ + status_t addPadding(int padding); + + /* + * Set information about the data for this entry. + */ + void setDataInfo(long uncompLen, long compLen, unsigned long crc32, + int compressionMethod); + + /* + * Set the modification date. + */ + void setModWhen(time_t when); + + /* + * Return the offset of the local file header. + */ + off_t getLFHOffset(void) const { return mCDE.mLocalHeaderRelOffset; } + + /* + * Set the offset of the local file header, relative to the start of + * the current file. + */ + void setLFHOffset(off_t offset) { + mCDE.mLocalHeaderRelOffset = (long) offset; + } + + /* mark for deletion; used by ZipFile::remove() */ + void setDeleted(void) { mDeleted = true; } + +private: + /* these are private and not defined */ + ZipEntry(const ZipEntry& src); + ZipEntry& operator=(const ZipEntry& src); + + /* returns "true" if the CDE and the LFH agree */ + bool compareHeaders(void) const; + void copyCDEtoLFH(void); + + bool mDeleted; // set if entry is pending deletion + bool mMarked; // app-defined marker + + /* + * Every entry in the Zip archive starts off with one of these. + */ + class LocalFileHeader { + public: + LocalFileHeader(void) : + mVersionToExtract(0), + mGPBitFlag(0), + mCompressionMethod(0), + mLastModFileTime(0), + mLastModFileDate(0), + mCRC32(0), + mCompressedSize(0), + mUncompressedSize(0), + mFileNameLength(0), + mExtraFieldLength(0), + mFileName(NULL), + mExtraField(NULL) + {} + virtual ~LocalFileHeader(void) { + delete[] mFileName; + delete[] mExtraField; + } + + status_t read(FILE* fp); + status_t write(FILE* fp); + + // unsigned long mSignature; + unsigned short mVersionToExtract; + unsigned short mGPBitFlag; + unsigned short mCompressionMethod; + unsigned short mLastModFileTime; + unsigned short mLastModFileDate; + unsigned long mCRC32; + unsigned long mCompressedSize; + unsigned long mUncompressedSize; + unsigned short mFileNameLength; + unsigned short mExtraFieldLength; + unsigned char* mFileName; + unsigned char* mExtraField; + + enum { + kSignature = 0x04034b50, + kLFHLen = 30, // LocalFileHdr len, excl. var fields + }; + + void dump(void) const; + }; + + /* + * Every entry in the Zip archive has one of these in the "central + * directory" at the end of the file. + */ + class CentralDirEntry { + public: + CentralDirEntry(void) : + mVersionMadeBy(0), + mVersionToExtract(0), + mGPBitFlag(0), + mCompressionMethod(0), + mLastModFileTime(0), + mLastModFileDate(0), + mCRC32(0), + mCompressedSize(0), + mUncompressedSize(0), + mFileNameLength(0), + mExtraFieldLength(0), + mFileCommentLength(0), + mDiskNumberStart(0), + mInternalAttrs(0), + mExternalAttrs(0), + mLocalHeaderRelOffset(0), + mFileName(NULL), + mExtraField(NULL), + mFileComment(NULL) + {} + virtual ~CentralDirEntry(void) { + delete[] mFileName; + delete[] mExtraField; + delete[] mFileComment; + } + + status_t read(FILE* fp); + status_t write(FILE* fp); + + // unsigned long mSignature; + unsigned short mVersionMadeBy; + unsigned short mVersionToExtract; + unsigned short mGPBitFlag; + unsigned short mCompressionMethod; + unsigned short mLastModFileTime; + unsigned short mLastModFileDate; + unsigned long mCRC32; + unsigned long mCompressedSize; + unsigned long mUncompressedSize; + unsigned short mFileNameLength; + unsigned short mExtraFieldLength; + unsigned short mFileCommentLength; + unsigned short mDiskNumberStart; + unsigned short mInternalAttrs; + unsigned long mExternalAttrs; + unsigned long mLocalHeaderRelOffset; + unsigned char* mFileName; + unsigned char* mExtraField; + unsigned char* mFileComment; + + void dump(void) const; + + enum { + kSignature = 0x02014b50, + kCDELen = 46, // CentralDirEnt len, excl. var fields + }; + }; + + enum { + //kDataDescriptorSignature = 0x08074b50, // currently unused + kDataDescriptorLen = 16, // four 32-bit fields + + kDefaultVersion = 20, // need deflate, nothing much else + kDefaultMadeBy = 0x0317, // 03=UNIX, 17=spec v2.3 + kUsesDataDescr = 0x0008, // GPBitFlag bit 3 + }; + + LocalFileHeader mLFH; + CentralDirEntry mCDE; +}; + +}; // namespace android + +#endif // __LIBS_ZIPENTRY_H diff --git a/include/utils/ZipFile.h b/include/utils/ZipFile.h new file mode 100644 index 000000000..44df5bbaa --- /dev/null +++ b/include/utils/ZipFile.h @@ -0,0 +1,269 @@ +/* + * Copyright (C) 2006 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. + */ + +// +// General-purpose Zip archive access. This class allows both reading and +// writing to Zip archives, including deletion of existing entries. +// +#ifndef __LIBS_ZIPFILE_H +#define __LIBS_ZIPFILE_H + +#include "ZipEntry.h" +#include "Vector.h" +#include "Errors.h" +#include + +namespace android { + +/* + * Manipulate a Zip archive. + * + * Some changes will not be visible in the until until "flush" is called. + * + * The correct way to update a file archive is to make all changes to a + * copy of the archive in a temporary file, and then unlink/rename over + * the original after everything completes. Because we're only interested + * in using this for packaging, we don't worry about such things. Crashing + * after making changes and before flush() completes could leave us with + * an unusable Zip archive. + */ +class ZipFile { +public: + ZipFile(void) + : mZipFp(NULL), mReadOnly(false), mNeedCDRewrite(false) + {} + ~ZipFile(void) { + if (!mReadOnly) + flush(); + if (mZipFp != NULL) + fclose(mZipFp); + discardEntries(); + } + + /* + * Open a new or existing archive. + */ + typedef enum { + kOpenReadOnly = 0x01, + kOpenReadWrite = 0x02, + kOpenCreate = 0x04, // create if it doesn't exist + kOpenTruncate = 0x08, // if it exists, empty it + }; + status_t open(const char* zipFileName, int flags); + + /* + * Add a file to the end of the archive. Specify whether you want the + * library to try to store it compressed. + * + * If "storageName" is specified, the archive will use that instead + * of "fileName". + * + * If there is already an entry with the same name, the call fails. + * Existing entries with the same name must be removed first. + * + * If "ppEntry" is non-NULL, a pointer to the new entry will be returned. + */ + status_t add(const char* fileName, int compressionMethod, + ZipEntry** ppEntry) + { + return add(fileName, fileName, compressionMethod, ppEntry); + } + status_t add(const char* fileName, const char* storageName, + int compressionMethod, ZipEntry** ppEntry) + { + return addCommon(fileName, NULL, 0, storageName, + ZipEntry::kCompressStored, + compressionMethod, ppEntry); + } + + /* + * Add a file that is already compressed with gzip. + * + * If "ppEntry" is non-NULL, a pointer to the new entry will be returned. + */ + status_t addGzip(const char* fileName, const char* storageName, + ZipEntry** ppEntry) + { + return addCommon(fileName, NULL, 0, storageName, + ZipEntry::kCompressDeflated, + ZipEntry::kCompressDeflated, ppEntry); + } + + /* + * Add a file from an in-memory data buffer. + * + * If "ppEntry" is non-NULL, a pointer to the new entry will be returned. + */ + status_t add(const void* data, size_t size, const char* storageName, + int compressionMethod, ZipEntry** ppEntry) + { + return addCommon(NULL, data, size, storageName, + ZipEntry::kCompressStored, + compressionMethod, ppEntry); + } + + /* + * Add an entry by copying it from another zip file. If "padding" is + * nonzero, the specified number of bytes will be added to the "extra" + * field in the header. + * + * If "ppEntry" is non-NULL, a pointer to the new entry will be returned. + */ + status_t add(const ZipFile* pSourceZip, const ZipEntry* pSourceEntry, + int padding, ZipEntry** ppEntry); + + /* + * Mark an entry as having been removed. It is not actually deleted + * from the archive or our internal data structures until flush() is + * called. + */ + status_t remove(ZipEntry* pEntry); + + /* + * Flush changes. If mNeedCDRewrite is set, this writes the central dir. + */ + status_t flush(void); + + /* + * Expand the data into the buffer provided. The buffer must hold + * at least bytes. Variation expands directly + * to a file. + * + * Returns "false" if an error was encountered in the compressed data. + */ + //bool uncompress(const ZipEntry* pEntry, void* buf) const; + //bool uncompress(const ZipEntry* pEntry, FILE* fp) const; + void* uncompress(const ZipEntry* pEntry); + + /* + * Get an entry, by name. Returns NULL if not found. + * + * Does not return entries pending deletion. + */ + ZipEntry* getEntryByName(const char* fileName) const; + + /* + * Get the Nth entry in the archive. + * + * This will return an entry that is pending deletion. + */ + int getNumEntries(void) const { return mEntries.size(); } + ZipEntry* getEntryByIndex(int idx) const; + +private: + /* these are private and not defined */ + ZipFile(const ZipFile& src); + ZipFile& operator=(const ZipFile& src); + + class EndOfCentralDir { + public: + EndOfCentralDir(void) : + mDiskNumber(0), + mDiskWithCentralDir(0), + mNumEntries(0), + mTotalNumEntries(0), + mCentralDirSize(0), + mCentralDirOffset(0), + mCommentLen(0), + mComment(NULL) + {} + virtual ~EndOfCentralDir(void) { + delete[] mComment; + } + + status_t readBuf(const unsigned char* buf, int len); + status_t write(FILE* fp); + + //unsigned long mSignature; + unsigned short mDiskNumber; + unsigned short mDiskWithCentralDir; + unsigned short mNumEntries; + unsigned short mTotalNumEntries; + unsigned long mCentralDirSize; + unsigned long mCentralDirOffset; // offset from first disk + unsigned short mCommentLen; + unsigned char* mComment; + + enum { + kSignature = 0x06054b50, + kEOCDLen = 22, // EndOfCentralDir len, excl. comment + + kMaxCommentLen = 65535, // longest possible in ushort + kMaxEOCDSearch = kMaxCommentLen + EndOfCentralDir::kEOCDLen, + + }; + + void dump(void) const; + }; + + + /* read all entries in the central dir */ + status_t readCentralDir(void); + + /* crunch deleted entries out */ + status_t crunchArchive(void); + + /* clean up mEntries */ + void discardEntries(void); + + /* common handler for all "add" functions */ + status_t addCommon(const char* fileName, const void* data, size_t size, + const char* storageName, int sourceType, int compressionMethod, + ZipEntry** ppEntry); + + /* copy all of "srcFp" into "dstFp" */ + status_t copyFpToFp(FILE* dstFp, FILE* srcFp, unsigned long* pCRC32); + /* copy all of "data" into "dstFp" */ + status_t copyDataToFp(FILE* dstFp, + const void* data, size_t size, unsigned long* pCRC32); + /* copy some of "srcFp" into "dstFp" */ + status_t copyPartialFpToFp(FILE* dstFp, FILE* srcFp, long length, + unsigned long* pCRC32); + /* like memmove(), but on parts of a single file */ + status_t filemove(FILE* fp, off_t dest, off_t src, size_t n); + /* compress all of "srcFp" into "dstFp", using Deflate */ + status_t compressFpToFp(FILE* dstFp, FILE* srcFp, + const void* data, size_t size, unsigned long* pCRC32); + + /* get modification date from a file descriptor */ + time_t getModTime(int fd); + + /* + * We use stdio FILE*, which gives us buffering but makes dealing + * with files >2GB awkward. Until we support Zip64, we're fine. + */ + FILE* mZipFp; // Zip file pointer + + /* one of these per file */ + EndOfCentralDir mEOCD; + + /* did we open this read-only? */ + bool mReadOnly; + + /* set this when we trash the central dir */ + bool mNeedCDRewrite; + + /* + * One ZipEntry per entry in the zip file. I'm using pointers instead + * of objects because it's easier than making operator= work for the + * classes and sub-classes. + */ + Vector mEntries; +}; + +}; // namespace android + +#endif // __LIBS_ZIPFILE_H diff --git a/include/utils/ZipFileCRO.h b/include/utils/ZipFileCRO.h new file mode 100644 index 000000000..30e00368e --- /dev/null +++ b/include/utils/ZipFileCRO.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// C API for ead-only access to Zip archives, with minimal heap allocation. +// +#ifndef __LIBS_ZIPFILECRO_H +#define __LIBS_ZIPFILECRO_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Trivial typedef to ensure that ZipFileCRO is not treated as a simple integer. + */ +typedef void* ZipFileCRO; + +/* + * Trivial typedef to ensure that ZipEntryCRO is not treated as a simple + * integer. We use NULL to indicate an invalid value. + */ +typedef void* ZipEntryCRO; + +extern ZipFileCRO ZipFileXRO_open(const char* path); + +extern void ZipFileCRO_destroy(ZipFileCRO zip); + +extern ZipEntryCRO ZipFileCRO_findEntryByName(ZipFileCRO zip, + const char* fileName); + +extern bool ZipFileCRO_getEntryInfo(ZipFileCRO zip, ZipEntryCRO entry, + int* pMethod, long* pUncompLen, + long* pCompLen, off_t* pOffset, long* pModWhen, long* pCrc32); + +extern bool ZipFileCRO_uncompressEntry(ZipFileCRO zip, ZipEntryCRO entry, int fd); + +#ifdef __cplusplus +} +#endif + +#endif /*__LIBS_ZIPFILECRO_H*/ diff --git a/include/utils/ZipFileRO.h b/include/utils/ZipFileRO.h new file mode 100644 index 000000000..51c4f2fb6 --- /dev/null +++ b/include/utils/ZipFileRO.h @@ -0,0 +1,222 @@ +/* + * 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. + */ + +// +// Read-only access to Zip archives, with minimal heap allocation. +// +// This is similar to the more-complete ZipFile class, but no attempt +// has been made to make them interchangeable. This class operates under +// a very different set of assumptions and constraints. +// +#ifndef __LIBS_ZIPFILERO_H +#define __LIBS_ZIPFILERO_H + +#include "Errors.h" +#include "FileMap.h" + +#include +#include +#include + +namespace android { + +/* + * Trivial typedef to ensure that ZipEntryRO is not treated as a simple + * integer. We use NULL to indicate an invalid value. + */ +typedef void* ZipEntryRO; + +/* + * Open a Zip archive for reading. + * + * We want "open" and "find entry by name" to be fast operations, and we + * want to use as little memory as possible. We memory-map the file, + * and load a hash table with pointers to the filenames (which aren't + * null-terminated). The other fields are at a fixed offset from the + * filename, so we don't need to extract those (but we do need to byte-read + * and endian-swap them every time we want them). + * + * To speed comparisons when doing a lookup by name, we could make the mapping + * "private" (copy-on-write) and null-terminate the filenames after verifying + * the record structure. However, this requires a private mapping of + * every page that the Central Directory touches. Easier to tuck a copy + * of the string length into the hash table entry. + */ +class ZipFileRO { +public: + ZipFileRO() + : mFd(-1), mFileMap(NULL), mHashTableSize(-1), mHashTable(NULL) + {} + ~ZipFileRO() { + free(mHashTable); + if (mFileMap) + mFileMap->release(); + if (mFd >= 0) + close(mFd); + } + + /* + * Open an archive. + */ + status_t open(const char* zipFileName); + + /* + * Find an entry, by name. Returns the entry identifier, or NULL if + * not found. + * + * If two entries have the same name, one will be chosen at semi-random. + */ + ZipEntryRO findEntryByName(const char* fileName) const; + + /* + * Return the #of entries in the Zip archive. + */ + int getNumEntries(void) const { + return mNumEntries; + } + + /* + * Return the Nth entry. Zip file entries are not stored in sorted + * order, and updated entries may appear at the end, so anyone walking + * the archive needs to avoid making ordering assumptions. We take + * that further by returning the Nth non-empty entry in the hash table + * rather than the Nth entry in the archive. + * + * Valid values are [0..numEntries). + * + * [This is currently O(n). If it needs to be fast we can allocate an + * additional data structure or provide an iterator interface.] + */ + ZipEntryRO findEntryByIndex(int idx) const; + + /* + * Copy the filename into the supplied buffer. Returns 0 on success, + * -1 if "entry" is invalid, or the filename length if it didn't fit. The + * length, and the returned string, include the null-termination. + */ + int getEntryFileName(ZipEntryRO entry, char* buffer, int bufLen) const; + + /* + * Get the vital stats for an entry. Pass in NULL pointers for anything + * you don't need. + * + * "*pOffset" holds the Zip file offset of the entry's data. + * + * Returns "false" if "entry" is bogus or if the data in the Zip file + * appears to be bad. + */ + bool getEntryInfo(ZipEntryRO entry, int* pMethod, long* pUncompLen, + long* pCompLen, off_t* pOffset, long* pModWhen, long* pCrc32) const; + + /* + * Create a new FileMap object that maps a subset of the archive. For + * an uncompressed entry this effectively provides a pointer to the + * actual data, for a compressed entry this provides the input buffer + * for inflate(). + */ + FileMap* createEntryFileMap(ZipEntryRO entry) const; + + /* + * Uncompress the data into a buffer. Depending on the compression + * format, this is either an "inflate" operation or a memcpy. + * + * Use "uncompLen" from getEntryInfo() to determine the required + * buffer size. + * + * Returns "true" on success. + */ + bool uncompressEntry(ZipEntryRO entry, void* buffer) const; + + /* + * Uncompress the data to an open file descriptor. + */ + bool uncompressEntry(ZipEntryRO entry, int fd) const; + + /* Zip compression methods we support */ + enum { + kCompressStored = 0, // no compression + kCompressDeflated = 8, // standard deflate + }; + + /* + * Utility function: uncompress deflated data, buffer to buffer. + */ + static bool inflateBuffer(void* outBuf, const void* inBuf, + long uncompLen, long compLen); + + /* + * Utility function: uncompress deflated data, buffer to fd. + */ + static bool inflateBuffer(int fd, const void* inBuf, + long uncompLen, long compLen); + + /* + * Some basic functions for raw data manipulation. "LE" means + * Little Endian. + */ + static inline unsigned short get2LE(const unsigned char* buf) { + return buf[0] | (buf[1] << 8); + } + static inline unsigned long get4LE(const unsigned char* buf) { + return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); + } + +private: + /* these are private and not defined */ + ZipFileRO(const ZipFileRO& src); + ZipFileRO& operator=(const ZipFileRO& src); + + /* parse the archive, prepping internal structures */ + bool parseZipArchive(void); + + /* add a new entry to the hash table */ + void addToHash(const char* str, int strLen, unsigned int hash); + + /* compute string hash code */ + static unsigned int computeHash(const char* str, int len); + + /* convert a ZipEntryRO back to a hash table index */ + int entryToIndex(const ZipEntryRO entry) const; + + /* + * One entry in the hash table. + */ + typedef struct HashEntry { + const char* name; + unsigned short nameLen; + //unsigned int hash; + } HashEntry; + + /* open Zip archive */ + int mFd; + + /* mapped file */ + FileMap* mFileMap; + + /* number of entries in the Zip archive */ + int mNumEntries; + + /* + * We know how many entries are in the Zip archive, so we have a + * fixed-size hash table. We probe for an empty slot. + */ + int mHashTableSize; + HashEntry* mHashTable; +}; + +}; // namespace android + +#endif /*__LIBS_ZIPFILERO_H*/ diff --git a/include/utils/ZipUtils.h b/include/utils/ZipUtils.h new file mode 100644 index 000000000..42c42b6c0 --- /dev/null +++ b/include/utils/ZipUtils.h @@ -0,0 +1,67 @@ +/* + * 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. + */ + +// +// Miscellaneous zip/gzip utility functions. +// +#ifndef __LIBS_ZIPUTILS_H +#define __LIBS_ZIPUTILS_H + +#include + +namespace android { + +/* + * Container class for utility functions, primarily for namespace reasons. + */ +class ZipUtils { +public: + /* + * General utility function for uncompressing "deflate" data from a file + * to a buffer. + */ + static bool inflateToBuffer(int fd, void* buf, long uncompressedLen, + long compressedLen); + static bool inflateToBuffer(FILE* fp, void* buf, long uncompressedLen, + long compressedLen); + + /* + * Someday we might want to make this generic and handle bzip2 ".bz2" + * files too. + * + * We could declare gzip to be a sub-class of zip that has exactly + * one always-compressed entry, but we currently want to treat Zip + * and gzip as distinct, so there's no value. + * + * The zlib library has some gzip utilities, but it has no interface + * for extracting the uncompressed length of the file (you do *not* + * want to gzseek to the end). + * + * Pass in a seeked file pointer for the gzip file. If this is a gzip + * file, we set our return values appropriately and return "true" with + * the file seeked to the start of the compressed data. + */ + static bool examineGzip(FILE* fp, int* pCompressionMethod, + long* pUncompressedLen, long* pCompressedLen, unsigned long* pCRC32); + +private: + ZipUtils() {} + ~ZipUtils() {} +}; + +}; // namespace android + +#endif /*__LIBS_ZIPUTILS_H*/ diff --git a/include/utils/ashmem.h b/include/utils/ashmem.h new file mode 100644 index 000000000..085477578 --- /dev/null +++ b/include/utils/ashmem.h @@ -0,0 +1,41 @@ +/* utils/ashmem.h + ** + ** Copyright 2008 The Android Open Source Project + ** + ** This file is dual licensed. It may be redistributed and/or modified + ** under the terms of the Apache 2.0 License OR version 2 of the GNU + ** General Public License. + */ + +#ifndef _UTILS_ASHMEM_H +#define _UTILS_ASHMEM_H + +#include +#include + +#define ASHMEM_NAME_LEN 256 + +#define ASHMEM_NAME_DEF "dev/ashmem" + +/* Return values from ASHMEM_PIN: Was the mapping purged while unpinned? */ +#define ASHMEM_NOT_REAPED 0 +#define ASHMEM_WAS_REAPED 1 + +/* Return values from ASHMEM_UNPIN: Is the mapping now pinned or unpinned? */ +#define ASHMEM_NOW_UNPINNED 0 +#define ASHMEM_NOW_PINNED 1 + +#define __ASHMEMIOC 0x77 + +#define ASHMEM_SET_NAME _IOW(__ASHMEMIOC, 1, char[ASHMEM_NAME_LEN]) +#define ASHMEM_GET_NAME _IOR(__ASHMEMIOC, 2, char[ASHMEM_NAME_LEN]) +#define ASHMEM_SET_SIZE _IOW(__ASHMEMIOC, 3, size_t) +#define ASHMEM_GET_SIZE _IO(__ASHMEMIOC, 4) +#define ASHMEM_SET_PROT_MASK _IOW(__ASHMEMIOC, 5, unsigned long) +#define ASHMEM_GET_PROT_MASK _IO(__ASHMEMIOC, 6) +#define ASHMEM_PIN _IO(__ASHMEMIOC, 7) +#define ASHMEM_UNPIN _IO(__ASHMEMIOC, 8) +#define ASHMEM_ISPINNED _IO(__ASHMEMIOC, 9) +#define ASHMEM_PURGE_ALL_CACHES _IO(__ASHMEMIOC, 10) + +#endif /* _UTILS_ASHMEM_H */ diff --git a/include/utils/executablepath.h b/include/utils/executablepath.h new file mode 100644 index 000000000..c979432ba --- /dev/null +++ b/include/utils/executablepath.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UTILS_EXECUTABLEPATH_H +#define _UTILS_EXECUTABLEPATH_H + +#include + +// returns the path to this executable +#if __cplusplus +extern "C" +#endif +void executablepath(char s[PATH_MAX]); + +#endif // _UTILS_EXECUTABLEPATH_H diff --git a/include/utils/inet_address.h b/include/utils/inet_address.h new file mode 100644 index 000000000..dbd8672e0 --- /dev/null +++ b/include/utils/inet_address.h @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2005 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. + */ + +// +// Internet address classes. Modeled after Java classes. +// +#ifndef _RUNTIME_INET_ADDRESS_H +#define _RUNTIME_INET_ADDRESS_H + +#ifdef HAVE_ANDROID_OS +#error DO NOT USE THIS FILE IN THE DEVICE BUILD +#endif + + +namespace android { + +/* + * This class holds Internet addresses. Perhaps more useful is its + * ability to look up addresses by name. + * + * Invoke one of the static factory methods to create a new object. + */ +class InetAddress { +public: + virtual ~InetAddress(void); + + // create from w.x.y.z or foo.bar.com notation + static InetAddress* getByName(const char* host); + + // copy-construction + InetAddress(const InetAddress& orig); + + const void* getAddress(void) const { return mAddress; } + int getAddressLength(void) const { return mLength; } + const char* getHostName(void) const { return mName; } + +private: + InetAddress(void); + // assignment (private) + InetAddress& operator=(const InetAddress& addr); + + // use a void* here so we don't have to expose actual socket headers + void* mAddress; // this is really a ptr to sockaddr_in + int mLength; + char* mName; +}; + + +/* + * Base class for socket addresses. + */ +class SocketAddress { +public: + SocketAddress() {} + virtual ~SocketAddress() {} +}; + + +/* + * Internet address class. This combines an InetAddress with a port. + */ +class InetSocketAddress : public SocketAddress { +public: + InetSocketAddress() : + mAddress(0), mPort(-1) + {} + ~InetSocketAddress(void) { + delete mAddress; + } + + // Create an address with a host wildcard (useful for servers). + bool create(int port); + // Create an address with the specified host and port. + bool create(const InetAddress* addr, int port); + // Create an address with the specified host and port. Does the + // hostname lookup. + bool create(const char* host, int port); + + const InetAddress* getAddress(void) const { return mAddress; } + const int getPort(void) const { return mPort; } + const char* getHostName(void) const { return mAddress->getHostName(); } + +private: + InetAddress* mAddress; + int mPort; +}; + +}; // namespace android + +#endif // _RUNTIME_INET_ADDRESS_H diff --git a/include/utils/logger.h b/include/utils/logger.h new file mode 100644 index 000000000..3a08019a8 --- /dev/null +++ b/include/utils/logger.h @@ -0,0 +1,46 @@ +/* utils/logger.h +** +** Copyright 2007, The Android Open Source Project +** +** This file is dual licensed. It may be redistributed and/or modified +** under the terms of the Apache 2.0 License OR version 2 of the GNU +** General Public License. +*/ + +#ifndef _UTILS_LOGGER_H +#define _UTILS_LOGGER_H + +#include + +struct logger_entry { + uint16_t len; /* length of the payload */ + uint16_t __pad; /* no matter what, we get 2 bytes of padding */ + int32_t pid; /* generating process's pid */ + int32_t tid; /* generating process's tid */ + int32_t sec; /* seconds since Epoch */ + int32_t nsec; /* nanoseconds */ + char msg[0]; /* the entry's payload */ +}; + +#define LOGGER_LOG_MAIN "log/main" +#define LOGGER_LOG_RADIO "log/radio" +#define LOGGER_LOG_EVENTS "log/events" + +#define LOGGER_ENTRY_MAX_LEN (4*1024) +#define LOGGER_ENTRY_MAX_PAYLOAD \ + (LOGGER_ENTRY_MAX_LEN - sizeof(struct logger_entry)) + +#ifdef HAVE_IOCTL + +#include + +#define __LOGGERIO 0xAE + +#define LOGGER_GET_LOG_BUF_SIZE _IO(__LOGGERIO, 1) /* size of log */ +#define LOGGER_GET_LOG_LEN _IO(__LOGGERIO, 2) /* used log len */ +#define LOGGER_GET_NEXT_ENTRY_LEN _IO(__LOGGERIO, 3) /* next entry len */ +#define LOGGER_FLUSH_LOG _IO(__LOGGERIO, 4) /* flush log */ + +#endif // HAVE_IOCTL + +#endif /* _UTILS_LOGGER_H */ diff --git a/include/utils/misc.h b/include/utils/misc.h new file mode 100644 index 000000000..62e84b489 --- /dev/null +++ b/include/utils/misc.h @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2005 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. + */ + +// +// Handy utility functions and portability code. +// +#ifndef _LIBS_UTILS_MISC_H +#define _LIBS_UTILS_MISC_H + +#include +#include "utils/Endian.h" + +namespace android { + +/* get #of elements in a static array */ +#ifndef NELEM +# define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0]))) +#endif + +/* + * Make a copy of the string, using "new[]" instead of "malloc". Free the + * string with delete[]. + * + * Returns NULL if "str" is NULL. + */ +char* strdupNew(const char* str); + +/* + * Concatenate an argument vector into a single string. If argc is >= 0 + * it will be used; if it's < 0 then the last element in the arg vector + * must be NULL. + * + * This inserts a space between each argument. + * + * This does not automatically add double quotes around arguments with + * spaces in them. This practice is necessary for Win32, because Win32's + * CreateProcess call is stupid. + * + * The caller should delete[] the returned string. + */ +char* concatArgv(int argc, const char* const argv[]); + +/* + * Count up the number of arguments in "argv". The count does not include + * the final NULL entry. + */ +int countArgv(const char* const argv[]); + +/* + * Some utility functions for working with files. These could be made + * part of a "File" class. + */ +typedef enum FileType { + kFileTypeUnknown = 0, + kFileTypeNonexistent, // i.e. ENOENT + kFileTypeRegular, + kFileTypeDirectory, + kFileTypeCharDev, + kFileTypeBlockDev, + kFileTypeFifo, + kFileTypeSymlink, + kFileTypeSocket, +} FileType; +/* get the file's type; follows symlinks */ +FileType getFileType(const char* fileName); +/* get the file's modification date; returns -1 w/errno set on failure */ +time_t getFileModDate(const char* fileName); + +/* + * Round up to the nearest power of 2. Handy for hash tables. + */ +unsigned int roundUpPower2(unsigned int val); + +void strreverse(char* begin, char* end); +void k_itoa(int value, char* str, int base); +char* itoa(int val, int base); + +}; // namespace android + +#endif // _LIBS_UTILS_MISC_H diff --git a/include/utils/ported.h b/include/utils/ported.h new file mode 100644 index 000000000..eb3be01e9 --- /dev/null +++ b/include/utils/ported.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2005 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. + */ + +// +// Standard functions ported to the current platform. Note these are NOT +// in the "android" namespace. +// +#ifndef _LIBS_UTILS_PORTED_H +#define _LIBS_UTILS_PORTED_H + +#include // for timeval + +#ifdef __cplusplus +extern "C" { +#endif + +/* library replacement functions */ +#if defined(NEED_GETTIMEOFDAY) +int gettimeofday(struct timeval* tv, struct timezone* tz); +#endif +#if defined(NEED_USLEEP) +void usleep(unsigned long usec); +#endif +#if defined(NEED_PIPE) +int pipe(int filedes[2]); +#endif +#if defined(NEED_SETENV) +int setenv(const char* name, const char* value, int overwrite); +void unsetenv(const char* name); +char* getenv(const char* name); +#endif + +#ifdef __cplusplus +} +#endif + +#endif // _LIBS_UTILS_PORTED_H diff --git a/include/utils/string_array.h b/include/utils/string_array.h new file mode 100644 index 000000000..ede0644cb --- /dev/null +++ b/include/utils/string_array.h @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2005 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. + */ + +// +// Sortable array of strings. STL-ish, but STL-free. +// +#ifndef _LIBS_UTILS_STRING_ARRAY_H +#define _LIBS_UTILS_STRING_ARRAY_H + +#include +#include + +namespace android { + +// +// An expanding array of strings. Add, get, sort, delete. +// +class StringArray { +public: + StringArray() + : mMax(0), mCurrent(0), mArray(NULL) + {} + virtual ~StringArray() { + for (int i = 0; i < mCurrent; i++) + delete[] mArray[i]; + delete[] mArray; + } + + // + // Add a string. A copy of the string is made. + // + bool push_back(const char* str) { + if (mCurrent >= mMax) { + char** tmp; + + if (mMax == 0) + mMax = 16; // initial storage + else + mMax *= 2; + + tmp = new char*[mMax]; + if (tmp == NULL) + return false; + + memcpy(tmp, mArray, mCurrent * sizeof(char*)); + delete[] mArray; + mArray = tmp; + } + + int len = strlen(str); + mArray[mCurrent] = new char[len+1]; + memcpy(mArray[mCurrent], str, len+1); + mCurrent++; + + return true; + } + + // + // Delete an entry. + // + void erase(int idx) { + if (idx < 0 || idx >= mCurrent) + return; + delete[] mArray[idx]; + if (idx < mCurrent-1) { + memmove(&mArray[idx], &mArray[idx+1], + (mCurrent-1 - idx) * sizeof(char*)); + } + mCurrent--; + } + + // + // Sort the array. + // + void sort(int (*compare)(const void*, const void*)) { + qsort(mArray, mCurrent, sizeof(char*), compare); + } + + // + // Pass this to the sort routine to do an ascending alphabetical sort. + // + static int cmpAscendingAlpha(const void* pstr1, const void* pstr2) { + return strcmp(*(const char**)pstr1, *(const char**)pstr2); + } + + // + // Get the #of items in the array. + // + inline int size(void) const { return mCurrent; } + + // + // Return entry N. + // [should use operator[] here] + // + const char* getEntry(int idx) const { + if (idx < 0 || idx >= mCurrent) + return NULL; + return mArray[idx]; + } + +private: + int mMax; + int mCurrent; + char** mArray; +}; + +}; // namespace android + +#endif // _LIBS_UTILS_STRING_ARRAY_H diff --git a/include/utils/threads.h b/include/utils/threads.h new file mode 100644 index 000000000..7dca81004 --- /dev/null +++ b/include/utils/threads.h @@ -0,0 +1,347 @@ +/* + * 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. + */ + +#ifndef _LIBS_UTILS_THREADS_H +#define _LIBS_UTILS_THREADS_H + +#include +#include +#include + +// ------------------------------------------------------------------ +// C API + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void* android_thread_id_t; + +typedef int (*android_thread_func_t)(void*); + +enum { + /* + * *********************************************** + * ** Keep in sync with android.os.Process.java ** + * *********************************************** + * + * This maps directly to the "nice" priorites we use in Android. + * A thread priority should be chosen inverse-proportinally to + * the amount of work the thread is expected to do. The more work + * a thread will do, the less favorable priority it should get so that + * it doesn't starve the system. Threads not behaving properly might + * be "punished" by the kernel. + * Use the levels below when appropriate. Intermediate values are + * acceptable, preferably use the {MORE|LESS}_FAVORABLE constants below. + */ + ANDROID_PRIORITY_LOWEST = 19, + + /* use for background tasks */ + ANDROID_PRIORITY_BACKGROUND = 10, + + /* most threads run at normal priority */ + ANDROID_PRIORITY_NORMAL = 0, + + /* threads currently running a UI that the user is interacting with */ + ANDROID_PRIORITY_FOREGROUND = -2, + + /* the main UI thread has a slightly more favorable priority */ + ANDROID_PRIORITY_DISPLAY = -4, + + /* ui service treads might want to run at a urgent display (uncommon) */ + ANDROID_PRIORITY_URGENT_DISPLAY = -8, + + /* all normal audio threads */ + ANDROID_PRIORITY_AUDIO = -16, + + /* service audio threads (uncommon) */ + ANDROID_PRIORITY_URGENT_AUDIO = -19, + + /* should never be used in practice. regular process might not + * be allowed to use this level */ + ANDROID_PRIORITY_HIGHEST = -20, + + ANDROID_PRIORITY_DEFAULT = ANDROID_PRIORITY_NORMAL, + ANDROID_PRIORITY_MORE_FAVORABLE = -1, + ANDROID_PRIORITY_LESS_FAVORABLE = +1, +}; + +// Create and run a new thread. +extern int androidCreateThread(android_thread_func_t, void *); + +// Create thread with lots of parameters +extern int androidCreateThreadEtc(android_thread_func_t entryFunction, + void *userData, + const char* threadName, + int32_t threadPriority, + size_t threadStackSize, + android_thread_id_t *threadId); + +// Get some sort of unique identifier for the current thread. +extern android_thread_id_t androidGetThreadId(); + +// Low-level thread creation -- never creates threads that can +// interact with the Java VM. +extern int androidCreateRawThreadEtc(android_thread_func_t entryFunction, + void *userData, + const char* threadName, + int32_t threadPriority, + size_t threadStackSize, + android_thread_id_t *threadId); + +// Used by the Java Runtime to control how threads are created, so that +// they can be proper and lovely Java threads. +typedef int (*android_create_thread_fn)(android_thread_func_t entryFunction, + void *userData, + const char* threadName, + int32_t threadPriority, + size_t threadStackSize, + android_thread_id_t *threadId); + +extern void androidSetCreateThreadFunc(android_create_thread_fn func); + +#ifdef __cplusplus +} +#endif + +// ------------------------------------------------------------------ +// C++ API + +#ifdef __cplusplus + +#include +#include +#include + +namespace android { + +typedef android_thread_id_t thread_id_t; + +typedef android_thread_func_t thread_func_t; + +enum { + PRIORITY_LOWEST = ANDROID_PRIORITY_LOWEST, + PRIORITY_BACKGROUND = ANDROID_PRIORITY_BACKGROUND, + PRIORITY_NORMAL = ANDROID_PRIORITY_NORMAL, + PRIORITY_FOREGROUND = ANDROID_PRIORITY_FOREGROUND, + PRIORITY_DISPLAY = ANDROID_PRIORITY_DISPLAY, + PRIORITY_URGENT_DISPLAY = ANDROID_PRIORITY_URGENT_DISPLAY, + PRIORITY_AUDIO = ANDROID_PRIORITY_AUDIO, + PRIORITY_URGENT_AUDIO = ANDROID_PRIORITY_URGENT_AUDIO, + PRIORITY_HIGHEST = ANDROID_PRIORITY_HIGHEST, + PRIORITY_DEFAULT = ANDROID_PRIORITY_DEFAULT, + PRIORITY_MORE_FAVORABLE = ANDROID_PRIORITY_MORE_FAVORABLE, + PRIORITY_LESS_FAVORABLE = ANDROID_PRIORITY_LESS_FAVORABLE, +}; + +// Create and run a new thread. +inline bool createThread(thread_func_t f, void *a) { + return androidCreateThread(f, a) ? true : false; +} + +// Create thread with lots of parameters +inline bool createThreadEtc(thread_func_t entryFunction, + void *userData, + const char* threadName = "android:unnamed_thread", + int32_t threadPriority = PRIORITY_DEFAULT, + size_t threadStackSize = 0, + thread_id_t *threadId = 0) +{ + return androidCreateThreadEtc(entryFunction, userData, threadName, + threadPriority, threadStackSize, threadId) ? true : false; +} + +// Get some sort of unique identifier for the current thread. +inline thread_id_t getThreadId() { + return androidGetThreadId(); +} + +/* + * Simple mutex class. The implementation is system-dependent. + * + * The mutex must be unlocked by the thread that locked it. They are not + * recursive, i.e. the same thread can't lock it multiple times. + */ +class Mutex { +public: + Mutex(); + Mutex(const char* name); + ~Mutex(); + + // lock or unlock the mutex + status_t lock(); + void unlock(); + + // lock if possible; returns 0 on success, error otherwise + status_t tryLock(); + + // Manages the mutex automatically. It'll be locked when Autolock is + // constructed and released when Autolock goes out of scope. + class Autolock { + public: + inline Autolock(Mutex& mutex) : mpMutex(&mutex) { mutex.lock(); } + inline Autolock(Mutex* mutex) : mpMutex(mutex) { mutex->lock(); } + inline ~Autolock() { mpMutex->unlock(); } + private: + Mutex* mpMutex; + }; + +private: + friend class Condition; + + // A mutex cannot be copied + Mutex(const Mutex&); + Mutex& operator = (const Mutex&); + void _init(); + + void* mState; +}; + +/* + * Automatic mutex. Declare one of these at the top of a function. + * When the function returns, it will go out of scope, and release the + * mutex. + */ + +typedef Mutex::Autolock AutoMutex; + + +/* + * Condition variable class. The implementation is system-dependent. + * + * Condition variables are paired up with mutexes. Lock the mutex, + * call wait(), then either re-wait() if things aren't quite what you want, + * or unlock the mutex and continue. All threads calling wait() must + * use the same mutex for a given Condition. + */ +class Condition { +public: + Condition(); + ~Condition(); + // Wait on the condition variable. Lock the mutex before calling. + status_t wait(Mutex& mutex); + // Wait on the condition variable until the given time. Lock the mutex + // before calling. + status_t wait(Mutex& mutex, nsecs_t abstime); + // same with relative timeout + status_t waitRelative(Mutex& mutex, nsecs_t reltime); + // Signal the condition variable, allowing one thread to continue. + void signal(); + // Signal the condition variable, allowing all threads to continue. + void broadcast(); + +private: + void* mState; +}; + + +/* + * Read/write lock. The resource can have multiple readers or one writer, + * but can't be read and written at the same time. + * + * The same thread should not call a lock function while it already has + * a lock. (Should be okay for multiple readers.) + */ +class ReadWriteLock { +public: + ReadWriteLock() + : mNumReaders(0), mNumWriters(0) + {} + ~ReadWriteLock() {} + + void lockForRead(); + bool tryLockForRead(); + void unlockForRead(); + + void lockForWrite(); + bool tryLockForWrite(); + void unlockForWrite(); + +private: + int mNumReaders; + int mNumWriters; + + Mutex mLock; + Condition mReadWaiter; + Condition mWriteWaiter; +#if defined(PRINT_RENDER_TIMES) + DurationTimer mDebugTimer; +#endif +}; + + +/* + * This is our spiffy thread object! + */ + +class Thread : virtual public RefBase +{ +public: + // Create a Thread object, but doesn't create or start the associated + // thread. See the run() method. + Thread(bool canCallJava = true); + virtual ~Thread(); + + // Start the thread in threadLoop() which needs to be implemented. + virtual status_t run( const char* name = 0, + int32_t priority = PRIORITY_DEFAULT, + size_t stack = 0); + + // Ask this object's thread to exit. This function is asynchronous, when the + // function returns the thread might still be running. Of course, this + // function can be called from a different thread. + virtual void requestExit(); + + // Good place to do one-time initializations + virtual status_t readyToRun(); + + // Call requestExit() and wait until this object's thread exits. + // BE VERY CAREFUL of deadlocks. In particular, it would be silly to call + // this function from this object's thread. Will return WOULD_BLOCK in + // that case. + status_t requestExitAndWait(); + +protected: + // exitPending() returns true if requestExit() has been called. + bool exitPending() const; + +private: + // Derived class must implemtent threadLoop(). The thread starts its life + // here. There are two ways of using the Thread object: + // 1) loop: if threadLoop() returns true, it will be called again if + // requestExit() wasn't called. + // 2) once: if threadLoop() returns false, the thread will exit upon return. + virtual bool threadLoop() = 0; + +private: + Thread& operator=(const Thread&); + static int _threadLoop(void* user); + const bool mCanCallJava; + thread_id_t mThread; + Mutex mLock; + Condition mThreadExitedCondition; + status_t mStatus; + volatile bool mExitPending; + volatile bool mRunning; + sp mHoldSelf; +}; + + +}; // namespace android + +#endif // __cplusplus + +#endif // _LIBS_UTILS_THREADS_H diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk new file mode 100644 index 000000000..4a68dc1f7 --- /dev/null +++ b/libs/utils/Android.mk @@ -0,0 +1,148 @@ +# Copyright (C) 2008 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +LOCAL_PATH:= $(call my-dir) + +# libutils is a little unique: It's built twice, once for the host +# and once for the device. + +commonSources:= \ + Asset.cpp \ + AssetDir.cpp \ + AssetManager.cpp \ + BufferedTextOutput.cpp \ + CallStack.cpp \ + Debug.cpp \ + FileMap.cpp \ + RefBase.cpp \ + ResourceTypes.cpp \ + SharedBuffer.cpp \ + Static.cpp \ + StopWatch.cpp \ + String8.cpp \ + String16.cpp \ + SystemClock.cpp \ + TextOutput.cpp \ + Threads.cpp \ + TimerProbe.cpp \ + Timers.cpp \ + VectorImpl.cpp \ + ZipFileCRO.cpp \ + ZipFileRO.cpp \ + ZipUtils.cpp \ + misc.cpp \ + ported.cpp \ + LogSocket.cpp + +# +# The cpp files listed here do not belong in the device +# build. Consult with the swetland before even thinking about +# putting them in commonSources. +# +# They're used by the simulator runtime and by host-side tools like +# aapt and the simulator front-end. +# +hostSources:= \ + InetAddress.cpp \ + Pipe.cpp \ + Socket.cpp \ + ZipEntry.cpp \ + ZipFile.cpp + +# For the host +# ===================================================== + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= $(commonSources) $(hostSources) + +ifeq ($(HOST_OS),linux) +# Use the futex based mutex and condition variable +# implementation from android-arm because it's shared mem safe + LOCAL_SRC_FILES += \ + futex_synchro.c \ + executablepath_linux.cpp +endif +ifeq ($(HOST_OS),darwin) + LOCAL_SRC_FILES += \ + executablepath_darwin.cpp +endif + +LOCAL_MODULE:= libutils + +LOCAL_CFLAGS += -DLIBUTILS_NATIVE=1 $(TOOL_CFLAGS) +LOCAL_C_INCLUDES += external/zlib + +ifeq ($(HOST_OS),windows) +ifeq ($(strip $(USE_CYGWIN),),) +# Under MinGW, ctype.h doesn't need multi-byte support +LOCAL_CFLAGS += -DMB_CUR_MAX=1 +endif +endif + +include $(BUILD_HOST_STATIC_LIBRARY) + + + +# For the device +# ===================================================== +include $(CLEAR_VARS) + + +# we have the common sources, plus some device-specific stuff +LOCAL_SRC_FILES:= \ + $(commonSources) \ + Binder.cpp \ + BpBinder.cpp \ + IInterface.cpp \ + IMemory.cpp \ + IPCThreadState.cpp \ + MemoryDealer.cpp \ + MemoryBase.cpp \ + MemoryHeapBase.cpp \ + MemoryHeapPmem.cpp \ + Parcel.cpp \ + ProcessState.cpp \ + IPermissionController.cpp \ + IServiceManager.cpp \ + Unicode.cpp + +ifeq ($(TARGET_SIMULATOR),true) +LOCAL_SRC_FILES += $(hostSources) +endif + +ifeq ($(TARGET_OS),linux) +# Use the futex based mutex and condition variable +# implementation from android-arm because it's shared mem safe +LOCAL_SRC_FILES += futex_synchro.c +LOCAL_LDLIBS += -lrt -ldl +endif + +LOCAL_C_INCLUDES += \ + external/zlib \ + external/icu4c/common +LOCAL_LDLIBS += -lpthread + +LOCAL_SHARED_LIBRARIES := \ + libz \ + liblog \ + libcutils + +LOCAL_MODULE:= libutils + +#LOCAL_CFLAGS+= +#LOCAL_LDFLAGS:= + +include $(BUILD_SHARED_LIBRARY) + diff --git a/libs/utils/Asset.cpp b/libs/utils/Asset.cpp new file mode 100644 index 000000000..91203ddb4 --- /dev/null +++ b/libs/utils/Asset.cpp @@ -0,0 +1,813 @@ +/* + * Copyright (C) 2006 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. + */ + +// +// Provide access to a read-only asset. +// + +#define LOG_TAG "asset" +//#define NDEBUG 0 + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +using namespace android; + +#ifndef O_BINARY +# define O_BINARY 0 +#endif + +static volatile int32_t gCount = 0; + +int32_t Asset::getGlobalCount() +{ + return gCount; +} + +Asset::Asset(void) + : mAccessMode(ACCESS_UNKNOWN) +{ + int count = android_atomic_inc(&gCount)+1; + //LOGI("Creating Asset %p #%d\n", this, count); +} + +Asset::~Asset(void) +{ + int count = android_atomic_dec(&gCount); + //LOGI("Destroying Asset in %p #%d\n", this, count); +} + +/* + * Create a new Asset from a file on disk. There is a fair chance that + * the file doesn't actually exist. + * + * We can use "mode" to decide how we want to go about it. + */ +/*static*/ Asset* Asset::createFromFile(const char* fileName, AccessMode mode) +{ + _FileAsset* pAsset; + status_t result; + off_t length; + int fd; + + fd = open(fileName, O_RDONLY | O_BINARY); + if (fd < 0) + return NULL; + + /* + * Under Linux, the lseek fails if we actually opened a directory. To + * be correct we should test the file type explicitly, but since we + * always open things read-only it doesn't really matter, so there's + * no value in incurring the extra overhead of an fstat() call. + */ + length = lseek(fd, 0, SEEK_END); + if (length < 0) { + ::close(fd); + return NULL; + } + (void) lseek(fd, 0, SEEK_SET); + + pAsset = new _FileAsset; + result = pAsset->openChunk(fileName, fd, 0, length); + if (result != NO_ERROR) { + delete pAsset; + return NULL; + } + + pAsset->mAccessMode = mode; + return pAsset; +} + + +/* + * Create a new Asset from a compressed file on disk. There is a fair chance + * that the file doesn't actually exist. + * + * We currently support gzip files. We might want to handle .bz2 someday. + */ +/*static*/ Asset* Asset::createFromCompressedFile(const char* fileName, + AccessMode mode) +{ + _CompressedAsset* pAsset; + status_t result; + off_t fileLen; + bool scanResult; + long offset; + int method; + long uncompressedLen, compressedLen; + int fd; + + fd = open(fileName, O_RDONLY | O_BINARY); + if (fd < 0) + return NULL; + + fileLen = lseek(fd, 0, SEEK_END); + if (fileLen < 0) { + ::close(fd); + return NULL; + } + (void) lseek(fd, 0, SEEK_SET); + + /* want buffered I/O for the file scan; must dup so fclose() is safe */ + FILE* fp = fdopen(dup(fd), "rb"); + if (fp == NULL) { + ::close(fd); + return NULL; + } + + unsigned long crc32; + scanResult = ZipUtils::examineGzip(fp, &method, &uncompressedLen, + &compressedLen, &crc32); + offset = ftell(fp); + fclose(fp); + if (!scanResult) { + LOGD("File '%s' is not in gzip format\n", fileName); + ::close(fd); + return NULL; + } + + pAsset = new _CompressedAsset; + result = pAsset->openChunk(fd, offset, method, uncompressedLen, + compressedLen); + if (result != NO_ERROR) { + delete pAsset; + return NULL; + } + + pAsset->mAccessMode = mode; + return pAsset; +} + + +#if 0 +/* + * Create a new Asset from part of an open file. + */ +/*static*/ Asset* Asset::createFromFileSegment(int fd, off_t offset, + size_t length, AccessMode mode) +{ + _FileAsset* pAsset; + status_t result; + + pAsset = new _FileAsset; + result = pAsset->openChunk(NULL, fd, offset, length); + if (result != NO_ERROR) + return NULL; + + pAsset->mAccessMode = mode; + return pAsset; +} + +/* + * Create a new Asset from compressed data in an open file. + */ +/*static*/ Asset* Asset::createFromCompressedData(int fd, off_t offset, + int compressionMethod, size_t uncompressedLen, size_t compressedLen, + AccessMode mode) +{ + _CompressedAsset* pAsset; + status_t result; + + pAsset = new _CompressedAsset; + result = pAsset->openChunk(fd, offset, compressionMethod, + uncompressedLen, compressedLen); + if (result != NO_ERROR) + return NULL; + + pAsset->mAccessMode = mode; + return pAsset; +} +#endif + +/* + * Create a new Asset from a memory mapping. + */ +/*static*/ Asset* Asset::createFromUncompressedMap(FileMap* dataMap, + AccessMode mode) +{ + _FileAsset* pAsset; + status_t result; + + pAsset = new _FileAsset; + result = pAsset->openChunk(dataMap); + if (result != NO_ERROR) + return NULL; + + pAsset->mAccessMode = mode; + return pAsset; +} + +/* + * Create a new Asset from compressed data in a memory mapping. + */ +/*static*/ Asset* Asset::createFromCompressedMap(FileMap* dataMap, + int method, size_t uncompressedLen, AccessMode mode) +{ + _CompressedAsset* pAsset; + status_t result; + + pAsset = new _CompressedAsset; + result = pAsset->openChunk(dataMap, method, uncompressedLen); + if (result != NO_ERROR) + return NULL; + + pAsset->mAccessMode = mode; + return pAsset; +} + + +/* + * Do generic seek() housekeeping. Pass in the offset/whence values from + * the seek request, along with the current chunk offset and the chunk + * length. + * + * Returns the new chunk offset, or -1 if the seek is illegal. + */ +off_t Asset::handleSeek(off_t offset, int whence, off_t curPosn, off_t maxPosn) +{ + off_t newOffset; + + switch (whence) { + case SEEK_SET: + newOffset = offset; + break; + case SEEK_CUR: + newOffset = curPosn + offset; + break; + case SEEK_END: + newOffset = maxPosn + offset; + break; + default: + LOGW("unexpected whence %d\n", whence); + // this was happening due to an off_t size mismatch + assert(false); + return (off_t) -1; + } + + if (newOffset < 0 || newOffset > maxPosn) { + LOGW("seek out of range: want %ld, end=%ld\n", + (long) newOffset, (long) maxPosn); + return (off_t) -1; + } + + return newOffset; +} + + +/* + * =========================================================================== + * _FileAsset + * =========================================================================== + */ + +/* + * Constructor. + */ +_FileAsset::_FileAsset(void) + : mStart(0), mLength(0), mOffset(0), mFp(NULL), mFileName(NULL), mMap(NULL), mBuf(NULL) +{ +} + +/* + * Destructor. Release resources. + */ +_FileAsset::~_FileAsset(void) +{ + close(); +} + +/* + * Operate on a chunk of an uncompressed file. + * + * Zero-length chunks are allowed. + */ +status_t _FileAsset::openChunk(const char* fileName, int fd, off_t offset, size_t length) +{ + assert(mFp == NULL); // no reopen + assert(mMap == NULL); + assert(fd >= 0); + assert(offset >= 0); + + /* + * Seek to end to get file length. + */ + off_t fileLength; + fileLength = lseek(fd, 0, SEEK_END); + if (fileLength == (off_t) -1) { + // probably a bad file descriptor + LOGD("failed lseek (errno=%d)\n", errno); + return UNKNOWN_ERROR; + } + + if ((off_t) (offset + length) > fileLength) { + LOGD("start (%ld) + len (%ld) > end (%ld)\n", + (long) offset, (long) length, (long) fileLength); + return BAD_INDEX; + } + + /* after fdopen, the fd will be closed on fclose() */ + mFp = fdopen(fd, "rb"); + if (mFp == NULL) + return UNKNOWN_ERROR; + + mStart = offset; + mLength = length; + assert(mOffset == 0); + + /* seek the FILE* to the start of chunk */ + if (fseek(mFp, mStart, SEEK_SET) != 0) { + assert(false); + } + + mFileName = fileName != NULL ? strdup(fileName) : NULL; + + return NO_ERROR; +} + +/* + * Create the chunk from the map. + */ +status_t _FileAsset::openChunk(FileMap* dataMap) +{ + assert(mFp == NULL); // no reopen + assert(mMap == NULL); + assert(dataMap != NULL); + + mMap = dataMap; + mStart = -1; // not used + mLength = dataMap->getDataLength(); + assert(mOffset == 0); + + return NO_ERROR; +} + +/* + * Read a chunk of data. + */ +ssize_t _FileAsset::read(void* buf, size_t count) +{ + size_t maxLen; + size_t actual; + + assert(mOffset >= 0 && mOffset <= mLength); + + if (getAccessMode() == ACCESS_BUFFER) { + /* + * On first access, read or map the entire file. The caller has + * requested buffer access, either because they're going to be + * using the buffer or because what they're doing has appropriate + * performance needs and access patterns. + */ + if (mBuf == NULL) + getBuffer(false); + } + + /* adjust count if we're near EOF */ + maxLen = mLength - mOffset; + if (count > maxLen) + count = maxLen; + + if (!count) + return 0; + + if (mMap != NULL) { + /* copy from mapped area */ + //printf("map read\n"); + memcpy(buf, (char*)mMap->getDataPtr() + mOffset, count); + actual = count; + } else if (mBuf != NULL) { + /* copy from buffer */ + //printf("buf read\n"); + memcpy(buf, (char*)mBuf + mOffset, count); + actual = count; + } else { + /* read from the file */ + //printf("file read\n"); + if (ftell(mFp) != mStart + mOffset) { + LOGE("Hosed: %ld != %ld+%ld\n", + ftell(mFp), (long) mStart, (long) mOffset); + assert(false); + } + + /* + * This returns 0 on error or eof. We need to use ferror() or feof() + * to tell the difference, but we don't currently have those on the + * device. However, we know how much data is *supposed* to be in the + * file, so if we don't read the full amount we know something is + * hosed. + */ + actual = fread(buf, 1, count, mFp); + if (actual == 0) // something failed -- I/O error? + return -1; + + assert(actual == count); + } + + mOffset += actual; + return actual; +} + +/* + * Seek to a new position. + */ +off_t _FileAsset::seek(off_t offset, int whence) +{ + off_t newPosn; + long actualOffset; + + // compute new position within chunk + newPosn = handleSeek(offset, whence, mOffset, mLength); + if (newPosn == (off_t) -1) + return newPosn; + + actualOffset = (long) (mStart + newPosn); + + if (mFp != NULL) { + if (fseek(mFp, (long) actualOffset, SEEK_SET) != 0) + return (off_t) -1; + } + + mOffset = actualOffset - mStart; + return mOffset; +} + +/* + * Close the asset. + */ +void _FileAsset::close(void) +{ + if (mMap != NULL) { + mMap->release(); + mMap = NULL; + } + if (mBuf != NULL) { + delete[] mBuf; + mBuf = NULL; + } + + if (mFileName != NULL) { + free(mFileName); + mFileName = NULL; + } + + if (mFp != NULL) { + // can only be NULL when called from destructor + // (otherwise we would never return this object) + fclose(mFp); + mFp = NULL; + } +} + +/* + * Return a read-only pointer to a buffer. + * + * We can either read the whole thing in or map the relevant piece of + * the source file. Ideally a map would be established at a higher + * level and we'd be using a different object, but we didn't, so we + * deal with it here. + */ +const void* _FileAsset::getBuffer(bool wordAligned) +{ + /* subsequent requests just use what we did previously */ + if (mBuf != NULL) + return mBuf; + if (mMap != NULL) { + if (!wordAligned) { + return mMap->getDataPtr(); + } + return ensureAlignment(mMap); + } + + assert(mFp != NULL); + + if (mLength < kReadVsMapThreshold) { + unsigned char* buf; + long allocLen; + + /* zero-length files are allowed; not sure about zero-len allocs */ + /* (works fine with gcc + x86linux) */ + allocLen = mLength; + if (mLength == 0) + allocLen = 1; + + buf = new unsigned char[allocLen]; + if (buf == NULL) { + LOGE("alloc of %ld bytes failed\n", (long) allocLen); + return NULL; + } + + LOGV("Asset %p allocating buffer size %d (smaller than threshold)", this, (int)allocLen); + if (mLength > 0) { + long oldPosn = ftell(mFp); + fseek(mFp, mStart, SEEK_SET); + if (fread(buf, 1, mLength, mFp) != (size_t) mLength) { + LOGE("failed reading %ld bytes\n", (long) mLength); + delete[] buf; + return NULL; + } + fseek(mFp, oldPosn, SEEK_SET); + } + + LOGV(" getBuffer: loaded into buffer\n"); + + mBuf = buf; + return mBuf; + } else { + FileMap* map; + + map = new FileMap; + if (!map->create(NULL, fileno(mFp), mStart, mLength, true)) { + map->release(); + return NULL; + } + + LOGV(" getBuffer: mapped\n"); + + mMap = map; + if (!wordAligned) { + return mMap->getDataPtr(); + } + return ensureAlignment(mMap); + } +} + +int _FileAsset::openFileDescriptor(off_t* outStart, off_t* outLength) const +{ + if (mMap != NULL) { + const char* fname = mMap->getFileName(); + if (fname == NULL) { + fname = mFileName; + } + if (fname == NULL) { + return -1; + } + *outStart = mMap->getDataOffset(); + *outLength = mMap->getDataLength(); + return open(fname, O_RDONLY | O_BINARY); + } + if (mFileName == NULL) { + return -1; + } + *outStart = mStart; + *outLength = mLength; + return open(mFileName, O_RDONLY | O_BINARY); +} + +const void* _FileAsset::ensureAlignment(FileMap* map) +{ + void* data = map->getDataPtr(); + if ((((size_t)data)&0x3) == 0) { + // We can return this directly if it is aligned on a word + // boundary. + return data; + } + // If not aligned on a word boundary, then we need to copy it into + // our own buffer. + LOGV("Copying FileAsset %p to buffer size %d to make it aligned.", this, (int)mLength); + unsigned char* buf = new unsigned char[mLength]; + if (buf == NULL) { + LOGE("alloc of %ld bytes failed\n", (long) mLength); + return NULL; + } + memcpy(buf, data, mLength); + mBuf = buf; + return buf; +} + +/* + * =========================================================================== + * _CompressedAsset + * =========================================================================== + */ + +/* + * Constructor. + */ +_CompressedAsset::_CompressedAsset(void) + : mStart(0), mCompressedLen(0), mUncompressedLen(0), mOffset(0), + mMap(NULL), mFd(-1), mBuf(NULL) +{ +} + +/* + * Destructor. Release resources. + */ +_CompressedAsset::~_CompressedAsset(void) +{ + close(); +} + +/* + * Open a chunk of compressed data inside a file. + * + * This currently just sets up some values and returns. On the first + * read, we expand the entire file into a buffer and return data from it. + */ +status_t _CompressedAsset::openChunk(int fd, off_t offset, + int compressionMethod, size_t uncompressedLen, size_t compressedLen) +{ + assert(mFd < 0); // no re-open + assert(mMap == NULL); + assert(fd >= 0); + assert(offset >= 0); + assert(compressedLen > 0); + + if (compressionMethod != ZipFileRO::kCompressDeflated) { + assert(false); + return UNKNOWN_ERROR; + } + + mStart = offset; + mCompressedLen = compressedLen; + mUncompressedLen = uncompressedLen; + assert(mOffset == 0); + mFd = fd; + assert(mBuf == NULL); + + return NO_ERROR; +} + +/* + * Open a chunk of compressed data in a mapped region. + * + * Nothing is expanded until the first read call. + */ +status_t _CompressedAsset::openChunk(FileMap* dataMap, int compressionMethod, + size_t uncompressedLen) +{ + assert(mFd < 0); // no re-open + assert(mMap == NULL); + assert(dataMap != NULL); + + if (compressionMethod != ZipFileRO::kCompressDeflated) { + assert(false); + return UNKNOWN_ERROR; + } + + mMap = dataMap; + mStart = -1; // not used + mCompressedLen = dataMap->getDataLength(); + mUncompressedLen = uncompressedLen; + assert(mOffset == 0); + + return NO_ERROR; +} + +/* + * Read data from a chunk of compressed data. + * + * [For now, that's just copying data out of a buffer.] + */ +ssize_t _CompressedAsset::read(void* buf, size_t count) +{ + size_t maxLen; + size_t actual; + + assert(mOffset >= 0 && mOffset <= mUncompressedLen); + + // TODO: if mAccessMode == ACCESS_STREAMING, use zlib more cleverly + + if (mBuf == NULL) { + if (getBuffer(false) == NULL) + return -1; + } + assert(mBuf != NULL); + + /* adjust count if we're near EOF */ + maxLen = mUncompressedLen - mOffset; + if (count > maxLen) + count = maxLen; + + if (!count) + return 0; + + /* copy from buffer */ + //printf("comp buf read\n"); + memcpy(buf, (char*)mBuf + mOffset, count); + actual = count; + + mOffset += actual; + return actual; +} + +/* + * Handle a seek request. + * + * If we're working in a streaming mode, this is going to be fairly + * expensive, because it requires plowing through a bunch of compressed + * data. + */ +off_t _CompressedAsset::seek(off_t offset, int whence) +{ + off_t newPosn; + + // compute new position within chunk + newPosn = handleSeek(offset, whence, mOffset, mUncompressedLen); + if (newPosn == (off_t) -1) + return newPosn; + + mOffset = newPosn; + return mOffset; +} + +/* + * Close the asset. + */ +void _CompressedAsset::close(void) +{ + if (mMap != NULL) { + mMap->release(); + mMap = NULL; + } + if (mBuf != NULL) { + delete[] mBuf; + mBuf = NULL; + } + + if (mFd > 0) { + ::close(mFd); + mFd = -1; + } +} + +/* + * Get a pointer to a read-only buffer of data. + * + * The first time this is called, we expand the compressed data into a + * buffer. + */ +const void* _CompressedAsset::getBuffer(bool wordAligned) +{ + unsigned char* buf = NULL; + + if (mBuf != NULL) + return mBuf; + + if (mUncompressedLen > UNCOMPRESS_DATA_MAX) { + LOGD("Data exceeds UNCOMPRESS_DATA_MAX (%ld vs %d)\n", + (long) mUncompressedLen, UNCOMPRESS_DATA_MAX); + goto bail; + } + + /* + * Allocate a buffer and read the file into it. + */ + buf = new unsigned char[mUncompressedLen]; + if (buf == NULL) { + LOGW("alloc %ld bytes failed\n", (long) mUncompressedLen); + goto bail; + } + + if (mMap != NULL) { + if (!ZipFileRO::inflateBuffer(buf, mMap->getDataPtr(), + mUncompressedLen, mCompressedLen)) + goto bail; + } else { + assert(mFd >= 0); + + /* + * Seek to the start of the compressed data. + */ + if (lseek(mFd, mStart, SEEK_SET) != mStart) + goto bail; + + /* + * Expand the data into it. + */ + if (!ZipUtils::inflateToBuffer(mFd, buf, mUncompressedLen, + mCompressedLen)) + goto bail; + } + + /* success! */ + mBuf = buf; + buf = NULL; + +bail: + delete[] buf; + return mBuf; +} + diff --git a/libs/utils/AssetDir.cpp b/libs/utils/AssetDir.cpp new file mode 100644 index 000000000..c5f664ecc --- /dev/null +++ b/libs/utils/AssetDir.cpp @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2006 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. + */ + +// +// Provide access to a virtual directory in "asset space". Most of the +// implementation is in the header file or in friend functions in +// AssetManager. +// +#include + +using namespace android; + + +/* + * Find a matching entry in a vector of FileInfo. Because it's sorted, we + * can use a binary search. + * + * Assumes the vector is sorted in ascending order. + */ +/*static*/ int AssetDir::FileInfo::findEntry(const SortedVector* pVector, + const String8& fileName) +{ + FileInfo tmpInfo; + + tmpInfo.setFileName(fileName); + return pVector->indexOf(tmpInfo); + +#if 0 // don't need this after all (uses 1/2 compares of SortedVector though) + int lo, hi, cur; + + lo = 0; + hi = pVector->size() -1; + while (lo <= hi) { + int cmp; + + cur = (hi + lo) / 2; + cmp = strcmp(pVector->itemAt(cur).getFileName(), fileName); + if (cmp == 0) { + /* match, bail */ + return cur; + } else if (cmp < 0) { + /* too low */ + lo = cur + 1; + } else { + /* too high */ + hi = cur -1; + } + } + + return -1; +#endif +} + diff --git a/libs/utils/AssetManager.cpp b/libs/utils/AssetManager.cpp new file mode 100644 index 000000000..447b80193 --- /dev/null +++ b/libs/utils/AssetManager.cpp @@ -0,0 +1,1637 @@ +/* + * Copyright (C) 2006 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. + */ + +// +// Provide access to read-only assets. +// + +#define LOG_TAG "asset" +//#define LOG_NDEBUG 0 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace android; + +/* + * Names for default app, locale, and vendor. We might want to change + * these to be an actual locale, e.g. always use en-US as the default. + */ +static const char* kDefaultLocale = "default"; +static const char* kDefaultVendor = "default"; +static const char* kAssetsRoot = "assets"; +static const char* kAppZipName = NULL; //"classes.jar"; +static const char* kSystemAssets = "framework/framework-res.apk"; + +static const char* kExcludeExtension = ".EXCLUDE"; + +static Asset* const kExcludedAsset = (Asset*) 0xd000000d; + +static volatile int32_t gCount = 0; + + +/* + * =========================================================================== + * AssetManager + * =========================================================================== + */ + +int32_t AssetManager::getGlobalCount() +{ + return gCount; +} + +AssetManager::AssetManager(CacheMode cacheMode) + : mLocale(NULL), mVendor(NULL), + mResources(NULL), mConfig(new ResTable_config), + mCacheMode(cacheMode), mCacheValid(false) +{ + int count = android_atomic_inc(&gCount)+1; + //LOGI("Creating AssetManager %p #%d\n", this, count); + memset(mConfig, 0, sizeof(ResTable_config)); +} + +AssetManager::~AssetManager(void) +{ + int count = android_atomic_dec(&gCount); + //LOGI("Destroying AssetManager in %p #%d\n", this, count); + + delete mConfig; + delete mResources; + + // don't have a String class yet, so make sure we clean up + delete[] mLocale; + delete[] mVendor; +} + +bool AssetManager::addAssetPath(const String8& path, void** cookie) +{ + AutoMutex _l(mLock); + + asset_path ap; + + String8 realPath(path); + if (kAppZipName) { + realPath.appendPath(kAppZipName); + } + ap.type = ::getFileType(realPath.string()); + if (ap.type == kFileTypeRegular) { + ap.path = realPath; + } else { + ap.path = path; + ap.type = ::getFileType(path.string()); + if (ap.type != kFileTypeDirectory && ap.type != kFileTypeRegular) { + LOGW("Asset path %s is neither a directory nor file (type=%d).", + path.string(), (int)ap.type); + return false; + } + } + + // Skip if we have it already. + for (size_t i=0; i mAssetPaths.size() ? NULL : (void*)next; +} + +String8 AssetManager::getAssetPath(void* cookie) const +{ + AutoMutex _l(mLock); + const size_t which = ((size_t)cookie)-1; + if (which < mAssetPaths.size()) { + return mAssetPaths[which].path; + } + return String8(); +} + +/* + * Set the current locale. Use NULL to indicate no locale. + * + * Close and reopen Zip archives as appropriate, and reset cached + * information in the locale-specific sections of the tree. + */ +void AssetManager::setLocale(const char* locale) +{ + AutoMutex _l(mLock); + setLocaleLocked(locale); +} + +void AssetManager::setLocaleLocked(const char* locale) +{ + if (mLocale != NULL) { + /* previously set, purge cached data */ + purgeFileNameCacheLocked(); + //mZipSet.purgeLocale(); + delete[] mLocale; + } + mLocale = strdupNew(locale); + + updateResourceParamsLocked(); +} + +/* + * Set the current vendor. Use NULL to indicate no vendor. + * + * Close and reopen Zip archives as appropriate, and reset cached + * information in the vendor-specific sections of the tree. + */ +void AssetManager::setVendor(const char* vendor) +{ + AutoMutex _l(mLock); + + if (mVendor != NULL) { + /* previously set, purge cached data */ + purgeFileNameCacheLocked(); + //mZipSet.purgeVendor(); + delete[] mVendor; + } + mVendor = strdupNew(vendor); +} + +void AssetManager::setConfiguration(const ResTable_config& config, const char* locale) +{ + AutoMutex _l(mLock); + *mConfig = config; + if (locale) { + setLocaleLocked(locale); + } else if (config.language[0] != 0) { + char spec[9]; + spec[0] = config.language[0]; + spec[1] = config.language[1]; + if (config.country[0] != 0) { + spec[2] = '_'; + spec[3] = config.country[0]; + spec[4] = config.country[1]; + spec[5] = 0; + } else { + spec[3] = 0; + } + setLocaleLocked(spec); + } else { + updateResourceParamsLocked(); + } +} + +/* + * Open an asset. + * + * The data could be; + * - In a file on disk (assetBase + fileName). + * - In a compressed file on disk (assetBase + fileName.gz). + * - In a Zip archive, uncompressed or compressed. + * + * It can be in a number of different directories and Zip archives. + * The search order is: + * - [appname] + * - locale + vendor + * - "default" + vendor + * - locale + "default" + * - "default + "default" + * - "common" + * - (same as above) + * + * To find a particular file, we have to try up to eight paths with + * all three forms of data. + * + * We should probably reject requests for "illegal" filenames, e.g. those + * with illegal characters or "../" backward relative paths. + */ +Asset* AssetManager::open(const char* fileName, AccessMode mode) +{ + AutoMutex _l(mLock); + + LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager"); + + + if (mCacheMode != CACHE_OFF && !mCacheValid) + loadFileNameCacheLocked(); + + String8 assetName(kAssetsRoot); + assetName.appendPath(fileName); + + /* + * For each top-level asset path, search for the asset. + */ + + size_t i = mAssetPaths.size(); + while (i > 0) { + i--; + LOGV("Looking for asset '%s' in '%s'\n", + assetName.string(), mAssetPaths.itemAt(i).path.string()); + Asset* pAsset = openNonAssetInPathLocked(assetName.string(), mode, mAssetPaths.itemAt(i)); + if (pAsset != NULL) { + return pAsset != kExcludedAsset ? pAsset : NULL; + } + } + + return NULL; +} + +/* + * Open a non-asset file as if it were an asset. + * + * The "fileName" is the partial path starting from the application + * name. + */ +Asset* AssetManager::openNonAsset(const char* fileName, AccessMode mode) +{ + AutoMutex _l(mLock); + + LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager"); + + + if (mCacheMode != CACHE_OFF && !mCacheValid) + loadFileNameCacheLocked(); + + /* + * For each top-level asset path, search for the asset. + */ + + size_t i = mAssetPaths.size(); + while (i > 0) { + i--; + LOGV("Looking for non-asset '%s' in '%s'\n", fileName, mAssetPaths.itemAt(i).path.string()); + Asset* pAsset = openNonAssetInPathLocked( + fileName, mode, mAssetPaths.itemAt(i)); + if (pAsset != NULL) { + return pAsset != kExcludedAsset ? pAsset : NULL; + } + } + + return NULL; +} + +Asset* AssetManager::openNonAsset(void* cookie, const char* fileName, AccessMode mode) +{ + const size_t which = ((size_t)cookie)-1; + + AutoMutex _l(mLock); + + LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager"); + + + if (mCacheMode != CACHE_OFF && !mCacheValid) + loadFileNameCacheLocked(); + + if (which < mAssetPaths.size()) { + LOGV("Looking for non-asset '%s' in '%s'\n", fileName, + mAssetPaths.itemAt(which).path.string()); + Asset* pAsset = openNonAssetInPathLocked( + fileName, mode, mAssetPaths.itemAt(which)); + if (pAsset != NULL) { + return pAsset != kExcludedAsset ? pAsset : NULL; + } + } + + return NULL; +} + +/* + * Get the type of a file in the asset namespace. + * + * This currently only works for regular files. All others (including + * directories) will return kFileTypeNonexistent. + */ +FileType AssetManager::getFileType(const char* fileName) +{ + Asset* pAsset = NULL; + + /* + * Open the asset. This is less efficient than simply finding the + * file, but it's not too bad (we don't uncompress or mmap data until + * the first read() call). + */ + pAsset = open(fileName, Asset::ACCESS_STREAMING); + delete pAsset; + + if (pAsset == NULL) + return kFileTypeNonexistent; + else + return kFileTypeRegular; +} + +const ResTable* AssetManager::getResTable(bool required) const +{ + ResTable* rt = mResources; + if (rt) { + return rt; + } + + // Iterate through all asset packages, collecting resources from each. + + AutoMutex _l(mLock); + + if (mResources != NULL) { + return mResources; + } + + if (required) { + LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager"); + } + + if (mCacheMode != CACHE_OFF && !mCacheValid) + const_cast(this)->loadFileNameCacheLocked(); + + const size_t N = mAssetPaths.size(); + for (size_t i=0; i(this)-> + mZipSet.getZipResourceTable(ap.path); + if (ass == NULL) { + LOGV("loading resource table %s\n", ap.path.string()); + ass = const_cast(this)-> + openNonAssetInPathLocked("resources.arsc", + Asset::ACCESS_BUFFER, + ap); + if (ass != NULL && ass != kExcludedAsset) { + ass = const_cast(this)-> + mZipSet.setZipResourceTable(ap.path, ass); + } + } + } else { + LOGV("loading resource table %s\n", ap.path.string()); + Asset* ass = const_cast(this)-> + openNonAssetInPathLocked("resources.arsc", + Asset::ACCESS_BUFFER, + ap); + shared = false; + } + if (ass != NULL && ass != kExcludedAsset) { + if (rt == NULL) { + mResources = rt = new ResTable(); + updateResourceParamsLocked(); + } + LOGV("Installing resource asset %p in to table %p\n", ass, mResources); + rt->add(ass, (void*)(i+1), !shared); + + if (!shared) { + delete ass; + } + } + } + + if (required && !rt) LOGW("Unable to find resources file resources.arsc"); + if (!rt) { + mResources = rt = new ResTable(); + } + return rt; +} + +void AssetManager::updateResourceParamsLocked() const +{ + ResTable* res = mResources; + if (!res) { + return; + } + + size_t llen = mLocale ? strlen(mLocale) : 0; + mConfig->language[0] = 0; + mConfig->language[1] = 0; + mConfig->country[0] = 0; + mConfig->country[1] = 0; + if (llen >= 2) { + mConfig->language[0] = mLocale[0]; + mConfig->language[1] = mLocale[1]; + } + if (llen >= 5) { + mConfig->country[0] = mLocale[3]; + mConfig->country[1] = mLocale[4]; + } + mConfig->size = sizeof(*mConfig); + + res->setParameters(mConfig); +} + +const ResTable& AssetManager::getResources(bool required) const +{ + const ResTable* rt = getResTable(required); + return *rt; +} + +bool AssetManager::isUpToDate() +{ + AutoMutex _l(mLock); + return mZipSet.isUpToDate(); +} + +void AssetManager::getLocales(Vector* locales) const +{ + ResTable* res = mResources; + if (res != NULL) { + res->getLocales(locales); + } +} + +/* + * Open a non-asset file as if it were an asset, searching for it in the + * specified app. + * + * Pass in a NULL values for "appName" if the common app directory should + * be used. + */ +Asset* AssetManager::openNonAssetInPathLocked(const char* fileName, AccessMode mode, + const asset_path& ap) +{ + Asset* pAsset = NULL; + + /* look at the filesystem on disk */ + if (ap.type == kFileTypeDirectory) { + String8 path(ap.path); + path.appendPath(fileName); + + pAsset = openAssetFromFileLocked(path, mode); + + if (pAsset == NULL) { + /* try again, this time with ".gz" */ + path.append(".gz"); + pAsset = openAssetFromFileLocked(path, mode); + } + + if (pAsset != NULL) { + //printf("FOUND NA '%s' on disk\n", fileName); + pAsset->setAssetSource(path); + } + + /* look inside the zip file */ + } else { + String8 path(fileName); + + /* check the appropriate Zip file */ + ZipFileRO* pZip; + ZipEntryRO entry; + + pZip = getZipFileLocked(ap); + if (pZip != NULL) { + //printf("GOT zip, checking NA '%s'\n", (const char*) path); + entry = pZip->findEntryByName(path.string()); + if (entry != NULL) { + //printf("FOUND NA in Zip file for %s\n", appName ? appName : kAppCommon); + pAsset = openAssetFromZipLocked(pZip, entry, mode, path); + } + } + + if (pAsset != NULL) { + /* create a "source" name, for debug/display */ + pAsset->setAssetSource( + createZipSourceNameLocked(ZipSet::getPathName(ap.path.string()), String8(""), + String8(fileName))); + } + } + + return pAsset; +} + +/* + * Open an asset, searching for it in the directory hierarchy for the + * specified app. + * + * Pass in a NULL values for "appName" if the common app directory should + * be used. + */ +Asset* AssetManager::openInPathLocked(const char* fileName, AccessMode mode, + const asset_path& ap) +{ + Asset* pAsset = NULL; + + /* + * Try various combinations of locale and vendor. + */ + if (mLocale != NULL && mVendor != NULL) + pAsset = openInLocaleVendorLocked(fileName, mode, ap, mLocale, mVendor); + if (pAsset == NULL && mVendor != NULL) + pAsset = openInLocaleVendorLocked(fileName, mode, ap, NULL, mVendor); + if (pAsset == NULL && mLocale != NULL) + pAsset = openInLocaleVendorLocked(fileName, mode, ap, mLocale, NULL); + if (pAsset == NULL) + pAsset = openInLocaleVendorLocked(fileName, mode, ap, NULL, NULL); + + return pAsset; +} + +/* + * Open an asset, searching for it in the directory hierarchy for the + * specified locale and vendor. + * + * We also search in "app.jar". + * + * Pass in NULL values for "appName", "locale", and "vendor" if the + * defaults should be used. + */ +Asset* AssetManager::openInLocaleVendorLocked(const char* fileName, AccessMode mode, + const asset_path& ap, const char* locale, const char* vendor) +{ + Asset* pAsset = NULL; + + if (ap.type == kFileTypeDirectory) { + if (mCacheMode == CACHE_OFF) { + /* look at the filesystem on disk */ + String8 path(createPathNameLocked(ap, locale, vendor)); + path.appendPath(fileName); + + String8 excludeName(path); + excludeName.append(kExcludeExtension); + if (::getFileType(excludeName.string()) != kFileTypeNonexistent) { + /* say no more */ + //printf("+++ excluding '%s'\n", (const char*) excludeName); + return kExcludedAsset; + } + + pAsset = openAssetFromFileLocked(path, mode); + + if (pAsset == NULL) { + /* try again, this time with ".gz" */ + path.append(".gz"); + pAsset = openAssetFromFileLocked(path, mode); + } + + if (pAsset != NULL) + pAsset->setAssetSource(path); + } else { + /* find in cache */ + String8 path(createPathNameLocked(ap, locale, vendor)); + path.appendPath(fileName); + + AssetDir::FileInfo tmpInfo; + bool found = false; + + String8 excludeName(path); + excludeName.append(kExcludeExtension); + + if (mCache.indexOf(excludeName) != NAME_NOT_FOUND) { + /* go no farther */ + //printf("+++ Excluding '%s'\n", (const char*) excludeName); + return kExcludedAsset; + } + + /* + * File compression extensions (".gz") don't get stored in the + * name cache, so we have to try both here. + */ + if (mCache.indexOf(path) != NAME_NOT_FOUND) { + found = true; + pAsset = openAssetFromFileLocked(path, mode); + if (pAsset == NULL) { + /* try again, this time with ".gz" */ + path.append(".gz"); + pAsset = openAssetFromFileLocked(path, mode); + } + } + + if (pAsset != NULL) + pAsset->setAssetSource(path); + + /* + * Don't continue the search into the Zip files. Our cached info + * said it was a file on disk; to be consistent with openDir() + * we want to return the loose asset. If the cached file gets + * removed, we fail. + * + * The alternative is to update our cache when files get deleted, + * or make some sort of "best effort" promise, but for now I'm + * taking the hard line. + */ + if (found) { + if (pAsset == NULL) + LOGD("Expected file not found: '%s'\n", path.string()); + return pAsset; + } + } + } + + /* + * Either it wasn't found on disk or on the cached view of the disk. + * Dig through the currently-opened set of Zip files. If caching + * is disabled, the Zip file may get reopened. + */ + if (pAsset == NULL && ap.type == kFileTypeRegular) { + String8 path; + + path.appendPath((locale != NULL) ? locale : kDefaultLocale); + path.appendPath((vendor != NULL) ? vendor : kDefaultVendor); + path.appendPath(fileName); + + /* check the appropriate Zip file */ + ZipFileRO* pZip; + ZipEntryRO entry; + + pZip = getZipFileLocked(ap); + if (pZip != NULL) { + //printf("GOT zip, checking '%s'\n", (const char*) path); + entry = pZip->findEntryByName(path.string()); + if (entry != NULL) { + //printf("FOUND in Zip file for %s/%s-%s\n", + // appName, locale, vendor); + pAsset = openAssetFromZipLocked(pZip, entry, mode, path); + } + } + + if (pAsset != NULL) { + /* create a "source" name, for debug/display */ + pAsset->setAssetSource(createZipSourceNameLocked(ZipSet::getPathName(ap.path.string()), + String8(""), String8(fileName))); + } + } + + return pAsset; +} + +/* + * Create a "source name" for a file from a Zip archive. + */ +String8 AssetManager::createZipSourceNameLocked(const String8& zipFileName, + const String8& dirName, const String8& fileName) +{ + String8 sourceName("zip:"); + sourceName.append(zipFileName); + sourceName.append(":"); + if (dirName.length() > 0) { + sourceName.appendPath(dirName); + } + sourceName.appendPath(fileName); + return sourceName; +} + +/* + * Create a path to a loose asset (asset-base/app/locale/vendor). + */ +String8 AssetManager::createPathNameLocked(const asset_path& ap, const char* locale, + const char* vendor) +{ + String8 path(ap.path); + path.appendPath((locale != NULL) ? locale : kDefaultLocale); + path.appendPath((vendor != NULL) ? vendor : kDefaultVendor); + return path; +} + +/* + * Create a path to a loose asset (asset-base/app/rootDir). + */ +String8 AssetManager::createPathNameLocked(const asset_path& ap, const char* rootDir) +{ + String8 path(ap.path); + if (rootDir != NULL) path.appendPath(rootDir); + return path; +} + +/* + * Return a pointer to one of our open Zip archives. Returns NULL if no + * matching Zip file exists. + * + * Right now we have 2 possible Zip files (1 each in app/"common"). + * + * If caching is set to CACHE_OFF, to get the expected behavior we + * need to reopen the Zip file on every request. That would be silly + * and expensive, so instead we just check the file modification date. + * + * Pass in NULL values for "appName", "locale", and "vendor" if the + * generics should be used. + */ +ZipFileRO* AssetManager::getZipFileLocked(const asset_path& ap) +{ + LOGV("getZipFileLocked() in %p\n", this); + + return mZipSet.getZip(ap.path); +} + +/* + * Try to open an asset from a file on disk. + * + * If the file is compressed with gzip, we seek to the start of the + * deflated data and pass that in (just like we would for a Zip archive). + * + * For uncompressed data, we may already have an mmap()ed version sitting + * around. If so, we want to hand that to the Asset instead. + * + * This returns NULL if the file doesn't exist, couldn't be opened, or + * claims to be a ".gz" but isn't. + */ +Asset* AssetManager::openAssetFromFileLocked(const String8& pathName, + AccessMode mode) +{ + Asset* pAsset = NULL; + + if (strcasecmp(pathName.getPathExtension().string(), ".gz") == 0) { + //printf("TRYING '%s'\n", (const char*) pathName); + pAsset = Asset::createFromCompressedFile(pathName.string(), mode); + } else { + //printf("TRYING '%s'\n", (const char*) pathName); + pAsset = Asset::createFromFile(pathName.string(), mode); + } + + return pAsset; +} + +/* + * Given an entry in a Zip archive, create a new Asset object. + * + * If the entry is uncompressed, we may want to create or share a + * slice of shared memory. + */ +Asset* AssetManager::openAssetFromZipLocked(const ZipFileRO* pZipFile, + const ZipEntryRO entry, AccessMode mode, const String8& entryName) +{ + Asset* pAsset = NULL; + + // TODO: look for previously-created shared memory slice? + int method; + long uncompressedLen; + + //printf("USING Zip '%s'\n", pEntry->getFileName()); + + //pZipFile->getEntryInfo(entry, &method, &uncompressedLen, &compressedLen, + // &offset); + if (!pZipFile->getEntryInfo(entry, &method, &uncompressedLen, NULL, NULL, + NULL, NULL)) + { + LOGW("getEntryInfo failed\n"); + return NULL; + } + + FileMap* dataMap = pZipFile->createEntryFileMap(entry); + if (dataMap == NULL) { + LOGW("create map from entry failed\n"); + return NULL; + } + + if (method == ZipFileRO::kCompressStored) { + pAsset = Asset::createFromUncompressedMap(dataMap, mode); + LOGV("Opened uncompressed entry %s in zip %s mode %d: %p", entryName.string(), + dataMap->getFileName(), mode, pAsset); + } else { + pAsset = Asset::createFromCompressedMap(dataMap, method, + uncompressedLen, mode); + LOGV("Opened compressed entry %s in zip %s mode %d: %p", entryName.string(), + dataMap->getFileName(), mode, pAsset); + } + if (pAsset == NULL) { + /* unexpected */ + LOGW("create from segment failed\n"); + } + + return pAsset; +} + + + +/* + * Open a directory in the asset namespace. + * + * An "asset directory" is simply the combination of all files in all + * locations, with ".gz" stripped for loose files. With app, locale, and + * vendor defined, we have 8 directories and 2 Zip archives to scan. + * + * Pass in "" for the root dir. + */ +AssetDir* AssetManager::openDir(const char* dirName) +{ + AutoMutex _l(mLock); + + AssetDir* pDir = NULL; + SortedVector* pMergedInfo = NULL; + + LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager"); + assert(dirName != NULL); + + //printf("+++ openDir(%s) in '%s'\n", dirName, (const char*) mAssetBase); + + if (mCacheMode != CACHE_OFF && !mCacheValid) + loadFileNameCacheLocked(); + + pDir = new AssetDir; + + /* + * Scan the various directories, merging what we find into a single + * vector. We want to scan them in reverse priority order so that + * the ".EXCLUDE" processing works correctly. Also, if we decide we + * want to remember where the file is coming from, we'll get the right + * version. + * + * We start with Zip archives, then do loose files. + */ + pMergedInfo = new SortedVector; + + size_t i = mAssetPaths.size(); + while (i > 0) { + i--; + const asset_path& ap = mAssetPaths.itemAt(i); + if (ap.type == kFileTypeRegular) { + LOGV("Adding directory %s from zip %s", dirName, ap.path.string()); + scanAndMergeZipLocked(pMergedInfo, ap, kAssetsRoot, dirName); + } else { + LOGV("Adding directory %s from dir %s", dirName, ap.path.string()); + scanAndMergeDirLocked(pMergedInfo, ap, kAssetsRoot, dirName); + } + } + +#if 0 + printf("FILE LIST:\n"); + for (i = 0; i < (size_t) pMergedInfo->size(); i++) { + printf(" %d: (%d) '%s'\n", i, + pMergedInfo->itemAt(i).getFileType(), + (const char*) pMergedInfo->itemAt(i).getFileName()); + } +#endif + + pDir->setFileList(pMergedInfo); + return pDir; +} + +/* + * Scan the contents of the specified directory and merge them into the + * "pMergedInfo" vector, removing previous entries if we find "exclude" + * directives. + * + * Returns "false" if we found nothing to contribute. + */ +bool AssetManager::scanAndMergeDirLocked(SortedVector* pMergedInfo, + const asset_path& ap, const char* rootDir, const char* dirName) +{ + SortedVector* pContents; + String8 path; + + assert(pMergedInfo != NULL); + + //printf("scanAndMergeDir: %s %s %s %s\n", appName, locale, vendor,dirName); + + if (mCacheValid) { + int i, start, count; + + pContents = new SortedVector; + + /* + * Get the basic partial path and find it in the cache. That's + * the start point for the search. + */ + path = createPathNameLocked(ap, rootDir); + if (dirName[0] != '\0') + path.appendPath(dirName); + + start = mCache.indexOf(path); + if (start == NAME_NOT_FOUND) { + //printf("+++ not found in cache: dir '%s'\n", (const char*) path); + delete pContents; + return false; + } + + /* + * The match string looks like "common/default/default/foo/bar/". + * The '/' on the end ensures that we don't match on the directory + * itself or on ".../foo/barfy/". + */ + path.append("/"); + + count = mCache.size(); + + /* + * Pick out the stuff in the current dir by examining the pathname. + * It needs to match the partial pathname prefix, and not have a '/' + * (fssep) anywhere after the prefix. + */ + for (i = start+1; i < count; i++) { + if (mCache[i].getFileName().length() > path.length() && + strncmp(mCache[i].getFileName().string(), path.string(), path.length()) == 0) + { + const char* name = mCache[i].getFileName().string(); + // XXX THIS IS BROKEN! Looks like we need to store the full + // path prefix separately from the file path. + if (strchr(name + path.length(), '/') == NULL) { + /* grab it, reducing path to just the filename component */ + AssetDir::FileInfo tmp = mCache[i]; + tmp.setFileName(tmp.getFileName().getPathLeaf()); + pContents->add(tmp); + } + } else { + /* no longer in the dir or its subdirs */ + break; + } + + } + } else { + path = createPathNameLocked(ap, rootDir); + if (dirName[0] != '\0') + path.appendPath(dirName); + pContents = scanDirLocked(path); + if (pContents == NULL) + return false; + } + + // if we wanted to do an incremental cache fill, we would do it here + + /* + * Process "exclude" directives. If we find a filename that ends with + * ".EXCLUDE", we look for a matching entry in the "merged" set, and + * remove it if we find it. We also delete the "exclude" entry. + */ + int i, count, exclExtLen; + + count = pContents->size(); + exclExtLen = strlen(kExcludeExtension); + for (i = 0; i < count; i++) { + const char* name; + int nameLen; + + name = pContents->itemAt(i).getFileName().string(); + nameLen = strlen(name); + if (nameLen > exclExtLen && + strcmp(name + (nameLen - exclExtLen), kExcludeExtension) == 0) + { + String8 match(name, nameLen - exclExtLen); + int matchIdx; + + matchIdx = AssetDir::FileInfo::findEntry(pMergedInfo, match); + if (matchIdx > 0) { + LOGV("Excluding '%s' [%s]\n", + pMergedInfo->itemAt(matchIdx).getFileName().string(), + pMergedInfo->itemAt(matchIdx).getSourceName().string()); + pMergedInfo->removeAt(matchIdx); + } else { + //printf("+++ no match on '%s'\n", (const char*) match); + } + + LOGD("HEY: size=%d removing %d\n", (int)pContents->size(), i); + pContents->removeAt(i); + i--; // adjust "for" loop + count--; // and loop limit + } + } + + mergeInfoLocked(pMergedInfo, pContents); + + delete pContents; + + return true; +} + +/* + * Scan the contents of the specified directory, and stuff what we find + * into a newly-allocated vector. + * + * Files ending in ".gz" will have their extensions removed. + * + * We should probably think about skipping files with "illegal" names, + * e.g. illegal characters (/\:) or excessive length. + * + * Returns NULL if the specified directory doesn't exist. + */ +SortedVector* AssetManager::scanDirLocked(const String8& path) +{ + SortedVector* pContents = NULL; + DIR* dir; + struct dirent* entry; + FileType fileType; + + LOGV("Scanning dir '%s'\n", path.string()); + + dir = opendir(path.string()); + if (dir == NULL) + return NULL; + + pContents = new SortedVector; + + while (1) { + entry = readdir(dir); + if (entry == NULL) + break; + + if (strcmp(entry->d_name, ".") == 0 || + strcmp(entry->d_name, "..") == 0) + continue; + +#ifdef _DIRENT_HAVE_D_TYPE + if (entry->d_type == DT_REG) + fileType = kFileTypeRegular; + else if (entry->d_type == DT_DIR) + fileType = kFileTypeDirectory; + else + fileType = kFileTypeUnknown; +#else + // stat the file + fileType = ::getFileType(path.appendPathCopy(entry->d_name).string()); +#endif + + if (fileType != kFileTypeRegular && fileType != kFileTypeDirectory) + continue; + + AssetDir::FileInfo info; + info.set(String8(entry->d_name), fileType); + if (strcasecmp(info.getFileName().getPathExtension().string(), ".gz") == 0) + info.setFileName(info.getFileName().getBasePath()); + info.setSourceName(path.appendPathCopy(info.getFileName())); + pContents->add(info); + } + + closedir(dir); + return pContents; +} + +/* + * Scan the contents out of the specified Zip archive, and merge what we + * find into "pMergedInfo". If the Zip archive in question doesn't exist, + * we return immediately. + * + * Returns "false" if we found nothing to contribute. + */ +bool AssetManager::scanAndMergeZipLocked(SortedVector* pMergedInfo, + const asset_path& ap, const char* rootDir, const char* baseDirName) +{ + ZipFileRO* pZip; + Vector dirs; + AssetDir::FileInfo info; + SortedVector contents; + String8 sourceName, zipName, dirName; + + pZip = mZipSet.getZip(ap.path); + if (pZip == NULL) { + LOGW("Failure opening zip %s\n", ap.path.string()); + return false; + } + + zipName = ZipSet::getPathName(ap.path.string()); + + /* convert "sounds" to "rootDir/sounds" */ + if (rootDir != NULL) dirName = rootDir; + dirName.appendPath(baseDirName); + + /* + * Scan through the list of files, looking for a match. The files in + * the Zip table of contents are not in sorted order, so we have to + * process the entire list. We're looking for a string that begins + * with the characters in "dirName", is followed by a '/', and has no + * subsequent '/' in the stuff that follows. + * + * What makes this especially fun is that directories are not stored + * explicitly in Zip archives, so we have to infer them from context. + * When we see "sounds/foo.wav" we have to leave a note to ourselves + * to insert a directory called "sounds" into the list. We store + * these in temporary vector so that we only return each one once. + * + * Name comparisons are case-sensitive to match UNIX filesystem + * semantics. + */ + int dirNameLen = dirName.length(); + for (int i = 0; i < pZip->getNumEntries(); i++) { + ZipEntryRO entry; + char nameBuf[256]; + + entry = pZip->findEntryByIndex(i); + if (pZip->getEntryFileName(entry, nameBuf, sizeof(nameBuf)) != 0) { + // TODO: fix this if we expect to have long names + LOGE("ARGH: name too long?\n"); + continue; + } + if (dirNameLen == 0 || + (strncmp(nameBuf, dirName.string(), dirNameLen) == 0 && + nameBuf[dirNameLen] == '/')) + { + const char* cp; + const char* nextSlash; + + cp = nameBuf + dirNameLen; + if (dirNameLen != 0) + cp++; // advance past the '/' + + nextSlash = strchr(cp, '/'); +//xxx this may break if there are bare directory entries + if (nextSlash == NULL) { + /* this is a file in the requested directory */ + + info.set(String8(nameBuf).getPathLeaf(), kFileTypeRegular); + + info.setSourceName( + createZipSourceNameLocked(zipName, dirName, info.getFileName())); + + contents.add(info); + //printf("FOUND: file '%s'\n", (const char*) info.mFileName); + } else { + /* this is a subdir; add it if we don't already have it*/ + String8 subdirName(cp, nextSlash - cp); + size_t j; + size_t N = dirs.size(); + + for (j = 0; j < N; j++) { + if (subdirName == dirs[j]) { + break; + } + } + if (j == N) { + dirs.add(subdirName); + } + + //printf("FOUND: dir '%s'\n", (const char*) subdirName); + } + } + } + + /* + * Add the set of unique directories. + */ + for (int i = 0; i < (int) dirs.size(); i++) { + info.set(dirs[i], kFileTypeDirectory); + info.setSourceName( + createZipSourceNameLocked(zipName, dirName, info.getFileName())); + contents.add(info); + } + + mergeInfoLocked(pMergedInfo, &contents); + + return true; +} + + +/* + * Merge two vectors of FileInfo. + * + * The merged contents will be stuffed into *pMergedInfo. + * + * If an entry for a file exists in both "pMergedInfo" and "pContents", + * we use the newer "pContents" entry. + */ +void AssetManager::mergeInfoLocked(SortedVector* pMergedInfo, + const SortedVector* pContents) +{ + /* + * Merge what we found in this directory with what we found in + * other places. + * + * Two basic approaches: + * (1) Create a new array that holds the unique values of the two + * arrays. + * (2) Take the elements from pContents and shove them into pMergedInfo. + * + * Because these are vectors of complex objects, moving elements around + * inside the vector requires constructing new objects and allocating + * storage for members. With approach #1, we're always adding to the + * end, whereas with #2 we could be inserting multiple elements at the + * front of the vector. Approach #1 requires a full copy of the + * contents of pMergedInfo, but approach #2 requires the same copy for + * every insertion at the front of pMergedInfo. + * + * (We should probably use a SortedVector interface that allows us to + * just stuff items in, trusting us to maintain the sort order.) + */ + SortedVector* pNewSorted; + int mergeMax, contMax; + int mergeIdx, contIdx; + + pNewSorted = new SortedVector; + mergeMax = pMergedInfo->size(); + contMax = pContents->size(); + mergeIdx = contIdx = 0; + + while (mergeIdx < mergeMax || contIdx < contMax) { + if (mergeIdx == mergeMax) { + /* hit end of "merge" list, copy rest of "contents" */ + pNewSorted->add(pContents->itemAt(contIdx)); + contIdx++; + } else if (contIdx == contMax) { + /* hit end of "cont" list, copy rest of "merge" */ + pNewSorted->add(pMergedInfo->itemAt(mergeIdx)); + mergeIdx++; + } else if (pMergedInfo->itemAt(mergeIdx) == pContents->itemAt(contIdx)) + { + /* items are identical, add newer and advance both indices */ + pNewSorted->add(pContents->itemAt(contIdx)); + mergeIdx++; + contIdx++; + } else if (pMergedInfo->itemAt(mergeIdx) < pContents->itemAt(contIdx)) + { + /* "merge" is lower, add that one */ + pNewSorted->add(pMergedInfo->itemAt(mergeIdx)); + mergeIdx++; + } else { + /* "cont" is lower, add that one */ + assert(pContents->itemAt(contIdx) < pMergedInfo->itemAt(mergeIdx)); + pNewSorted->add(pContents->itemAt(contIdx)); + contIdx++; + } + } + + /* + * Overwrite the "merged" list with the new stuff. + */ + *pMergedInfo = *pNewSorted; + delete pNewSorted; + +#if 0 // for Vector, rather than SortedVector + int i, j; + for (i = pContents->size() -1; i >= 0; i--) { + bool add = true; + + for (j = pMergedInfo->size() -1; j >= 0; j--) { + /* case-sensitive comparisons, to behave like UNIX fs */ + if (strcmp(pContents->itemAt(i).mFileName, + pMergedInfo->itemAt(j).mFileName) == 0) + { + /* match, don't add this entry */ + add = false; + break; + } + } + + if (add) + pMergedInfo->add(pContents->itemAt(i)); + } +#endif +} + + +/* + * Load all files into the file name cache. We want to do this across + * all combinations of { appname, locale, vendor }, performing a recursive + * directory traversal. + * + * This is not the most efficient data structure. Also, gathering the + * information as we needed it (file-by-file or directory-by-directory) + * would be faster. However, on the actual device, 99% of the files will + * live in Zip archives, so this list will be very small. The trouble + * is that we have to check the "loose" files first, so it's important + * that we don't beat the filesystem silly looking for files that aren't + * there. + * + * Note on thread safety: this is the only function that causes updates + * to mCache, and anybody who tries to use it will call here if !mCacheValid, + * so we need to employ a mutex here. + */ +void AssetManager::loadFileNameCacheLocked(void) +{ + assert(!mCacheValid); + assert(mCache.size() == 0); + +#ifdef DO_TIMINGS // need to link against -lrt for this now + DurationTimer timer; + timer.start(); +#endif + + fncScanLocked(&mCache, ""); + +#ifdef DO_TIMINGS + timer.stop(); + LOGD("Cache scan took %.3fms\n", + timer.durationUsecs() / 1000.0); +#endif + +#if 0 + int i; + printf("CACHED FILE LIST (%d entries):\n", mCache.size()); + for (i = 0; i < (int) mCache.size(); i++) { + printf(" %d: (%d) '%s'\n", i, + mCache.itemAt(i).getFileType(), + (const char*) mCache.itemAt(i).getFileName()); + } +#endif + + mCacheValid = true; +} + +/* + * Scan up to 8 versions of the specified directory. + */ +void AssetManager::fncScanLocked(SortedVector* pMergedInfo, + const char* dirName) +{ + size_t i = mAssetPaths.size(); + while (i > 0) { + i--; + const asset_path& ap = mAssetPaths.itemAt(i); + fncScanAndMergeDirLocked(pMergedInfo, ap, NULL, NULL, dirName); + if (mLocale != NULL) + fncScanAndMergeDirLocked(pMergedInfo, ap, mLocale, NULL, dirName); + if (mVendor != NULL) + fncScanAndMergeDirLocked(pMergedInfo, ap, NULL, mVendor, dirName); + if (mLocale != NULL && mVendor != NULL) + fncScanAndMergeDirLocked(pMergedInfo, ap, mLocale, mVendor, dirName); + } +} + +/* + * Recursively scan this directory and all subdirs. + * + * This is similar to scanAndMergeDir, but we don't remove the .EXCLUDE + * files, and we prepend the extended partial path to the filenames. + */ +bool AssetManager::fncScanAndMergeDirLocked( + SortedVector* pMergedInfo, + const asset_path& ap, const char* locale, const char* vendor, + const char* dirName) +{ + SortedVector* pContents; + String8 partialPath; + String8 fullPath; + + // XXX This is broken -- the filename cache needs to hold the base + // asset path separately from its filename. + + partialPath = createPathNameLocked(ap, locale, vendor); + if (dirName[0] != '\0') { + partialPath.appendPath(dirName); + } + + fullPath = partialPath; + pContents = scanDirLocked(fullPath); + if (pContents == NULL) { + return false; // directory did not exist + } + + /* + * Scan all subdirectories of the current dir, merging what we find + * into "pMergedInfo". + */ + for (int i = 0; i < (int) pContents->size(); i++) { + if (pContents->itemAt(i).getFileType() == kFileTypeDirectory) { + String8 subdir(dirName); + subdir.appendPath(pContents->itemAt(i).getFileName()); + + fncScanAndMergeDirLocked(pMergedInfo, ap, locale, vendor, subdir.string()); + } + } + + /* + * To be consistent, we want entries for the root directory. If + * we're the root, add one now. + */ + if (dirName[0] == '\0') { + AssetDir::FileInfo tmpInfo; + + tmpInfo.set(String8(""), kFileTypeDirectory); + tmpInfo.setSourceName(createPathNameLocked(ap, locale, vendor)); + pContents->add(tmpInfo); + } + + /* + * We want to prepend the extended partial path to every entry in + * "pContents". It's the same value for each entry, so this will + * not change the sorting order of the vector contents. + */ + for (int i = 0; i < (int) pContents->size(); i++) { + const AssetDir::FileInfo& info = pContents->itemAt(i); + pContents->editItemAt(i).setFileName(partialPath.appendPathCopy(info.getFileName())); + } + + mergeInfoLocked(pMergedInfo, pContents); + return true; +} + +/* + * Trash the cache. + */ +void AssetManager::purgeFileNameCacheLocked(void) +{ + mCacheValid = false; + mCache.clear(); +} + +/* + * =========================================================================== + * AssetManager::SharedZip + * =========================================================================== + */ + + +Mutex AssetManager::SharedZip::gLock; +DefaultKeyedVector > AssetManager::SharedZip::gOpen; + +AssetManager::SharedZip::SharedZip(const String8& path, time_t modWhen) + : mPath(path), mZipFile(NULL), mModWhen(modWhen), mResourceTableAsset(NULL) +{ + //LOGI("Creating SharedZip %p %s\n", this, (const char*)mPath); + mZipFile = new ZipFileRO; + LOGV("+++ opening zip '%s'\n", mPath.string()); + if (mZipFile->open(mPath.string()) != NO_ERROR) { + LOGD("failed to open Zip archive '%s'\n", mPath.string()); + delete mZipFile; + mZipFile = NULL; + } +} + +sp AssetManager::SharedZip::get(const String8& path) +{ + AutoMutex _l(gLock); + time_t modWhen = getFileModDate(path); + sp zip = gOpen.valueFor(path).promote(); + if (zip != NULL && zip->mModWhen == modWhen) { + return zip; + } + zip = new SharedZip(path, modWhen); + gOpen.add(path, zip); + return zip; + +} + +ZipFileRO* AssetManager::SharedZip::getZip() +{ + return mZipFile; +} + +Asset* AssetManager::SharedZip::getResourceTableAsset() +{ + LOGV("Getting from SharedZip %p resource asset %p\n", this, mResourceTableAsset); + return mResourceTableAsset; +} + +Asset* AssetManager::SharedZip::setResourceTableAsset(Asset* asset) +{ + { + AutoMutex _l(gLock); + if (mResourceTableAsset == NULL) { + mResourceTableAsset = asset; + // This is not thread safe the first time it is called, so + // do it here with the global lock held. + asset->getBuffer(true); + return asset; + } + } + delete asset; + return mResourceTableAsset; +} + +bool AssetManager::SharedZip::isUpToDate() +{ + time_t modWhen = getFileModDate(mPath.string()); + return mModWhen == modWhen; +} + +AssetManager::SharedZip::~SharedZip() +{ + //LOGI("Destroying SharedZip %p %s\n", this, (const char*)mPath); + if (mResourceTableAsset != NULL) { + delete mResourceTableAsset; + } + if (mZipFile != NULL) { + delete mZipFile; + LOGV("Closed '%s'\n", mPath.string()); + } +} + +/* + * =========================================================================== + * AssetManager::ZipSet + * =========================================================================== + */ + +/* + * Constructor. + */ +AssetManager::ZipSet::ZipSet(void) +{ +} + +/* + * Destructor. Close any open archives. + */ +AssetManager::ZipSet::~ZipSet(void) +{ + size_t N = mZipFile.size(); + for (size_t i = 0; i < N; i++) + closeZip(i); +} + +/* + * Close a Zip file and reset the entry. + */ +void AssetManager::ZipSet::closeZip(int idx) +{ + mZipFile.editItemAt(idx) = NULL; +} + + +/* + * Retrieve the appropriate Zip file from the set. + */ +ZipFileRO* AssetManager::ZipSet::getZip(const String8& path) +{ + int idx = getIndex(path); + sp zip = mZipFile[idx]; + if (zip == NULL) { + zip = SharedZip::get(path); + mZipFile.editItemAt(idx) = zip; + } + return zip->getZip(); +} + +Asset* AssetManager::ZipSet::getZipResourceTable(const String8& path) +{ + int idx = getIndex(path); + sp zip = mZipFile[idx]; + if (zip == NULL) { + zip = SharedZip::get(path); + mZipFile.editItemAt(idx) = zip; + } + return zip->getResourceTableAsset(); +} + +Asset* AssetManager::ZipSet::setZipResourceTable(const String8& path, + Asset* asset) +{ + int idx = getIndex(path); + sp zip = mZipFile[idx]; + // doesn't make sense to call before previously accessing. + return zip->setResourceTableAsset(asset); +} + +/* + * Generate the partial pathname for the specified archive. The caller + * gets to prepend the asset root directory. + * + * Returns something like "common/en-US-noogle.jar". + */ +/*static*/ String8 AssetManager::ZipSet::getPathName(const char* zipPath) +{ + return String8(zipPath); +} + +bool AssetManager::ZipSet::isUpToDate() +{ + const size_t N = mZipFile.size(); + for (size_t i=0; iisUpToDate()) { + return false; + } + } + return true; +} + +/* + * Compute the zip file's index. + * + * "appName", "locale", and "vendor" should be set to NULL to indicate the + * default directory. + */ +int AssetManager::ZipSet::getIndex(const String8& zip) const +{ + const size_t N = mZipPath.size(); + for (size_t i=0; i + +#include +#include +#include +#include + +#include + +namespace android { + +// --------------------------------------------------------------------------- + +sp IBinder::queryLocalInterface(const String16& descriptor) +{ + return NULL; +} + +BBinder* IBinder::localBinder() +{ + return NULL; +} + +BpBinder* IBinder::remoteBinder() +{ + return NULL; +} + +bool IBinder::checkSubclass(const void* /*subclassID*/) const +{ + return false; +} + +// --------------------------------------------------------------------------- + +class BBinder::Extras +{ +public: + Mutex mLock; + BpBinder::ObjectManager mObjects; +}; + +// --------------------------------------------------------------------------- + +BBinder::BBinder() + : mExtras(NULL) +{ +} + +bool BBinder::isBinderAlive() const +{ + return true; +} + +status_t BBinder::pingBinder() +{ + return NO_ERROR; +} + +String16 BBinder::getInterfaceDescriptor() const +{ + LOGW("reached BBinder::getInterfaceDescriptor (this=%p)", this); + return String16(); +} + +status_t BBinder::transact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + data.setDataPosition(0); + + status_t err = NO_ERROR; + switch (code) { + case PING_TRANSACTION: + reply->writeInt32(pingBinder()); + break; + default: + err = onTransact(code, data, reply, flags); + break; + } + + if (reply != NULL) { + reply->setDataPosition(0); + } + + return err; +} + +status_t BBinder::linkToDeath( + const sp& recipient, void* cookie, uint32_t flags) +{ + return INVALID_OPERATION; +} + +status_t BBinder::unlinkToDeath( + const wp& recipient, void* cookie, uint32_t flags, + wp* outRecipient) +{ + return INVALID_OPERATION; +} + +status_t BBinder::dump(int fd, const Vector& args) +{ + return NO_ERROR; +} + +void BBinder::attachObject( + const void* objectID, void* object, void* cleanupCookie, + object_cleanup_func func) +{ + Extras* e = mExtras; + + if (!e) { + e = new Extras; + if (android_atomic_cmpxchg(0, reinterpret_cast(e), + reinterpret_cast(&mExtras)) != 0) { + delete e; + e = mExtras; + } + if (e == 0) return; // out of memory + } + + AutoMutex _l(e->mLock); + e->mObjects.attach(objectID, object, cleanupCookie, func); +} + +void* BBinder::findObject(const void* objectID) const +{ + Extras* e = mExtras; + if (!e) return NULL; + + AutoMutex _l(e->mLock); + return e->mObjects.find(objectID); +} + +void BBinder::detachObject(const void* objectID) +{ + Extras* e = mExtras; + if (!e) return; + + AutoMutex _l(e->mLock); + e->mObjects.detach(objectID); +} + +BBinder* BBinder::localBinder() +{ + return this; +} + +BBinder::~BBinder() +{ + if (mExtras) delete mExtras; +} + + +status_t BBinder::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch (code) { + case INTERFACE_TRANSACTION: + reply->writeString16(getInterfaceDescriptor()); + return NO_ERROR; + + case DUMP_TRANSACTION: { + int fd = data.readFileDescriptor(); + int argc = data.readInt32(); + Vector args; + for (int i = 0; i < argc && data.dataAvail() > 0; i++) { + args.add(data.readString16()); + } + return dump(fd, args); + } + default: + return UNKNOWN_TRANSACTION; + } +} + +// --------------------------------------------------------------------------- + +enum { + // This is used to transfer ownership of the remote binder from + // the BpRefBase object holding it (when it is constructed), to the + // owner of the BpRefBase object when it first acquires that BpRefBase. + kRemoteAcquired = 0x00000001 +}; + +BpRefBase::BpRefBase(const sp& o) + : mRemote(o.get()), mRefs(NULL), mState(0) +{ + extendObjectLifetime(OBJECT_LIFETIME_WEAK); + + if (mRemote) { + mRemote->incStrong(this); // Removed on first IncStrong(). + mRefs = mRemote->createWeak(this); // Held for our entire lifetime. + } +} + +BpRefBase::~BpRefBase() +{ + if (mRemote) { + if (!(mState&kRemoteAcquired)) { + mRemote->decStrong(this); + } + mRefs->decWeak(this); + } +} + +void BpRefBase::onFirstRef() +{ + android_atomic_or(kRemoteAcquired, &mState); +} + +void BpRefBase::onLastStrongRef(const void* id) +{ + if (mRemote) { + mRemote->decStrong(this); + } +} + +bool BpRefBase::onIncStrongAttempted(uint32_t flags, const void* id) +{ + return mRemote ? mRefs->attemptIncStrong(this) : false; +} + +// --------------------------------------------------------------------------- + +}; // namespace android diff --git a/libs/utils/BpBinder.cpp b/libs/utils/BpBinder.cpp new file mode 100644 index 000000000..69ab19574 --- /dev/null +++ b/libs/utils/BpBinder.cpp @@ -0,0 +1,348 @@ +/* + * Copyright (C) 2005 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 "BpBinder" +//#define LOG_NDEBUG 0 + +#include + +#include +#include + +#include + +//#undef LOGV +//#define LOGV(...) fprintf(stderr, __VA_ARGS__) + +namespace android { + +// --------------------------------------------------------------------------- + +BpBinder::ObjectManager::ObjectManager() +{ +} + +BpBinder::ObjectManager::~ObjectManager() +{ + kill(); +} + +void BpBinder::ObjectManager::attach( + const void* objectID, void* object, void* cleanupCookie, + IBinder::object_cleanup_func func) +{ + entry_t e; + e.object = object; + e.cleanupCookie = cleanupCookie; + e.func = func; + + if (mObjects.indexOfKey(objectID) >= 0) { + LOGE("Trying to attach object ID %p to binder ObjectManager %p with object %p, but object ID already in use", + objectID, this, object); + return; + } + + mObjects.add(objectID, e); +} + +void* BpBinder::ObjectManager::find(const void* objectID) const +{ + const ssize_t i = mObjects.indexOfKey(objectID); + if (i < 0) return NULL; + return mObjects.valueAt(i).object; +} + +void BpBinder::ObjectManager::detach(const void* objectID) +{ + mObjects.removeItem(objectID); +} + +void BpBinder::ObjectManager::kill() +{ + const size_t N = mObjects.size(); + LOGV("Killing %d objects in manager %p", N, this); + for (size_t i=0; iincWeakHandle(handle); +} + +String16 BpBinder::getInterfaceDescriptor() const +{ + String16 res; + Parcel send, reply; + status_t err = const_cast(this)->transact( + INTERFACE_TRANSACTION, send, &reply); + if (err == NO_ERROR) { + res = reply.readString16(); + } + return res; +} + +bool BpBinder::isBinderAlive() const +{ + return mAlive != 0; +} + +status_t BpBinder::pingBinder() +{ + Parcel send; + Parcel reply; + status_t err = transact(PING_TRANSACTION, send, &reply); + if (err != NO_ERROR) return err; + if (reply.dataSize() < sizeof(status_t)) return NOT_ENOUGH_DATA; + return (status_t)reply.readInt32(); +} + +status_t BpBinder::dump(int fd, const Vector& args) +{ + Parcel send; + Parcel reply; + send.writeFileDescriptor(fd); + const size_t numArgs = args.size(); + send.writeInt32(numArgs); + for (size_t i = 0; i < numArgs; i++) { + send.writeString16(args[i]); + } + status_t err = transact(DUMP_TRANSACTION, send, &reply); + return err; +} + +status_t BpBinder::transact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + // Once a binder has died, it will never come back to life. + if (mAlive) { + status_t status = IPCThreadState::self()->transact( + mHandle, code, data, reply, flags); + if (status == DEAD_OBJECT) mAlive = 0; + return status; + } + + return DEAD_OBJECT; +} + +status_t BpBinder::linkToDeath( + const sp& recipient, void* cookie, uint32_t flags) +{ + Obituary ob; + ob.recipient = recipient; + ob.cookie = cookie; + ob.flags = flags; + + LOG_ALWAYS_FATAL_IF(recipient == NULL, + "linkToDeath(): recipient must be non-NULL"); + + { + AutoMutex _l(mLock); + + if (!mObitsSent) { + if (!mObituaries) { + mObituaries = new Vector; + if (!mObituaries) { + return NO_MEMORY; + } + LOGV("Requesting death notification: %p handle %d\n", this, mHandle); + getWeakRefs()->incWeak(this); + IPCThreadState* self = IPCThreadState::self(); + self->requestDeathNotification(mHandle, this); + self->flushCommands(); + } + ssize_t res = mObituaries->add(ob); + return res >= (ssize_t)NO_ERROR ? (status_t)NO_ERROR : res; + } + } + + return DEAD_OBJECT; +} + +status_t BpBinder::unlinkToDeath( + const wp& recipient, void* cookie, uint32_t flags, + wp* outRecipient) +{ + AutoMutex _l(mLock); + + if (mObitsSent) { + return DEAD_OBJECT; + } + + const size_t N = mObituaries ? mObituaries->size() : 0; + for (size_t i=0; iitemAt(i); + if ((obit.recipient == recipient + || (recipient == NULL && obit.cookie == cookie)) + && obit.flags == flags) { + const uint32_t allFlags = obit.flags|flags; + if (outRecipient != NULL) { + *outRecipient = mObituaries->itemAt(i).recipient; + } + mObituaries->removeAt(i); + if (mObituaries->size() == 0) { + LOGV("Clearing death notification: %p handle %d\n", this, mHandle); + IPCThreadState* self = IPCThreadState::self(); + self->clearDeathNotification(mHandle, this); + self->flushCommands(); + delete mObituaries; + mObituaries = NULL; + } + return NO_ERROR; + } + } + + return NAME_NOT_FOUND; +} + +void BpBinder::sendObituary() +{ + LOGV("Sending obituary for proxy %p handle %d, mObitsSent=%s\n", + this, mHandle, mObitsSent ? "true" : "false"); + + mAlive = 0; + if (mObitsSent) return; + + mLock.lock(); + Vector* obits = mObituaries; + if(obits != NULL) { + LOGV("Clearing sent death notification: %p handle %d\n", this, mHandle); + IPCThreadState* self = IPCThreadState::self(); + self->clearDeathNotification(mHandle, this); + self->flushCommands(); + mObituaries = NULL; + } + mObitsSent = 1; + mLock.unlock(); + + LOGV("Reporting death of proxy %p for %d recipients\n", + this, obits ? obits->size() : 0); + + if (obits != NULL) { + const size_t N = obits->size(); + for (size_t i=0; iitemAt(i)); + } + + delete obits; + } +} + +void BpBinder::reportOneDeath(const Obituary& obit) +{ + sp recipient = obit.recipient.promote(); + LOGV("Reporting death to recipient: %p\n", recipient.get()); + if (recipient == NULL) return; + + recipient->binderDied(this); +} + + +void BpBinder::attachObject( + const void* objectID, void* object, void* cleanupCookie, + object_cleanup_func func) +{ + AutoMutex _l(mLock); + LOGV("Attaching object %p to binder %p (manager=%p)", object, this, &mObjects); + mObjects.attach(objectID, object, cleanupCookie, func); +} + +void* BpBinder::findObject(const void* objectID) const +{ + AutoMutex _l(mLock); + return mObjects.find(objectID); +} + +void BpBinder::detachObject(const void* objectID) +{ + AutoMutex _l(mLock); + mObjects.detach(objectID); +} + +BpBinder* BpBinder::remoteBinder() +{ + return this; +} + +BpBinder::~BpBinder() +{ + LOGV("Destroying BpBinder %p handle %d\n", this, mHandle); + + IPCThreadState* ipc = IPCThreadState::self(); + + mLock.lock(); + Vector* obits = mObituaries; + if(obits != NULL) { + if (ipc) ipc->clearDeathNotification(mHandle, this); + mObituaries = NULL; + } + mLock.unlock(); + + if (obits != NULL) { + // XXX Should we tell any remaining DeathRecipient + // objects that the last strong ref has gone away, so they + // are no longer linked? + delete obits; + } + + if (ipc) { + ipc->expungeHandle(mHandle, this); + ipc->decWeakHandle(mHandle); + } +} + +void BpBinder::onFirstRef() +{ + LOGV("onFirstRef BpBinder %p handle %d\n", this, mHandle); + IPCThreadState* ipc = IPCThreadState::self(); + if (ipc) ipc->incStrongHandle(mHandle); +} + +void BpBinder::onLastStrongRef(const void* id) +{ + LOGV("onLastStrongRef BpBinder %p handle %d\n", this, mHandle); + IF_LOGV() { + printRefs(); + } + IPCThreadState* ipc = IPCThreadState::self(); + if (ipc) ipc->decStrongHandle(mHandle); +} + +bool BpBinder::onIncStrongAttempted(uint32_t flags, const void* id) +{ + LOGV("onIncStrongAttempted BpBinder %p handle %d\n", this, mHandle); + IPCThreadState* ipc = IPCThreadState::self(); + return ipc ? ipc->attemptIncStrongHandle(mHandle) == NO_ERROR : false; +} + +// --------------------------------------------------------------------------- + +}; // namespace android diff --git a/libs/utils/BufferedTextOutput.cpp b/libs/utils/BufferedTextOutput.cpp new file mode 100644 index 000000000..989662e84 --- /dev/null +++ b/libs/utils/BufferedTextOutput.cpp @@ -0,0 +1,279 @@ +/* + * Copyright (C) 2006 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 + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +// --------------------------------------------------------------------------- + +namespace android { + +struct BufferedTextOutput::BufferState : public RefBase +{ + BufferState(int32_t _seq) + : seq(_seq) + , buffer(NULL) + , bufferPos(0) + , bufferSize(0) + , atFront(true) + , indent(0) + , bundle(0) { + } + ~BufferState() { + free(buffer); + } + + status_t append(const char* txt, size_t len) { + if ((len+bufferPos) > bufferSize) { + void* b = realloc(buffer, ((len+bufferPos)*3)/2); + if (!b) return NO_MEMORY; + buffer = (char*)b; + } + memcpy(buffer+bufferPos, txt, len); + bufferPos += len; + return NO_ERROR; + } + + void restart() { + bufferPos = 0; + atFront = true; + if (bufferSize > 256) { + void* b = realloc(buffer, 256); + if (b) { + buffer = (char*)b; + bufferSize = 256; + } + } + } + + const int32_t seq; + char* buffer; + size_t bufferPos; + size_t bufferSize; + bool atFront; + int32_t indent; + int32_t bundle; +}; + +struct BufferedTextOutput::ThreadState +{ + Vector > states; +}; + +static mutex_t gMutex; + +static thread_store_t tls; + +BufferedTextOutput::ThreadState* BufferedTextOutput::getThreadState() +{ + ThreadState* ts = (ThreadState*) thread_store_get( &tls ); + if (ts) return ts; + ts = new ThreadState; + thread_store_set( &tls, ts, threadDestructor ); + return ts; +} + +void BufferedTextOutput::threadDestructor(void *st) +{ + delete ((ThreadState*)st); +} + +static volatile int32_t gSequence = 0; + +static volatile int32_t gFreeBufferIndex = -1; + +static int32_t allocBufferIndex() +{ + int32_t res = -1; + + mutex_lock(&gMutex); + + if (gFreeBufferIndex >= 0) { + res = gFreeBufferIndex; + gFreeBufferIndex = gTextBuffers[res]; + gTextBuffers.editItemAt(res) = -1; + + } else { + res = gTextBuffers.size(); + gTextBuffers.add(-1); + } + + mutex_unlock(&gMutex); + + return res; +} + +static void freeBufferIndex(int32_t idx) +{ + mutex_lock(&gMutex); + gTextBuffers.editItemAt(idx) = gFreeBufferIndex; + gFreeBufferIndex = idx; + mutex_unlock(&gMutex); +} + +// --------------------------------------------------------------------------- + +BufferedTextOutput::BufferedTextOutput(uint32_t flags) + : mFlags(flags) + , mSeq(android_atomic_inc(&gSequence)) + , mIndex(allocBufferIndex()) +{ + mGlobalState = new BufferState(mSeq); + if (mGlobalState) mGlobalState->incStrong(this); +} + +BufferedTextOutput::~BufferedTextOutput() +{ + if (mGlobalState) mGlobalState->decStrong(this); + freeBufferIndex(mIndex); +} + +status_t BufferedTextOutput::print(const char* txt, size_t len) +{ + //printf("BufferedTextOutput: printing %d\n", len); + + AutoMutex _l(mLock); + BufferState* b = getBuffer(); + + const char* const end = txt+len; + + status_t err; + + while (txt < end) { + // Find the next line. + const char* first = txt; + while (txt < end && *txt != '\n') txt++; + + // Include this and all following empty lines. + while (txt < end && *txt == '\n') txt++; + + // Special cases for first data on a line. + if (b->atFront) { + if (b->indent > 0) { + // If this is the start of a line, add the indent. + const char* prefix = stringForIndent(b->indent); + err = b->append(prefix, strlen(prefix)); + if (err != NO_ERROR) return err; + + } else if (*(txt-1) == '\n' && !b->bundle) { + // Fast path: if we are not indenting or bundling, and + // have been given one or more complete lines, just write + // them out without going through the buffer. + + // Slurp up all of the lines. + const char* lastLine = txt+1; + while (txt < end) { + if (*txt++ == '\n') lastLine = txt; + } + struct iovec vec; + vec.iov_base = (void*)first; + vec.iov_len = lastLine-first; + //printf("Writing %d bytes of data!\n", vec.iov_len); + writeLines(vec, 1); + txt = lastLine; + continue; + } + } + + // Append the new text to the buffer. + err = b->append(first, txt-first); + if (err != NO_ERROR) return err; + b->atFront = *(txt-1) == '\n'; + + // If we have finished a line and are not bundling, write + // it out. + //printf("Buffer is now %d bytes\n", b->bufferPos); + if (b->atFront && !b->bundle) { + struct iovec vec; + vec.iov_base = b->buffer; + vec.iov_len = b->bufferPos; + //printf("Writing %d bytes of data!\n", vec.iov_len); + writeLines(vec, 1); + b->restart(); + } + } + + return NO_ERROR; +} + +void BufferedTextOutput::moveIndent(int delta) +{ + AutoMutex _l(mLock); + BufferState* b = getBuffer(); + b->indent += delta; + if (b->indent < 0) b->indent = 0; +} + +void BufferedTextOutput::pushBundle() +{ + AutoMutex _l(mLock); + BufferState* b = getBuffer(); + b->bundle++; +} + +void BufferedTextOutput::popBundle() +{ + AutoMutex _l(mLock); + BufferState* b = getBuffer(); + b->bundle--; + LOG_FATAL_IF(b->bundle < 0, + "TextOutput::popBundle() called more times than pushBundle()"); + if (b->bundle < 0) b->bundle = 0; + + if (b->bundle == 0) { + // Last bundle, write out data if it is complete. If it is not + // complete, don't write until the last line is done... this may + // or may not be the write thing to do, but it's the easiest. + if (b->bufferPos > 0 && b->atFront) { + struct iovec vec; + vec.iov_base = b->buffer; + vec.iov_len = b->bufferPos; + writeLines(vec, 1); + b->restart(); + } + } +} + +BufferedTextOutput::BufferState* BufferedTextOutput::getBuffer() const +{ + if ((mFlags&MULTITHREADED) != 0) { + ThreadState* ts = getThreadState(); + if (ts) { + while (ts->states.size() <= (size_t)mIndex) ts->states.add(NULL); + BufferState* bs = ts->states[mIndex].get(); + if (bs != NULL && bs->seq == mSeq) return bs; + + ts->states.editItemAt(mIndex) = new BufferState(mIndex); + bs = ts->states[mIndex].get(); + if (bs != NULL) return bs; + } + } + + return mGlobalState; +} + +}; // namespace android diff --git a/libs/utils/CallStack.cpp b/libs/utils/CallStack.cpp new file mode 100644 index 000000000..496866673 --- /dev/null +++ b/libs/utils/CallStack.cpp @@ -0,0 +1,335 @@ +/* + * 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. + */ + +#define LOG_TAG "CallStack" + +#include +#include +#include + +#if HAVE_DLADDR +#include +#endif + +#if HAVE_CXXABI +#include +#endif + +#include + +#include +#include +#include +#include + + +/*****************************************************************************/ +namespace android { + + +typedef struct { + size_t count; + size_t ignore; + const void** addrs; +} stack_crawl_state_t; + +static +_Unwind_Reason_Code trace_function(_Unwind_Context *context, void *arg) +{ + stack_crawl_state_t* state = (stack_crawl_state_t*)arg; + if (state->count) { + void* ip = (void*)_Unwind_GetIP(context); + if (ip) { + if (state->ignore) { + state->ignore--; + } else { + state->addrs[0] = ip; + state->addrs++; + state->count--; + } + } + } + return _URC_NO_REASON; +} + +static +int backtrace(const void** addrs, size_t ignore, size_t size) +{ + stack_crawl_state_t state; + state.count = size; + state.ignore = ignore; + state.addrs = addrs; + _Unwind_Backtrace(trace_function, (void*)&state); + return size - state.count; +} + +/*****************************************************************************/ + +static +const char *lookup_symbol(const void* addr, uint32_t *offset, char* name, size_t bufSize) +{ +#if HAVE_DLADDR + Dl_info info; + if (dladdr(addr, &info)) { + *offset = (uint32_t)info.dli_saddr; + return info.dli_sname; + } +#endif + return NULL; +} + +static +int32_t linux_gcc_demangler(const char *mangled_name, char *unmangled_name, size_t buffersize) +{ + size_t out_len = 0; +#if HAVE_CXXABI + int status = 0; + char *demangled = abi::__cxa_demangle(mangled_name, 0, &out_len, &status); + if (status == 0) { + // OK + if (out_len < buffersize) memcpy(unmangled_name, demangled, out_len); + else out_len = 0; + free(demangled); + } else { + out_len = 0; + } +#endif + return out_len; +} + +/*****************************************************************************/ + +class MapInfo { + struct mapinfo { + struct mapinfo *next; + unsigned start; + unsigned end; + char name[]; + }; + + const char *map_to_name(unsigned pc, const char* def) { + mapinfo* mi = getMapInfoList(); + while(mi) { + if ((pc >= mi->start) && (pc < mi->end)) + return mi->name; + mi = mi->next; + } + return def; + } + + mapinfo *parse_maps_line(char *line) { + mapinfo *mi; + int len = strlen(line); + if (len < 1) return 0; + line[--len] = 0; + if (len < 50) return 0; + if (line[20] != 'x') return 0; + mi = (mapinfo*)malloc(sizeof(mapinfo) + (len - 47)); + if (mi == 0) return 0; + mi->start = strtoul(line, 0, 16); + mi->end = strtoul(line + 9, 0, 16); + mi->next = 0; + strcpy(mi->name, line + 49); + return mi; + } + + mapinfo* getMapInfoList() { + Mutex::Autolock _l(mLock); + if (milist == 0) { + char data[1024]; + FILE *fp; + sprintf(data, "/proc/%d/maps", getpid()); + fp = fopen(data, "r"); + if (fp) { + while(fgets(data, 1024, fp)) { + mapinfo *mi = parse_maps_line(data); + if(mi) { + mi->next = milist; + milist = mi; + } + } + fclose(fp); + } + } + return milist; + } + mapinfo* milist; + Mutex mLock; + static MapInfo sMapInfo; + +public: + MapInfo() + : milist(0) { + } + + ~MapInfo() { + while (milist) { + mapinfo *next = milist->next; + free(milist); + milist = next; + } + } + + static const char *mapAddressToName(const void* pc, const char* def) { + return sMapInfo.map_to_name((unsigned)pc, def); + } + +}; + +/*****************************************************************************/ + +MapInfo MapInfo::sMapInfo; + +/*****************************************************************************/ + +CallStack::CallStack() + : mCount(0) +{ +} + +CallStack::CallStack(const CallStack& rhs) + : mCount(rhs.mCount) +{ + if (mCount) { + memcpy(mStack, rhs.mStack, mCount*sizeof(void*)); + } +} + +CallStack::~CallStack() +{ +} + +CallStack& CallStack::operator = (const CallStack& rhs) +{ + mCount = rhs.mCount; + if (mCount) { + memcpy(mStack, rhs.mStack, mCount*sizeof(void*)); + } + return *this; +} + +bool CallStack::operator == (const CallStack& rhs) const { + if (mCount != rhs.mCount) + return false; + return !mCount || (memcmp(mStack, rhs.mStack, mCount*sizeof(void*)) == 0); +} + +bool CallStack::operator != (const CallStack& rhs) const { + return !operator == (rhs); +} + +bool CallStack::operator < (const CallStack& rhs) const { + if (mCount != rhs.mCount) + return mCount < rhs.mCount; + return memcmp(mStack, rhs.mStack, mCount*sizeof(void*)) < 0; +} + +bool CallStack::operator >= (const CallStack& rhs) const { + return !operator < (rhs); +} + +bool CallStack::operator > (const CallStack& rhs) const { + if (mCount != rhs.mCount) + return mCount > rhs.mCount; + return memcmp(mStack, rhs.mStack, mCount*sizeof(void*)) > 0; +} + +bool CallStack::operator <= (const CallStack& rhs) const { + return !operator > (rhs); +} + +const void* CallStack::operator [] (int index) const { + if (index >= int(mCount)) + return 0; + return mStack[index]; +} + + +void CallStack::clear() +{ + mCount = 0; +} + +void CallStack::update(int32_t ignoreDepth, int32_t maxDepth) +{ + if (maxDepth > MAX_DEPTH) + maxDepth = MAX_DEPTH; + mCount = backtrace(mStack, ignoreDepth, maxDepth); +} + +// Return the stack frame name on the designated level +String8 CallStack::toStringSingleLevel(const char* prefix, int32_t level) const +{ + String8 res; + char namebuf[1024]; + char tmp[256]; + char tmp1[32]; + char tmp2[32]; + uint32_t offs; + + const void* ip = mStack[level]; + if (!ip) return res; + + if (prefix) res.append(prefix); + snprintf(tmp1, 32, "#%02d ", level); + res.append(tmp1); + + const char* name = lookup_symbol(ip, &offs, namebuf, sizeof(namebuf)); + if (name) { + if (linux_gcc_demangler(name, tmp, 256) != 0) + name = tmp; + snprintf(tmp1, 32, "0x%08x: <", (size_t)ip); + snprintf(tmp2, 32, ">+0x%08x", offs); + res.append(tmp1); + res.append(name); + res.append(tmp2); + } else { + name = MapInfo::mapAddressToName(ip, ""); + snprintf(tmp, 256, "pc %08x %s", (size_t)ip, name); + res.append(tmp); + } + res.append("\n"); + + return res; +} + +// Dump a stack trace to the log +void CallStack::dump(const char* prefix) const +{ + /* + * Sending a single long log may be truncated since the stack levels can + * get very deep. So we request function names of each frame individually. + */ + for (int i=0; i + +#include + +#include +#include +#include + +namespace android { + +// --------------------------------------------------------------------- + +static const char indentStr[] = +" " +" "; + +const char* stringForIndent(int32_t indentLevel) +{ + ssize_t off = sizeof(indentStr)-1-(indentLevel*2); + return indentStr + (off < 0 ? 0 : off); +} + +// --------------------------------------------------------------------- + +static void defaultPrintFunc(void* cookie, const char* txt) +{ + printf("%s", txt); +} + +// --------------------------------------------------------------------- + +static inline int isident(int c) +{ + return isalnum(c) || c == '_'; +} + +static inline bool isasciitype(char c) +{ + if( c >= ' ' && c < 127 && c != '\'' && c != '\\' ) return true; + return false; +} + +static inline char makehexdigit(uint32_t val) +{ + return "0123456789abcdef"[val&0xF]; +} + +static char* appendhexnum(uint32_t val, char* out) +{ + for( int32_t i=28; i>=0; i-=4 ) { + *out++ = makehexdigit( val>>i ); + } + *out = 0; + return out; +} + +static inline char makeupperhexdigit(uint32_t val) +{ + return "0123456789ABCDEF"[val&0xF]; +} + +static char* appendupperhexnum(uint32_t val, char* out) +{ + for( int32_t i=28; i>=0; i-=4 ) { + *out++ = makeupperhexdigit( val>>i ); + } + *out = 0; + return out; +} + +static char* appendcharornum(char c, char* out, bool skipzero = true) +{ + if (skipzero && c == 0) return out; + + if (isasciitype(c)) { + *out++ = c; + return out; + } + + *out++ = '\\'; + *out++ = 'x'; + *out++ = makehexdigit(c>>4); + *out++ = makehexdigit(c); + return out; +} + +static char* typetostring(uint32_t type, char* out, + bool fullContext = true, + bool strict = false) +{ + char* pos = out; + char c[4]; + c[0] = (char)((type>>24)&0xFF); + c[1] = (char)((type>>16)&0xFF); + c[2] = (char)((type>>8)&0xFF); + c[3] = (char)(type&0xFF); + bool valid; + if( !strict ) { + // now even less strict! + // valid = isasciitype(c[3]); + valid = true; + int32_t i = 0; + bool zero = true; + while (valid && i<3) { + if (c[i] == 0) { + if (!zero) valid = false; + } else { + zero = false; + //if (!isasciitype(c[i])) valid = false; + } + i++; + } + // if all zeros, not a valid type code. + if (zero) valid = false; + } else { + valid = isident(c[3]) ? true : false; + int32_t i = 0; + bool zero = true; + while (valid && i<3) { + if (c[i] == 0) { + if (!zero) valid = false; + } else { + zero = false; + if (!isident(c[i])) valid = false; + } + i++; + } + } + if( valid && (!fullContext || c[0] != '0' || c[1] != 'x') ) { + if( fullContext ) *pos++ = '\''; + pos = appendcharornum(c[0], pos); + pos = appendcharornum(c[1], pos); + pos = appendcharornum(c[2], pos); + pos = appendcharornum(c[3], pos); + if( fullContext ) *pos++ = '\''; + *pos = 0; + return pos; + } + + if( fullContext ) { + *pos++ = '0'; + *pos++ = 'x'; + } + return appendhexnum(type, pos); +} + +void printTypeCode(uint32_t typeCode, debugPrintFunc func, void* cookie) +{ + char buffer[32]; + char* end = typetostring(typeCode, buffer); + *end = 0; + func ? (*func)(cookie, buffer) : defaultPrintFunc(cookie, buffer); +} + +void printHexData(int32_t indent, const void *buf, size_t length, + size_t bytesPerLine, int32_t singleLineBytesCutoff, + size_t alignment, bool cStyle, + debugPrintFunc func, void* cookie) +{ + if (alignment == 0) { + if (bytesPerLine >= 16) alignment = 4; + else if (bytesPerLine >= 8) alignment = 2; + else alignment = 1; + } + if (func == NULL) func = defaultPrintFunc; + + size_t offset; + + unsigned char *pos = (unsigned char *)buf; + + if (pos == NULL) { + if (singleLineBytesCutoff < 0) func(cookie, "\n"); + func(cookie, "(NULL)"); + return; + } + + if (length == 0) { + if (singleLineBytesCutoff < 0) func(cookie, "\n"); + func(cookie, "(empty)"); + return; + } + + if ((int32_t)length < 0) { + if (singleLineBytesCutoff < 0) func(cookie, "\n"); + char buf[64]; + sprintf(buf, "(bad length: %d)", length); + func(cookie, buf); + return; + } + + char buffer[256]; + static const size_t maxBytesPerLine = (sizeof(buffer)-1-11-4)/(3+1); + + if (bytesPerLine > maxBytesPerLine) bytesPerLine = maxBytesPerLine; + + const bool oneLine = (int32_t)length <= singleLineBytesCutoff; + bool newLine = false; + if (cStyle) { + indent++; + func(cookie, "{\n"); + newLine = true; + } else if (!oneLine) { + func(cookie, "\n"); + newLine = true; + } + + for (offset = 0; ; offset += bytesPerLine, pos += bytesPerLine) { + long remain = length; + + char* c = buffer; + if (!oneLine && !cStyle) { + sprintf(c, "0x%08x: ", (int)offset); + c += 12; + } + + size_t index; + size_t word; + + for (word = 0; word < bytesPerLine; ) { + +#ifdef HAVE_LITTLE_ENDIAN + const size_t startIndex = word+(alignment-(alignment?1:0)); + const ssize_t dir = -1; +#else + const size_t startIndex = word; + const ssize_t dir = 1; +#endif + + for (index = 0; index < alignment || (alignment == 0 && index < bytesPerLine); index++) { + + if (!cStyle) { + if (index == 0 && word > 0 && alignment > 0) { + *c++ = ' '; + } + + if (remain-- > 0) { + const unsigned char val = *(pos+startIndex+(index*dir)); + *c++ = makehexdigit(val>>4); + *c++ = makehexdigit(val); + } else if (!oneLine) { + *c++ = ' '; + *c++ = ' '; + } + } else { + if (remain > 0) { + if (index == 0 && word > 0) { + *c++ = ','; + *c++ = ' '; + } + if (index == 0) { + *c++ = '0'; + *c++ = 'x'; + } + const unsigned char val = *(pos+startIndex+(index*dir)); + *c++ = makehexdigit(val>>4); + *c++ = makehexdigit(val); + remain--; + } + } + } + + word += index; + } + + if (!cStyle) { + remain = length; + *c++ = ' '; + *c++ = '\''; + for (index = 0; index < bytesPerLine; index++) { + + if (remain-- > 0) { + const unsigned char val = pos[index]; + *c++ = (val >= ' ' && val < 127) ? val : '.'; + } else if (!oneLine) { + *c++ = ' '; + } + } + + *c++ = '\''; + if (length > bytesPerLine) *c++ = '\n'; + } else { + if (remain > 0) *c++ = ','; + *c++ = '\n'; + } + + if (newLine && indent) func(cookie, stringForIndent(indent)); + *c = 0; + func(cookie, buffer); + newLine = true; + + if (length <= bytesPerLine) break; + length -= bytesPerLine; + } + + if (cStyle) { + if (indent > 0) func(cookie, stringForIndent(indent-1)); + func(cookie, "};"); + } +} + +}; // namespace android + diff --git a/libs/utils/FileMap.cpp b/libs/utils/FileMap.cpp new file mode 100644 index 000000000..e1ba9b238 --- /dev/null +++ b/libs/utils/FileMap.cpp @@ -0,0 +1,222 @@ +/* + * Copyright (C) 2006 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. + */ + +// +// Shared file mapping class. +// + +#define LOG_TAG "filemap" + +#include +#include + +#include +#include + +#ifdef HAVE_POSIX_FILEMAP +#include +#endif + +#include +#include +#include +#include + +using namespace android; + +/*static*/ long FileMap::mPageSize = -1; + + +/* + * Constructor. Create an empty object. + */ +FileMap::FileMap(void) + : mRefCount(1), mFileName(NULL), mBasePtr(NULL), mBaseLength(0), + mDataPtr(NULL), mDataLength(0) +{ +} + +/* + * Destructor. + */ +FileMap::~FileMap(void) +{ + assert(mRefCount == 0); + + //printf("+++ removing FileMap %p %u\n", mDataPtr, mDataLength); + + mRefCount = -100; // help catch double-free + if (mFileName != NULL) { + free(mFileName); + } +#ifdef HAVE_POSIX_FILEMAP + if (munmap(mBasePtr, mBaseLength) != 0) { + LOGD("munmap(%p, %d) failed\n", mBasePtr, (int) mBaseLength); + } +#endif +#ifdef HAVE_WIN32_FILEMAP + if ( UnmapViewOfFile(mBasePtr) == 0) { + LOGD("UnmapViewOfFile(%p) failed, error = %ld\n", mBasePtr, + GetLastError() ); + } + CloseHandle(mFileMapping); + CloseHandle(mFileHandle); +#endif +} + + +/* + * Create a new mapping on an open file. + * + * Closing the file descriptor does not unmap the pages, so we don't + * claim ownership of the fd. + * + * Returns "false" on failure. + */ +bool FileMap::create(const char* origFileName, int fd, off_t offset, size_t length, bool readOnly) +{ +#ifdef HAVE_WIN32_FILEMAP + int adjust; + off_t adjOffset; + size_t adjLength; + + if (mPageSize == -1) { + SYSTEM_INFO si; + + GetSystemInfo( &si ); + mPageSize = si.dwAllocationGranularity; + } + + DWORD protect = readOnly ? PAGE_READONLY : PAGE_READWRITE; + + mFileHandle = (HANDLE) _get_osfhandle(fd); + mFileMapping = CreateFileMapping( mFileHandle, NULL, protect, 0, 0, NULL); + if (mFileMapping == NULL) { + LOGE("CreateFileMapping(%p, %lx) failed with error %ld\n", + mFileHandle, protect, GetLastError() ); + return false; + } + + adjust = offset % mPageSize; + adjOffset = offset - adjust; + adjLength = length + adjust; + + mBasePtr = MapViewOfFile( mFileMapping, + readOnly ? FILE_MAP_READ : FILE_MAP_ALL_ACCESS, + 0, + (DWORD)(adjOffset), + adjLength ); + if (mBasePtr == NULL) { + LOGE("MapViewOfFile(%ld, %ld) failed with error %ld\n", + adjOffset, adjLength, GetLastError() ); + CloseHandle(mFileMapping); + mFileMapping = INVALID_HANDLE_VALUE; + return false; + } +#endif +#ifdef HAVE_POSIX_FILEMAP + int prot, flags, adjust; + off_t adjOffset; + size_t adjLength; + + void* ptr; + + assert(mRefCount == 1); + assert(fd >= 0); + assert(offset >= 0); + assert(length > 0); + + /* init on first use */ + if (mPageSize == -1) { +#if NOT_USING_KLIBC + mPageSize = sysconf(_SC_PAGESIZE); + if (mPageSize == -1) { + LOGE("could not get _SC_PAGESIZE\n"); + return false; + } +#else + /* this holds for Linux, Darwin, Cygwin, and doesn't pain the ARM */ + mPageSize = 4096; +#endif + } + + adjust = offset % mPageSize; +try_again: + adjOffset = offset - adjust; + adjLength = length + adjust; + + flags = MAP_SHARED; + prot = PROT_READ; + if (!readOnly) + prot |= PROT_WRITE; + + ptr = mmap(NULL, adjLength, prot, flags, fd, adjOffset); + if (ptr == MAP_FAILED) { + // Cygwin does not seem to like file mapping files from an offset. + // So if we fail, try again with offset zero + if (adjOffset > 0) { + adjust = offset; + goto try_again; + } + + LOGE("mmap(%ld,%ld) failed: %s\n", + (long) adjOffset, (long) adjLength, strerror(errno)); + return false; + } + mBasePtr = ptr; +#endif /* HAVE_POSIX_FILEMAP */ + + mFileName = origFileName != NULL ? strdup(origFileName) : NULL; + mBaseLength = adjLength; + mDataOffset = offset; + mDataPtr = (char*) mBasePtr + adjust; + mDataLength = length; + + assert(mBasePtr != NULL); + + LOGV("MAP: base %p/%d data %p/%d\n", + mBasePtr, (int) mBaseLength, mDataPtr, (int) mDataLength); + + return true; +} + +/* + * Provide guidance to the system. + */ +int FileMap::advise(MapAdvice advice) +{ +#if HAVE_MADVISE + int cc, sysAdvice; + + switch (advice) { + case NORMAL: sysAdvice = MADV_NORMAL; break; + case RANDOM: sysAdvice = MADV_RANDOM; break; + case SEQUENTIAL: sysAdvice = MADV_SEQUENTIAL; break; + case WILLNEED: sysAdvice = MADV_WILLNEED; break; + case DONTNEED: sysAdvice = MADV_DONTNEED; break; + default: + assert(false); + return -1; + } + + cc = madvise(mBasePtr, mBaseLength, sysAdvice); + if (cc != 0) + LOGW("madvise(%d) failed: %s\n", sysAdvice, strerror(errno)); + return cc; +#else + return -1; +#endif // HAVE_MADVISE +} diff --git a/libs/utils/IDataConnection.cpp b/libs/utils/IDataConnection.cpp new file mode 100644 index 000000000..c6d49aa48 --- /dev/null +++ b/libs/utils/IDataConnection.cpp @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2006 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 +#include + +#include + +#include + +namespace android { + +// --------------------------------------------------------------------------- + +enum +{ + CONNECT_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION, + DISCONNECT_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION + 1 +}; + +class BpDataConnection : public BpInterface +{ +public: + BpDataConnection::BpDataConnection(const sp& impl) + : BpInterface(impl) + { + } + + virtual void connect() + { + Parcel data, reply; + data.writeInterfaceToken(IDataConnection::descriptor()); + remote()->transact(CONNECT_TRANSACTION, data, &reply); + } + + virtual void disconnect() + { + Parcel data, reply; + remote()->transact(DISCONNECT_TRANSACTION, data, &reply); + } +}; + +IMPLEMENT_META_INTERFACE(DataConnection, "android.utils.IDataConnection"); + +#define CHECK_INTERFACE(interface, data, reply) \ + do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ + LOGW("Call incorrectly routed to " #interface); \ + return PERMISSION_DENIED; \ + } } while (0) + +status_t BnDataConnection::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch(code) + { + case CONNECT_TRANSACTION: + { + CHECK_INTERFACE(IDataConnection, data, reply); + connect(); + return NO_ERROR; + } + + case DISCONNECT_TRANSACTION: + { + CHECK_INTERFACE(IDataConnection, data, reply); + disconnect(); + return NO_ERROR; + } + + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +// ---------------------------------------------------------------------------- + +}; // namespace android diff --git a/libs/utils/IInterface.cpp b/libs/utils/IInterface.cpp new file mode 100644 index 000000000..6ea817887 --- /dev/null +++ b/libs/utils/IInterface.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2005 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 + +namespace android { + +// --------------------------------------------------------------------------- + +sp IInterface::asBinder() +{ + return this ? onAsBinder() : NULL; +} + +sp IInterface::asBinder() const +{ + return this ? const_cast(this)->onAsBinder() : NULL; +} + +// --------------------------------------------------------------------------- + +}; // namespace android diff --git a/libs/utils/IMemory.cpp b/libs/utils/IMemory.cpp new file mode 100644 index 000000000..429bc2b94 --- /dev/null +++ b/libs/utils/IMemory.cpp @@ -0,0 +1,486 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "IMemory" + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#define VERBOSE 0 + +namespace android { +// --------------------------------------------------------------------------- + +class HeapCache : public IBinder::DeathRecipient +{ +public: + HeapCache(); + virtual ~HeapCache(); + + virtual void binderDied(const wp& who); + + sp find_heap(const sp& binder); + void pin_heap(const sp& binder); + void free_heap(const sp& binder); + sp get_heap(const sp& binder); + void dump_heaps(); + +private: + // For IMemory.cpp + struct heap_info_t { + sp heap; + int32_t count; + }; + + void free_heap(const wp& binder); + + Mutex mHeapCacheLock; + KeyedVector< wp, heap_info_t > mHeapCache; +}; + +static sp gHeapCache = new HeapCache(); + +/******************************************************************************/ + +enum { + HEAP_ID = IBinder::FIRST_CALL_TRANSACTION +}; + +class BpMemoryHeap : public BpInterface +{ +public: + BpMemoryHeap(const sp& impl); + virtual ~BpMemoryHeap(); + + virtual int getHeapID() const; + virtual void* getBase() const; + virtual size_t getSize() const; + virtual uint32_t getFlags() const; + +private: + friend class IMemory; + friend class HeapCache; + + // for debugging in this module + static inline sp find_heap(const sp& binder) { + return gHeapCache->find_heap(binder); + } + static inline void free_heap(const sp& binder) { + gHeapCache->free_heap(binder); + } + static inline sp get_heap(const sp& binder) { + return gHeapCache->get_heap(binder); + } + static inline void dump_heaps() { + gHeapCache->dump_heaps(); + } + void inline pin_heap() const { + gHeapCache->pin_heap(const_cast(this)->asBinder()); + } + + void assertMapped() const; + void assertReallyMapped() const; + void pinHeap() const; + + mutable volatile int32_t mHeapId; + mutable void* mBase; + mutable size_t mSize; + mutable uint32_t mFlags; + mutable bool mRealHeap; + mutable Mutex mLock; +}; + +// ---------------------------------------------------------------------------- + +enum { + GET_MEMORY = IBinder::FIRST_CALL_TRANSACTION +}; + +class BpMemory : public BpInterface +{ +public: + BpMemory(const sp& impl); + virtual ~BpMemory(); + virtual sp getMemory(ssize_t* offset=0, size_t* size=0) const; + +private: + mutable sp mHeap; + mutable ssize_t mOffset; + mutable size_t mSize; +}; + +/******************************************************************************/ + +void* IMemory::fastPointer(const sp& binder, ssize_t offset) const +{ + sp realHeap = BpMemoryHeap::get_heap(binder); + void* const base = realHeap->base(); + if (base == MAP_FAILED) + return 0; + return static_cast(base) + offset; +} + +void* IMemory::pointer() const { + ssize_t offset; + sp heap = getMemory(&offset); + void* const base = heap!=0 ? heap->base() : MAP_FAILED; + if (base == MAP_FAILED) + return 0; + return static_cast(base) + offset; +} + +size_t IMemory::size() const { + size_t size; + getMemory(NULL, &size); + return size; +} + +ssize_t IMemory::offset() const { + ssize_t offset; + getMemory(&offset); + return offset; +} + +/******************************************************************************/ + +BpMemory::BpMemory(const sp& impl) + : BpInterface(impl), mOffset(0), mSize(0) +{ +} + +BpMemory::~BpMemory() +{ +} + +sp BpMemory::getMemory(ssize_t* offset, size_t* size) const +{ + if (mHeap == 0) { + Parcel data, reply; + data.writeInterfaceToken(IMemory::getInterfaceDescriptor()); + if (remote()->transact(GET_MEMORY, data, &reply) == NO_ERROR) { + sp heap = reply.readStrongBinder(); + ssize_t o = reply.readInt32(); + size_t s = reply.readInt32(); + if (heap != 0) { + mHeap = interface_cast(heap); + if (mHeap != 0) { + mOffset = o; + mSize = s; + } + } + } + } + if (offset) *offset = mOffset; + if (size) *size = mSize; + return mHeap; +} + +// --------------------------------------------------------------------------- + +IMPLEMENT_META_INTERFACE(Memory, "android.utils.IMemory"); + +#define CHECK_INTERFACE(interface, data, reply) \ + do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ + LOGW("Call incorrectly routed to " #interface); \ + return PERMISSION_DENIED; \ + } } while (0) + +status_t BnMemory::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch(code) { + case GET_MEMORY: { + CHECK_INTERFACE(IMemory, data, reply); + ssize_t offset; + size_t size; + reply->writeStrongBinder( getMemory(&offset, &size)->asBinder() ); + reply->writeInt32(offset); + reply->writeInt32(size); + return NO_ERROR; + } break; + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + + +/******************************************************************************/ + +BpMemoryHeap::BpMemoryHeap(const sp& impl) + : BpInterface(impl), + mHeapId(-1), mBase(MAP_FAILED), mSize(0), mFlags(0), mRealHeap(false) +{ +} + +BpMemoryHeap::~BpMemoryHeap() { + if (mHeapId != -1) { + close(mHeapId); + if (mRealHeap) { + // by construction we're the last one + if (mBase != MAP_FAILED) { + sp binder = const_cast(this)->asBinder(); + + if (VERBOSE) { + LOGD("UNMAPPING binder=%p, heap=%p, size=%d, fd=%d", + binder.get(), this, mSize, mHeapId); + CallStack stack; + stack.update(); + stack.dump("callstack"); + } + + munmap(mBase, mSize); + } + } else { + // remove from list only if it was mapped before + sp binder = const_cast(this)->asBinder(); + free_heap(binder); + } + } +} + +void BpMemoryHeap::assertMapped() const +{ + if (mHeapId == -1) { + sp binder(const_cast(this)->asBinder()); + sp heap(static_cast(find_heap(binder).get())); + heap->assertReallyMapped(); + if (heap->mBase != MAP_FAILED) { + Mutex::Autolock _l(mLock); + if (mHeapId == -1) { + mBase = heap->mBase; + mSize = heap->mSize; + android_atomic_write( dup( heap->mHeapId ), &mHeapId ); + } + } else { + // something went wrong + free_heap(binder); + } + } +} + +void BpMemoryHeap::assertReallyMapped() const +{ + if (mHeapId == -1) { + + // remote call without mLock held, worse case scenario, we end up + // calling transact() from multiple threads, but that's not a problem, + // only mmap below must be in the critical section. + + Parcel data, reply; + data.writeInterfaceToken(IMemoryHeap::getInterfaceDescriptor()); + status_t err = remote()->transact(HEAP_ID, data, &reply); + int parcel_fd = reply.readFileDescriptor(); + ssize_t size = reply.readInt32(); + uint32_t flags = reply.readInt32(); + + LOGE_IF(err, "binder=%p transaction failed fd=%d, size=%d, err=%d (%s)", + asBinder().get(), parcel_fd, size, err, strerror(-err)); + + int fd = dup( parcel_fd ); + LOGE_IF(fd==-1, "cannot dup fd=%d, size=%d, err=%d (%s)", + parcel_fd, size, err, strerror(errno)); + + int access = PROT_READ; + if (!(flags & READ_ONLY)) { + access |= PROT_WRITE; + } + + Mutex::Autolock _l(mLock); + if (mHeapId == -1) { + mRealHeap = true; + mBase = mmap(0, size, access, MAP_SHARED, fd, 0); + if (mBase == MAP_FAILED) { + LOGE("cannot map BpMemoryHeap (binder=%p), size=%d, fd=%d (%s)", + asBinder().get(), size, fd, strerror(errno)); + close(fd); + } else { + if (flags & MAP_ONCE) { + //LOGD("pinning heap (binder=%p, size=%d, fd=%d", + // asBinder().get(), size, fd); + pin_heap(); + } + mSize = size; + mFlags = flags; + android_atomic_write(fd, &mHeapId); + } + } + } +} + +int BpMemoryHeap::getHeapID() const { + assertMapped(); + return mHeapId; +} + +void* BpMemoryHeap::getBase() const { + assertMapped(); + return mBase; +} + +size_t BpMemoryHeap::getSize() const { + assertMapped(); + return mSize; +} + +uint32_t BpMemoryHeap::getFlags() const { + assertMapped(); + return mFlags; +} + +// --------------------------------------------------------------------------- + +IMPLEMENT_META_INTERFACE(MemoryHeap, "android.utils.IMemoryHeap"); + +status_t BnMemoryHeap::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch(code) { + case HEAP_ID: { + CHECK_INTERFACE(IMemoryHeap, data, reply); + reply->writeFileDescriptor(getHeapID()); + reply->writeInt32(getSize()); + reply->writeInt32(getFlags()); + return NO_ERROR; + } break; + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +/*****************************************************************************/ + +HeapCache::HeapCache() + : DeathRecipient() +{ +} + +HeapCache::~HeapCache() +{ +} + +void HeapCache::binderDied(const wp& binder) +{ + //LOGD("binderDied binder=%p", binder.unsafe_get()); + free_heap(binder); +} + +sp HeapCache::find_heap(const sp& binder) +{ + Mutex::Autolock _l(mHeapCacheLock); + ssize_t i = mHeapCache.indexOfKey(binder); + if (i>=0) { + heap_info_t& info = mHeapCache.editValueAt(i); + LOGD_IF(VERBOSE, + "found binder=%p, heap=%p, size=%d, fd=%d, count=%d", + binder.get(), info.heap.get(), + static_cast(info.heap.get())->mSize, + static_cast(info.heap.get())->mHeapId, + info.count); + android_atomic_inc(&info.count); + return info.heap; + } else { + heap_info_t info; + info.heap = interface_cast(binder); + info.count = 1; + //LOGD("adding binder=%p, heap=%p, count=%d", + // binder.get(), info.heap.get(), info.count); + mHeapCache.add(binder, info); + return info.heap; + } +} + +void HeapCache::pin_heap(const sp& binder) +{ + Mutex::Autolock _l(mHeapCacheLock); + ssize_t i = mHeapCache.indexOfKey(binder); + if (i>=0) { + heap_info_t& info(mHeapCache.editValueAt(i)); + android_atomic_inc(&info.count); + binder->linkToDeath(this); + } else { + LOGE("pin_heap binder=%p not found!!!", binder.get()); + } +} + +void HeapCache::free_heap(const sp& binder) { + free_heap( wp(binder) ); +} + +void HeapCache::free_heap(const wp& binder) +{ + sp rel; + { + Mutex::Autolock _l(mHeapCacheLock); + ssize_t i = mHeapCache.indexOfKey(binder); + if (i>=0) { + heap_info_t& info(mHeapCache.editValueAt(i)); + int32_t c = android_atomic_dec(&info.count); + if (c == 1) { + LOGD_IF(VERBOSE, + "removing binder=%p, heap=%p, size=%d, fd=%d, count=%d", + binder.unsafe_get(), info.heap.get(), + static_cast(info.heap.get())->mSize, + static_cast(info.heap.get())->mHeapId, + info.count); + rel = mHeapCache.valueAt(i).heap; + mHeapCache.removeItemsAt(i); + } + } else { + LOGE("free_heap binder=%p not found!!!", binder.unsafe_get()); + } + } +} + +sp HeapCache::get_heap(const sp& binder) +{ + sp realHeap; + Mutex::Autolock _l(mHeapCacheLock); + ssize_t i = mHeapCache.indexOfKey(binder); + if (i>=0) realHeap = mHeapCache.valueAt(i).heap; + else realHeap = interface_cast(binder); + return realHeap; +} + +void HeapCache::dump_heaps() +{ + Mutex::Autolock _l(mHeapCacheLock); + int c = mHeapCache.size(); + for (int i=0 ; i(info.heap.get())); + LOGD("hey=%p, heap=%p, count=%d, (fd=%d, base=%p, size=%d)", + mHeapCache.keyAt(i).unsafe_get(), + info.heap.get(), info.count, + h->mHeapId, h->mBase, h->mSize); + } +} + + +// --------------------------------------------------------------------------- +}; // namespace android diff --git a/libs/utils/IPCThreadState.cpp b/libs/utils/IPCThreadState.cpp new file mode 100644 index 000000000..ca49d9a5b --- /dev/null +++ b/libs/utils/IPCThreadState.cpp @@ -0,0 +1,1007 @@ +/* + * Copyright (C) 2005 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 + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#ifdef HAVE_PTHREADS +#include +#include +#include +#endif +#ifdef HAVE_WIN32_THREADS +#include +#endif + + +#if LOG_NDEBUG + +#define IF_LOG_TRANSACTIONS() if (false) +#define IF_LOG_COMMANDS() if (false) +#define LOG_REMOTEREFS(...) +#define IF_LOG_REMOTEREFS() if (false) +#define LOG_THREADPOOL(...) +#define LOG_ONEWAY(...) + +#else + +#define IF_LOG_TRANSACTIONS() IF_LOG(LOG_VERBOSE, "transact") +#define IF_LOG_COMMANDS() IF_LOG(LOG_VERBOSE, "ipc") +#define LOG_REMOTEREFS(...) LOG(LOG_DEBUG, "remoterefs", __VA_ARGS__) +#define IF_LOG_REMOTEREFS() IF_LOG(LOG_DEBUG, "remoterefs") +#define LOG_THREADPOOL(...) LOG(LOG_DEBUG, "threadpool", __VA_ARGS__) +#define LOG_ONEWAY(...) LOG(LOG_DEBUG, "ipc", __VA_ARGS__) + +#endif + +// --------------------------------------------------------------------------- + +namespace android { + +static const char* getReturnString(size_t idx); +static const char* getCommandString(size_t idx); +static const void* printReturnCommand(TextOutput& out, const void* _cmd); +static const void* printCommand(TextOutput& out, const void* _cmd); + +// This will result in a missing symbol failure if the IF_LOG_COMMANDS() +// conditionals don't get stripped... but that is probably what we want. +#if !LOG_NDEBUG +static const char *kReturnStrings[] = { +#if 1 /* TODO: error update strings */ + "unknown", +#else + "BR_OK", + "BR_TIMEOUT", + "BR_WAKEUP", + "BR_TRANSACTION", + "BR_REPLY", + "BR_ACQUIRE_RESULT", + "BR_DEAD_REPLY", + "BR_TRANSACTION_COMPLETE", + "BR_INCREFS", + "BR_ACQUIRE", + "BR_RELEASE", + "BR_DECREFS", + "BR_ATTEMPT_ACQUIRE", + "BR_EVENT_OCCURRED", + "BR_NOOP", + "BR_SPAWN_LOOPER", + "BR_FINISHED", + "BR_DEAD_BINDER", + "BR_CLEAR_DEATH_NOTIFICATION_DONE" +#endif +}; + +static const char *kCommandStrings[] = { +#if 1 /* TODO: error update strings */ + "unknown", +#else + "BC_NOOP", + "BC_TRANSACTION", + "BC_REPLY", + "BC_ACQUIRE_RESULT", + "BC_FREE_BUFFER", + "BC_TRANSACTION_COMPLETE", + "BC_INCREFS", + "BC_ACQUIRE", + "BC_RELEASE", + "BC_DECREFS", + "BC_INCREFS_DONE", + "BC_ACQUIRE_DONE", + "BC_ATTEMPT_ACQUIRE", + "BC_RETRIEVE_ROOT_OBJECT", + "BC_SET_THREAD_ENTRY", + "BC_REGISTER_LOOPER", + "BC_ENTER_LOOPER", + "BC_EXIT_LOOPER", + "BC_SYNC", + "BC_STOP_PROCESS", + "BC_STOP_SELF", + "BC_REQUEST_DEATH_NOTIFICATION", + "BC_CLEAR_DEATH_NOTIFICATION", + "BC_DEAD_BINDER_DONE" +#endif +}; + +static const char* getReturnString(size_t idx) +{ + if (idx < sizeof(kReturnStrings) / sizeof(kReturnStrings[0])) + return kReturnStrings[idx]; + else + return "unknown"; +} + +static const char* getCommandString(size_t idx) +{ + if (idx < sizeof(kCommandStrings) / sizeof(kCommandStrings[0])) + return kCommandStrings[idx]; + else + return "unknown"; +} + +static const void* printBinderTransactionData(TextOutput& out, const void* data) +{ + const binder_transaction_data* btd = + (const binder_transaction_data*)data; + out << "target=" << btd->target.ptr << " (cookie " << btd->cookie << ")" << endl + << "code=" << TypeCode(btd->code) << ", flags=" << (void*)btd->flags << endl + << "data=" << btd->data.ptr.buffer << " (" << (void*)btd->data_size + << " bytes)" << endl + << "offsets=" << btd->data.ptr.offsets << " (" << (void*)btd->offsets_size + << " bytes)" << endl; + return btd+1; +} + +static const void* printReturnCommand(TextOutput& out, const void* _cmd) +{ + static const int32_t N = sizeof(kReturnStrings)/sizeof(kReturnStrings[0]); + + const int32_t* cmd = (const int32_t*)_cmd; + int32_t code = *cmd++; + if (code == BR_ERROR) { + out << "BR_ERROR: " << (void*)(*cmd++) << endl; + return cmd; + } else if (code < 0 || code >= N) { + out << "Unknown reply: " << code << endl; + return cmd; + } + + out << kReturnStrings[code]; + switch (code) { + case BR_TRANSACTION: + case BR_REPLY: { + out << ": " << indent; + cmd = (const int32_t *)printBinderTransactionData(out, cmd); + out << dedent; + } break; + + case BR_ACQUIRE_RESULT: { + const int32_t res = *cmd++; + out << ": " << res << (res ? " (SUCCESS)" : " (FAILURE)"); + } break; + + case BR_INCREFS: + case BR_ACQUIRE: + case BR_RELEASE: + case BR_DECREFS: { + const int32_t b = *cmd++; + const int32_t c = *cmd++; + out << ": target=" << (void*)b << " (cookie " << (void*)c << ")"; + } break; + + case BR_ATTEMPT_ACQUIRE: { + const int32_t p = *cmd++; + const int32_t b = *cmd++; + const int32_t c = *cmd++; + out << ": target=" << (void*)b << " (cookie " << (void*)c + << "), pri=" << p; + } break; + + case BR_DEAD_BINDER: + case BR_CLEAR_DEATH_NOTIFICATION_DONE: { + const int32_t c = *cmd++; + out << ": death cookie " << (void*)c; + } break; + } + + out << endl; + return cmd; +} + +static const void* printCommand(TextOutput& out, const void* _cmd) +{ + static const int32_t N = sizeof(kCommandStrings)/sizeof(kCommandStrings[0]); + + const int32_t* cmd = (const int32_t*)_cmd; + int32_t code = *cmd++; + if (code < 0 || code >= N) { + out << "Unknown command: " << code << endl; + return cmd; + } + + out << kCommandStrings[code]; + switch (code) { + case BC_TRANSACTION: + case BC_REPLY: { + out << ": " << indent; + cmd = (const int32_t *)printBinderTransactionData(out, cmd); + out << dedent; + } break; + + case BC_ACQUIRE_RESULT: { + const int32_t res = *cmd++; + out << ": " << res << (res ? " (SUCCESS)" : " (FAILURE)"); + } break; + + case BC_FREE_BUFFER: { + const int32_t buf = *cmd++; + out << ": buffer=" << (void*)buf; + } break; + + case BC_INCREFS: + case BC_ACQUIRE: + case BC_RELEASE: + case BC_DECREFS: { + const int32_t d = *cmd++; + out << ": descriptor=" << (void*)d; + } break; + + case BC_INCREFS_DONE: + case BC_ACQUIRE_DONE: { + const int32_t b = *cmd++; + const int32_t c = *cmd++; + out << ": target=" << (void*)b << " (cookie " << (void*)c << ")"; + } break; + + case BC_ATTEMPT_ACQUIRE: { + const int32_t p = *cmd++; + const int32_t d = *cmd++; + out << ": decriptor=" << (void*)d << ", pri=" << p; + } break; + + case BC_REQUEST_DEATH_NOTIFICATION: + case BC_CLEAR_DEATH_NOTIFICATION: { + const int32_t h = *cmd++; + const int32_t c = *cmd++; + out << ": handle=" << h << " (death cookie " << (void*)c << ")"; + } break; + + case BC_DEAD_BINDER_DONE: { + const int32_t c = *cmd++; + out << ": death cookie " << (void*)c; + } break; + } + + out << endl; + return cmd; +} +#endif + +static pthread_mutex_t gTLSMutex = PTHREAD_MUTEX_INITIALIZER; +static bool gHaveTLS = false; +static pthread_key_t gTLS = 0; +static bool gShutdown = false; + +IPCThreadState* IPCThreadState::self() +{ + if (gHaveTLS) { +restart: + const pthread_key_t k = gTLS; + IPCThreadState* st = (IPCThreadState*)pthread_getspecific(k); + if (st) return st; + return new IPCThreadState; + } + + if (gShutdown) return NULL; + + pthread_mutex_lock(&gTLSMutex); + if (!gHaveTLS) { + if (pthread_key_create(&gTLS, threadDestructor) != 0) { + pthread_mutex_unlock(&gTLSMutex); + return NULL; + } + gHaveTLS = true; + } + pthread_mutex_unlock(&gTLSMutex); + goto restart; +} + +void IPCThreadState::shutdown() +{ + gShutdown = true; + + if (gHaveTLS) { + // XXX Need to wait for all thread pool threads to exit! + IPCThreadState* st = (IPCThreadState*)pthread_getspecific(gTLS); + if (st) { + delete st; + pthread_setspecific(gTLS, NULL); + } + gHaveTLS = false; + } +} + +sp IPCThreadState::process() +{ + return mProcess; +} + +status_t IPCThreadState::clearLastError() +{ + const status_t err = mLastError; + mLastError = NO_ERROR; + return err; +} + +int IPCThreadState::getCallingPid() +{ + return mCallingPid; +} + +int IPCThreadState::getCallingUid() +{ + return mCallingUid; +} + +int64_t IPCThreadState::clearCallingIdentity() +{ + int64_t token = ((int64_t)mCallingUid<<32) | mCallingPid; + clearCaller(); + return token; +} + +void IPCThreadState::restoreCallingIdentity(int64_t token) +{ + mCallingUid = (int)(token>>32); + mCallingPid = (int)token; +} + +void IPCThreadState::clearCaller() +{ + if (mProcess->supportsProcesses()) { + mCallingPid = getpid(); + mCallingUid = getuid(); + } else { + mCallingPid = -1; + mCallingUid = -1; + } +} + +void IPCThreadState::flushCommands() +{ + if (mProcess->mDriverFD <= 0) + return; + talkWithDriver(false); +} + +void IPCThreadState::joinThreadPool(bool isMain) +{ + LOG_THREADPOOL("**** THREAD %p (PID %d) IS JOINING THE THREAD POOL\n", (void*)pthread_self(), getpid()); + + mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER); + + status_t result; + do { + int32_t cmd; + result = talkWithDriver(); + if (result >= NO_ERROR) { + size_t IN = mIn.dataAvail(); + if (IN < sizeof(int32_t)) continue; + cmd = mIn.readInt32(); + IF_LOG_COMMANDS() { + alog << "Processing top-level Command: " + << getReturnString(cmd) << endl; + } + result = executeCommand(cmd); + } + + // Let this thread exit the thread pool if it is no longer + // needed and it is not the main process thread. + if(result == TIMED_OUT && !isMain) { + break; + } + } while (result != -ECONNREFUSED && result != -EBADF); + + LOG_THREADPOOL("**** THREAD %p (PID %d) IS LEAVING THE THREAD POOL err=%p\n", + (void*)pthread_self(), getpid(), (void*)result); + + mOut.writeInt32(BC_EXIT_LOOPER); + talkWithDriver(false); +} + +void IPCThreadState::stopProcess(bool immediate) +{ + //LOGI("**** STOPPING PROCESS"); + flushCommands(); + int fd = mProcess->mDriverFD; + mProcess->mDriverFD = -1; + close(fd); + //kill(getpid(), SIGKILL); +} + +status_t IPCThreadState::transact(int32_t handle, + uint32_t code, const Parcel& data, + Parcel* reply, uint32_t flags) +{ + status_t err = data.errorCheck(); + + flags |= TF_ACCEPT_FDS; + + IF_LOG_TRANSACTIONS() { + TextOutput::Bundle _b(alog); + alog << "BC_TRANSACTION thr " << (void*)pthread_self() << " / hand " + << handle << " / code " << TypeCode(code) << ": " + << indent << data << dedent << endl; + } + + if (err == NO_ERROR) { + LOG_ONEWAY(">>>> SEND from pid %d uid %d %s", getpid(), getuid(), + (flags & TF_ONE_WAY) == 0 ? "READ REPLY" : "ONE WAY"); + err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL); + } + + if (err != NO_ERROR) { + if (reply) reply->setError(err); + return (mLastError = err); + } + + if ((flags & TF_ONE_WAY) == 0) { + if (reply) { + err = waitForResponse(reply); + } else { + Parcel fakeReply; + err = waitForResponse(&fakeReply); + } + + IF_LOG_TRANSACTIONS() { + TextOutput::Bundle _b(alog); + alog << "BR_REPLY thr " << (void*)pthread_self() << " / hand " + << handle << ": "; + if (reply) alog << indent << *reply << dedent << endl; + else alog << "(none requested)" << endl; + } + } else { + err = waitForResponse(NULL, NULL); + } + + return err; +} + +void IPCThreadState::incStrongHandle(int32_t handle) +{ + LOG_REMOTEREFS("IPCThreadState::incStrongHandle(%d)\n", handle); + mOut.writeInt32(BC_ACQUIRE); + mOut.writeInt32(handle); +} + +void IPCThreadState::decStrongHandle(int32_t handle) +{ + LOG_REMOTEREFS("IPCThreadState::decStrongHandle(%d)\n", handle); + mOut.writeInt32(BC_RELEASE); + mOut.writeInt32(handle); +} + +void IPCThreadState::incWeakHandle(int32_t handle) +{ + LOG_REMOTEREFS("IPCThreadState::incWeakHandle(%d)\n", handle); + mOut.writeInt32(BC_INCREFS); + mOut.writeInt32(handle); +} + +void IPCThreadState::decWeakHandle(int32_t handle) +{ + LOG_REMOTEREFS("IPCThreadState::decWeakHandle(%d)\n", handle); + mOut.writeInt32(BC_DECREFS); + mOut.writeInt32(handle); +} + +status_t IPCThreadState::attemptIncStrongHandle(int32_t handle) +{ + mOut.writeInt32(BC_ATTEMPT_ACQUIRE); + mOut.writeInt32(0); // xxx was thread priority + mOut.writeInt32(handle); + status_t result = UNKNOWN_ERROR; + + waitForResponse(NULL, &result); + +#if LOG_REFCOUNTS + printf("IPCThreadState::attemptIncStrongHandle(%ld) = %s\n", + handle, result == NO_ERROR ? "SUCCESS" : "FAILURE"); +#endif + + return result; +} + +void IPCThreadState::expungeHandle(int32_t handle, IBinder* binder) +{ +#if LOG_REFCOUNTS + printf("IPCThreadState::expungeHandle(%ld)\n", handle); +#endif + self()->mProcess->expungeHandle(handle, binder); +} + +status_t IPCThreadState::requestDeathNotification(int32_t handle, BpBinder* proxy) +{ + mOut.writeInt32(BC_REQUEST_DEATH_NOTIFICATION); + mOut.writeInt32((int32_t)handle); + mOut.writeInt32((int32_t)proxy); + return NO_ERROR; +} + +status_t IPCThreadState::clearDeathNotification(int32_t handle, BpBinder* proxy) +{ + mOut.writeInt32(BC_CLEAR_DEATH_NOTIFICATION); + mOut.writeInt32((int32_t)handle); + mOut.writeInt32((int32_t)proxy); + return NO_ERROR; +} + +IPCThreadState::IPCThreadState() + : mProcess(ProcessState::self()) +{ + pthread_setspecific(gTLS, this); + clearCaller(); + mIn.setDataCapacity(256); + mOut.setDataCapacity(256); +} + +IPCThreadState::~IPCThreadState() +{ +} + +status_t IPCThreadState::sendReply(const Parcel& reply, uint32_t flags) +{ + status_t err; + status_t statusBuffer; + err = writeTransactionData(BC_REPLY, flags, -1, 0, reply, &statusBuffer); + if (err < NO_ERROR) return err; + + return waitForResponse(NULL, NULL); +} + +status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult) +{ + int32_t cmd; + int32_t err; + + while (1) { + if ((err=talkWithDriver()) < NO_ERROR) break; + err = mIn.errorCheck(); + if (err < NO_ERROR) break; + if (mIn.dataAvail() == 0) continue; + + cmd = mIn.readInt32(); + + IF_LOG_COMMANDS() { + alog << "Processing waitForResponse Command: " + << getReturnString(cmd) << endl; + } + + switch (cmd) { + case BR_TRANSACTION_COMPLETE: + if (!reply && !acquireResult) goto finish; + break; + + case BR_DEAD_REPLY: + err = DEAD_OBJECT; + goto finish; + + case BR_FAILED_REPLY: + err = FAILED_TRANSACTION; + goto finish; + + case BR_ACQUIRE_RESULT: + { + LOG_ASSERT(acquireResult != NULL, "Unexpected brACQUIRE_RESULT"); + const int32_t result = mIn.readInt32(); + if (!acquireResult) continue; + *acquireResult = result ? NO_ERROR : INVALID_OPERATION; + } + goto finish; + + case BR_REPLY: + { + binder_transaction_data tr; + err = mIn.read(&tr, sizeof(tr)); + LOG_ASSERT(err == NO_ERROR, "Not enough command data for brREPLY"); + if (err != NO_ERROR) goto finish; + + if (reply) { + if ((tr.flags & TF_STATUS_CODE) == 0) { + reply->ipcSetDataReference( + reinterpret_cast(tr.data.ptr.buffer), + tr.data_size, + reinterpret_cast(tr.data.ptr.offsets), + tr.offsets_size/sizeof(size_t), + freeBuffer, this); + } else { + err = *static_cast(tr.data.ptr.buffer); + freeBuffer(NULL, + reinterpret_cast(tr.data.ptr.buffer), + tr.data_size, + reinterpret_cast(tr.data.ptr.offsets), + tr.offsets_size/sizeof(size_t), this); + } + } else { + freeBuffer(NULL, + reinterpret_cast(tr.data.ptr.buffer), + tr.data_size, + reinterpret_cast(tr.data.ptr.offsets), + tr.offsets_size/sizeof(size_t), this); + continue; + } + } + goto finish; + + default: + err = executeCommand(cmd); + if (err != NO_ERROR) goto finish; + break; + } + } + +finish: + if (err != NO_ERROR) { + if (acquireResult) *acquireResult = err; + if (reply) reply->setError(err); + mLastError = err; + } + + return err; +} + +status_t IPCThreadState::talkWithDriver(bool doReceive) +{ + LOG_ASSERT(mProcess->mDriverFD >= 0, "Binder driver is not opened"); + + binder_write_read bwr; + + // Is the read buffer empty? + const bool needRead = mIn.dataPosition() >= mIn.dataSize(); + + // We don't want to write anything if we are still reading + // from data left in the input buffer and the caller + // has requested to read the next data. + const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : 0; + + bwr.write_size = outAvail; + bwr.write_buffer = (long unsigned int)mOut.data(); + + // This is what we'll read. + if (doReceive && needRead) { + bwr.read_size = mIn.dataCapacity(); + bwr.read_buffer = (long unsigned int)mIn.data(); + } else { + bwr.read_size = 0; + } + + IF_LOG_COMMANDS() { + TextOutput::Bundle _b(alog); + if (outAvail != 0) { + alog << "Sending commands to driver: " << indent; + const void* cmds = (const void*)bwr.write_buffer; + const void* end = ((const uint8_t*)cmds)+bwr.write_size; + alog << HexDump(cmds, bwr.write_size) << endl; + while (cmds < end) cmds = printCommand(alog, cmds); + alog << dedent; + } + alog << "Size of receive buffer: " << bwr.read_size + << ", needRead: " << needRead << ", doReceive: " << doReceive << endl; + } + + // Return immediately if there is nothing to do. + if ((bwr.write_size == 0) && (bwr.read_size == 0)) return NO_ERROR; + + bwr.write_consumed = 0; + bwr.read_consumed = 0; + status_t err; + do { + IF_LOG_COMMANDS() { + alog << "About to read/write, write size = " << mOut.dataSize() << endl; + } +#if defined(HAVE_ANDROID_OS) + if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0) + err = NO_ERROR; + else + err = -errno; +#else + err = INVALID_OPERATION; +#endif + IF_LOG_COMMANDS() { + alog << "Finished read/write, write size = " << mOut.dataSize() << endl; + } + } while (err == -EINTR); + + IF_LOG_COMMANDS() { + alog << "Our err: " << (void*)err << ", write consumed: " + << bwr.write_consumed << " (of " << mOut.dataSize() + << "), read consumed: " << bwr.read_consumed << endl; + } + + if (err >= NO_ERROR) { + if (bwr.write_consumed > 0) { + if (bwr.write_consumed < (ssize_t)mOut.dataSize()) + mOut.remove(0, bwr.write_consumed); + else + mOut.setDataSize(0); + } + if (bwr.read_consumed > 0) { + mIn.setDataSize(bwr.read_consumed); + mIn.setDataPosition(0); + } + IF_LOG_COMMANDS() { + TextOutput::Bundle _b(alog); + alog << "Remaining data size: " << mOut.dataSize() << endl; + alog << "Received commands from driver: " << indent; + const void* cmds = mIn.data(); + const void* end = mIn.data() + mIn.dataSize(); + alog << HexDump(cmds, mIn.dataSize()) << endl; + while (cmds < end) cmds = printReturnCommand(alog, cmds); + alog << dedent; + } + return NO_ERROR; + } + + return err; +} + +status_t IPCThreadState::writeTransactionData(int32_t cmd, uint32_t binderFlags, + int32_t handle, uint32_t code, const Parcel& data, status_t* statusBuffer) +{ + binder_transaction_data tr; + + tr.target.handle = handle; + tr.code = code; + tr.flags = binderFlags; + + const status_t err = data.errorCheck(); + if (err == NO_ERROR) { + tr.data_size = data.ipcDataSize(); + tr.data.ptr.buffer = data.ipcData(); + tr.offsets_size = data.ipcObjectsCount()*sizeof(size_t); + tr.data.ptr.offsets = data.ipcObjects(); + } else if (statusBuffer) { + tr.flags |= TF_STATUS_CODE; + *statusBuffer = err; + tr.data_size = sizeof(status_t); + tr.data.ptr.buffer = statusBuffer; + tr.offsets_size = 0; + tr.data.ptr.offsets = NULL; + } else { + return (mLastError = err); + } + + mOut.writeInt32(cmd); + mOut.write(&tr, sizeof(tr)); + + return NO_ERROR; +} + +sp the_context_object; + +void setTheContextObject(sp obj) +{ + the_context_object = obj; +} + +status_t IPCThreadState::executeCommand(int32_t cmd) +{ + BBinder* obj; + RefBase::weakref_type* refs; + status_t result = NO_ERROR; + + switch (cmd) { + case BR_ERROR: + result = mIn.readInt32(); + break; + + case BR_OK: + break; + + case BR_ACQUIRE: + refs = (RefBase::weakref_type*)mIn.readInt32(); + obj = (BBinder*)mIn.readInt32(); + LOG_ASSERT(refs->refBase() == obj, + "BR_ACQUIRE: object %p does not match cookie %p (expected %p)", + refs, obj, refs->refBase()); + obj->incStrong(mProcess.get()); + IF_LOG_REMOTEREFS() { + LOG_REMOTEREFS("BR_ACQUIRE from driver on %p", obj); + obj->printRefs(); + } + mOut.writeInt32(BC_ACQUIRE_DONE); + mOut.writeInt32((int32_t)refs); + mOut.writeInt32((int32_t)obj); + break; + + case BR_RELEASE: + refs = (RefBase::weakref_type*)mIn.readInt32(); + obj = (BBinder*)mIn.readInt32(); + LOG_ASSERT(refs->refBase() == obj, + "BR_RELEASE: object %p does not match cookie %p (expected %p)", + refs, obj, refs->refBase()); + IF_LOG_REMOTEREFS() { + LOG_REMOTEREFS("BR_RELEASE from driver on %p", obj); + obj->printRefs(); + } + obj->decStrong(mProcess.get()); + break; + + case BR_INCREFS: + refs = (RefBase::weakref_type*)mIn.readInt32(); + obj = (BBinder*)mIn.readInt32(); + refs->incWeak(mProcess.get()); + mOut.writeInt32(BC_INCREFS_DONE); + mOut.writeInt32((int32_t)refs); + mOut.writeInt32((int32_t)obj); + break; + + case BR_DECREFS: + refs = (RefBase::weakref_type*)mIn.readInt32(); + obj = (BBinder*)mIn.readInt32(); + // NOTE: This assertion is not valid, because the object may no + // longer exist (thus the (BBinder*)cast above resulting in a different + // memory address). + //LOG_ASSERT(refs->refBase() == obj, + // "BR_DECREFS: object %p does not match cookie %p (expected %p)", + // refs, obj, refs->refBase()); + refs->decWeak(mProcess.get()); + break; + + case BR_ATTEMPT_ACQUIRE: + refs = (RefBase::weakref_type*)mIn.readInt32(); + obj = (BBinder*)mIn.readInt32(); + + { + const bool success = refs->attemptIncStrong(mProcess.get()); + LOG_ASSERT(success && refs->refBase() == obj, + "BR_ATTEMPT_ACQUIRE: object %p does not match cookie %p (expected %p)", + refs, obj, refs->refBase()); + + mOut.writeInt32(BC_ACQUIRE_RESULT); + mOut.writeInt32((int32_t)success); + } + break; + + case BR_TRANSACTION: + { + binder_transaction_data tr; + result = mIn.read(&tr, sizeof(tr)); + LOG_ASSERT(result == NO_ERROR, + "Not enough command data for brTRANSACTION"); + if (result != NO_ERROR) break; + + Parcel buffer; + buffer.ipcSetDataReference( + reinterpret_cast(tr.data.ptr.buffer), + tr.data_size, + reinterpret_cast(tr.data.ptr.offsets), + tr.offsets_size/sizeof(size_t), freeBuffer, this); + + const pid_t origPid = mCallingPid; + const uid_t origUid = mCallingUid; + + mCallingPid = tr.sender_pid; + mCallingUid = tr.sender_euid; + + //LOGI(">>>> TRANSACT from pid %d uid %d\n", mCallingPid, mCallingUid); + + Parcel reply; + IF_LOG_TRANSACTIONS() { + TextOutput::Bundle _b(alog); + alog << "BR_TRANSACTION thr " << (void*)pthread_self() + << " / obj " << tr.target.ptr << " / code " + << TypeCode(tr.code) << ": " << indent << buffer + << dedent << endl + << "Data addr = " + << reinterpret_cast(tr.data.ptr.buffer) + << ", offsets addr=" + << reinterpret_cast(tr.data.ptr.offsets) << endl; + } + if (tr.target.ptr) { + sp b((BBinder*)tr.cookie); + const status_t error = b->transact(tr.code, buffer, &reply, 0); + if (error < NO_ERROR) reply.setError(error); + + } else { + const status_t error = the_context_object->transact(tr.code, buffer, &reply, 0); + if (error < NO_ERROR) reply.setError(error); + } + + //LOGI("<<<< TRANSACT from pid %d restore pid %d uid %d\n", + // mCallingPid, origPid, origUid); + + if ((tr.flags & TF_ONE_WAY) == 0) { + LOG_ONEWAY("Sending reply to %d!", mCallingPid); + sendReply(reply, 0); + } else { + LOG_ONEWAY("NOT sending reply to %d!", mCallingPid); + } + + mCallingPid = origPid; + mCallingUid = origUid; + + IF_LOG_TRANSACTIONS() { + TextOutput::Bundle _b(alog); + alog << "BC_REPLY thr " << (void*)pthread_self() << " / obj " + << tr.target.ptr << ": " << indent << reply << dedent << endl; + } + + } + break; + + case BR_DEAD_BINDER: + { + BpBinder *proxy = (BpBinder*)mIn.readInt32(); + proxy->sendObituary(); + mOut.writeInt32(BC_DEAD_BINDER_DONE); + mOut.writeInt32((int32_t)proxy); + } break; + + case BR_CLEAR_DEATH_NOTIFICATION_DONE: + { + BpBinder *proxy = (BpBinder*)mIn.readInt32(); + proxy->getWeakRefs()->decWeak(proxy); + } break; + + case BR_FINISHED: + result = TIMED_OUT; + break; + + case BR_NOOP: + break; + + case BR_SPAWN_LOOPER: + mProcess->spawnPooledThread(false); + break; + + default: + printf("*** BAD COMMAND %d received from Binder driver\n", cmd); + result = UNKNOWN_ERROR; + break; + } + + if (result != NO_ERROR) { + mLastError = result; + } + + return result; +} + +void IPCThreadState::threadDestructor(void *st) +{ + IPCThreadState* const self = static_cast(st); + if (self) { + self->flushCommands(); +#if defined(HAVE_ANDROID_OS) + ioctl(self->mProcess->mDriverFD, BINDER_THREAD_EXIT, 0); +#endif + delete self; + } +} + + +void IPCThreadState::freeBuffer(Parcel* parcel, const uint8_t* data, size_t dataSize, + const size_t* objects, size_t objectsSize, + void* cookie) +{ + //LOGI("Freeing parcel %p", &parcel); + IF_LOG_COMMANDS() { + alog << "Writing BC_FREE_BUFFER for " << data << endl; + } + LOG_ASSERT(data != NULL, "Called with NULL data"); + if (parcel != NULL) parcel->closeFileDescriptors(); + IPCThreadState* state = self(); + state->mOut.writeInt32(BC_FREE_BUFFER); + state->mOut.writeInt32((int32_t)data); +} + +}; // namespace android diff --git a/libs/utils/IPermissionController.cpp b/libs/utils/IPermissionController.cpp new file mode 100644 index 000000000..f01d38fd1 --- /dev/null +++ b/libs/utils/IPermissionController.cpp @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2005 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 "PermissionController" + +#include + +#include +#include +#include +#include + +#include + +namespace android { + +// ---------------------------------------------------------------------- + +class BpPermissionController : public BpInterface +{ +public: + BpPermissionController(const sp& impl) + : BpInterface(impl) + { + } + + virtual bool checkPermission(const String16& permission, int32_t pid, int32_t uid) + { + Parcel data, reply; + data.writeInterfaceToken(IPermissionController::getInterfaceDescriptor()); + data.writeString16(permission); + data.writeInt32(pid); + data.writeInt32(uid); + remote()->transact(CHECK_PERMISSION_TRANSACTION, data, &reply); + // fail on exception + if (reply.readInt32() != 0) return 0; + return reply.readInt32() != 0; + } +}; + +IMPLEMENT_META_INTERFACE(PermissionController, "android.os.IPermissionController"); + +// ---------------------------------------------------------------------- + +#define CHECK_INTERFACE(interface, data, reply) \ + do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ + LOGW("Call incorrectly routed to " #interface); \ + return PERMISSION_DENIED; \ + } } while (0) + +status_t BnPermissionController::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + //printf("PermissionController received: "); data.print(); + switch(code) { + case CHECK_PERMISSION_TRANSACTION: { + CHECK_INTERFACE(IPermissionController, data, reply); + String16 permission = data.readString16(); + int32_t pid = data.readInt32(); + int32_t uid = data.readInt32(); + bool res = checkPermission(permission, pid, uid); + // write exception + reply->writeInt32(0); + reply->writeInt32(res ? 1 : 0); + return NO_ERROR; + } break; + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +}; // namespace android + diff --git a/libs/utils/IServiceManager.cpp b/libs/utils/IServiceManager.cpp new file mode 100644 index 000000000..9beeaddde --- /dev/null +++ b/libs/utils/IServiceManager.cpp @@ -0,0 +1,230 @@ +/* + * Copyright (C) 2005 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 "ServiceManager" + +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include + +namespace android { + +sp defaultServiceManager() +{ + if (gDefaultServiceManager != NULL) return gDefaultServiceManager; + + { + AutoMutex _l(gDefaultServiceManagerLock); + if (gDefaultServiceManager == NULL) { + gDefaultServiceManager = interface_cast( + ProcessState::self()->getContextObject(NULL)); + } + } + + return gDefaultServiceManager; +} + +bool checkCallingPermission(const String16& permission) +{ + return checkCallingPermission(permission, NULL, NULL); +} + +static String16 _permission("permission"); + +bool checkCallingPermission(const String16& permission, int32_t* outPid, int32_t* outUid) +{ + IPCThreadState* ipcState = IPCThreadState::self(); + int32_t pid = ipcState->getCallingPid(); + int32_t uid = ipcState->getCallingUid(); + if (outPid) *outPid = pid; + if (outUid) *outUid= uid; + + sp pc; + gDefaultServiceManagerLock.lock(); + pc = gPermissionController; + gDefaultServiceManagerLock.unlock(); + + int64_t startTime = 0; + + while (true) { + if (pc != NULL) { + bool res = pc->checkPermission(permission, pid, uid); + if (res) { + if (startTime != 0) { + LOGI("Check passed after %d seconds for %s from uid=%d pid=%d", + (int)((uptimeMillis()-startTime)/1000), + String8(permission).string(), uid, pid); + } + return res; + } + + // Is this a permission failure, or did the controller go away? + if (pc->asBinder()->isBinderAlive()) { + LOGW("Permission failure: %s from uid=%d pid=%d", + String8(permission).string(), uid, pid); + return false; + } + + // Object is dead! + gDefaultServiceManagerLock.lock(); + if (gPermissionController == pc) { + gPermissionController = NULL; + } + gDefaultServiceManagerLock.unlock(); + } + + // Need to retrieve the permission controller. + sp binder = defaultServiceManager()->checkService(_permission); + if (binder == NULL) { + // Wait for the permission controller to come back... + if (startTime == 0) { + startTime = uptimeMillis(); + LOGI("Waiting to check permission %s from uid=%d pid=%d", + String8(permission).string(), uid, pid); + } + sleep(1); + } else { + pc = interface_cast(binder); + // Install the new permission controller, and try again. + gDefaultServiceManagerLock.lock(); + gPermissionController = pc; + gDefaultServiceManagerLock.unlock(); + } + } +} + +// ---------------------------------------------------------------------- + +class BpServiceManager : public BpInterface +{ +public: + BpServiceManager(const sp& impl) + : BpInterface(impl) + { + } + + virtual sp getService(const String16& name) const + { + unsigned n; + for (n = 0; n < 5; n++){ + sp svc = checkService(name); + if (svc != NULL) return svc; + LOGI("Waiting for sevice %s...\n", String8(name).string()); + sleep(1); + } + return NULL; + } + + virtual sp checkService( const String16& name) const + { + Parcel data, reply; + data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor()); + data.writeString16(name); + remote()->transact(CHECK_SERVICE_TRANSACTION, data, &reply); + return reply.readStrongBinder(); + } + + virtual status_t addService(const String16& name, const sp& service) + { + Parcel data, reply; + data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor()); + data.writeString16(name); + data.writeStrongBinder(service); + status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply); + return err == NO_ERROR ? reply.readInt32() : err; + } + + virtual Vector listServices() + { + Vector res; + int n = 0; + + for (;;) { + Parcel data, reply; + data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor()); + data.writeInt32(n++); + status_t err = remote()->transact(LIST_SERVICES_TRANSACTION, data, &reply); + if (err != NO_ERROR) + break; + res.add(reply.readString16()); + } + return res; + } +}; + +IMPLEMENT_META_INTERFACE(ServiceManager, "android.os.IServiceManager"); + +// ---------------------------------------------------------------------- + +#define CHECK_INTERFACE(interface, data, reply) \ + do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ + LOGW("Call incorrectly routed to " #interface); \ + return PERMISSION_DENIED; \ + } } while (0) + +status_t BnServiceManager::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + //printf("ServiceManager received: "); data.print(); + switch(code) { + case GET_SERVICE_TRANSACTION: { + CHECK_INTERFACE(IServiceManager, data, reply); + String16 which = data.readString16(); + sp b = const_cast(this)->getService(which); + reply->writeStrongBinder(b); + return NO_ERROR; + } break; + case CHECK_SERVICE_TRANSACTION: { + CHECK_INTERFACE(IServiceManager, data, reply); + String16 which = data.readString16(); + sp b = const_cast(this)->checkService(which); + reply->writeStrongBinder(b); + return NO_ERROR; + } break; + case ADD_SERVICE_TRANSACTION: { + CHECK_INTERFACE(IServiceManager, data, reply); + String16 which = data.readString16(); + sp b = data.readStrongBinder(); + status_t err = addService(which, b); + reply->writeInt32(err); + return NO_ERROR; + } break; + case LIST_SERVICES_TRANSACTION: { + CHECK_INTERFACE(IServiceManager, data, reply); + Vector list = listServices(); + const size_t N = list.size(); + reply->writeInt32(N); + for (size_t i=0; iwriteString16(list[i]); + } + return NO_ERROR; + } break; + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +}; // namespace android + diff --git a/libs/utils/InetAddress.cpp b/libs/utils/InetAddress.cpp new file mode 100644 index 000000000..39a0a6839 --- /dev/null +++ b/libs/utils/InetAddress.cpp @@ -0,0 +1,236 @@ +/* + * Copyright (C) 2005 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. + */ + +// +// Internet address class. +// +#ifdef HAVE_WINSOCK +# include +#else +# include +# include +# include +//# include +# include +#endif + +#include +#include +#include + +#include +#include +#include +#include +#include + +using namespace android; + + +/* + * =========================================================================== + * InetAddress + * =========================================================================== + */ + +// lock for the next couple of functions; could tuck into InetAddress +static Mutex* gGHBNLock; + +/* + * Lock/unlock access to the hostent struct returned by gethostbyname(). + */ +static inline void lock_gethostbyname(void) +{ + if (gGHBNLock == NULL) + gGHBNLock = new Mutex; + gGHBNLock->lock(); +} +static inline void unlock_gethostbyname(void) +{ + assert(gGHBNLock != NULL); + gGHBNLock->unlock(); +} + + +/* + * Constructor -- just init members. This is private so that callers + * are required to use getByName(). + */ +InetAddress::InetAddress(void) + : mAddress(NULL), mLength(-1), mName(NULL) +{ +} + +/* + * Destructor -- free address storage. + */ +InetAddress::~InetAddress(void) +{ + delete[] (char*) mAddress; + delete[] mName; +} + +/* + * Copy constructor. + */ +InetAddress::InetAddress(const InetAddress& orig) +{ + *this = orig; // use assignment code +} + +/* + * Assignment operator. + */ +InetAddress& InetAddress::operator=(const InetAddress& addr) +{ + // handle self-assignment + if (this == &addr) + return *this; + // copy mLength and mAddress + mLength = addr.mLength; + if (mLength > 0) { + mAddress = new char[mLength]; + memcpy(mAddress, addr.mAddress, mLength); + LOG(LOG_DEBUG, "socket", + "HEY: copied %d bytes in assignment operator\n", mLength); + } else { + mAddress = NULL; + } + // copy mName + mName = new char[strlen(addr.mName)+1]; + strcpy(mName, addr.mName); + + return *this; +} + +/* + * Create a new object from a name or a dotted-number IP notation. + * + * Returns NULL on failure. + */ +InetAddress* +InetAddress::getByName(const char* host) +{ + InetAddress* newAddr = NULL; + struct sockaddr_in addr; + struct hostent* he; + DurationTimer hostTimer, lockTimer; + + // gethostbyname() isn't reentrant, so we need to lock things until + // we can copy the data out. + lockTimer.start(); + lock_gethostbyname(); + hostTimer.start(); + + he = gethostbyname(host); + if (he == NULL) { + LOG(LOG_WARN, "socket", "WARNING: cannot resolve host %s\n", host); + unlock_gethostbyname(); + return NULL; + } + + memcpy(&addr.sin_addr, he->h_addr, he->h_length); + addr.sin_family = he->h_addrtype; + addr.sin_port = 0; + + // got it, unlock us + hostTimer.stop(); + he = NULL; + unlock_gethostbyname(); + + lockTimer.stop(); + if ((long) lockTimer.durationUsecs() > 100000) { + long lockTime = (long) lockTimer.durationUsecs(); + long hostTime = (long) hostTimer.durationUsecs(); + LOG(LOG_DEBUG, "socket", + "Lookup of %s took %.3fs (gethostbyname=%.3fs lock=%.3fs)\n", + host, lockTime / 1000000.0, hostTime / 1000000.0, + (lockTime - hostTime) / 1000000.0); + } + + // Alloc storage and copy it over. + newAddr = new InetAddress(); + if (newAddr == NULL) + return NULL; + + newAddr->mLength = sizeof(struct sockaddr_in); + newAddr->mAddress = new char[sizeof(struct sockaddr_in)]; + if (newAddr->mAddress == NULL) { + delete newAddr; + return NULL; + } + memcpy(newAddr->mAddress, &addr, newAddr->mLength); + + // Keep this for debug messages. + newAddr->mName = new char[strlen(host)+1]; + if (newAddr->mName == NULL) { + delete newAddr; + return NULL; + } + strcpy(newAddr->mName, host); + + return newAddr; +} + + +/* + * =========================================================================== + * InetSocketAddress + * =========================================================================== + */ + +/* + * Create an address with the host wildcard (INADDR_ANY). + */ +bool InetSocketAddress::create(int port) +{ + assert(mAddress == NULL); + + mAddress = InetAddress::getByName("0.0.0.0"); + if (mAddress == NULL) + return false; + mPort = port; + return true; +} + +/* + * Create address with host and port specified. + */ +bool InetSocketAddress::create(const InetAddress* addr, int port) +{ + assert(mAddress == NULL); + + mAddress = new InetAddress(*addr); // make a copy + if (mAddress == NULL) + return false; + mPort = port; + return true; +} + +/* + * Create address with host and port specified. + */ +bool InetSocketAddress::create(const char* host, int port) +{ + assert(mAddress == NULL); + + mAddress = InetAddress::getByName(host); + if (mAddress == NULL) + return false; + mPort = port; + return true; +} + diff --git a/libs/utils/LogSocket.cpp b/libs/utils/LogSocket.cpp new file mode 100644 index 000000000..e64f794a8 --- /dev/null +++ b/libs/utils/LogSocket.cpp @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#ifndef HAVE_WINSOCK +#define SOCKETLOG +#endif + +#ifdef SOCKETLOG + +#define LOG_TAG "SOCKETLOG" + +#include +#include +#include "utils/LogSocket.h" +#include "utils/logger.h" +#include "cutils/hashmap.h" + +// defined in //device/data/etc/event-log-tags +#define SOCKET_CLOSE_LOG 51000 + +static Hashmap* statsMap = NULL; + +#define LOG_LIST_NUMBER 5 + +typedef struct SocketStats { + int fd; + unsigned int send; + unsigned int recv; + unsigned int ip; + unsigned short port; + short reason; +}SocketStats; + +SocketStats *get_socket_stats(int fd) { + if (statsMap == NULL) { + statsMap = hashmapCreate(8, &hashmapIntHash, &hashmapIntEquals); + } + + SocketStats *s = (SocketStats*) hashmapGet(statsMap, &fd); + if (s == NULL) { + // LOGD("create SocketStats for fd %d", fd); + s = (SocketStats*) malloc(sizeof(SocketStats)); + memset(s, 0, sizeof(SocketStats)); + s->fd = fd; + hashmapPut(statsMap, &s->fd, s); + } + return s; +} + +void log_socket_connect(int fd, unsigned int ip, unsigned short port) { + // LOGD("log_socket_connect for fd %d ip %d port%d", fd, ip, port); + SocketStats *s = get_socket_stats(fd); + s->ip = ip; + s->port = port; +} + +void add_send_stats(int fd, int send) { + if (send <=0) { + LOGE("add_send_stats send %d", send); + return; + } + SocketStats *s = get_socket_stats(fd); + s->send += send; + // LOGD("add_send_stats for fd %d ip %d port%d", fd, s->ip, s->port); +} + +void add_recv_stats(int fd, int recv) { + if (recv <=0) { + LOGE("add_recv_stats recv %d", recv); + return; + } + SocketStats *s = get_socket_stats(fd); + s->recv += recv; + // LOGD("add_recv_stats for fd %d ip %d port%d", fd, s->ip, s->port); +} + +char* put_int(char* buf, int value) { + *buf = EVENT_TYPE_INT; + buf++; + memcpy(buf, &value, sizeof(int)); + return buf + sizeof(int); +} + +void log_socket_close(int fd, short reason) { + if (statsMap) { + SocketStats *s = (SocketStats*) hashmapGet(statsMap, &fd); + if (s != NULL) { + if (s->send != 0 || s->recv != 0) { + s->reason = reason; + // 5 int + list type need 2 bytes + char buf[LOG_LIST_NUMBER * 5 + 2]; + buf[0] = EVENT_TYPE_LIST; + buf[1] = LOG_LIST_NUMBER; + char* writePos = buf + 2; + writePos = put_int(writePos, s->send); + writePos = put_int(writePos, s->recv); + writePos = put_int(writePos, s->ip); + writePos = put_int(writePos, s->port); + writePos = put_int(writePos, s->reason); + + android_bWriteLog(SOCKET_CLOSE_LOG, buf, sizeof(buf)); + // LOGD("send %d recv %d reason %d", s->send, s->recv, s->reason); + } + hashmapRemove(statsMap, &s->fd); + free(s); + } + } +} + +#else +void add_send_stats(int fd, int send) {} +void add_recv_stats(int fd, int recv) {} +void log_socket_close(int fd, short reason) {} +void log_socket_connect(int fd, unsigned int ip, unsigned short port) {} +#endif diff --git a/libs/utils/MODULE_LICENSE_APACHE2 b/libs/utils/MODULE_LICENSE_APACHE2 new file mode 100644 index 000000000..e69de29bb diff --git a/libs/utils/MemoryBase.cpp b/libs/utils/MemoryBase.cpp new file mode 100644 index 000000000..f25e11c6b --- /dev/null +++ b/libs/utils/MemoryBase.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include +#include + +#include + + +namespace android { + +// --------------------------------------------------------------------------- + +MemoryBase::MemoryBase(const sp& heap, + ssize_t offset, size_t size) + : mSize(size), mOffset(offset), mHeap(heap) +{ +} + +sp MemoryBase::getMemory(ssize_t* offset, size_t* size) const +{ + if (offset) *offset = mOffset; + if (size) *size = mSize; + return mHeap; +} + +MemoryBase::~MemoryBase() +{ +} + +// --------------------------------------------------------------------------- +}; // namespace android diff --git a/libs/utils/MemoryDealer.cpp b/libs/utils/MemoryDealer.cpp new file mode 100644 index 000000000..e6d1d180f --- /dev/null +++ b/libs/utils/MemoryDealer.cpp @@ -0,0 +1,407 @@ +/* + * 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. + */ + +#define LOG_TAG "MemoryDealer" + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace android { + + +// ---------------------------------------------------------------------------- + +class SimpleMemory : public MemoryBase { +public: + SimpleMemory(const sp& heap, ssize_t offset, size_t size); + virtual ~SimpleMemory(); +}; + + +// ---------------------------------------------------------------------------- + +MemoryDealer::Allocation::Allocation( + const sp& dealer, ssize_t offset, size_t size, + const sp& memory) + : mDealer(dealer), mOffset(offset), mSize(size), mMemory(memory) +{ +} + +MemoryDealer::Allocation::~Allocation() +{ + if (mSize) { + /* NOTE: it's VERY important to not free allocations of size 0 because + * they're special as they don't have any record in the allocator + * and could alias some real allocation (their offset is zero). */ + mDealer->deallocate(mOffset); + } +} + +sp MemoryDealer::Allocation::getMemory( + ssize_t* offset, size_t* size) const +{ + return mMemory->getMemory(offset, size); +} + +// ---------------------------------------------------------------------------- + +MemoryDealer::MemoryDealer(size_t size, uint32_t flags, const char* name) + : mHeap(new SharedHeap(size, flags, name)), + mAllocator(new SimpleBestFitAllocator(size)) +{ +} + +MemoryDealer::MemoryDealer(const sp& heap) + : mHeap(heap), + mAllocator(new SimpleBestFitAllocator(heap->virtualSize())) +{ +} + +MemoryDealer::MemoryDealer( const sp& heap, + const sp& allocator) + : mHeap(heap), mAllocator(allocator) +{ +} + +MemoryDealer::~MemoryDealer() +{ +} + +sp MemoryDealer::allocate(size_t size, uint32_t flags) +{ + sp memory; + const ssize_t offset = allocator()->allocate(size, flags); + if (offset >= 0) { + sp new_memory = heap()->mapMemory(offset, size); + if (new_memory != 0) { + memory = new Allocation(this, offset, size, new_memory); + } else { + LOGE("couldn't map [%8x, %d]", offset, size); + if (size) { + /* NOTE: it's VERY important to not free allocations of size 0 + * because they're special as they don't have any record in the + * allocator and could alias some real allocation + * (their offset is zero). */ + allocator()->deallocate(offset); + } + } + } + return memory; +} + +void MemoryDealer::deallocate(size_t offset) +{ + allocator()->deallocate(offset); +} + +void MemoryDealer::dump(const char* what, uint32_t flags) const +{ + allocator()->dump(what, flags); +} + +const sp& MemoryDealer::heap() const { + return mHeap; +} + +const sp& MemoryDealer::allocator() const { + return mAllocator; +} + +// ---------------------------------------------------------------------------- + +// align all the memory blocks on a cache-line boundary +const int SimpleBestFitAllocator::kMemoryAlign = 32; + +SimpleBestFitAllocator::SimpleBestFitAllocator(size_t size) +{ + size_t pagesize = getpagesize(); + mHeapSize = ((size + pagesize-1) & ~(pagesize-1)); + + chunk_t* node = new chunk_t(0, mHeapSize / kMemoryAlign); + mList.insertHead(node); +} + +SimpleBestFitAllocator::~SimpleBestFitAllocator() +{ + while(!mList.isEmpty()) { + delete mList.remove(mList.head()); + } +} + +size_t SimpleBestFitAllocator::size() const +{ + return mHeapSize; +} + +size_t SimpleBestFitAllocator::allocate(size_t size, uint32_t flags) +{ + Mutex::Autolock _l(mLock); + ssize_t offset = alloc(size, flags); + return offset; +} + +status_t SimpleBestFitAllocator::deallocate(size_t offset) +{ + Mutex::Autolock _l(mLock); + chunk_t const * const freed = dealloc(offset); + if (freed) { + return NO_ERROR; + } + return NAME_NOT_FOUND; +} + +ssize_t SimpleBestFitAllocator::alloc(size_t size, uint32_t flags) +{ + if (size == 0) { + return 0; + } + size = (size + kMemoryAlign-1) / kMemoryAlign; + chunk_t* free_chunk = 0; + chunk_t* cur = mList.head(); + + size_t pagesize = getpagesize(); + while (cur) { + int extra = 0; + if (flags & PAGE_ALIGNED) + extra = ( -cur->start & ((pagesize/kMemoryAlign)-1) ) ; + + // best fit + if (cur->free && (cur->size >= (size+extra))) { + if ((!free_chunk) || (cur->size < free_chunk->size)) { + free_chunk = cur; + } + if (cur->size == size) { + break; + } + } + cur = cur->next; + } + + if (free_chunk) { + const size_t free_size = free_chunk->size; + free_chunk->free = 0; + free_chunk->size = size; + if (free_size > size) { + int extra = 0; + if (flags & PAGE_ALIGNED) + extra = ( -free_chunk->start & ((pagesize/kMemoryAlign)-1) ) ; + if (extra) { + chunk_t* split = new chunk_t(free_chunk->start, extra); + free_chunk->start += extra; + mList.insertBefore(free_chunk, split); + } + + LOGE_IF((flags&PAGE_ALIGNED) && + ((free_chunk->start*kMemoryAlign)&(pagesize-1)), + "PAGE_ALIGNED requested, but page is not aligned!!!"); + + const ssize_t tail_free = free_size - (size+extra); + if (tail_free > 0) { + chunk_t* split = new chunk_t( + free_chunk->start + free_chunk->size, tail_free); + mList.insertAfter(free_chunk, split); + } + } + return (free_chunk->start)*kMemoryAlign; + } + return NO_MEMORY; +} + +SimpleBestFitAllocator::chunk_t* SimpleBestFitAllocator::dealloc(size_t start) +{ + start = start / kMemoryAlign; + chunk_t* cur = mList.head(); + while (cur) { + if (cur->start == start) { + LOG_FATAL_IF(cur->free, + "block at offset 0x%08lX of size 0x%08lX already freed", + cur->start*kMemoryAlign, cur->size*kMemoryAlign); + + // merge freed blocks together + chunk_t* freed = cur; + cur->free = 1; + do { + chunk_t* const p = cur->prev; + chunk_t* const n = cur->next; + if (p && (p->free || !cur->size)) { + freed = p; + p->size += cur->size; + mList.remove(cur); + delete cur; + } + cur = n; + } while (cur && cur->free); + + #ifndef NDEBUG + if (!freed->free) { + dump_l("dealloc (!freed->free)"); + } + #endif + LOG_FATAL_IF(!freed->free, + "freed block at offset 0x%08lX of size 0x%08lX is not free!", + freed->start * kMemoryAlign, freed->size * kMemoryAlign); + + return freed; + } + cur = cur->next; + } + return 0; +} + +void SimpleBestFitAllocator::dump(const char* what, uint32_t flags) const +{ + Mutex::Autolock _l(mLock); + dump_l(what, flags); +} + +void SimpleBestFitAllocator::dump_l(const char* what, uint32_t flags) const +{ + String8 result; + dump_l(result, what, flags); + LOGD("%s", result.string()); +} + +void SimpleBestFitAllocator::dump(String8& result, + const char* what, uint32_t flags) const +{ + Mutex::Autolock _l(mLock); + dump_l(result, what, flags); +} + +void SimpleBestFitAllocator::dump_l(String8& result, + const char* what, uint32_t flags) const +{ + size_t size = 0; + int32_t i = 0; + chunk_t const* cur = mList.head(); + + const size_t SIZE = 256; + char buffer[SIZE]; + snprintf(buffer, SIZE, " %s (%p, size=%u)\n", + what, this, (unsigned int)mHeapSize); + + result.append(buffer); + + while (cur) { + const char* errs[] = {"", "| link bogus NP", + "| link bogus PN", "| link bogus NP+PN" }; + int np = ((cur->next) && cur->next->prev != cur) ? 1 : 0; + int pn = ((cur->prev) && cur->prev->next != cur) ? 2 : 0; + + snprintf(buffer, SIZE, " %3u: %08x | 0x%08X | 0x%08X | %s %s\n", + i, int(cur), int(cur->start*kMemoryAlign), + int(cur->size*kMemoryAlign), + int(cur->free) ? "F" : "A", + errs[np|pn]); + + result.append(buffer); + + if (!cur->free) + size += cur->size*kMemoryAlign; + + i++; + cur = cur->next; + } + snprintf(buffer, SIZE, " size allocated: %u (%u KB)\n", int(size), int(size/1024)); + result.append(buffer); +} + +// ---------------------------------------------------------------------------- + + +SharedHeap::SharedHeap(size_t size, uint32_t flags, char const * name) + : MemoryHeapBase(size, flags, name) +{ +} + +SharedHeap::~SharedHeap() +{ +} + +sp SharedHeap::mapMemory(size_t offset, size_t size) +{ + return new SimpleMemory(this, offset, size); +} + + +SimpleMemory::SimpleMemory(const sp& heap, + ssize_t offset, size_t size) + : MemoryBase(heap, offset, size) +{ +#ifndef NDEBUG + void* const start_ptr = (void*)(intptr_t(heap->base()) + offset); + memset(start_ptr, 0xda, size); +#endif +} + +SimpleMemory::~SimpleMemory() +{ + size_t freedOffset = getOffset(); + size_t freedSize = getSize(); + + // keep the size to unmap in excess + size_t pagesize = getpagesize(); + size_t start = freedOffset; + size_t end = start + freedSize; + start &= ~(pagesize-1); + end = (end + pagesize-1) & ~(pagesize-1); + + // give back to the kernel the pages we don't need + size_t free_start = freedOffset; + size_t free_end = free_start + freedSize; + if (start < free_start) + start = free_start; + if (end > free_end) + end = free_end; + start = (start + pagesize-1) & ~(pagesize-1); + end &= ~(pagesize-1); + + void* const start_ptr = (void*)(intptr_t(getHeap()->base()) + start); + size_t size = end-start; + +#ifndef NDEBUG + memset(start_ptr, 0xdf, size); +#endif + +// MADV_REMOVE is not defined on Dapper based Goobuntu +#ifdef MADV_REMOVE + if (size) { + int err = madvise(start_ptr, size, MADV_REMOVE); + LOGW_IF(err, "madvise(%p, %u, MADV_REMOVE) returned %s", + start_ptr, size, err<0 ? strerror(errno) : "Ok"); + } +#endif +} + +}; // namespace android diff --git a/libs/utils/MemoryHeapBase.cpp b/libs/utils/MemoryHeapBase.cpp new file mode 100644 index 000000000..59963c92b --- /dev/null +++ b/libs/utils/MemoryHeapBase.cpp @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "MemoryHeapBase" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#if HAVE_ANDROID_OS +#include +#endif + + +namespace android { + +// --------------------------------------------------------------------------- + +MemoryHeapBase::MemoryHeapBase() + : mFD(-1), mSize(0), mBase(MAP_FAILED), + mDevice(NULL), mNeedUnmap(false) +{ +} + +MemoryHeapBase::MemoryHeapBase(size_t size, uint32_t flags, char const * name) + : mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags), + mDevice(0), mNeedUnmap(false) +{ + const size_t pagesize = getpagesize(); + size = ((size + pagesize-1) & ~(pagesize-1)); + int fd = ashmem_create_region(name == NULL ? "MemoryHeapBase" : name, size); + LOGE_IF(fd<0, "error creating ashmem region: %s", strerror(errno)); + if (fd >= 0) { + if (mapfd(fd, size) == NO_ERROR) { + if (flags & READ_ONLY) { + ashmem_set_prot_region(fd, PROT_READ); + } + } + } +} + +MemoryHeapBase::MemoryHeapBase(const char* device, size_t size, uint32_t flags) + : mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags), + mDevice(0), mNeedUnmap(false) +{ + int fd = open(device, O_RDWR); + LOGE_IF(fd<0, "error opening %s: %s", device, strerror(errno)); + if (fd >= 0) { + const size_t pagesize = getpagesize(); + size = ((size + pagesize-1) & ~(pagesize-1)); + if (mapfd(fd, size) == NO_ERROR) { + mDevice = device; + } + } +} + +MemoryHeapBase::MemoryHeapBase(int fd, size_t size, uint32_t flags) + : mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags), + mDevice(0), mNeedUnmap(false) +{ + const size_t pagesize = getpagesize(); + size = ((size + pagesize-1) & ~(pagesize-1)); + mapfd(dup(fd), size); +} + +status_t MemoryHeapBase::init(int fd, void *base, int size, int flags, const char* device) +{ + if (mFD != -1) { + return INVALID_OPERATION; + } + mFD = fd; + mBase = base; + mSize = size; + mFlags = flags; + mDevice = device; + return NO_ERROR; +} + +status_t MemoryHeapBase::mapfd(int fd, size_t size) +{ + if (size == 0) { + // try to figure out the size automatically +#if HAVE_ANDROID_OS + // first try the PMEM ioctl + pmem_region reg; + int err = ioctl(fd, PMEM_GET_TOTAL_SIZE, ®); + if (err == 0) + size = reg.len; +#endif + if (size == 0) { // try fstat + struct stat sb; + if (fstat(fd, &sb) == 0) + size = sb.st_size; + } + // if it didn't work, let mmap() fail. + } + + void* base = (uint8_t*)mmap(0, size, + PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); + if (base == MAP_FAILED) { + LOGE("mmap(fd=%d, size=%u) failed (%s)", + fd, uint32_t(size), strerror(errno)); + close(fd); + return -errno; + } + //LOGD("mmap(fd=%d, base=%p, size=%lu)", fd, base, size); + mFD = fd; + mBase = base; + mSize = size; + mNeedUnmap = true; + return NO_ERROR; +} + +MemoryHeapBase::~MemoryHeapBase() +{ + dispose(); +} + +void MemoryHeapBase::dispose() +{ + int fd = android_atomic_or(-1, &mFD); + if (fd >= 0) { + if (mNeedUnmap) { + //LOGD("munmap(fd=%d, base=%p, size=%lu)", fd, mBase, mSize); + munmap(mBase, mSize); + } + mBase = 0; + mSize = 0; + close(fd); + } +} + +int MemoryHeapBase::getHeapID() const { + return mFD; +} + +void* MemoryHeapBase::getBase() const { + return mBase; +} + +size_t MemoryHeapBase::getSize() const { + return mSize; +} + +uint32_t MemoryHeapBase::getFlags() const { + return mFlags; +} + +const char* MemoryHeapBase::getDevice() const { + return mDevice; +} + +// --------------------------------------------------------------------------- +}; // namespace android diff --git a/libs/utils/MemoryHeapPmem.cpp b/libs/utils/MemoryHeapPmem.cpp new file mode 100644 index 000000000..1e5a1cc96 --- /dev/null +++ b/libs/utils/MemoryHeapPmem.cpp @@ -0,0 +1,226 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "MemoryHeapPmem" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#if HAVE_ANDROID_OS +#include +#endif + +namespace android { + +// --------------------------------------------------------------------------- + +class MemoryHeapPmem; + +class SubRegionMemory : public BnMemory { +public: + SubRegionMemory(const sp& heap, ssize_t offset, size_t size); + virtual ~SubRegionMemory(); + virtual sp getMemory(ssize_t* offset, size_t* size) const; +private: + friend class MemoryHeapPmem; + void revoke(); + size_t mSize; + ssize_t mOffset; + sp mClientHeap; +}; + +SubRegionMemory::SubRegionMemory(const sp& heap, + ssize_t offset, size_t size) + : mSize(size), mOffset(offset), mClientHeap(heap) +{ +#ifndef NDEBUG + void* const start_ptr = (void*)(intptr_t(mClientHeap->base()) + offset); + memset(start_ptr, 0xda, size); +#endif + +#if HAVE_ANDROID_OS + if (size > 0) { + const size_t pagesize = getpagesize(); + size = (size + pagesize-1) & ~(pagesize-1); + int our_fd = heap->heapID(); + struct pmem_region sub = { offset, size }; + int err = ioctl(our_fd, PMEM_MAP, &sub); + LOGE_IF(err<0, "PMEM_MAP failed (%s), " + "mFD=%d, sub.offset=%lu, sub.size=%lu", + strerror(errno), our_fd, sub.offset, sub.len); +} +#endif +} + +sp SubRegionMemory::getMemory(ssize_t* offset, size_t* size) const +{ + if (offset) *offset = mOffset; + if (size) *size = mSize; + return mClientHeap; +} + +SubRegionMemory::~SubRegionMemory() +{ + revoke(); +} + + +void SubRegionMemory::revoke() +{ + // NOTE: revoke() doesn't need to be protected by a lock because it + // can only be called from MemoryHeapPmem::revoke(), which means + // that we can't be in ~SubRegionMemory(), or in ~SubRegionMemory(), + // which means MemoryHeapPmem::revoke() wouldn't have been able to + // promote() it. + +#if HAVE_ANDROID_OS + if (mClientHeap != NULL) { + int our_fd = mClientHeap->heapID(); + struct pmem_region sub; + sub.offset = mOffset; + sub.len = mSize; + int err = ioctl(our_fd, PMEM_UNMAP, &sub); + LOGE_IF(err<0, "PMEM_UNMAP failed (%s), " + "mFD=%d, sub.offset=%lu, sub.size=%lu", + strerror(errno), our_fd, sub.offset, sub.len); + mClientHeap.clear(); + } +#endif +} + +// --------------------------------------------------------------------------- + +MemoryHeapPmem::MemoryHeapPmem(const sp& pmemHeap, + uint32_t flags) + : HeapInterface(), MemoryHeapBase() +{ + char const * const device = pmemHeap->getDevice(); +#if HAVE_ANDROID_OS + if (device) { + int fd = open(device, O_RDWR); + LOGE_IF(fd<0, "couldn't open %s (%s)", device, strerror(errno)); + if (fd >= 0) { + int err = ioctl(fd, PMEM_CONNECT, pmemHeap->heapID()); + if (err < 0) { + LOGE("PMEM_CONNECT failed (%s), mFD=%d, sub-fd=%d", + strerror(errno), fd, pmemHeap->heapID()); + close(fd); + } else { + // everything went well... + mParentHeap = pmemHeap; + MemoryHeapBase::init(fd, + pmemHeap->getBase(), + pmemHeap->getSize(), + pmemHeap->getFlags() | flags, + device); + } + } + } +#else + mParentHeap = pmemHeap; + MemoryHeapBase::init( + dup(pmemHeap->heapID()), + pmemHeap->getBase(), + pmemHeap->getSize(), + pmemHeap->getFlags() | flags, + device); +#endif +} + +MemoryHeapPmem::~MemoryHeapPmem() +{ +} + +sp MemoryHeapPmem::mapMemory(size_t offset, size_t size) +{ + sp memory; + if (heapID() > 0) + memory = new SubRegionMemory(this, offset, size); + + if (memory != 0) { + Mutex::Autolock _l(mLock); + mAllocations.add(memory); + } + return memory; +} + +status_t MemoryHeapPmem::slap() +{ +#if HAVE_ANDROID_OS + size_t size = getSize(); + const size_t pagesize = getpagesize(); + size = (size + pagesize-1) & ~(pagesize-1); + int our_fd = getHeapID(); + struct pmem_region sub = { 0, size }; + int err = ioctl(our_fd, PMEM_MAP, &sub); + LOGE_IF(err<0, "PMEM_MAP failed (%s), " + "mFD=%d, sub.offset=%lu, sub.size=%lu", + strerror(errno), our_fd, sub.offset, sub.len); + return -errno; +#else + return NO_ERROR; +#endif +} + +status_t MemoryHeapPmem::unslap() +{ +#if HAVE_ANDROID_OS + size_t size = getSize(); + const size_t pagesize = getpagesize(); + size = (size + pagesize-1) & ~(pagesize-1); + int our_fd = getHeapID(); + struct pmem_region sub = { 0, size }; + int err = ioctl(our_fd, PMEM_UNMAP, &sub); + LOGE_IF(err<0, "PMEM_UNMAP failed (%s), " + "mFD=%d, sub.offset=%lu, sub.size=%lu", + strerror(errno), our_fd, sub.offset, sub.len); + return -errno; +#else + return NO_ERROR; +#endif +} + +void MemoryHeapPmem::revoke() +{ + Vector< wp > allocations; + + { // scope for lock + Mutex::Autolock _l(mLock); + allocations = mAllocations; + mAllocations.clear(); + } + + ssize_t count = allocations.size(); + for (ssize_t i=0 ; i memory(allocations[i].promote()); + if (memory != 0) + memory->revoke(); + } +} + +// --------------------------------------------------------------------------- +}; // namespace android diff --git a/libs/utils/NOTICE b/libs/utils/NOTICE new file mode 100644 index 000000000..c5b1efa7a --- /dev/null +++ b/libs/utils/NOTICE @@ -0,0 +1,190 @@ + + Copyright (c) 2005-2008, The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + + 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. + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + diff --git a/libs/utils/Parcel.cpp b/libs/utils/Parcel.cpp new file mode 100644 index 000000000..3eca4b010 --- /dev/null +++ b/libs/utils/Parcel.cpp @@ -0,0 +1,1311 @@ +/* + * Copyright (C) 2005 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 "Parcel" +//#define LOG_NDEBUG 0 + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#ifndef INT32_MAX +#define INT32_MAX ((int32_t)(2147483647)) +#endif + +#define LOG_REFS(...) +//#define LOG_REFS(...) LOG(LOG_DEBUG, "Parcel", __VA_ARGS__) + +// --------------------------------------------------------------------------- + +#define PAD_SIZE(s) (((s)+3)&~3) + +// XXX This can be made public if we want to provide +// support for typed data. +struct small_flat_data +{ + uint32_t type; + uint32_t data; +}; + +namespace android { + +void acquire_object(const sp& proc, + const flat_binder_object& obj, const void* who) +{ + switch (obj.type) { + case BINDER_TYPE_BINDER: + if (obj.binder) { + LOG_REFS("Parcel %p acquiring reference on local %p", who, obj.cookie); + static_cast(obj.cookie)->incStrong(who); + } + return; + case BINDER_TYPE_WEAK_BINDER: + if (obj.binder) + static_cast(obj.binder)->incWeak(who); + return; + case BINDER_TYPE_HANDLE: { + const sp b = proc->getStrongProxyForHandle(obj.handle); + if (b != NULL) { + LOG_REFS("Parcel %p acquiring reference on remote %p", who, b.get()); + b->incStrong(who); + } + return; + } + case BINDER_TYPE_WEAK_HANDLE: { + const wp b = proc->getWeakProxyForHandle(obj.handle); + if (b != NULL) b.get_refs()->incWeak(who); + return; + } + case BINDER_TYPE_FD: { + // intentionally blank -- nothing to do to acquire this, but we do + // recognize it as a legitimate object type. + return; + } + } + + LOGD("Invalid object type 0x%08lx", obj.type); +} + +void release_object(const sp& proc, + const flat_binder_object& obj, const void* who) +{ + switch (obj.type) { + case BINDER_TYPE_BINDER: + if (obj.binder) { + LOG_REFS("Parcel %p releasing reference on local %p", who, obj.cookie); + static_cast(obj.cookie)->decStrong(who); + } + return; + case BINDER_TYPE_WEAK_BINDER: + if (obj.binder) + static_cast(obj.binder)->decWeak(who); + return; + case BINDER_TYPE_HANDLE: { + const sp b = proc->getStrongProxyForHandle(obj.handle); + if (b != NULL) { + LOG_REFS("Parcel %p releasing reference on remote %p", who, b.get()); + b->decStrong(who); + } + return; + } + case BINDER_TYPE_WEAK_HANDLE: { + const wp b = proc->getWeakProxyForHandle(obj.handle); + if (b != NULL) b.get_refs()->decWeak(who); + return; + } + case BINDER_TYPE_FD: { + if (obj.cookie != (void*)0) close(obj.handle); + return; + } + } + + LOGE("Invalid object type 0x%08lx", obj.type); +} + +inline static status_t finish_flatten_binder( + const sp& binder, const flat_binder_object& flat, Parcel* out) +{ + return out->writeObject(flat, false); +} + +status_t flatten_binder(const sp& proc, + const sp& binder, Parcel* out) +{ + flat_binder_object obj; + + obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS; + if (binder != NULL) { + IBinder *local = binder->localBinder(); + if (!local) { + BpBinder *proxy = binder->remoteBinder(); + if (proxy == NULL) { + LOGE("null proxy"); + } + const int32_t handle = proxy ? proxy->handle() : 0; + obj.type = BINDER_TYPE_HANDLE; + obj.handle = handle; + obj.cookie = NULL; + } else { + obj.type = BINDER_TYPE_BINDER; + obj.binder = local->getWeakRefs(); + obj.cookie = local; + } + } else { + obj.type = BINDER_TYPE_BINDER; + obj.binder = NULL; + obj.cookie = NULL; + } + + return finish_flatten_binder(binder, obj, out); +} + +status_t flatten_binder(const sp& proc, + const wp& binder, Parcel* out) +{ + flat_binder_object obj; + + obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS; + if (binder != NULL) { + sp real = binder.promote(); + if (real != NULL) { + IBinder *local = real->localBinder(); + if (!local) { + BpBinder *proxy = real->remoteBinder(); + if (proxy == NULL) { + LOGE("null proxy"); + } + const int32_t handle = proxy ? proxy->handle() : 0; + obj.type = BINDER_TYPE_WEAK_HANDLE; + obj.handle = handle; + obj.cookie = NULL; + } else { + obj.type = BINDER_TYPE_WEAK_BINDER; + obj.binder = binder.get_refs(); + obj.cookie = binder.unsafe_get(); + } + return finish_flatten_binder(real, obj, out); + } + + // XXX How to deal? In order to flatten the given binder, + // we need to probe it for information, which requires a primary + // reference... but we don't have one. + // + // The OpenBinder implementation uses a dynamic_cast<> here, + // but we can't do that with the different reference counting + // implementation we are using. + LOGE("Unable to unflatten Binder weak reference!"); + obj.type = BINDER_TYPE_BINDER; + obj.binder = NULL; + obj.cookie = NULL; + return finish_flatten_binder(NULL, obj, out); + + } else { + obj.type = BINDER_TYPE_BINDER; + obj.binder = NULL; + obj.cookie = NULL; + return finish_flatten_binder(NULL, obj, out); + } +} + +inline static status_t finish_unflatten_binder( + BpBinder* proxy, const flat_binder_object& flat, const Parcel& in) +{ + return NO_ERROR; +} + +status_t unflatten_binder(const sp& proc, + const Parcel& in, sp* out) +{ + const flat_binder_object* flat = in.readObject(false); + + if (flat) { + switch (flat->type) { + case BINDER_TYPE_BINDER: + *out = static_cast(flat->cookie); + return finish_unflatten_binder(NULL, *flat, in); + case BINDER_TYPE_HANDLE: + *out = proc->getStrongProxyForHandle(flat->handle); + return finish_unflatten_binder( + static_cast(out->get()), *flat, in); + } + } + return BAD_TYPE; +} + +status_t unflatten_binder(const sp& proc, + const Parcel& in, wp* out) +{ + const flat_binder_object* flat = in.readObject(false); + + if (flat) { + switch (flat->type) { + case BINDER_TYPE_BINDER: + *out = static_cast(flat->cookie); + return finish_unflatten_binder(NULL, *flat, in); + case BINDER_TYPE_WEAK_BINDER: + if (flat->binder != NULL) { + out->set_object_and_refs( + static_cast(flat->cookie), + static_cast(flat->binder)); + } else { + *out = NULL; + } + return finish_unflatten_binder(NULL, *flat, in); + case BINDER_TYPE_HANDLE: + case BINDER_TYPE_WEAK_HANDLE: + *out = proc->getWeakProxyForHandle(flat->handle); + return finish_unflatten_binder( + static_cast(out->unsafe_get()), *flat, in); + } + } + return BAD_TYPE; +} + +// --------------------------------------------------------------------------- + +Parcel::Parcel() +{ + initState(); +} + +Parcel::~Parcel() +{ + freeDataNoInit(); +} + +const uint8_t* Parcel::data() const +{ + return mData; +} + +size_t Parcel::dataSize() const +{ + return (mDataSize > mDataPos ? mDataSize : mDataPos); +} + +size_t Parcel::dataAvail() const +{ + // TODO: decide what to do about the possibility that this can + // report an available-data size that exceeds a Java int's max + // positive value, causing havoc. Fortunately this will only + // happen if someone constructs a Parcel containing more than two + // gigabytes of data, which on typical phone hardware is simply + // not possible. + return dataSize() - dataPosition(); +} + +size_t Parcel::dataPosition() const +{ + return mDataPos; +} + +size_t Parcel::dataCapacity() const +{ + return mDataCapacity; +} + +status_t Parcel::setDataSize(size_t size) +{ + status_t err; + err = continueWrite(size); + if (err == NO_ERROR) { + mDataSize = size; + LOGV("setDataSize Setting data size of %p to %d\n", this, mDataSize); + } + return err; +} + +void Parcel::setDataPosition(size_t pos) const +{ + mDataPos = pos; + mNextObjectHint = 0; +} + +status_t Parcel::setDataCapacity(size_t size) +{ + if (size > mDataSize) return continueWrite(size); + return NO_ERROR; +} + +status_t Parcel::setData(const uint8_t* buffer, size_t len) +{ + status_t err = restartWrite(len); + if (err == NO_ERROR) { + memcpy(const_cast(data()), buffer, len); + mDataSize = len; + mFdsKnown = false; + } + return err; +} + +status_t Parcel::appendFrom(Parcel *parcel, size_t offset, size_t len) +{ + const sp proc(ProcessState::self()); + status_t err; + uint8_t *data = parcel->mData; + size_t *objects = parcel->mObjects; + size_t size = parcel->mObjectsSize; + int startPos = mDataPos; + int firstIndex = -1, lastIndex = -2; + + if (len == 0) { + return NO_ERROR; + } + + // range checks against the source parcel size + if ((offset > parcel->mDataSize) + || (len > parcel->mDataSize) + || (offset + len > parcel->mDataSize)) { + return BAD_VALUE; + } + + // Count objects in range + for (int i = 0; i < (int) size; i++) { + size_t off = objects[i]; + if ((off >= offset) && (off < offset + len)) { + if (firstIndex == -1) { + firstIndex = i; + } + lastIndex = i; + } + } + int numObjects = lastIndex - firstIndex + 1; + + // grow data + err = growData(len); + if (err != NO_ERROR) { + return err; + } + + // append data + memcpy(mData + mDataPos, data + offset, len); + mDataPos += len; + mDataSize += len; + + if (numObjects > 0) { + // grow objects + if (mObjectsCapacity < mObjectsSize + numObjects) { + int newSize = ((mObjectsSize + numObjects)*3)/2; + size_t *objects = + (size_t*)realloc(mObjects, newSize*sizeof(size_t)); + if (objects == (size_t*)0) { + return NO_MEMORY; + } + mObjects = objects; + mObjectsCapacity = newSize; + } + + // append and acquire objects + int idx = mObjectsSize; + for (int i = firstIndex; i <= lastIndex; i++) { + size_t off = objects[i] - offset + startPos; + mObjects[idx++] = off; + mObjectsSize++; + + const flat_binder_object* flat + = reinterpret_cast(mData + off); + acquire_object(proc, *flat, this); + + // take note if the object is a file descriptor + if (flat->type == BINDER_TYPE_FD) { + mHasFds = mFdsKnown = true; + } + } + } + + return NO_ERROR; +} + +bool Parcel::hasFileDescriptors() const +{ + if (!mFdsKnown) { + scanForFds(); + } + return mHasFds; +} + +status_t Parcel::writeInterfaceToken(const String16& interface) +{ + // currently the interface identification token is just its name as a string + return writeString16(interface); +} + +bool Parcel::enforceInterface(const String16& interface) const +{ + String16 str = readString16(); + if (str == interface) { + return true; + } else { + LOGW("**** enforceInterface() expected '%s' but read '%s'\n", + String8(interface).string(), String8(str).string()); + return false; + } +} + +const size_t* Parcel::objects() const +{ + return mObjects; +} + +size_t Parcel::objectsCount() const +{ + return mObjectsSize; +} + +status_t Parcel::errorCheck() const +{ + return mError; +} + +void Parcel::setError(status_t err) +{ + mError = err; +} + +status_t Parcel::finishWrite(size_t len) +{ + //printf("Finish write of %d\n", len); + mDataPos += len; + LOGV("finishWrite Setting data pos of %p to %d\n", this, mDataPos); + if (mDataPos > mDataSize) { + mDataSize = mDataPos; + LOGV("finishWrite Setting data size of %p to %d\n", this, mDataSize); + } + //printf("New pos=%d, size=%d\n", mDataPos, mDataSize); + return NO_ERROR; +} + +status_t Parcel::writeUnpadded(const void* data, size_t len) +{ + size_t end = mDataPos + len; + if (end < mDataPos) { + // integer overflow + return BAD_VALUE; + } + + if (end <= mDataCapacity) { +restart_write: + memcpy(mData+mDataPos, data, len); + return finishWrite(len); + } + + status_t err = growData(len); + if (err == NO_ERROR) goto restart_write; + return err; +} + +status_t Parcel::write(const void* data, size_t len) +{ + void* const d = writeInplace(len); + if (d) { + memcpy(d, data, len); + return NO_ERROR; + } + return mError; +} + +void* Parcel::writeInplace(size_t len) +{ + const size_t padded = PAD_SIZE(len); + + // sanity check for integer overflow + if (mDataPos+padded < mDataPos) { + return NULL; + } + + if ((mDataPos+padded) <= mDataCapacity) { +restart_write: + //printf("Writing %ld bytes, padded to %ld\n", len, padded); + uint8_t* const data = mData+mDataPos; + + // Need to pad at end? + if (padded != len) { +#if BYTE_ORDER == BIG_ENDIAN + static const uint32_t mask[4] = { + 0x00000000, 0xffffff00, 0xffff0000, 0xff000000 + }; +#endif +#if BYTE_ORDER == LITTLE_ENDIAN + static const uint32_t mask[4] = { + 0x00000000, 0x00ffffff, 0x0000ffff, 0x000000ff + }; +#endif + //printf("Applying pad mask: %p to %p\n", (void*)mask[padded-len], + // *reinterpret_cast(data+padded-4)); + *reinterpret_cast(data+padded-4) &= mask[padded-len]; + } + + finishWrite(padded); + return data; + } + + status_t err = growData(padded); + if (err == NO_ERROR) goto restart_write; + return NULL; +} + +status_t Parcel::writeInt32(int32_t val) +{ + if ((mDataPos+sizeof(val)) <= mDataCapacity) { +restart_write: + *reinterpret_cast(mData+mDataPos) = val; + return finishWrite(sizeof(val)); + } + + status_t err = growData(sizeof(val)); + if (err == NO_ERROR) goto restart_write; + return err; +} + +status_t Parcel::writeInt64(int64_t val) +{ + if ((mDataPos+sizeof(val)) <= mDataCapacity) { +restart_write: + *reinterpret_cast(mData+mDataPos) = val; + return finishWrite(sizeof(val)); + } + + status_t err = growData(sizeof(val)); + if (err == NO_ERROR) goto restart_write; + return err; +} + +status_t Parcel::writeFloat(float val) +{ + if ((mDataPos+sizeof(val)) <= mDataCapacity) { +restart_write: + *reinterpret_cast(mData+mDataPos) = val; + return finishWrite(sizeof(val)); + } + + status_t err = growData(sizeof(val)); + if (err == NO_ERROR) goto restart_write; + return err; +} + +status_t Parcel::writeDouble(double val) +{ + if ((mDataPos+sizeof(val)) <= mDataCapacity) { +restart_write: + *reinterpret_cast(mData+mDataPos) = val; + return finishWrite(sizeof(val)); + } + + status_t err = growData(sizeof(val)); + if (err == NO_ERROR) goto restart_write; + return err; +} + +status_t Parcel::writeCString(const char* str) +{ + return write(str, strlen(str)+1); +} + +status_t Parcel::writeString8(const String8& str) +{ + status_t err = writeInt32(str.bytes()); + if (err == NO_ERROR) { + err = write(str.string(), str.bytes()+1); + } + return err; +} + +status_t Parcel::writeString16(const String16& str) +{ + return writeString16(str.string(), str.size()); +} + +status_t Parcel::writeString16(const char16_t* str, size_t len) +{ + if (str == NULL) return writeInt32(-1); + + status_t err = writeInt32(len); + if (err == NO_ERROR) { + len *= sizeof(char16_t); + uint8_t* data = (uint8_t*)writeInplace(len+sizeof(char16_t)); + if (data) { + memcpy(data, str, len); + *reinterpret_cast(data+len) = 0; + return NO_ERROR; + } + err = mError; + } + return err; +} + +status_t Parcel::writeStrongBinder(const sp& val) +{ + return flatten_binder(ProcessState::self(), val, this); +} + +status_t Parcel::writeWeakBinder(const wp& val) +{ + return flatten_binder(ProcessState::self(), val, this); +} + +status_t Parcel::writeFileDescriptor(int fd) +{ + flat_binder_object obj; + obj.type = BINDER_TYPE_FD; + obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS; + obj.handle = fd; + obj.cookie = (void*)0; + return writeObject(obj, true); +} + +status_t Parcel::writeDupFileDescriptor(int fd) +{ + flat_binder_object obj; + obj.type = BINDER_TYPE_FD; + obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS; + obj.handle = dup(fd); + obj.cookie = (void*)1; + return writeObject(obj, true); +} + +status_t Parcel::writeObject(const flat_binder_object& val, bool nullMetaData) +{ + const bool enoughData = (mDataPos+sizeof(val)) <= mDataCapacity; + const bool enoughObjects = mObjectsSize < mObjectsCapacity; + if (enoughData && enoughObjects) { +restart_write: + *reinterpret_cast(mData+mDataPos) = val; + + // Need to write meta-data? + if (nullMetaData || val.binder != NULL) { + mObjects[mObjectsSize] = mDataPos; + acquire_object(ProcessState::self(), val, this); + mObjectsSize++; + } + + // remember if it's a file descriptor + if (val.type == BINDER_TYPE_FD) { + mHasFds = mFdsKnown = true; + } + + return finishWrite(sizeof(flat_binder_object)); + } + + if (!enoughData) { + const status_t err = growData(sizeof(val)); + if (err != NO_ERROR) return err; + } + if (!enoughObjects) { + size_t newSize = ((mObjectsSize+2)*3)/2; + size_t* objects = (size_t*)realloc(mObjects, newSize*sizeof(size_t)); + if (objects == NULL) return NO_MEMORY; + mObjects = objects; + mObjectsCapacity = newSize; + } + + goto restart_write; +} + + +void Parcel::remove(size_t start, size_t amt) +{ + LOG_ALWAYS_FATAL("Parcel::remove() not yet implemented!"); +} + +status_t Parcel::read(void* outData, size_t len) const +{ + if ((mDataPos+PAD_SIZE(len)) >= mDataPos && (mDataPos+PAD_SIZE(len)) <= mDataSize) { + memcpy(outData, mData+mDataPos, len); + mDataPos += PAD_SIZE(len); + LOGV("read Setting data pos of %p to %d\n", this, mDataPos); + return NO_ERROR; + } + return NOT_ENOUGH_DATA; +} + +const void* Parcel::readInplace(size_t len) const +{ + if ((mDataPos+PAD_SIZE(len)) >= mDataPos && (mDataPos+PAD_SIZE(len)) <= mDataSize) { + const void* data = mData+mDataPos; + mDataPos += PAD_SIZE(len); + LOGV("readInplace Setting data pos of %p to %d\n", this, mDataPos); + return data; + } + return NULL; +} + +status_t Parcel::readInt32(int32_t *pArg) const +{ + if ((mDataPos+sizeof(int32_t)) <= mDataSize) { + const void* data = mData+mDataPos; + mDataPos += sizeof(int32_t); + *pArg = *reinterpret_cast(data); + return NO_ERROR; + } else { + return NOT_ENOUGH_DATA; + } +} + +int32_t Parcel::readInt32() const +{ + if ((mDataPos+sizeof(int32_t)) <= mDataSize) { + const void* data = mData+mDataPos; + mDataPos += sizeof(int32_t); + LOGV("readInt32 Setting data pos of %p to %d\n", this, mDataPos); + return *reinterpret_cast(data); + } + return 0; +} + + +status_t Parcel::readInt64(int64_t *pArg) const +{ + if ((mDataPos+sizeof(int64_t)) <= mDataSize) { + const void* data = mData+mDataPos; + mDataPos += sizeof(int64_t); + *pArg = *reinterpret_cast(data); + LOGV("readInt64 Setting data pos of %p to %d\n", this, mDataPos); + return NO_ERROR; + } else { + return NOT_ENOUGH_DATA; + } +} + + +int64_t Parcel::readInt64() const +{ + if ((mDataPos+sizeof(int64_t)) <= mDataSize) { + const void* data = mData+mDataPos; + mDataPos += sizeof(int64_t); + LOGV("readInt64 Setting data pos of %p to %d\n", this, mDataPos); + return *reinterpret_cast(data); + } + return 0; +} + +status_t Parcel::readFloat(float *pArg) const +{ + if ((mDataPos+sizeof(float)) <= mDataSize) { + const void* data = mData+mDataPos; + mDataPos += sizeof(float); + LOGV("readFloat Setting data pos of %p to %d\n", this, mDataPos); + *pArg = *reinterpret_cast(data); + return NO_ERROR; + } else { + return NOT_ENOUGH_DATA; + } +} + + +float Parcel::readFloat() const +{ + if ((mDataPos+sizeof(float)) <= mDataSize) { + const void* data = mData+mDataPos; + mDataPos += sizeof(float); + LOGV("readFloat Setting data pos of %p to %d\n", this, mDataPos); + return *reinterpret_cast(data); + } + return 0; +} + +status_t Parcel::readDouble(double *pArg) const +{ + if ((mDataPos+sizeof(double)) <= mDataSize) { + const void* data = mData+mDataPos; + mDataPos += sizeof(double); + LOGV("readDouble Setting data pos of %p to %d\n", this, mDataPos); + *pArg = *reinterpret_cast(data); + return NO_ERROR; + } else { + return NOT_ENOUGH_DATA; + } +} + + +double Parcel::readDouble() const +{ + if ((mDataPos+sizeof(double)) <= mDataSize) { + const void* data = mData+mDataPos; + mDataPos += sizeof(double); + LOGV("readDouble Setting data pos of %p to %d\n", this, mDataPos); + return *reinterpret_cast(data); + } + return 0; +} + + +const char* Parcel::readCString() const +{ + const size_t avail = mDataSize-mDataPos; + if (avail > 0) { + const char* str = reinterpret_cast(mData+mDataPos); + // is the string's trailing NUL within the parcel's valid bounds? + const char* eos = reinterpret_cast(memchr(str, 0, avail)); + if (eos) { + const size_t len = eos - str; + mDataPos += PAD_SIZE(len+1); + LOGV("readCString Setting data pos of %p to %d\n", this, mDataPos); + return str; + } + } + return NULL; +} + +String8 Parcel::readString8() const +{ + int32_t size = readInt32(); + // watch for potential int overflow adding 1 for trailing NUL + if (size > 0 && size < INT32_MAX) { + const char* str = (const char*)readInplace(size+1); + if (str) return String8(str, size); + } + return String8(); +} + +String16 Parcel::readString16() const +{ + size_t len; + const char16_t* str = readString16Inplace(&len); + if (str) return String16(str, len); + LOGE("Reading a NULL string not supported here."); + return String16(); +} + +const char16_t* Parcel::readString16Inplace(size_t* outLen) const +{ + int32_t size = readInt32(); + // watch for potential int overflow from size+1 + if (size >= 0 && size < INT32_MAX) { + *outLen = size; + const char16_t* str = (const char16_t*)readInplace((size+1)*sizeof(char16_t)); + if (str != NULL) { + return str; + } + } + *outLen = 0; + return NULL; +} + +sp Parcel::readStrongBinder() const +{ + sp val; + unflatten_binder(ProcessState::self(), *this, &val); + return val; +} + +wp Parcel::readWeakBinder() const +{ + wp val; + unflatten_binder(ProcessState::self(), *this, &val); + return val; +} + +int Parcel::readFileDescriptor() const +{ + const flat_binder_object* flat = readObject(true); + if (flat) { + switch (flat->type) { + case BINDER_TYPE_FD: + //LOGI("Returning file descriptor %ld from parcel %p\n", flat->handle, this); + return flat->handle; + } + } + return BAD_TYPE; +} + +const flat_binder_object* Parcel::readObject(bool nullMetaData) const +{ + const size_t DPOS = mDataPos; + if ((DPOS+sizeof(flat_binder_object)) <= mDataSize) { + const flat_binder_object* obj + = reinterpret_cast(mData+DPOS); + mDataPos = DPOS + sizeof(flat_binder_object); + if (!nullMetaData && (obj->cookie == NULL && obj->binder == NULL)) { + // When transfering a NULL object, we don't write it into + // the object list, so we don't want to check for it when + // reading. + LOGV("readObject Setting data pos of %p to %d\n", this, mDataPos); + return obj; + } + + // Ensure that this object is valid... + size_t* const OBJS = mObjects; + const size_t N = mObjectsSize; + size_t opos = mNextObjectHint; + + if (N > 0) { + LOGV("Parcel %p looking for obj at %d, hint=%d\n", + this, DPOS, opos); + + // Start at the current hint position, looking for an object at + // the current data position. + if (opos < N) { + while (opos < (N-1) && OBJS[opos] < DPOS) { + opos++; + } + } else { + opos = N-1; + } + if (OBJS[opos] == DPOS) { + // Found it! + LOGV("Parcel found obj %d at index %d with forward search", + this, DPOS, opos); + mNextObjectHint = opos+1; + LOGV("readObject Setting data pos of %p to %d\n", this, mDataPos); + return obj; + } + + // Look backwards for it... + while (opos > 0 && OBJS[opos] > DPOS) { + opos--; + } + if (OBJS[opos] == DPOS) { + // Found it! + LOGV("Parcel found obj %d at index %d with backward search", + this, DPOS, opos); + mNextObjectHint = opos+1; + LOGV("readObject Setting data pos of %p to %d\n", this, mDataPos); + return obj; + } + } + LOGW("Attempt to read object from Parcel %p at offset %d that is not in the object list", + this, DPOS); + } + return NULL; +} + +void Parcel::closeFileDescriptors() +{ + size_t i = mObjectsSize; + if (i > 0) { + //LOGI("Closing file descriptors for %d objects...", mObjectsSize); + } + while (i > 0) { + i--; + const flat_binder_object* flat + = reinterpret_cast(mData+mObjects[i]); + if (flat->type == BINDER_TYPE_FD) { + //LOGI("Closing fd: %ld\n", flat->handle); + close(flat->handle); + } + } +} + +const uint8_t* Parcel::ipcData() const +{ + return mData; +} + +size_t Parcel::ipcDataSize() const +{ + return (mDataSize > mDataPos ? mDataSize : mDataPos); +} + +const size_t* Parcel::ipcObjects() const +{ + return mObjects; +} + +size_t Parcel::ipcObjectsCount() const +{ + return mObjectsSize; +} + +void Parcel::ipcSetDataReference(const uint8_t* data, size_t dataSize, + const size_t* objects, size_t objectsCount, release_func relFunc, void* relCookie) +{ + freeDataNoInit(); + mError = NO_ERROR; + mData = const_cast(data); + mDataSize = mDataCapacity = dataSize; + //LOGI("setDataReference Setting data size of %p to %lu (pid=%d)\n", this, mDataSize, getpid()); + mDataPos = 0; + LOGV("setDataReference Setting data pos of %p to %d\n", this, mDataPos); + mObjects = const_cast(objects); + mObjectsSize = mObjectsCapacity = objectsCount; + mNextObjectHint = 0; + mOwner = relFunc; + mOwnerCookie = relCookie; + scanForFds(); +} + +void Parcel::print(TextOutput& to, uint32_t flags) const +{ + to << "Parcel("; + + if (errorCheck() != NO_ERROR) { + const status_t err = errorCheck(); + to << "Error: " << (void*)err << " \"" << strerror(-err) << "\""; + } else if (dataSize() > 0) { + const uint8_t* DATA = data(); + to << indent << HexDump(DATA, dataSize()) << dedent; + const size_t* OBJS = objects(); + const size_t N = objectsCount(); + for (size_t i=0; i(DATA+OBJS[i]); + to << endl << "Object #" << i << " @ " << (void*)OBJS[i] << ": " + << TypeCode(flat->type & 0x7f7f7f00) + << " = " << flat->binder; + } + } else { + to << "NULL"; + } + + to << ")"; +} + +void Parcel::releaseObjects() +{ + const sp proc(ProcessState::self()); + size_t i = mObjectsSize; + uint8_t* const data = mData; + size_t* const objects = mObjects; + while (i > 0) { + i--; + const flat_binder_object* flat + = reinterpret_cast(data+objects[i]); + release_object(proc, *flat, this); + } +} + +void Parcel::acquireObjects() +{ + const sp proc(ProcessState::self()); + size_t i = mObjectsSize; + uint8_t* const data = mData; + size_t* const objects = mObjects; + while (i > 0) { + i--; + const flat_binder_object* flat + = reinterpret_cast(data+objects[i]); + acquire_object(proc, *flat, this); + } +} + +void Parcel::freeData() +{ + freeDataNoInit(); + initState(); +} + +void Parcel::freeDataNoInit() +{ + if (mOwner) { + //LOGI("Freeing data ref of %p (pid=%d)\n", this, getpid()); + mOwner(this, mData, mDataSize, mObjects, mObjectsSize, mOwnerCookie); + } else { + releaseObjects(); + if (mData) free(mData); + if (mObjects) free(mObjects); + } +} + +status_t Parcel::growData(size_t len) +{ + size_t newSize = ((mDataSize+len)*3)/2; + return (newSize <= mDataSize) + ? (status_t) NO_MEMORY + : continueWrite(newSize); +} + +status_t Parcel::restartWrite(size_t desired) +{ + if (mOwner) { + freeData(); + return continueWrite(desired); + } + + uint8_t* data = (uint8_t*)realloc(mData, desired); + if (!data && desired > mDataCapacity) { + mError = NO_MEMORY; + return NO_MEMORY; + } + + releaseObjects(); + + if (data) { + mData = data; + mDataCapacity = desired; + } + + mDataSize = mDataPos = 0; + LOGV("restartWrite Setting data size of %p to %d\n", this, mDataSize); + LOGV("restartWrite Setting data pos of %p to %d\n", this, mDataPos); + + free(mObjects); + mObjects = NULL; + mObjectsSize = mObjectsCapacity = 0; + mNextObjectHint = 0; + mHasFds = false; + mFdsKnown = true; + + return NO_ERROR; +} + +status_t Parcel::continueWrite(size_t desired) +{ + // If shrinking, first adjust for any objects that appear + // after the new data size. + size_t objectsSize = mObjectsSize; + if (desired < mDataSize) { + if (desired == 0) { + objectsSize = 0; + } else { + while (objectsSize > 0) { + if (mObjects[objectsSize-1] < desired) + break; + objectsSize--; + } + } + } + + if (mOwner) { + // If the size is going to zero, just release the owner's data. + if (desired == 0) { + freeData(); + return NO_ERROR; + } + + // If there is a different owner, we need to take + // posession. + uint8_t* data = (uint8_t*)malloc(desired); + if (!data) { + mError = NO_MEMORY; + return NO_MEMORY; + } + size_t* objects = NULL; + + if (objectsSize) { + objects = (size_t*)malloc(objectsSize*sizeof(size_t)); + if (!objects) { + mError = NO_MEMORY; + return NO_MEMORY; + } + + // Little hack to only acquire references on objects + // we will be keeping. + size_t oldObjectsSize = mObjectsSize; + mObjectsSize = objectsSize; + acquireObjects(); + mObjectsSize = oldObjectsSize; + } + + if (mData) { + memcpy(data, mData, mDataSize < desired ? mDataSize : desired); + } + if (objects && mObjects) { + memcpy(objects, mObjects, objectsSize*sizeof(size_t)); + } + //LOGI("Freeing data ref of %p (pid=%d)\n", this, getpid()); + mOwner(this, mData, mDataSize, mObjects, mObjectsSize, mOwnerCookie); + mOwner = NULL; + + mData = data; + mObjects = objects; + mDataSize = (mDataSize < desired) ? mDataSize : desired; + LOGV("continueWrite Setting data size of %p to %d\n", this, mDataSize); + mDataCapacity = desired; + mObjectsSize = mObjectsCapacity = objectsSize; + mNextObjectHint = 0; + + } else if (mData) { + if (objectsSize < mObjectsSize) { + // Need to release refs on any objects we are dropping. + const sp proc(ProcessState::self()); + for (size_t i=objectsSize; i(mData+mObjects[i]); + if (flat->type == BINDER_TYPE_FD) { + // will need to rescan because we may have lopped off the only FDs + mFdsKnown = false; + } + release_object(proc, *flat, this); + } + size_t* objects = + (size_t*)realloc(mObjects, objectsSize*sizeof(size_t)); + if (objects) { + mObjects = objects; + } + mObjectsSize = objectsSize; + mNextObjectHint = 0; + } + + // We own the data, so we can just do a realloc(). + if (desired > mDataCapacity) { + uint8_t* data = (uint8_t*)realloc(mData, desired); + if (data) { + mData = data; + mDataCapacity = desired; + } else if (desired > mDataCapacity) { + mError = NO_MEMORY; + return NO_MEMORY; + } + } else { + mDataSize = desired; + LOGV("continueWrite Setting data size of %p to %d\n", this, mDataSize); + if (mDataPos > desired) { + mDataPos = desired; + LOGV("continueWrite Setting data pos of %p to %d\n", this, mDataPos); + } + } + + } else { + // This is the first data. Easy! + uint8_t* data = (uint8_t*)malloc(desired); + if (!data) { + mError = NO_MEMORY; + return NO_MEMORY; + } + + if(!(mDataCapacity == 0 && mObjects == NULL + && mObjectsCapacity == 0)) { + LOGE("continueWrite: %d/%p/%d/%d", mDataCapacity, mObjects, mObjectsCapacity, desired); + } + + mData = data; + mDataSize = mDataPos = 0; + LOGV("continueWrite Setting data size of %p to %d\n", this, mDataSize); + LOGV("continueWrite Setting data pos of %p to %d\n", this, mDataPos); + mDataCapacity = desired; + } + + return NO_ERROR; +} + +void Parcel::initState() +{ + mError = NO_ERROR; + mData = 0; + mDataSize = 0; + mDataCapacity = 0; + mDataPos = 0; + LOGV("initState Setting data size of %p to %d\n", this, mDataSize); + LOGV("initState Setting data pos of %p to %d\n", this, mDataPos); + mObjects = NULL; + mObjectsSize = 0; + mObjectsCapacity = 0; + mNextObjectHint = 0; + mHasFds = false; + mFdsKnown = true; + mOwner = NULL; +} + +void Parcel::scanForFds() const +{ + bool hasFds = false; + for (size_t i=0; i(mData + mObjects[i]); + if (flat->type == BINDER_TYPE_FD) { + hasFds = true; + break; + } + } + mHasFds = hasFds; + mFdsKnown = true; +} + +}; // namespace android diff --git a/libs/utils/Pipe.cpp b/libs/utils/Pipe.cpp new file mode 100644 index 000000000..613906bed --- /dev/null +++ b/libs/utils/Pipe.cpp @@ -0,0 +1,465 @@ +/* + * Copyright (C) 2005 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. + */ + +// +// Unidirectional pipe. +// + +#include +#include + +#if defined(HAVE_WIN32_IPC) +# include +#else +# include +# include +# include +#endif + +#include +#include +#include +#include + +using namespace android; + +const unsigned long kInvalidHandle = (unsigned long) -1; + + +/* + * Constructor. Do little. + */ +Pipe::Pipe(void) + : mReadNonBlocking(false), mReadHandle(kInvalidHandle), + mWriteHandle(kInvalidHandle) +{ +} + +/* + * Destructor. Use the system-appropriate close call. + */ +Pipe::~Pipe(void) +{ +#if defined(HAVE_WIN32_IPC) + if (mReadHandle != kInvalidHandle) { + if (!CloseHandle((HANDLE)mReadHandle)) + LOG(LOG_WARN, "pipe", "failed closing read handle (%ld)\n", + mReadHandle); + } + if (mWriteHandle != kInvalidHandle) { + FlushFileBuffers((HANDLE)mWriteHandle); + if (!CloseHandle((HANDLE)mWriteHandle)) + LOG(LOG_WARN, "pipe", "failed closing write handle (%ld)\n", + mWriteHandle); + } +#else + if (mReadHandle != kInvalidHandle) { + if (close((int) mReadHandle) != 0) + LOG(LOG_WARN, "pipe", "failed closing read fd (%d)\n", + (int) mReadHandle); + } + if (mWriteHandle != kInvalidHandle) { + if (close((int) mWriteHandle) != 0) + LOG(LOG_WARN, "pipe", "failed closing write fd (%d)\n", + (int) mWriteHandle); + } +#endif +} + +/* + * Create the pipe. + * + * Use the POSIX stuff for everything but Windows. + */ +bool Pipe::create(void) +{ + assert(mReadHandle == kInvalidHandle); + assert(mWriteHandle == kInvalidHandle); + +#if defined(HAVE_WIN32_IPC) + /* we use this across processes, so they need to be inheritable */ + HANDLE handles[2]; + SECURITY_ATTRIBUTES saAttr; + + saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); + saAttr.bInheritHandle = TRUE; + saAttr.lpSecurityDescriptor = NULL; + + if (!CreatePipe(&handles[0], &handles[1], &saAttr, 0)) { + LOG(LOG_ERROR, "pipe", "unable to create pipe\n"); + return false; + } + mReadHandle = (unsigned long) handles[0]; + mWriteHandle = (unsigned long) handles[1]; + return true; +#else + int fds[2]; + + if (pipe(fds) != 0) { + LOG(LOG_ERROR, "pipe", "unable to create pipe\n"); + return false; + } + mReadHandle = fds[0]; + mWriteHandle = fds[1]; + return true; +#endif +} + +/* + * Create a "half pipe". Please, no Segway riding. + */ +bool Pipe::createReader(unsigned long handle) +{ + mReadHandle = handle; + assert(mWriteHandle == kInvalidHandle); + return true; +} + +/* + * Create a "half pipe" for writing. + */ +bool Pipe::createWriter(unsigned long handle) +{ + mWriteHandle = handle; + assert(mReadHandle == kInvalidHandle); + return true; +} + +/* + * Return "true" if create() has been called successfully. + */ +bool Pipe::isCreated(void) +{ + // one or the other should be open + return (mReadHandle != kInvalidHandle || mWriteHandle != kInvalidHandle); +} + + +/* + * Read data from the pipe. + * + * For Linux and Darwin, just call read(). For Windows, implement + * non-blocking reads by calling PeekNamedPipe first. + */ +int Pipe::read(void* buf, int count) +{ + assert(mReadHandle != kInvalidHandle); + +#if defined(HAVE_WIN32_IPC) + DWORD totalBytesAvail = count; + DWORD bytesRead; + + if (mReadNonBlocking) { + // use PeekNamedPipe to adjust read count expectations + if (!PeekNamedPipe((HANDLE) mReadHandle, NULL, 0, NULL, + &totalBytesAvail, NULL)) + { + LOG(LOG_ERROR, "pipe", "PeekNamedPipe failed\n"); + return -1; + } + + if (totalBytesAvail == 0) + return 0; + } + + if (!ReadFile((HANDLE) mReadHandle, buf, totalBytesAvail, &bytesRead, + NULL)) + { + DWORD err = GetLastError(); + if (err == ERROR_HANDLE_EOF || err == ERROR_BROKEN_PIPE) + return 0; + LOG(LOG_ERROR, "pipe", "ReadFile failed (err=%ld)\n", err); + return -1; + } + + return (int) bytesRead; +#else + int cc; + cc = ::read(mReadHandle, buf, count); + if (cc < 0 && errno == EAGAIN) + return 0; + return cc; +#endif +} + +/* + * Write data to the pipe. + * + * POSIX systems are trivial, Windows uses a different call and doesn't + * handle non-blocking writes. + * + * If we add non-blocking support here, we probably want to make it an + * all-or-nothing write. + * + * DO NOT use LOG() here, we could be writing a log message. + */ +int Pipe::write(const void* buf, int count) +{ + assert(mWriteHandle != kInvalidHandle); + +#if defined(HAVE_WIN32_IPC) + DWORD bytesWritten; + + if (mWriteNonBlocking) { + // BUG: can't use PeekNamedPipe() to get the amount of space + // left. Looks like we need to use "overlapped I/O" functions. + // I just don't care that much. + } + + if (!WriteFile((HANDLE) mWriteHandle, buf, count, &bytesWritten, NULL)) { + // can't LOG, use stderr + fprintf(stderr, "WriteFile failed (err=%ld)\n", GetLastError()); + return -1; + } + + return (int) bytesWritten; +#else + int cc; + cc = ::write(mWriteHandle, buf, count); + if (cc < 0 && errno == EAGAIN) + return 0; + return cc; +#endif +} + +/* + * Figure out if there is data available on the read fd. + * + * We return "true" on error because we want the caller to try to read + * from the pipe. They'll notice the read failure and do something + * appropriate. + */ +bool Pipe::readReady(void) +{ + assert(mReadHandle != kInvalidHandle); + +#if defined(HAVE_WIN32_IPC) + DWORD totalBytesAvail; + + if (!PeekNamedPipe((HANDLE) mReadHandle, NULL, 0, NULL, + &totalBytesAvail, NULL)) + { + LOG(LOG_ERROR, "pipe", "PeekNamedPipe failed\n"); + return true; + } + + return (totalBytesAvail != 0); +#else + errno = 0; + fd_set readfds; + struct timeval tv = { 0, 0 }; + int cc; + + FD_ZERO(&readfds); + FD_SET(mReadHandle, &readfds); + + cc = select(mReadHandle+1, &readfds, NULL, NULL, &tv); + if (cc < 0) { + LOG(LOG_ERROR, "pipe", "select() failed\n"); + return true; + } else if (cc == 0) { + /* timed out, nothing available */ + return false; + } else if (cc == 1) { + /* our fd is ready */ + return true; + } else { + LOG(LOG_ERROR, "pipe", "HUH? select() returned > 1\n"); + return true; + } +#endif +} + +/* + * Enable or disable non-blocking mode for the read descriptor. + * + * NOTE: the calls succeed under Mac OS X, but the pipe doesn't appear to + * actually be in non-blocking mode. If this matters -- i.e. you're not + * using a select() call -- put a call to readReady() in front of the + * ::read() call, with a PIPE_NONBLOCK_BROKEN #ifdef in the Makefile for + * Darwin. + */ +bool Pipe::setReadNonBlocking(bool val) +{ + assert(mReadHandle != kInvalidHandle); + +#if defined(HAVE_WIN32_IPC) + // nothing to do +#else + int flags; + + if (fcntl(mReadHandle, F_GETFL, &flags) == -1) { + LOG(LOG_ERROR, "pipe", "couldn't get flags for pipe read fd\n"); + return false; + } + if (val) + flags |= O_NONBLOCK; + else + flags &= ~(O_NONBLOCK); + if (fcntl(mReadHandle, F_SETFL, &flags) == -1) { + LOG(LOG_ERROR, "pipe", "couldn't set flags for pipe read fd\n"); + return false; + } +#endif + + mReadNonBlocking = val; + return true; +} + +/* + * Enable or disable non-blocking mode for the write descriptor. + * + * As with setReadNonBlocking(), this does not work on the Mac. + */ +bool Pipe::setWriteNonBlocking(bool val) +{ + assert(mWriteHandle != kInvalidHandle); + +#if defined(HAVE_WIN32_IPC) + // nothing to do +#else + int flags; + + if (fcntl(mWriteHandle, F_GETFL, &flags) == -1) { + LOG(LOG_WARN, "pipe", + "Warning: couldn't get flags for pipe write fd (errno=%d)\n", + errno); + return false; + } + if (val) + flags |= O_NONBLOCK; + else + flags &= ~(O_NONBLOCK); + if (fcntl(mWriteHandle, F_SETFL, &flags) == -1) { + LOG(LOG_WARN, "pipe", + "Warning: couldn't set flags for pipe write fd (errno=%d)\n", + errno); + return false; + } +#endif + + mWriteNonBlocking = val; + return true; +} + +/* + * Specify whether a file descriptor can be inherited by a child process. + * Under Linux this means setting the close-on-exec flag, under Windows + * this is SetHandleInformation(HANDLE_FLAG_INHERIT). + */ +bool Pipe::disallowReadInherit(void) +{ + if (mReadHandle == kInvalidHandle) + return false; + +#if defined(HAVE_WIN32_IPC) + if (SetHandleInformation((HANDLE) mReadHandle, HANDLE_FLAG_INHERIT, 0) == 0) + return false; +#else + if (fcntl((int) mReadHandle, F_SETFD, FD_CLOEXEC) != 0) + return false; +#endif + return true; +} +bool Pipe::disallowWriteInherit(void) +{ + if (mWriteHandle == kInvalidHandle) + return false; + +#if defined(HAVE_WIN32_IPC) + if (SetHandleInformation((HANDLE) mWriteHandle, HANDLE_FLAG_INHERIT, 0) == 0) + return false; +#else + if (fcntl((int) mWriteHandle, F_SETFD, FD_CLOEXEC) != 0) + return false; +#endif + return true; +} + +/* + * Close read descriptor. + */ +bool Pipe::closeRead(void) +{ + if (mReadHandle == kInvalidHandle) + return false; + +#if defined(HAVE_WIN32_IPC) + if (mReadHandle != kInvalidHandle) { + if (!CloseHandle((HANDLE)mReadHandle)) { + LOG(LOG_WARN, "pipe", "failed closing read handle\n"); + return false; + } + } +#else + if (mReadHandle != kInvalidHandle) { + if (close((int) mReadHandle) != 0) { + LOG(LOG_WARN, "pipe", "failed closing read fd\n"); + return false; + } + } +#endif + mReadHandle = kInvalidHandle; + return true; +} + +/* + * Close write descriptor. + */ +bool Pipe::closeWrite(void) +{ + if (mWriteHandle == kInvalidHandle) + return false; + +#if defined(HAVE_WIN32_IPC) + if (mWriteHandle != kInvalidHandle) { + if (!CloseHandle((HANDLE)mWriteHandle)) { + LOG(LOG_WARN, "pipe", "failed closing write handle\n"); + return false; + } + } +#else + if (mWriteHandle != kInvalidHandle) { + if (close((int) mWriteHandle) != 0) { + LOG(LOG_WARN, "pipe", "failed closing write fd\n"); + return false; + } + } +#endif + mWriteHandle = kInvalidHandle; + return true; +} + +/* + * Get the read handle. + */ +unsigned long Pipe::getReadHandle(void) +{ + assert(mReadHandle != kInvalidHandle); + + return mReadHandle; +} + +/* + * Get the write handle. + */ +unsigned long Pipe::getWriteHandle(void) +{ + assert(mWriteHandle != kInvalidHandle); + + return mWriteHandle; +} + diff --git a/libs/utils/ProcessState.cpp b/libs/utils/ProcessState.cpp new file mode 100644 index 000000000..4567df60f --- /dev/null +++ b/libs/utils/ProcessState.cpp @@ -0,0 +1,398 @@ +/* + * Copyright (C) 2005 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 "ProcessState" + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#define BINDER_VM_SIZE (1*1024*1024) + +static bool gSingleProcess = false; + + +// --------------------------------------------------------------------------- + +namespace android { + +// Global variables +int mArgC; +const char* const* mArgV; +int mArgLen; + +class PoolThread : public Thread +{ +public: + PoolThread(bool isMain) + : mIsMain(isMain) + { + } + +protected: + virtual bool threadLoop() + { + IPCThreadState::self()->joinThreadPool(mIsMain); + return false; + } + + const bool mIsMain; +}; + +sp ProcessState::self() +{ + if (gProcess != NULL) return gProcess; + + AutoMutex _l(gProcessMutex); + if (gProcess == NULL) gProcess = new ProcessState; + return gProcess; +} + +void ProcessState::setSingleProcess(bool singleProcess) +{ + gSingleProcess = singleProcess; +} + + +void ProcessState::setContextObject(const sp& object) +{ + setContextObject(object, String16("default")); +} + +sp ProcessState::getContextObject(const sp& caller) +{ + if (supportsProcesses()) { + return getStrongProxyForHandle(0); + } else { + return getContextObject(String16("default"), caller); + } +} + +void ProcessState::setContextObject(const sp& object, const String16& name) +{ + AutoMutex _l(mLock); + mContexts.add(name, object); +} + +sp ProcessState::getContextObject(const String16& name, const sp& caller) +{ + mLock.lock(); + sp object( + mContexts.indexOfKey(name) >= 0 ? mContexts.valueFor(name) : NULL); + mLock.unlock(); + + //printf("Getting context object %s for %p\n", String8(name).string(), caller.get()); + + if (object != NULL) return object; + + // Don't attempt to retrieve contexts if we manage them + if (mManagesContexts) { + LOGE("getContextObject(%s) failed, but we manage the contexts!\n", + String8(name).string()); + return NULL; + } + + IPCThreadState* ipc = IPCThreadState::self(); + { + Parcel data, reply; + // no interface token on this magic transaction + data.writeString16(name); + data.writeStrongBinder(caller); + status_t result = ipc->transact(0 /*magic*/, 0, data, &reply, 0); + if (result == NO_ERROR) { + object = reply.readStrongBinder(); + } + } + + ipc->flushCommands(); + + if (object != NULL) setContextObject(object, name); + return object; +} + +bool ProcessState::supportsProcesses() const +{ + return mDriverFD >= 0; +} + +void ProcessState::startThreadPool() +{ + AutoMutex _l(mLock); + if (!mThreadPoolStarted) { + mThreadPoolStarted = true; + spawnPooledThread(true); + } +} + +bool ProcessState::isContextManager(void) const +{ + return mManagesContexts; +} + +bool ProcessState::becomeContextManager(context_check_func checkFunc, void* userData) +{ + if (!mManagesContexts) { + AutoMutex _l(mLock); + mBinderContextCheckFunc = checkFunc; + mBinderContextUserData = userData; + if (mDriverFD >= 0) { + int dummy = 0; +#if defined(HAVE_ANDROID_OS) + status_t result = ioctl(mDriverFD, BINDER_SET_CONTEXT_MGR, &dummy); +#else + status_t result = INVALID_OPERATION; +#endif + if (result == 0) { + mManagesContexts = true; + } else if (result == -1) { + mBinderContextCheckFunc = NULL; + mBinderContextUserData = NULL; + LOGE("Binder ioctl to become context manager failed: %s\n", strerror(errno)); + } + } else { + // If there is no driver, our only world is the local + // process so we can always become the context manager there. + mManagesContexts = true; + } + } + return mManagesContexts; +} + +ProcessState::handle_entry* ProcessState::lookupHandleLocked(int32_t handle) +{ + const size_t N=mHandleToObject.size(); + if (N <= (size_t)handle) { + handle_entry e; + e.binder = NULL; + e.refs = NULL; + status_t err = mHandleToObject.insertAt(e, N, handle+1-N); + if (err < NO_ERROR) return NULL; + } + return &mHandleToObject.editItemAt(handle); +} + +sp ProcessState::getStrongProxyForHandle(int32_t handle) +{ + sp result; + + AutoMutex _l(mLock); + + handle_entry* e = lookupHandleLocked(handle); + + if (e != NULL) { + // We need to create a new BpBinder if there isn't currently one, OR we + // are unable to acquire a weak reference on this current one. See comment + // in getWeakProxyForHandle() for more info about this. + IBinder* b = e->binder; + if (b == NULL || !e->refs->attemptIncWeak(this)) { + b = new BpBinder(handle); + e->binder = b; + if (b) e->refs = b->getWeakRefs(); + result = b; + } else { + // This little bit of nastyness is to allow us to add a primary + // reference to the remote proxy when this team doesn't have one + // but another team is sending the handle to us. + result.force_set(b); + e->refs->decWeak(this); + } + } + + return result; +} + +wp ProcessState::getWeakProxyForHandle(int32_t handle) +{ + wp result; + + AutoMutex _l(mLock); + + handle_entry* e = lookupHandleLocked(handle); + + if (e != NULL) { + // We need to create a new BpBinder if there isn't currently one, OR we + // are unable to acquire a weak reference on this current one. The + // attemptIncWeak() is safe because we know the BpBinder destructor will always + // call expungeHandle(), which acquires the same lock we are holding now. + // We need to do this because there is a race condition between someone + // releasing a reference on this BpBinder, and a new reference on its handle + // arriving from the driver. + IBinder* b = e->binder; + if (b == NULL || !e->refs->attemptIncWeak(this)) { + b = new BpBinder(handle); + result = b; + e->binder = b; + if (b) e->refs = b->getWeakRefs(); + } else { + result = b; + e->refs->decWeak(this); + } + } + + return result; +} + +void ProcessState::expungeHandle(int32_t handle, IBinder* binder) +{ + AutoMutex _l(mLock); + + handle_entry* e = lookupHandleLocked(handle); + + // This handle may have already been replaced with a new BpBinder + // (if someone failed the AttemptIncWeak() above); we don't want + // to overwrite it. + if (e && e->binder == binder) e->binder = NULL; +} + +void ProcessState::setArgs(int argc, const char* const argv[]) +{ + mArgC = argc; + mArgV = (const char **)argv; + + mArgLen = 0; + for (int i=0; i t = new PoolThread(isMain); + t->run(buf); + } +} + +static int open_driver() +{ + if (gSingleProcess) { + return -1; + } + + int fd = open("/dev/binder", O_RDWR); + if (fd >= 0) { + fcntl(fd, F_SETFD, FD_CLOEXEC); + int vers; +#if defined(HAVE_ANDROID_OS) + status_t result = ioctl(fd, BINDER_VERSION, &vers); +#else + status_t result = -1; + errno = EPERM; +#endif + if (result == -1) { + LOGE("Binder ioctl to obtain version failed: %s", strerror(errno)); + close(fd); + fd = -1; + } + if (result != 0 || vers != BINDER_CURRENT_PROTOCOL_VERSION) { + LOGE("Binder driver protocol does not match user space protocol!"); + close(fd); + fd = -1; + } +#if defined(HAVE_ANDROID_OS) + size_t maxThreads = 15; + result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads); + if (result == -1) { + LOGE("Binder ioctl to set max threads failed: %s", strerror(errno)); + } +#endif + + } else { + LOGW("Opening '/dev/binder' failed: %s\n", strerror(errno)); + } + return fd; +} + +ProcessState::ProcessState() + : mDriverFD(open_driver()) + , mVMStart(MAP_FAILED) + , mManagesContexts(false) + , mBinderContextCheckFunc(NULL) + , mBinderContextUserData(NULL) + , mThreadPoolStarted(false) + , mThreadPoolSeq(1) +{ + if (mDriverFD >= 0) { + // XXX Ideally, there should be a specific define for whether we + // have mmap (or whether we could possibly have the kernel module + // availabla). +#if !defined(HAVE_WIN32_IPC) + // mmap the binder, providing a chunk of virtual address space to receive transactions. + mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0); + if (mVMStart == MAP_FAILED) { + // *sigh* + LOGE("Using /dev/binder failed: unable to mmap transaction memory.\n"); + close(mDriverFD); + mDriverFD = -1; + } +#else + mDriverFD = -1; +#endif + } + if (mDriverFD < 0) { + // Need to run without the driver, starting our own thread pool. + } +} + +ProcessState::~ProcessState() +{ +} + +}; // namespace android diff --git a/libs/utils/README b/libs/utils/README new file mode 100644 index 000000000..36a706d5c --- /dev/null +++ b/libs/utils/README @@ -0,0 +1,14 @@ +Android Utility Function Library + +If you need a feature that is native to Linux but not present on other +platforms, construct a platform-dependent implementation that shares +the Linux interface. That way the actual device runs as "light" as +possible. + +If that isn't feasible, create a system-independent interface and hide +the details. + +The ultimate goal is *not* to create a super-duper platform abstraction +layer. The goal is to provide an optimized solution for Linux with +reasonable implementations for other platforms. + diff --git a/libs/utils/RefBase.cpp b/libs/utils/RefBase.cpp new file mode 100644 index 000000000..0bd1af4eb --- /dev/null +++ b/libs/utils/RefBase.cpp @@ -0,0 +1,534 @@ +/* + * Copyright (C) 2005 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 "RefBase" + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +// compile with refcounting debugging enabled +#define DEBUG_REFS 0 +#define DEBUG_REFS_ENABLED_BY_DEFAULT 1 +#define DEBUG_REFS_CALLSTACK_ENABLED 1 + +// log all reference counting operations +#define PRINT_REFS 0 + +// --------------------------------------------------------------------------- + +namespace android { + +#define INITIAL_STRONG_VALUE (1<<28) + +// --------------------------------------------------------------------------- + +class RefBase::weakref_impl : public RefBase::weakref_type +{ +public: + volatile int32_t mStrong; + volatile int32_t mWeak; + RefBase* const mBase; + volatile int32_t mFlags; + + +#if !DEBUG_REFS + + weakref_impl(RefBase* base) + : mStrong(INITIAL_STRONG_VALUE) + , mWeak(0) + , mBase(base) + , mFlags(0) + { + } + + void addStrongRef(const void* /*id*/) { } + void removeStrongRef(const void* /*id*/) { } + void addWeakRef(const void* /*id*/) { } + void removeWeakRef(const void* /*id*/) { } + void printRefs() const { } + void trackMe(bool, bool) { } + +#else + + weakref_impl(RefBase* base) + : mStrong(INITIAL_STRONG_VALUE) + , mWeak(0) + , mBase(base) + , mFlags(0) + , mStrongRefs(NULL) + , mWeakRefs(NULL) + , mTrackEnabled(!!DEBUG_REFS_ENABLED_BY_DEFAULT) + , mRetain(false) + { + //LOGI("NEW weakref_impl %p for RefBase %p", this, base); + } + + ~weakref_impl() + { + LOG_ALWAYS_FATAL_IF(!mRetain && mStrongRefs != NULL, "Strong references remain!"); + LOG_ALWAYS_FATAL_IF(!mRetain && mWeakRefs != NULL, "Weak references remain!"); + } + + void addStrongRef(const void* id) + { + addRef(&mStrongRefs, id, mStrong); + } + + void removeStrongRef(const void* id) + { + if (!mRetain) + removeRef(&mStrongRefs, id); + else + addRef(&mStrongRefs, id, -mStrong); + } + + void addWeakRef(const void* id) + { + addRef(&mWeakRefs, id, mWeak); + } + + void removeWeakRef(const void* id) + { + if (!mRetain) + removeRef(&mWeakRefs, id); + else + addRef(&mWeakRefs, id, -mWeak); + } + + void trackMe(bool track, bool retain) + { + mTrackEnabled = track; + mRetain = retain; + } + + void printRefs() const + { + String8 text; + + { + AutoMutex _l(const_cast(this)->mMutex); + + char buf[128]; + sprintf(buf, "Strong references on RefBase %p (weakref_type %p):\n", mBase, this); + text.append(buf); + printRefsLocked(&text, mStrongRefs); + sprintf(buf, "Weak references on RefBase %p (weakref_type %p):\n", mBase, this); + text.append(buf); + printRefsLocked(&text, mWeakRefs); + } + + { + char name[100]; + snprintf(name, 100, "/data/%p.stack", this); + int rc = open(name, O_RDWR | O_CREAT | O_APPEND); + if (rc >= 0) { + write(rc, text.string(), text.length()); + close(rc); + LOGD("STACK TRACE for %p saved in %s", this, name); + } + else LOGE("FAILED TO PRINT STACK TRACE for %p in %s: %s", this, + name, strerror(errno)); + } + } + +private: + struct ref_entry + { + ref_entry* next; + const void* id; +#if DEBUG_REFS_CALLSTACK_ENABLED + CallStack stack; +#endif + int32_t ref; + }; + + void addRef(ref_entry** refs, const void* id, int32_t mRef) + { + if (mTrackEnabled) { + AutoMutex _l(mMutex); + ref_entry* ref = new ref_entry; + // Reference count at the time of the snapshot, but before the + // update. Positive value means we increment, negative--we + // decrement the reference count. + ref->ref = mRef; + ref->id = id; +#if DEBUG_REFS_CALLSTACK_ENABLED + ref->stack.update(2); +#endif + + ref->next = *refs; + *refs = ref; + } + } + + void removeRef(ref_entry** refs, const void* id) + { + if (mTrackEnabled) { + AutoMutex _l(mMutex); + + ref_entry* ref = *refs; + while (ref != NULL) { + if (ref->id == id) { + *refs = ref->next; + delete ref; + return; + } + + refs = &ref->next; + ref = *refs; + } + + LOG_ALWAYS_FATAL("RefBase: removing id %p on RefBase %p (weakref_type %p) that doesn't exist!", + id, mBase, this); + } + } + + void printRefsLocked(String8* out, const ref_entry* refs) const + { + char buf[128]; + while (refs) { + char inc = refs->ref >= 0 ? '+' : '-'; + sprintf(buf, "\t%c ID %p (ref %d):\n", + inc, refs->id, refs->ref); + out->append(buf); +#if DEBUG_REFS_CALLSTACK_ENABLED + out->append(refs->stack.toString("\t\t")); +#else + out->append("\t\t(call stacks disabled)"); +#endif + refs = refs->next; + } + } + + Mutex mMutex; + ref_entry* mStrongRefs; + ref_entry* mWeakRefs; + + bool mTrackEnabled; + // Collect stack traces on addref and removeref, instead of deleting the stack references + // on removeref that match the address ones. + bool mRetain; + +#if 0 + void addRef(KeyedVector* refs, const void* id) + { + AutoMutex _l(mMutex); + ssize_t i = refs->indexOfKey(id); + if (i >= 0) { + ++(refs->editValueAt(i)); + } else { + i = refs->add(id, 1); + } + } + + void removeRef(KeyedVector* refs, const void* id) + { + AutoMutex _l(mMutex); + ssize_t i = refs->indexOfKey(id); + LOG_ALWAYS_FATAL_IF(i < 0, "RefBase: removing id %p that doesn't exist!", id); + if (i >= 0) { + int32_t val = --(refs->editValueAt(i)); + if (val == 0) { + refs->removeItemsAt(i); + } + } + } + + void printRefs(const KeyedVector& refs) + { + const size_t N=refs.size(); + for (size_t i=0; i mStrongRefs; + KeyedVector mWeakRefs; +#endif + +#endif +}; + +// --------------------------------------------------------------------------- + +void RefBase::incStrong(const void* id) const +{ + weakref_impl* const refs = mRefs; + refs->addWeakRef(id); + refs->incWeak(id); + + refs->addStrongRef(id); + const int32_t c = android_atomic_inc(&refs->mStrong); + LOG_ASSERT(c > 0, "incStrong() called on %p after last strong ref", refs); +#if PRINT_REFS + LOGD("incStrong of %p from %p: cnt=%d\n", this, id, c); +#endif + if (c != INITIAL_STRONG_VALUE) { + return; + } + + android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong); + const_cast(this)->onFirstRef(); +} + +void RefBase::decStrong(const void* id) const +{ + weakref_impl* const refs = mRefs; + refs->removeStrongRef(id); + const int32_t c = android_atomic_dec(&refs->mStrong); +#if PRINT_REFS + LOGD("decStrong of %p from %p: cnt=%d\n", this, id, c); +#endif + LOG_ASSERT(c >= 1, "decStrong() called on %p too many times", refs); + if (c == 1) { + const_cast(this)->onLastStrongRef(id); + if ((refs->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) { + delete this; + } + } + refs->removeWeakRef(id); + refs->decWeak(id); +} + +void RefBase::forceIncStrong(const void* id) const +{ + weakref_impl* const refs = mRefs; + refs->addWeakRef(id); + refs->incWeak(id); + + refs->addStrongRef(id); + const int32_t c = android_atomic_inc(&refs->mStrong); + LOG_ASSERT(c >= 0, "forceIncStrong called on %p after ref count underflow", + refs); +#if PRINT_REFS + LOGD("forceIncStrong of %p from %p: cnt=%d\n", this, id, c); +#endif + + switch (c) { + case INITIAL_STRONG_VALUE: + android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong); + // fall through... + case 0: + const_cast(this)->onFirstRef(); + } +} + +int32_t RefBase::getStrongCount() const +{ + return mRefs->mStrong; +} + + + +RefBase* RefBase::weakref_type::refBase() const +{ + return static_cast(this)->mBase; +} + +void RefBase::weakref_type::incWeak(const void* id) +{ + weakref_impl* const impl = static_cast(this); + impl->addWeakRef(id); + const int32_t c = android_atomic_inc(&impl->mWeak); + LOG_ASSERT(c >= 0, "incWeak called on %p after last weak ref", this); +} + +void RefBase::weakref_type::decWeak(const void* id) +{ + weakref_impl* const impl = static_cast(this); + impl->removeWeakRef(id); + const int32_t c = android_atomic_dec(&impl->mWeak); + LOG_ASSERT(c >= 1, "decWeak called on %p too many times", this); + if (c != 1) return; + + if ((impl->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) { + if (impl->mStrong == INITIAL_STRONG_VALUE) + delete impl->mBase; + else { +// LOGV("Freeing refs %p of old RefBase %p\n", this, impl->mBase); + delete impl; + } + } else { + impl->mBase->onLastWeakRef(id); + if ((impl->mFlags&OBJECT_LIFETIME_FOREVER) != OBJECT_LIFETIME_FOREVER) { + delete impl->mBase; + } + } +} + +bool RefBase::weakref_type::attemptIncStrong(const void* id) +{ + incWeak(id); + + weakref_impl* const impl = static_cast(this); + + int32_t curCount = impl->mStrong; + LOG_ASSERT(curCount >= 0, "attemptIncStrong called on %p after underflow", + this); + while (curCount > 0 && curCount != INITIAL_STRONG_VALUE) { + if (android_atomic_cmpxchg(curCount, curCount+1, &impl->mStrong) == 0) { + break; + } + curCount = impl->mStrong; + } + + if (curCount <= 0 || curCount == INITIAL_STRONG_VALUE) { + bool allow; + if (curCount == INITIAL_STRONG_VALUE) { + // Attempting to acquire first strong reference... this is allowed + // if the object does NOT have a longer lifetime (meaning the + // implementation doesn't need to see this), or if the implementation + // allows it to happen. + allow = (impl->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK + || impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id); + } else { + // Attempting to revive the object... this is allowed + // if the object DOES have a longer lifetime (so we can safely + // call the object with only a weak ref) and the implementation + // allows it to happen. + allow = (impl->mFlags&OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_WEAK + && impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id); + } + if (!allow) { + decWeak(id); + return false; + } + curCount = android_atomic_inc(&impl->mStrong); + + // If the strong reference count has already been incremented by + // someone else, the implementor of onIncStrongAttempted() is holding + // an unneeded reference. So call onLastStrongRef() here to remove it. + // (No, this is not pretty.) Note that we MUST NOT do this if we + // are in fact acquiring the first reference. + if (curCount > 0 && curCount < INITIAL_STRONG_VALUE) { + impl->mBase->onLastStrongRef(id); + } + } + + impl->addWeakRef(id); + impl->addStrongRef(id); + +#if PRINT_REFS + LOGD("attemptIncStrong of %p from %p: cnt=%d\n", this, id, curCount); +#endif + + if (curCount == INITIAL_STRONG_VALUE) { + android_atomic_add(-INITIAL_STRONG_VALUE, &impl->mStrong); + impl->mBase->onFirstRef(); + } + + return true; +} + +bool RefBase::weakref_type::attemptIncWeak(const void* id) +{ + weakref_impl* const impl = static_cast(this); + + int32_t curCount = impl->mWeak; + LOG_ASSERT(curCount >= 0, "attemptIncWeak called on %p after underflow", + this); + while (curCount > 0) { + if (android_atomic_cmpxchg(curCount, curCount+1, &impl->mWeak) == 0) { + break; + } + curCount = impl->mWeak; + } + + if (curCount > 0) { + impl->addWeakRef(id); + } + + return curCount > 0; +} + +int32_t RefBase::weakref_type::getWeakCount() const +{ + return static_cast(this)->mWeak; +} + +void RefBase::weakref_type::printRefs() const +{ + static_cast(this)->printRefs(); +} + +void RefBase::weakref_type::trackMe(bool enable, bool retain) +{ + static_cast(this)->trackMe(enable, retain); +} + +RefBase::weakref_type* RefBase::createWeak(const void* id) const +{ + mRefs->incWeak(id); + return mRefs; +} + +RefBase::weakref_type* RefBase::getWeakRefs() const +{ + return mRefs; +} + +RefBase::RefBase() + : mRefs(new weakref_impl(this)) +{ +// LOGV("Creating refs %p with RefBase %p\n", mRefs, this); +} + +RefBase::~RefBase() +{ +// LOGV("Destroying RefBase %p (refs %p)\n", this, mRefs); + if (mRefs->mWeak == 0) { +// LOGV("Freeing refs %p of old RefBase %p\n", mRefs, this); + delete mRefs; + } +} + +void RefBase::extendObjectLifetime(int32_t mode) +{ + android_atomic_or(mode, &mRefs->mFlags); +} + +void RefBase::onFirstRef() +{ +} + +void RefBase::onLastStrongRef(const void* /*id*/) +{ +} + +bool RefBase::onIncStrongAttempted(uint32_t flags, const void* id) +{ + return (flags&FIRST_INC_STRONG) ? true : false; +} + +void RefBase::onLastWeakRef(const void* /*id*/) +{ +} + +}; // namespace android diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp new file mode 100644 index 000000000..a5fe9fba5 --- /dev/null +++ b/libs/utils/ResourceTypes.cpp @@ -0,0 +1,3969 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "ResourceType" +//#define LOG_NDEBUG 0 + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#ifndef INT32_MAX +#define INT32_MAX ((int32_t)(2147483647)) +#endif + +#define POOL_NOISY(x) //x +#define XML_NOISY(x) //x +#define TABLE_NOISY(x) //x +#define TABLE_GETENTRY(x) //x +#define TABLE_SUPER_NOISY(x) //x +#define LOAD_TABLE_NOISY(x) //x + +namespace android { + +#ifdef HAVE_WINSOCK +#undef nhtol +#undef htonl + +#ifdef HAVE_LITTLE_ENDIAN +#define ntohl(x) ( ((x) << 24) | (((x) >> 24) & 255) | (((x) << 8) & 0xff0000) | (((x) >> 8) & 0xff00) ) +#define htonl(x) ntohl(x) +#define ntohs(x) ( (((x) << 8) & 0xff00) | (((x) >> 8) & 255) ) +#define htons(x) ntohs(x) +#else +#define ntohl(x) (x) +#define htonl(x) (x) +#define ntohs(x) (x) +#define htons(x) (x) +#endif +#endif + +static void printToLogFunc(void* cookie, const char* txt) +{ + LOGV("%s", txt); +} + +// Standard C isspace() is only required to look at the low byte of its input, so +// produces incorrect results for UTF-16 characters. For safety's sake, assume that +// any high-byte UTF-16 code point is not whitespace. +inline int isspace16(char16_t c) { + return (c < 0x0080 && isspace(c)); +} + +// range checked; guaranteed to NUL-terminate within the stated number of available slots +// NOTE: if this truncates the dst string due to running out of space, no attempt is +// made to avoid splitting surrogate pairs. +static void strcpy16_dtoh(uint16_t* dst, const uint16_t* src, size_t avail) +{ + uint16_t* last = dst + avail - 1; + while (*src && (dst < last)) { + char16_t s = dtohs(*src); + *dst++ = s; + src++; + } + *dst = 0; +} + +static status_t validate_chunk(const ResChunk_header* chunk, + size_t minSize, + const uint8_t* dataEnd, + const char* name) +{ + const uint16_t headerSize = dtohs(chunk->headerSize); + const uint32_t size = dtohl(chunk->size); + + if (headerSize >= minSize) { + if (headerSize <= size) { + if (((headerSize|size)&0x3) == 0) { + if ((ssize_t)size <= (dataEnd-((const uint8_t*)chunk))) { + return NO_ERROR; + } + LOGW("%s data size %p extends beyond resource end %p.", + name, (void*)size, + (void*)(dataEnd-((const uint8_t*)chunk))); + return BAD_TYPE; + } + LOGW("%s size 0x%x or headerSize 0x%x is not on an integer boundary.", + name, (int)size, (int)headerSize); + return BAD_TYPE; + } + LOGW("%s size %p is smaller than header size %p.", + name, (void*)size, (void*)(int)headerSize); + return BAD_TYPE; + } + LOGW("%s header size %p is too small.", + name, (void*)(int)headerSize); + return BAD_TYPE; +} + +inline void Res_value::copyFrom_dtoh(const Res_value& src) +{ + size = dtohs(src.size); + res0 = src.res0; + dataType = src.dataType; + data = dtohl(src.data); +} + +void Res_png_9patch::deviceToFile() +{ + for (int i = 0; i < numXDivs; i++) { + xDivs[i] = htonl(xDivs[i]); + } + for (int i = 0; i < numYDivs; i++) { + yDivs[i] = htonl(yDivs[i]); + } + paddingLeft = htonl(paddingLeft); + paddingRight = htonl(paddingRight); + paddingTop = htonl(paddingTop); + paddingBottom = htonl(paddingBottom); + for (int i=0; ixDivs, numXDivs * sizeof(int32_t)); + data += numXDivs * sizeof(int32_t); + memmove(data, this->yDivs, numYDivs * sizeof(int32_t)); + data += numYDivs * sizeof(int32_t); + memmove(data, this->colors, numColors * sizeof(uint32_t)); +} + +Res_png_9patch* Res_png_9patch::deserialize(const void* inData) +{ + deserialize(inData, (Res_png_9patch*) inData); + return (Res_png_9patch*) inData; +} + +void Res_png_9patch::deserialize(const void* inData, Res_png_9patch* outData) { + Res_png_9patch* patch = (Res_png_9patch*) inData; + if (inData != outData) { + memcpy(outData, inData, patch->serializedSize()); + } + outData->wasDeserialized = true; + char* data = (char*)outData; + data += sizeof(Res_png_9patch); + outData->xDivs = (int32_t*) data; + data += patch->numXDivs * sizeof(int32_t); + outData->yDivs = (int32_t*) data; + data += patch->numYDivs * sizeof(int32_t); + outData->colors = (uint32_t*) data; +} + +// -------------------------------------------------------------------- +// -------------------------------------------------------------------- +// -------------------------------------------------------------------- + +ResStringPool::ResStringPool() + : mError(NO_INIT), mOwnedData(NULL) +{ +} + +ResStringPool::ResStringPool(const void* data, size_t size, bool copyData) + : mError(NO_INIT), mOwnedData(NULL) +{ + setTo(data, size, copyData); +} + +ResStringPool::~ResStringPool() +{ + uninit(); +} + +status_t ResStringPool::setTo(const void* data, size_t size, bool copyData) +{ + if (!data || !size) { + return (mError=BAD_TYPE); + } + + uninit(); + + const bool notDeviceEndian = htods(0xf0) != 0xf0; + + if (copyData || notDeviceEndian) { + mOwnedData = malloc(size); + if (mOwnedData == NULL) { + return (mError=NO_MEMORY); + } + memcpy(mOwnedData, data, size); + data = mOwnedData; + } + + mHeader = (const ResStringPool_header*)data; + + if (notDeviceEndian) { + ResStringPool_header* h = const_cast(mHeader); + h->header.headerSize = dtohs(mHeader->header.headerSize); + h->header.type = dtohs(mHeader->header.type); + h->header.size = dtohl(mHeader->header.size); + h->stringCount = dtohl(mHeader->stringCount); + h->styleCount = dtohl(mHeader->styleCount); + h->flags = dtohl(mHeader->flags); + h->stringsStart = dtohl(mHeader->stringsStart); + h->stylesStart = dtohl(mHeader->stylesStart); + } + + if (mHeader->header.headerSize > mHeader->header.size + || mHeader->header.size > size) { + LOGW("Bad string block: header size %d or total size %d is larger than data size %d\n", + (int)mHeader->header.headerSize, (int)mHeader->header.size, (int)size); + return (mError=BAD_TYPE); + } + mSize = mHeader->header.size; + mEntries = (const uint32_t*) + (((const uint8_t*)data)+mHeader->header.headerSize); + + if (mHeader->stringCount > 0) { + if ((mHeader->stringCount*sizeof(uint32_t) < mHeader->stringCount) // uint32 overflow? + || (mHeader->header.headerSize+(mHeader->stringCount*sizeof(uint32_t))) + > size) { + LOGW("Bad string block: entry of %d items extends past data size %d\n", + (int)(mHeader->header.headerSize+(mHeader->stringCount*sizeof(uint32_t))), + (int)size); + return (mError=BAD_TYPE); + } + mStrings = (const char16_t*) + (((const uint8_t*)data)+mHeader->stringsStart); + if (mHeader->stringsStart >= (mHeader->header.size-sizeof(uint16_t))) { + LOGW("Bad string block: string pool starts at %d, after total size %d\n", + (int)mHeader->stringsStart, (int)mHeader->header.size); + return (mError=BAD_TYPE); + } + if (mHeader->styleCount == 0) { + mStringPoolSize = + (mHeader->header.size-mHeader->stringsStart)/sizeof(uint16_t); + } else { + // check invariant: styles follow the strings + if (mHeader->stylesStart <= mHeader->stringsStart) { + LOGW("Bad style block: style block starts at %d, before strings at %d\n", + (int)mHeader->stylesStart, (int)mHeader->stringsStart); + return (mError=BAD_TYPE); + } + mStringPoolSize = + (mHeader->stylesStart-mHeader->stringsStart)/sizeof(uint16_t); + } + + // check invariant: stringCount > 0 requires a string pool to exist + if (mStringPoolSize == 0) { + LOGW("Bad string block: stringCount is %d but pool size is 0\n", (int)mHeader->stringCount); + return (mError=BAD_TYPE); + } + + if (notDeviceEndian) { + size_t i; + uint32_t* e = const_cast(mEntries); + for (i=0; istringCount; i++) { + e[i] = dtohl(mEntries[i]); + } + char16_t* s = const_cast(mStrings); + for (i=0; istyleCount > 0) { + mEntryStyles = mEntries + mHeader->stringCount; + // invariant: integer overflow in calculating mEntryStyles + if (mEntryStyles < mEntries) { + LOGW("Bad string block: integer overflow finding styles\n"); + return (mError=BAD_TYPE); + } + + if (((const uint8_t*)mEntryStyles-(const uint8_t*)mHeader) > (int)size) { + LOGW("Bad string block: entry of %d styles extends past data size %d\n", + (int)((const uint8_t*)mEntryStyles-(const uint8_t*)mHeader), + (int)size); + return (mError=BAD_TYPE); + } + mStyles = (const uint32_t*) + (((const uint8_t*)data)+mHeader->stylesStart); + if (mHeader->stylesStart >= mHeader->header.size) { + LOGW("Bad string block: style pool starts %d, after total size %d\n", + (int)mHeader->stylesStart, (int)mHeader->header.size); + return (mError=BAD_TYPE); + } + mStylePoolSize = + (mHeader->header.size-mHeader->stylesStart)/sizeof(uint32_t); + + if (notDeviceEndian) { + size_t i; + uint32_t* e = const_cast(mEntryStyles); + for (i=0; istyleCount; i++) { + e[i] = dtohl(mEntryStyles[i]); + } + uint32_t* s = const_cast(mStyles); + for (i=0; istringCount) { + const uint32_t off = (mEntries[idx]/sizeof(uint16_t)); + if (off < (mStringPoolSize-1)) { + const char16_t* str = mStrings+off; + *outLen = *str; + if ((*str)&0x8000) { + str++; + *outLen = (((*outLen)&0x7fff)<<16) + *str; + } + if ((uint32_t)(str+1+*outLen-mStrings) < mStringPoolSize) { + return str+1; + } else { + LOGW("Bad string block: string #%d extends to %d, past end at %d\n", + (int)idx, (int)(str+1+*outLen-mStrings), (int)mStringPoolSize); + } + } else { + LOGW("Bad string block: string #%d entry is at %d, past end at %d\n", + (int)idx, (int)(off*sizeof(uint16_t)), + (int)(mStringPoolSize*sizeof(uint16_t))); + } + } + return NULL; +} + +const ResStringPool_span* ResStringPool::styleAt(const ResStringPool_ref& ref) const +{ + return styleAt(ref.index); +} + +const ResStringPool_span* ResStringPool::styleAt(size_t idx) const +{ + if (mError == NO_ERROR && idx < mHeader->styleCount) { + const uint32_t off = (mEntryStyles[idx]/sizeof(uint32_t)); + if (off < mStylePoolSize) { + return (const ResStringPool_span*)(mStyles+off); + } else { + LOGW("Bad string block: style #%d entry is at %d, past end at %d\n", + (int)idx, (int)(off*sizeof(uint32_t)), + (int)(mStylePoolSize*sizeof(uint32_t))); + } + } + return NULL; +} + +ssize_t ResStringPool::indexOfString(const char16_t* str, size_t strLen) const +{ + if (mError != NO_ERROR) { + return mError; + } + + size_t len; + + if (mHeader->flags&ResStringPool_header::SORTED_FLAG) { + // Do a binary search for the string... + ssize_t l = 0; + ssize_t h = mHeader->stringCount-1; + + ssize_t mid; + while (l <= h) { + mid = l + (h - l)/2; + const char16_t* s = stringAt(mid, &len); + int c = s ? strzcmp16(s, len, str, strLen) : -1; + POOL_NOISY(printf("Looking for %s, at %s, cmp=%d, l/mid/h=%d/%d/%d\n", + String8(str).string(), + String8(s).string(), + c, (int)l, (int)mid, (int)h)); + if (c == 0) { + return mid; + } else if (c < 0) { + l = mid + 1; + } else { + h = mid - 1; + } + } + } else { + // It is unusual to get the ID from an unsorted string block... + // most often this happens because we want to get IDs for style + // span tags; since those always appear at the end of the string + // block, start searching at the back. + for (int i=mHeader->stringCount-1; i>=0; i--) { + const char16_t* s = stringAt(i, &len); + POOL_NOISY(printf("Looking for %s, at %s, i=%d\n", + String8(str, strLen).string(), + String8(s).string(), + i)); + if (s && strzcmp16(s, len, str, strLen) == 0) { + return i; + } + } + } + + return NAME_NOT_FOUND; +} + +size_t ResStringPool::size() const +{ + return (mError == NO_ERROR) ? mHeader->stringCount : 0; +} + +// -------------------------------------------------------------------- +// -------------------------------------------------------------------- +// -------------------------------------------------------------------- + +ResXMLParser::ResXMLParser(const ResXMLTree& tree) + : mTree(tree), mEventCode(BAD_DOCUMENT) +{ +} + +void ResXMLParser::restart() +{ + mCurNode = NULL; + mEventCode = mTree.mError == NO_ERROR ? START_DOCUMENT : BAD_DOCUMENT; +} + +ResXMLParser::event_code_t ResXMLParser::getEventType() const +{ + return mEventCode; +} + +ResXMLParser::event_code_t ResXMLParser::next() +{ + if (mEventCode == START_DOCUMENT) { + mCurNode = mTree.mRootNode; + mCurExt = mTree.mRootExt; + return (mEventCode=mTree.mRootCode); + } else if (mEventCode >= FIRST_CHUNK_CODE) { + return nextNode(); + } + return mEventCode; +} + +const int32_t ResXMLParser::getCommentID() const +{ + return mCurNode != NULL ? dtohl(mCurNode->comment.index) : -1; +} + +const uint16_t* ResXMLParser::getComment(size_t* outLen) const +{ + int32_t id = getCommentID(); + return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; +} + +const uint32_t ResXMLParser::getLineNumber() const +{ + return mCurNode != NULL ? dtohl(mCurNode->lineNumber) : -1; +} + +const int32_t ResXMLParser::getTextID() const +{ + if (mEventCode == TEXT) { + return dtohl(((const ResXMLTree_cdataExt*)mCurExt)->data.index); + } + return -1; +} + +const uint16_t* ResXMLParser::getText(size_t* outLen) const +{ + int32_t id = getTextID(); + return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; +} + +ssize_t ResXMLParser::getTextValue(Res_value* outValue) const +{ + if (mEventCode == TEXT) { + outValue->copyFrom_dtoh(((const ResXMLTree_cdataExt*)mCurExt)->typedData); + return sizeof(Res_value); + } + return BAD_TYPE; +} + +const int32_t ResXMLParser::getNamespacePrefixID() const +{ + if (mEventCode == START_NAMESPACE || mEventCode == END_NAMESPACE) { + return dtohl(((const ResXMLTree_namespaceExt*)mCurExt)->prefix.index); + } + return -1; +} + +const uint16_t* ResXMLParser::getNamespacePrefix(size_t* outLen) const +{ + int32_t id = getNamespacePrefixID(); + //printf("prefix=%d event=%p\n", id, mEventCode); + return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; +} + +const int32_t ResXMLParser::getNamespaceUriID() const +{ + if (mEventCode == START_NAMESPACE || mEventCode == END_NAMESPACE) { + return dtohl(((const ResXMLTree_namespaceExt*)mCurExt)->uri.index); + } + return -1; +} + +const uint16_t* ResXMLParser::getNamespaceUri(size_t* outLen) const +{ + int32_t id = getNamespaceUriID(); + //printf("uri=%d event=%p\n", id, mEventCode); + return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; +} + +const int32_t ResXMLParser::getElementNamespaceID() const +{ + if (mEventCode == START_TAG) { + return dtohl(((const ResXMLTree_attrExt*)mCurExt)->ns.index); + } + if (mEventCode == END_TAG) { + return dtohl(((const ResXMLTree_endElementExt*)mCurExt)->ns.index); + } + return -1; +} + +const uint16_t* ResXMLParser::getElementNamespace(size_t* outLen) const +{ + int32_t id = getElementNamespaceID(); + return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; +} + +const int32_t ResXMLParser::getElementNameID() const +{ + if (mEventCode == START_TAG) { + return dtohl(((const ResXMLTree_attrExt*)mCurExt)->name.index); + } + if (mEventCode == END_TAG) { + return dtohl(((const ResXMLTree_endElementExt*)mCurExt)->name.index); + } + return -1; +} + +const uint16_t* ResXMLParser::getElementName(size_t* outLen) const +{ + int32_t id = getElementNameID(); + return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; +} + +size_t ResXMLParser::getAttributeCount() const +{ + if (mEventCode == START_TAG) { + return dtohs(((const ResXMLTree_attrExt*)mCurExt)->attributeCount); + } + return 0; +} + +const int32_t ResXMLParser::getAttributeNamespaceID(size_t idx) const +{ + if (mEventCode == START_TAG) { + const ResXMLTree_attrExt* tag = (const ResXMLTree_attrExt*)mCurExt; + if (idx < dtohs(tag->attributeCount)) { + const ResXMLTree_attribute* attr = (const ResXMLTree_attribute*) + (((const uint8_t*)tag) + + dtohs(tag->attributeStart) + + (dtohs(tag->attributeSize)*idx)); + return dtohl(attr->ns.index); + } + } + return -2; +} + +const uint16_t* ResXMLParser::getAttributeNamespace(size_t idx, size_t* outLen) const +{ + int32_t id = getAttributeNamespaceID(idx); + //printf("attribute namespace=%d idx=%d event=%p\n", id, idx, mEventCode); + //XML_NOISY(printf("getAttributeNamespace 0x%x=0x%x\n", idx, id)); + return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; +} + +const int32_t ResXMLParser::getAttributeNameID(size_t idx) const +{ + if (mEventCode == START_TAG) { + const ResXMLTree_attrExt* tag = (const ResXMLTree_attrExt*)mCurExt; + if (idx < dtohs(tag->attributeCount)) { + const ResXMLTree_attribute* attr = (const ResXMLTree_attribute*) + (((const uint8_t*)tag) + + dtohs(tag->attributeStart) + + (dtohs(tag->attributeSize)*idx)); + return dtohl(attr->name.index); + } + } + return -1; +} + +const uint16_t* ResXMLParser::getAttributeName(size_t idx, size_t* outLen) const +{ + int32_t id = getAttributeNameID(idx); + //printf("attribute name=%d idx=%d event=%p\n", id, idx, mEventCode); + //XML_NOISY(printf("getAttributeName 0x%x=0x%x\n", idx, id)); + return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; +} + +const uint32_t ResXMLParser::getAttributeNameResID(size_t idx) const +{ + int32_t id = getAttributeNameID(idx); + if (id >= 0 && (size_t)id < mTree.mNumResIds) { + return dtohl(mTree.mResIds[id]); + } + return 0; +} + +const int32_t ResXMLParser::getAttributeValueStringID(size_t idx) const +{ + if (mEventCode == START_TAG) { + const ResXMLTree_attrExt* tag = (const ResXMLTree_attrExt*)mCurExt; + if (idx < dtohs(tag->attributeCount)) { + const ResXMLTree_attribute* attr = (const ResXMLTree_attribute*) + (((const uint8_t*)tag) + + dtohs(tag->attributeStart) + + (dtohs(tag->attributeSize)*idx)); + return dtohl(attr->rawValue.index); + } + } + return -1; +} + +const uint16_t* ResXMLParser::getAttributeStringValue(size_t idx, size_t* outLen) const +{ + int32_t id = getAttributeValueStringID(idx); + //XML_NOISY(printf("getAttributeValue 0x%x=0x%x\n", idx, id)); + return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; +} + +int32_t ResXMLParser::getAttributeDataType(size_t idx) const +{ + if (mEventCode == START_TAG) { + const ResXMLTree_attrExt* tag = (const ResXMLTree_attrExt*)mCurExt; + if (idx < dtohs(tag->attributeCount)) { + const ResXMLTree_attribute* attr = (const ResXMLTree_attribute*) + (((const uint8_t*)tag) + + dtohs(tag->attributeStart) + + (dtohs(tag->attributeSize)*idx)); + return attr->typedValue.dataType; + } + } + return Res_value::TYPE_NULL; +} + +int32_t ResXMLParser::getAttributeData(size_t idx) const +{ + if (mEventCode == START_TAG) { + const ResXMLTree_attrExt* tag = (const ResXMLTree_attrExt*)mCurExt; + if (idx < dtohs(tag->attributeCount)) { + const ResXMLTree_attribute* attr = (const ResXMLTree_attribute*) + (((const uint8_t*)tag) + + dtohs(tag->attributeStart) + + (dtohs(tag->attributeSize)*idx)); + return dtohl(attr->typedValue.data); + } + } + return 0; +} + +ssize_t ResXMLParser::getAttributeValue(size_t idx, Res_value* outValue) const +{ + if (mEventCode == START_TAG) { + const ResXMLTree_attrExt* tag = (const ResXMLTree_attrExt*)mCurExt; + if (idx < dtohs(tag->attributeCount)) { + const ResXMLTree_attribute* attr = (const ResXMLTree_attribute*) + (((const uint8_t*)tag) + + dtohs(tag->attributeStart) + + (dtohs(tag->attributeSize)*idx)); + outValue->copyFrom_dtoh(attr->typedValue); + return sizeof(Res_value); + } + } + return BAD_TYPE; +} + +ssize_t ResXMLParser::indexOfAttribute(const char* ns, const char* attr) const +{ + String16 nsStr(ns != NULL ? ns : ""); + String16 attrStr(attr); + return indexOfAttribute(ns ? nsStr.string() : NULL, ns ? nsStr.size() : 0, + attrStr.string(), attrStr.size()); +} + +ssize_t ResXMLParser::indexOfAttribute(const char16_t* ns, size_t nsLen, + const char16_t* attr, size_t attrLen) const +{ + if (mEventCode == START_TAG) { + const size_t N = getAttributeCount(); + for (size_t i=0; i attr=%s, curAttr=%s\n", + // String8(attr).string(), String8(curAttr).string()); + if (attr && curAttr && (strzcmp16(attr, attrLen, curAttr, curAttrLen) == 0)) { + if (ns == NULL) { + if (curNs == NULL) return i; + } else if (curNs != NULL) { + //printf(" --> ns=%s, curNs=%s\n", + // String8(ns).string(), String8(curNs).string()); + if (strzcmp16(ns, nsLen, curNs, curNsLen) == 0) return i; + } + } + } + } + + return NAME_NOT_FOUND; +} + +ssize_t ResXMLParser::indexOfID() const +{ + if (mEventCode == START_TAG) { + const ssize_t idx = dtohs(((const ResXMLTree_attrExt*)mCurExt)->idIndex); + if (idx > 0) return (idx-1); + } + return NAME_NOT_FOUND; +} + +ssize_t ResXMLParser::indexOfClass() const +{ + if (mEventCode == START_TAG) { + const ssize_t idx = dtohs(((const ResXMLTree_attrExt*)mCurExt)->classIndex); + if (idx > 0) return (idx-1); + } + return NAME_NOT_FOUND; +} + +ssize_t ResXMLParser::indexOfStyle() const +{ + if (mEventCode == START_TAG) { + const ssize_t idx = dtohs(((const ResXMLTree_attrExt*)mCurExt)->styleIndex); + if (idx > 0) return (idx-1); + } + return NAME_NOT_FOUND; +} + +ResXMLParser::event_code_t ResXMLParser::nextNode() +{ + if (mEventCode < 0) { + return mEventCode; + } + + do { + const ResXMLTree_node* next = (const ResXMLTree_node*) + (((const uint8_t*)mCurNode) + dtohl(mCurNode->header.size)); + //LOGW("Next node: prev=%p, next=%p\n", mCurNode, next); + + if (((const uint8_t*)next) >= mTree.mDataEnd) { + mCurNode = NULL; + return (mEventCode=END_DOCUMENT); + } + + if (mTree.validateNode(next) != NO_ERROR) { + mCurNode = NULL; + return (mEventCode=BAD_DOCUMENT); + } + + mCurNode = next; + const uint16_t headerSize = dtohs(next->header.headerSize); + const uint32_t totalSize = dtohl(next->header.size); + mCurExt = ((const uint8_t*)next) + headerSize; + size_t minExtSize = 0; + event_code_t eventCode = (event_code_t)dtohs(next->header.type); + switch ((mEventCode=eventCode)) { + case RES_XML_START_NAMESPACE_TYPE: + case RES_XML_END_NAMESPACE_TYPE: + minExtSize = sizeof(ResXMLTree_namespaceExt); + break; + case RES_XML_START_ELEMENT_TYPE: + minExtSize = sizeof(ResXMLTree_attrExt); + break; + case RES_XML_END_ELEMENT_TYPE: + minExtSize = sizeof(ResXMLTree_endElementExt); + break; + case RES_XML_CDATA_TYPE: + minExtSize = sizeof(ResXMLTree_cdataExt); + break; + default: + LOGW("Unknown XML block: header type %d in node at %d\n", + (int)dtohs(next->header.type), + (int)(((const uint8_t*)next)-((const uint8_t*)mTree.mHeader))); + continue; + } + + if ((totalSize-headerSize) < minExtSize) { + LOGW("Bad XML block: header type 0x%x in node at 0x%x has size %d, need %d\n", + (int)dtohs(next->header.type), + (int)(((const uint8_t*)next)-((const uint8_t*)mTree.mHeader)), + (int)(totalSize-headerSize), (int)minExtSize); + return (mEventCode=BAD_DOCUMENT); + } + + //printf("CurNode=%p, CurExt=%p, headerSize=%d, minExtSize=%d\n", + // mCurNode, mCurExt, headerSize, minExtSize); + + return eventCode; + } while (true); +} + +void ResXMLParser::getPosition(ResXMLParser::ResXMLPosition* pos) const +{ + pos->eventCode = mEventCode; + pos->curNode = mCurNode; + pos->curExt = mCurExt; +} + +void ResXMLParser::setPosition(const ResXMLParser::ResXMLPosition& pos) +{ + mEventCode = pos.eventCode; + mCurNode = pos.curNode; + mCurExt = pos.curExt; +} + + +// -------------------------------------------------------------------- + +static volatile int32_t gCount = 0; + +ResXMLTree::ResXMLTree() + : ResXMLParser(*this) + , mError(NO_INIT), mOwnedData(NULL) +{ + //LOGI("Creating ResXMLTree %p #%d\n", this, android_atomic_inc(&gCount)+1); + restart(); +} + +ResXMLTree::ResXMLTree(const void* data, size_t size, bool copyData) + : ResXMLParser(*this) + , mError(NO_INIT), mOwnedData(NULL) +{ + //LOGI("Creating ResXMLTree %p #%d\n", this, android_atomic_inc(&gCount)+1); + setTo(data, size, copyData); +} + +ResXMLTree::~ResXMLTree() +{ + //LOGI("Destroying ResXMLTree in %p #%d\n", this, android_atomic_dec(&gCount)-1); + uninit(); +} + +status_t ResXMLTree::setTo(const void* data, size_t size, bool copyData) +{ + uninit(); + mEventCode = START_DOCUMENT; + + if (copyData) { + mOwnedData = malloc(size); + if (mOwnedData == NULL) { + return (mError=NO_MEMORY); + } + memcpy(mOwnedData, data, size); + data = mOwnedData; + } + + mHeader = (const ResXMLTree_header*)data; + mSize = dtohl(mHeader->header.size); + if (dtohs(mHeader->header.headerSize) > mSize || mSize > size) { + LOGW("Bad XML block: header size %d or total size %d is larger than data size %d\n", + (int)dtohs(mHeader->header.headerSize), + (int)dtohl(mHeader->header.size), (int)size); + mError = BAD_TYPE; + restart(); + return mError; + } + mDataEnd = ((const uint8_t*)mHeader) + mSize; + + mStrings.uninit(); + mRootNode = NULL; + mResIds = NULL; + mNumResIds = 0; + + // First look for a couple interesting chunks: the string block + // and first XML node. + const ResChunk_header* chunk = + (const ResChunk_header*)(((const uint8_t*)mHeader) + dtohs(mHeader->header.headerSize)); + const ResChunk_header* lastChunk = chunk; + while (((const uint8_t*)chunk) < (mDataEnd-sizeof(ResChunk_header)) && + ((const uint8_t*)chunk) < (mDataEnd-dtohl(chunk->size))) { + status_t err = validate_chunk(chunk, sizeof(ResChunk_header), mDataEnd, "XML"); + if (err != NO_ERROR) { + mError = err; + goto done; + } + const uint16_t type = dtohs(chunk->type); + const size_t size = dtohl(chunk->size); + XML_NOISY(printf("Scanning @ %p: type=0x%x, size=0x%x\n", + (void*)(((uint32_t)chunk)-((uint32_t)mHeader)), type, size)); + if (type == RES_STRING_POOL_TYPE) { + mStrings.setTo(chunk, size); + } else if (type == RES_XML_RESOURCE_MAP_TYPE) { + mResIds = (const uint32_t*) + (((const uint8_t*)chunk)+dtohs(chunk->headerSize)); + mNumResIds = (dtohl(chunk->size)-dtohs(chunk->headerSize))/sizeof(uint32_t); + } else if (type >= RES_XML_FIRST_CHUNK_TYPE + && type <= RES_XML_LAST_CHUNK_TYPE) { + if (validateNode((const ResXMLTree_node*)chunk) != NO_ERROR) { + mError = BAD_TYPE; + goto done; + } + mCurNode = (const ResXMLTree_node*)lastChunk; + if (nextNode() == BAD_DOCUMENT) { + mError = BAD_TYPE; + goto done; + } + mRootNode = mCurNode; + mRootExt = mCurExt; + mRootCode = mEventCode; + break; + } else { + XML_NOISY(printf("Skipping unknown chunk!\n")); + } + lastChunk = chunk; + chunk = (const ResChunk_header*) + (((const uint8_t*)chunk) + size); + } + + if (mRootNode == NULL) { + LOGW("Bad XML block: no root element node found\n"); + mError = BAD_TYPE; + goto done; + } + + mError = mStrings.getError(); + +done: + restart(); + return mError; +} + +status_t ResXMLTree::getError() const +{ + return mError; +} + +void ResXMLTree::uninit() +{ + mError = NO_INIT; + if (mOwnedData) { + free(mOwnedData); + mOwnedData = NULL; + } + restart(); +} + +const ResStringPool& ResXMLTree::getStrings() const +{ + return mStrings; +} + +status_t ResXMLTree::validateNode(const ResXMLTree_node* node) const +{ + const uint16_t eventCode = dtohs(node->header.type); + + status_t err = validate_chunk( + &node->header, sizeof(ResXMLTree_node), + mDataEnd, "ResXMLTree_node"); + + if (err >= NO_ERROR) { + // Only perform additional validation on START nodes + if (eventCode != RES_XML_START_ELEMENT_TYPE) { + return NO_ERROR; + } + + const uint16_t headerSize = dtohs(node->header.headerSize); + const uint32_t size = dtohl(node->header.size); + const ResXMLTree_attrExt* attrExt = (const ResXMLTree_attrExt*) + (((const uint8_t*)node) + headerSize); + // check for sensical values pulled out of the stream so far... + if ((size >= headerSize + sizeof(ResXMLTree_attrExt)) + && ((void*)attrExt > (void*)node)) { + const size_t attrSize = ((size_t)dtohs(attrExt->attributeSize)) + * dtohs(attrExt->attributeCount); + if ((dtohs(attrExt->attributeStart)+attrSize) <= (size-headerSize)) { + return NO_ERROR; + } + LOGW("Bad XML block: node attributes use 0x%x bytes, only have 0x%x bytes\n", + (unsigned int)(dtohs(attrExt->attributeStart)+attrSize), + (unsigned int)(size-headerSize)); + } + else { + LOGW("Bad XML start block: node header size 0x%x, size 0x%x\n", + (unsigned int)headerSize, (unsigned int)size); + } + return BAD_TYPE; + } + + return err; + +#if 0 + const bool isStart = dtohs(node->header.type) == RES_XML_START_ELEMENT_TYPE; + + const uint16_t headerSize = dtohs(node->header.headerSize); + const uint32_t size = dtohl(node->header.size); + + if (headerSize >= (isStart ? sizeof(ResXMLTree_attrNode) : sizeof(ResXMLTree_node))) { + if (size >= headerSize) { + if (((const uint8_t*)node) <= (mDataEnd-size)) { + if (!isStart) { + return NO_ERROR; + } + if ((((size_t)dtohs(node->attributeSize))*dtohs(node->attributeCount)) + <= (size-headerSize)) { + return NO_ERROR; + } + LOGW("Bad XML block: node attributes use 0x%x bytes, only have 0x%x bytes\n", + ((int)dtohs(node->attributeSize))*dtohs(node->attributeCount), + (int)(size-headerSize)); + return BAD_TYPE; + } + LOGW("Bad XML block: node at 0x%x extends beyond data end 0x%x\n", + (int)(((const uint8_t*)node)-((const uint8_t*)mHeader)), (int)mSize); + return BAD_TYPE; + } + LOGW("Bad XML block: node at 0x%x header size 0x%x smaller than total size 0x%x\n", + (int)(((const uint8_t*)node)-((const uint8_t*)mHeader)), + (int)headerSize, (int)size); + return BAD_TYPE; + } + LOGW("Bad XML block: node at 0x%x header size 0x%x too small\n", + (int)(((const uint8_t*)node)-((const uint8_t*)mHeader)), + (int)headerSize); + return BAD_TYPE; +#endif +} + +// -------------------------------------------------------------------- +// -------------------------------------------------------------------- +// -------------------------------------------------------------------- + +struct ResTable::Header +{ + Header() : ownedData(NULL), header(NULL) { } + + void* ownedData; + const ResTable_header* header; + size_t size; + const uint8_t* dataEnd; + size_t index; + void* cookie; + + ResStringPool values; +}; + +struct ResTable::Type +{ + Type(const Header* _header, const Package* _package, size_t count) + : header(_header), package(_package), entryCount(count), + typeSpec(NULL), typeSpecFlags(NULL) { } + const Header* const header; + const Package* const package; + const size_t entryCount; + const ResTable_typeSpec* typeSpec; + const uint32_t* typeSpecFlags; + Vector configs; +}; + +struct ResTable::Package +{ + Package(const Header* _header, const ResTable_package* _package) + : header(_header), package(_package) { } + ~Package() + { + size_t i = types.size(); + while (i > 0) { + i--; + delete types[i]; + } + } + + const Header* const header; + const ResTable_package* const package; + Vector types; + + const Type* getType(size_t idx) const { + return idx < types.size() ? types[idx] : NULL; + } +}; + +// A group of objects describing a particular resource package. +// The first in 'package' is always the root object (from the resource +// table that defined the package); the ones after are skins on top of it. +struct ResTable::PackageGroup +{ + PackageGroup(const String16& _name, uint32_t _id) + : name(_name), id(_id), typeCount(0), bags(NULL) { } + ~PackageGroup() { + clearBagCache(); + const size_t N = packages.size(); + for (size_t i=0; igetType(i); + if (type != NULL) { + bag_set** typeBags = bags[i]; + TABLE_NOISY(printf("typeBags=%p\n", typeBags)); + if (typeBags) { + TABLE_NOISY(printf("type->entryCount=%x\n", type->entryCount)); + const size_t N = type->entryCount; + for (size_t j=0; j packages; + + // Taken from the root package. + ResStringPool typeStrings; + ResStringPool keyStrings; + size_t typeCount; + + // Computed attribute bags, first indexed by the type and second + // by the entry in that type. + bag_set*** bags; +}; + +struct ResTable::bag_set +{ + size_t numAttrs; // number in array + size_t availAttrs; // total space in array + uint32_t typeSpecFlags; + // Followed by 'numAttr' bag_entry structures. +}; + +ResTable::Theme::Theme(const ResTable& table) + : mTable(table) +{ + memset(mPackages, 0, sizeof(mPackages)); +} + +ResTable::Theme::~Theme() +{ + for (size_t i=0; inumTypes; j++) { + theme_entry* te = pi->types[j].entries; + if (te != NULL) { + free(te); + } + } + free(pi); +} + +ResTable::Theme::package_info* ResTable::Theme::copy_package(package_info* pi) +{ + package_info* newpi = (package_info*)malloc( + sizeof(package_info) + (pi->numTypes*sizeof(type_info))); + newpi->numTypes = pi->numTypes; + for (size_t j=0; jnumTypes; j++) { + size_t cnt = pi->types[j].numEntries; + newpi->types[j].numEntries = cnt; + theme_entry* te = pi->types[j].entries; + if (te != NULL) { + theme_entry* newte = (theme_entry*)malloc(cnt*sizeof(theme_entry)); + newpi->types[j].entries = newte; + memcpy(newte, te, cnt*sizeof(theme_entry)); + } else { + newpi->types[j].entries = NULL; + } + } + return newpi; +} + +status_t ResTable::Theme::applyStyle(uint32_t resID, bool force) +{ + const bag_entry* bag; + uint32_t bagTypeSpecFlags = 0; + mTable.lock(); + const ssize_t N = mTable.getBagLocked(resID, &bag, &bagTypeSpecFlags); + TABLE_NOISY(LOGV("Applying style 0x%08x to theme %p, count=%d", resID, this, N)); + if (N < 0) { + mTable.unlock(); + return N; + } + + uint32_t curPackage = 0xffffffff; + ssize_t curPackageIndex = 0; + package_info* curPI = NULL; + uint32_t curType = 0xffffffff; + size_t numEntries = 0; + theme_entry* curEntries = NULL; + + const bag_entry* end = bag + N; + while (bag < end) { + const uint32_t attrRes = bag->map.name.ident; + const uint32_t p = Res_GETPACKAGE(attrRes); + const uint32_t t = Res_GETTYPE(attrRes); + const uint32_t e = Res_GETENTRY(attrRes); + + if (curPackage != p) { + const ssize_t pidx = mTable.getResourcePackageIndex(attrRes); + if (pidx < 0) { + LOGE("Style contains key with bad package: 0x%08x\n", attrRes); + bag++; + continue; + } + curPackage = p; + curPackageIndex = pidx; + curPI = mPackages[pidx]; + if (curPI == NULL) { + PackageGroup* const grp = mTable.mPackageGroups[pidx]; + int cnt = grp->typeCount; + curPI = (package_info*)malloc( + sizeof(package_info) + (cnt*sizeof(type_info))); + curPI->numTypes = cnt; + memset(curPI->types, 0, cnt*sizeof(type_info)); + mPackages[pidx] = curPI; + } + curType = 0xffffffff; + } + if (curType != t) { + if (t >= curPI->numTypes) { + LOGE("Style contains key with bad type: 0x%08x\n", attrRes); + bag++; + continue; + } + curType = t; + curEntries = curPI->types[t].entries; + if (curEntries == NULL) { + PackageGroup* const grp = mTable.mPackageGroups[curPackageIndex]; + const Type* type = grp->packages[0]->getType(t); + int cnt = type != NULL ? type->entryCount : 0; + curEntries = (theme_entry*)malloc(cnt*sizeof(theme_entry)); + memset(curEntries, Res_value::TYPE_NULL, cnt*sizeof(theme_entry)); + curPI->types[t].numEntries = cnt; + curPI->types[t].entries = curEntries; + } + numEntries = curPI->types[t].numEntries; + } + if (e >= numEntries) { + LOGE("Style contains key with bad entry: 0x%08x\n", attrRes); + bag++; + continue; + } + theme_entry* curEntry = curEntries + e; + TABLE_NOISY(LOGV("Attr 0x%08x: type=0x%x, data=0x%08x; curType=0x%x", + attrRes, bag->map.value.dataType, bag->map.value.data, + curEntry->value.dataType)); + if (force || curEntry->value.dataType == Res_value::TYPE_NULL) { + curEntry->stringBlock = bag->stringBlock; + curEntry->typeSpecFlags |= bagTypeSpecFlags; + curEntry->value = bag->map.value; + } + + bag++; + } + + mTable.unlock(); + + //LOGI("Applying style 0x%08x (force=%d) theme %p...\n", resID, force, this); + //dumpToLog(); + + return NO_ERROR; +} + +status_t ResTable::Theme::setTo(const Theme& other) +{ + //LOGI("Setting theme %p from theme %p...\n", this, &other); + //dumpToLog(); + //other.dumpToLog(); + + if (&mTable == &other.mTable) { + for (size_t i=0; i= 0) { + const package_info* const pi = mPackages[p]; + if (pi != NULL) { + if (t < pi->numTypes) { + const type_info& ti = pi->types[t]; + if (e < ti.numEntries) { + const theme_entry& te = ti.entries[e]; + if (outTypeSpecFlags != NULL) { + *outTypeSpecFlags |= te.typeSpecFlags; + } + const uint8_t type = te.value.dataType; + if (type == Res_value::TYPE_ATTRIBUTE) { + if (cnt > 0) { + cnt--; + resID = te.value.data; + continue; + } + LOGW("Too many attribute references, stopped at: 0x%08x\n", resID); + return BAD_INDEX; + } else if (type != Res_value::TYPE_NULL) { + *outValue = te.value; + return te.stringBlock; + } + return BAD_INDEX; + } + } + } + } + break; + + } while (true); + + return BAD_INDEX; +} + +ssize_t ResTable::Theme::resolveAttributeReference(Res_value* inOutValue, + ssize_t blockIndex, uint32_t* outLastRef, + uint32_t* inoutTypeSpecFlags) const +{ + //printf("Resolving type=0x%x\n", inOutValue->dataType); + if (inOutValue->dataType == Res_value::TYPE_ATTRIBUTE) { + uint32_t newTypeSpecFlags; + blockIndex = getAttribute(inOutValue->data, inOutValue, &newTypeSpecFlags); + if (inoutTypeSpecFlags != NULL) *inoutTypeSpecFlags |= newTypeSpecFlags; + //printf("Retrieved attribute new type=0x%x\n", inOutValue->dataType); + if (blockIndex < 0) { + return blockIndex; + } + } + return mTable.resolveReference(inOutValue, blockIndex, outLastRef); +} + +void ResTable::Theme::dumpToLog() const +{ + LOGI("Theme %p:\n", this); + for (size_t i=0; inumTypes; j++) { + type_info& ti = pi->types[j]; + if (ti.numEntries == 0) continue; + + LOGI(" Type #0x%02x:\n", (int)(j+1)); + for (size_t k=0; kgetBuffer(true); + if (data == NULL) { + LOGW("Unable to get buffer of resource asset file"); + return UNKNOWN_ERROR; + } + size_t size = (size_t)asset->getLength(); + return add(data, size, cookie, asset, copyData); +} + +status_t ResTable::add(const void* data, size_t size, void* cookie, + Asset* asset, bool copyData) +{ + if (!data) return NO_ERROR; + Header* header = new Header; + header->index = mHeaders.size(); + header->cookie = cookie; + mHeaders.add(header); + + const bool notDeviceEndian = htods(0xf0) != 0xf0; + + LOAD_TABLE_NOISY( + LOGV("Adding resources to ResTable: data=%p, size=0x%x, cookie=%p, asset=%p, copy=%d\n", + data, size, cookie, asset, copyData)); + + if (copyData || notDeviceEndian) { + header->ownedData = malloc(size); + if (header->ownedData == NULL) { + return (mError=NO_MEMORY); + } + memcpy(header->ownedData, data, size); + data = header->ownedData; + } + + header->header = (const ResTable_header*)data; + header->size = dtohl(header->header->header.size); + //LOGI("Got size 0x%x, again size 0x%x, raw size 0x%x\n", header->size, + // dtohl(header->header->header.size), header->header->header.size); + LOAD_TABLE_NOISY(LOGV("Loading ResTable @%p:\n", header->header)); + LOAD_TABLE_NOISY(printHexData(2, header->header, header->size < 256 ? header->size : 256, + 16, 16, 0, false, printToLogFunc)); + if (dtohs(header->header->header.headerSize) > header->size + || header->size > size) { + LOGW("Bad resource table: header size 0x%x or total size 0x%x is larger than data size 0x%x\n", + (int)dtohs(header->header->header.headerSize), + (int)header->size, (int)size); + return (mError=BAD_TYPE); + } + if (((dtohs(header->header->header.headerSize)|header->size)&0x3) != 0) { + LOGW("Bad resource table: header size 0x%x or total size 0x%x is not on an integer boundary\n", + (int)dtohs(header->header->header.headerSize), + (int)header->size); + return (mError=BAD_TYPE); + } + header->dataEnd = ((const uint8_t*)header->header) + header->size; + + // Iterate through all chunks. + size_t curPackage = 0; + + const ResChunk_header* chunk = + (const ResChunk_header*)(((const uint8_t*)header->header) + + dtohs(header->header->header.headerSize)); + while (((const uint8_t*)chunk) <= (header->dataEnd-sizeof(ResChunk_header)) && + ((const uint8_t*)chunk) <= (header->dataEnd-dtohl(chunk->size))) { + status_t err = validate_chunk(chunk, sizeof(ResChunk_header), header->dataEnd, "ResTable"); + if (err != NO_ERROR) { + return (mError=err); + } + TABLE_NOISY(LOGV("Chunk: type=0x%x, headerSize=0x%x, size=0x%x, pos=%p\n", + dtohs(chunk->type), dtohs(chunk->headerSize), dtohl(chunk->size), + (void*)(((const uint8_t*)chunk) - ((const uint8_t*)header->header)))); + const size_t csize = dtohl(chunk->size); + const uint16_t ctype = dtohs(chunk->type); + if (ctype == RES_STRING_POOL_TYPE) { + if (header->values.getError() != NO_ERROR) { + // Only use the first string chunk; ignore any others that + // may appear. + status_t err = header->values.setTo(chunk, csize); + if (err != NO_ERROR) { + return (mError=err); + } + } else { + LOGW("Multiple string chunks found in resource table."); + } + } else if (ctype == RES_TABLE_PACKAGE_TYPE) { + if (curPackage >= dtohl(header->header->packageCount)) { + LOGW("More package chunks were found than the %d declared in the header.", + dtohl(header->header->packageCount)); + return (mError=BAD_TYPE); + } + if (parsePackage((ResTable_package*)chunk, header) != NO_ERROR) { + return mError; + } + curPackage++; + } else { + LOGW("Unknown chunk type %p in table at %p.\n", + (void*)(int)(ctype), + (void*)(((const uint8_t*)chunk) - ((const uint8_t*)header->header))); + } + chunk = (const ResChunk_header*) + (((const uint8_t*)chunk) + csize); + } + + if (curPackage < dtohl(header->header->packageCount)) { + LOGW("Fewer package chunks (%d) were found than the %d declared in the header.", + (int)curPackage, dtohl(header->header->packageCount)); + return (mError=BAD_TYPE); + } + mError = header->values.getError(); + if (mError != NO_ERROR) { + LOGW("No string values found in resource table!"); + } + TABLE_NOISY(LOGV("Returning from add with mError=%d\n", mError)); + return mError; +} + +status_t ResTable::getError() const +{ + return mError; +} + +void ResTable::uninit() +{ + mError = NO_INIT; + size_t N = mPackageGroups.size(); + for (size_t i=0; iownedData) { + free(header->ownedData); + } + delete header; + } + + mPackageGroups.clear(); + mHeaders.clear(); +} + +bool ResTable::getResourceName(uint32_t resID, resource_name* outName) const +{ + if (mError != NO_ERROR) { + return false; + } + + const ssize_t p = getResourcePackageIndex(resID); + const int t = Res_GETTYPE(resID); + const int e = Res_GETENTRY(resID); + + if (p < 0) { + LOGW("No package identifier when getting name for resource number 0x%08x", resID); + return false; + } + if (t < 0) { + LOGW("No type identifier when getting name for resource number 0x%08x", resID); + return false; + } + + const PackageGroup* const grp = mPackageGroups[p]; + if (grp == NULL) { + LOGW("Bad identifier when getting name for resource number 0x%08x", resID); + return false; + } + if (grp->packages.size() > 0) { + const Package* const package = grp->packages[0]; + + const ResTable_type* type; + const ResTable_entry* entry; + ssize_t offset = getEntry(package, t, e, NULL, &type, &entry, NULL); + if (offset <= 0) { + return false; + } + + outName->package = grp->name.string(); + outName->packageLen = grp->name.size(); + outName->type = grp->typeStrings.stringAt(t, &outName->typeLen); + outName->name = grp->keyStrings.stringAt( + dtohl(entry->key.index), &outName->nameLen); + return true; + } + + return false; +} + +ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag, + uint32_t* outSpecFlags) const +{ + if (mError != NO_ERROR) { + return mError; + } + + const ssize_t p = getResourcePackageIndex(resID); + const int t = Res_GETTYPE(resID); + const int e = Res_GETENTRY(resID); + + if (p < 0) { + LOGW("No package identifier when getting value for resource number 0x%08x", resID); + return BAD_INDEX; + } + if (t < 0) { + LOGW("No type identifier when getting value for resource number 0x%08x", resID); + return BAD_INDEX; + } + + const Res_value* bestValue = NULL; + const Package* bestPackage = NULL; + ResTable_config bestItem; + memset(&bestItem, 0, sizeof(bestItem)); // make the compiler shut up + + if (outSpecFlags != NULL) *outSpecFlags = 0; + + // Look through all resource packages, starting with the most + // recently added. + const PackageGroup* const grp = mPackageGroups[p]; + if (grp == NULL) { + LOGW("Bad identifier when getting value for resource number 0x%08x", resID); + return false; + } + size_t ip = grp->packages.size(); + while (ip > 0) { + ip--; + + const Package* const package = grp->packages[ip]; + + const ResTable_type* type; + const ResTable_entry* entry; + const Type* typeClass; + ssize_t offset = getEntry(package, t, e, &mParams, &type, &entry, &typeClass); + if (offset <= 0) { + if (offset < 0) { + LOGW("Failure getting entry for 0x%08x (t=%d e=%d) in package %d: 0x%08x\n", + resID, t, e, (int)ip, (int)offset); + return offset; + } + continue; + } + + if ((dtohs(entry->flags)&entry->FLAG_COMPLEX) != 0) { + if (!mayBeBag) { + LOGW("Requesting resource %p failed because it is complex\n", + (void*)resID); + } + continue; + } + + TABLE_NOISY(aout << "Resource type data: " + << HexDump(type, dtohl(type->header.size)) << endl); + + if ((size_t)offset > (dtohl(type->header.size)-sizeof(Res_value))) { + LOGW("ResTable_item at %d is beyond type chunk data %d", + (int)offset, dtohl(type->header.size)); + return BAD_TYPE; + } + + const Res_value* item = + (const Res_value*)(((const uint8_t*)type) + offset); + ResTable_config thisConfig; + thisConfig.copyFromDtoH(type->config); + + if (outSpecFlags != NULL) { + if (typeClass->typeSpecFlags != NULL) { + *outSpecFlags |= dtohl(typeClass->typeSpecFlags[e]); + } else { + *outSpecFlags = -1; + } + } + + if (bestPackage != NULL && bestItem.isBetterThan(thisConfig)) { + continue; + } + + bestItem = thisConfig; + bestValue = item; + bestPackage = package; + } + + TABLE_NOISY(printf("Found result: package %p\n", bestPackage)); + + if (bestValue) { + outValue->size = dtohs(bestValue->size); + outValue->res0 = bestValue->res0; + outValue->dataType = bestValue->dataType; + outValue->data = dtohl(bestValue->data); + TABLE_NOISY(size_t len; + printf("Found value: pkg=%d, type=%d, str=%s, int=%d\n", + bestPackage->header->index, + outValue->dataType, + outValue->dataType == bestValue->TYPE_STRING + ? String8(bestPackage->header->values.stringAt( + outValue->data, &len)).string() + : "", + outValue->data)); + return bestPackage->header->index; + } + + return BAD_INDEX; +} + +ssize_t ResTable::resolveReference(Res_value* value, ssize_t blockIndex, + uint32_t* outLastRef, uint32_t* inoutTypeSpecFlags) const +{ + int count=0; + while (blockIndex >= 0 && value->dataType == value->TYPE_REFERENCE + && value->data != 0 && count < 20) { + if (outLastRef) *outLastRef = value->data; + uint32_t lastRef = value->data; + uint32_t newFlags = 0; + const ssize_t newIndex = getResource(value->data, value, true, &newFlags); + //LOGI("Resolving reference d=%p: newIndex=%d, t=0x%02x, d=%p\n", + // (void*)lastRef, (int)newIndex, (int)value->dataType, (void*)value->data); + //printf("Getting reference 0x%08x: newIndex=%d\n", value->data, newIndex); + if (inoutTypeSpecFlags != NULL) *inoutTypeSpecFlags |= newFlags; + if (newIndex < 0) { + // This can fail if the resource being referenced is a style... + // in this case, just return the reference, and expect the + // caller to deal with. + return blockIndex; + } + blockIndex = newIndex; + count++; + } + return blockIndex; +} + +const char16_t* ResTable::valueToString( + const Res_value* value, size_t stringBlock, + char16_t tmpBuffer[TMP_BUFFER_SIZE], size_t* outLen) +{ + if (!value) { + return NULL; + } + if (value->dataType == value->TYPE_STRING) { + return getTableStringBlock(stringBlock)->stringAt(value->data, outLen); + } + // XXX do int to string conversions. + return NULL; +} + +ssize_t ResTable::lockBag(uint32_t resID, const bag_entry** outBag) const +{ + mLock.lock(); + ssize_t err = getBagLocked(resID, outBag); + if (err < NO_ERROR) { + //printf("*** get failed! unlocking\n"); + mLock.unlock(); + } + return err; +} + +void ResTable::unlockBag(const bag_entry* bag) const +{ + //printf("<<< unlockBag %p\n", this); + mLock.unlock(); +} + +void ResTable::lock() const +{ + mLock.lock(); +} + +void ResTable::unlock() const +{ + mLock.unlock(); +} + +ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag, + uint32_t* outTypeSpecFlags) const +{ + if (mError != NO_ERROR) { + return mError; + } + + const ssize_t p = getResourcePackageIndex(resID); + const int t = Res_GETTYPE(resID); + const int e = Res_GETENTRY(resID); + + if (p < 0) { + LOGW("Invalid package identifier when getting bag for resource number 0x%08x", resID); + return BAD_INDEX; + } + if (t < 0) { + LOGW("No type identifier when getting bag for resource number 0x%08x", resID); + return BAD_INDEX; + } + + //printf("Get bag: id=0x%08x, p=%d, t=%d\n", resID, p, t); + PackageGroup* const grp = mPackageGroups[p]; + if (grp == NULL) { + LOGW("Bad identifier when getting bag for resource number 0x%08x", resID); + return false; + } + + if (t >= (int)grp->typeCount) { + LOGW("Type identifier 0x%x is larger than type count 0x%x", + t+1, (int)grp->typeCount); + return BAD_INDEX; + } + + const Package* const basePackage = grp->packages[0]; + + const Type* const typeConfigs = basePackage->getType(t); + + const size_t NENTRY = typeConfigs->entryCount; + if (e >= (int)NENTRY) { + LOGW("Entry identifier 0x%x is larger than entry count 0x%x", + e, (int)typeConfigs->entryCount); + return BAD_INDEX; + } + + // First see if we've already computed this bag... + if (grp->bags) { + bag_set** typeSet = grp->bags[t]; + if (typeSet) { + bag_set* set = typeSet[e]; + if (set) { + if (set != (bag_set*)0xFFFFFFFF) { + if (outTypeSpecFlags != NULL) { + *outTypeSpecFlags = set->typeSpecFlags; + } + *outBag = (bag_entry*)(set+1); + //LOGI("Found existing bag for: %p\n", (void*)resID); + return set->numAttrs; + } + LOGW("Attempt to retrieve bag 0x%08x which is invalid or in a cycle.", + resID); + return BAD_INDEX; + } + } + } + + // Bag not found, we need to compute it! + if (!grp->bags) { + grp->bags = (bag_set***)malloc(sizeof(bag_set*)*grp->typeCount); + if (!grp->bags) return NO_MEMORY; + memset(grp->bags, 0, sizeof(bag_set*)*grp->typeCount); + } + + bag_set** typeSet = grp->bags[t]; + if (!typeSet) { + typeSet = (bag_set**)malloc(sizeof(bag_set*)*NENTRY); + if (!typeSet) return NO_MEMORY; + memset(typeSet, 0, sizeof(bag_set*)*NENTRY); + grp->bags[t] = typeSet; + } + + // Mark that we are currently working on this one. + typeSet[e] = (bag_set*)0xFFFFFFFF; + + // This is what we are building. + bag_set* set = NULL; + + TABLE_NOISY(LOGI("Building bag: %p\n", (void*)resID)); + + // Now collect all bag attributes from all packages. + size_t ip = grp->packages.size(); + while (ip > 0) { + ip--; + + const Package* const package = grp->packages[ip]; + + const ResTable_type* type; + const ResTable_entry* entry; + const Type* typeClass; + LOGV("Getting entry pkg=%p, t=%d, e=%d\n", package, t, e); + ssize_t offset = getEntry(package, t, e, &mParams, &type, &entry, &typeClass); + LOGV("Resulting offset=%d\n", offset); + if (offset <= 0) { + if (offset < 0) { + if (set) free(set); + return offset; + } + continue; + } + + if ((dtohs(entry->flags)&entry->FLAG_COMPLEX) == 0) { + LOGW("Skipping entry %p in package table %d because it is not complex!\n", + (void*)resID, (int)ip); + continue; + } + + const uint16_t entrySize = dtohs(entry->size); + const uint32_t parent = entrySize >= sizeof(ResTable_map_entry) + ? dtohl(((const ResTable_map_entry*)entry)->parent.ident) : 0; + const uint32_t count = entrySize >= sizeof(ResTable_map_entry) + ? dtohl(((const ResTable_map_entry*)entry)->count) : 0; + + size_t N = count; + + TABLE_NOISY(LOGI("Found map: size=%p parent=%p count=%d\n", + entrySize, parent, count)); + + if (set == NULL) { + // If this map inherits from another, we need to start + // with its parent's values. Otherwise start out empty. + TABLE_NOISY(printf("Creating new bag, entrySize=0x%08x, parent=0x%08x\n", + entrySize, parent)); + if (parent) { + const bag_entry* parentBag; + uint32_t parentTypeSpecFlags = 0; + const ssize_t NP = getBagLocked(parent, &parentBag, &parentTypeSpecFlags); + const size_t NT = ((NP >= 0) ? NP : 0) + N; + set = (bag_set*)malloc(sizeof(bag_set)+sizeof(bag_entry)*NT); + if (set == NULL) { + return NO_MEMORY; + } + if (NP > 0) { + memcpy(set+1, parentBag, NP*sizeof(bag_entry)); + set->numAttrs = NP; + TABLE_NOISY(LOGI("Initialized new bag with %d inherited attributes.\n", NP)); + } else { + TABLE_NOISY(LOGI("Initialized new bag with no inherited attributes.\n")); + set->numAttrs = 0; + } + set->availAttrs = NT; + set->typeSpecFlags = parentTypeSpecFlags; + } else { + set = (bag_set*)malloc(sizeof(bag_set)+sizeof(bag_entry)*N); + if (set == NULL) { + return NO_MEMORY; + } + set->numAttrs = 0; + set->availAttrs = N; + set->typeSpecFlags = 0; + } + } + + if (typeClass->typeSpecFlags != NULL) { + set->typeSpecFlags |= dtohl(typeClass->typeSpecFlags[e]); + } else { + set->typeSpecFlags = -1; + } + + // Now merge in the new attributes... + ssize_t curOff = offset; + const ResTable_map* map; + bag_entry* entries = (bag_entry*)(set+1); + size_t curEntry = 0; + uint32_t pos = 0; + TABLE_NOISY(LOGI("Starting with set %p, entries=%p, avail=%d\n", + set, entries, set->availAttrs)); + while (pos < count) { + TABLE_NOISY(printf("Now at %p\n", (void*)curOff)); + + if ((size_t)curOff > (dtohl(type->header.size)-sizeof(ResTable_map))) { + LOGW("ResTable_map at %d is beyond type chunk data %d", + (int)curOff, dtohl(type->header.size)); + return BAD_TYPE; + } + map = (const ResTable_map*)(((const uint8_t*)type) + curOff); + N++; + + const uint32_t newName = htodl(map->name.ident); + bool isInside; + uint32_t oldName = 0; + while ((isInside=(curEntry < set->numAttrs)) + && (oldName=entries[curEntry].map.name.ident) < newName) { + TABLE_NOISY(printf("#%d: Keeping existing attribute: 0x%08x\n", + curEntry, entries[curEntry].map.name.ident)); + curEntry++; + } + + if ((!isInside) || oldName != newName) { + // This is a new attribute... figure out what to do with it. + if (set->numAttrs >= set->availAttrs) { + // Need to alloc more memory... + const size_t newAvail = set->availAttrs+N; + set = (bag_set*)realloc(set, + sizeof(bag_set) + + sizeof(bag_entry)*newAvail); + if (set == NULL) { + return NO_MEMORY; + } + set->availAttrs = newAvail; + entries = (bag_entry*)(set+1); + TABLE_NOISY(printf("Reallocated set %p, entries=%p, avail=%d\n", + set, entries, set->availAttrs)); + } + if (isInside) { + // Going in the middle, need to make space. + memmove(entries+curEntry+1, entries+curEntry, + sizeof(bag_entry)*(set->numAttrs-curEntry)); + set->numAttrs++; + } + TABLE_NOISY(printf("#%d: Inserting new attribute: 0x%08x\n", + curEntry, newName)); + } else { + TABLE_NOISY(printf("#%d: Replacing existing attribute: 0x%08x\n", + curEntry, oldName)); + } + + bag_entry* cur = entries+curEntry; + + cur->stringBlock = package->header->index; + cur->map.name.ident = newName; + cur->map.value.copyFrom_dtoh(map->value); + TABLE_NOISY(printf("Setting entry #%d %p: block=%d, name=0x%08x, type=%d, data=0x%08x\n", + curEntry, cur, cur->stringBlock, cur->map.name.ident, + cur->map.value.dataType, cur->map.value.data)); + + // On to the next! + curEntry++; + pos++; + const size_t size = dtohs(map->value.size); + curOff += size + sizeof(*map)-sizeof(map->value); + }; + if (curEntry > set->numAttrs) { + set->numAttrs = curEntry; + } + } + + // And this is it... + typeSet[e] = set; + if (set) { + if (outTypeSpecFlags != NULL) { + *outTypeSpecFlags = set->typeSpecFlags; + } + *outBag = (bag_entry*)(set+1); + TABLE_NOISY(LOGI("Returning %d attrs\n", set->numAttrs)); + return set->numAttrs; + } + return BAD_INDEX; +} + +void ResTable::setParameters(const ResTable_config* params) +{ + mLock.lock(); + TABLE_GETENTRY(LOGI("Setting parameters: imsi:%d/%d lang:%c%c cnt:%c%c " + "orien:%d touch:%d density:%d key:%d inp:%d nav:%d w:%d h:%d\n", + params->mcc, params->mnc, + params->language[0] ? params->language[0] : '-', + params->language[1] ? params->language[1] : '-', + params->country[0] ? params->country[0] : '-', + params->country[1] ? params->country[1] : '-', + params->orientation, + params->touchscreen, + params->density, + params->keyboard, + params->inputFlags, + params->navigation, + params->screenWidth, + params->screenHeight)); + mParams = *params; + for (size_t i=0; iclearBagCache(); + } + mLock.unlock(); +} + +void ResTable::getParameters(ResTable_config* params) const +{ + mLock.lock(); + *params = mParams; + mLock.unlock(); +} + +struct id_name_map { + uint32_t id; + size_t len; + char16_t name[6]; +}; + +const static id_name_map ID_NAMES[] = { + { ResTable_map::ATTR_TYPE, 5, { '^', 't', 'y', 'p', 'e' } }, + { ResTable_map::ATTR_L10N, 5, { '^', 'l', '1', '0', 'n' } }, + { ResTable_map::ATTR_MIN, 4, { '^', 'm', 'i', 'n' } }, + { ResTable_map::ATTR_MAX, 4, { '^', 'm', 'a', 'x' } }, + { ResTable_map::ATTR_OTHER, 6, { '^', 'o', 't', 'h', 'e', 'r' } }, + { ResTable_map::ATTR_ZERO, 5, { '^', 'z', 'e', 'r', 'o' } }, + { ResTable_map::ATTR_ONE, 4, { '^', 'o', 'n', 'e' } }, + { ResTable_map::ATTR_TWO, 4, { '^', 't', 'w', 'o' } }, + { ResTable_map::ATTR_FEW, 4, { '^', 'f', 'e', 'w' } }, + { ResTable_map::ATTR_MANY, 5, { '^', 'm', 'a', 'n', 'y' } }, +}; + +uint32_t ResTable::identifierForName(const char16_t* name, size_t nameLen, + const char16_t* type, size_t typeLen, + const char16_t* package, + size_t packageLen, + uint32_t* outTypeSpecFlags) const +{ + TABLE_SUPER_NOISY(printf("Identifier for name: error=%d\n", mError)); + + // Check for internal resource identifier as the very first thing, so + // that we will always find them even when there are no resources. + if (name[0] == '^') { + const int N = (sizeof(ID_NAMES)/sizeof(ID_NAMES[0])); + size_t len; + for (int i=0; ilen; + if (len != nameLen) { + continue; + } + for (size_t j=1; jname[j] != name[j]) { + goto nope; + } + } + return m->id; +nope: + ; + } + if (nameLen > 7) { + if (name[1] == 'i' && name[2] == 'n' + && name[3] == 'd' && name[4] == 'e' && name[5] == 'x' + && name[6] == '_') { + int index = atoi(String8(name + 7, nameLen - 7).string()); + if (Res_CHECKID(index)) { + LOGW("Array resource index: %d is too large.", + index); + return 0; + } + return Res_MAKEARRAY(index); + } + } + return 0; + } + + if (mError != NO_ERROR) { + return 0; + } + + // Figure out the package and type we are looking in... + + const char16_t* packageEnd = NULL; + const char16_t* typeEnd = NULL; + const char16_t* const nameEnd = name+nameLen; + const char16_t* p = name; + while (p < nameEnd) { + if (*p == ':') packageEnd = p; + else if (*p == '/') typeEnd = p; + p++; + } + if (*name == '@') name++; + if (name >= nameEnd) { + return 0; + } + + if (packageEnd) { + package = name; + packageLen = packageEnd-name; + name = packageEnd+1; + } else if (!package) { + return 0; + } + + if (typeEnd) { + type = name; + typeLen = typeEnd-name; + name = typeEnd+1; + } else if (!type) { + return 0; + } + + if (name >= nameEnd) { + return 0; + } + nameLen = nameEnd-name; + + TABLE_NOISY(printf("Looking for identifier: type=%s, name=%s, package=%s\n", + String8(type, typeLen).string(), + String8(name, nameLen).string(), + String8(package, packageLen).string())); + + const size_t NG = mPackageGroups.size(); + for (size_t ig=0; igname.string(), group->name.size())) { + TABLE_NOISY(printf("Skipping package group: %s\n", String8(group->name).string())); + continue; + } + + const ssize_t ti = group->typeStrings.indexOfString(type, typeLen); + if (ti < 0) { + TABLE_NOISY(printf("Type not found in package %s\n", String8(group->name).string())); + continue; + } + + const ssize_t ei = group->keyStrings.indexOfString(name, nameLen); + if (ei < 0) { + TABLE_NOISY(printf("Name not found in package %s\n", String8(group->name).string())); + continue; + } + + TABLE_NOISY(printf("Search indices: type=%d, name=%d\n", ti, ei)); + + const Type* const typeConfigs = group->packages[0]->getType(ti); + if (typeConfigs == NULL || typeConfigs->configs.size() <= 0) { + TABLE_NOISY(printf("Expected type structure not found in package %s for idnex %d\n", + String8(group->name).string(), ti)); + } + + size_t NTC = typeConfigs->configs.size(); + for (size_t tci=0; tciconfigs[tci]; + const uint32_t typeOffset = dtohl(ty->entriesStart); + + const uint8_t* const end = ((const uint8_t*)ty) + dtohl(ty->header.size); + const uint32_t* const eindex = (const uint32_t*) + (((const uint8_t*)ty) + dtohs(ty->header.headerSize)); + + const size_t NE = dtohl(ty->entryCount); + for (size_t i=0; i (dtohl(ty->header.size)-sizeof(ResTable_entry))) { + LOGW("ResTable_entry at %d is beyond type chunk data %d", + offset, dtohl(ty->header.size)); + return 0; + } + if ((offset&0x3) != 0) { + LOGW("ResTable_entry at %d (pkg=%d type=%d ent=%d) is not on an integer boundary when looking for %s:%s/%s", + (int)offset, (int)group->id, (int)ti+1, (int)i, + String8(package, packageLen).string(), + String8(type, typeLen).string(), + String8(name, nameLen).string()); + return 0; + } + + const ResTable_entry* const entry = (const ResTable_entry*) + (((const uint8_t*)ty) + offset); + if (dtohs(entry->size) < sizeof(*entry)) { + LOGW("ResTable_entry size %d is too small", dtohs(entry->size)); + return BAD_TYPE; + } + + TABLE_SUPER_NOISY(printf("Looking at entry #%d: want str %d, have %d\n", + i, ei, dtohl(entry->key.index))); + if (dtohl(entry->key.index) == (size_t)ei) { + if (outTypeSpecFlags) { + *outTypeSpecFlags = typeConfigs->typeSpecFlags[i]; + } + return Res_MAKEID(group->id-1, ti, i); + } + } + } + } + + return 0; +} + +bool ResTable::expandResourceRef(const uint16_t* refStr, size_t refLen, + String16* outPackage, + String16* outType, + String16* outName, + const String16* defType, + const String16* defPackage, + const char** outErrorMsg) +{ + const char16_t* packageEnd = NULL; + const char16_t* typeEnd = NULL; + const char16_t* p = refStr; + const char16_t* const end = p + refLen; + while (p < end) { + if (*p == ':') packageEnd = p; + else if (*p == '/') { + typeEnd = p; + break; + } + p++; + } + p = refStr; + if (*p == '@') p++; + + if (packageEnd) { + *outPackage = String16(p, packageEnd-p); + p = packageEnd+1; + } else { + if (!defPackage) { + if (outErrorMsg) { + *outErrorMsg = "No resource package specified"; + } + return false; + } + *outPackage = *defPackage; + } + if (typeEnd) { + *outType = String16(p, typeEnd-p); + p = typeEnd+1; + } else { + if (!defType) { + if (outErrorMsg) { + *outErrorMsg = "No resource type specified"; + } + return false; + } + *outType = *defType; + } + *outName = String16(p, end-p); + return true; +} + +static uint32_t get_hex(char c, bool* outError) +{ + if (c >= '0' && c <= '9') { + return c - '0'; + } else if (c >= 'a' && c <= 'f') { + return c - 'a' + 0xa; + } else if (c >= 'A' && c <= 'F') { + return c - 'A' + 0xa; + } + *outError = true; + return 0; +} + +struct unit_entry +{ + const char* name; + size_t len; + uint8_t type; + uint32_t unit; + float scale; +}; + +static const unit_entry unitNames[] = { + { "px", strlen("px"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_PX, 1.0f }, + { "dip", strlen("dip"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_DIP, 1.0f }, + { "dp", strlen("dp"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_DIP, 1.0f }, + { "sp", strlen("sp"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_SP, 1.0f }, + { "pt", strlen("pt"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_PT, 1.0f }, + { "in", strlen("in"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_IN, 1.0f }, + { "mm", strlen("mm"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_MM, 1.0f }, + { "%", strlen("%"), Res_value::TYPE_FRACTION, Res_value::COMPLEX_UNIT_FRACTION, 1.0f/100 }, + { "%p", strlen("%p"), Res_value::TYPE_FRACTION, Res_value::COMPLEX_UNIT_FRACTION_PARENT, 1.0f/100 }, + { NULL, 0, 0, 0, 0 } +}; + +static bool parse_unit(const char* str, Res_value* outValue, + float* outScale, const char** outEnd) +{ + const char* end = str; + while (*end != 0 && !isspace((unsigned char)*end)) { + end++; + } + const size_t len = end-str; + + const char* realEnd = end; + while (*realEnd != 0 && isspace((unsigned char)*realEnd)) { + realEnd++; + } + if (*realEnd != 0) { + return false; + } + + const unit_entry* cur = unitNames; + while (cur->name) { + if (len == cur->len && strncmp(cur->name, str, len) == 0) { + outValue->dataType = cur->type; + outValue->data = cur->unit << Res_value::COMPLEX_UNIT_SHIFT; + *outScale = cur->scale; + *outEnd = end; + //printf("Found unit %s for %s\n", cur->name, str); + return true; + } + cur++; + } + + return false; +} + + +bool ResTable::stringToInt(const char16_t* s, size_t len, Res_value* outValue) +{ + while (len > 0 && isspace16(*s)) { + s++; + len--; + } + + if (len <= 0) { + return false; + } + + size_t i = 0; + int32_t val = 0; + bool neg = false; + + if (*s == '-') { + neg = true; + i++; + } + + if (s[i] < '0' || s[i] > '9') { + return false; + } + + // Decimal or hex? + if (s[i] == '0' && s[i+1] == 'x') { + if (outValue) + outValue->dataType = outValue->TYPE_INT_HEX; + i += 2; + bool error = false; + while (i < len && !error) { + val = (val*16) + get_hex(s[i], &error); + i++; + } + if (error) { + return false; + } + } else { + if (outValue) + outValue->dataType = outValue->TYPE_INT_DEC; + while (i < len) { + if (s[i] < '0' || s[i] > '9') { + return false; + } + val = (val*10) + s[i]-'0'; + i++; + } + } + + if (neg) val = -val; + + while (i < len && isspace16(s[i])) { + i++; + } + + if (i == len) { + if (outValue) + outValue->data = val; + return true; + } + + return false; +} + +bool ResTable::stringToFloat(const char16_t* s, size_t len, Res_value* outValue) +{ + while (len > 0 && isspace16(*s)) { + s++; + len--; + } + + if (len <= 0) { + return false; + } + + char buf[128]; + int i=0; + while (len > 0 && *s != 0 && i < 126) { + if (*s > 255) { + return false; + } + buf[i++] = *s++; + len--; + } + + if (len > 0) { + return false; + } + if (buf[0] < '0' && buf[0] > '9' && buf[0] != '.') { + return false; + } + + buf[i] = 0; + const char* end; + float f = strtof(buf, (char**)&end); + + if (*end != 0 && !isspace((unsigned char)*end)) { + // Might be a unit... + float scale; + if (parse_unit(end, outValue, &scale, &end)) { + f *= scale; + const bool neg = f < 0; + if (neg) f = -f; + uint64_t bits = (uint64_t)(f*(1<<23)+.5f); + uint32_t radix; + uint32_t shift; + if ((bits&0x7fffff) == 0) { + // Always use 23p0 if there is no fraction, just to make + // things easier to read. + radix = Res_value::COMPLEX_RADIX_23p0; + shift = 23; + } else if ((bits&0xffffffffff800000LL) == 0) { + // Magnitude is zero -- can fit in 0 bits of precision. + radix = Res_value::COMPLEX_RADIX_0p23; + shift = 0; + } else if ((bits&0xffffffff80000000LL) == 0) { + // Magnitude can fit in 8 bits of precision. + radix = Res_value::COMPLEX_RADIX_8p15; + shift = 8; + } else if ((bits&0xffffff8000000000LL) == 0) { + // Magnitude can fit in 16 bits of precision. + radix = Res_value::COMPLEX_RADIX_16p7; + shift = 16; + } else { + // Magnitude needs entire range, so no fractional part. + radix = Res_value::COMPLEX_RADIX_23p0; + shift = 23; + } + int32_t mantissa = (int32_t)( + (bits>>shift) & Res_value::COMPLEX_MANTISSA_MASK); + if (neg) { + mantissa = (-mantissa) & Res_value::COMPLEX_MANTISSA_MASK; + } + outValue->data |= + (radix<data); + return true; + } + return false; + } + + while (*end != 0 && isspace((unsigned char)*end)) { + end++; + } + + if (*end == 0) { + if (outValue) { + outValue->dataType = outValue->TYPE_FLOAT; + *(float*)(&outValue->data) = f; + return true; + } + } + + return false; +} + +bool ResTable::stringToValue(Res_value* outValue, String16* outString, + const char16_t* s, size_t len, + bool preserveSpaces, bool coerceType, + uint32_t attrID, + const String16* defType, + const String16* defPackage, + Accessor* accessor, + void* accessorCookie, + uint32_t attrType, + bool enforcePrivate) const +{ + bool localizationSetting = accessor != NULL && accessor->getLocalizationSetting(); + const char* errorMsg = NULL; + + outValue->size = sizeof(Res_value); + outValue->res0 = 0; + + // First strip leading/trailing whitespace. Do this before handling + // escapes, so they can be used to force whitespace into the string. + if (!preserveSpaces) { + while (len > 0 && isspace16(*s)) { + s++; + len--; + } + while (len > 0 && isspace16(s[len-1])) { + len--; + } + // If the string ends with '\', then we keep the space after it. + if (len > 0 && s[len-1] == '\\' && s[len] != 0) { + len++; + } + } + + //printf("Value for: %s\n", String8(s, len).string()); + + uint32_t l10nReq = ResTable_map::L10N_NOT_REQUIRED; + uint32_t attrMin = 0x80000000, attrMax = 0x7fffffff; + bool fromAccessor = false; + if (attrID != 0 && !Res_INTERNALID(attrID)) { + const ssize_t p = getResourcePackageIndex(attrID); + const bag_entry* bag; + ssize_t cnt = p >= 0 ? lockBag(attrID, &bag) : -1; + //printf("For attr 0x%08x got bag of %d\n", attrID, cnt); + if (cnt >= 0) { + while (cnt > 0) { + //printf("Entry 0x%08x = 0x%08x\n", bag->map.name.ident, bag->map.value.data); + switch (bag->map.name.ident) { + case ResTable_map::ATTR_TYPE: + attrType = bag->map.value.data; + break; + case ResTable_map::ATTR_MIN: + attrMin = bag->map.value.data; + break; + case ResTable_map::ATTR_MAX: + attrMax = bag->map.value.data; + break; + case ResTable_map::ATTR_L10N: + l10nReq = bag->map.value.data; + break; + } + bag++; + cnt--; + } + unlockBag(bag); + } else if (accessor && accessor->getAttributeType(attrID, &attrType)) { + fromAccessor = true; + if (attrType == ResTable_map::TYPE_ENUM + || attrType == ResTable_map::TYPE_FLAGS + || attrType == ResTable_map::TYPE_INTEGER) { + accessor->getAttributeMin(attrID, &attrMin); + accessor->getAttributeMax(attrID, &attrMax); + } + if (localizationSetting) { + l10nReq = accessor->getAttributeL10N(attrID); + } + } + } + + const bool canStringCoerce = + coerceType && (attrType&ResTable_map::TYPE_STRING) != 0; + + if (*s == '@') { + outValue->dataType = outValue->TYPE_REFERENCE; + + // Note: we don't check attrType here because the reference can + // be to any other type; we just need to count on the client making + // sure the referenced type is correct. + + //printf("Looking up ref: %s\n", String8(s, len).string()); + + // It's a reference! + if (len == 5 && s[1]=='n' && s[2]=='u' && s[3]=='l' && s[4]=='l') { + outValue->data = 0; + return true; + } else { + bool createIfNotFound = false; + const char16_t* resourceRefName; + int resourceNameLen; + if (len > 2 && s[1] == '+') { + createIfNotFound = true; + resourceRefName = s + 2; + resourceNameLen = len - 2; + } else if (len > 2 && s[1] == '*') { + enforcePrivate = false; + resourceRefName = s + 2; + resourceNameLen = len - 2; + } else { + createIfNotFound = false; + resourceRefName = s + 1; + resourceNameLen = len - 1; + } + String16 package, type, name; + if (!expandResourceRef(resourceRefName,resourceNameLen, &package, &type, &name, + defType, defPackage, &errorMsg)) { + if (accessor != NULL) { + accessor->reportError(accessorCookie, errorMsg); + } + return false; + } + + uint32_t specFlags = 0; + uint32_t rid = identifierForName(name.string(), name.size(), type.string(), + type.size(), package.string(), package.size(), &specFlags); + if (rid != 0) { + if (enforcePrivate) { + if ((specFlags&ResTable_typeSpec::SPEC_PUBLIC) == 0) { + if (accessor != NULL) { + accessor->reportError(accessorCookie, "Resource is not public."); + } + return false; + } + } + if (!accessor) { + outValue->data = rid; + return true; + } + rid = Res_MAKEID( + accessor->getRemappedPackage(Res_GETPACKAGE(rid)), + Res_GETTYPE(rid), Res_GETENTRY(rid)); + TABLE_NOISY(printf("Incl %s:%s/%s: 0x%08x\n", + String8(package).string(), String8(type).string(), + String8(name).string(), rid)); + outValue->data = rid; + return true; + } + + if (accessor) { + uint32_t rid = accessor->getCustomResourceWithCreation(package, type, name, + createIfNotFound); + if (rid != 0) { + TABLE_NOISY(printf("Pckg %s:%s/%s: 0x%08x\n", + String8(package).string(), String8(type).string(), + String8(name).string(), rid)); + outValue->data = rid; + return true; + } + } + } + + if (accessor != NULL) { + accessor->reportError(accessorCookie, "No resource found that matches the given name"); + } + return false; + } + + // if we got to here, and localization is required and it's not a reference, + // complain and bail. + if (l10nReq == ResTable_map::L10N_SUGGESTED) { + if (localizationSetting) { + if (accessor != NULL) { + accessor->reportError(accessorCookie, "This attribute must be localized."); + } + } + } + + if (*s == '#') { + // It's a color! Convert to an integer of the form 0xaarrggbb. + uint32_t color = 0; + bool error = false; + if (len == 4) { + outValue->dataType = outValue->TYPE_INT_COLOR_RGB4; + color |= 0xFF000000; + color |= get_hex(s[1], &error) << 20; + color |= get_hex(s[1], &error) << 16; + color |= get_hex(s[2], &error) << 12; + color |= get_hex(s[2], &error) << 8; + color |= get_hex(s[3], &error) << 4; + color |= get_hex(s[3], &error); + } else if (len == 5) { + outValue->dataType = outValue->TYPE_INT_COLOR_ARGB4; + color |= get_hex(s[1], &error) << 28; + color |= get_hex(s[1], &error) << 24; + color |= get_hex(s[2], &error) << 20; + color |= get_hex(s[2], &error) << 16; + color |= get_hex(s[3], &error) << 12; + color |= get_hex(s[3], &error) << 8; + color |= get_hex(s[4], &error) << 4; + color |= get_hex(s[4], &error); + } else if (len == 7) { + outValue->dataType = outValue->TYPE_INT_COLOR_RGB8; + color |= 0xFF000000; + color |= get_hex(s[1], &error) << 20; + color |= get_hex(s[2], &error) << 16; + color |= get_hex(s[3], &error) << 12; + color |= get_hex(s[4], &error) << 8; + color |= get_hex(s[5], &error) << 4; + color |= get_hex(s[6], &error); + } else if (len == 9) { + outValue->dataType = outValue->TYPE_INT_COLOR_ARGB8; + color |= get_hex(s[1], &error) << 28; + color |= get_hex(s[2], &error) << 24; + color |= get_hex(s[3], &error) << 20; + color |= get_hex(s[4], &error) << 16; + color |= get_hex(s[5], &error) << 12; + color |= get_hex(s[6], &error) << 8; + color |= get_hex(s[7], &error) << 4; + color |= get_hex(s[8], &error); + } else { + error = true; + } + if (!error) { + if ((attrType&ResTable_map::TYPE_COLOR) == 0) { + if (!canStringCoerce) { + if (accessor != NULL) { + accessor->reportError(accessorCookie, + "Color types not allowed"); + } + return false; + } + } else { + outValue->data = color; + //printf("Color input=%s, output=0x%x\n", String8(s, len).string(), color); + return true; + } + } else { + if ((attrType&ResTable_map::TYPE_COLOR) != 0) { + if (accessor != NULL) { + accessor->reportError(accessorCookie, "Color value not valid --" + " must be #rgb, #argb, #rrggbb, or #aarrggbb"); + } + #if 0 + fprintf(stderr, "%s: Color ID %s value %s is not valid\n", + "Resource File", //(const char*)in->getPrintableSource(), + String8(*curTag).string(), + String8(s, len).string()); + #endif + return false; + } + } + } + + if (*s == '?') { + outValue->dataType = outValue->TYPE_ATTRIBUTE; + + // Note: we don't check attrType here because the reference can + // be to any other type; we just need to count on the client making + // sure the referenced type is correct. + + //printf("Looking up attr: %s\n", String8(s, len).string()); + + static const String16 attr16("attr"); + String16 package, type, name; + if (!expandResourceRef(s+1, len-1, &package, &type, &name, + &attr16, defPackage, &errorMsg)) { + if (accessor != NULL) { + accessor->reportError(accessorCookie, errorMsg); + } + return false; + } + + //printf("Pkg: %s, Type: %s, Name: %s\n", + // String8(package).string(), String8(type).string(), + // String8(name).string()); + uint32_t specFlags = 0; + uint32_t rid = + identifierForName(name.string(), name.size(), + type.string(), type.size(), + package.string(), package.size(), &specFlags); + if (rid != 0) { + if (enforcePrivate) { + if ((specFlags&ResTable_typeSpec::SPEC_PUBLIC) == 0) { + if (accessor != NULL) { + accessor->reportError(accessorCookie, "Attribute is not public."); + } + return false; + } + } + if (!accessor) { + outValue->data = rid; + return true; + } + rid = Res_MAKEID( + accessor->getRemappedPackage(Res_GETPACKAGE(rid)), + Res_GETTYPE(rid), Res_GETENTRY(rid)); + //printf("Incl %s:%s/%s: 0x%08x\n", + // String8(package).string(), String8(type).string(), + // String8(name).string(), rid); + outValue->data = rid; + return true; + } + + if (accessor) { + uint32_t rid = accessor->getCustomResource(package, type, name); + if (rid != 0) { + //printf("Mine %s:%s/%s: 0x%08x\n", + // String8(package).string(), String8(type).string(), + // String8(name).string(), rid); + outValue->data = rid; + return true; + } + } + + if (accessor != NULL) { + accessor->reportError(accessorCookie, "No resource found that matches the given name"); + } + return false; + } + + if (stringToInt(s, len, outValue)) { + if ((attrType&ResTable_map::TYPE_INTEGER) == 0) { + // If this type does not allow integers, but does allow floats, + // fall through on this error case because the float type should + // be able to accept any integer value. + if (!canStringCoerce && (attrType&ResTable_map::TYPE_FLOAT) == 0) { + if (accessor != NULL) { + accessor->reportError(accessorCookie, "Integer types not allowed"); + } + return false; + } + } else { + if (((int32_t)outValue->data) < ((int32_t)attrMin) + || ((int32_t)outValue->data) > ((int32_t)attrMax)) { + if (accessor != NULL) { + accessor->reportError(accessorCookie, "Integer value out of range"); + } + return false; + } + return true; + } + } + + if (stringToFloat(s, len, outValue)) { + if (outValue->dataType == Res_value::TYPE_DIMENSION) { + if ((attrType&ResTable_map::TYPE_DIMENSION) != 0) { + return true; + } + if (!canStringCoerce) { + if (accessor != NULL) { + accessor->reportError(accessorCookie, "Dimension types not allowed"); + } + return false; + } + } else if (outValue->dataType == Res_value::TYPE_FRACTION) { + if ((attrType&ResTable_map::TYPE_FRACTION) != 0) { + return true; + } + if (!canStringCoerce) { + if (accessor != NULL) { + accessor->reportError(accessorCookie, "Fraction types not allowed"); + } + return false; + } + } else if ((attrType&ResTable_map::TYPE_FLOAT) == 0) { + if (!canStringCoerce) { + if (accessor != NULL) { + accessor->reportError(accessorCookie, "Float types not allowed"); + } + return false; + } + } else { + return true; + } + } + + if (len == 4) { + if ((s[0] == 't' || s[0] == 'T') && + (s[1] == 'r' || s[1] == 'R') && + (s[2] == 'u' || s[2] == 'U') && + (s[3] == 'e' || s[3] == 'E')) { + if ((attrType&ResTable_map::TYPE_BOOLEAN) == 0) { + if (!canStringCoerce) { + if (accessor != NULL) { + accessor->reportError(accessorCookie, "Boolean types not allowed"); + } + return false; + } + } else { + outValue->dataType = outValue->TYPE_INT_BOOLEAN; + outValue->data = (uint32_t)-1; + return true; + } + } + } + + if (len == 5) { + if ((s[0] == 'f' || s[0] == 'F') && + (s[1] == 'a' || s[1] == 'A') && + (s[2] == 'l' || s[2] == 'L') && + (s[3] == 's' || s[3] == 'S') && + (s[4] == 'e' || s[4] == 'E')) { + if ((attrType&ResTable_map::TYPE_BOOLEAN) == 0) { + if (!canStringCoerce) { + if (accessor != NULL) { + accessor->reportError(accessorCookie, "Boolean types not allowed"); + } + return false; + } + } else { + outValue->dataType = outValue->TYPE_INT_BOOLEAN; + outValue->data = 0; + return true; + } + } + } + + if ((attrType&ResTable_map::TYPE_ENUM) != 0) { + const ssize_t p = getResourcePackageIndex(attrID); + const bag_entry* bag; + ssize_t cnt = p >= 0 ? lockBag(attrID, &bag) : -1; + //printf("Got %d for enum\n", cnt); + if (cnt >= 0) { + resource_name rname; + while (cnt > 0) { + if (!Res_INTERNALID(bag->map.name.ident)) { + //printf("Trying attr #%08x\n", bag->map.name.ident); + if (getResourceName(bag->map.name.ident, &rname)) { + #if 0 + printf("Matching %s against %s (0x%08x)\n", + String8(s, len).string(), + String8(rname.name, rname.nameLen).string(), + bag->map.name.ident); + #endif + if (strzcmp16(s, len, rname.name, rname.nameLen) == 0) { + outValue->dataType = bag->map.value.dataType; + outValue->data = bag->map.value.data; + unlockBag(bag); + return true; + } + } + + } + bag++; + cnt--; + } + unlockBag(bag); + } + + if (fromAccessor) { + if (accessor->getAttributeEnum(attrID, s, len, outValue)) { + return true; + } + } + } + + if ((attrType&ResTable_map::TYPE_FLAGS) != 0) { + const ssize_t p = getResourcePackageIndex(attrID); + const bag_entry* bag; + ssize_t cnt = p >= 0 ? lockBag(attrID, &bag) : -1; + //printf("Got %d for flags\n", cnt); + if (cnt >= 0) { + bool failed = false; + resource_name rname; + outValue->dataType = Res_value::TYPE_INT_HEX; + outValue->data = 0; + const char16_t* end = s + len; + const char16_t* pos = s; + while (pos < end && !failed) { + const char16_t* start = pos; + end++; + while (pos < end && *pos != '|') { + pos++; + } + //printf("Looking for: %s\n", String8(start, pos-start).string()); + const bag_entry* bagi = bag; + ssize_t i; + for (i=0; imap.name.ident)) { + //printf("Trying attr #%08x\n", bagi->map.name.ident); + if (getResourceName(bagi->map.name.ident, &rname)) { + #if 0 + printf("Matching %s against %s (0x%08x)\n", + String8(start,pos-start).string(), + String8(rname.name, rname.nameLen).string(), + bagi->map.name.ident); + #endif + if (strzcmp16(start, pos-start, rname.name, rname.nameLen) == 0) { + outValue->data |= bagi->map.value.data; + break; + } + } + } + } + if (i >= cnt) { + // Didn't find this flag identifier. + failed = true; + } + if (pos < end) { + pos++; + } + } + unlockBag(bag); + if (!failed) { + //printf("Final flag value: 0x%lx\n", outValue->data); + return true; + } + } + + + if (fromAccessor) { + if (accessor->getAttributeFlags(attrID, s, len, outValue)) { + //printf("Final flag value: 0x%lx\n", outValue->data); + return true; + } + } + } + + if ((attrType&ResTable_map::TYPE_STRING) == 0) { + if (accessor != NULL) { + accessor->reportError(accessorCookie, "String types not allowed"); + } + return false; + } + + // Generic string handling... + outValue->dataType = outValue->TYPE_STRING; + if (outString) { + bool failed = collectString(outString, s, len, preserveSpaces, &errorMsg); + if (accessor != NULL) { + accessor->reportError(accessorCookie, errorMsg); + } + return failed; + } + + return true; +} + +bool ResTable::collectString(String16* outString, + const char16_t* s, size_t len, + bool preserveSpaces, + const char** outErrorMsg, + bool append) +{ + String16 tmp; + + char quoted = 0; + const char16_t* p = s; + while (p < (s+len)) { + while (p < (s+len)) { + const char16_t c = *p; + if (c == '\\') { + break; + } + if (!preserveSpaces) { + if (quoted == 0 && isspace16(c) + && (c != ' ' || isspace16(*(p+1)))) { + break; + } + if (c == '"' && (quoted == 0 || quoted == '"')) { + break; + } + if (c == '\'' && (quoted == 0 || quoted == '\'')) { + break; + } + } + p++; + } + if (p < (s+len)) { + if (p > s) { + tmp.append(String16(s, p-s)); + } + if (!preserveSpaces && (*p == '"' || *p == '\'')) { + if (quoted == 0) { + quoted = *p; + } else { + quoted = 0; + } + p++; + } else if (!preserveSpaces && isspace16(*p)) { + // Space outside of a quote -- consume all spaces and + // leave a single plain space char. + tmp.append(String16(" ")); + p++; + while (p < (s+len) && isspace16(*p)) { + p++; + } + } else if (*p == '\\') { + p++; + if (p < (s+len)) { + switch (*p) { + case 't': + tmp.append(String16("\t")); + break; + case 'n': + tmp.append(String16("\n")); + break; + case '#': + tmp.append(String16("#")); + break; + case '@': + tmp.append(String16("@")); + break; + case '?': + tmp.append(String16("?")); + break; + case '"': + tmp.append(String16("\"")); + break; + case '\'': + tmp.append(String16("'")); + break; + case '\\': + tmp.append(String16("\\")); + break; + case 'u': + { + char16_t chr = 0; + int i = 0; + while (i < 4 && p[1] != 0) { + p++; + i++; + int c; + if (*p >= '0' && *p <= '9') { + c = *p - '0'; + } else if (*p >= 'a' && *p <= 'f') { + c = *p - 'a' + 10; + } else if (*p >= 'A' && *p <= 'F') { + c = *p - 'A' + 10; + } else { + if (outErrorMsg) { + *outErrorMsg = "Bad character in \\u unicode escape sequence"; + } + return false; + } + chr = (chr<<4) | c; + } + tmp.append(String16(&chr, 1)); + } break; + default: + // ignore unknown escape chars. + break; + } + p++; + } + } + len -= (p-s); + s = p; + } + } + + if (tmp.size() != 0) { + if (len > 0) { + tmp.append(String16(s, len)); + } + if (append) { + outString->append(tmp); + } else { + outString->setTo(tmp); + } + } else { + if (append) { + outString->append(String16(s, len)); + } else { + outString->setTo(s, len); + } + } + + return true; +} + +size_t ResTable::getBasePackageCount() const +{ + if (mError != NO_ERROR) { + return 0; + } + return mPackageGroups.size(); +} + +const char16_t* ResTable::getBasePackageName(size_t idx) const +{ + if (mError != NO_ERROR) { + return 0; + } + LOG_FATAL_IF(idx >= mPackageGroups.size(), + "Requested package index %d past package count %d", + (int)idx, (int)mPackageGroups.size()); + return mPackageGroups[idx]->name.string(); +} + +uint32_t ResTable::getBasePackageId(size_t idx) const +{ + if (mError != NO_ERROR) { + return 0; + } + LOG_FATAL_IF(idx >= mPackageGroups.size(), + "Requested package index %d past package count %d", + (int)idx, (int)mPackageGroups.size()); + return mPackageGroups[idx]->id; +} + +size_t ResTable::getTableCount() const +{ + return mHeaders.size(); +} + +const ResStringPool* ResTable::getTableStringBlock(size_t index) const +{ + return &mHeaders[index]->values; +} + +void* ResTable::getTableCookie(size_t index) const +{ + return mHeaders[index]->cookie; +} + +void ResTable::getConfigurations(Vector* configs) const +{ + const size_t I = mPackageGroups.size(); + for (size_t i=0; ipackages.size(); + for (size_t j=0; jpackages[j]; + const size_t K = package->types.size(); + for (size_t k=0; ktypes[k]; + if (type == NULL) continue; + const size_t L = type->configs.size(); + for (size_t l=0; lconfigs[l]; + const ResTable_config* cfg = &config->config; + // only insert unique + const size_t M = configs->size(); + size_t m; + for (m=0; madd(*cfg); + } + } + } + } + } +} + +void ResTable::getLocales(Vector* locales) const +{ + Vector configs; + LOGD("calling getConfigurations"); + getConfigurations(&configs); + LOGD("called getConfigurations size=%d", (int)configs.size()); + const size_t I = configs.size(); + for (size_t i=0; isize(); + size_t j; + for (j=0; jadd(String8(locale)); + } + } +} + +ssize_t ResTable::getEntry( + const Package* package, int typeIndex, int entryIndex, + const ResTable_config* config, + const ResTable_type** outType, const ResTable_entry** outEntry, + const Type** outTypeClass) const +{ + LOGV("Getting entry from package %p\n", package); + const ResTable_package* const pkg = package->package; + + const Type* allTypes = package->getType(typeIndex); + LOGV("allTypes=%p\n", allTypes); + if (allTypes == NULL) { + LOGV("Skipping entry type index 0x%02x because type is NULL!\n", typeIndex); + return 0; + } + + if ((size_t)entryIndex >= allTypes->entryCount) { + LOGW("getEntry failing because entryIndex %d is beyond type entryCount %d", + entryIndex, (int)allTypes->entryCount); + return BAD_TYPE; + } + + const ResTable_type* type = NULL; + uint32_t offset = ResTable_type::NO_ENTRY; + ResTable_config bestConfig; + memset(&bestConfig, 0, sizeof(bestConfig)); // make the compiler shut up + + const size_t NT = allTypes->configs.size(); + for (size_t i=0; iconfigs[i]; + if (thisType == NULL) continue; + + ResTable_config thisConfig; + thisConfig.copyFromDtoH(thisType->config); + + TABLE_GETENTRY(LOGI("Match entry 0x%x in type 0x%x (sz 0x%x): imsi:%d/%d=%d/%d lang:%c%c=%c%c cnt:%c%c=%c%c " + "orien:%d=%d touch:%d=%d density:%d=%d key:%d=%d inp:%d=%d nav:%d=%d w:%d=%d h:%d=%d\n", + entryIndex, typeIndex+1, dtohl(thisType->config.size), + thisConfig.mcc, thisConfig.mnc, + config ? config->mcc : 0, config ? config->mnc : 0, + thisConfig.language[0] ? thisConfig.language[0] : '-', + thisConfig.language[1] ? thisConfig.language[1] : '-', + config && config->language[0] ? config->language[0] : '-', + config && config->language[1] ? config->language[1] : '-', + thisConfig.country[0] ? thisConfig.country[0] : '-', + thisConfig.country[1] ? thisConfig.country[1] : '-', + config && config->country[0] ? config->country[0] : '-', + config && config->country[1] ? config->country[1] : '-', + thisConfig.orientation, + config ? config->orientation : 0, + thisConfig.touchscreen, + config ? config->touchscreen : 0, + thisConfig.density, + config ? config->density : 0, + thisConfig.keyboard, + config ? config->keyboard : 0, + thisConfig.inputFlags, + config ? config->inputFlags : 0, + thisConfig.navigation, + config ? config->navigation : 0, + thisConfig.screenWidth, + config ? config->screenWidth : 0, + thisConfig.screenHeight, + config ? config->screenHeight : 0)); + + // Check to make sure this one is valid for the current parameters. + if (config && !thisConfig.match(*config)) { + TABLE_GETENTRY(LOGI("Does not match config!\n")); + continue; + } + + // Check if there is the desired entry in this type. + + const uint8_t* const end = ((const uint8_t*)thisType) + + dtohl(thisType->header.size); + const uint32_t* const eindex = (const uint32_t*) + (((const uint8_t*)thisType) + dtohs(thisType->header.headerSize)); + + uint32_t thisOffset = dtohl(eindex[entryIndex]); + if (thisOffset == ResTable_type::NO_ENTRY) { + TABLE_GETENTRY(LOGI("Skipping because it is not defined!\n")); + continue; + } + + if (type != NULL) { + // Check if this one is less specific than the last found. If so, + // we will skip it. We check starting with things we most care + // about to those we least care about. + if (!thisConfig.isBetterThan(bestConfig, config)) { + TABLE_GETENTRY(LOGI("This config is worse than last!\n")); + continue; + } + } + + type = thisType; + offset = thisOffset; + bestConfig = thisConfig; + TABLE_GETENTRY(LOGI("Best entry so far -- using it!\n")); + if (!config) break; + } + + if (type == NULL) { + TABLE_GETENTRY(LOGI("No value found for requested entry!\n")); + return BAD_INDEX; + } + + offset += dtohl(type->entriesStart); + TABLE_NOISY(aout << "Looking in resource table " << package->header->header + << ", typeOff=" + << (void*)(((const char*)type)-((const char*)package->header->header)) + << ", offset=" << (void*)offset << endl); + + if (offset > (dtohl(type->header.size)-sizeof(ResTable_entry))) { + LOGW("ResTable_entry at 0x%x is beyond type chunk data 0x%x", + offset, dtohl(type->header.size)); + return BAD_TYPE; + } + if ((offset&0x3) != 0) { + LOGW("ResTable_entry at 0x%x is not on an integer boundary", + offset); + return BAD_TYPE; + } + + const ResTable_entry* const entry = (const ResTable_entry*) + (((const uint8_t*)type) + offset); + if (dtohs(entry->size) < sizeof(*entry)) { + LOGW("ResTable_entry size 0x%x is too small", dtohs(entry->size)); + return BAD_TYPE; + } + + *outType = type; + *outEntry = entry; + if (outTypeClass != NULL) { + *outTypeClass = allTypes; + } + return offset + dtohs(entry->size); +} + +status_t ResTable::parsePackage(const ResTable_package* const pkg, + const Header* const header) +{ + const uint8_t* base = (const uint8_t*)pkg; + status_t err = validate_chunk(&pkg->header, sizeof(*pkg), + header->dataEnd, "ResTable_package"); + if (err != NO_ERROR) { + return (mError=err); + } + + const size_t pkgSize = dtohl(pkg->header.size); + + if (dtohl(pkg->typeStrings) >= pkgSize) { + LOGW("ResTable_package type strings at %p are past chunk size %p.", + (void*)dtohl(pkg->typeStrings), (void*)pkgSize); + return (mError=BAD_TYPE); + } + if ((dtohl(pkg->typeStrings)&0x3) != 0) { + LOGW("ResTable_package type strings at %p is not on an integer boundary.", + (void*)dtohl(pkg->typeStrings)); + return (mError=BAD_TYPE); + } + if (dtohl(pkg->keyStrings) >= pkgSize) { + LOGW("ResTable_package key strings at %p are past chunk size %p.", + (void*)dtohl(pkg->keyStrings), (void*)pkgSize); + return (mError=BAD_TYPE); + } + if ((dtohl(pkg->keyStrings)&0x3) != 0) { + LOGW("ResTable_package key strings at %p is not on an integer boundary.", + (void*)dtohl(pkg->keyStrings)); + return (mError=BAD_TYPE); + } + + Package* package = NULL; + PackageGroup* group = NULL; + uint32_t id = dtohl(pkg->id); + if (id != 0 && id < 256) { + size_t idx = mPackageMap[id]; + if (idx == 0) { + idx = mPackageGroups.size()+1; + + char16_t tmpName[sizeof(pkg->name)/sizeof(char16_t)]; + strcpy16_dtoh(tmpName, pkg->name, sizeof(pkg->name)/sizeof(char16_t)); + group = new PackageGroup(String16(tmpName), id); + if (group == NULL) { + return (mError=NO_MEMORY); + } + + err = group->typeStrings.setTo(base+dtohl(pkg->typeStrings), + header->dataEnd-(base+dtohl(pkg->typeStrings))); + if (err != NO_ERROR) { + return (mError=err); + } + err = group->keyStrings.setTo(base+dtohl(pkg->keyStrings), + header->dataEnd-(base+dtohl(pkg->keyStrings))); + if (err != NO_ERROR) { + return (mError=err); + } + + //printf("Adding new package id %d at index %d\n", id, idx); + err = mPackageGroups.add(group); + if (err < NO_ERROR) { + return (mError=err); + } + mPackageMap[id] = (uint8_t)idx; + } else { + group = mPackageGroups.itemAt(idx-1); + if (group == NULL) { + return (mError=UNKNOWN_ERROR); + } + } + package = new Package(header, pkg); + if (package == NULL) { + return (mError=NO_MEMORY); + } + err = group->packages.add(package); + if (err < NO_ERROR) { + return (mError=err); + } + } else { + LOG_ALWAYS_FATAL("Skins not supported!"); + return NO_ERROR; + } + + + // Iterate through all chunks. + size_t curPackage = 0; + + const ResChunk_header* chunk = + (const ResChunk_header*)(((const uint8_t*)pkg) + + dtohs(pkg->header.headerSize)); + const uint8_t* endPos = ((const uint8_t*)pkg) + dtohs(pkg->header.size); + while (((const uint8_t*)chunk) <= (endPos-sizeof(ResChunk_header)) && + ((const uint8_t*)chunk) <= (endPos-dtohl(chunk->size))) { + TABLE_NOISY(LOGV("PackageChunk: type=0x%x, headerSize=0x%x, size=0x%x, pos=%p\n", + dtohs(chunk->type), dtohs(chunk->headerSize), dtohl(chunk->size), + (void*)(((const uint8_t*)chunk) - ((const uint8_t*)header->header)))); + const size_t csize = dtohl(chunk->size); + const uint16_t ctype = dtohs(chunk->type); + if (ctype == RES_TABLE_TYPE_SPEC_TYPE) { + const ResTable_typeSpec* typeSpec = (const ResTable_typeSpec*)(chunk); + err = validate_chunk(&typeSpec->header, sizeof(*typeSpec), + endPos, "ResTable_typeSpec"); + if (err != NO_ERROR) { + return (mError=err); + } + + const size_t typeSpecSize = dtohl(typeSpec->header.size); + + LOAD_TABLE_NOISY(printf("TypeSpec off %p: type=0x%x, headerSize=0x%x, size=%p\n", + (void*)(base-(const uint8_t*)chunk), + dtohs(typeSpec->header.type), + dtohs(typeSpec->header.headerSize), + (void*)typeSize)); + // look for block overrun or int overflow when multiplying by 4 + if ((dtohl(typeSpec->entryCount) > (INT32_MAX/sizeof(uint32_t)) + || dtohs(typeSpec->header.headerSize)+(sizeof(uint32_t)*dtohl(typeSpec->entryCount)) + > typeSpecSize)) { + LOGW("ResTable_typeSpec entry index to %p extends beyond chunk end %p.", + (void*)(dtohs(typeSpec->header.headerSize) + +(sizeof(uint32_t)*dtohl(typeSpec->entryCount))), + (void*)typeSpecSize); + return (mError=BAD_TYPE); + } + + if (typeSpec->id == 0) { + LOGW("ResTable_type has an id of 0."); + return (mError=BAD_TYPE); + } + + while (package->types.size() < typeSpec->id) { + package->types.add(NULL); + } + Type* t = package->types[typeSpec->id-1]; + if (t == NULL) { + t = new Type(header, package, dtohl(typeSpec->entryCount)); + package->types.editItemAt(typeSpec->id-1) = t; + } else if (dtohl(typeSpec->entryCount) != t->entryCount) { + LOGW("ResTable_typeSpec entry count inconsistent: given %d, previously %d", + (int)dtohl(typeSpec->entryCount), (int)t->entryCount); + return (mError=BAD_TYPE); + } + t->typeSpecFlags = (const uint32_t*)( + ((const uint8_t*)typeSpec) + dtohs(typeSpec->header.headerSize)); + t->typeSpec = typeSpec; + + } else if (ctype == RES_TABLE_TYPE_TYPE) { + const ResTable_type* type = (const ResTable_type*)(chunk); + err = validate_chunk(&type->header, sizeof(*type)-sizeof(ResTable_config)+4, + endPos, "ResTable_type"); + if (err != NO_ERROR) { + return (mError=err); + } + + const size_t typeSize = dtohl(type->header.size); + + LOAD_TABLE_NOISY(printf("Type off %p: type=0x%x, headerSize=0x%x, size=%p\n", + (void*)(base-(const uint8_t*)chunk), + dtohs(type->header.type), + dtohs(type->header.headerSize), + (void*)typeSize)); + if (dtohs(type->header.headerSize)+(sizeof(uint32_t)*dtohl(type->entryCount)) + > typeSize) { + LOGW("ResTable_type entry index to %p extends beyond chunk end %p.", + (void*)(dtohs(type->header.headerSize) + +(sizeof(uint32_t)*dtohl(type->entryCount))), + (void*)typeSize); + return (mError=BAD_TYPE); + } + if (dtohl(type->entryCount) != 0 + && dtohl(type->entriesStart) > (typeSize-sizeof(ResTable_entry))) { + LOGW("ResTable_type entriesStart at %p extends beyond chunk end %p.", + (void*)dtohl(type->entriesStart), (void*)typeSize); + return (mError=BAD_TYPE); + } + if (type->id == 0) { + LOGW("ResTable_type has an id of 0."); + return (mError=BAD_TYPE); + } + + while (package->types.size() < type->id) { + package->types.add(NULL); + } + Type* t = package->types[type->id-1]; + if (t == NULL) { + t = new Type(header, package, dtohl(type->entryCount)); + package->types.editItemAt(type->id-1) = t; + } else if (dtohl(type->entryCount) != t->entryCount) { + LOGW("ResTable_type entry count inconsistent: given %d, previously %d", + (int)dtohl(type->entryCount), (int)t->entryCount); + return (mError=BAD_TYPE); + } + + TABLE_GETENTRY( + ResTable_config thisConfig; + thisConfig.copyFromDtoH(type->config); + LOGI("Adding config to type %d: imsi:%d/%d lang:%c%c cnt:%c%c " + "orien:%d touch:%d density:%d key:%d inp:%d nav:%d w:%d h:%d\n", + type->id, + thisConfig.mcc, thisConfig.mnc, + thisConfig.language[0] ? thisConfig.language[0] : '-', + thisConfig.language[1] ? thisConfig.language[1] : '-', + thisConfig.country[0] ? thisConfig.country[0] : '-', + thisConfig.country[1] ? thisConfig.country[1] : '-', + thisConfig.orientation, + thisConfig.touchscreen, + thisConfig.density, + thisConfig.keyboard, + thisConfig.inputFlags, + thisConfig.navigation, + thisConfig.screenWidth, + thisConfig.screenHeight)); + t->configs.add(type); + } else { + status_t err = validate_chunk(chunk, sizeof(ResChunk_header), + endPos, "ResTable_package:unknown"); + if (err != NO_ERROR) { + return (mError=err); + } + } + chunk = (const ResChunk_header*) + (((const uint8_t*)chunk) + csize); + } + + if (group->typeCount == 0) { + group->typeCount = package->types.size(); + } + + return NO_ERROR; +} + +#ifndef HAVE_ANDROID_OS +#define CHAR16_TO_CSTR(c16, len) (String8(String16(c16,len)).string()) + +#define CHAR16_ARRAY_EQ(constant, var, len) \ + ((len == (sizeof(constant)/sizeof(constant[0]))) && (0 == memcmp((var), (constant), (len)))) + +void ResTable::print() const +{ + printf("mError=0x%x (%s)\n", mError, strerror(mError)); +#if 0 + printf("mParams=%c%c-%c%c,\n", + mParams.language[0], mParams.language[1], + mParams.country[0], mParams.country[1]); +#endif + size_t pgCount = mPackageGroups.size(); + printf("Package Groups (%d)\n", (int)pgCount); + for (size_t pgIndex=0; pgIndexid, (int)pg->packages.size(), + String8(pg->name).string()); + + size_t pkgCount = pg->packages.size(); + for (size_t pkgIndex=0; pkgIndexpackages[pkgIndex]; + size_t typeCount = pkg->types.size(); + printf(" Package %d id=%d name=%s typeCount=%d\n", (int)pkgIndex, + pkg->package->id, String8(String16(pkg->package->name)).string(), + (int)typeCount); + for (size_t typeIndex=0; typeIndexgetType(typeIndex); + if (typeConfigs == NULL) { + printf(" type %d NULL\n", (int)typeIndex); + continue; + } + const size_t NTC = typeConfigs->configs.size(); + printf(" type %d configCount=%d entryCount=%d\n", + (int)typeIndex, (int)NTC, (int)typeConfigs->entryCount); + if (typeConfigs->typeSpecFlags != NULL) { + for (size_t entryIndex=0; entryIndexentryCount; entryIndex++) { + uint32_t resID = (0xff000000 & ((pkg->package->id)<<24)) + | (0x00ff0000 & ((typeIndex+1)<<16)) + | (0x0000ffff & (entryIndex)); + resource_name resName; + this->getResourceName(resID, &resName); + printf(" spec resource 0x%08x %s:%s/%s: flags=0x%08x\n", + resID, + CHAR16_TO_CSTR(resName.package, resName.packageLen), + CHAR16_TO_CSTR(resName.type, resName.typeLen), + CHAR16_TO_CSTR(resName.name, resName.nameLen), + dtohl(typeConfigs->typeSpecFlags[entryIndex])); + } + } + for (size_t configIndex=0; configIndexconfigs[configIndex]; + if ((((int)type)&0x3) != 0) { + printf(" NON-INTEGER ResTable_type ADDRESS: %p\n", type); + continue; + } + printf(" config %d lang=%c%c cnt=%c%c orien=%d touch=%d density=%d key=%d infl=%d nav=%d w=%d h=%d\n", + (int)configIndex, + type->config.language[0] ? type->config.language[0] : '-', + type->config.language[1] ? type->config.language[1] : '-', + type->config.country[0] ? type->config.country[0] : '-', + type->config.country[1] ? type->config.country[1] : '-', + type->config.orientation, + type->config.touchscreen, + dtohs(type->config.density), + type->config.keyboard, + type->config.inputFlags, + type->config.navigation, + dtohs(type->config.screenWidth), + dtohs(type->config.screenHeight)); + size_t entryCount = dtohl(type->entryCount); + uint32_t entriesStart = dtohl(type->entriesStart); + if ((entriesStart&0x3) != 0) { + printf(" NON-INTEGER ResTable_type entriesStart OFFSET: %p\n", (void*)entriesStart); + continue; + } + uint32_t typeSize = dtohl(type->header.size); + if ((typeSize&0x3) != 0) { + printf(" NON-INTEGER ResTable_type header.size: %p\n", (void*)typeSize); + continue; + } + for (size_t entryIndex=0; entryIndexheader.size); + const uint32_t* const eindex = (const uint32_t*) + (((const uint8_t*)type) + dtohs(type->header.headerSize)); + + uint32_t thisOffset = dtohl(eindex[entryIndex]); + if (thisOffset == ResTable_type::NO_ENTRY) { + continue; + } + + uint32_t resID = (0xff000000 & ((pkg->package->id)<<24)) + | (0x00ff0000 & ((typeIndex+1)<<16)) + | (0x0000ffff & (entryIndex)); + resource_name resName; + this->getResourceName(resID, &resName); + printf(" resource 0x%08x %s:%s/%s: ", resID, + CHAR16_TO_CSTR(resName.package, resName.packageLen), + CHAR16_TO_CSTR(resName.type, resName.typeLen), + CHAR16_TO_CSTR(resName.name, resName.nameLen)); + if ((thisOffset&0x3) != 0) { + printf("NON-INTEGER OFFSET: %p\n", (void*)thisOffset); + continue; + } + if ((thisOffset+sizeof(ResTable_entry)) > typeSize) { + printf("OFFSET OUT OF BOUNDS: %p+%p (size is %p)\n", + (void*)entriesStart, (void*)thisOffset, + (void*)typeSize); + continue; + } + + const ResTable_entry* ent = (const ResTable_entry*) + (((const uint8_t*)type) + entriesStart + thisOffset); + if (((entriesStart + thisOffset)&0x3) != 0) { + printf("NON-INTEGER ResTable_entry OFFSET: %p\n", + (void*)(entriesStart + thisOffset)); + continue; + } + if ((dtohs(ent->flags)&ResTable_entry::FLAG_COMPLEX) != 0) { + printf(""); + } 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; + } + + const Res_value* value = (const Res_value*) + (((const uint8_t*)ent) + esize); + 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); + } + + if ((dtohs(ent->flags)&ResTable_entry::FLAG_PUBLIC) != 0) { + printf(" (PUBLIC)"); + } + printf("\n"); + } + } + } + } + } +} + +#endif // HAVE_ANDROID_OS + +} // namespace android diff --git a/libs/utils/SharedBuffer.cpp b/libs/utils/SharedBuffer.cpp new file mode 100644 index 000000000..3555fb712 --- /dev/null +++ b/libs/utils/SharedBuffer.cpp @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2005 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 +#include + +#include +#include + +// --------------------------------------------------------------------------- + +namespace android { + +SharedBuffer* SharedBuffer::alloc(size_t size) +{ + SharedBuffer* sb = static_cast(malloc(sizeof(SharedBuffer) + size)); + if (sb) { + sb->mRefs = 1; + sb->mSize = size; + } + return sb; +} + + +ssize_t SharedBuffer::dealloc(const SharedBuffer* released) +{ + if (released->mRefs != 0) return -1; // XXX: invalid operation + free(const_cast(released)); + return 0; +} + +SharedBuffer* SharedBuffer::edit() const +{ + if (onlyOwner()) { + return const_cast(this); + } + SharedBuffer* sb = alloc(mSize); + if (sb) { + memcpy(sb->data(), data(), size()); + release(); + } + return sb; +} + +SharedBuffer* SharedBuffer::editResize(size_t newSize) const +{ + if (onlyOwner()) { + SharedBuffer* buf = const_cast(this); + if (buf->mSize == newSize) return buf; + buf = (SharedBuffer*)realloc(buf, sizeof(SharedBuffer) + newSize); + if (buf != NULL) { + buf->mSize = newSize; + return buf; + } + } + SharedBuffer* sb = alloc(newSize); + if (sb) { + const size_t mySize = mSize; + memcpy(sb->data(), data(), newSize < mySize ? newSize : mySize); + release(); + } + return sb; +} + +SharedBuffer* SharedBuffer::attemptEdit() const +{ + if (onlyOwner()) { + return const_cast(this); + } + return 0; +} + +SharedBuffer* SharedBuffer::reset(size_t new_size) const +{ + // cheap-o-reset. + SharedBuffer* sb = alloc(new_size); + if (sb) { + release(); + } + return sb; +} + +void SharedBuffer::acquire() const { + android_atomic_inc(&mRefs); +} + +int32_t SharedBuffer::release(uint32_t flags) const +{ + int32_t prev = 1; + if (onlyOwner() || ((prev = android_atomic_dec(&mRefs)) == 1)) { + mRefs = 0; + if ((flags & eKeepStorage) == 0) { + free(const_cast(this)); + } + } + return prev; +} + + +}; // namespace android diff --git a/libs/utils/Socket.cpp b/libs/utils/Socket.cpp new file mode 100644 index 000000000..51509a304 --- /dev/null +++ b/libs/utils/Socket.cpp @@ -0,0 +1,388 @@ +/* + * Copyright (C) 2005 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. + */ + +// +// Internet address class. +// + +#ifdef HAVE_WINSOCK +// This needs to come first, or Cygwin gets concerned about a potential +// clash between WinSock and . +# include +#endif + +#include +#include +#include +#include + +#ifndef HAVE_WINSOCK +# include +# include +# include +# include +#endif + +#include +#include +#include +#include +#include +#include + +using namespace android; + + +/* + * =========================================================================== + * Socket + * =========================================================================== + */ + +#ifndef INVALID_SOCKET +# define INVALID_SOCKET (-1) +#endif +#define UNDEF_SOCKET ((unsigned long) INVALID_SOCKET) + +/*static*/ bool Socket::mBootInitialized = false; + +/* + * Extract system-dependent error code. + */ +static inline int getSocketError(void) { +#ifdef HAVE_WINSOCK + return WSAGetLastError(); +#else + return errno; +#endif +} + +/* + * One-time initialization for socket code. + */ +/*static*/ bool Socket::bootInit(void) +{ +#ifdef HAVE_WINSOCK + WSADATA wsaData; + int err; + + err = WSAStartup(MAKEWORD(2, 0), &wsaData); + if (err != 0) { + LOG(LOG_ERROR, "socket", "Unable to start WinSock\n"); + return false; + } + + LOG(LOG_INFO, "socket", "Using WinSock v%d.%d\n", + LOBYTE(wsaData.wVersion), HIBYTE(wsaData.wVersion)); +#endif + + mBootInitialized = true; + return true; +} + +/* + * One-time shutdown for socket code. + */ +/*static*/ void Socket::finalShutdown(void) +{ +#ifdef HAVE_WINSOCK + WSACleanup(); +#endif + mBootInitialized = false; +} + + +/* + * Simple constructor. Allow the application to create us and then make + * bind/connect calls. + */ +Socket::Socket(void) + : mSock(UNDEF_SOCKET) +{ + if (!mBootInitialized) + LOG(LOG_WARN, "socket", "WARNING: sockets not initialized\n"); +} + +/* + * Destructor. Closes the socket and resets our storage. + */ +Socket::~Socket(void) +{ + close(); +} + + +/* + * Create a socket and connect to the specified host and port. + */ +int Socket::connect(const char* host, int port) +{ + if (mSock != UNDEF_SOCKET) { + LOG(LOG_WARN, "socket", "Socket already connected\n"); + return -1; + } + + InetSocketAddress sockAddr; + if (!sockAddr.create(host, port)) + return -1; + + //return doConnect(sockAddr); + int foo; + foo = doConnect(sockAddr); + return foo; +} + +/* + * Create a socket and connect to the specified host and port. + */ +int Socket::connect(const InetAddress* addr, int port) +{ + if (mSock != UNDEF_SOCKET) { + LOG(LOG_WARN, "socket", "Socket already connected\n"); + return -1; + } + + InetSocketAddress sockAddr; + if (!sockAddr.create(addr, port)) + return -1; + + return doConnect(sockAddr); +} + +/* + * Finish creating a socket by connecting to the remote host. + * + * Returns 0 on success. + */ +int Socket::doConnect(const InetSocketAddress& sockAddr) +{ +#ifdef HAVE_WINSOCK + SOCKET sock; +#else + int sock; +#endif + const InetAddress* addr = sockAddr.getAddress(); + int port = sockAddr.getPort(); + struct sockaddr_in inaddr; + DurationTimer connectTimer; + + assert(sizeof(struct sockaddr_in) == addr->getAddressLength()); + memcpy(&inaddr, addr->getAddress(), addr->getAddressLength()); + inaddr.sin_port = htons(port); + + //fprintf(stderr, "--- connecting to %s:%d\n", + // sockAddr.getHostName(), port); + + sock = ::socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (sock == INVALID_SOCKET) { + int err = getSocketError(); + LOG(LOG_ERROR, "socket", "Unable to create socket (err=%d)\n", err); + return (err != 0) ? err : -1; + } + + connectTimer.start(); + + if (::connect(sock, (struct sockaddr*) &inaddr, sizeof(inaddr)) != 0) { + int err = getSocketError(); + LOG(LOG_WARN, "socket", "Connect to %s:%d failed: %d\n", + sockAddr.getHostName(), port, err); + return (err != 0) ? err : -1; + } + + connectTimer.stop(); + if ((long) connectTimer.durationUsecs() > 100000) { + LOG(LOG_INFO, "socket", + "Connect to %s:%d took %.3fs\n", sockAddr.getHostName(), + port, ((long) connectTimer.durationUsecs()) / 1000000.0); + } + + mSock = (unsigned long) sock; + LOG(LOG_VERBOSE, "socket", + "--- connected to %s:%d\n", sockAddr.getHostName(), port); + return 0; +} + + +/* + * Close the socket if it needs closing. + */ +bool Socket::close(void) +{ + if (mSock != UNDEF_SOCKET) { + //fprintf(stderr, "--- closing socket %lu\n", mSock); +#ifdef HAVE_WINSOCK + if (::closesocket((SOCKET) mSock) != 0) + return false; +#else + if (::close((int) mSock) != 0) + return false; +#endif + } + + mSock = UNDEF_SOCKET; + + return true; +} + +/* + * Read data from socket. + * + * Standard semantics: read up to "len" bytes into "buf". Returns the + * number of bytes read, or less than zero on error. + */ +int Socket::read(void* buf, ssize_t len) const +{ + if (mSock == UNDEF_SOCKET) { + LOG(LOG_ERROR, "socket", "ERROR: read on invalid socket\n"); + return -500; + } + +#ifdef HAVE_WINSOCK + SOCKET sock = (SOCKET) mSock; +#else + int sock = (int) mSock; +#endif + int cc; + + cc = recv(sock, (char*)buf, len, 0); + if (cc < 0) { + int err = getSocketError(); + return (err > 0) ? -err : -1; + } + + return cc; +} + +/* + * Write data to a socket. + * + * Standard semantics: write up to "len" bytes into "buf". Returns the + * number of bytes written, or less than zero on error. + */ +int Socket::write(const void* buf, ssize_t len) const +{ + if (mSock == UNDEF_SOCKET) { + LOG(LOG_ERROR, "socket", "ERROR: write on invalid socket\n"); + return -500; + } + +#ifdef HAVE_WINSOCK + SOCKET sock = (SOCKET) mSock; +#else + int sock = (int) mSock; +#endif + int cc; + + cc = send(sock, (const char*)buf, len, 0); + if (cc < 0) { + int err = getSocketError(); + return (err > 0) ? -err : -1; + } + + return cc; +} + + +/* + * =========================================================================== + * Socket tests + * =========================================================================== + */ + +/* + * Read all data from the socket. The data is read into a buffer that + * expands as needed. + * + * On exit, the buffer is returned, and the length of the data is stored + * in "*sz". A null byte is added to the end, but is not included in + * the length. + */ +static char* socketReadAll(const Socket& s, int *sz) +{ + int max, r; + char *data, *ptr, *tmp; + + data = (char*) malloc(max = 32768); + if (data == NULL) + return NULL; + + ptr = data; + + for (;;) { + if ((ptr - data) == max) { + tmp = (char*) realloc(data, max *= 2); + if(tmp == 0) { + free(data); + return 0; + } + } + r = s.read(ptr, max - (ptr - data)); + if (r == 0) + break; + if (r < 0) { + LOG(LOG_WARN, "socket", "WARNING: socket read failed (res=%d)\n",r); + break; + } + ptr += r; + } + + if ((ptr - data) == max) { + tmp = (char*) realloc(data, max + 1); + if (tmp == NULL) { + free(data); + return NULL; + } + } + *ptr = '\0'; + *sz = (ptr - data); + return data; +} + +/* + * Exercise the Socket class. + */ +void android::TestSockets(void) +{ + printf("----- SOCKET TEST ------\n"); + Socket::bootInit(); + + char* buf = NULL; + int len, cc; + const char* kTestStr = + "GET / HTTP/1.0\n" + "Connection: close\n" + "\n"; + + Socket sock; + if (sock.connect("www.google.com", 80) != 0) { + fprintf(stderr, "socket connected failed\n"); + goto bail; + } + + cc = sock.write(kTestStr, strlen(kTestStr)); + if (cc != (int) strlen(kTestStr)) { + fprintf(stderr, "write failed, res=%d\n", cc); + goto bail; + } + buf = socketReadAll(sock, &len); + + printf("GOT '%s'\n", buf); + +bail: + sock.close(); + free(buf); +} + diff --git a/libs/utils/Static.cpp b/libs/utils/Static.cpp new file mode 100644 index 000000000..93f7e4f0c --- /dev/null +++ b/libs/utils/Static.cpp @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// All static variables go here, to control initialization and +// destruction order in the library. + +#include + +#include +#include +#include + +namespace android { + +class LibUtilsFirstStatics +{ +public: + LibUtilsFirstStatics() + { + initialize_string8(); + initialize_string16(); + } + + ~LibUtilsFirstStatics() + { + terminate_string16(); + terminate_string8(); + } +}; + +static LibUtilsFirstStatics gFirstStatics; +int gDarwinCantLoadAllObjects = 1; + +// ------------ Text output streams + +Vector gTextBuffers; + +class LogTextOutput : public BufferedTextOutput +{ +public: + LogTextOutput() : BufferedTextOutput(MULTITHREADED) { } + virtual ~LogTextOutput() { }; + +protected: + virtual status_t writeLines(const struct iovec& vec, size_t N) + { + android_writevLog(&vec, N); + return NO_ERROR; + } +}; + +class FdTextOutput : public BufferedTextOutput +{ +public: + FdTextOutput(int fd) : BufferedTextOutput(MULTITHREADED), mFD(fd) { } + virtual ~FdTextOutput() { }; + +protected: + virtual status_t writeLines(const struct iovec& vec, size_t N) + { + writev(mFD, &vec, N); + return NO_ERROR; + } + +private: + int mFD; +}; + +static LogTextOutput gLogTextOutput; +static FdTextOutput gStdoutTextOutput(STDOUT_FILENO); +static FdTextOutput gStderrTextOutput(STDERR_FILENO); + +TextOutput& alog(gLogTextOutput); +TextOutput& aout(gStdoutTextOutput); +TextOutput& aerr(gStderrTextOutput); + +#ifndef LIBUTILS_NATIVE + +// ------------ ProcessState.cpp + +Mutex gProcessMutex; +sp gProcess; + +class LibUtilsIPCtStatics +{ +public: + LibUtilsIPCtStatics() + { + } + + ~LibUtilsIPCtStatics() + { + IPCThreadState::shutdown(); + } +}; + +static LibUtilsIPCtStatics gIPCStatics; + +// ------------ ServiceManager.cpp + +Mutex gDefaultServiceManagerLock; +sp gDefaultServiceManager; +sp gPermissionController; + +#endif + +} // namespace android diff --git a/libs/utils/StopWatch.cpp b/libs/utils/StopWatch.cpp new file mode 100644 index 000000000..68a1c5217 --- /dev/null +++ b/libs/utils/StopWatch.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2005 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 "StopWatch" + +#include +#include +#include + +#include +#include +#include + +/*****************************************************************************/ + +namespace android { + + +StopWatch::StopWatch(const char *name, int clock, uint32_t flags) + : mName(name), mClock(clock), mFlags(flags), + mStartTime(0), mNumLaps(0) +{ + mStartTime = systemTime(mClock); +} + +StopWatch::~StopWatch() +{ + nsecs_t elapsed = elapsedTime(); + const int n = mNumLaps; + LOGD("StopWatch %s (us): %lld ", mName, ns2us(elapsed)); + for (int i=0 ; i= 8) { + elapsed = 0; + } else { + const int n = mNumLaps; + mLaps[n].soFar = elapsed; + mLaps[n].thisLap = n ? (elapsed - mLaps[n-1].soFar) : elapsed; + mNumLaps = n+1; + } + return elapsed; +} + +nsecs_t StopWatch::elapsedTime() const +{ + return systemTime(mClock) - mStartTime; +} + + +/*****************************************************************************/ + +}; // namespace android + diff --git a/libs/utils/String16.cpp b/libs/utils/String16.cpp new file mode 100644 index 000000000..1f81cadb7 --- /dev/null +++ b/libs/utils/String16.cpp @@ -0,0 +1,609 @@ +/* + * Copyright (C) 2005 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 + +#include +#include +#include +#include +#include + +#include + +#ifdef HAVE_WINSOCK +# undef nhtol +# undef htonl +# undef nhtos +# undef htons + +# ifdef HAVE_LITTLE_ENDIAN +# define ntohl(x) ( ((x) << 24) | (((x) >> 24) & 255) | (((x) << 8) & 0xff0000) | (((x) >> 8) & 0xff00) ) +# define htonl(x) ntohl(x) +# define ntohs(x) ( (((x) << 8) & 0xff00) | (((x) >> 8) & 255) ) +# define htons(x) ntohs(x) +# else +# define ntohl(x) (x) +# define htonl(x) (x) +# define ntohs(x) (x) +# define htons(x) (x) +# endif +#else +# include +#endif + +#include +#include +#include + +// --------------------------------------------------------------------------- + +int strcmp16(const char16_t *s1, const char16_t *s2) +{ + char16_t ch; + int d = 0; + + while ( 1 ) { + d = (int)(ch = *s1++) - (int)*s2++; + if ( d || !ch ) + break; + } + + return d; +} + +int strncmp16(const char16_t *s1, const char16_t *s2, size_t n) +{ + char16_t ch; + int d = 0; + + while ( n-- ) { + d = (int)(ch = *s1++) - (int)*s2++; + if ( d || !ch ) + break; + } + + return d; +} + +char16_t *strcpy16(char16_t *dst, const char16_t *src) +{ + char16_t *q = dst; + const char16_t *p = src; + char16_t ch; + + do { + *q++ = ch = *p++; + } while ( ch ); + + return dst; +} + +size_t strlen16(const char16_t *s) +{ + const char16_t *ss = s; + while ( *ss ) + ss++; + return ss-s; +} + + +char16_t *strncpy16(char16_t *dst, const char16_t *src, size_t n) +{ + char16_t *q = dst; + const char16_t *p = src; + char ch; + + while (n) { + n--; + *q++ = ch = *p++; + if ( !ch ) + break; + } + + *q = 0; + + return dst; +} + +size_t strnlen16(const char16_t *s, size_t maxlen) +{ + const char16_t *ss = s; + + /* Important: the maxlen test must precede the reference through ss; + since the byte beyond the maximum may segfault */ + while ((maxlen > 0) && *ss) { + ss++; + maxlen--; + } + return ss-s; +} + +int strzcmp16(const char16_t *s1, size_t n1, const char16_t *s2, size_t n2) +{ + const char16_t* e1 = s1+n1; + const char16_t* e2 = s2+n2; + + while (s1 < e1 && s2 < e2) { + const int d = (int)*s1++ - (int)*s2++; + if (d) { + return d; + } + } + + return n1 < n2 + ? (0 - (int)*s2) + : (n1 > n2 + ? ((int)*s1 - 0) + : 0); +} + +int strzcmp16_h_n(const char16_t *s1H, size_t n1, const char16_t *s2N, size_t n2) +{ + const char16_t* e1 = s1H+n1; + const char16_t* e2 = s2N+n2; + + while (s1H < e1 && s2N < e2) { + const char16_t c2 = ntohs(*s2N); + const int d = (int)*s1H++ - (int)c2; + s2N++; + if (d) { + return d; + } + } + + return n1 < n2 + ? (0 - (int)ntohs(*s2N)) + : (n1 > n2 + ? ((int)*s1H - 0) + : 0); +} + +// --------------------------------------------------------------------------- + +namespace android { + +static inline size_t +utf8_char_len(uint8_t ch) +{ + return ((0xe5000000 >> ((ch >> 3) & 0x1e)) & 3) + 1; +} + +#define UTF8_SHIFT_AND_MASK(unicode, byte) (unicode)<<=6; (unicode) |= (0x3f & (byte)); + +static inline uint32_t +utf8_to_utf32(const uint8_t *src, size_t length) +{ + uint32_t unicode; + + switch (length) + { + case 1: + return src[0]; + case 2: + unicode = src[0] & 0x1f; + UTF8_SHIFT_AND_MASK(unicode, src[1]) + return unicode; + case 3: + unicode = src[0] & 0x0f; + UTF8_SHIFT_AND_MASK(unicode, src[1]) + UTF8_SHIFT_AND_MASK(unicode, src[2]) + return unicode; + case 4: + unicode = src[0] & 0x07; + UTF8_SHIFT_AND_MASK(unicode, src[1]) + UTF8_SHIFT_AND_MASK(unicode, src[2]) + UTF8_SHIFT_AND_MASK(unicode, src[3]) + return unicode; + default: + return 0xffff; + } + + //printf("Char at %p: len=%d, utf-16=%p\n", src, length, (void*)result); +} + +// --------------------------------------------------------------------------- + +static SharedBuffer* gEmptyStringBuf = NULL; +static char16_t* gEmptyString = NULL; + +static inline char16_t* getEmptyString() +{ + gEmptyStringBuf->acquire(); + return gEmptyString; +} + +void initialize_string16() +{ + SharedBuffer* buf = SharedBuffer::alloc(sizeof(char16_t)); + char16_t* str = (char16_t*)buf->data(); + *str = 0; + gEmptyStringBuf = buf; + gEmptyString = str; +} + +void terminate_string16() +{ + SharedBuffer::bufferFromData(gEmptyString)->release(); + gEmptyStringBuf = NULL; + gEmptyString = NULL; +} + +// --------------------------------------------------------------------------- + +// Note: not dealing with generating surrogate pairs. +static char16_t* allocFromUTF8(const char* in, size_t len) +{ + if (len == 0) return getEmptyString(); + + size_t chars = 0; + const char* end = in+len; + const char* p = in; + + while (p < end) { + chars++; + p += utf8_char_len(*p); + } + + SharedBuffer* buf = SharedBuffer::alloc((chars+1)*sizeof(char16_t)); + if (buf) { + p = in; + char16_t* str = (char16_t*)buf->data(); + char16_t* d = str; + while (p < end) { + size_t len = utf8_char_len(*p); + *d++ = (char16_t)utf8_to_utf32((const uint8_t*)p, len); + p += len; + } + *d = 0; + + //printf("Created UTF-16 string from UTF-8 \"%s\":", in); + //printHexData(1, str, buf->size(), 16, 1); + //printf("\n"); + + return str; + } + + return getEmptyString(); +} + +// --------------------------------------------------------------------------- + +String16::String16() + : mString(getEmptyString()) +{ +} + +String16::String16(const String16& o) + : mString(o.mString) +{ + SharedBuffer::bufferFromData(mString)->acquire(); +} + +String16::String16(const String16& o, size_t len, size_t begin) + : mString(getEmptyString()) +{ + setTo(o, len, begin); +} + +String16::String16(const char16_t* o) +{ + size_t len = strlen16(o); + SharedBuffer* buf = SharedBuffer::alloc((len+1)*sizeof(char16_t)); + LOG_ASSERT(buf, "Unable to allocate shared buffer"); + if (buf) { + char16_t* str = (char16_t*)buf->data(); + strcpy16(str, o); + mString = str; + return; + } + + mString = getEmptyString(); +} + +String16::String16(const char16_t* o, size_t len) +{ + SharedBuffer* buf = SharedBuffer::alloc((len+1)*sizeof(char16_t)); + LOG_ASSERT(buf, "Unable to allocate shared buffer"); + if (buf) { + char16_t* str = (char16_t*)buf->data(); + memcpy(str, o, len*sizeof(char16_t)); + str[len] = 0; + mString = str; + return; + } + + mString = getEmptyString(); +} + +String16::String16(const String8& o) + : mString(allocFromUTF8(o.string(), o.size())) +{ +} + +String16::String16(const char* o) + : mString(allocFromUTF8(o, strlen(o))) +{ +} + +String16::String16(const char* o, size_t len) + : mString(allocFromUTF8(o, len)) +{ +} + +String16::~String16() +{ + SharedBuffer::bufferFromData(mString)->release(); +} + +void String16::setTo(const String16& other) +{ + SharedBuffer::bufferFromData(other.mString)->acquire(); + SharedBuffer::bufferFromData(mString)->release(); + mString = other.mString; +} + +status_t String16::setTo(const String16& other, size_t len, size_t begin) +{ + const size_t N = other.size(); + if (begin >= N) { + SharedBuffer::bufferFromData(mString)->release(); + mString = getEmptyString(); + return NO_ERROR; + } + if ((begin+len) > N) len = N-begin; + if (begin == 0 && len == N) { + setTo(other); + return NO_ERROR; + } + + if (&other == this) { + LOG_ALWAYS_FATAL("Not implemented"); + } + + return setTo(other.string()+begin, len); +} + +status_t String16::setTo(const char16_t* other) +{ + return setTo(other, strlen16(other)); +} + +status_t String16::setTo(const char16_t* other, size_t len) +{ + SharedBuffer* buf = SharedBuffer::bufferFromData(mString) + ->editResize((len+1)*sizeof(char16_t)); + if (buf) { + char16_t* str = (char16_t*)buf->data(); + memcpy(str, other, len*sizeof(char16_t)); + str[len] = 0; + mString = str; + return NO_ERROR; + } + return NO_MEMORY; +} + +status_t String16::append(const String16& other) +{ + const size_t myLen = size(); + const size_t otherLen = other.size(); + if (myLen == 0) { + setTo(other); + return NO_ERROR; + } else if (otherLen == 0) { + return NO_ERROR; + } + + SharedBuffer* buf = SharedBuffer::bufferFromData(mString) + ->editResize((myLen+otherLen+1)*sizeof(char16_t)); + if (buf) { + char16_t* str = (char16_t*)buf->data(); + memcpy(str+myLen, other, (otherLen+1)*sizeof(char16_t)); + mString = str; + return NO_ERROR; + } + return NO_MEMORY; +} + +status_t String16::append(const char16_t* chrs, size_t otherLen) +{ + const size_t myLen = size(); + if (myLen == 0) { + setTo(chrs, otherLen); + return NO_ERROR; + } else if (otherLen == 0) { + return NO_ERROR; + } + + SharedBuffer* buf = SharedBuffer::bufferFromData(mString) + ->editResize((myLen+otherLen+1)*sizeof(char16_t)); + if (buf) { + char16_t* str = (char16_t*)buf->data(); + memcpy(str+myLen, chrs, otherLen*sizeof(char16_t)); + str[myLen+otherLen] = 0; + mString = str; + return NO_ERROR; + } + return NO_MEMORY; +} + +status_t String16::insert(size_t pos, const char16_t* chrs) +{ + return insert(pos, chrs, strlen16(chrs)); +} + +status_t String16::insert(size_t pos, const char16_t* chrs, size_t len) +{ + const size_t myLen = size(); + if (myLen == 0) { + return setTo(chrs, len); + return NO_ERROR; + } else if (len == 0) { + return NO_ERROR; + } + + if (pos > myLen) pos = myLen; + + #if 0 + printf("Insert in to %s: pos=%d, len=%d, myLen=%d, chrs=%s\n", + String8(*this).string(), pos, + len, myLen, String8(chrs, len).string()); + #endif + + SharedBuffer* buf = SharedBuffer::bufferFromData(mString) + ->editResize((myLen+len+1)*sizeof(char16_t)); + if (buf) { + char16_t* str = (char16_t*)buf->data(); + if (pos < myLen) { + memmove(str+pos+len, str+pos, (myLen-pos)*sizeof(char16_t)); + } + memcpy(str+pos, chrs, len*sizeof(char16_t)); + str[myLen+len] = 0; + mString = str; + #if 0 + printf("Result (%d chrs): %s\n", size(), String8(*this).string()); + #endif + return NO_ERROR; + } + return NO_MEMORY; +} + +ssize_t String16::findFirst(char16_t c) const +{ + const char16_t* str = string(); + const char16_t* p = str; + const char16_t* e = p + size(); + while (p < e) { + if (*p == c) { + return p-str; + } + p++; + } + return -1; +} + +ssize_t String16::findLast(char16_t c) const +{ + const char16_t* str = string(); + const char16_t* p = str; + const char16_t* e = p + size(); + while (p < e) { + e--; + if (*e == c) { + return e-str; + } + } + return -1; +} + +bool String16::startsWith(const String16& prefix) const +{ + const size_t ps = prefix.size(); + if (ps > size()) return false; + return strzcmp16(mString, ps, prefix.string(), ps) == 0; +} + +bool String16::startsWith(const char16_t* prefix) const +{ + const size_t ps = strlen16(prefix); + if (ps > size()) return false; + return strncmp16(mString, prefix, ps) == 0; +} + +status_t String16::makeLower() +{ + const size_t N = size(); + const char16_t* str = string(); + char16_t* edit = NULL; + for (size_t i=0; i= 'A' && v <= 'Z') { + if (!edit) { + SharedBuffer* buf = SharedBuffer::bufferFromData(mString)->edit(); + if (!buf) { + return NO_MEMORY; + } + edit = (char16_t*)buf->data(); + mString = str = edit; + } + edit[i] = tolower((char)v); + } + } + return NO_ERROR; +} + +status_t String16::replaceAll(char16_t replaceThis, char16_t withThis) +{ + const size_t N = size(); + const char16_t* str = string(); + char16_t* edit = NULL; + for (size_t i=0; iedit(); + if (!buf) { + return NO_MEMORY; + } + edit = (char16_t*)buf->data(); + mString = str = edit; + } + edit[i] = withThis; + } + } + return NO_ERROR; +} + +status_t String16::remove(size_t len, size_t begin) +{ + const size_t N = size(); + if (begin >= N) { + SharedBuffer::bufferFromData(mString)->release(); + mString = getEmptyString(); + return NO_ERROR; + } + if ((begin+len) > N) len = N-begin; + if (begin == 0 && len == N) { + return NO_ERROR; + } + + if (begin > 0) { + SharedBuffer* buf = SharedBuffer::bufferFromData(mString) + ->editResize((N+1)*sizeof(char16_t)); + if (!buf) { + return NO_MEMORY; + } + char16_t* str = (char16_t*)buf->data(); + memmove(str, str+begin, (N-begin+1)*sizeof(char16_t)); + mString = str; + } + SharedBuffer* buf = SharedBuffer::bufferFromData(mString) + ->editResize((len+1)*sizeof(char16_t)); + if (buf) { + char16_t* str = (char16_t*)buf->data(); + str[len] = 0; + mString = str; + return NO_ERROR; + } + return NO_MEMORY; +} + +TextOutput& operator<<(TextOutput& to, const String16& val) +{ + to << String8(val).string(); + return to; +} + +}; // namespace android diff --git a/libs/utils/String8.cpp b/libs/utils/String8.cpp new file mode 100644 index 000000000..ab843f6e7 --- /dev/null +++ b/libs/utils/String8.cpp @@ -0,0 +1,602 @@ +/* + * Copyright (C) 2005 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 + +#include +#include +#include +#include + +#include + +#include + +namespace android { + +// --------------------------------------------------------------------------- + +static const uint32_t kByteMask = 0x000000BF; +static const uint32_t kByteMark = 0x00000080; + +// Surrogates aren't valid for UTF-32 characters, so define some +// constants that will let us screen them out. +static const uint32_t kUnicodeSurrogateHighStart = 0x0000D800; +static const uint32_t kUnicodeSurrogateHighEnd = 0x0000DBFF; +static const uint32_t kUnicodeSurrogateLowStart = 0x0000DC00; +static const uint32_t kUnicodeSurrogateLowEnd = 0x0000DFFF; +static const uint32_t kUnicodeSurrogateStart = kUnicodeSurrogateHighStart; +static const uint32_t kUnicodeSurrogateEnd = kUnicodeSurrogateLowEnd; + +// Mask used to set appropriate bits in first byte of UTF-8 sequence, +// indexed by number of bytes in the sequence. +static const uint32_t kFirstByteMark[] = { + 0x00000000, 0x00000000, 0x000000C0, 0x000000E0, 0x000000F0 +}; + +// Separator used by resource paths. This is not platform dependent contrary +// to OS_PATH_SEPARATOR. +#define RES_PATH_SEPARATOR '/' + +// Return number of utf8 bytes required for the character. +static size_t utf32_to_utf8_bytes(uint32_t srcChar) +{ + size_t bytesToWrite; + + // Figure out how many bytes the result will require. + if (srcChar < 0x00000080) + { + bytesToWrite = 1; + } + else if (srcChar < 0x00000800) + { + bytesToWrite = 2; + } + else if (srcChar < 0x00010000) + { + if ((srcChar < kUnicodeSurrogateStart) + || (srcChar > kUnicodeSurrogateEnd)) + { + bytesToWrite = 3; + } + else + { + // Surrogates are invalid UTF-32 characters. + return 0; + } + } + // Max code point for Unicode is 0x0010FFFF. + else if (srcChar < 0x00110000) + { + bytesToWrite = 4; + } + else + { + // Invalid UTF-32 character. + return 0; + } + + return bytesToWrite; +} + +// Write out the source character to . + +static void utf32_to_utf8(uint8_t* dstP, uint32_t srcChar, size_t bytes) +{ + dstP += bytes; + switch (bytes) + { /* note: everything falls through. */ + case 4: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6; + case 3: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6; + case 2: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6; + case 1: *--dstP = (uint8_t)(srcChar | kFirstByteMark[bytes]); + } +} + +// --------------------------------------------------------------------------- + +static SharedBuffer* gEmptyStringBuf = NULL; +static char* gEmptyString = NULL; + +extern int gDarwinCantLoadAllObjects; +int gDarwinIsReallyAnnoying; + +static inline char* getEmptyString() +{ + gEmptyStringBuf->acquire(); + return gEmptyString; +} + +void initialize_string8() +{ +#ifdef LIBUTILS_NATIVE + // Bite me, Darwin! + gDarwinIsReallyAnnoying = gDarwinCantLoadAllObjects; +#endif + + SharedBuffer* buf = SharedBuffer::alloc(1); + char* str = (char*)buf->data(); + *str = 0; + gEmptyStringBuf = buf; + gEmptyString = str; +} + +void terminate_string8() +{ + SharedBuffer::bufferFromData(gEmptyString)->release(); + gEmptyStringBuf = NULL; + gEmptyString = NULL; +} + +// --------------------------------------------------------------------------- + +static char* allocFromUTF8(const char* in, size_t len) +{ + if (len > 0) { + SharedBuffer* buf = SharedBuffer::alloc(len+1); + LOG_ASSERT(buf, "Unable to allocate shared buffer"); + if (buf) { + char* str = (char*)buf->data(); + memcpy(str, in, len); + str[len] = 0; + return str; + } + return NULL; + } + + return getEmptyString(); +} + +// Note: not dealing with expanding surrogate pairs. +static char* allocFromUTF16(const char16_t* in, size_t len) +{ + if (len == 0) return getEmptyString(); + + size_t bytes = 0; + const char16_t* end = in+len; + const char16_t* p = in; + + while (p < end) { + bytes += utf32_to_utf8_bytes(*p); + p++; + } + + SharedBuffer* buf = SharedBuffer::alloc(bytes+1); + LOG_ASSERT(buf, "Unable to allocate shared buffer"); + if (buf) { + p = in; + char* str = (char*)buf->data(); + char* d = str; + while (p < end) { + uint32_t c = *p++; + size_t len = utf32_to_utf8_bytes(c); + utf32_to_utf8((uint8_t*)d, c, len); + d += len; + } + *d = 0; + + return str; + } + + return getEmptyString(); +} + +// --------------------------------------------------------------------------- + +String8::String8() + : mString(getEmptyString()) +{ +} + +String8::String8(const String8& o) + : mString(o.mString) +{ + SharedBuffer::bufferFromData(mString)->acquire(); +} + +String8::String8(const char* o) + : mString(allocFromUTF8(o, strlen(o))) +{ + if (mString == NULL) { + mString = getEmptyString(); + } +} + +String8::String8(const char* o, size_t len) + : mString(allocFromUTF8(o, len)) +{ + if (mString == NULL) { + mString = getEmptyString(); + } +} + +String8::String8(const String16& o) + : mString(allocFromUTF16(o.string(), o.size())) +{ +} + +String8::String8(const char16_t* o) + : mString(allocFromUTF16(o, strlen16(o))) +{ +} + +String8::String8(const char16_t* o, size_t len) + : mString(allocFromUTF16(o, len)) +{ +} + +String8::~String8() +{ + SharedBuffer::bufferFromData(mString)->release(); +} + +void String8::setTo(const String8& other) +{ + SharedBuffer::bufferFromData(other.mString)->acquire(); + SharedBuffer::bufferFromData(mString)->release(); + mString = other.mString; +} + +status_t String8::setTo(const char* other) +{ + SharedBuffer::bufferFromData(mString)->release(); + mString = allocFromUTF8(other, strlen(other)); + if (mString) return NO_ERROR; + + mString = getEmptyString(); + return NO_MEMORY; +} + +status_t String8::setTo(const char* other, size_t len) +{ + SharedBuffer::bufferFromData(mString)->release(); + mString = allocFromUTF8(other, len); + if (mString) return NO_ERROR; + + mString = getEmptyString(); + return NO_MEMORY; +} + +status_t String8::setTo(const char16_t* other, size_t len) +{ + SharedBuffer::bufferFromData(mString)->release(); + mString = allocFromUTF16(other, len); + if (mString) return NO_ERROR; + + mString = getEmptyString(); + return NO_MEMORY; +} + +status_t String8::append(const String8& other) +{ + const size_t otherLen = other.bytes(); + if (bytes() == 0) { + setTo(other); + return NO_ERROR; + } else if (otherLen == 0) { + return NO_ERROR; + } + + return real_append(other.string(), otherLen); +} + +status_t String8::append(const char* other) +{ + return append(other, strlen(other)); +} + +status_t String8::append(const char* other, size_t otherLen) +{ + if (bytes() == 0) { + return setTo(other, otherLen); + } else if (otherLen == 0) { + return NO_ERROR; + } + + return real_append(other, otherLen); +} + +status_t String8::real_append(const char* other, size_t otherLen) +{ + const size_t myLen = bytes(); + + SharedBuffer* buf = SharedBuffer::bufferFromData(mString) + ->editResize(myLen+otherLen+1); + if (buf) { + char* str = (char*)buf->data(); + memcpy(str+myLen, other, otherLen+1); + mString = str; + return NO_ERROR; + } + return NO_MEMORY; +} + +char* String8::lockBuffer(size_t size) +{ + SharedBuffer* buf = SharedBuffer::bufferFromData(mString) + ->editResize(size+1); + if (buf) { + char* str = (char*)buf->data(); + mString = str; + return str; + } + return NULL; +} + +void String8::unlockBuffer() +{ + unlockBuffer(strlen(mString)); +} + +status_t String8::unlockBuffer(size_t size) +{ + if (size != this->size()) { + SharedBuffer* buf = SharedBuffer::bufferFromData(mString) + ->editResize(size+1); + if (buf) { + char* str = (char*)buf->data(); + str[size] = 0; + mString = str; + return NO_ERROR; + } + } + + return NO_MEMORY; +} + +ssize_t String8::find(const char* other, size_t start) const +{ + size_t len = size(); + if (start >= len) { + return -1; + } + const char* s = mString+start; + const char* p = strstr(s, other); + return p ? p-mString : -1; +} + +void String8::toLower() +{ + toLower(0, size()); +} + +void String8::toLower(size_t start, size_t length) +{ + const size_t len = size(); + if (start >= len) { + return; + } + if (start+length > len) { + length = len-start; + } + char* buf = lockBuffer(len); + buf += start; + while (length > 0) { + *buf = tolower(*buf); + buf++; + length--; + } + unlockBuffer(len); +} + +void String8::toUpper() +{ + toUpper(0, size()); +} + +void String8::toUpper(size_t start, size_t length) +{ + const size_t len = size(); + if (start >= len) { + return; + } + if (start+length > len) { + length = len-start; + } + char* buf = lockBuffer(len); + buf += start; + while (length > 0) { + *buf = toupper(*buf); + buf++; + length--; + } + unlockBuffer(len); +} + +TextOutput& operator<<(TextOutput& to, const String8& val) +{ + to << val.string(); + return to; +} + +// --------------------------------------------------------------------------- +// Path functions + + +void String8::setPathName(const char* name) +{ + setPathName(name, strlen(name)); +} + +void String8::setPathName(const char* name, size_t len) +{ + char* buf = lockBuffer(len); + + memcpy(buf, name, len); + + // remove trailing path separator, if present + if (len > 0 && buf[len-1] == OS_PATH_SEPARATOR) + len--; + + buf[len] = '\0'; + + unlockBuffer(len); +} + +String8 String8::getPathLeaf(void) const +{ + const char* cp; + const char*const buf = mString; + + cp = strrchr(buf, OS_PATH_SEPARATOR); + if (cp == NULL) + return String8(*this); + else + return String8(cp+1); +} + +String8 String8::getPathDir(void) const +{ + const char* cp; + const char*const str = mString; + + cp = strrchr(str, OS_PATH_SEPARATOR); + if (cp == NULL) + return String8(""); + else + return String8(str, cp - str); +} + +String8 String8::walkPath(String8* outRemains) const +{ + const char* cp; + const char*const str = mString; + const char* buf = str; + + cp = strchr(buf, OS_PATH_SEPARATOR); + if (cp == buf) { + // don't include a leading '/'. + buf = buf+1; + cp = strchr(buf, OS_PATH_SEPARATOR); + } + + if (cp == NULL) { + String8 res = buf != str ? String8(buf) : *this; + if (outRemains) *outRemains = String8(""); + return res; + } + + String8 res(buf, cp-buf); + if (outRemains) *outRemains = String8(cp+1); + return res; +} + +/* + * Helper function for finding the start of an extension in a pathname. + * + * Returns a pointer inside mString, or NULL if no extension was found. + */ +char* String8::find_extension(void) const +{ + const char* lastSlash; + const char* lastDot; + int extLen; + const char* const str = mString; + + // only look at the filename + lastSlash = strrchr(str, OS_PATH_SEPARATOR); + if (lastSlash == NULL) + lastSlash = str; + else + lastSlash++; + + // find the last dot + lastDot = strrchr(lastSlash, '.'); + if (lastDot == NULL) + return NULL; + + // looks good, ship it + return const_cast(lastDot); +} + +String8 String8::getPathExtension(void) const +{ + char* ext; + + ext = find_extension(); + if (ext != NULL) + return String8(ext); + else + return String8(""); +} + +String8 String8::getBasePath(void) const +{ + char* ext; + const char* const str = mString; + + ext = find_extension(); + if (ext == NULL) + return String8(*this); + else + return String8(str, ext - str); +} + +String8& String8::appendPath(const char* name) +{ + // TODO: The test below will fail for Win32 paths. Fix later or ignore. + if (name[0] != OS_PATH_SEPARATOR) { + if (*name == '\0') { + // nothing to do + return *this; + } + + size_t len = length(); + if (len == 0) { + // no existing filename, just use the new one + setPathName(name); + return *this; + } + + // make room for oldPath + '/' + newPath + int newlen = strlen(name); + + char* buf = lockBuffer(len+1+newlen); + + // insert a '/' if needed + if (buf[len-1] != OS_PATH_SEPARATOR) + buf[len++] = OS_PATH_SEPARATOR; + + memcpy(buf+len, name, newlen+1); + len += newlen; + + unlockBuffer(len); + + return *this; + } else { + setPathName(name); + return *this; + } +} + +String8& String8::convertToResPath() +{ +#if OS_PATH_SEPARATOR != RES_PATH_SEPARATOR + size_t len = length(); + if (len > 0) { + char * buf = lockBuffer(len); + for (char * end = buf + len; buf < end; ++buf) { + if (*buf == OS_PATH_SEPARATOR) + *buf = RES_PATH_SEPARATOR; + } + unlockBuffer(len); + } +#endif + return *this; +} + + +}; // namespace android diff --git a/libs/utils/SystemClock.cpp b/libs/utils/SystemClock.cpp new file mode 100644 index 000000000..2bdc0ce27 --- /dev/null +++ b/libs/utils/SystemClock.cpp @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +/* + * System clock functions. + */ + +#if HAVE_ANDROID_OS +#include +#include +#include +#include +#endif + +#include +#include +#include +#include +#include + +#include +#include + +#define LOG_TAG "SystemClock" +#include "utils/Log.h" + +namespace android { + +/* + * Set the current time. This only works when running as root. + */ +int setCurrentTimeMillis(int64_t millis) +{ +#if WIN32 + // not implemented + return -1; +#else + struct timeval tv; +#if HAVE_ANDROID_OS + struct timespec ts; + int fd; + int res; +#endif + int ret = 0; + + if (millis <= 0 || millis / 1000LL >= INT_MAX) { + return -1; + } + + tv.tv_sec = (time_t) (millis / 1000LL); + tv.tv_usec = (suseconds_t) ((millis % 1000LL) * 1000LL); + + LOGD("Setting time of day to sec=%d\n", (int) tv.tv_sec); + +#if HAVE_ANDROID_OS + fd = open("/dev/alarm", O_RDWR); + if(fd < 0) { + LOGW("Unable to open alarm driver: %s\n", strerror(errno)); + return -1; + } + ts.tv_sec = tv.tv_sec; + ts.tv_nsec = tv.tv_usec * 1000; + res = ioctl(fd, ANDROID_ALARM_SET_RTC, &ts); + if(res < 0) { + LOGW("Unable to set rtc to %ld: %s\n", tv.tv_sec, strerror(errno)); + ret = -1; + } + close(fd); +#else + if (settimeofday(&tv, NULL) != 0) { + LOGW("Unable to set clock to %d.%d: %s\n", + (int) tv.tv_sec, (int) tv.tv_usec, strerror(errno)); + ret = -1; + } +#endif + + return ret; +#endif // WIN32 +} + +/* + * native public static long uptimeMillis(); + */ +int64_t uptimeMillis() +{ + int64_t when = systemTime(SYSTEM_TIME_MONOTONIC); + return (int64_t) nanoseconds_to_milliseconds(when); +} + +/* + * native public static long elapsedRealtime(); + */ +int64_t elapsedRealtime() +{ +#if HAVE_ANDROID_OS + static int s_fd = -1; + + if (s_fd == -1) { + int fd = open("/dev/alarm", O_RDONLY); + if (android_atomic_cmpxchg(-1, fd, &s_fd)) { + close(fd); + } + } + + struct timespec ts; + int result = ioctl(s_fd, + ANDROID_ALARM_GET_TIME(ANDROID_ALARM_ELAPSED_REALTIME), &ts); + + if (result == 0) { + int64_t when = seconds_to_nanoseconds(ts.tv_sec) + ts.tv_nsec; + return (int64_t) nanoseconds_to_milliseconds(when); + } else { + // XXX: there was an error, probably because the driver didn't + // exist ... this should return + // a real error, like an exception! + int64_t when = systemTime(SYSTEM_TIME_MONOTONIC); + return (int64_t) nanoseconds_to_milliseconds(when); + } +#else + int64_t when = systemTime(SYSTEM_TIME_MONOTONIC); + return (int64_t) nanoseconds_to_milliseconds(when); +#endif +} + +}; // namespace android diff --git a/libs/utils/TextOutput.cpp b/libs/utils/TextOutput.cpp new file mode 100644 index 000000000..cebee99e5 --- /dev/null +++ b/libs/utils/TextOutput.cpp @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2005 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 + +#include + +#include +#include +#include + +// --------------------------------------------------------------------------- + +namespace android { + +TextOutput& operator<<(TextOutput& to, bool val) +{ + if (val) to.print("true", 4); + else to.print("false", 5); + return to; +} + +TextOutput& operator<<(TextOutput& to, int val) +{ + char buf[16]; + sprintf(buf, "%d", val); + to.print(buf, strlen(buf)); + return to; +} + +TextOutput& operator<<(TextOutput& to, long val) +{ + char buf[16]; + sprintf(buf, "%ld", val); + to.print(buf, strlen(buf)); + return to; +} + +TextOutput& operator<<(TextOutput& to, unsigned int val) +{ + char buf[16]; + sprintf(buf, "%u", val); + to.print(buf, strlen(buf)); + return to; +} + +TextOutput& operator<<(TextOutput& to, unsigned long val) +{ + char buf[16]; + sprintf(buf, "%lu", val); + to.print(buf, strlen(buf)); + return to; +} + +TextOutput& operator<<(TextOutput& to, long long val) +{ + char buf[32]; + sprintf(buf, "%Ld", val); + to.print(buf, strlen(buf)); + return to; +} + +TextOutput& operator<<(TextOutput& to, unsigned long long val) +{ + char buf[32]; + sprintf(buf, "%Lu", val); + to.print(buf, strlen(buf)); + return to; +} + +static TextOutput& print_float(TextOutput& to, double value) +{ + char buf[64]; + sprintf(buf, "%g", value); + if( !strchr(buf, '.') && !strchr(buf, 'e') && + !strchr(buf, 'E') ) { + strncat(buf, ".0", sizeof(buf)-1); + } + to.print(buf, strlen(buf)); + return to; +} + +TextOutput& operator<<(TextOutput& to, float val) +{ + return print_float(to,val); +} + +TextOutput& operator<<(TextOutput& to, double val) +{ + return print_float(to,val); +} + +TextOutput& operator<<(TextOutput& to, const void* val) +{ + char buf[16]; + sprintf(buf, "%p", val); + to.print(buf, strlen(buf)); + return to; +} + +static void textOutputPrinter(void* cookie, const char* txt) +{ + ((TextOutput*)cookie)->print(txt, strlen(txt)); +} + +TextOutput& operator<<(TextOutput& to, const TypeCode& val) +{ + printTypeCode(val.typeCode(), textOutputPrinter, (void*)&to); + return to; +} + +HexDump::HexDump(const void *buf, size_t size, size_t bytesPerLine) + : mBuffer(buf) + , mSize(size) + , mBytesPerLine(bytesPerLine) + , mSingleLineCutoff(16) + , mAlignment(4) + , mCArrayStyle(false) +{ + if (bytesPerLine >= 16) mAlignment = 4; + else if (bytesPerLine >= 8) mAlignment = 2; + else mAlignment = 1; +} + +TextOutput& operator<<(TextOutput& to, const HexDump& val) +{ + printHexData(0, val.buffer(), val.size(), val.bytesPerLine(), + val.singleLineCutoff(), val.alignment(), val.carrayStyle(), + textOutputPrinter, (void*)&to); + return to; +} + +}; // namespace android diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp new file mode 100644 index 000000000..74271ba3b --- /dev/null +++ b/libs/utils/Threads.cpp @@ -0,0 +1,1126 @@ +/* + * 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. + */ + +#define LOG_TAG "libutils.threads" + +#include +#include + +#include +#include +#include +#include +#include +#include + +#if defined(HAVE_PTHREADS) +# include +# include +# include +#elif defined(HAVE_WIN32_THREADS) +# include +# include +# include +# define HAVE_CREATETHREAD // Cygwin, vs. HAVE__BEGINTHREADEX for MinGW +#endif + +#if defined(HAVE_FUTEX) +#include +#endif + +#if defined(HAVE_PRCTL) +#include +#endif + +/* + * =========================================================================== + * Thread wrappers + * =========================================================================== + */ + +using namespace android; + +// ---------------------------------------------------------------------------- +#if defined(HAVE_PTHREADS) +#if 0 +#pragma mark - +#pragma mark PTHREAD +#endif +// ---------------------------------------------------------------------------- + +/* + * Create and run a new thead. + * + * We create it "detached", so it cleans up after itself. + */ + +typedef void* (*android_pthread_entry)(void*); + +struct thread_data_t { + thread_func_t entryFunction; + void* userData; + int priority; + char * threadName; + + // we use this trampoline when we need to set the priority with + // nice/setpriority. + static int trampoline(const thread_data_t* t) { + thread_func_t f = t->entryFunction; + void* u = t->userData; + int prio = t->priority; + char * name = t->threadName; + delete t; + setpriority(PRIO_PROCESS, 0, prio); + if (name) { +#if defined(HAVE_PRCTL) + // Mac OS doesn't have this, and we build libutil for the host too + int hasAt = 0; + int hasDot = 0; + char *s = name; + while (*s) { + if (*s == '.') hasDot = 1; + else if (*s == '@') hasAt = 1; + s++; + } + int len = s - name; + if (len < 15 || hasAt || !hasDot) { + s = name; + } else { + s = name + len - 15; + } + prctl(PR_SET_NAME, (unsigned long) s, 0, 0, 0); +#endif + free(name); + } + return f(u); + } +}; + +int androidCreateRawThreadEtc(android_thread_func_t entryFunction, + void *userData, + const char* threadName, + int32_t threadPriority, + size_t threadStackSize, + android_thread_id_t *threadId) +{ + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + +#ifdef HAVE_ANDROID_OS /* valgrind is rejecting RT-priority create reqs */ + if (threadPriority != PRIORITY_DEFAULT || threadName != NULL) { + // We could avoid the trampoline if there was a way to get to the + // android_thread_id_t (pid) from pthread_t + thread_data_t* t = new thread_data_t; + t->priority = threadPriority; + t->threadName = threadName ? strdup(threadName) : NULL; + t->entryFunction = entryFunction; + t->userData = userData; + entryFunction = (android_thread_func_t)&thread_data_t::trampoline; + userData = t; + } +#endif + + if (threadStackSize) { + pthread_attr_setstacksize(&attr, threadStackSize); + } + + errno = 0; + pthread_t thread; + int result = pthread_create(&thread, &attr, + (android_pthread_entry)entryFunction, userData); + if (result != 0) { + LOGE("androidCreateRawThreadEtc failed (entry=%p, res=%d, errno=%d)\n" + "(android threadPriority=%d)", + entryFunction, result, errno, threadPriority); + return 0; + } + + if (threadId != NULL) { + *threadId = (android_thread_id_t)thread; // XXX: this is not portable + } + return 1; +} + +android_thread_id_t androidGetThreadId() +{ + return (android_thread_id_t)pthread_self(); +} + +// ---------------------------------------------------------------------------- +#elif defined(HAVE_WIN32_THREADS) +#if 0 +#pragma mark - +#pragma mark WIN32_THREADS +#endif +// ---------------------------------------------------------------------------- + +/* + * Trampoline to make us __stdcall-compliant. + * + * We're expected to delete "vDetails" when we're done. + */ +struct threadDetails { + int (*func)(void*); + void* arg; +}; +static __stdcall unsigned int threadIntermediary(void* vDetails) +{ + struct threadDetails* pDetails = (struct threadDetails*) vDetails; + int result; + + result = (*(pDetails->func))(pDetails->arg); + + delete pDetails; + + LOG(LOG_VERBOSE, "thread", "thread exiting\n"); + return (unsigned int) result; +} + +/* + * Create and run a new thread. + */ +static bool doCreateThread(android_thread_func_t fn, void* arg, android_thread_id_t *id) +{ + HANDLE hThread; + struct threadDetails* pDetails = new threadDetails; // must be on heap + unsigned int thrdaddr; + + pDetails->func = fn; + pDetails->arg = arg; + +#if defined(HAVE__BEGINTHREADEX) + hThread = (HANDLE) _beginthreadex(NULL, 0, threadIntermediary, pDetails, 0, + &thrdaddr); + if (hThread == 0) +#elif defined(HAVE_CREATETHREAD) + hThread = CreateThread(NULL, 0, + (LPTHREAD_START_ROUTINE) threadIntermediary, + (void*) pDetails, 0, (DWORD*) &thrdaddr); + if (hThread == NULL) +#endif + { + LOG(LOG_WARN, "thread", "WARNING: thread create failed\n"); + return false; + } + +#if defined(HAVE_CREATETHREAD) + /* close the management handle */ + CloseHandle(hThread); +#endif + + if (id != NULL) { + *id = (android_thread_id_t)thrdaddr; + } + + return true; +} + +int androidCreateRawThreadEtc(android_thread_func_t fn, + void *userData, + const char* threadName, + int32_t threadPriority, + size_t threadStackSize, + android_thread_id_t *threadId) +{ + return doCreateThread( fn, userData, threadId); +} + +android_thread_id_t androidGetThreadId() +{ + return (android_thread_id_t)GetCurrentThreadId(); +} + +// ---------------------------------------------------------------------------- +#else +#error "Threads not supported" +#endif + +// ---------------------------------------------------------------------------- + +#if 0 +#pragma mark - +#pragma mark Common Thread functions +#endif + +int androidCreateThread(android_thread_func_t fn, void* arg) +{ + return createThreadEtc(fn, arg); +} + +int androidCreateThreadGetID(android_thread_func_t fn, void *arg, android_thread_id_t *id) +{ + return createThreadEtc(fn, arg, "android:unnamed_thread", + PRIORITY_DEFAULT, 0, id); +} + +static android_create_thread_fn gCreateThreadFn = androidCreateRawThreadEtc; + +int androidCreateThreadEtc(android_thread_func_t entryFunction, + void *userData, + const char* threadName, + int32_t threadPriority, + size_t threadStackSize, + android_thread_id_t *threadId) +{ + return gCreateThreadFn(entryFunction, userData, threadName, + threadPriority, threadStackSize, threadId); +} + +void androidSetCreateThreadFunc(android_create_thread_fn func) +{ + gCreateThreadFn = func; +} + +namespace android { + +/* + * =========================================================================== + * Mutex class + * =========================================================================== + */ + +#if 0 +#pragma mark - +#pragma mark Mutex +#endif + +#if defined(HAVE_PTHREADS) && !defined(HAVE_FUTEX) +/* + * Simple pthread wrapper. + */ + +Mutex::Mutex() +{ + _init(); +} + +Mutex::Mutex(const char* name) +{ + // XXX: name not used for now + _init(); +} + +void Mutex::_init() +{ + pthread_mutex_t* pMutex = new pthread_mutex_t; + pthread_mutex_init(pMutex, NULL); + mState = pMutex; +} + +Mutex::~Mutex() +{ + delete (pthread_mutex_t*) mState; +} + +status_t Mutex::lock() +{ + int res; + while ((res=pthread_mutex_lock((pthread_mutex_t*) mState)) == EINTR) ; + return -res; +} + +void Mutex::unlock() +{ + pthread_mutex_unlock((pthread_mutex_t*) mState); +} + +status_t Mutex::tryLock() +{ + int res; + while ((res=pthread_mutex_trylock((pthread_mutex_t*) mState)) == EINTR) ; + return -res; +} + +#elif defined(HAVE_FUTEX) +#if 0 +#pragma mark - +#endif + +#define STATE ((futex_mutex_t*) (&mState)) + +Mutex::Mutex() +{ + _init(); +} + +Mutex::Mutex(const char* name) +{ + _init(); +} + +void +Mutex::_init() +{ + futex_mutex_init(STATE); +} + +Mutex::~Mutex() +{ +} + +status_t Mutex::lock() +{ + int res; + while ((res=futex_mutex_lock(STATE, FUTEX_WAIT_INFINITE)) == EINTR) ; + return -res; +} + +void Mutex::unlock() +{ + futex_mutex_unlock(STATE); +} + +status_t Mutex::tryLock() +{ + int res; + while ((res=futex_mutex_trylock(STATE)) == EINTR) ; + return -res; +} +#undef STATE + +#elif defined(HAVE_WIN32_THREADS) +#if 0 +#pragma mark - +#endif + +Mutex::Mutex() +{ + HANDLE hMutex; + + assert(sizeof(hMutex) == sizeof(mState)); + + hMutex = CreateMutex(NULL, FALSE, NULL); + mState = (void*) hMutex; +} + +Mutex::Mutex(const char* name) +{ + // XXX: name not used for now + HANDLE hMutex; + + hMutex = CreateMutex(NULL, FALSE, NULL); + mState = (void*) hMutex; +} + +Mutex::~Mutex() +{ + CloseHandle((HANDLE) mState); +} + +status_t Mutex::lock() +{ + DWORD dwWaitResult; + dwWaitResult = WaitForSingleObject((HANDLE) mState, INFINITE); + return dwWaitResult != WAIT_OBJECT_0 ? -1 : NO_ERROR; +} + +void Mutex::unlock() +{ + if (!ReleaseMutex((HANDLE) mState)) + LOG(LOG_WARN, "thread", "WARNING: bad result from unlocking mutex\n"); +} + +status_t Mutex::tryLock() +{ + DWORD dwWaitResult; + + dwWaitResult = WaitForSingleObject((HANDLE) mState, 0); + if (dwWaitResult != WAIT_OBJECT_0 && dwWaitResult != WAIT_TIMEOUT) + LOG(LOG_WARN, "thread", "WARNING: bad result from try-locking mutex\n"); + return (dwWaitResult == WAIT_OBJECT_0) ? 0 : -1; +} + +#else +#error "Somebody forgot to implement threads for this platform." +#endif + + +/* + * =========================================================================== + * Condition class + * =========================================================================== + */ + +#if 0 +#pragma mark - +#pragma mark Condition +#endif + +#if defined(HAVE_PTHREADS) && !defined(HAVE_FUTEX) + +/* + * Constructor. This is a simple pthread wrapper. + */ +Condition::Condition() +{ + pthread_cond_t* pCond = new pthread_cond_t; + + pthread_cond_init(pCond, NULL); + mState = pCond; +} + +/* + * Destructor. + */ +Condition::~Condition() +{ + pthread_cond_destroy((pthread_cond_t*) mState); + delete (pthread_cond_t*) mState; +} + +/* + * Wait on a condition variable. Lock the mutex before calling. + */ + +status_t Condition::wait(Mutex& mutex) +{ + assert(mutex.mState != NULL); + + int cc; + while ((cc = pthread_cond_wait((pthread_cond_t*)mState, + (pthread_mutex_t*) mutex.mState)) == EINTR) ; + return -cc; +} + +status_t Condition::wait(Mutex& mutex, nsecs_t abstime) +{ + assert(mutex.mState != NULL); + + struct timespec ts; + ts.tv_sec = abstime/1000000000; + ts.tv_nsec = abstime-(ts.tv_sec*1000000000); + + int cc; + while ((cc = pthread_cond_timedwait((pthread_cond_t*)mState, + (pthread_mutex_t*) mutex.mState, &ts)) == EINTR) ; + return -cc; +} + +status_t Condition::waitRelative(Mutex& mutex, nsecs_t reltime) +{ + return wait(mutex, systemTime()+reltime); +} + +/* + * Signal the condition variable, allowing one thread to continue. + */ +void Condition::signal() +{ + pthread_cond_signal((pthread_cond_t*) mState); +} + +/* + * Signal the condition variable, allowing all threads to continue. + */ +void Condition::broadcast() +{ + pthread_cond_broadcast((pthread_cond_t*) mState); +} + +#elif defined(HAVE_FUTEX) +#if 0 +#pragma mark - +#endif + +#define STATE ((futex_cond_t*) (&mState)) + +/* + * Constructor. This is a simple pthread wrapper. + */ +Condition::Condition() +{ + futex_cond_init(STATE); +} + +/* + * Destructor. + */ +Condition::~Condition() +{ +} + +/* + * Wait on a condition variable. Lock the mutex before calling. + */ + +status_t Condition::wait(Mutex& mutex) +{ + assert(mutex.mState != NULL); + + int res; + while ((res = futex_cond_wait(STATE, + (futex_mutex_t*)(&mutex.mState), FUTEX_WAIT_INFINITE)) == -EINTR) ; + + return -res; +} + +status_t Condition::wait(Mutex& mutex, nsecs_t abstime) +{ + nsecs_t reltime = abstime - systemTime(); + if (reltime <= 0) return true; + return waitRelative(mutex, reltime); +} + +status_t Condition::waitRelative(Mutex& mutex, nsecs_t reltime) +{ + assert(mutex.mState != NULL); + int res; + unsigned msec = ns2ms(reltime); + if(msec == 0) + return true; + // This code will not time out at the correct time if interrupted by signals + while ((res = futex_cond_wait(STATE, + (futex_mutex_t*)(&mutex.mState), msec)) == -EINTR) ; + return res; +} + +/* + * Signal the condition variable, allowing one thread to continue. + */ +void Condition::signal() +{ + futex_cond_signal(STATE); +} + +/* + * Signal the condition variable, allowing all threads to continue. + */ +void Condition::broadcast() +{ + futex_cond_broadcast(STATE); +} + +#undef STATE + +#elif defined(HAVE_WIN32_THREADS) +#if 0 +#pragma mark - +#endif + +/* + * Windows doesn't have a condition variable solution. It's possible + * to create one, but it's easy to get it wrong. For a discussion, and + * the origin of this implementation, see: + * + * http://www.cs.wustl.edu/~schmidt/win32-cv-1.html + * + * The implementation shown on the page does NOT follow POSIX semantics. + * As an optimization they require acquiring the external mutex before + * calling signal() and broadcast(), whereas POSIX only requires grabbing + * it before calling wait(). The implementation here has been un-optimized + * to have the correct behavior. + */ +typedef struct WinCondition { + // Number of waiting threads. + int waitersCount; + + // Serialize access to waitersCount. + CRITICAL_SECTION waitersCountLock; + + // Semaphore used to queue up threads waiting for the condition to + // become signaled. + HANDLE sema; + + // An auto-reset event used by the broadcast/signal thread to wait + // for all the waiting thread(s) to wake up and be released from + // the semaphore. + HANDLE waitersDone; + + // This mutex wouldn't be necessary if we required that the caller + // lock the external mutex before calling signal() and broadcast(). + // I'm trying to mimic pthread semantics though. + HANDLE internalMutex; + + // Keeps track of whether we were broadcasting or signaling. This + // allows us to optimize the code if we're just signaling. + bool wasBroadcast; + + status_t wait(WinCondition* condState, HANDLE hMutex, nsecs_t* abstime) + { + // Increment the wait count, avoiding race conditions. + EnterCriticalSection(&condState->waitersCountLock); + condState->waitersCount++; + //printf("+++ wait: incr waitersCount to %d (tid=%ld)\n", + // condState->waitersCount, getThreadId()); + LeaveCriticalSection(&condState->waitersCountLock); + + DWORD timeout = INFINITE; + if (abstime) { + nsecs_t reltime = *abstime - systemTime(); + if (reltime < 0) + reltime = 0; + timeout = reltime/1000000; + } + + // Atomically release the external mutex and wait on the semaphore. + DWORD res = + SignalObjectAndWait(hMutex, condState->sema, timeout, FALSE); + + //printf("+++ wait: awake (tid=%ld)\n", getThreadId()); + + // Reacquire lock to avoid race conditions. + EnterCriticalSection(&condState->waitersCountLock); + + // No longer waiting. + condState->waitersCount--; + + // Check to see if we're the last waiter after a broadcast. + bool lastWaiter = (condState->wasBroadcast && condState->waitersCount == 0); + + //printf("+++ wait: lastWaiter=%d (wasBc=%d wc=%d)\n", + // lastWaiter, condState->wasBroadcast, condState->waitersCount); + + LeaveCriticalSection(&condState->waitersCountLock); + + // If we're the last waiter thread during this particular broadcast + // then signal broadcast() that we're all awake. It'll drop the + // internal mutex. + if (lastWaiter) { + // Atomically signal the "waitersDone" event and wait until we + // can acquire the internal mutex. We want to do this in one step + // because it ensures that everybody is in the mutex FIFO before + // any thread has a chance to run. Without it, another thread + // could wake up, do work, and hop back in ahead of us. + SignalObjectAndWait(condState->waitersDone, condState->internalMutex, + INFINITE, FALSE); + } else { + // Grab the internal mutex. + WaitForSingleObject(condState->internalMutex, INFINITE); + } + + // Release the internal and grab the external. + ReleaseMutex(condState->internalMutex); + WaitForSingleObject(hMutex, INFINITE); + + return res == WAIT_OBJECT_0 ? NO_ERROR : -1; + } +} WinCondition; + +/* + * Constructor. Set up the WinCondition stuff. + */ +Condition::Condition() +{ + WinCondition* condState = new WinCondition; + + condState->waitersCount = 0; + condState->wasBroadcast = false; + // semaphore: no security, initial value of 0 + condState->sema = CreateSemaphore(NULL, 0, 0x7fffffff, NULL); + InitializeCriticalSection(&condState->waitersCountLock); + // auto-reset event, not signaled initially + condState->waitersDone = CreateEvent(NULL, FALSE, FALSE, NULL); + // used so we don't have to lock external mutex on signal/broadcast + condState->internalMutex = CreateMutex(NULL, FALSE, NULL); + + mState = condState; +} + +/* + * Destructor. Free Windows resources as well as our allocated storage. + */ +Condition::~Condition() +{ + WinCondition* condState = (WinCondition*) mState; + if (condState != NULL) { + CloseHandle(condState->sema); + CloseHandle(condState->waitersDone); + delete condState; + } +} + + +status_t Condition::wait(Mutex& mutex) +{ + WinCondition* condState = (WinCondition*) mState; + HANDLE hMutex = (HANDLE) mutex.mState; + + return ((WinCondition*)mState)->wait(condState, hMutex, NULL); +} + +status_t Condition::wait(Mutex& mutex, nsecs_t abstime) +{ + WinCondition* condState = (WinCondition*) mState; + HANDLE hMutex = (HANDLE) mutex.mState; + + return ((WinCondition*)mState)->wait(condState, hMutex, &abstime); +} + +status_t Condition::waitRelative(Mutex& mutex, nsecs_t reltime) +{ + return wait(mutex, systemTime()+reltime); +} + +/* + * Signal the condition variable, allowing one thread to continue. + */ +void Condition::signal() +{ + WinCondition* condState = (WinCondition*) mState; + + // Lock the internal mutex. This ensures that we don't clash with + // broadcast(). + WaitForSingleObject(condState->internalMutex, INFINITE); + + EnterCriticalSection(&condState->waitersCountLock); + bool haveWaiters = (condState->waitersCount > 0); + LeaveCriticalSection(&condState->waitersCountLock); + + // If no waiters, then this is a no-op. Otherwise, knock the semaphore + // down a notch. + if (haveWaiters) + ReleaseSemaphore(condState->sema, 1, 0); + + // Release internal mutex. + ReleaseMutex(condState->internalMutex); +} + +/* + * Signal the condition variable, allowing all threads to continue. + * + * First we have to wake up all threads waiting on the semaphore, then + * we wait until all of the threads have actually been woken before + * releasing the internal mutex. This ensures that all threads are woken. + */ +void Condition::broadcast() +{ + WinCondition* condState = (WinCondition*) mState; + + // Lock the internal mutex. This keeps the guys we're waking up + // from getting too far. + WaitForSingleObject(condState->internalMutex, INFINITE); + + EnterCriticalSection(&condState->waitersCountLock); + bool haveWaiters = false; + + if (condState->waitersCount > 0) { + haveWaiters = true; + condState->wasBroadcast = true; + } + + if (haveWaiters) { + // Wake up all the waiters. + ReleaseSemaphore(condState->sema, condState->waitersCount, 0); + + LeaveCriticalSection(&condState->waitersCountLock); + + // Wait for all awakened threads to acquire the counting semaphore. + // The last guy who was waiting sets this. + WaitForSingleObject(condState->waitersDone, INFINITE); + + // Reset wasBroadcast. (No crit section needed because nobody + // else can wake up to poke at it.) + condState->wasBroadcast = 0; + } else { + // nothing to do + LeaveCriticalSection(&condState->waitersCountLock); + } + + // Release internal mutex. + ReleaseMutex(condState->internalMutex); +} + +#else +#error "condition variables not supported on this platform" +#endif + + +/* + * =========================================================================== + * ReadWriteLock class + * =========================================================================== + */ + +#if 0 +#pragma mark - +#pragma mark ReadWriteLock +#endif + +/* + * Add a reader. Readers are nice. They share. + */ +void ReadWriteLock::lockForRead() +{ + mLock.lock(); + while (mNumWriters > 0) { + LOG(LOG_DEBUG, "thread", "+++ lockForRead: waiting\n"); + mReadWaiter.wait(mLock); + } + assert(mNumWriters == 0); + mNumReaders++; +#if defined(PRINT_RENDER_TIMES) + if (mNumReaders == 1) + mDebugTimer.start(); +#endif + mLock.unlock(); +} + +/* + * Try to add a reader. If it doesn't work right away, return "false". + */ +bool ReadWriteLock::tryLockForRead() +{ + mLock.lock(); + if (mNumWriters > 0) { + mLock.unlock(); + return false; + } + assert(mNumWriters == 0); + mNumReaders++; +#if defined(PRINT_RENDER_TIMES) + if (mNumReaders == 1) + mDebugTimer.start(); +#endif + mLock.unlock(); + return true; +} + +/* + * Remove a reader. + */ +void ReadWriteLock::unlockForRead() +{ + mLock.lock(); + if (mNumReaders == 0) { + LOG(LOG_WARN, "thread", + "WARNING: unlockForRead requested, but not locked\n"); + return; + } + assert(mNumReaders > 0); + assert(mNumWriters == 0); + mNumReaders--; + if (mNumReaders == 0) { // last reader? +#if defined(PRINT_RENDER_TIMES) + mDebugTimer.stop(); + printf(" rdlk held %.3f msec\n", + (double) mDebugTimer.durationUsecs() / 1000.0); +#endif + //printf("+++ signaling writers (if any)\n"); + mWriteWaiter.signal(); // wake one writer (if any) + } + mLock.unlock(); +} + +/* + * Add a writer. This requires exclusive access to the object. + */ +void ReadWriteLock::lockForWrite() +{ + mLock.lock(); + while (mNumReaders > 0 || mNumWriters > 0) { + LOG(LOG_DEBUG, "thread", "+++ lockForWrite: waiting\n"); + mWriteWaiter.wait(mLock); + } + assert(mNumReaders == 0); + assert(mNumWriters == 0); + mNumWriters++; +#if defined(PRINT_RENDER_TIMES) + mDebugTimer.start(); +#endif + mLock.unlock(); +} + +/* + * Try to add a writer. If it doesn't work right away, return "false". + */ +bool ReadWriteLock::tryLockForWrite() +{ + mLock.lock(); + if (mNumReaders > 0 || mNumWriters > 0) { + mLock.unlock(); + return false; + } + assert(mNumReaders == 0); + assert(mNumWriters == 0); + mNumWriters++; +#if defined(PRINT_RENDER_TIMES) + mDebugTimer.start(); +#endif + mLock.unlock(); + return true; +} + +/* + * Remove a writer. + */ +void ReadWriteLock::unlockForWrite() +{ + mLock.lock(); + if (mNumWriters == 0) { + LOG(LOG_WARN, "thread", + "WARNING: unlockForWrite requested, but not locked\n"); + return; + } + assert(mNumWriters == 1); + mNumWriters--; +#if defined(PRINT_RENDER_TIMES) + mDebugTimer.stop(); + //printf(" wrlk held %.3f msec\n", + // (double) mDebugTimer.durationUsecs() / 1000.0); +#endif + // mWriteWaiter.signal(); // should other writers get first dibs? + //printf("+++ signaling readers (if any)\n"); + mReadWaiter.broadcast(); // wake all readers (if any) + mLock.unlock(); +} + +// ---------------------------------------------------------------------------- + +#if 0 +#pragma mark - +#pragma mark Thread::Thread +#endif + +/* + * This is our thread object! + */ + +Thread::Thread(bool canCallJava) + : mCanCallJava(canCallJava), + mThread(thread_id_t(-1)), + mLock("Thread::mLock"), + mStatus(NO_ERROR), + mExitPending(false), mRunning(false) +{ +} + +Thread::~Thread() +{ +} + +status_t Thread::readyToRun() +{ + return NO_ERROR; +} + +status_t Thread::run(const char* name, int32_t priority, size_t stack) +{ + Mutex::Autolock _l(mLock); + + if (mRunning) { + // thread already started + return INVALID_OPERATION; + } + + // reset status and exitPending to their default value, so we can + // try again after an error happened (either below, or in readyToRun()) + mStatus = NO_ERROR; + mExitPending = false; + mThread = thread_id_t(-1); + + // hold a strong reference on ourself + mHoldSelf = this; + + bool res; + if (mCanCallJava) { + res = createThreadEtc(_threadLoop, + this, name, priority, stack, &mThread); + } else { + res = androidCreateRawThreadEtc(_threadLoop, + this, name, priority, stack, &mThread); + } + + if (res == false) { + mStatus = UNKNOWN_ERROR; // something happened! + mRunning = false; + mThread = thread_id_t(-1); + } + + if (mStatus < 0) { + // something happened, don't leak + mHoldSelf.clear(); + } + + return mStatus; +} + +int Thread::_threadLoop(void* user) +{ + Thread* const self = static_cast(user); + sp strong(self->mHoldSelf); + wp weak(strong); + self->mHoldSelf.clear(); + + // we're about to run... + self->mStatus = self->readyToRun(); + if (self->mStatus!=NO_ERROR || self->mExitPending) { + // pretend the thread never started... + self->mExitPending = false; + self->mRunning = false; + return 0; + } + + // thread is running now + self->mRunning = true; + + do { + bool result = self->threadLoop(); + if (result == false || self->mExitPending) { + self->mExitPending = true; + self->mLock.lock(); + self->mRunning = false; + self->mThreadExitedCondition.signal(); + self->mLock.unlock(); + break; + } + + // Release our strong reference, to let a chance to the thread + // to die a peaceful death. + strong.clear(); + // And immediately, reacquire a strong reference for the next loop + strong = weak.promote(); + } while(strong != 0); + + return 0; +} + +void Thread::requestExit() +{ + mExitPending = true; +} + +status_t Thread::requestExitAndWait() +{ + if (mStatus == OK) { + + if (mThread == getThreadId()) { + LOGW( + "Thread (this=%p): don't call waitForExit() from this " + "Thread object's thread. It's a guaranteed deadlock!", + this); + return WOULD_BLOCK; + } + + requestExit(); + + Mutex::Autolock _l(mLock); + while (mRunning == true) { + mThreadExitedCondition.wait(mLock); + } + mExitPending = false; + } + return mStatus; +} + +bool Thread::exitPending() const +{ + return mExitPending; +} + + + +}; // namespace android diff --git a/libs/utils/TimerProbe.cpp b/libs/utils/TimerProbe.cpp new file mode 100644 index 000000000..835480d36 --- /dev/null +++ b/libs/utils/TimerProbe.cpp @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#if ENABLE_TIMER_PROBE + +#ifdef LOG_TAG +#undef LOG_TAG +#endif +#define LOG_TAG "time" + +namespace android { + +Vector TimerProbe::gBuckets; +TimerProbe* TimerProbe::gExecuteChain; +int TimerProbe::gIndent; +timespec TimerProbe::gRealBase; + +TimerProbe::TimerProbe(const char tag[], int* slot) : mTag(tag) +{ + mNext = gExecuteChain; + gExecuteChain = this; + mIndent = gIndent; + gIndent += 1; + if (mIndent > 0) { + if (*slot == 0) { + int count = gBuckets.add(); + *slot = count; + Bucket& bucket = gBuckets.editItemAt(count); + memset(&bucket, 0, sizeof(Bucket)); + bucket.mTag = tag; + bucket.mSlotPtr = slot; + bucket.mIndent = mIndent; + } + mBucket = *slot; + } + clock_gettime(CLOCK_REALTIME, &mRealStart); + if (gRealBase.tv_sec == 0) + gRealBase = mRealStart; + clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &mPStart); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &mTStart); +} + +void TimerProbe::end() +{ + timespec realEnd, pEnd, tEnd; + clock_gettime(CLOCK_REALTIME, &realEnd); + clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &pEnd); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tEnd); + print(realEnd, pEnd, tEnd); + mTag = NULL; +} + +TimerProbe::~TimerProbe() +{ + if (mTag != NULL) + end(); + gExecuteChain = mNext; + gIndent--; +} + + +uint32_t TimerProbe::ElapsedTime(const timespec& start, const timespec& end) +{ + int sec = end.tv_sec - start.tv_sec; + int nsec = end.tv_nsec - start.tv_nsec; + if (nsec < 0) { + sec--; + nsec += 1000000000; + } + return sec * 1000000 + nsec / 1000; +} + +void TimerProbe::print(const timespec& r, const timespec& p, + const timespec& t) const +{ + uint32_t es = ElapsedTime(gRealBase, mRealStart); + uint32_t er = ElapsedTime(mRealStart, r); + uint32_t ep = ElapsedTime(mPStart, p); + uint32_t et = ElapsedTime(mTStart, t); + if (mIndent > 0) { + Bucket& bucket = gBuckets.editItemAt(mBucket); + if (bucket.mStart == 0) + bucket.mStart = es; + bucket.mReal += er; + bucket.mProcess += ep; + bucket.mThread += et; + bucket.mCount++; + return; + } + int index = 0; + int buckets = gBuckets.size(); + int count = 1; + const char* tag = mTag; + int indent = mIndent; + do { + LOGD("%-30.30s: (%3d) %-5.*s time=%-10.3f real=%7dus process=%7dus (%3d%%) thread=%7dus (%3d%%)\n", + tag, count, indent > 5 ? 5 : indent, "+++++", es / 1000000.0, + er, ep, ep * 100 / er, et, et * 100 / er); + if (index >= buckets) + break; + Bucket& bucket = gBuckets.editItemAt(index); + count = bucket.mCount; + es = bucket.mStart; + er = bucket.mReal; + ep = bucket.mProcess; + et = bucket.mThread; + tag = bucket.mTag; + indent = bucket.mIndent; + *bucket.mSlotPtr = 0; + } while (++index); // always true + gBuckets.clear(); +} + +}; // namespace android + +#endif diff --git a/libs/utils/Timers.cpp b/libs/utils/Timers.cpp new file mode 100644 index 000000000..2abc811a0 --- /dev/null +++ b/libs/utils/Timers.cpp @@ -0,0 +1,240 @@ +/* + * Copyright (C) 2005 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. + */ + +// +// Timer functions. +// +#include +#include // may need usleep +#include + +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_WIN32_THREADS +#include +#endif + +nsecs_t systemTime(int clock) +{ +#if defined(HAVE_POSIX_CLOCKS) + static const clockid_t clocks[] = { + CLOCK_REALTIME, + CLOCK_MONOTONIC, + CLOCK_PROCESS_CPUTIME_ID, + CLOCK_THREAD_CPUTIME_ID + }; + struct timespec t; + t.tv_sec = t.tv_nsec = 0; + clock_gettime(clocks[clock], &t); + return nsecs_t(t.tv_sec)*1000000000LL + t.tv_nsec; +#else + // we don't support the clocks here. + struct timeval t; + t.tv_sec = t.tv_usec = 0; + gettimeofday(&t, NULL); + return nsecs_t(t.tv_sec)*1000000000LL + nsecs_t(t.tv_usec)*1000LL; +#endif +} + +//#define MONITOR_USLEEP + +/* + * Sleep long enough that we'll wake up "interval" milliseconds after + * the previous snooze. + * + * The "nextTick" argument is updated on each call, and should be passed + * in every time. Set its fields to zero on the first call. + * + * Returns the #of intervals we have overslept, which will be zero if we're + * on time. [Currently just returns 0 or 1.] + */ +int sleepForInterval(long interval, struct timeval* pNextTick) +{ + struct timeval now; + long long timeBeforeNext; + long sleepTime = 0; + bool overSlept = false; + //int usleepBias = 0; + +#ifdef USLEEP_BIAS + /* + * Linux likes to add 9000ms or so. + * [not using this for now] + */ + //usleepBias = USLEEP_BIAS; +#endif + + gettimeofday(&now, NULL); + + if (pNextTick->tv_sec == 0) { + /* special-case for first time through */ + *pNextTick = now; + sleepTime = interval; + android::DurationTimer::addToTimeval(pNextTick, interval); + } else { + /* + * Compute how much time there is before the next tick. If this + * value is negative, we've run over. If we've run over a little + * bit we can shorten the next frame to keep the pace steady, but + * if we've dramatically overshot we need to re-sync. + */ + timeBeforeNext = android::DurationTimer::subtractTimevals(pNextTick, &now); + //printf("TOP: now=%ld.%ld next=%ld.%ld diff=%ld\n", + // now.tv_sec, now.tv_usec, pNextTick->tv_sec, pNextTick->tv_usec, + // (long) timeBeforeNext); + if (timeBeforeNext < -interval) { + /* way over */ + overSlept = true; + sleepTime = 0; + *pNextTick = now; + } else if (timeBeforeNext <= 0) { + /* slightly over, keep the pace steady */ + overSlept = true; + sleepTime = 0; + } else if (timeBeforeNext <= interval) { + /* right on schedule */ + sleepTime = timeBeforeNext; + } else if (timeBeforeNext > interval && timeBeforeNext <= 2*interval) { + /* sleep call returned early; do a longer sleep this time */ + sleepTime = timeBeforeNext; + } else if (timeBeforeNext > interval) { + /* we went back in time -- somebody updated system clock? */ + /* (could also be a *seriously* broken usleep()) */ + LOG(LOG_DEBUG, "", + " Impossible: timeBeforeNext = %ld\n", (long)timeBeforeNext); + sleepTime = 0; + *pNextTick = now; + } + android::DurationTimer::addToTimeval(pNextTick, interval); + } + //printf(" Before sleep: now=%ld.%ld next=%ld.%ld sleepTime=%ld\n", + // now.tv_sec, now.tv_usec, pNextTick->tv_sec, pNextTick->tv_usec, + // sleepTime); + + /* + * Sleep for the designated period of time. + * + * Linux tends to sleep for longer than requested, often by 17-18ms. + * MinGW tends to sleep for less than requested, by as much as 14ms, + * but occasionally oversleeps for 40+ms (looks like some external + * factors plus round-off on a 64Hz clock). Cygwin is pretty steady. + * + * If you start the MinGW version, and then launch the Cygwin version, + * the MinGW clock becomes more erratic. Not entirely sure why. + * + * (There's a lot of stuff here; it's really just a usleep() call with + * a bunch of instrumentation.) + */ + if (sleepTime > 0) { +#if defined(MONITOR_USLEEP) + struct timeval before, after; + long long actual; + + gettimeofday(&before, NULL); + usleep((long) sleepTime); + gettimeofday(&after, NULL); + + /* check usleep() accuracy; default Linux threads are pretty sloppy */ + actual = android::DurationTimer::subtractTimevals(&after, &before); + if ((long) actual < sleepTime - 14000 /*(sleepTime/10)*/ || + (long) actual > sleepTime + 20000 /*(sleepTime/10)*/) + { + LOG(LOG_DEBUG, "", " Odd usleep: req=%ld, actual=%ld\n", sleepTime, + (long) actual); + } +#else +#ifdef HAVE_WIN32_THREADS + Sleep( sleepTime/1000 ); +#else + usleep((long) sleepTime); +#endif +#endif + } + + //printf("slept %d\n", sleepTime); + + if (overSlept) + return 1; // close enough + else + return 0; +} + + +/* + * =========================================================================== + * DurationTimer + * =========================================================================== + */ + +using namespace android; + +// Start the timer. +void DurationTimer::start(void) +{ + gettimeofday(&mStartWhen, NULL); +} + +// Stop the timer. +void DurationTimer::stop(void) +{ + gettimeofday(&mStopWhen, NULL); +} + +// Get the duration in microseconds. +long long DurationTimer::durationUsecs(void) const +{ + return (long) subtractTimevals(&mStopWhen, &mStartWhen); +} + +// Subtract two timevals. Returns the difference (ptv1-ptv2) in +// microseconds. +/*static*/ long long DurationTimer::subtractTimevals(const struct timeval* ptv1, + const struct timeval* ptv2) +{ + long long stop = ((long long) ptv1->tv_sec) * 1000000LL + + ((long long) ptv1->tv_usec); + long long start = ((long long) ptv2->tv_sec) * 1000000LL + + ((long long) ptv2->tv_usec); + return stop - start; +} + +// Add the specified amount of time to the timeval. +/*static*/ void DurationTimer::addToTimeval(struct timeval* ptv, long usec) +{ + if (usec < 0) { + LOG(LOG_WARN, "", "Negative values not supported in addToTimeval\n"); + return; + } + + // normalize tv_usec if necessary + if (ptv->tv_usec >= 1000000) { + ptv->tv_sec += ptv->tv_usec / 1000000; + ptv->tv_usec %= 1000000; + } + + ptv->tv_usec += usec % 1000000; + if (ptv->tv_usec >= 1000000) { + ptv->tv_usec -= 1000000; + ptv->tv_sec++; + } + ptv->tv_sec += usec / 1000000; +} + diff --git a/libs/utils/Unicode.cpp b/libs/utils/Unicode.cpp new file mode 100644 index 000000000..33f535fd1 --- /dev/null +++ b/libs/utils/Unicode.cpp @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "utils/AndroidUnicode.h" +#include "characterData.h" + +#define LOG_TAG "Unicode" +#include "utils/Log.h" + +// ICU headers for using macros +#include + +#define MIN_RADIX 2 +#define MAX_RADIX 36 + +#define TYPE_SHIFT 0 +#define TYPE_MASK ((1<<5)-1) + +#define DIRECTION_SHIFT (TYPE_SHIFT+5) +#define DIRECTION_MASK ((1<<5)-1) + +#define MIRRORED_SHIFT (DIRECTION_SHIFT+5) +#define MIRRORED_MASK ((1<<1)-1) + +#define TOUPPER_SHIFT (MIRRORED_SHIFT+1) +#define TOUPPER_MASK ((1<<6)-1) + +#define TOLOWER_SHIFT (TOUPPER_SHIFT+6) +#define TOLOWER_MASK ((1<<6)-1) + +#define TOTITLE_SHIFT (TOLOWER_SHIFT+6) +#define TOTITLE_MASK ((1<<2)-1) + +#define MIRROR_SHIFT (TOTITLE_SHIFT+2) +#define MIRROR_MASK ((1<<5)-1) + +#define NUMERIC_SHIFT (TOTITLE_SHIFT+2) +#define NUMERIC_MASK ((1<<7)-1) + +#define DECOMPOSITION_SHIFT (11) +#define DECOMPOSITION_MASK ((1<<5)-1) + +/* + * Returns the value stored in the CharacterData tables that contains + * an index into the packed data table and the decomposition type. + */ +static uint16_t findCharacterValue(UChar32 c) +{ + LOG_ASSERT(c >= 0 && c <= 0x10FFFF, "findCharacterValue received an invalid codepoint"); + if (c < 256) + return CharacterData::LATIN1_DATA[c]; + + // Rotate the bits because the tables are separated into even and odd codepoints + c = (c >> 1) | ((c & 1) << 20); + + CharacterData::Range search = CharacterData::FULL_DATA[c >> 16]; + const uint32_t* array = search.array; + + // This trick is so that that compare in the while loop does not + // need to shift the array entry down by 16 + c <<= 16; + c |= 0xFFFF; + + int high = (int)search.length - 1; + int low = 0; + + if (high < 0) + return 0; + + while (low < high - 1) + { + int probe = (high + low) >> 1; + + // The entries contain the codepoint in the high 16 bits and the index + // into PACKED_DATA in the low 16. + if (array[probe] > (unsigned)c) + high = probe; + else + low = probe; + } + + LOG_ASSERT((array[low] <= (unsigned)c), "A suitable range was not found"); + return array[low] & 0xFFFF; +} + +uint32_t android::Unicode::getPackedData(UChar32 c) +{ + // findCharacterValue returns a 16-bit value with the top 5 bits containing a decomposition type + // and the remaining bits containing an index. + return CharacterData::PACKED_DATA[findCharacterValue(c) & 0x7FF]; +} + +android::Unicode::CharType android::Unicode::getType(UChar32 c) +{ + if (c < 0 || c >= 0x10FFFF) + return CHARTYPE_UNASSIGNED; + return (CharType)((getPackedData(c) >> TYPE_SHIFT) & TYPE_MASK); +} + +android::Unicode::DecompositionType android::Unicode::getDecompositionType(UChar32 c) +{ + // findCharacterValue returns a 16-bit value with the top 5 bits containing a decomposition type + // and the remaining bits containing an index. + return (DecompositionType)((findCharacterValue(c) >> DECOMPOSITION_SHIFT) & DECOMPOSITION_MASK); +} + +int android::Unicode::getDigitValue(UChar32 c, int radix) +{ + if (radix < MIN_RADIX || radix > MAX_RADIX) + return -1; + + int tempValue = radix; + + if (c >= '0' && c <= '9') + tempValue = c - '0'; + else if (c >= 'a' && c <= 'z') + tempValue = c - 'a' + 10; + else if (c >= 'A' && c <= 'Z') + tempValue = c - 'A' + 10; + + return tempValue < radix ? tempValue : -1; +} + +int android::Unicode::getNumericValue(UChar32 c) +{ + if (isMirrored(c)) + return -1; + + return (int) CharacterData::NUMERICS[((getPackedData(c) >> NUMERIC_SHIFT) & NUMERIC_MASK)]; +} + +UChar32 android::Unicode::toLower(UChar32 c) +{ + return c + CharacterData::LCDIFF[(getPackedData(c) >> TOLOWER_SHIFT) & TOLOWER_MASK]; +} + +UChar32 android::Unicode::toUpper(UChar32 c) +{ + return c + CharacterData::UCDIFF[(getPackedData(c) >> TOUPPER_SHIFT) & TOUPPER_MASK]; +} + +android::Unicode::Direction android::Unicode::getDirectionality(UChar32 c) +{ + uint32_t data = getPackedData(c); + + if (0 == data) + return DIRECTIONALITY_UNDEFINED; + + Direction d = (Direction) ((data >> DIRECTION_SHIFT) & DIRECTION_MASK); + + if (DIRECTION_MASK == d) + return DIRECTIONALITY_UNDEFINED; + + return d; +} + +bool android::Unicode::isMirrored(UChar32 c) +{ + return ((getPackedData(c) >> MIRRORED_SHIFT) & MIRRORED_MASK) != 0; +} + +UChar32 android::Unicode::toMirror(UChar32 c) +{ + if (!isMirrored(c)) + return c; + + return c + CharacterData::MIRROR_DIFF[(getPackedData(c) >> MIRROR_SHIFT) & MIRROR_MASK]; +} + +UChar32 android::Unicode::toTitle(UChar32 c) +{ + int32_t diff = CharacterData::TCDIFF[(getPackedData(c) >> TOTITLE_SHIFT) & TOTITLE_MASK]; + + if (TOTITLE_MASK == diff) + return toUpper(c); + + return c + diff; +} + + diff --git a/libs/utils/VectorImpl.cpp b/libs/utils/VectorImpl.cpp new file mode 100644 index 000000000..2c2d6675c --- /dev/null +++ b/libs/utils/VectorImpl.cpp @@ -0,0 +1,611 @@ +/* + * Copyright (C) 2005 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 "Vector" + +#include +#include +#include + +#include +#include +#include +#include + +/*****************************************************************************/ + + +namespace android { + +// ---------------------------------------------------------------------------- + +const size_t kMinVectorCapacity = 4; + +static inline size_t max(size_t a, size_t b) { + return a>b ? a : b; +} + +// ---------------------------------------------------------------------------- + +VectorImpl::VectorImpl(size_t itemSize, uint32_t flags) + : mStorage(0), mCount(0), mFlags(flags), mItemSize(itemSize) +{ +} + +VectorImpl::VectorImpl(const VectorImpl& rhs) + : mStorage(rhs.mStorage), mCount(rhs.mCount), + mFlags(rhs.mFlags), mItemSize(rhs.mItemSize) +{ + if (mStorage) { + SharedBuffer::sharedBuffer(mStorage)->acquire(); + } +} + +VectorImpl::~VectorImpl() +{ + LOG_ASSERT(!mCount, + "[%p] " + "subclasses of VectorImpl must call finish_vector()" + " in their destructor. Leaking %d bytes.", + this, (int)(mCount*mItemSize)); + // We can't call _do_destroy() here because the vtable is already gone. +} + +VectorImpl& VectorImpl::operator = (const VectorImpl& rhs) +{ + LOG_ASSERT(mItemSize == rhs.mItemSize, + "Vector<> have different types (this=%p, rhs=%p)", this, &rhs); + if (this != &rhs) { + release_storage(); + if (rhs.mCount) { + mStorage = rhs.mStorage; + mCount = rhs.mCount; + SharedBuffer::sharedBuffer(mStorage)->acquire(); + } else { + mStorage = 0; + mCount = 0; + } + } + return *this; +} + +void* VectorImpl::editArrayImpl() +{ + if (mStorage) { + SharedBuffer* sb = SharedBuffer::sharedBuffer(mStorage)->attemptEdit(); + if (sb == 0) { + sb = SharedBuffer::alloc(capacity() * mItemSize); + if (sb) { + _do_copy(sb->data(), mStorage, mCount); + release_storage(); + mStorage = sb->data(); + } + } + } + return mStorage; +} + +size_t VectorImpl::capacity() const +{ + if (mStorage) { + return SharedBuffer::sharedBuffer(mStorage)->size() / mItemSize; + } + return 0; +} + +ssize_t VectorImpl::insertVectorAt(const VectorImpl& vector, size_t index) +{ + if (index > size()) + return BAD_INDEX; + void* where = _grow(index, vector.size()); + if (where) { + _do_copy(where, vector.arrayImpl(), vector.size()); + } + return where ? index : (ssize_t)NO_MEMORY; +} + +ssize_t VectorImpl::appendVector(const VectorImpl& vector) +{ + return insertVectorAt(vector, size()); +} + +ssize_t VectorImpl::insertAt(size_t index, size_t numItems) +{ + return insertAt(0, index, numItems); +} + +ssize_t VectorImpl::insertAt(const void* item, size_t index, size_t numItems) +{ + if (index > size()) + return BAD_INDEX; + void* where = _grow(index, numItems); + if (where) { + if (item) { + _do_splat(where, item, numItems); + } else { + _do_construct(where, numItems); + } + } + return where ? index : (ssize_t)NO_MEMORY; +} + +static int sortProxy(const void* lhs, const void* rhs, void* func) +{ + return (*(VectorImpl::compar_t)func)(lhs, rhs); +} + +status_t VectorImpl::sort(VectorImpl::compar_t cmp) +{ + return sort(sortProxy, (void*)cmp); +} + +status_t VectorImpl::sort(VectorImpl::compar_r_t cmp, void* state) +{ + // the sort must be stable. we're using insertion sort which + // is well suited for small and already sorted arrays + // for big arrays, it could be better to use mergesort + const ssize_t count = size(); + if (count > 1) { + void* array = const_cast(arrayImpl()); + void* temp = 0; + ssize_t i = 1; + while (i < count) { + void* item = reinterpret_cast(array) + mItemSize*(i); + void* curr = reinterpret_cast(array) + mItemSize*(i-1); + if (cmp(curr, item, state) > 0) { + + if (!temp) { + // we're going to have to modify the array... + array = editArrayImpl(); + if (!array) return NO_MEMORY; + temp = malloc(mItemSize); + if (!temp) return NO_MEMORY; + _do_construct(temp, 1); + item = reinterpret_cast(array) + mItemSize*(i); + curr = reinterpret_cast(array) + mItemSize*(i-1); + } + + _do_copy(temp, item, 1); + + ssize_t j = i-1; + void* next = reinterpret_cast(array) + mItemSize*(i); + do { + _do_copy(next, curr, 1); + next = curr; + --j; + curr = reinterpret_cast(array) + mItemSize*(j); + } while (j>=0 && (cmp(curr, temp, state) > 0)); + + _do_copy(next, temp, 1); + } + i++; + } + + if (temp) { + _do_destroy(temp, 1); + free(temp); + } + } + return NO_ERROR; +} + +void VectorImpl::pop() +{ + if (size()) + removeItemsAt(size()-1, 1); +} + +void VectorImpl::push() +{ + push(0); +} + +void VectorImpl::push(const void* item) +{ + insertAt(item, size()); +} + +ssize_t VectorImpl::add() +{ + return add(0); +} + +ssize_t VectorImpl::add(const void* item) +{ + return insertAt(item, size()); +} + +ssize_t VectorImpl::replaceAt(size_t index) +{ + return replaceAt(0, index); +} + +ssize_t VectorImpl::replaceAt(const void* prototype, size_t index) +{ + LOG_ASSERT(index size()) + return BAD_VALUE; + _shrink(index, count); + return index; +} + +void VectorImpl::finish_vector() +{ + release_storage(); + mStorage = 0; + mCount = 0; +} + +void VectorImpl::clear() +{ + _shrink(0, mCount); +} + +void* VectorImpl::editItemLocation(size_t index) +{ + LOG_ASSERT(index(buffer) + index*mItemSize; + return 0; +} + +const void* VectorImpl::itemLocation(size_t index) const +{ + LOG_ASSERT(index(buffer) + index*mItemSize; + return 0; +} + +ssize_t VectorImpl::setCapacity(size_t new_capacity) +{ + size_t current_capacity = capacity(); + ssize_t amount = new_capacity - size(); + if (amount <= 0) { + // we can't reduce the capacity + return current_capacity; + } + SharedBuffer* sb = SharedBuffer::alloc(new_capacity * mItemSize); + if (sb) { + void* array = sb->data(); + _do_copy(array, mStorage, size()); + release_storage(); + mStorage = const_cast(array); + } else { + return NO_MEMORY; + } + return new_capacity; +} + +void VectorImpl::release_storage() +{ + if (mStorage) { + const SharedBuffer* sb = SharedBuffer::sharedBuffer(mStorage); + if (sb->release(SharedBuffer::eKeepStorage) == 1) { + _do_destroy(mStorage, mCount); + SharedBuffer::dealloc(sb); + } + } +} + +void* VectorImpl::_grow(size_t where, size_t amount) +{ +// LOGV("_grow(this=%p, where=%d, amount=%d) count=%d, capacity=%d", +// this, (int)where, (int)amount, (int)mCount, (int)capacity()); + + if (where > mCount) + where = mCount; + + const size_t new_size = mCount + amount; + if (capacity() < new_size) { + const size_t new_capacity = max(kMinVectorCapacity, ((new_size*3)+1)/2); +// LOGV("grow vector %p, new_capacity=%d", this, (int)new_capacity); + if ((mStorage) && + (mCount==where) && + (mFlags & HAS_TRIVIAL_COPY) && + (mFlags & HAS_TRIVIAL_DTOR)) + { + const SharedBuffer* cur_sb = SharedBuffer::sharedBuffer(mStorage); + SharedBuffer* sb = cur_sb->editResize(new_capacity * mItemSize); + mStorage = sb->data(); + } else { + SharedBuffer* sb = SharedBuffer::alloc(new_capacity * mItemSize); + if (sb) { + void* array = sb->data(); + if (where>0) { + _do_copy(array, mStorage, where); + } + if (mCount>where) { + const void* from = reinterpret_cast(mStorage) + where*mItemSize; + void* dest = reinterpret_cast(array) + (where+amount)*mItemSize; + _do_copy(dest, from, mCount-where); + } + release_storage(); + mStorage = const_cast(array); + } + } + } else { + ssize_t s = mCount-where; + if (s>0) { + void* array = editArrayImpl(); + void* to = reinterpret_cast(array) + (where+amount)*mItemSize; + const void* from = reinterpret_cast(array) + where*mItemSize; + _do_move_forward(to, from, s); + } + } + mCount += amount; + void* free_space = const_cast(itemLocation(where)); + return free_space; +} + +void VectorImpl::_shrink(size_t where, size_t amount) +{ + if (!mStorage) + return; + +// LOGV("_shrink(this=%p, where=%d, amount=%d) count=%d, capacity=%d", +// this, (int)where, (int)amount, (int)mCount, (int)capacity()); + + if (where >= mCount) + where = mCount - amount; + + const size_t new_size = mCount - amount; + if (new_size*3 < capacity()) { + const size_t new_capacity = max(kMinVectorCapacity, new_size*2); +// LOGV("shrink vector %p, new_capacity=%d", this, (int)new_capacity); + if ((where == mCount-amount) && + (mFlags & HAS_TRIVIAL_COPY) && + (mFlags & HAS_TRIVIAL_DTOR)) + { + const SharedBuffer* cur_sb = SharedBuffer::sharedBuffer(mStorage); + SharedBuffer* sb = cur_sb->editResize(new_capacity * mItemSize); + mStorage = sb->data(); + } else { + SharedBuffer* sb = SharedBuffer::alloc(new_capacity * mItemSize); + if (sb) { + void* array = sb->data(); + if (where>0) { + _do_copy(array, mStorage, where); + } + if (mCount > where+amount) { + const void* from = reinterpret_cast(mStorage) + (where+amount)*mItemSize; + void* dest = reinterpret_cast(array) + where*mItemSize; + _do_copy(dest, from, mCount-(where+amount)); + } + release_storage(); + mStorage = const_cast(array); + } + } + } else { + void* array = editArrayImpl(); + void* to = reinterpret_cast(array) + where*mItemSize; + _do_destroy(to, amount); + ssize_t s = mCount-(where+amount); + if (s>0) { + const void* from = reinterpret_cast(array) + (where+amount)*mItemSize; + _do_move_backward(to, from, s); + } + } + + // adjust the number of items... + mCount -= amount; +} + +size_t VectorImpl::itemSize() const { + return mItemSize; +} + +void VectorImpl::_do_construct(void* storage, size_t num) const +{ + if (!(mFlags & HAS_TRIVIAL_CTOR)) { + do_construct(storage, num); + } +} + +void VectorImpl::_do_destroy(void* storage, size_t num) const +{ + if (!(mFlags & HAS_TRIVIAL_DTOR)) { + do_destroy(storage, num); + } +} + +void VectorImpl::_do_copy(void* dest, const void* from, size_t num) const +{ + if (!(mFlags & HAS_TRIVIAL_COPY)) { + do_copy(dest, from, num); + } else { + memcpy(dest, from, num*itemSize()); + } +} + +void VectorImpl::_do_splat(void* dest, const void* item, size_t num) const { + do_splat(dest, item, num); +} + +void VectorImpl::_do_move_forward(void* dest, const void* from, size_t num) const { + do_move_forward(dest, from, num); +} + +void VectorImpl::_do_move_backward(void* dest, const void* from, size_t num) const { + do_move_backward(dest, from, num); +} + +void VectorImpl::reservedVectorImpl1() { } +void VectorImpl::reservedVectorImpl2() { } +void VectorImpl::reservedVectorImpl3() { } +void VectorImpl::reservedVectorImpl4() { } +void VectorImpl::reservedVectorImpl5() { } +void VectorImpl::reservedVectorImpl6() { } +void VectorImpl::reservedVectorImpl7() { } +void VectorImpl::reservedVectorImpl8() { } + +/*****************************************************************************/ + +SortedVectorImpl::SortedVectorImpl(size_t itemSize, uint32_t flags) + : VectorImpl(itemSize, flags) +{ +} + +SortedVectorImpl::SortedVectorImpl(const VectorImpl& rhs) +: VectorImpl(rhs) +{ +} + +SortedVectorImpl::~SortedVectorImpl() +{ +} + +SortedVectorImpl& SortedVectorImpl::operator = (const SortedVectorImpl& rhs) +{ + return static_cast( VectorImpl::operator = (static_cast(rhs)) ); +} + +ssize_t SortedVectorImpl::indexOf(const void* item) const +{ + return _indexOrderOf(item); +} + +size_t SortedVectorImpl::orderOf(const void* item) const +{ + size_t o; + _indexOrderOf(item, &o); + return o; +} + +ssize_t SortedVectorImpl::_indexOrderOf(const void* item, size_t* order) const +{ + // binary search + ssize_t err = NAME_NOT_FOUND; + ssize_t l = 0; + ssize_t h = size()-1; + ssize_t mid; + const void* a = arrayImpl(); + const size_t s = itemSize(); + while (l <= h) { + mid = l + (h - l)/2; + const void* const curr = reinterpret_cast(a) + (mid*s); + const int c = do_compare(curr, item); + if (c == 0) { + err = l = mid; + break; + } else if (c < 0) { + l = mid + 1; + } else { + h = mid - 1; + } + } + if (order) *order = l; + return err; +} + +ssize_t SortedVectorImpl::add(const void* item) +{ + size_t order; + ssize_t index = _indexOrderOf(item, &order); + if (index < 0) { + index = VectorImpl::insertAt(item, order, 1); + } else { + index = VectorImpl::replaceAt(item, index); + } + return index; +} + +ssize_t SortedVectorImpl::merge(const VectorImpl& vector) +{ + // naive merge... + if (!vector.isEmpty()) { + const void* buffer = vector.arrayImpl(); + const size_t is = itemSize(); + size_t s = vector.size(); + for (size_t i=0 ; i(buffer) + i*is ); + if (err<0) { + return err; + } + } + } + return NO_ERROR; +} + +ssize_t SortedVectorImpl::merge(const SortedVectorImpl& vector) +{ + // we've merging a sorted vector... nice! + ssize_t err = NO_ERROR; + if (!vector.isEmpty()) { + // first take care of the case where the vectors are sorted together + if (do_compare(vector.itemLocation(vector.size()-1), arrayImpl()) <= 0) { + err = VectorImpl::insertVectorAt(static_cast(vector), 0); + } else if (do_compare(vector.arrayImpl(), itemLocation(size()-1)) >= 0) { + err = VectorImpl::appendVector(static_cast(vector)); + } else { + // this could be made a little better + err = merge(static_cast(vector)); + } + } + return err; +} + +ssize_t SortedVectorImpl::remove(const void* item) +{ + ssize_t i = indexOf(item); + if (i>=0) { + VectorImpl::removeItemsAt(i, 1); + } + return i; +} + +void SortedVectorImpl::reservedSortedVectorImpl1() { }; +void SortedVectorImpl::reservedSortedVectorImpl2() { }; +void SortedVectorImpl::reservedSortedVectorImpl3() { }; +void SortedVectorImpl::reservedSortedVectorImpl4() { }; +void SortedVectorImpl::reservedSortedVectorImpl5() { }; +void SortedVectorImpl::reservedSortedVectorImpl6() { }; +void SortedVectorImpl::reservedSortedVectorImpl7() { }; +void SortedVectorImpl::reservedSortedVectorImpl8() { }; + + +/*****************************************************************************/ + +}; // namespace android + diff --git a/libs/utils/ZipEntry.cpp b/libs/utils/ZipEntry.cpp new file mode 100644 index 000000000..fbc9e6784 --- /dev/null +++ b/libs/utils/ZipEntry.cpp @@ -0,0 +1,696 @@ +/* + * Copyright (C) 2006 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. + */ + +// +// Access to entries in a Zip archive. +// + +#define LOG_TAG "zip" + +#include "utils/ZipEntry.h" +#include "utils/Log.h" + +#include +#include +#include + +using namespace android; + +/* + * Initialize a new ZipEntry structure from a FILE* positioned at a + * CentralDirectoryEntry. + * + * On exit, the file pointer will be at the start of the next CDE or + * at the EOCD. + */ +status_t ZipEntry::initFromCDE(FILE* fp) +{ + status_t result; + long posn; + bool hasDD; + + //LOGV("initFromCDE ---\n"); + + /* read the CDE */ + result = mCDE.read(fp); + if (result != NO_ERROR) { + LOGD("mCDE.read failed\n"); + return result; + } + + //mCDE.dump(); + + /* using the info in the CDE, go load up the LFH */ + posn = ftell(fp); + if (fseek(fp, mCDE.mLocalHeaderRelOffset, SEEK_SET) != 0) { + LOGD("local header seek failed (%ld)\n", + mCDE.mLocalHeaderRelOffset); + return UNKNOWN_ERROR; + } + + result = mLFH.read(fp); + if (result != NO_ERROR) { + LOGD("mLFH.read failed\n"); + return result; + } + + if (fseek(fp, posn, SEEK_SET) != 0) + return UNKNOWN_ERROR; + + //mLFH.dump(); + + /* + * We *might* need to read the Data Descriptor at this point and + * integrate it into the LFH. If this bit is set, the CRC-32, + * compressed size, and uncompressed size will be zero. In practice + * these seem to be rare. + */ + hasDD = (mLFH.mGPBitFlag & kUsesDataDescr) != 0; + if (hasDD) { + // do something clever + //LOGD("+++ has data descriptor\n"); + } + + /* + * Sanity-check the LFH. Note that this will fail if the "kUsesDataDescr" + * flag is set, because the LFH is incomplete. (Not a problem, since we + * prefer the CDE values.) + */ + if (!hasDD && !compareHeaders()) { + LOGW("WARNING: header mismatch\n"); + // keep going? + } + + /* + * If the mVersionToExtract is greater than 20, we may have an + * issue unpacking the record -- could be encrypted, compressed + * with something we don't support, or use Zip64 extensions. We + * can defer worrying about that to when we're extracting data. + */ + + return NO_ERROR; +} + +/* + * Initialize a new entry. Pass in the file name and an optional comment. + * + * Initializes the CDE and the LFH. + */ +void ZipEntry::initNew(const char* fileName, const char* comment) +{ + assert(fileName != NULL && *fileName != '\0'); // name required + + /* most fields are properly initialized by constructor */ + mCDE.mVersionMadeBy = kDefaultMadeBy; + mCDE.mVersionToExtract = kDefaultVersion; + mCDE.mCompressionMethod = kCompressStored; + mCDE.mFileNameLength = strlen(fileName); + if (comment != NULL) + mCDE.mFileCommentLength = strlen(comment); + mCDE.mExternalAttrs = 0x81b60020; // matches what WinZip does + + if (mCDE.mFileNameLength > 0) { + mCDE.mFileName = new unsigned char[mCDE.mFileNameLength+1]; + strcpy((char*) mCDE.mFileName, fileName); + } + if (mCDE.mFileCommentLength > 0) { + /* TODO: stop assuming null-terminated ASCII here? */ + mCDE.mFileComment = new unsigned char[mCDE.mFileCommentLength+1]; + strcpy((char*) mCDE.mFileComment, comment); + } + + copyCDEtoLFH(); +} + +/* + * Initialize a new entry, starting with the ZipEntry from a different + * archive. + * + * Initializes the CDE and the LFH. + */ +status_t ZipEntry::initFromExternal(const ZipFile* pZipFile, + const ZipEntry* pEntry) +{ + /* + * Copy everything in the CDE over, then fix up the hairy bits. + */ + memcpy(&mCDE, &pEntry->mCDE, sizeof(mCDE)); + + if (mCDE.mFileNameLength > 0) { + mCDE.mFileName = new unsigned char[mCDE.mFileNameLength+1]; + if (mCDE.mFileName == NULL) + return NO_MEMORY; + strcpy((char*) mCDE.mFileName, (char*)pEntry->mCDE.mFileName); + } + if (mCDE.mFileCommentLength > 0) { + mCDE.mFileComment = new unsigned char[mCDE.mFileCommentLength+1]; + if (mCDE.mFileComment == NULL) + return NO_MEMORY; + strcpy((char*) mCDE.mFileComment, (char*)pEntry->mCDE.mFileComment); + } + if (mCDE.mExtraFieldLength > 0) { + /* we null-terminate this, though it may not be a string */ + mCDE.mExtraField = new unsigned char[mCDE.mExtraFieldLength+1]; + if (mCDE.mExtraField == NULL) + return NO_MEMORY; + memcpy(mCDE.mExtraField, pEntry->mCDE.mExtraField, + mCDE.mExtraFieldLength+1); + } + + /* construct the LFH from the CDE */ + copyCDEtoLFH(); + + /* + * The LFH "extra" field is independent of the CDE "extra", so we + * handle it here. + */ + assert(mLFH.mExtraField == NULL); + mLFH.mExtraFieldLength = pEntry->mLFH.mExtraFieldLength; + if (mLFH.mExtraFieldLength > 0) { + mLFH.mExtraField = new unsigned char[mLFH.mExtraFieldLength+1]; + if (mLFH.mExtraField == NULL) + return NO_MEMORY; + memcpy(mLFH.mExtraField, pEntry->mLFH.mExtraField, + mLFH.mExtraFieldLength+1); + } + + return NO_ERROR; +} + +/* + * Insert pad bytes in the LFH by tweaking the "extra" field. This will + * potentially confuse something that put "extra" data in here earlier, + * but I can't find an actual problem. + */ +status_t ZipEntry::addPadding(int padding) +{ + if (padding <= 0) + return INVALID_OPERATION; + + //LOGI("HEY: adding %d pad bytes to existing %d in %s\n", + // padding, mLFH.mExtraFieldLength, mCDE.mFileName); + + if (mLFH.mExtraFieldLength > 0) { + /* extend existing field */ + unsigned char* newExtra; + + newExtra = new unsigned char[mLFH.mExtraFieldLength + padding]; + if (newExtra == NULL) + return NO_MEMORY; + memset(newExtra + mLFH.mExtraFieldLength, 0, padding); + memcpy(newExtra, mLFH.mExtraField, mLFH.mExtraFieldLength); + + delete[] mLFH.mExtraField; + mLFH.mExtraField = newExtra; + mLFH.mExtraFieldLength += padding; + } else { + /* create new field */ + mLFH.mExtraField = new unsigned char[padding]; + memset(mLFH.mExtraField, 0, padding); + mLFH.mExtraFieldLength = padding; + } + + return NO_ERROR; +} + +/* + * Set the fields in the LFH equal to the corresponding fields in the CDE. + * + * This does not touch the LFH "extra" field. + */ +void ZipEntry::copyCDEtoLFH(void) +{ + mLFH.mVersionToExtract = mCDE.mVersionToExtract; + mLFH.mGPBitFlag = mCDE.mGPBitFlag; + mLFH.mCompressionMethod = mCDE.mCompressionMethod; + mLFH.mLastModFileTime = mCDE.mLastModFileTime; + mLFH.mLastModFileDate = mCDE.mLastModFileDate; + mLFH.mCRC32 = mCDE.mCRC32; + mLFH.mCompressedSize = mCDE.mCompressedSize; + mLFH.mUncompressedSize = mCDE.mUncompressedSize; + mLFH.mFileNameLength = mCDE.mFileNameLength; + // the "extra field" is independent + + delete[] mLFH.mFileName; + if (mLFH.mFileNameLength > 0) { + mLFH.mFileName = new unsigned char[mLFH.mFileNameLength+1]; + strcpy((char*) mLFH.mFileName, (const char*) mCDE.mFileName); + } else { + mLFH.mFileName = NULL; + } +} + +/* + * Set some information about a file after we add it. + */ +void ZipEntry::setDataInfo(long uncompLen, long compLen, unsigned long crc32, + int compressionMethod) +{ + mCDE.mCompressionMethod = compressionMethod; + mCDE.mCRC32 = crc32; + mCDE.mCompressedSize = compLen; + mCDE.mUncompressedSize = uncompLen; + mCDE.mCompressionMethod = compressionMethod; + if (compressionMethod == kCompressDeflated) { + mCDE.mGPBitFlag |= 0x0002; // indicates maximum compression used + } + copyCDEtoLFH(); +} + +/* + * See if the data in mCDE and mLFH match up. This is mostly useful for + * debugging these classes, but it can be used to identify damaged + * archives. + * + * Returns "false" if they differ. + */ +bool ZipEntry::compareHeaders(void) const +{ + if (mCDE.mVersionToExtract != mLFH.mVersionToExtract) { + LOGV("cmp: VersionToExtract\n"); + return false; + } + if (mCDE.mGPBitFlag != mLFH.mGPBitFlag) { + LOGV("cmp: GPBitFlag\n"); + return false; + } + if (mCDE.mCompressionMethod != mLFH.mCompressionMethod) { + LOGV("cmp: CompressionMethod\n"); + return false; + } + if (mCDE.mLastModFileTime != mLFH.mLastModFileTime) { + LOGV("cmp: LastModFileTime\n"); + return false; + } + if (mCDE.mLastModFileDate != mLFH.mLastModFileDate) { + LOGV("cmp: LastModFileDate\n"); + return false; + } + if (mCDE.mCRC32 != mLFH.mCRC32) { + LOGV("cmp: CRC32\n"); + return false; + } + if (mCDE.mCompressedSize != mLFH.mCompressedSize) { + LOGV("cmp: CompressedSize\n"); + return false; + } + if (mCDE.mUncompressedSize != mLFH.mUncompressedSize) { + LOGV("cmp: UncompressedSize\n"); + return false; + } + if (mCDE.mFileNameLength != mLFH.mFileNameLength) { + LOGV("cmp: FileNameLength\n"); + return false; + } +#if 0 // this seems to be used for padding, not real data + if (mCDE.mExtraFieldLength != mLFH.mExtraFieldLength) { + LOGV("cmp: ExtraFieldLength\n"); + return false; + } +#endif + if (mCDE.mFileName != NULL) { + if (strcmp((char*) mCDE.mFileName, (char*) mLFH.mFileName) != 0) { + LOGV("cmp: FileName\n"); + return false; + } + } + + return true; +} + + +/* + * Convert the DOS date/time stamp into a UNIX time stamp. + */ +time_t ZipEntry::getModWhen(void) const +{ + struct tm parts; + + parts.tm_sec = (mCDE.mLastModFileTime & 0x001f) << 1; + parts.tm_min = (mCDE.mLastModFileTime & 0x07e0) >> 5; + parts.tm_hour = (mCDE.mLastModFileTime & 0xf800) >> 11; + parts.tm_mday = (mCDE.mLastModFileDate & 0x001f); + parts.tm_mon = ((mCDE.mLastModFileDate & 0x01e0) >> 5) -1; + parts.tm_year = ((mCDE.mLastModFileDate & 0xfe00) >> 9) + 80; + parts.tm_wday = parts.tm_yday = 0; + parts.tm_isdst = -1; // DST info "not available" + + return mktime(&parts); +} + +/* + * Set the CDE/LFH timestamp from UNIX time. + */ +void ZipEntry::setModWhen(time_t when) +{ +#ifdef HAVE_LOCALTIME_R + struct tm tmResult; +#endif + time_t even; + unsigned short zdate, ztime; + + struct tm* ptm; + + /* round up to an even number of seconds */ + even = (time_t)(((unsigned long)(when) + 1) & (~1)); + + /* expand */ +#ifdef HAVE_LOCALTIME_R + ptm = localtime_r(&even, &tmResult); +#else + ptm = localtime(&even); +#endif + + int year; + year = ptm->tm_year; + if (year < 80) + year = 80; + + zdate = (year - 80) << 9 | (ptm->tm_mon+1) << 5 | ptm->tm_mday; + ztime = ptm->tm_hour << 11 | ptm->tm_min << 5 | ptm->tm_sec >> 1; + + mCDE.mLastModFileTime = mLFH.mLastModFileTime = ztime; + mCDE.mLastModFileDate = mLFH.mLastModFileDate = zdate; +} + + +/* + * =========================================================================== + * ZipEntry::LocalFileHeader + * =========================================================================== + */ + +/* + * Read a local file header. + * + * On entry, "fp" points to the signature at the start of the header. + * On exit, "fp" points to the start of data. + */ +status_t ZipEntry::LocalFileHeader::read(FILE* fp) +{ + status_t result = NO_ERROR; + unsigned char buf[kLFHLen]; + + assert(mFileName == NULL); + assert(mExtraField == NULL); + + if (fread(buf, 1, kLFHLen, fp) != kLFHLen) { + result = UNKNOWN_ERROR; + goto bail; + } + + if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) { + LOGD("whoops: didn't find expected signature\n"); + result = UNKNOWN_ERROR; + goto bail; + } + + mVersionToExtract = ZipEntry::getShortLE(&buf[0x04]); + mGPBitFlag = ZipEntry::getShortLE(&buf[0x06]); + mCompressionMethod = ZipEntry::getShortLE(&buf[0x08]); + mLastModFileTime = ZipEntry::getShortLE(&buf[0x0a]); + mLastModFileDate = ZipEntry::getShortLE(&buf[0x0c]); + mCRC32 = ZipEntry::getLongLE(&buf[0x0e]); + mCompressedSize = ZipEntry::getLongLE(&buf[0x12]); + mUncompressedSize = ZipEntry::getLongLE(&buf[0x16]); + mFileNameLength = ZipEntry::getShortLE(&buf[0x1a]); + mExtraFieldLength = ZipEntry::getShortLE(&buf[0x1c]); + + // TODO: validate sizes + + /* grab filename */ + if (mFileNameLength != 0) { + mFileName = new unsigned char[mFileNameLength+1]; + if (mFileName == NULL) { + result = NO_MEMORY; + goto bail; + } + if (fread(mFileName, 1, mFileNameLength, fp) != mFileNameLength) { + result = UNKNOWN_ERROR; + goto bail; + } + mFileName[mFileNameLength] = '\0'; + } + + /* grab extra field */ + if (mExtraFieldLength != 0) { + mExtraField = new unsigned char[mExtraFieldLength+1]; + if (mExtraField == NULL) { + result = NO_MEMORY; + goto bail; + } + if (fread(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) { + result = UNKNOWN_ERROR; + goto bail; + } + mExtraField[mExtraFieldLength] = '\0'; + } + +bail: + return result; +} + +/* + * Write a local file header. + */ +status_t ZipEntry::LocalFileHeader::write(FILE* fp) +{ + unsigned char buf[kLFHLen]; + + ZipEntry::putLongLE(&buf[0x00], kSignature); + ZipEntry::putShortLE(&buf[0x04], mVersionToExtract); + ZipEntry::putShortLE(&buf[0x06], mGPBitFlag); + ZipEntry::putShortLE(&buf[0x08], mCompressionMethod); + ZipEntry::putShortLE(&buf[0x0a], mLastModFileTime); + ZipEntry::putShortLE(&buf[0x0c], mLastModFileDate); + ZipEntry::putLongLE(&buf[0x0e], mCRC32); + ZipEntry::putLongLE(&buf[0x12], mCompressedSize); + ZipEntry::putLongLE(&buf[0x16], mUncompressedSize); + ZipEntry::putShortLE(&buf[0x1a], mFileNameLength); + ZipEntry::putShortLE(&buf[0x1c], mExtraFieldLength); + + if (fwrite(buf, 1, kLFHLen, fp) != kLFHLen) + return UNKNOWN_ERROR; + + /* write filename */ + if (mFileNameLength != 0) { + if (fwrite(mFileName, 1, mFileNameLength, fp) != mFileNameLength) + return UNKNOWN_ERROR; + } + + /* write "extra field" */ + if (mExtraFieldLength != 0) { + if (fwrite(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) + return UNKNOWN_ERROR; + } + + return NO_ERROR; +} + + +/* + * Dump the contents of a LocalFileHeader object. + */ +void ZipEntry::LocalFileHeader::dump(void) const +{ + LOGD(" LocalFileHeader contents:\n"); + LOGD(" versToExt=%u gpBits=0x%04x compression=%u\n", + mVersionToExtract, mGPBitFlag, mCompressionMethod); + LOGD(" modTime=0x%04x modDate=0x%04x crc32=0x%08lx\n", + mLastModFileTime, mLastModFileDate, mCRC32); + LOGD(" compressedSize=%lu uncompressedSize=%lu\n", + mCompressedSize, mUncompressedSize); + LOGD(" filenameLen=%u extraLen=%u\n", + mFileNameLength, mExtraFieldLength); + if (mFileName != NULL) + LOGD(" filename: '%s'\n", mFileName); +} + + +/* + * =========================================================================== + * ZipEntry::CentralDirEntry + * =========================================================================== + */ + +/* + * Read the central dir entry that appears next in the file. + * + * On entry, "fp" should be positioned on the signature bytes for the + * entry. On exit, "fp" will point at the signature word for the next + * entry or for the EOCD. + */ +status_t ZipEntry::CentralDirEntry::read(FILE* fp) +{ + status_t result = NO_ERROR; + unsigned char buf[kCDELen]; + + /* no re-use */ + assert(mFileName == NULL); + assert(mExtraField == NULL); + assert(mFileComment == NULL); + + if (fread(buf, 1, kCDELen, fp) != kCDELen) { + result = UNKNOWN_ERROR; + goto bail; + } + + if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) { + LOGD("Whoops: didn't find expected signature\n"); + result = UNKNOWN_ERROR; + goto bail; + } + + mVersionMadeBy = ZipEntry::getShortLE(&buf[0x04]); + mVersionToExtract = ZipEntry::getShortLE(&buf[0x06]); + mGPBitFlag = ZipEntry::getShortLE(&buf[0x08]); + mCompressionMethod = ZipEntry::getShortLE(&buf[0x0a]); + mLastModFileTime = ZipEntry::getShortLE(&buf[0x0c]); + mLastModFileDate = ZipEntry::getShortLE(&buf[0x0e]); + mCRC32 = ZipEntry::getLongLE(&buf[0x10]); + mCompressedSize = ZipEntry::getLongLE(&buf[0x14]); + mUncompressedSize = ZipEntry::getLongLE(&buf[0x18]); + mFileNameLength = ZipEntry::getShortLE(&buf[0x1c]); + mExtraFieldLength = ZipEntry::getShortLE(&buf[0x1e]); + mFileCommentLength = ZipEntry::getShortLE(&buf[0x20]); + mDiskNumberStart = ZipEntry::getShortLE(&buf[0x22]); + mInternalAttrs = ZipEntry::getShortLE(&buf[0x24]); + mExternalAttrs = ZipEntry::getLongLE(&buf[0x26]); + mLocalHeaderRelOffset = ZipEntry::getLongLE(&buf[0x2a]); + + // TODO: validate sizes and offsets + + /* grab filename */ + if (mFileNameLength != 0) { + mFileName = new unsigned char[mFileNameLength+1]; + if (mFileName == NULL) { + result = NO_MEMORY; + goto bail; + } + if (fread(mFileName, 1, mFileNameLength, fp) != mFileNameLength) { + result = UNKNOWN_ERROR; + goto bail; + } + mFileName[mFileNameLength] = '\0'; + } + + /* read "extra field" */ + if (mExtraFieldLength != 0) { + mExtraField = new unsigned char[mExtraFieldLength+1]; + if (mExtraField == NULL) { + result = NO_MEMORY; + goto bail; + } + if (fread(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) { + result = UNKNOWN_ERROR; + goto bail; + } + mExtraField[mExtraFieldLength] = '\0'; + } + + + /* grab comment, if any */ + if (mFileCommentLength != 0) { + mFileComment = new unsigned char[mFileCommentLength+1]; + if (mFileComment == NULL) { + result = NO_MEMORY; + goto bail; + } + if (fread(mFileComment, 1, mFileCommentLength, fp) != mFileCommentLength) + { + result = UNKNOWN_ERROR; + goto bail; + } + mFileComment[mFileCommentLength] = '\0'; + } + +bail: + return result; +} + +/* + * Write a central dir entry. + */ +status_t ZipEntry::CentralDirEntry::write(FILE* fp) +{ + unsigned char buf[kCDELen]; + + ZipEntry::putLongLE(&buf[0x00], kSignature); + ZipEntry::putShortLE(&buf[0x04], mVersionMadeBy); + ZipEntry::putShortLE(&buf[0x06], mVersionToExtract); + ZipEntry::putShortLE(&buf[0x08], mGPBitFlag); + ZipEntry::putShortLE(&buf[0x0a], mCompressionMethod); + ZipEntry::putShortLE(&buf[0x0c], mLastModFileTime); + ZipEntry::putShortLE(&buf[0x0e], mLastModFileDate); + ZipEntry::putLongLE(&buf[0x10], mCRC32); + ZipEntry::putLongLE(&buf[0x14], mCompressedSize); + ZipEntry::putLongLE(&buf[0x18], mUncompressedSize); + ZipEntry::putShortLE(&buf[0x1c], mFileNameLength); + ZipEntry::putShortLE(&buf[0x1e], mExtraFieldLength); + ZipEntry::putShortLE(&buf[0x20], mFileCommentLength); + ZipEntry::putShortLE(&buf[0x22], mDiskNumberStart); + ZipEntry::putShortLE(&buf[0x24], mInternalAttrs); + ZipEntry::putLongLE(&buf[0x26], mExternalAttrs); + ZipEntry::putLongLE(&buf[0x2a], mLocalHeaderRelOffset); + + if (fwrite(buf, 1, kCDELen, fp) != kCDELen) + return UNKNOWN_ERROR; + + /* write filename */ + if (mFileNameLength != 0) { + if (fwrite(mFileName, 1, mFileNameLength, fp) != mFileNameLength) + return UNKNOWN_ERROR; + } + + /* write "extra field" */ + if (mExtraFieldLength != 0) { + if (fwrite(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) + return UNKNOWN_ERROR; + } + + /* write comment */ + if (mFileCommentLength != 0) { + if (fwrite(mFileComment, 1, mFileCommentLength, fp) != mFileCommentLength) + return UNKNOWN_ERROR; + } + + return NO_ERROR; +} + +/* + * Dump the contents of a CentralDirEntry object. + */ +void ZipEntry::CentralDirEntry::dump(void) const +{ + LOGD(" CentralDirEntry contents:\n"); + LOGD(" versMadeBy=%u versToExt=%u gpBits=0x%04x compression=%u\n", + mVersionMadeBy, mVersionToExtract, mGPBitFlag, mCompressionMethod); + LOGD(" modTime=0x%04x modDate=0x%04x crc32=0x%08lx\n", + mLastModFileTime, mLastModFileDate, mCRC32); + LOGD(" compressedSize=%lu uncompressedSize=%lu\n", + mCompressedSize, mUncompressedSize); + LOGD(" filenameLen=%u extraLen=%u commentLen=%u\n", + mFileNameLength, mExtraFieldLength, mFileCommentLength); + LOGD(" diskNumStart=%u intAttr=0x%04x extAttr=0x%08lx relOffset=%lu\n", + mDiskNumberStart, mInternalAttrs, mExternalAttrs, + mLocalHeaderRelOffset); + + if (mFileName != NULL) + LOGD(" filename: '%s'\n", mFileName); + if (mFileComment != NULL) + LOGD(" comment: '%s'\n", mFileComment); +} + diff --git a/libs/utils/ZipFile.cpp b/libs/utils/ZipFile.cpp new file mode 100644 index 000000000..89aa874b4 --- /dev/null +++ b/libs/utils/ZipFile.cpp @@ -0,0 +1,1296 @@ +/* + * Copyright (C) 2006 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. + */ + +// +// Access to Zip archives. +// + +#define LOG_TAG "zip" + +#include "utils/ZipFile.h" +#include "utils/ZipUtils.h" +#include "utils/Log.h" + +#include +#define DEF_MEM_LEVEL 8 // normally in zutil.h? + +#include +#include +#include +#include + +using namespace android; + +/* + * Some environments require the "b", some choke on it. + */ +#define FILE_OPEN_RO "rb" +#define FILE_OPEN_RW "r+b" +#define FILE_OPEN_RW_CREATE "w+b" + +/* should live somewhere else? */ +static status_t errnoToStatus(int err) +{ + if (err == ENOENT) + return NAME_NOT_FOUND; + else if (err == EACCES) + return PERMISSION_DENIED; + else + return UNKNOWN_ERROR; +} + +/* + * Open a file and parse its guts. + */ +status_t ZipFile::open(const char* zipFileName, int flags) +{ + bool newArchive = false; + + assert(mZipFp == NULL); // no reopen + + if ((flags & kOpenTruncate)) + flags |= kOpenCreate; // trunc implies create + + if ((flags & kOpenReadOnly) && (flags & kOpenReadWrite)) + return INVALID_OPERATION; // not both + if (!((flags & kOpenReadOnly) || (flags & kOpenReadWrite))) + return INVALID_OPERATION; // not neither + if ((flags & kOpenCreate) && !(flags & kOpenReadWrite)) + return INVALID_OPERATION; // create requires write + + if (flags & kOpenTruncate) { + newArchive = true; + } else { + newArchive = (access(zipFileName, F_OK) != 0); + if (!(flags & kOpenCreate) && newArchive) { + /* not creating, must already exist */ + LOGD("File %s does not exist", zipFileName); + return NAME_NOT_FOUND; + } + } + + /* open the file */ + const char* openflags; + if (flags & kOpenReadWrite) { + if (newArchive) + openflags = FILE_OPEN_RW_CREATE; + else + openflags = FILE_OPEN_RW; + } else { + openflags = FILE_OPEN_RO; + } + mZipFp = fopen(zipFileName, openflags); + if (mZipFp == NULL) { + int err = errno; + LOGD("fopen failed: %d\n", err); + return errnoToStatus(err); + } + + status_t result; + if (!newArchive) { + /* + * Load the central directory. If that fails, then this probably + * isn't a Zip archive. + */ + result = readCentralDir(); + } else { + /* + * Newly-created. The EndOfCentralDir constructor actually + * sets everything to be the way we want it (all zeroes). We + * set mNeedCDRewrite so that we create *something* if the + * caller doesn't add any files. (We could also just unlink + * the file if it's brand new and nothing was added, but that's + * probably doing more than we really should -- the user might + * have a need for empty zip files.) + */ + mNeedCDRewrite = true; + result = NO_ERROR; + } + + if (flags & kOpenReadOnly) + mReadOnly = true; + else + assert(!mReadOnly); + + return result; +} + +/* + * Return the Nth entry in the archive. + */ +ZipEntry* ZipFile::getEntryByIndex(int idx) const +{ + if (idx < 0 || idx >= (int) mEntries.size()) + return NULL; + + return mEntries[idx]; +} + +/* + * Find an entry by name. + */ +ZipEntry* ZipFile::getEntryByName(const char* fileName) const +{ + /* + * Do a stupid linear string-compare search. + * + * There are various ways to speed this up, especially since it's rare + * to intermingle changes to the archive with "get by name" calls. We + * don't want to sort the mEntries vector itself, however, because + * it's used to recreate the Central Directory. + * + * (Hash table works, parallel list of pointers in sorted order is good.) + */ + int idx; + + for (idx = mEntries.size()-1; idx >= 0; idx--) { + ZipEntry* pEntry = mEntries[idx]; + if (!pEntry->getDeleted() && + strcmp(fileName, pEntry->getFileName()) == 0) + { + return pEntry; + } + } + + return NULL; +} + +/* + * Empty the mEntries vector. + */ +void ZipFile::discardEntries(void) +{ + int count = mEntries.size(); + + while (--count >= 0) + delete mEntries[count]; + + mEntries.clear(); +} + + +/* + * Find the central directory and read the contents. + * + * The fun thing about ZIP archives is that they may or may not be + * readable from start to end. In some cases, notably for archives + * that were written to stdout, the only length information is in the + * central directory at the end of the file. + * + * Of course, the central directory can be followed by a variable-length + * comment field, so we have to scan through it backwards. The comment + * is at most 64K, plus we have 18 bytes for the end-of-central-dir stuff + * itself, plus apparently sometimes people throw random junk on the end + * just for the fun of it. + * + * This is all a little wobbly. If the wrong value ends up in the EOCD + * area, we're hosed. This appears to be the way that everbody handles + * it though, so we're in pretty good company if this fails. + */ +status_t ZipFile::readCentralDir(void) +{ + status_t result = NO_ERROR; + unsigned char* buf = NULL; + off_t fileLength, seekStart; + long readAmount; + int i; + + fseek(mZipFp, 0, SEEK_END); + fileLength = ftell(mZipFp); + rewind(mZipFp); + + /* too small to be a ZIP archive? */ + if (fileLength < EndOfCentralDir::kEOCDLen) { + LOGD("Length is %ld -- too small\n", (long)fileLength); + result = INVALID_OPERATION; + goto bail; + } + + buf = new unsigned char[EndOfCentralDir::kMaxEOCDSearch]; + if (buf == NULL) { + LOGD("Failure allocating %d bytes for EOCD search", + EndOfCentralDir::kMaxEOCDSearch); + result = NO_MEMORY; + goto bail; + } + + if (fileLength > EndOfCentralDir::kMaxEOCDSearch) { + seekStart = fileLength - EndOfCentralDir::kMaxEOCDSearch; + readAmount = EndOfCentralDir::kMaxEOCDSearch; + } else { + seekStart = 0; + readAmount = (long) fileLength; + } + if (fseek(mZipFp, seekStart, SEEK_SET) != 0) { + LOGD("Failure seeking to end of zip at %ld", (long) seekStart); + result = UNKNOWN_ERROR; + goto bail; + } + + /* read the last part of the file into the buffer */ + if (fread(buf, 1, readAmount, mZipFp) != (size_t) readAmount) { + LOGD("short file? wanted %ld\n", readAmount); + result = UNKNOWN_ERROR; + goto bail; + } + + /* find the end-of-central-dir magic */ + for (i = readAmount - 4; i >= 0; i--) { + if (buf[i] == 0x50 && + ZipEntry::getLongLE(&buf[i]) == EndOfCentralDir::kSignature) + { + LOGV("+++ Found EOCD at buf+%d\n", i); + break; + } + } + if (i < 0) { + LOGD("EOCD not found, not Zip\n"); + result = INVALID_OPERATION; + goto bail; + } + + /* extract eocd values */ + result = mEOCD.readBuf(buf + i, readAmount - i); + if (result != NO_ERROR) { + LOGD("Failure reading %ld bytes of EOCD values", readAmount - i); + goto bail; + } + //mEOCD.dump(); + + if (mEOCD.mDiskNumber != 0 || mEOCD.mDiskWithCentralDir != 0 || + mEOCD.mNumEntries != mEOCD.mTotalNumEntries) + { + LOGD("Archive spanning not supported\n"); + result = INVALID_OPERATION; + goto bail; + } + + /* + * So far so good. "mCentralDirSize" is the size in bytes of the + * central directory, so we can just seek back that far to find it. + * We can also seek forward mCentralDirOffset bytes from the + * start of the file. + * + * We're not guaranteed to have the rest of the central dir in the + * buffer, nor are we guaranteed that the central dir will have any + * sort of convenient size. We need to skip to the start of it and + * read the header, then the other goodies. + * + * The only thing we really need right now is the file comment, which + * we're hoping to preserve. + */ + if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) { + LOGD("Failure seeking to central dir offset %ld\n", + mEOCD.mCentralDirOffset); + result = UNKNOWN_ERROR; + goto bail; + } + + /* + * Loop through and read the central dir entries. + */ + LOGV("Scanning %d entries...\n", mEOCD.mTotalNumEntries); + int entry; + for (entry = 0; entry < mEOCD.mTotalNumEntries; entry++) { + ZipEntry* pEntry = new ZipEntry; + + result = pEntry->initFromCDE(mZipFp); + if (result != NO_ERROR) { + LOGD("initFromCDE failed\n"); + delete pEntry; + goto bail; + } + + mEntries.add(pEntry); + } + + + /* + * If all went well, we should now be back at the EOCD. + */ + { + unsigned char checkBuf[4]; + if (fread(checkBuf, 1, 4, mZipFp) != 4) { + LOGD("EOCD check read failed\n"); + result = INVALID_OPERATION; + goto bail; + } + if (ZipEntry::getLongLE(checkBuf) != EndOfCentralDir::kSignature) { + LOGD("EOCD read check failed\n"); + result = UNKNOWN_ERROR; + goto bail; + } + LOGV("+++ EOCD read check passed\n"); + } + +bail: + delete[] buf; + return result; +} + + +/* + * Add a new file to the archive. + * + * This requires creating and populating a ZipEntry structure, and copying + * the data into the file at the appropriate position. The "appropriate + * position" is the current location of the central directory, which we + * casually overwrite (we can put it back later). + * + * If we were concerned about safety, we would want to make all changes + * in a temp file and then overwrite the original after everything was + * safely written. Not really a concern for us. + */ +status_t ZipFile::addCommon(const char* fileName, const void* data, size_t size, + const char* storageName, int sourceType, int compressionMethod, + ZipEntry** ppEntry) +{ + ZipEntry* pEntry = NULL; + status_t result = NO_ERROR; + long lfhPosn, startPosn, endPosn, uncompressedLen; + FILE* inputFp = NULL; + unsigned long crc; + time_t modWhen; + + if (mReadOnly) + return INVALID_OPERATION; + + assert(compressionMethod == ZipEntry::kCompressDeflated || + compressionMethod == ZipEntry::kCompressStored); + + /* make sure we're in a reasonable state */ + assert(mZipFp != NULL); + assert(mEntries.size() == mEOCD.mTotalNumEntries); + + /* make sure it doesn't already exist */ + if (getEntryByName(storageName) != NULL) + return ALREADY_EXISTS; + + if (!data) { + inputFp = fopen(fileName, FILE_OPEN_RO); + if (inputFp == NULL) + return errnoToStatus(errno); + } + + if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) { + result = UNKNOWN_ERROR; + goto bail; + } + + pEntry = new ZipEntry; + pEntry->initNew(storageName, NULL); + + /* + * From here on out, failures are more interesting. + */ + mNeedCDRewrite = true; + + /* + * Write the LFH, even though it's still mostly blank. We need it + * as a place-holder. In theory the LFH isn't necessary, but in + * practice some utilities demand it. + */ + lfhPosn = ftell(mZipFp); + pEntry->mLFH.write(mZipFp); + startPosn = ftell(mZipFp); + + /* + * Copy the data in, possibly compressing it as we go. + */ + if (sourceType == ZipEntry::kCompressStored) { + if (compressionMethod == ZipEntry::kCompressDeflated) { + bool failed = false; + result = compressFpToFp(mZipFp, inputFp, data, size, &crc); + if (result != NO_ERROR) { + LOGD("compression failed, storing\n"); + failed = true; + } else { + /* + * Make sure it has compressed "enough". This probably ought + * to be set through an API call, but I don't expect our + * criteria to change over time. + */ + long src = inputFp ? ftell(inputFp) : size; + long dst = ftell(mZipFp) - startPosn; + if (dst + (dst / 10) > src) { + LOGD("insufficient compression (src=%ld dst=%ld), storing\n", + src, dst); + failed = true; + } + } + + if (failed) { + compressionMethod = ZipEntry::kCompressStored; + if (inputFp) rewind(inputFp); + fseek(mZipFp, startPosn, SEEK_SET); + /* fall through to kCompressStored case */ + } + } + /* handle "no compression" request, or failed compression from above */ + if (compressionMethod == ZipEntry::kCompressStored) { + if (inputFp) { + result = copyFpToFp(mZipFp, inputFp, &crc); + } else { + result = copyDataToFp(mZipFp, data, size, &crc); + } + if (result != NO_ERROR) { + // don't need to truncate; happens in CDE rewrite + LOGD("failed copying data in\n"); + goto bail; + } + } + + // currently seeked to end of file + uncompressedLen = inputFp ? ftell(inputFp) : size; + } else if (sourceType == ZipEntry::kCompressDeflated) { + /* we should support uncompressed-from-compressed, but it's not + * important right now */ + assert(compressionMethod == ZipEntry::kCompressDeflated); + + bool scanResult; + int method; + long compressedLen; + + scanResult = ZipUtils::examineGzip(inputFp, &method, &uncompressedLen, + &compressedLen, &crc); + if (!scanResult || method != ZipEntry::kCompressDeflated) { + LOGD("this isn't a deflated gzip file?"); + result = UNKNOWN_ERROR; + goto bail; + } + + result = copyPartialFpToFp(mZipFp, inputFp, compressedLen, NULL); + if (result != NO_ERROR) { + LOGD("failed copying gzip data in\n"); + goto bail; + } + } else { + assert(false); + result = UNKNOWN_ERROR; + goto bail; + } + + /* + * We could write the "Data Descriptor", but there doesn't seem to + * be any point since we're going to go back and write the LFH. + * + * Update file offsets. + */ + endPosn = ftell(mZipFp); // seeked to end of compressed data + + /* + * Success! Fill out new values. + */ + pEntry->setDataInfo(uncompressedLen, endPosn - startPosn, crc, + compressionMethod); + modWhen = getModTime(inputFp ? fileno(inputFp) : fileno(mZipFp)); + pEntry->setModWhen(modWhen); + pEntry->setLFHOffset(lfhPosn); + mEOCD.mNumEntries++; + mEOCD.mTotalNumEntries++; + mEOCD.mCentralDirSize = 0; // mark invalid; set by flush() + mEOCD.mCentralDirOffset = endPosn; + + /* + * Go back and write the LFH. + */ + if (fseek(mZipFp, lfhPosn, SEEK_SET) != 0) { + result = UNKNOWN_ERROR; + goto bail; + } + pEntry->mLFH.write(mZipFp); + + /* + * Add pEntry to the list. + */ + mEntries.add(pEntry); + if (ppEntry != NULL) + *ppEntry = pEntry; + pEntry = NULL; + +bail: + if (inputFp != NULL) + fclose(inputFp); + delete pEntry; + return result; +} + +/* + * Add an entry by copying it from another zip file. If "padding" is + * nonzero, the specified number of bytes will be added to the "extra" + * field in the header. + * + * If "ppEntry" is non-NULL, a pointer to the new entry will be returned. + */ +status_t ZipFile::add(const ZipFile* pSourceZip, const ZipEntry* pSourceEntry, + int padding, ZipEntry** ppEntry) +{ + ZipEntry* pEntry = NULL; + status_t result; + long lfhPosn, endPosn; + + if (mReadOnly) + return INVALID_OPERATION; + + /* make sure we're in a reasonable state */ + assert(mZipFp != NULL); + assert(mEntries.size() == mEOCD.mTotalNumEntries); + + if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) { + result = UNKNOWN_ERROR; + goto bail; + } + + pEntry = new ZipEntry; + if (pEntry == NULL) { + result = NO_MEMORY; + goto bail; + } + + result = pEntry->initFromExternal(pSourceZip, pSourceEntry); + if (result != NO_ERROR) + goto bail; + if (padding != 0) { + result = pEntry->addPadding(padding); + if (result != NO_ERROR) + goto bail; + } + + /* + * From here on out, failures are more interesting. + */ + mNeedCDRewrite = true; + + /* + * Write the LFH. Since we're not recompressing the data, we already + * have all of the fields filled out. + */ + lfhPosn = ftell(mZipFp); + pEntry->mLFH.write(mZipFp); + + /* + * Copy the data over. + * + * If the "has data descriptor" flag is set, we want to copy the DD + * fields as well. This is a fixed-size area immediately following + * the data. + */ + if (fseek(pSourceZip->mZipFp, pSourceEntry->getFileOffset(), SEEK_SET) != 0) + { + result = UNKNOWN_ERROR; + goto bail; + } + + off_t copyLen; + copyLen = pSourceEntry->getCompressedLen(); + if ((pSourceEntry->mLFH.mGPBitFlag & ZipEntry::kUsesDataDescr) != 0) + copyLen += ZipEntry::kDataDescriptorLen; + + if (copyPartialFpToFp(mZipFp, pSourceZip->mZipFp, copyLen, NULL) + != NO_ERROR) + { + LOGW("copy of '%s' failed\n", pEntry->mCDE.mFileName); + result = UNKNOWN_ERROR; + goto bail; + } + + /* + * Update file offsets. + */ + endPosn = ftell(mZipFp); + + /* + * Success! Fill out new values. + */ + pEntry->setLFHOffset(lfhPosn); // sets mCDE.mLocalHeaderRelOffset + mEOCD.mNumEntries++; + mEOCD.mTotalNumEntries++; + mEOCD.mCentralDirSize = 0; // mark invalid; set by flush() + mEOCD.mCentralDirOffset = endPosn; + + /* + * Add pEntry to the list. + */ + mEntries.add(pEntry); + if (ppEntry != NULL) + *ppEntry = pEntry; + pEntry = NULL; + + result = NO_ERROR; + +bail: + delete pEntry; + return result; +} + +/* + * Copy all of the bytes in "src" to "dst". + * + * On exit, "srcFp" will be seeked to the end of the file, and "dstFp" + * will be seeked immediately past the data. + */ +status_t ZipFile::copyFpToFp(FILE* dstFp, FILE* srcFp, unsigned long* pCRC32) +{ + unsigned char tmpBuf[32768]; + size_t count; + + *pCRC32 = crc32(0L, Z_NULL, 0); + + while (1) { + count = fread(tmpBuf, 1, sizeof(tmpBuf), srcFp); + if (ferror(srcFp) || ferror(dstFp)) + return errnoToStatus(errno); + if (count == 0) + break; + + *pCRC32 = crc32(*pCRC32, tmpBuf, count); + + if (fwrite(tmpBuf, 1, count, dstFp) != count) { + LOGD("fwrite %d bytes failed\n", (int) count); + return UNKNOWN_ERROR; + } + } + + return NO_ERROR; +} + +/* + * Copy all of the bytes in "src" to "dst". + * + * On exit, "dstFp" will be seeked immediately past the data. + */ +status_t ZipFile::copyDataToFp(FILE* dstFp, + const void* data, size_t size, unsigned long* pCRC32) +{ + size_t count; + + *pCRC32 = crc32(0L, Z_NULL, 0); + if (size > 0) { + *pCRC32 = crc32(*pCRC32, (const unsigned char*)data, size); + if (fwrite(data, 1, size, dstFp) != size) { + LOGD("fwrite %d bytes failed\n", (int) size); + return UNKNOWN_ERROR; + } + } + + return NO_ERROR; +} + +/* + * Copy some of the bytes in "src" to "dst". + * + * If "pCRC32" is NULL, the CRC will not be computed. + * + * On exit, "srcFp" will be seeked to the end of the file, and "dstFp" + * will be seeked immediately past the data just written. + */ +status_t ZipFile::copyPartialFpToFp(FILE* dstFp, FILE* srcFp, long length, + unsigned long* pCRC32) +{ + unsigned char tmpBuf[32768]; + size_t count; + + if (pCRC32 != NULL) + *pCRC32 = crc32(0L, Z_NULL, 0); + + while (length) { + long readSize; + + readSize = sizeof(tmpBuf); + if (readSize > length) + readSize = length; + + count = fread(tmpBuf, 1, readSize, srcFp); + if ((long) count != readSize) { // error or unexpected EOF + LOGD("fread %d bytes failed\n", (int) readSize); + return UNKNOWN_ERROR; + } + + if (pCRC32 != NULL) + *pCRC32 = crc32(*pCRC32, tmpBuf, count); + + if (fwrite(tmpBuf, 1, count, dstFp) != count) { + LOGD("fwrite %d bytes failed\n", (int) count); + return UNKNOWN_ERROR; + } + + length -= readSize; + } + + return NO_ERROR; +} + +/* + * Compress all of the data in "srcFp" and write it to "dstFp". + * + * On exit, "srcFp" will be seeked to the end of the file, and "dstFp" + * will be seeked immediately past the compressed data. + */ +status_t ZipFile::compressFpToFp(FILE* dstFp, FILE* srcFp, + const void* data, size_t size, unsigned long* pCRC32) +{ + status_t result = NO_ERROR; + const size_t kBufSize = 32768; + unsigned char* inBuf = NULL; + unsigned char* outBuf = NULL; + z_stream zstream; + bool atEof = false; // no feof() aviailable yet + unsigned long crc; + int zerr; + + /* + * Create an input buffer and an output buffer. + */ + inBuf = new unsigned char[kBufSize]; + outBuf = new unsigned char[kBufSize]; + if (inBuf == NULL || outBuf == NULL) { + result = NO_MEMORY; + goto bail; + } + + /* + * Initialize the zlib stream. + */ + memset(&zstream, 0, sizeof(zstream)); + zstream.zalloc = Z_NULL; + zstream.zfree = Z_NULL; + zstream.opaque = Z_NULL; + zstream.next_in = NULL; + zstream.avail_in = 0; + zstream.next_out = outBuf; + zstream.avail_out = kBufSize; + zstream.data_type = Z_UNKNOWN; + + zerr = deflateInit2(&zstream, Z_BEST_COMPRESSION, + Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY); + if (zerr != Z_OK) { + result = UNKNOWN_ERROR; + if (zerr == Z_VERSION_ERROR) { + LOGE("Installed zlib is not compatible with linked version (%s)\n", + ZLIB_VERSION); + } else { + LOGD("Call to deflateInit2 failed (zerr=%d)\n", zerr); + } + goto bail; + } + + crc = crc32(0L, Z_NULL, 0); + + /* + * Loop while we have data. + */ + do { + size_t getSize; + int flush; + + /* only read if the input buffer is empty */ + if (zstream.avail_in == 0 && !atEof) { + LOGV("+++ reading %d bytes\n", (int)kBufSize); + if (data) { + getSize = size > kBufSize ? kBufSize : size; + memcpy(inBuf, data, getSize); + data = ((const char*)data) + getSize; + size -= getSize; + } else { + getSize = fread(inBuf, 1, kBufSize, srcFp); + if (ferror(srcFp)) { + LOGD("deflate read failed (errno=%d)\n", errno); + goto z_bail; + } + } + if (getSize < kBufSize) { + LOGV("+++ got %d bytes, EOF reached\n", + (int)getSize); + atEof = true; + } + + crc = crc32(crc, inBuf, getSize); + + zstream.next_in = inBuf; + zstream.avail_in = getSize; + } + + if (atEof) + flush = Z_FINISH; /* tell zlib that we're done */ + else + flush = Z_NO_FLUSH; /* more to come! */ + + zerr = deflate(&zstream, flush); + if (zerr != Z_OK && zerr != Z_STREAM_END) { + LOGD("zlib deflate call failed (zerr=%d)\n", zerr); + result = UNKNOWN_ERROR; + goto z_bail; + } + + /* write when we're full or when we're done */ + if (zstream.avail_out == 0 || + (zerr == Z_STREAM_END && zstream.avail_out != (uInt) kBufSize)) + { + LOGV("+++ writing %d bytes\n", (int) (zstream.next_out - outBuf)); + if (fwrite(outBuf, 1, zstream.next_out - outBuf, dstFp) != + (size_t)(zstream.next_out - outBuf)) + { + LOGD("write %d failed in deflate\n", + (int) (zstream.next_out - outBuf)); + goto z_bail; + } + + zstream.next_out = outBuf; + zstream.avail_out = kBufSize; + } + } while (zerr == Z_OK); + + assert(zerr == Z_STREAM_END); /* other errors should've been caught */ + + *pCRC32 = crc; + +z_bail: + deflateEnd(&zstream); /* free up any allocated structures */ + +bail: + delete[] inBuf; + delete[] outBuf; + + return result; +} + +/* + * Mark an entry as deleted. + * + * We will eventually need to crunch the file down, but if several files + * are being removed (perhaps as part of an "update" process) we can make + * things considerably faster by deferring the removal to "flush" time. + */ +status_t ZipFile::remove(ZipEntry* pEntry) +{ + /* + * Should verify that pEntry is actually part of this archive, and + * not some stray ZipEntry from a different file. + */ + + /* mark entry as deleted, and mark archive as dirty */ + pEntry->setDeleted(); + mNeedCDRewrite = true; + return NO_ERROR; +} + +/* + * Flush any pending writes. + * + * In particular, this will crunch out deleted entries, and write the + * Central Directory and EOCD if we have stomped on them. + */ +status_t ZipFile::flush(void) +{ + status_t result = NO_ERROR; + long eocdPosn; + int i, count; + + if (mReadOnly) + return INVALID_OPERATION; + if (!mNeedCDRewrite) + return NO_ERROR; + + assert(mZipFp != NULL); + + result = crunchArchive(); + if (result != NO_ERROR) + return result; + + if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) + return UNKNOWN_ERROR; + + count = mEntries.size(); + for (i = 0; i < count; i++) { + ZipEntry* pEntry = mEntries[i]; + pEntry->mCDE.write(mZipFp); + } + + eocdPosn = ftell(mZipFp); + mEOCD.mCentralDirSize = eocdPosn - mEOCD.mCentralDirOffset; + + mEOCD.write(mZipFp); + + /* + * If we had some stuff bloat up during compression and get replaced + * with plain files, or if we deleted some entries, there's a lot + * of wasted space at the end of the file. Remove it now. + */ + if (ftruncate(fileno(mZipFp), ftell(mZipFp)) != 0) { + LOGW("ftruncate failed %ld: %s\n", ftell(mZipFp), strerror(errno)); + // not fatal + } + + /* should we clear the "newly added" flag in all entries now? */ + + mNeedCDRewrite = false; + return NO_ERROR; +} + +/* + * Crunch deleted files out of an archive by shifting the later files down. + * + * Because we're not using a temp file, we do the operation inside the + * current file. + */ +status_t ZipFile::crunchArchive(void) +{ + status_t result = NO_ERROR; + int i, count; + long delCount, adjust; + +#if 0 + printf("CONTENTS:\n"); + for (i = 0; i < (int) mEntries.size(); i++) { + printf(" %d: lfhOff=%ld del=%d\n", + i, mEntries[i]->getLFHOffset(), mEntries[i]->getDeleted()); + } + printf(" END is %ld\n", (long) mEOCD.mCentralDirOffset); +#endif + + /* + * Roll through the set of files, shifting them as appropriate. We + * could probably get a slight performance improvement by sliding + * multiple files down at once (because we could use larger reads + * when operating on batches of small files), but it's not that useful. + */ + count = mEntries.size(); + delCount = adjust = 0; + for (i = 0; i < count; i++) { + ZipEntry* pEntry = mEntries[i]; + long span; + + if (pEntry->getLFHOffset() != 0) { + long nextOffset; + + /* Get the length of this entry by finding the offset + * of the next entry. Directory entries don't have + * file offsets, so we need to find the next non-directory + * entry. + */ + nextOffset = 0; + for (int ii = i+1; nextOffset == 0 && ii < count; ii++) + nextOffset = mEntries[ii]->getLFHOffset(); + if (nextOffset == 0) + nextOffset = mEOCD.mCentralDirOffset; + span = nextOffset - pEntry->getLFHOffset(); + + assert(span >= ZipEntry::LocalFileHeader::kLFHLen); + } else { + /* This is a directory entry. It doesn't have + * any actual file contents, so there's no need to + * move anything. + */ + span = 0; + } + + //printf("+++ %d: off=%ld span=%ld del=%d [count=%d]\n", + // i, pEntry->getLFHOffset(), span, pEntry->getDeleted(), count); + + if (pEntry->getDeleted()) { + adjust += span; + delCount++; + + delete pEntry; + mEntries.removeAt(i); + + /* adjust loop control */ + count--; + i--; + } else if (span != 0 && adjust > 0) { + /* shuffle this entry back */ + //printf("+++ Shuffling '%s' back %ld\n", + // pEntry->getFileName(), adjust); + result = filemove(mZipFp, pEntry->getLFHOffset() - adjust, + pEntry->getLFHOffset(), span); + if (result != NO_ERROR) { + /* this is why you use a temp file */ + LOGE("error during crunch - archive is toast\n"); + return result; + } + + pEntry->setLFHOffset(pEntry->getLFHOffset() - adjust); + } + } + + /* + * Fix EOCD info. We have to wait until the end to do some of this + * because we use mCentralDirOffset to determine "span" for the + * last entry. + */ + mEOCD.mCentralDirOffset -= adjust; + mEOCD.mNumEntries -= delCount; + mEOCD.mTotalNumEntries -= delCount; + mEOCD.mCentralDirSize = 0; // mark invalid; set by flush() + + assert(mEOCD.mNumEntries == mEOCD.mTotalNumEntries); + assert(mEOCD.mNumEntries == count); + + return result; +} + +/* + * Works like memmove(), but on pieces of a file. + */ +status_t ZipFile::filemove(FILE* fp, off_t dst, off_t src, size_t n) +{ + if (dst == src || n <= 0) + return NO_ERROR; + + unsigned char readBuf[32768]; + + if (dst < src) { + /* shift stuff toward start of file; must read from start */ + while (n != 0) { + size_t getSize = sizeof(readBuf); + if (getSize > n) + getSize = n; + + if (fseek(fp, (long) src, SEEK_SET) != 0) { + LOGD("filemove src seek %ld failed\n", (long) src); + return UNKNOWN_ERROR; + } + + if (fread(readBuf, 1, getSize, fp) != getSize) { + LOGD("filemove read %ld off=%ld failed\n", + (long) getSize, (long) src); + return UNKNOWN_ERROR; + } + + if (fseek(fp, (long) dst, SEEK_SET) != 0) { + LOGD("filemove dst seek %ld failed\n", (long) dst); + return UNKNOWN_ERROR; + } + + if (fwrite(readBuf, 1, getSize, fp) != getSize) { + LOGD("filemove write %ld off=%ld failed\n", + (long) getSize, (long) dst); + return UNKNOWN_ERROR; + } + + src += getSize; + dst += getSize; + n -= getSize; + } + } else { + /* shift stuff toward end of file; must read from end */ + assert(false); // write this someday, maybe + return UNKNOWN_ERROR; + } + + return NO_ERROR; +} + + +/* + * Get the modification time from a file descriptor. + */ +time_t ZipFile::getModTime(int fd) +{ + struct stat sb; + + if (fstat(fd, &sb) < 0) { + LOGD("HEY: fstat on fd %d failed\n", fd); + return (time_t) -1; + } + + return sb.st_mtime; +} + + +#if 0 /* this is a bad idea */ +/* + * Get a copy of the Zip file descriptor. + * + * We don't allow this if the file was opened read-write because we tend + * to leave the file contents in an uncertain state between calls to + * flush(). The duplicated file descriptor should only be valid for reads. + */ +int ZipFile::getZipFd(void) const +{ + if (!mReadOnly) + return INVALID_OPERATION; + assert(mZipFp != NULL); + + int fd; + fd = dup(fileno(mZipFp)); + if (fd < 0) { + LOGD("didn't work, errno=%d\n", errno); + } + + return fd; +} +#endif + + +#if 0 +/* + * Expand data. + */ +bool ZipFile::uncompress(const ZipEntry* pEntry, void* buf) const +{ + return false; +} +#endif + +// free the memory when you're done +void* ZipFile::uncompress(const ZipEntry* entry) +{ + size_t unlen = entry->getUncompressedLen(); + size_t clen = entry->getCompressedLen(); + + void* buf = malloc(unlen); + if (buf == NULL) { + return NULL; + } + + fseek(mZipFp, 0, SEEK_SET); + + off_t offset = entry->getFileOffset(); + if (fseek(mZipFp, offset, SEEK_SET) != 0) { + goto bail; + } + + switch (entry->getCompressionMethod()) + { + case ZipEntry::kCompressStored: { + ssize_t amt = fread(buf, 1, unlen, mZipFp); + if (amt != (ssize_t)unlen) { + goto bail; + } +#if 0 + printf("data...\n"); + const unsigned char* p = (unsigned char*)buf; + const unsigned char* end = p+unlen; + for (int i=0; i<32 && p < end; i++) { + printf("0x%08x ", (int)(offset+(i*0x10))); + for (int j=0; j<0x10 && p < end; j++) { + printf(" %02x", *p); + p++; + } + printf("\n"); + } +#endif + + } + break; + case ZipEntry::kCompressDeflated: { + if (!ZipUtils::inflateToBuffer(mZipFp, buf, unlen, clen)) { + goto bail; + } + } + break; + default: + goto bail; + } + return buf; + +bail: + free(buf); + return NULL; +} + + +/* + * =========================================================================== + * ZipFile::EndOfCentralDir + * =========================================================================== + */ + +/* + * Read the end-of-central-dir fields. + * + * "buf" should be positioned at the EOCD signature, and should contain + * the entire EOCD area including the comment. + */ +status_t ZipFile::EndOfCentralDir::readBuf(const unsigned char* buf, int len) +{ + /* don't allow re-use */ + assert(mComment == NULL); + + if (len < kEOCDLen) { + /* looks like ZIP file got truncated */ + LOGD(" Zip EOCD: expected >= %d bytes, found %d\n", + kEOCDLen, len); + return INVALID_OPERATION; + } + + /* this should probably be an assert() */ + if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) + return UNKNOWN_ERROR; + + mDiskNumber = ZipEntry::getShortLE(&buf[0x04]); + mDiskWithCentralDir = ZipEntry::getShortLE(&buf[0x06]); + mNumEntries = ZipEntry::getShortLE(&buf[0x08]); + mTotalNumEntries = ZipEntry::getShortLE(&buf[0x0a]); + mCentralDirSize = ZipEntry::getLongLE(&buf[0x0c]); + mCentralDirOffset = ZipEntry::getLongLE(&buf[0x10]); + mCommentLen = ZipEntry::getShortLE(&buf[0x14]); + + // TODO: validate mCentralDirOffset + + if (mCommentLen > 0) { + if (kEOCDLen + mCommentLen > len) { + LOGD("EOCD(%d) + comment(%d) exceeds len (%d)\n", + kEOCDLen, mCommentLen, len); + return UNKNOWN_ERROR; + } + mComment = new unsigned char[mCommentLen]; + memcpy(mComment, buf + kEOCDLen, mCommentLen); + } + + return NO_ERROR; +} + +/* + * Write an end-of-central-directory section. + */ +status_t ZipFile::EndOfCentralDir::write(FILE* fp) +{ + unsigned char buf[kEOCDLen]; + + ZipEntry::putLongLE(&buf[0x00], kSignature); + ZipEntry::putShortLE(&buf[0x04], mDiskNumber); + ZipEntry::putShortLE(&buf[0x06], mDiskWithCentralDir); + ZipEntry::putShortLE(&buf[0x08], mNumEntries); + ZipEntry::putShortLE(&buf[0x0a], mTotalNumEntries); + ZipEntry::putLongLE(&buf[0x0c], mCentralDirSize); + ZipEntry::putLongLE(&buf[0x10], mCentralDirOffset); + ZipEntry::putShortLE(&buf[0x14], mCommentLen); + + if (fwrite(buf, 1, kEOCDLen, fp) != kEOCDLen) + return UNKNOWN_ERROR; + if (mCommentLen > 0) { + assert(mComment != NULL); + if (fwrite(mComment, mCommentLen, 1, fp) != mCommentLen) + return UNKNOWN_ERROR; + } + + return NO_ERROR; +} + +/* + * Dump the contents of an EndOfCentralDir object. + */ +void ZipFile::EndOfCentralDir::dump(void) const +{ + LOGD(" EndOfCentralDir contents:\n"); + LOGD(" diskNum=%u diskWCD=%u numEnt=%u totalNumEnt=%u\n", + mDiskNumber, mDiskWithCentralDir, mNumEntries, mTotalNumEntries); + LOGD(" centDirSize=%lu centDirOff=%lu commentLen=%u\n", + mCentralDirSize, mCentralDirOffset, mCommentLen); +} + diff --git a/libs/utils/ZipFileCRO.cpp b/libs/utils/ZipFileCRO.cpp new file mode 100644 index 000000000..d312dafad --- /dev/null +++ b/libs/utils/ZipFileCRO.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "utils/ZipFileCRO.h" +#include "utils/ZipFileRO.h" + +using namespace android; + +ZipFileCRO ZipFileXRO_open(const char* path) { + ZipFileRO* zip = new ZipFileRO(); + if (zip->open(path) == NO_ERROR) { + return (ZipFileCRO)zip; + } + return NULL; +} + +void ZipFileCRO_destroy(ZipFileCRO zipToken) { + ZipFileRO* zip = (ZipFileRO*)zipToken; + delete zip; +} + +ZipEntryCRO ZipFileCRO_findEntryByName(ZipFileCRO zipToken, + const char* fileName) { + ZipFileRO* zip = (ZipFileRO*)zipToken; + return (ZipEntryCRO)zip->findEntryByName(fileName); +} + +bool ZipFileCRO_getEntryInfo(ZipFileCRO zipToken, ZipEntryRO entryToken, + int* pMethod, long* pUncompLen, + long* pCompLen, off_t* pOffset, long* pModWhen, long* pCrc32) { + ZipFileRO* zip = (ZipFileRO*)zipToken; + ZipEntryRO entry = (ZipEntryRO)entryToken; + return zip->getEntryInfo(entry, pMethod, pUncompLen, pCompLen, pOffset, + pModWhen, pCrc32); +} + +bool ZipFileCRO_uncompressEntry(ZipFileCRO zipToken, ZipEntryRO entryToken, int fd) { + ZipFileRO* zip = (ZipFileRO*)zipToken; + ZipEntryRO entry = (ZipEntryRO)entryToken; + return zip->uncompressEntry(entry, fd); +} diff --git a/libs/utils/ZipFileRO.cpp b/libs/utils/ZipFileRO.cpp new file mode 100644 index 000000000..ae8c71972 --- /dev/null +++ b/libs/utils/ZipFileRO.cpp @@ -0,0 +1,724 @@ +/* + * 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. + */ + +// +// Read-only access to Zip archives, with minimal heap allocation. +// +#define LOG_TAG "zipro" +//#define LOG_NDEBUG 0 +#include "utils/ZipFileRO.h" +#include "utils/Log.h" +#include "utils/misc.h" + +#include + +#include +#include +#include +#include + +using namespace android; + +/* + * Zip file constants. + */ +#define kEOCDSignature 0x06054b50 +#define kEOCDLen 22 +#define kEOCDNumEntries 8 // offset to #of entries in file +#define kEOCDFileOffset 16 // offset to central directory + +#define kMaxCommentLen 65535 // longest possible in ushort +#define kMaxEOCDSearch (kMaxCommentLen + kEOCDLen) + +#define kLFHSignature 0x04034b50 +#define kLFHLen 30 // excluding variable-len fields +#define kLFHNameLen 26 // offset to filename length +#define kLFHExtraLen 28 // offset to extra length + +#define kCDESignature 0x02014b50 +#define kCDELen 46 // excluding variable-len fields +#define kCDEMethod 10 // offset to compression method +#define kCDEModWhen 12 // offset to modification timestamp +#define kCDECRC 16 // offset to entry CRC +#define kCDECompLen 20 // offset to compressed length +#define kCDEUncompLen 24 // offset to uncompressed length +#define kCDENameLen 28 // offset to filename length +#define kCDEExtraLen 30 // offset to extra length +#define kCDECommentLen 32 // offset to comment length +#define kCDELocalOffset 42 // offset to local hdr + +/* + * The values we return for ZipEntryRO use 0 as an invalid value, so we + * want to adjust the hash table index by a fixed amount. Using a large + * value helps insure that people don't mix & match arguments, e.g. to + * findEntryByIndex(). + */ +#define kZipEntryAdj 10000 + +/* + * Convert a ZipEntryRO to a hash table index, verifying that it's in a + * valid range. + */ +int ZipFileRO::entryToIndex(const ZipEntryRO entry) const +{ + long ent = ((long) entry) - kZipEntryAdj; + if (ent < 0 || ent >= mHashTableSize || mHashTable[ent].name == NULL) { + LOGW("Invalid ZipEntryRO %p (%ld)\n", entry, ent); + return -1; + } + return ent; +} + + +/* + * Open the specified file read-only. We memory-map the entire thing and + * close the file before returning. + */ +status_t ZipFileRO::open(const char* zipFileName) +{ + int fd = -1; + off_t length; + + assert(mFileMap == NULL); + + /* + * Open and map the specified file. + */ + fd = ::open(zipFileName, O_RDONLY); + if (fd < 0) { + LOGW("Unable to open zip '%s': %s\n", zipFileName, strerror(errno)); + return NAME_NOT_FOUND; + } + + length = lseek(fd, 0, SEEK_END); + if (length < 0) { + close(fd); + return UNKNOWN_ERROR; + } + + mFileMap = new FileMap(); + if (mFileMap == NULL) { + close(fd); + return NO_MEMORY; + } + if (!mFileMap->create(zipFileName, fd, 0, length, true)) { + LOGW("Unable to map '%s': %s\n", zipFileName, strerror(errno)); + close(fd); + return UNKNOWN_ERROR; + } + + mFd = fd; + + /* + * Got it mapped, verify it and create data structures for fast access. + */ + if (!parseZipArchive()) { + mFileMap->release(); + mFileMap = NULL; + return UNKNOWN_ERROR; + } + + return OK; +} + +/* + * Parse the Zip archive, verifying its contents and initializing internal + * data structures. + */ +bool ZipFileRO::parseZipArchive(void) +{ +#define CHECK_OFFSET(_off) { \ + if ((unsigned int) (_off) >= maxOffset) { \ + LOGE("ERROR: bad offset %u (max %d): %s\n", \ + (unsigned int) (_off), maxOffset, #_off); \ + goto bail; \ + } \ + } + const unsigned char* basePtr = (const unsigned char*)mFileMap->getDataPtr(); + const unsigned char* ptr; + size_t length = mFileMap->getDataLength(); + bool result = false; + unsigned int i, numEntries, cdOffset; + unsigned int val; + + /* + * The first 4 bytes of the file will either be the local header + * signature for the first file (kLFHSignature) or, if the archive doesn't + * have any files in it, the end-of-central-directory signature + * (kEOCDSignature). + */ + val = get4LE(basePtr); + if (val == kEOCDSignature) { + LOGI("Found Zip archive, but it looks empty\n"); + goto bail; + } else if (val != kLFHSignature) { + LOGV("Not a Zip archive (found 0x%08x)\n", val); + goto bail; + } + + /* + * Find the EOCD. We'll find it immediately unless they have a file + * comment. + */ + ptr = basePtr + length - kEOCDLen; + + while (ptr >= basePtr) { + if (*ptr == (kEOCDSignature & 0xff) && get4LE(ptr) == kEOCDSignature) + break; + ptr--; + } + if (ptr < basePtr) { + LOGI("Could not find end-of-central-directory in Zip\n"); + goto bail; + } + + /* + * There are two interesting items in the EOCD block: the number of + * entries in the file, and the file offset of the start of the + * central directory. + * + * (There's actually a count of the #of entries in this file, and for + * all files which comprise a spanned archive, but for our purposes + * we're only interested in the current file. Besides, we expect the + * two to be equivalent for our stuff.) + */ + numEntries = get2LE(ptr + kEOCDNumEntries); + cdOffset = get4LE(ptr + kEOCDFileOffset); + + /* valid offsets are [0,EOCD] */ + unsigned int maxOffset; + maxOffset = (ptr - basePtr) +1; + + LOGV("+++ numEntries=%d cdOffset=%d\n", numEntries, cdOffset); + if (numEntries == 0 || cdOffset >= length) { + LOGW("Invalid entries=%d offset=%d (len=%zd)\n", + numEntries, cdOffset, length); + goto bail; + } + + /* + * Create hash table. We have a minimum 75% load factor, possibly as + * low as 50% after we round off to a power of 2. + */ + mNumEntries = numEntries; + mHashTableSize = roundUpPower2(1 + ((numEntries * 4) / 3)); + mHashTable = (HashEntry*) calloc(1, sizeof(HashEntry) * mHashTableSize); + + /* + * Walk through the central directory, adding entries to the hash + * table. + */ + ptr = basePtr + cdOffset; + for (i = 0; i < numEntries; i++) { + unsigned int fileNameLen, extraLen, commentLen, localHdrOffset; + const unsigned char* localHdr; + unsigned int hash; + + if (get4LE(ptr) != kCDESignature) { + LOGW("Missed a central dir sig (at %d)\n", i); + goto bail; + } + if (ptr + kCDELen > basePtr + length) { + LOGW("Ran off the end (at %d)\n", i); + goto bail; + } + + localHdrOffset = get4LE(ptr + kCDELocalOffset); + CHECK_OFFSET(localHdrOffset); + fileNameLen = get2LE(ptr + kCDENameLen); + extraLen = get2LE(ptr + kCDEExtraLen); + commentLen = get2LE(ptr + kCDECommentLen); + + //LOGV("+++ %d: localHdr=%d fnl=%d el=%d cl=%d\n", + // i, localHdrOffset, fileNameLen, extraLen, commentLen); + //LOGV(" '%.*s'\n", fileNameLen, ptr + kCDELen); + + /* add the CDE filename to the hash table */ + hash = computeHash((const char*)ptr + kCDELen, fileNameLen); + addToHash((const char*)ptr + kCDELen, fileNameLen, hash); + + localHdr = basePtr + localHdrOffset; + if (get4LE(localHdr) != kLFHSignature) { + LOGW("Bad offset to local header: %d (at %d)\n", + localHdrOffset, i); + goto bail; + } + + ptr += kCDELen + fileNameLen + extraLen + commentLen; + CHECK_OFFSET(ptr - basePtr); + } + + result = true; + +bail: + return result; +#undef CHECK_OFFSET +} + + +/* + * Simple string hash function for non-null-terminated strings. + */ +/*static*/ unsigned int ZipFileRO::computeHash(const char* str, int len) +{ + unsigned int hash = 0; + + while (len--) + hash = hash * 31 + *str++; + + return hash; +} + +/* + * Add a new entry to the hash table. + */ +void ZipFileRO::addToHash(const char* str, int strLen, unsigned int hash) +{ + int ent = hash & (mHashTableSize-1); + + /* + * We over-allocate the table, so we're guaranteed to find an empty slot. + */ + while (mHashTable[ent].name != NULL) + ent = (ent + 1) & (mHashTableSize-1); + + mHashTable[ent].name = str; + mHashTable[ent].nameLen = strLen; +} + +/* + * Find a matching entry. + * + * Returns 0 if not found. + */ +ZipEntryRO ZipFileRO::findEntryByName(const char* fileName) const +{ + int nameLen = strlen(fileName); + unsigned int hash = computeHash(fileName, nameLen); + int ent = hash & (mHashTableSize-1); + + while (mHashTable[ent].name != NULL) { + if (mHashTable[ent].nameLen == nameLen && + memcmp(mHashTable[ent].name, fileName, nameLen) == 0) + { + /* match */ + return (ZipEntryRO) (ent + kZipEntryAdj); + } + + ent = (ent + 1) & (mHashTableSize-1); + } + + return NULL; +} + +/* + * Find the Nth entry. + * + * This currently involves walking through the sparse hash table, counting + * non-empty entries. If we need to speed this up we can either allocate + * a parallel lookup table or (perhaps better) provide an iterator interface. + */ +ZipEntryRO ZipFileRO::findEntryByIndex(int idx) const +{ + if (idx < 0 || idx >= mNumEntries) { + LOGW("Invalid index %d\n", idx); + return NULL; + } + + for (int ent = 0; ent < mHashTableSize; ent++) { + if (mHashTable[ent].name != NULL) { + if (idx-- == 0) + return (ZipEntryRO) (ent + kZipEntryAdj); + } + } + + return NULL; +} + +/* + * Get the useful fields from the zip entry. + * + * Returns "false" if the offsets to the fields or the contents of the fields + * appear to be bogus. + */ +bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, long* pUncompLen, + long* pCompLen, off_t* pOffset, long* pModWhen, long* pCrc32) const +{ + int ent = entryToIndex(entry); + if (ent < 0) + return false; + + /* + * Recover the start of the central directory entry from the filename + * pointer. + */ + const unsigned char* basePtr = (const unsigned char*)mFileMap->getDataPtr(); + const unsigned char* ptr = (const unsigned char*) mHashTable[ent].name; + size_t zipLength = mFileMap->getDataLength(); + + ptr -= kCDELen; + + int method = get2LE(ptr + kCDEMethod); + if (pMethod != NULL) + *pMethod = method; + + if (pModWhen != NULL) + *pModWhen = get4LE(ptr + kCDEModWhen); + if (pCrc32 != NULL) + *pCrc32 = get4LE(ptr + kCDECRC); + + /* + * We need to make sure that the lengths are not so large that somebody + * trying to map the compressed or uncompressed data runs off the end + * of the mapped region. + */ + unsigned long localHdrOffset = get4LE(ptr + kCDELocalOffset); + if (localHdrOffset + kLFHLen >= zipLength) { + LOGE("ERROR: bad local hdr offset in zip\n"); + return false; + } + const unsigned char* localHdr = basePtr + localHdrOffset; + off_t dataOffset = localHdrOffset + kLFHLen + + get2LE(localHdr + kLFHNameLen) + get2LE(localHdr + kLFHExtraLen); + if ((unsigned long) dataOffset >= zipLength) { + LOGE("ERROR: bad data offset in zip\n"); + return false; + } + + if (pCompLen != NULL) { + *pCompLen = get4LE(ptr + kCDECompLen); + if (*pCompLen < 0 || (size_t)(dataOffset + *pCompLen) >= zipLength) { + LOGE("ERROR: bad compressed length in zip\n"); + return false; + } + } + if (pUncompLen != NULL) { + *pUncompLen = get4LE(ptr + kCDEUncompLen); + if (*pUncompLen < 0) { + LOGE("ERROR: negative uncompressed length in zip\n"); + return false; + } + if (method == kCompressStored && + (size_t)(dataOffset + *pUncompLen) >= zipLength) + { + LOGE("ERROR: bad uncompressed length in zip\n"); + return false; + } + } + + if (pOffset != NULL) { + *pOffset = dataOffset; + } + return true; +} + +/* + * Copy the entry's filename to the buffer. + */ +int ZipFileRO::getEntryFileName(ZipEntryRO entry, char* buffer, int bufLen) + const +{ + int ent = entryToIndex(entry); + if (ent < 0) + return -1; + + int nameLen = mHashTable[ent].nameLen; + if (bufLen < nameLen+1) + return nameLen+1; + + memcpy(buffer, mHashTable[ent].name, nameLen); + buffer[nameLen] = '\0'; + return 0; +} + +/* + * Create a new FileMap object that spans the data in "entry". + */ +FileMap* ZipFileRO::createEntryFileMap(ZipEntryRO entry) const +{ + /* + * TODO: the efficient way to do this is to modify FileMap to allow + * sub-regions of a file to be mapped. A reference-counting scheme + * can manage the base memory mapping. For now, we just create a brand + * new mapping off of the Zip archive file descriptor. + */ + + FileMap* newMap; + long compLen; + off_t offset; + + if (!getEntryInfo(entry, NULL, NULL, &compLen, &offset, NULL, NULL)) + return NULL; + + newMap = new FileMap(); + if (!newMap->create(mFileMap->getFileName(), mFd, offset, compLen, true)) { + newMap->release(); + return NULL; + } + + return newMap; +} + +/* + * Uncompress an entry, in its entirety, into the provided output buffer. + * + * This doesn't verify the data's CRC, which might be useful for + * uncompressed data. The caller should be able to manage it. + */ +bool ZipFileRO::uncompressEntry(ZipEntryRO entry, void* buffer) const +{ + const int kSequentialMin = 32768; + bool result = false; + int ent = entryToIndex(entry); + if (ent < 0) + return -1; + + const unsigned char* basePtr = (const unsigned char*)mFileMap->getDataPtr(); + int method; + long uncompLen, compLen; + off_t offset; + + getEntryInfo(entry, &method, &uncompLen, &compLen, &offset, NULL, NULL); + + /* + * Experiment with madvise hint. When we want to uncompress a file, + * we pull some stuff out of the central dir entry and then hit a + * bunch of compressed or uncompressed data sequentially. The CDE + * visit will cause a limited amount of read-ahead because it's at + * the end of the file. We could end up doing lots of extra disk + * access if the file we're prying open is small. Bottom line is we + * probably don't want to turn MADV_SEQUENTIAL on and leave it on. + * + * So, if the compressed size of the file is above a certain minimum + * size, temporarily boost the read-ahead in the hope that the extra + * pair of system calls are negated by a reduction in page faults. + */ + if (compLen > kSequentialMin) + mFileMap->advise(FileMap::SEQUENTIAL); + + if (method == kCompressStored) { + memcpy(buffer, basePtr + offset, uncompLen); + } else { + if (!inflateBuffer(buffer, basePtr + offset, uncompLen, compLen)) + goto bail; + } + + if (compLen > kSequentialMin) + mFileMap->advise(FileMap::NORMAL); + + result = true; + +bail: + return result; +} + +/* + * Uncompress an entry, in its entirety, to an open file descriptor. + * + * This doesn't verify the data's CRC, but probably should. + */ +bool ZipFileRO::uncompressEntry(ZipEntryRO entry, int fd) const +{ + bool result = false; + int ent = entryToIndex(entry); + if (ent < 0) + return -1; + + const unsigned char* basePtr = (const unsigned char*)mFileMap->getDataPtr(); + int method; + long uncompLen, compLen; + off_t offset; + + getEntryInfo(entry, &method, &uncompLen, &compLen, &offset, NULL, NULL); + + if (method == kCompressStored) { + ssize_t actual; + + actual = write(fd, basePtr + offset, uncompLen); + if (actual < 0) { + LOGE("Write failed: %s\n", strerror(errno)); + goto bail; + } else if (actual != uncompLen) { + LOGE("Partial write during uncompress (%d of %ld)\n", + (int)actual, uncompLen); + goto bail; + } else { + LOGI("+++ successful write\n"); + } + } else { + if (!inflateBuffer(fd, basePtr+offset, uncompLen, compLen)) + goto bail; + } + + result = true; + +bail: + return result; +} + +/* + * Uncompress "deflate" data from one buffer to another. + */ +/*static*/ bool ZipFileRO::inflateBuffer(void* outBuf, const void* inBuf, + long uncompLen, long compLen) +{ + bool result = false; + z_stream zstream; + int zerr; + + /* + * Initialize the zlib stream struct. + */ + memset(&zstream, 0, sizeof(zstream)); + zstream.zalloc = Z_NULL; + zstream.zfree = Z_NULL; + zstream.opaque = Z_NULL; + zstream.next_in = (Bytef*)inBuf; + zstream.avail_in = compLen; + zstream.next_out = (Bytef*) outBuf; + zstream.avail_out = uncompLen; + zstream.data_type = Z_UNKNOWN; + + /* + * Use the undocumented "negative window bits" feature to tell zlib + * that there's no zlib header waiting for it. + */ + zerr = inflateInit2(&zstream, -MAX_WBITS); + if (zerr != Z_OK) { + if (zerr == Z_VERSION_ERROR) { + LOGE("Installed zlib is not compatible with linked version (%s)\n", + ZLIB_VERSION); + } else { + LOGE("Call to inflateInit2 failed (zerr=%d)\n", zerr); + } + goto bail; + } + + /* + * Expand data. + */ + zerr = inflate(&zstream, Z_FINISH); + if (zerr != Z_STREAM_END) { + LOGW("Zip inflate failed, zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)\n", + zerr, zstream.next_in, zstream.avail_in, + zstream.next_out, zstream.avail_out); + goto z_bail; + } + + /* paranoia */ + if ((long) zstream.total_out != uncompLen) { + LOGW("Size mismatch on inflated file (%ld vs %ld)\n", + zstream.total_out, uncompLen); + goto z_bail; + } + + result = true; + +z_bail: + inflateEnd(&zstream); /* free up any allocated structures */ + +bail: + return result; +} + +/* + * Uncompress "deflate" data from one buffer to an open file descriptor. + */ +/*static*/ bool ZipFileRO::inflateBuffer(int fd, const void* inBuf, + long uncompLen, long compLen) +{ + bool result = false; + const int kWriteBufSize = 32768; + unsigned char writeBuf[kWriteBufSize]; + z_stream zstream; + int zerr; + + /* + * Initialize the zlib stream struct. + */ + memset(&zstream, 0, sizeof(zstream)); + zstream.zalloc = Z_NULL; + zstream.zfree = Z_NULL; + zstream.opaque = Z_NULL; + zstream.next_in = (Bytef*)inBuf; + zstream.avail_in = compLen; + zstream.next_out = (Bytef*) writeBuf; + zstream.avail_out = sizeof(writeBuf); + zstream.data_type = Z_UNKNOWN; + + /* + * Use the undocumented "negative window bits" feature to tell zlib + * that there's no zlib header waiting for it. + */ + zerr = inflateInit2(&zstream, -MAX_WBITS); + if (zerr != Z_OK) { + if (zerr == Z_VERSION_ERROR) { + LOGE("Installed zlib is not compatible with linked version (%s)\n", + ZLIB_VERSION); + } else { + LOGE("Call to inflateInit2 failed (zerr=%d)\n", zerr); + } + goto bail; + } + + /* + * Loop while we have more to do. + */ + do { + /* + * Expand data. + */ + zerr = inflate(&zstream, Z_NO_FLUSH); + if (zerr != Z_OK && zerr != Z_STREAM_END) { + LOGW("zlib inflate: zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)\n", + zerr, zstream.next_in, zstream.avail_in, + zstream.next_out, zstream.avail_out); + goto z_bail; + } + + /* write when we're full or when we're done */ + if (zstream.avail_out == 0 || + (zerr == Z_STREAM_END && zstream.avail_out != sizeof(writeBuf))) + { + long writeSize = zstream.next_out - writeBuf; + int cc = write(fd, writeBuf, writeSize); + if (cc != (int) writeSize) { + LOGW("write failed in inflate (%d vs %ld)\n", cc, writeSize); + goto z_bail; + } + + zstream.next_out = writeBuf; + zstream.avail_out = sizeof(writeBuf); + } + } while (zerr == Z_OK); + + assert(zerr == Z_STREAM_END); /* other errors should've been caught */ + + /* paranoia */ + if ((long) zstream.total_out != uncompLen) { + LOGW("Size mismatch on inflated file (%ld vs %ld)\n", + zstream.total_out, uncompLen); + goto z_bail; + } + + result = true; + +z_bail: + inflateEnd(&zstream); /* free up any allocated structures */ + +bail: + return result; +} diff --git a/libs/utils/ZipUtils.cpp b/libs/utils/ZipUtils.cpp new file mode 100644 index 000000000..bfbacfecd --- /dev/null +++ b/libs/utils/ZipUtils.cpp @@ -0,0 +1,344 @@ +/* + * 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. + */ + +// +// Misc zip/gzip utility functions. +// + +#define LOG_TAG "ziputil" + +#include "utils/ZipUtils.h" +#include "utils/ZipFileRO.h" +#include "utils/Log.h" + +#include +#include +#include + +#include + +using namespace android; + +/* + * Utility function that expands zip/gzip "deflate" compressed data + * into a buffer. + * + * "fd" is an open file positioned at the start of the "deflate" data + * "buf" must hold at least "uncompressedLen" bytes. + */ +/*static*/ bool ZipUtils::inflateToBuffer(int fd, void* buf, + long uncompressedLen, long compressedLen) +{ + bool result = false; + const unsigned long kReadBufSize = 32768; + unsigned char* readBuf = NULL; + z_stream zstream; + int zerr; + unsigned long compRemaining; + + assert(uncompressedLen >= 0); + assert(compressedLen >= 0); + + readBuf = new unsigned char[kReadBufSize]; + if (readBuf == NULL) + goto bail; + compRemaining = compressedLen; + + /* + * Initialize the zlib stream. + */ + memset(&zstream, 0, sizeof(zstream)); + zstream.zalloc = Z_NULL; + zstream.zfree = Z_NULL; + zstream.opaque = Z_NULL; + zstream.next_in = NULL; + zstream.avail_in = 0; + zstream.next_out = (Bytef*) buf; + zstream.avail_out = uncompressedLen; + zstream.data_type = Z_UNKNOWN; + + /* + * Use the undocumented "negative window bits" feature to tell zlib + * that there's no zlib header waiting for it. + */ + zerr = inflateInit2(&zstream, -MAX_WBITS); + if (zerr != Z_OK) { + if (zerr == Z_VERSION_ERROR) { + LOGE("Installed zlib is not compatible with linked version (%s)\n", + ZLIB_VERSION); + } else { + LOGE("Call to inflateInit2 failed (zerr=%d)\n", zerr); + } + goto bail; + } + + /* + * Loop while we have data. + */ + do { + unsigned long getSize; + + /* read as much as we can */ + if (zstream.avail_in == 0) { + getSize = (compRemaining > kReadBufSize) ? + kReadBufSize : compRemaining; + LOGV("+++ reading %ld bytes (%ld left)\n", + getSize, compRemaining); + + int cc = read(fd, readBuf, getSize); + if (cc != (int) getSize) { + LOGD("inflate read failed (%d vs %ld)\n", + cc, getSize); + goto z_bail; + } + + compRemaining -= getSize; + + zstream.next_in = readBuf; + zstream.avail_in = getSize; + } + + /* uncompress the data */ + zerr = inflate(&zstream, Z_NO_FLUSH); + if (zerr != Z_OK && zerr != Z_STREAM_END) { + LOGD("zlib inflate call failed (zerr=%d)\n", zerr); + goto z_bail; + } + + /* output buffer holds all, so no need to write the output */ + } while (zerr == Z_OK); + + assert(zerr == Z_STREAM_END); /* other errors should've been caught */ + + if ((long) zstream.total_out != uncompressedLen) { + LOGW("Size mismatch on inflated file (%ld vs %ld)\n", + zstream.total_out, uncompressedLen); + goto z_bail; + } + + // success! + result = true; + +z_bail: + inflateEnd(&zstream); /* free up any allocated structures */ + +bail: + delete[] readBuf; + return result; +} + +/* + * Utility function that expands zip/gzip "deflate" compressed data + * into a buffer. + * + * (This is a clone of the previous function, but it takes a FILE* instead + * of an fd. We could pass fileno(fd) to the above, but we can run into + * trouble when "fp" has a different notion of what fd's file position is.) + * + * "fp" is an open file positioned at the start of the "deflate" data + * "buf" must hold at least "uncompressedLen" bytes. + */ +/*static*/ bool ZipUtils::inflateToBuffer(FILE* fp, void* buf, + long uncompressedLen, long compressedLen) +{ + bool result = false; + const unsigned long kReadBufSize = 32768; + unsigned char* readBuf = NULL; + z_stream zstream; + int zerr; + unsigned long compRemaining; + + assert(uncompressedLen >= 0); + assert(compressedLen >= 0); + + readBuf = new unsigned char[kReadBufSize]; + if (readBuf == NULL) + goto bail; + compRemaining = compressedLen; + + /* + * Initialize the zlib stream. + */ + memset(&zstream, 0, sizeof(zstream)); + zstream.zalloc = Z_NULL; + zstream.zfree = Z_NULL; + zstream.opaque = Z_NULL; + zstream.next_in = NULL; + zstream.avail_in = 0; + zstream.next_out = (Bytef*) buf; + zstream.avail_out = uncompressedLen; + zstream.data_type = Z_UNKNOWN; + + /* + * Use the undocumented "negative window bits" feature to tell zlib + * that there's no zlib header waiting for it. + */ + zerr = inflateInit2(&zstream, -MAX_WBITS); + if (zerr != Z_OK) { + if (zerr == Z_VERSION_ERROR) { + LOGE("Installed zlib is not compatible with linked version (%s)\n", + ZLIB_VERSION); + } else { + LOGE("Call to inflateInit2 failed (zerr=%d)\n", zerr); + } + goto bail; + } + + /* + * Loop while we have data. + */ + do { + unsigned long getSize; + + /* read as much as we can */ + if (zstream.avail_in == 0) { + getSize = (compRemaining > kReadBufSize) ? + kReadBufSize : compRemaining; + LOGV("+++ reading %ld bytes (%ld left)\n", + getSize, compRemaining); + + int cc = fread(readBuf, getSize, 1, fp); + if (cc != (int) getSize) { + LOGD("inflate read failed (%d vs %ld)\n", + cc, getSize); + goto z_bail; + } + + compRemaining -= getSize; + + zstream.next_in = readBuf; + zstream.avail_in = getSize; + } + + /* uncompress the data */ + zerr = inflate(&zstream, Z_NO_FLUSH); + if (zerr != Z_OK && zerr != Z_STREAM_END) { + LOGD("zlib inflate call failed (zerr=%d)\n", zerr); + goto z_bail; + } + + /* output buffer holds all, so no need to write the output */ + } while (zerr == Z_OK); + + assert(zerr == Z_STREAM_END); /* other errors should've been caught */ + + if ((long) zstream.total_out != uncompressedLen) { + LOGW("Size mismatch on inflated file (%ld vs %ld)\n", + zstream.total_out, uncompressedLen); + goto z_bail; + } + + // success! + result = true; + +z_bail: + inflateEnd(&zstream); /* free up any allocated structures */ + +bail: + delete[] readBuf; + return result; +} + +/* + * Look at the contents of a gzip archive. We want to know where the + * data starts, and how long it will be after it is uncompressed. + * + * We expect to find the CRC and length as the last 8 bytes on the file. + * This is a pretty reasonable thing to expect for locally-compressed + * files, but there's a small chance that some extra padding got thrown + * on (the man page talks about compressed data written to tape). We + * don't currently deal with that here. If "gzip -l" whines, we're going + * to fail too. + * + * On exit, "fp" is pointing at the start of the compressed data. + */ +/*static*/ bool ZipUtils::examineGzip(FILE* fp, int* pCompressionMethod, + long* pUncompressedLen, long* pCompressedLen, unsigned long* pCRC32) +{ + enum { // flags + FTEXT = 0x01, + FHCRC = 0x02, + FEXTRA = 0x04, + FNAME = 0x08, + FCOMMENT = 0x10, + }; + int ic; + int method, flags; + int i; + + ic = getc(fp); + if (ic != 0x1f || getc(fp) != 0x8b) + return false; // not gzip + method = getc(fp); + flags = getc(fp); + + /* quick sanity checks */ + if (method == EOF || flags == EOF) + return false; + if (method != ZipFileRO::kCompressDeflated) + return false; + + /* skip over 4 bytes of mod time, 1 byte XFL, 1 byte OS */ + for (i = 0; i < 6; i++) + (void) getc(fp); + /* consume "extra" field, if present */ + if ((flags & FEXTRA) != 0) { + int len; + + len = getc(fp); + len |= getc(fp) << 8; + while (len-- && getc(fp) != EOF) + ; + } + /* consume filename, if present */ + if ((flags & FNAME) != 0) { + do { + ic = getc(fp); + } while (ic != 0 && ic != EOF); + } + /* consume comment, if present */ + if ((flags & FCOMMENT) != 0) { + do { + ic = getc(fp); + } while (ic != 0 && ic != EOF); + } + /* consume 16-bit header CRC, if present */ + if ((flags & FHCRC) != 0) { + (void) getc(fp); + (void) getc(fp); + } + + if (feof(fp) || ferror(fp)) + return false; + + /* seek to the end; CRC and length are in the last 8 bytes */ + long curPosn = ftell(fp); + unsigned char buf[8]; + fseek(fp, -8, SEEK_END); + *pCompressedLen = ftell(fp) - curPosn; + + if (fread(buf, 1, 8, fp) != 8) + return false; + /* seek back to start of compressed data */ + fseek(fp, curPosn, SEEK_SET); + + *pCompressionMethod = method; + *pCRC32 = ZipFileRO::get4LE(&buf[0]); + *pUncompressedLen = ZipFileRO::get4LE(&buf[4]); + + return true; +} + diff --git a/libs/utils/characterData.h b/libs/utils/characterData.h new file mode 100644 index 000000000..e931d995e --- /dev/null +++ b/libs/utils/characterData.h @@ -0,0 +1,730 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Automatically generated on 07-11-2006 by make-CharacterDataC +// DO NOT EDIT DIRECTLY +namespace CharacterData { + + // Structure containing an array of ranges + struct Range { + int length; + const uint32_t* array; + }; + + // For Latin1 characters just index into this array to get the index and decomposition + static const uint16_t LATIN1_DATA[] = { + 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, + 0x0001, 0x0002, 0x0003, 0x0002, 0x0004, 0x0003, 0x0001, 0x0001, + 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, + 0x0001, 0x0001, 0x0001, 0x0001, 0x0003, 0x0003, 0x0003, 0x0002, + 0x0005, 0x0006, 0x0006, 0x0007, 0x0008, 0x0007, 0x0006, 0x0006, + 0x0009, 0x000A, 0x0006, 0x000B, 0x000C, 0x000D, 0x000C, 0x000C, + 0x000E, 0x000F, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, + 0x0016, 0x0017, 0x000C, 0x0006, 0x0018, 0x0019, 0x001A, 0x0006, + 0x0006, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, 0x0020, 0x0021, + 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, + 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, 0x0030, 0x0031, + 0x0032, 0x0033, 0x0034, 0x0035, 0x0006, 0x0036, 0x0037, 0x0038, + 0x0037, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, + 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, + 0x0050, 0x0051, 0x0052, 0x0035, 0x0019, 0x0036, 0x0019, 0x0001, + 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0003, 0x0001, 0x0001, + 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, + 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, + 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, + 0x5853, 0x0006, 0x0008, 0x0008, 0x0008, 0x0008, 0x0054, 0x0054, + 0x1037, 0x0054, 0x7855, 0x0056, 0x0019, 0x0057, 0x0054, 0x1037, + 0x0058, 0x0059, 0x785A, 0x785B, 0x1037, 0x105C, 0x0054, 0x0006, + 0x1037, 0x785D, 0x7855, 0x005E, 0x305F, 0x305F, 0x305F, 0x0006, + 0x0860, 0x0860, 0x0860, 0x0860, 0x0860, 0x0860, 0x0060, 0x0860, + 0x0860, 0x0860, 0x0860, 0x0860, 0x0860, 0x0860, 0x0860, 0x0860, + 0x0060, 0x0860, 0x0860, 0x0860, 0x0860, 0x0860, 0x0860, 0x0019, + 0x0060, 0x0860, 0x0860, 0x0860, 0x0860, 0x0860, 0x0060, 0x0055, + 0x0861, 0x0861, 0x0861, 0x0861, 0x0861, 0x0861, 0x0061, 0x0861, + 0x0861, 0x0861, 0x0861, 0x0861, 0x0861, 0x0861, 0x0861, 0x0861, + 0x0061, 0x0861, 0x0861, 0x0861, 0x0861, 0x0861, 0x0861, 0x0019, + 0x0061, 0x0861, 0x0861, 0x0861, 0x0861, 0x0861, 0x0061, 0x0862 + }; + + // Each of these arrays is stripped into ranges. In order to build the arrays, each + // codepoint was bit-shifted so that even and odd characters were separated into different + // arrays. The identifier of each array is the top byte after bit-shifting. + // The numbers stored in the array are the bit-shifted codepoint, the decomposition, and an + // index into another array of all possible packed data values. The top 16 bits are the + // codepoint and the bottom 16 are the decomposition and index. The top 5 bits for the decomposition + // and the rest for the index. + static const uint32_t a0[] = { + 0x00800863, 0x00880063, 0x00890863, 0x00930063, 0x00940863, 0x00980864, 0x00991063, 0x009A0863, + 0x009C0055, 0x009D0865, 0x00A01065, 0x00A10065, 0x00A20865, 0x00A50063, 0x00A60863, 0x00A90063, + 0x00AA0863, 0x00B30063, 0x00B40863, 0x00BC0866, 0x00BD0865, 0x00C00055, 0x00C10063, 0x00C30067, + 0x00C40065, 0x00C50068, 0x00C60065, 0x00C70069, 0x00C8006A, 0x00C90065, 0x00CA006B, 0x00CB006C, + 0x00CC0063, 0x00CD006D, 0x00CE006C, 0x00CF006E, 0x00D00863, 0x00D10063, 0x00D3006F, 0x00D40065, + 0x00D50055, 0x00D60063, 0x00D7006F, 0x00D80865, 0x00D90070, 0x00DA0065, 0x00DC0063, 0x00DD0055, + 0x00DE0063, 0x00DF0055, 0x00E00071, 0x00E21072, 0x00E31073, 0x00E41074, 0x00E51072, 0x00E61073, + 0x00E70865, 0x00EF0863, 0x00F20063, 0x00F30863, 0x00F80855, 0x00F91074, 0x00FA0863, 0x00FB0075, + 0x00FC0863, 0x010E0063, 0x010F0863, 0x01100076, 0x01110063, 0x01130863, 0x011A0055, 0x011D0077, + 0x011E0065, 0x011F0077, 0x01200055, 0x01210078, 0x01280055, 0x012A0079, 0x012B007A, 0x012C0055, + 0x0130007A, 0x01310055, 0x0134007B, 0x01350055, 0x0139007C, 0x013A0055, 0x0140007D, 0x01410055, + 0x0144007D, 0x0145007E, 0x01460055, 0x0149007F, 0x014A0080, 0x014B0055, 0x01587881, 0x015D0082, + 0x015E0081, 0x01610037, 0x01630082, 0x01680081, 0x01690037, 0x016C1037, 0x016F0037, 0x01707881, + 0x01730037, 0x01770081, 0x01780037, 0x01800083, 0x01A00883, 0x01A10083, 0x01A20883, 0x01A30083, + 0x01B80078, 0x01BA0837, 0x01BB0078, 0x01BD1081, 0x01BE0078, 0x01BF0806, 0x01C00078, 0x01C21037, + 0x01C30884, 0x01C40885, 0x01C60886, 0x01C70887, 0x01C80855, 0x01C90060, 0x01D10078, 0x01D20060, + 0x01D50860, 0x01D60888, 0x01D70889, 0x01D80855, 0x01D90061, 0x01E1008A, 0x01E20061, 0x01E50861, + 0x01E6088B, 0x01E7088C, 0x01E8108D, 0x01E91077, 0x01EA0877, 0x01EB108E, 0x01EC0063, 0x01F8108F, + 0x01F91090, 0x01FA1091, 0x01FB0019, 0x01FC0065, 0x01FD0063, 0x01FE0055, 0x01FF0077, 0x02000892, + 0x02010092, 0x02060892, 0x02080060, 0x02180061, 0x02280893, 0x02290093, 0x022E0893, 0x02300063, + 0x023B0863, 0x023C0063, 0x02410094, 0x02420083, 0x02440095, 0x02450063, 0x02600077, 0x02610865, + 0x02620065, 0x02680863, 0x026A0063, 0x026B0863, 0x026C0063, 0x026D0863, 0x02700063, 0x02710863, + 0x02740063, 0x02750863, 0x027B0063, 0x027C0863, 0x027D0078, 0x02800063, 0x02880078, 0x02990096, + 0x02AC0078, 0x02AD0097, 0x02B00078, 0x02B10098, 0x02C40078, 0x02C50099, 0x02C60078, 0x02C90083, + 0x02DD0078, 0x02DE0083, 0x02DF009A, 0x02E10083, 0x02E3009A, 0x02E40078, 0x02E8009B, 0x02F60078, + 0x02F8009B, 0x02FA009A, 0x02FB0078, 0x0300009C, 0x03020078, 0x0306000C, 0x03070054, 0x03080083, + 0x030B0078, 0x030F009D, 0x03100078, 0x0311089E, 0x0314009E, 0x031E0078, 0x0320009F, 0x0321009E, + 0x03260083, 0x033000A0, 0x033100A1, 0x033200A2, 0x033300A3, 0x033400A4, 0x03350007, 0x033600A5, + 0x0337009E, 0x03380083, 0x0339009E, 0x033B109E, 0x033D009E, 0x0360089E, 0x0362009E, 0x036A009D, + 0x036B0083, 0x036F0095, 0x03700083, 0x0373009F, 0x03740083, 0x0377009E, 0x0378000E, 0x03790010, + 0x037A0012, 0x037B0014, 0x037C0016, 0x037D009E, 0x037F00A6, 0x0380009D, 0x03870078, 0x0388009E, + 0x03980083, 0x03A60078, 0x03A7009E, 0x03B70078, 0x03C0009E, 0x03D30083, 0x03D90078, 0x04810083, + 0x04820071, 0x049A0871, 0x049B0071, 0x049D0078, 0x049E0083, 0x049F00A7, 0x04A10083, 0x04A500A7, + 0x04A70078, 0x04A80071, 0x04A90083, 0x04AB0078, 0x04AC0871, 0x04B00071, 0x04B10083, 0x04B20097, + 0x04B300A8, 0x04B400A9, 0x04B500AA, 0x04B600AB, 0x04B700AC, 0x04B80097, 0x04B90078, 0x04C100A7, + 0x04C20078, 0x04C30071, 0x04C70078, 0x04C80071, 0x04C90078, 0x04CA0071, 0x04DA0078, 0x04DB0071, + 0x04DD0078, 0x04DE0083, 0x04DF00A7, 0x04E10083, 0x04E30078, 0x04E400A7, 0x04E50078, 0x04E608A7, + 0x04E70071, 0x04E80078, 0x04EE0871, 0x04EF0078, 0x04F00071, 0x04F10083, 0x04F20078, 0x04F300A8, + 0x04F400A9, 0x04F500AA, 0x04F600AB, 0x04F700AC, 0x04F80071, 0x04F90008, 0x04FA00AD, 0x04FB00AE, + 0x04FC00AF, 0x04FD0094, 0x04FE0078, 0x05010083, 0x05020078, 0x05030071, 0x05060078, 0x05080071, + 0x05090078, 0x050A0071, 0x051A0078, 0x051B0871, 0x051C0071, 0x051D0078, 0x051E0083, 0x051F00A7, + 0x05210083, 0x05220078, 0x05240083, 0x05250078, 0x05260083, 0x05270078, 0x052D0871, 0x052E0071, + 0x052F0871, 0x05300078, 0x053300A8, 0x053400A9, 0x053500AA, 0x053600AB, 0x053700AC, 0x05380083, + 0x05390071, 0x053B0078, 0x05410083, 0x05420078, 0x05430071, 0x05470078, 0x05480071, 0x05490078, + 0x054A0071, 0x055A0078, 0x055B0071, 0x055D0078, 0x055E0083, 0x055F00A7, 0x05610083, 0x05630078, + 0x05640083, 0x05650078, 0x056600A7, 0x05670078, 0x05680071, 0x05690078, 0x05700071, 0x05710083, + 0x05720078, 0x057300A8, 0x057400A9, 0x057500AA, 0x057600AB, 0x057700AC, 0x05780078, 0x058100A7, + 0x05820078, 0x05830071, 0x05870078, 0x05880071, 0x05890078, 0x058A0071, 0x059A0078, 0x059B0071, + 0x059D0078, 0x059E0083, 0x059F00A7, 0x05A10083, 0x05A20078, 0x05A408A7, 0x05A50078, 0x05A608A7, + 0x05A70078, 0x05AB0083, 0x05AC0078, 0x05AE0871, 0x05AF0078, 0x05B00071, 0x05B10078, 0x05B300A8, + 0x05B400A9, 0x05B500AA, 0x05B600AB, 0x05B700AC, 0x05B80094, 0x05B90078, 0x05C10083, 0x05C20078, + 0x05C30071, 0x05C60078, 0x05C70071, 0x05CA0871, 0x05CB0078, 0x05CD0071, 0x05D00078, 0x05D20071, + 0x05D30078, 0x05D40071, 0x05D60078, 0x05D70071, 0x05DD0078, 0x05DF00A7, 0x05E00083, 0x05E100A7, + 0x05E20078, 0x05E300A7, 0x05E508A7, 0x05E70078, 0x05F300A8, 0x05F400A9, 0x05F500AA, 0x05F600AB, + 0x05F700AC, 0x05F800B0, 0x05F900B1, 0x05FA0054, 0x05FE0078, 0x060100A7, 0x06020078, 0x06030071, + 0x061A0078, 0x061B0071, 0x061D0078, 0x061F0083, 0x062100A7, 0x06230083, 0x06240883, 0x06250083, + 0x06270078, 0x062B0083, 0x062C0078, 0x06300071, 0x06310078, 0x063300A8, 0x063400A9, 0x063500AA, + 0x063600AB, 0x063700AC, 0x06380078, 0x064100A7, 0x06420078, 0x06430071, 0x065A0078, 0x065B0071, + 0x065D0078, 0x065E0083, 0x065F00A7, 0x066008A7, 0x066100A7, 0x066300B2, 0x066408A7, 0x06660083, + 0x06670078, 0x066B00A7, 0x066C0078, 0x066F0071, 0x06710078, 0x067300A8, 0x067400A9, 0x067500AA, + 0x067600AB, 0x067700AC, 0x06780078, 0x068100A7, 0x06820078, 0x06830071, 0x069D0078, 0x069F00A7, + 0x06A10083, 0x06A20078, 0x06A300A7, 0x06A508A7, 0x06A70078, 0x06B00071, 0x06B10078, 0x06B300A8, + 0x06B400A9, 0x06B500AA, 0x06B600AB, 0x06B700AC, 0x06B80078, 0x06C100A7, 0x06C20078, 0x06C30071, + 0x06CC0078, 0x06CD0071, 0x06D90078, 0x06DA0071, 0x06DE0078, 0x06E00071, 0x06E40078, 0x06E50083, + 0x06E60078, 0x06E800A7, 0x06E90083, 0x06EC00A7, 0x06ED08A7, 0x06F00078, 0x06F900A7, 0x06FA0097, + 0x06FB0078, 0x07010071, 0x071A0083, 0x071E0078, 0x07200071, 0x07230081, 0x07240083, 0x072800A8, + 0x072900A9, 0x072A00AA, 0x072B00AB, 0x072C00AC, 0x072D0097, 0x072E0078, 0x07410071, 0x07430078, + 0x07440071, 0x07460078, 0x074A0071, 0x074C0078, 0x074D0071, 0x07500078, 0x07510071, 0x07520078, + 0x07550071, 0x07560078, 0x07570071, 0x075A0083, 0x075D0078, 0x075E0083, 0x075F0078, 0x07600071, + 0x07630081, 0x07640083, 0x07670078, 0x076800A8, 0x076900A9, 0x076A00AA, 0x076B00AB, 0x076C00AC, + 0x076D0078, 0x076E1071, 0x076F0078, 0x07800071, 0x07810094, 0x07820097, 0x07865897, 0x07870097, + 0x078A0094, 0x078C0083, 0x078D0094, 0x079000A8, 0x079100A9, 0x079200AA, 0x079300AB, 0x079400AC, + 0x079500B3, 0x079A0094, 0x079D00B4, 0x079F00A7, 0x07A00071, 0x07A40078, 0x07A50071, 0x07A90871, + 0x07AA0071, 0x07AE0871, 0x07AF0071, 0x07B60078, 0x07B90083, 0x07BB0883, 0x07BD0083, 0x07C40071, + 0x07C60078, 0x07C80083, 0x07CC0078, 0x07CD0083, 0x07D10883, 0x07D20083, 0x07D60883, 0x07D70083, + 0x07DF0094, 0x07E30083, 0x07E40094, 0x07E70078, 0x07E80097, 0x07E90078, 0x08000071, 0x08110078, + 0x08120071, 0x08130871, 0x08140078, 0x08150071, 0x081600A7, 0x08170083, 0x081A0078, 0x081B0083, + 0x081C00A7, 0x081D0078, 0x082000A8, 0x082100A9, 0x082200AA, 0x082300AB, 0x082400AC, 0x08250097, + 0x08280071, 0x082B00A7, 0x082C0083, 0x082D0078, 0x085000B5, 0x08630078, 0x08680071, 0x087E7881, + 0x087F0078, 0x08800071, 0x08AD0078, 0x08B00071, 0x08D20078, 0x08D40071, 0x08FD0078, 0x09000071, + 0x09270078, 0x09280071, 0x092F0078, 0x09300071, 0x09470078, 0x09480071, 0x095B0078, 0x095C0071, + 0x09630078, 0x09640071, 0x098B0078, 0x098C0071, 0x09AE0078, 0x09B00094, 0x09B10097, 0x09B500B6, + 0x09B600B7, 0x09B700B8, 0x09B800B9, 0x09B900B0, 0x09BA00BA, 0x09BB00BB, 0x09BC00BC, 0x09BD00BD, + 0x09BE00BE, 0x09BF0078, 0x09C00071, 0x09C80054, 0x09CD0078, 0x09D00071, 0x09FB0078, 0x0A010071, + 0x0B370097, 0x0B380071, 0x0B3C0078, 0x0B400005, 0x0B410071, 0x0B4E00BF, 0x0B4F0078, 0x0B500071, + 0x0B760097, 0x0B7700C0, 0x0B7800C1, 0x0B790078, 0x0B800071, 0x0B890083, 0x0B8B0078, 0x0B900071, + 0x0B990083, 0x0B9B0097, 0x0B9C0078, 0x0BA00071, 0x0BA90083, 0x0BAA0078, 0x0BB00071, 0x0BB90083, + 0x0BBA0078, 0x0BC00071, 0x0BDA00C2, 0x0BDB00A7, 0x0BDC0083, 0x0BDF00A7, 0x0BE30083, 0x0BE400A7, + 0x0BE50083, 0x0BEA0097, 0x0BEE0071, 0x0BEF0078, 0x0BF000A8, 0x0BF100A9, 0x0BF200AA, 0x0BF300AB, + 0x0BF400AC, 0x0BF50078, 0x0BF800C3, 0x0BF900C4, 0x0BFA00C5, 0x0BFB00C6, 0x0BFC00C7, 0x0BFD0078, + 0x0C000006, 0x0C030099, 0x0C040006, 0x0C060083, 0x0C070005, 0x0C0800A8, 0x0C0900A9, 0x0C0A00AA, + 0x0C0B00AB, 0x0C0C00AC, 0x0C0D0078, 0x0C100071, 0x0C3C0078, 0x0C400071, 0x0C550078, 0x0C800071, + 0x0C8F0078, 0x0C900083, 0x0C9200A7, 0x0C940083, 0x0C9500C8, 0x0C960078, 0x0C9800A7, 0x0C990083, + 0x0C9A00A7, 0x0C9D0083, 0x0C9E0078, 0x0CA00054, 0x0CA10078, 0x0CA20006, 0x0CA300A8, 0x0CA400A9, + 0x0CA500AA, 0x0CA600AB, 0x0CA700AC, 0x0CA80071, 0x0CB70078, 0x0CB80071, 0x0CBB0078, 0x0CC00071, + 0x0CD50078, 0x0CD800A7, 0x0CE10071, 0x0CE400A7, 0x0CE50078, 0x0CE800A8, 0x0CE900A9, 0x0CEA00AA, + 0x0CEB00AB, 0x0CEC00AC, 0x0CED0078, 0x0CEF0006, 0x0CF00054, 0x0D000071, 0x0D0C0083, 0x0D0D00A7, + 0x0D0E0078, 0x0D0F0097, 0x0D100078, 0x0E800055, 0x0E967881, 0x0EA70081, 0x0EA87881, 0x0EB17055, + 0x0EB60055, 0x0EBC7881, 0x0EBD0055, 0x0ECE7881, 0x0EE00083, 0x0EE20078, 0x0F000863, 0x0F4B0855, + 0x0F4D1055, 0x0F4E0078, 0x0F500863, 0x0F7D0078, 0x0F8008C9, 0x0F8408CA, 0x0F8808C9, 0x0F8B0078, + 0x0F8C08CA, 0x0F8F0078, 0x0F9008C9, 0x0F9408CA, 0x0F9808C9, 0x0F9C08CA, 0x0FA008C9, 0x0FA30078, + 0x0FA408CA, 0x0FA70078, 0x0FA80855, 0x0FAC0078, 0x0FB008C9, 0x0FB408CA, 0x0FB808CB, 0x0FB908CC, + 0x0FBB08CD, 0x0FBC08CE, 0x0FBD08CF, 0x0FBE08D0, 0x0FBF0078, 0x0FC008C9, 0x0FC408D1, 0x0FC808C9, + 0x0FCC08D1, 0x0FD008C9, 0x0FD408D1, 0x0FD808C9, 0x0FD90855, 0x0FDC08CA, 0x0FDD08D2, 0x0FDE08D3, + 0x0FDF08D4, 0x0FE01037, 0x0FE10855, 0x0FE408D5, 0x0FE608D3, 0x0FE70837, 0x0FE808C9, 0x0FE90855, + 0x0FEA0078, 0x0FEB0855, 0x0FEC08CA, 0x0FED08D6, 0x0FEE0078, 0x0FEF0837, 0x0FF008C9, 0x0FF10855, + 0x0FF408CA, 0x0FF508D7, 0x0FF608D8, 0x0FF70837, 0x0FF80078, 0x0FF90855, 0x0FFC08D9, 0x0FFD08DA, + 0x0FFE08D3, 0x0FFF1037, 0x10000805, 0x10011005, 0x10060057, 0x100700C2, 0x10080099, 0x100B0006, + 0x100C00DB, 0x100D00B4, 0x100E00DB, 0x100F00B4, 0x10100006, 0x10121006, 0x101400DC, 0x101500DD, + 0x101600DE, 0x101700DF, 0x10180007, 0x101A1007, 0x101B1006, 0x101C0006, 0x101D00E0, 0x101E1006, + 0x10200038, 0x10210006, 0x102200E1, 0x1023000A, 0x10241006, 0x10250006, 0x10290019, 0x102A0038, + 0x102B0006, 0x10300057, 0x10320078, 0x10350057, 0x103878E2, 0x10390078, 0x103A78E3, 0x103B78E4, + 0x103C78E5, 0x103D780B, 0x103E7819, 0x103F780A, 0x104070E2, 0x1041705A, 0x104270E3, 0x104370E4, + 0x104470E5, 0x1045700B, 0x10467019, 0x1047700A, 0x10487081, 0x104B0078, 0x10500008, 0x10541008, + 0x10550008, 0x105B0078, 0x10680083, 0x106F0095, 0x10730083, 0x10760078, 0x10801054, 0x10812877, + 0x10820054, 0x10831054, 0x10840054, 0x10852855, 0x10862877, 0x10872855, 0x10882877, 0x108A0054, + 0x108B1054, 0x108C0054, 0x108D2877, 0x108F0054, 0x10907854, 0x10922877, 0x109308E6, 0x10942877, + 0x109508E7, 0x10962877, 0x10970058, 0x10982877, 0x10990054, 0x109A2855, 0x109B1071, 0x109D0054, + 0x109E2855, 0x109F2877, 0x10A028E8, 0x10A10019, 0x10A32855, 0x10A50054, 0x10A70078, 0x10AA305F, + 0x10B010E9, 0x10B110EA, 0x10B210EB, 0x10B310EC, 0x10B410ED, 0x10B510EE, 0x10B610EF, 0x10B710F0, + 0x10B810F1, 0x10B910F2, 0x10BA10F3, 0x10BB10F4, 0x10BC10F5, 0x10BD10F6, 0x10BE10F7, 0x10BF10F8, + 0x10C000F9, 0x10C100FA, 0x10C20078, 0x10C80019, 0x10CB0054, 0x10CD0819, 0x10CE0054, 0x10D00019, + 0x10D10054, 0x10D30019, 0x10D40054, 0x10D70819, 0x10D80054, 0x10E70819, 0x10E80054, 0x10E90019, + 0x10EB0054, 0x10FA0019, 0x110100E8, 0x110208E8, 0x11030019, 0x110400FB, 0x110608FC, 0x11070019, + 0x1109000B, 0x110A0019, 0x110B00E8, 0x110C0019, 0x110D00E8, 0x110F0019, 0x111000E8, 0x111208E8, + 0x11140019, 0x111610E8, 0x111700E8, 0x111810E8, 0x111900E8, 0x111A0019, 0x111E00FD, 0x111F00E8, + 0x112208E8, 0x112300E8, 0x11270019, 0x112900FD, 0x112B0019, 0x113008E8, 0x113200FD, 0x11360019, + 0x113708FD, 0x113900FD, 0x113A08FD, 0x113B00FD, 0x113C08FD, 0x113D00FD, 0x114008FD, 0x114100FD, + 0x114208FD, 0x114300FD, 0x114408FD, 0x114500FD, 0x114600E8, 0x11470019, 0x114800FE, 0x114A0019, + 0x114C00FF, 0x114D0019, 0x115100FD, 0x11520019, 0x11530100, 0x11540101, 0x115500E8, 0x115608E8, + 0x115800FD, 0x115C00E8, 0x115D0019, 0x115F00E8, 0x11600019, 0x116500FE, 0x11670019, 0x116800FD, + 0x11690019, 0x116B00FD, 0x117008FD, 0x117200FD, 0x117508FD, 0x11770019, 0x117800FD, 0x11790102, + 0x117B0103, 0x117C00E8, 0x117D0104, 0x117F0105, 0x11800054, 0x118400FD, 0x11860054, 0x119000E8, + 0x11910054, 0x1195080A, 0x11960054, 0x119B0094, 0x11BE0019, 0x11BF0054, 0x11CE0019, 0x11DA00B4, + 0x11DB0006, 0x11DC0054, 0x11EE0078, 0x12000054, 0x12140078, 0x12200054, 0x12260078, 0x12301906, + 0x12311907, 0x12321908, 0x12331909, 0x1234190A, 0x1235190B, 0x1236190C, 0x1237190D, 0x1238190E, + 0x1239190F, 0x123A1106, 0x123B1107, 0x123C1108, 0x123D1109, 0x123E110A, 0x123F110B, 0x1240110C, + 0x1241110D, 0x1242110E, 0x1243110F, 0x1244105D, 0x1245105B, 0x12461110, 0x12471111, 0x12481112, + 0x12491113, 0x124A1114, 0x124B1115, 0x124C1116, 0x124D1117, 0x124E1094, 0x125B1918, 0x12681919, + 0x127518C3, 0x1276011A, 0x1277011B, 0x1278011C, 0x1279011D, 0x127A011E, 0x127B00C4, 0x127C00C5, + 0x127D00C6, 0x127E00C7, 0x127F011F, 0x12800054, 0x12FC0019, 0x13000054, 0x134F0078, 0x13500054, + 0x13560094, 0x13570054, 0x13590078, 0x13810054, 0x13850078, 0x13860054, 0x13940078, 0x13950054, + 0x13A60078, 0x13A80054, 0x13AA0078, 0x13AB0054, 0x13B00078, 0x13B10054, 0x13B40009, 0x13BB0106, + 0x13BC0107, 0x13BD0108, 0x13BE0109, 0x13BF010A, 0x13C00106, 0x13C10107, 0x13C20108, 0x13C30109, + 0x13C4010A, 0x13C50106, 0x13C60107, 0x13C70108, 0x13C80109, 0x13C9010A, 0x13CA0054, 0x13CB0078, + 0x13CC0054, 0x13D80078, 0x13D90054, 0x13E000E8, 0x13E10019, 0x13E200FE, 0x13E3000A, 0x13E40078, + 0x13E80019, 0x13EA00E8, 0x13EB00FE, 0x13EC0019, 0x13EE00E8, 0x13EF00FE, 0x13F00019, 0x13F100FD, + 0x13F30009, 0x13F60078, 0x13F80019, 0x14000094, 0x14800019, 0x14C2000A, 0x14C70120, 0x14C80121, + 0x14C9000A, 0x14CD0019, 0x14CE00E8, 0x14D80019, 0x14DC0122, 0x14DD0019, 0x14E000FD, 0x14E100E8, + 0x14E200FD, 0x14E30019, 0x14E700E8, 0x14E800FE, 0x14EA00FD, 0x14EB0019, 0x14EC0009, 0x14EE00E8, + 0x14EF0019, 0x14F200E8, 0x14F30019, 0x14F400E8, 0x14F50019, 0x14FA00E8, 0x14FC00FD, 0x14FD0019, + 0x14FE0009, 0x14FF0019, 0x150500E8, 0x150610E8, 0x150700E8, 0x15110019, 0x151200E8, 0x15140019, + 0x151600FE, 0x15180019, 0x151A00FD, 0x151B0019, 0x151E00FD, 0x151F00E8, 0x15200019, 0x152C00E8, + 0x152D0019, 0x153200FD, 0x15330019, 0x153500E8, 0x15370019, 0x153800E8, 0x15390019, 0x153A10E8, + 0x153B1019, 0x153C0019, 0x153D00FE, 0x153E00E8, 0x153F00FE, 0x154300E8, 0x154600FE, 0x154700E8, + 0x154900FE, 0x154F00E8, 0x155100FE, 0x15520019, 0x155300FD, 0x15570019, 0x155800FE, 0x155900E8, + 0x155A00FE, 0x155B00E8, 0x155E00FE, 0x156400E8, 0x156700FE, 0x156C0019, 0x156E08E8, 0x156F0123, + 0x15700019, 0x157100E8, 0x15720124, 0x157300E8, 0x15740019, 0x157600FD, 0x157700E8, 0x15780019, + 0x157C00FE, 0x157E0019, 0x15800054, 0x158A0078, 0x16000096, 0x16180098, 0x16300078, 0x16400063, + 0x16720055, 0x16730054, 0x16760078, 0x167D0006, 0x16800125, 0x16930078, 0x16980071, 0x16B30078, + 0x16C00071, 0x16CC0078, 0x16D00071, 0x16F00078, 0x17000006, 0x17010126, 0x17030006, 0x170500E0, + 0x17060126, 0x17070006, 0x170C0078, 0x170E0126, 0x170F0078, 0x17400054, 0x174D0078, 0x174E0054, + 0x177A0078, 0x17801054, 0x17EB0078, 0x17F80054, 0x17FE0078, 0x18008805, 0x18010006, 0x18020054, + 0x18030071, 0x18040009, 0x18090054, 0x180A0009, 0x180E0099, 0x180F00BF, 0x18100054, 0x18110127, + 0x18120128, 0x18130129, 0x1814012A, 0x18150083, 0x18180099, 0x18190081, 0x181B1054, 0x181C112B, + 0x181D112C, 0x181E0071, 0x181F0054, 0x18200078, 0x18210071, 0x18260871, 0x18320071, 0x18380871, + 0x18390071, 0x183A0871, 0x183C0071, 0x183D0871, 0x183F0071, 0x184A0871, 0x184B0071, 0x184C0078, + 0x184D0083, 0x184E1037, 0x184F0881, 0x18500099, 0x18510071, 0x18560871, 0x18620071, 0x18680871, + 0x18690071, 0x186A0871, 0x186C0071, 0x186D0871, 0x186F0071, 0x187A0871, 0x187B0071, 0x187C0871, + 0x187E0081, 0x187F0881, 0x18800078, 0x18830071, 0x18970078, 0x18991071, 0x18C80094, 0x18C978AD, + 0x18CA78AE, 0x18CB7894, 0x18D00071, 0x18DC0078, 0x18E00054, 0x18E80078, 0x18F80071, 0x19001094, + 0x190F1054, 0x191010AD, 0x191110AE, 0x1912112D, 0x1913112E, 0x1914112F, 0x19151094, 0x19220078, + 0x19286854, 0x19291930, 0x192A1931, 0x192B1932, 0x192C1933, 0x192D1934, 0x192E1935, 0x192F1936, + 0x19301894, 0x193E1854, 0x194018AD, 0x194118AE, 0x1942192D, 0x1943192E, 0x1944192F, 0x19451894, + 0x19591937, 0x195A1938, 0x195B1939, 0x195C193A, 0x195D193B, 0x195E193C, 0x195F193D, 0x19601094, + 0x19666854, 0x19681894, 0x19806894, 0x19AC1094, 0x19B96894, 0x19BC6854, 0x19BE6894, 0x19EF6854, + 0x19F01094, 0x1A000071, 0x26DB0078, 0x26E00054, 0x27000071, 0x4FDE0078, 0x50000071, 0x52470078, + 0x52480054, 0x52640078, 0x53800037, 0x538C0078, 0x54000071, 0x540100C8, 0x54020071, 0x54030083, + 0x54040071, 0x541200A7, 0x54130083, 0x54140054, 0x54160078, 0x56000071, 0x6BD20078, 0x6C00013E, + 0x7000013F, 0x7C800871, 0x7D070071, 0x7D080871, 0x7D0A0071, 0x7D0B0871, 0x7D120071, 0x7D130871, + 0x7D140071, 0x7D150871, 0x7D170078, 0x7D180871, 0x7D360078, 0x7D380871, 0x7D6D0078, 0x7D801055, + 0x7D840078, 0x7D8A1055, 0x7D8C0078, 0x7D8F0083, 0x7D90289B, 0x7D95089B, 0x7DA10078, 0x7DA2089B, + 0x7DA8409E, 0x7DAA389E, 0x7DAB409E, 0x7DAC389E, 0x7DAD409E, 0x7DAE389E, 0x7DAF409E, 0x7DB0389E, + 0x7DB1409E, 0x7DB2389E, 0x7DB3409E, 0x7DB4389E, 0x7DB5409E, 0x7DB6389E, 0x7DB7409E, 0x7DB8389E, + 0x7DB9409E, 0x7DBA389E, 0x7DBB409E, 0x7DBC389E, 0x7DBD409E, 0x7DBE389E, 0x7DBF409E, 0x7DC0389E, + 0x7DC1409E, 0x7DC8389E, 0x7DC9409E, 0x7DCA389E, 0x7DCB409E, 0x7DCC389E, 0x7DCD409E, 0x7DCE389E, + 0x7DCF409E, 0x7DD1389E, 0x7DD2409E, 0x7DD4389E, 0x7DD5409E, 0x7DD6389E, 0x7DD7409E, 0x7DD90078, + 0x7DEA209E, 0x7DEB489E, 0x7DEC209E, 0x7DEF409E, 0x7DF3389E, 0x7DF5409E, 0x7DFC389E, 0x7DFD209E, + 0x7DFE409E, 0x7DFF389E, 0x7E00409E, 0x7E32209E, 0x7E4C389E, 0x7E70489E, 0x7E7B409E, 0x7E89209E, + 0x7E97389E, 0x7E9A489E, 0x7E9E209E, 0x7E9F00B4, 0x7EA00078, 0x7EA8389E, 0x7EAC209E, 0x7EAE389E, + 0x7EAF209E, 0x7EB0389E, 0x7EB1209E, 0x7EB4389E, 0x7EB5209E, 0x7EB8389E, 0x7EBA209E, 0x7EC3389E, + 0x7EC80078, 0x7EC9389E, 0x7ECB209E, 0x7ECC389E, 0x7ECD209E, 0x7EDA389E, 0x7EDB209E, 0x7EDC389E, + 0x7EDE209E, 0x7EE2389E, 0x7EE3209E, 0x7EE40078, 0x7EF8409E, 0x7EFE4140, 0x7EFF0078, 0x7F000083, + 0x7F088006, 0x7F0C80BF, 0x7F0D0078, 0x7F100083, 0x7F120078, 0x7F188006, 0x7F198099, 0x7F1A8038, + 0x7F1B80BF, 0x7F230006, 0x7F2480BF, 0x7F251006, 0x7F271038, 0x7F28600C, 0x7F2A6006, 0x7F2C6099, + 0x7F2D60BF, 0x7F306006, 0x7F31600B, 0x7F326019, 0x7F346006, 0x7F356007, 0x7F360078, 0x7F38409E, + 0x7F41209E, 0x7F46489E, 0x7F47209E, 0x7F49489E, 0x7F4A209E, 0x7F4C489E, 0x7F4D209E, 0x7F4E489E, + 0x7F4F209E, 0x7F50489E, 0x7F51209E, 0x7F52489E, 0x7F53209E, 0x7F54489E, 0x7F55209E, 0x7F5A489E, + 0x7F5B209E, 0x7F5C489E, 0x7F5D209E, 0x7F5E489E, 0x7F5F209E, 0x7F60489E, 0x7F61209E, 0x7F62489E, + 0x7F63209E, 0x7F64489E, 0x7F65209E, 0x7F66489E, 0x7F67209E, 0x7F68489E, 0x7F69209E, 0x7F6A489E, + 0x7F6B209E, 0x7F6C489E, 0x7F6D209E, 0x7F6E489E, 0x7F6F209E, 0x7F70489E, 0x7F71209E, 0x7F72489E, + 0x7F73209E, 0x7F74489E, 0x7F75209E, 0x7F76489E, 0x7F77209E, 0x7F7A489E, 0x7F7B209E, 0x7F7F0078, + 0x7F818806, 0x7F828808, 0x7F838806, 0x7F848809, 0x7F858806, 0x7F86880C, 0x7F88880E, 0x7F898810, + 0x7F8A8812, 0x7F8B8814, 0x7F8C8816, 0x7F8D880C, 0x7F8E8818, 0x7F8F881A, 0x7F908806, 0x7F918860, + 0x7F9E8806, 0x7F9F8837, 0x7FA18861, 0x7FAE8819, 0x7FB0880A, 0x7FB15009, 0x7FB25006, 0x7FB35071, + 0x7FB85081, 0x7FB95071, 0x7FCF5081, 0x7FD05071, 0x7FE00078, 0x7FE15071, 0x7FE40078, 0x7FE55071, + 0x7FE80078, 0x7FE95071, 0x7FEC0078, 0x7FED5071, 0x7FEF0078, 0x7FF08808, 0x7FF18819, 0x7FF28854, + 0x7FF38808, 0x7FF45054, 0x7FF55019, 0x7FF75054, 0x7FF80078, 0x7FFD0141, 0x7FFE0054, 0x7FFF0078, + 0x80000071, 0x80060078, 0x80070071, 0x801F0078, 0x80200071, 0x80270078, 0x80280071, 0x802F0078, + 0x80400071, 0x807E0078, 0x80800097, 0x80810094, 0x80820078, 0x808400B6, 0x808500B7, 0x808600B8, + 0x808700B9, 0x808800B0, 0x808900BA, 0x808A00BB, 0x808B00BC, 0x808C00BD, 0x808D0142, 0x808E0143, + 0x808F0144, 0x80900145, 0x809100B1, 0x80920146, 0x80930147, 0x80940148, 0x80950149, 0x8096014A, + 0x8097014B, 0x8098014C, 0x8099014D, 0x809A0078, 0x809C0094, 0x80A0014E, 0x80A1014F, 0x80A20150, + 0x80A30151, 0x80A40152, 0x80A50150, 0x80A60153, 0x80A70151, 0x80A80154, 0x80A90155, 0x80AA0156, + 0x80AB0157, 0x80AC014F, 0x80AE0158, 0x80B00154, 0x80B30150, 0x80B50155, 0x80B60153, 0x80B90151, + 0x80BA0150, 0x80BB005F, 0x80BD0054, 0x80C500C3, 0x80C60078, 0x81800071, 0x819000AD, 0x819100B0, + 0x81920078, 0x81980071, 0x81A50159, 0x81A60078, 0x81C00071, 0x81CF0078, 0x81D00071, 0x81E20078, + 0x81E40071, 0x81E80094, 0x81E90158, 0x81EA015A, 0x81EB0078, 0x8200015B, 0x8214015C, 0x82280071, + 0x824F0078, 0x825000A8, 0x825100A9, 0x825200AA, 0x825300AB, 0x825400AC, 0x82550078, 0x8400009B, + 0x84030078, 0x8404009B, 0x841B0078, 0x841C009B, 0x841D0078, 0x841E009B, 0x841F0078, 0x8500009B, + 0x85010083, 0x85020078, 0x85030083, 0x85040078, 0x85060083, 0x8508009B, 0x850A0078, 0x850B009B, + 0x850C0078, 0x850D009B, 0x851A0078, 0x851C0083, 0x851E0078, 0x8520015D, 0x8521015E, 0x8522015F, + 0x85230160, 0x85240078, 0x8528009A, 0x852D0078, 0xE8000094, 0xE87B0078, 0xE8800094, 0xE8940078, + 0xE8950094, 0xE8AF0894, 0xE8B300A7, 0xE8B40083, 0xE8B50094, 0xE8B700A7, 0xE8BA0057, 0xE8BE0083, + 0xE8C20094, 0xE8C30083, 0xE8C60094, 0xE8D50083, 0xE8D70094, 0xE8DE0894, 0xE8E10094, 0xE8EF0078, + 0xE9000054, 0xE9210083, 0xE9230078, 0xE9800054, 0xE9AC0078, 0xEA002877, 0xEA0D2855, 0xEA1A2877, + 0xEA272855, 0xEA342877, 0xEA412855, 0xEA4E2877, 0xEA500078, 0xEA512877, 0xEA520078, 0xEA532877, + 0xEA540078, 0xEA552877, 0xEA5B2855, 0xEA5D0078, 0xEA5F2855, 0xEA620078, 0xEA632855, 0xEA682877, + 0xEA752855, 0xEA822877, 0xEA830078, 0xEA842877, 0xEA860078, 0xEA872877, 0xEA8F2855, 0xEA9C2877, + 0xEA9D0078, 0xEA9E2877, 0xEAA40078, 0xEAA52877, 0xEAA92855, 0xEAB62877, 0xEAC32855, 0xEAD02877, + 0xEADD2855, 0xEAEA2877, 0xEAF72855, 0xEB042877, 0xEB112855, 0xEB1E2877, 0xEB2B2855, 0xEB382877, + 0xEB452855, 0xEB530078, 0xEB542877, 0xEB612855, 0xEB712877, 0xEB7E2855, 0xEB8E2877, 0xEB9B2855, + 0xEBAB2877, 0xEBB82855, 0xEBC82877, 0xEBD52855, 0xEBE50078, 0xEBE7280E, 0xEBE82810, 0xEBE92812, + 0xEBEA2814, 0xEBEB2816, 0xEBEC280E, 0xEBED2810, 0xEBEE2812, 0xEBEF2814, 0xEBF02816, 0xEBF1280E, + 0xEBF22810, 0xEBF32812, 0xEBF42814, 0xEBF52816, 0xEBF6280E, 0xEBF72810, 0xEBF82812, 0xEBF92814, + 0xEBFA2816, 0xEBFB280E, 0xEBFC2810, 0xEBFD2812, 0xEBFE2814, 0xEBFF2816, 0xEC000078 + }; + + static const uint32_t a1[] = { + 0x00000071, 0x536C0078, 0x7C000871, 0x7D0F0078 + }; + + static const uint32_t a7[] = { + 0x00100057, 0x00400078, 0x00800083, 0x00F80078, 0x8000013F, 0xFFFF0078 + }; + + static const uint32_t a8[] = { + 0x0000013F, 0x7FFF0078 + }; + + static const uint32_t a16[] = { + 0x00800865, 0x00880065, 0x00890865, 0x00930065, 0x00940865, 0x00980161, 0x00991065, 0x009A0865, + 0x009C0863, 0x009F1063, 0x00A00063, 0x00A10863, 0x00A41055, 0x00A50065, 0x00A60865, 0x00A90065, + 0x00AA0865, 0x00B30065, 0x00B40865, 0x00BC0863, 0x00BF1162, 0x00C00163, 0x00C10065, 0x00C30063, + 0x00C40068, 0x00C50063, 0x00C60055, 0x00C70164, 0x00C80063, 0x00C90068, 0x00CA0165, 0x00CB0166, + 0x00CC0065, 0x00CD0055, 0x00CE0167, 0x00CF0168, 0x00D00865, 0x00D10065, 0x00D30063, 0x00D4006F, + 0x00D50055, 0x00D60065, 0x00D70863, 0x00D80070, 0x00D90063, 0x00DB0169, 0x00DC0065, 0x00DD0071, + 0x00DE0065, 0x00DF016A, 0x00E00071, 0x00E21074, 0x00E31072, 0x00E41073, 0x00E51074, 0x00E60863, + 0x00EE016B, 0x00EF0865, 0x00F20065, 0x00F30865, 0x00F81072, 0x00F91073, 0x00FA0865, 0x00FB016C, + 0x00FC0865, 0x010E0065, 0x010F0865, 0x01100055, 0x01110065, 0x01130865, 0x011A0055, 0x011D0063, + 0x011E016D, 0x011F0055, 0x0120016E, 0x01210078, 0x01280055, 0x0129016F, 0x012A0055, 0x012B007A, + 0x012C0170, 0x012D0171, 0x012E0055, 0x01310172, 0x01320055, 0x01340173, 0x01350055, 0x01370173, + 0x01380055, 0x013A0174, 0x013B0055, 0x0141007D, 0x01420055, 0x0145007E, 0x01460055, 0x01587881, + 0x015C0082, 0x015D0081, 0x01610037, 0x01630082, 0x01680081, 0x01690037, 0x016C1037, 0x016F0037, + 0x01707881, 0x01720037, 0x01800083, 0x01A00883, 0x01A20175, 0x01A30083, 0x01B80078, 0x01BA0037, + 0x01BB0078, 0x01C20837, 0x01C30806, 0x01C40885, 0x01C50078, 0x01C70887, 0x01C80060, 0x01D50860, + 0x01D60889, 0x01D80061, 0x01E50861, 0x01E6088C, 0x01E70078, 0x01E81176, 0x01E90877, 0x01EA1177, + 0x01EB0055, 0x01EC0065, 0x01F81093, 0x01F90055, 0x01FA1178, 0x01FB0063, 0x01FC10D8, 0x01FD0065, + 0x01FE0077, 0x02000892, 0x02020092, 0x02030892, 0x02040092, 0x02060892, 0x02070092, 0x02080060, + 0x020C0860, 0x020D0060, 0x02180061, 0x021C0861, 0x021D0061, 0x02280893, 0x022A0093, 0x022B0893, + 0x022C0093, 0x022E0893, 0x022F0093, 0x02300065, 0x023B0865, 0x023C0065, 0x02410083, 0x02430078, + 0x02440095, 0x02450065, 0x02600863, 0x02610063, 0x02670078, 0x02680865, 0x026A0065, 0x026B0865, + 0x026C0065, 0x026D0865, 0x02700065, 0x02710865, 0x02740065, 0x02750865, 0x027B0065, 0x027C0865, + 0x027D0078, 0x02800065, 0x02880078, 0x02980096, 0x02AB0078, 0x02AC0081, 0x02AD0097, 0x02B00098, + 0x02C31055, 0x02C40097, 0x02C50078, 0x02C80083, 0x02E1009A, 0x02E20083, 0x02E40078, 0x02E8009B, + 0x02F50078, 0x02F8009B, 0x02F9009A, 0x02FA0078, 0x0300009C, 0x03020078, 0x03050140, 0x0306009D, + 0x03070054, 0x03080083, 0x030B0078, 0x030D009D, 0x030E0078, 0x030F009D, 0x0310009E, 0x0311089E, + 0x0313009E, 0x031D0078, 0x0320009E, 0x03250083, 0x032F0078, 0x03300179, 0x0331017A, 0x0332017B, + 0x0333017C, 0x0334017D, 0x033500A5, 0x0336009D, 0x0337009E, 0x033A109E, 0x033C009E, 0x0369089E, + 0x036A009E, 0x036B0083, 0x036E009C, 0x036F0083, 0x0372009F, 0x03730083, 0x03740054, 0x03750083, + 0x0377009E, 0x0378000F, 0x03790011, 0x037A0013, 0x037B0015, 0x037C0017, 0x037D009E, 0x037E00A6, + 0x037F009E, 0x0380009D, 0x03870057, 0x03880083, 0x0389009E, 0x03980083, 0x03A50078, 0x03A6009E, + 0x03B70078, 0x03C0009E, 0x03D30083, 0x03D8009E, 0x03D90078, 0x04800083, 0x048100A7, 0x04820071, + 0x04940871, 0x04950071, 0x04980871, 0x04990071, 0x049D0078, 0x049E0071, 0x049F00A7, 0x04A00083, + 0x04A400A7, 0x04A60083, 0x04A70078, 0x04A80083, 0x04AA0078, 0x04AC0871, 0x04B00071, 0x04B10083, + 0x04B20097, 0x04B3017E, 0x04B4017F, 0x04B50180, 0x04B60181, 0x04B70182, 0x04B80078, 0x04BE0071, + 0x04BF0078, 0x04C00083, 0x04C100A7, 0x04C20071, 0x04C60078, 0x04C70071, 0x04C80078, 0x04C90071, + 0x04D40078, 0x04D50071, 0x04D80078, 0x04DB0071, 0x04DD0078, 0x04DE0071, 0x04DF00A7, 0x04E00083, + 0x04E20078, 0x04E300A7, 0x04E40078, 0x04E508A7, 0x04E60083, 0x04E70078, 0x04EB00A7, 0x04EC0078, + 0x04EE0871, 0x04F00071, 0x04F10083, 0x04F20078, 0x04F3017E, 0x04F4017F, 0x04F50180, 0x04F60181, + 0x04F70182, 0x04F80071, 0x04F90008, 0x04FA00B6, 0x04FB00B7, 0x04FC0183, 0x04FD0078, 0x05000083, + 0x050100A7, 0x05020071, 0x05050078, 0x05070071, 0x05080078, 0x05090071, 0x05140078, 0x05150071, + 0x05180078, 0x05190871, 0x051A0071, 0x051B0078, 0x051C0071, 0x051D0078, 0x051F00A7, 0x05200083, + 0x05210078, 0x05230083, 0x05240078, 0x05250083, 0x05270078, 0x052C0871, 0x052E0078, 0x0533017E, + 0x0534017F, 0x05350180, 0x05360181, 0x05370182, 0x05380083, 0x05390071, 0x053A0078, 0x05400083, + 0x054100A7, 0x05420071, 0x05540078, 0x05550071, 0x05580078, 0x05590071, 0x055D0078, 0x055E0071, + 0x055F00A7, 0x05600083, 0x056400A7, 0x05660083, 0x05670078, 0x05700071, 0x05710083, 0x05720078, + 0x0573017E, 0x0574017F, 0x05750180, 0x05760181, 0x05770182, 0x05780008, 0x05790078, 0x05800083, + 0x058100A7, 0x05820071, 0x05860078, 0x05870071, 0x05880078, 0x05890071, 0x05940078, 0x05950071, + 0x05980078, 0x05990071, 0x059D0078, 0x059E0071, 0x059F0083, 0x05A20078, 0x05A300A7, 0x05A40078, + 0x05A508A7, 0x05A60083, 0x05A70078, 0x05AB00A7, 0x05AC0078, 0x05AE0871, 0x05AF0071, 0x05B10078, + 0x05B3017E, 0x05B4017F, 0x05B50180, 0x05B60181, 0x05B70182, 0x05B80071, 0x05B90078, 0x05C10071, + 0x05C50078, 0x05C70071, 0x05C80078, 0x05C90071, 0x05CB0078, 0x05CC0071, 0x05CD0078, 0x05CF0071, + 0x05D00078, 0x05D10071, 0x05D20078, 0x05D40071, 0x05D50078, 0x05D70071, 0x05DD0078, 0x05DF00A7, + 0x05E10078, 0x05E300A7, 0x05E40078, 0x05E508A7, 0x05E60083, 0x05E70078, 0x05EB00A7, 0x05EC0078, + 0x05F3017E, 0x05F4017F, 0x05F50180, 0x05F60181, 0x05F70182, 0x05F80184, 0x05F90054, 0x05FC0008, + 0x05FD0078, 0x060000A7, 0x06020071, 0x06060078, 0x06070071, 0x06080078, 0x06090071, 0x06140078, + 0x06150071, 0x061D0078, 0x061F0083, 0x062000A7, 0x06220078, 0x06230083, 0x06240078, 0x06250083, + 0x06270078, 0x062A0083, 0x062B0078, 0x06300071, 0x06310078, 0x0633017E, 0x0634017F, 0x06350180, + 0x06360181, 0x06370182, 0x06380078, 0x064100A7, 0x06420071, 0x06460078, 0x06470071, 0x06480078, + 0x06490071, 0x06540078, 0x06550071, 0x065D0078, 0x065E0071, 0x065F00B2, 0x066000A7, 0x06620078, + 0x066308A7, 0x06640078, 0x066508A7, 0x06660083, 0x06670078, 0x066A00A7, 0x066B0078, 0x06700071, + 0x06710078, 0x0673017E, 0x0674017F, 0x06750180, 0x06760181, 0x06770182, 0x06780078, 0x068100A7, + 0x06820071, 0x06860078, 0x06870071, 0x06880078, 0x06890071, 0x06940078, 0x06950071, 0x069D0078, + 0x069F00A7, 0x06A00083, 0x06A20078, 0x06A300A7, 0x06A40078, 0x06A508A7, 0x06A60083, 0x06A70078, + 0x06AB00A7, 0x06AC0078, 0x06B00071, 0x06B10078, 0x06B3017E, 0x06B4017F, 0x06B50180, 0x06B60181, + 0x06B70182, 0x06B80078, 0x06C100A7, 0x06C20071, 0x06CB0078, 0x06CD0071, 0x06DF0078, 0x06E00071, + 0x06E30078, 0x06E700A7, 0x06E90083, 0x06EA0078, 0x06EC00A7, 0x06EE08A7, 0x06EF00A7, 0x06F00078, + 0x06F900A7, 0x06FA0078, 0x07000071, 0x07180083, 0x07191071, 0x071A0083, 0x071D0078, 0x071F0008, + 0x07200071, 0x07230083, 0x07270097, 0x0728017E, 0x0729017F, 0x072A0180, 0x072B0181, 0x072C0182, + 0x072D0097, 0x072E0078, 0x07400071, 0x07410078, 0x07430071, 0x07440078, 0x07460071, 0x07470078, + 0x074A0071, 0x07540078, 0x07550071, 0x07580083, 0x07591071, 0x075A0083, 0x075E0071, 0x075F0078, + 0x07600071, 0x07620078, 0x07640083, 0x07670078, 0x0768017E, 0x0769017F, 0x076A0180, 0x076B0181, + 0x076C0182, 0x076D0078, 0x076E1071, 0x076F0078, 0x07800094, 0x07820097, 0x07890094, 0x078C0083, + 0x078D0094, 0x0790017E, 0x0791017F, 0x07920180, 0x07930181, 0x07940182, 0x079500B3, 0x079A0083, + 0x079D00BF, 0x079F00A7, 0x07A00071, 0x07A10871, 0x07A20071, 0x07A60871, 0x07A70071, 0x07AB0871, + 0x07AC0071, 0x07B40871, 0x07B50078, 0x07B80083, 0x07B90883, 0x07BB1083, 0x07BD0083, 0x07BF00A7, + 0x07C00883, 0x07C10083, 0x07C20097, 0x07C30083, 0x07C40071, 0x07C60078, 0x07C80083, 0x07C90883, + 0x07CA0083, 0x07CE0883, 0x07CF0083, 0x07D30883, 0x07D40083, 0x07DC0883, 0x07DD0083, 0x07DE0078, + 0x07DF0094, 0x07E60078, 0x07E70094, 0x07E80097, 0x07E90078, 0x08000071, 0x08150078, 0x08160083, + 0x081800A7, 0x08190078, 0x081B0083, 0x081D0078, 0x0820017E, 0x0821017F, 0x08220180, 0x08230181, + 0x08240182, 0x08250097, 0x08280071, 0x082B00A7, 0x082C0083, 0x082D0078, 0x085000B5, 0x08630078, + 0x08680071, 0x087D0097, 0x087E0078, 0x08800071, 0x08AD0078, 0x08AF0071, 0x08D10078, 0x08D40071, + 0x08FD0078, 0x09000071, 0x09240078, 0x09250071, 0x09270078, 0x09280071, 0x092B0078, 0x092D0071, + 0x092F0078, 0x09300071, 0x09440078, 0x09450071, 0x09470078, 0x09480071, 0x09580078, 0x09590071, + 0x095B0078, 0x095C0071, 0x095F0078, 0x09610071, 0x09630078, 0x09640071, 0x096B0078, 0x096C0071, + 0x09880078, 0x09890071, 0x098B0078, 0x098C0071, 0x09AD0078, 0x09AF0083, 0x09B00097, 0x09B400AD, + 0x09B500AE, 0x09B6012D, 0x09B7012E, 0x09B8012F, 0x09B90185, 0x09BA0186, 0x09BB0187, 0x09BC0188, + 0x09BD0184, 0x09BE0078, 0x09C00071, 0x09C80054, 0x09CD0078, 0x09D00071, 0x09FA0078, 0x0A000071, + 0x0B360097, 0x0B370071, 0x0B3B0078, 0x0B400071, 0x0B4D00B4, 0x0B4E0078, 0x0B500071, 0x0B750097, + 0x0B770189, 0x0B780078, 0x0B800071, 0x0B860078, 0x0B870071, 0x0B890083, 0x0B8A0078, 0x0B900071, + 0x0B990083, 0x0B9A0097, 0x0B9B0078, 0x0BA00071, 0x0BA90083, 0x0BAA0078, 0x0BB00071, 0x0BB60078, + 0x0BB70071, 0x0BB80078, 0x0BB90083, 0x0BBA0078, 0x0BC00071, 0x0BDA00C2, 0x0BDB0083, 0x0BDF00A7, + 0x0BE40083, 0x0BEA0097, 0x0BEB0081, 0x0BEC0097, 0x0BED0008, 0x0BEE0083, 0x0BEF0078, 0x0BF0017E, + 0x0BF1017F, 0x0BF20180, 0x0BF30181, 0x0BF40182, 0x0BF50078, 0x0BF80106, 0x0BF90107, 0x0BFA0108, + 0x0BFB0109, 0x0BFC010A, 0x0BFD0078, 0x0C000006, 0x0C050083, 0x0C070078, 0x0C08017E, 0x0C09017F, + 0x0C0A0180, 0x0C0B0181, 0x0C0C0182, 0x0C0D0078, 0x0C100071, 0x0C210081, 0x0C220071, 0x0C3C0078, + 0x0C400071, 0x0C540083, 0x0C550078, 0x0C800071, 0x0C8E0078, 0x0C900083, 0x0C9100A7, 0x0C930083, + 0x0C9400C8, 0x0C960078, 0x0C9800A7, 0x0C9C0083, 0x0C9E0078, 0x0CA20006, 0x0CA3017E, 0x0CA4017F, + 0x0CA50180, 0x0CA60181, 0x0CA70182, 0x0CA80071, 0x0CB70078, 0x0CB80071, 0x0CBA0078, 0x0CC00071, + 0x0CD50078, 0x0CD800A7, 0x0CE00071, 0x0CE400A7, 0x0CE50078, 0x0CE8017E, 0x0CE9017F, 0x0CEA0180, + 0x0CEB0181, 0x0CEC0182, 0x0CED0078, 0x0CEF0006, 0x0CF00054, 0x0D000071, 0x0D0B0083, 0x0D0C00A7, + 0x0D0E0078, 0x0D0F0097, 0x0D100078, 0x0E800055, 0x0E967881, 0x0E970081, 0x0E987881, 0x0E9D0081, + 0x0E9E7881, 0x0EB17055, 0x0EB50055, 0x0ECD7881, 0x0EE00083, 0x0EE20078, 0x0F000865, 0x0F4B0855, + 0x0F4D098A, 0x0F4E0078, 0x0F500865, 0x0F7D0078, 0x0F8008C9, 0x0F8408CA, 0x0F8808C9, 0x0F8B0078, + 0x0F8C08CA, 0x0F8F0078, 0x0F9008C9, 0x0F9408CA, 0x0F9808C9, 0x0F9C08CA, 0x0FA008C9, 0x0FA30078, + 0x0FA408CA, 0x0FA70078, 0x0FA808C9, 0x0FAC08CA, 0x0FB008C9, 0x0FB408CA, 0x0FB808CB, 0x0FB908CC, + 0x0FBB08CD, 0x0FBC08CE, 0x0FBD08CF, 0x0FBE08D0, 0x0FBF0078, 0x0FC008C9, 0x0FC408D1, 0x0FC808C9, + 0x0FCC08D1, 0x0FD008C9, 0x0FD408D1, 0x0FD808C9, 0x0FD9098B, 0x0FDA0078, 0x0FDB0855, 0x0FDC08CA, + 0x0FDD08D2, 0x0FDE1037, 0x0FE00837, 0x0FE1098B, 0x0FE20078, 0x0FE30855, 0x0FE408D5, 0x0FE60837, + 0x0FE808C9, 0x0FE90855, 0x0FEA0078, 0x0FEB0855, 0x0FEC08CA, 0x0FED08D6, 0x0FEE0837, 0x0FF008C9, + 0x0FF10855, 0x0FF20890, 0x0FF30855, 0x0FF408CA, 0x0FF508D7, 0x0FF60837, 0x0FF80078, 0x0FF9098B, + 0x0FFA0078, 0x0FFB0855, 0x0FFC08D9, 0x0FFD08DA, 0x0FFE0837, 0x0FFF0078, 0x10000805, 0x10011005, + 0x10035805, 0x10041005, 0x10050057, 0x1007018C, 0x10085899, 0x10090099, 0x100B1006, 0x100C018D, + 0x100D00DB, 0x100E018D, 0x100F00DB, 0x10100006, 0x10121006, 0x10130006, 0x1014018E, 0x1015018F, + 0x10160190, 0x10175853, 0x10180007, 0x10191007, 0x101A0006, 0x101B1006, 0x101C0126, 0x101D0006, + 0x101F0038, 0x10200006, 0x10220009, 0x10231006, 0x10250006, 0x102B1006, 0x102C0006, 0x102F1005, + 0x10300057, 0x10320078, 0x10350057, 0x10387855, 0x10390078, 0x103A7910, 0x103B7911, 0x103C7912, + 0x103D780B, 0x103E7809, 0x103F7855, 0x1040705D, 0x1041705B, 0x10427110, 0x10437111, 0x10447112, + 0x1045700B, 0x10467009, 0x10470078, 0x10487081, 0x104A0078, 0x10500008, 0x105B0078, 0x10680083, + 0x106E0095, 0x10700083, 0x10710095, 0x10720083, 0x10760078, 0x10801054, 0x10831077, 0x10841054, + 0x10852877, 0x10872855, 0x10882877, 0x10892855, 0x108A2877, 0x108B0054, 0x108C2877, 0x108F0054, + 0x10901054, 0x10910054, 0x10950991, 0x10962877, 0x10972855, 0x10982877, 0x109A1071, 0x109C2855, + 0x109D1054, 0x109E2855, 0x109F2877, 0x10A00019, 0x10A22877, 0x10A32855, 0x10A50019, 0x10A60078, + 0x10A9305F, 0x10AF3106, 0x10B01192, 0x10B11193, 0x10B21194, 0x10B31195, 0x10B41196, 0x10B51197, + 0x10B61198, 0x10B71199, 0x10B8119A, 0x10B9119B, 0x10BA119C, 0x10BB119D, 0x10BC119E, 0x10BD119F, + 0x10BE11A0, 0x10BF11A1, 0x10C001A2, 0x10C101A3, 0x10C20078, 0x10C80019, 0x10CA0054, 0x10CD0819, + 0x10CE0054, 0x10D10019, 0x10D20054, 0x10E60854, 0x10E70819, 0x10E80054, 0x10FA0019, 0x110000E8, + 0x11020019, 0x110408FB, 0x110500FC, 0x11070019, 0x110800E8, 0x11090059, 0x110A01A4, 0x110B0019, + 0x110D00E8, 0x11110019, 0x111500E8, 0x111610E8, 0x111800E8, 0x111A0019, 0x111C00E8, 0x111E00FE, + 0x111F00E8, 0x112008E8, 0x112101A5, 0x112200E8, 0x112308E8, 0x112500E8, 0x11260019, 0x112900FE, + 0x112B0019, 0x112F00E8, 0x11300019, 0x113200FE, 0x11360819, 0x113708FE, 0x113900FE, 0x113A08FE, + 0x113B00FE, 0x113C08FE, 0x113D00FE, 0x114008FE, 0x114100FE, 0x114208FE, 0x114300FE, 0x114408FE, + 0x114500FE, 0x11460019, 0x114700FD, 0x11490019, 0x115100FE, 0x11520019, 0x115300E8, 0x115401A6, + 0x115608E8, 0x115800FE, 0x115C0019, 0x115F00E8, 0x11600019, 0x116400FD, 0x116601A7, 0x11670019, + 0x116800FE, 0x11690019, 0x116B00FE, 0x117008FE, 0x117200FE, 0x117508FE, 0x11770019, 0x117800FE, + 0x11790102, 0x117A00E8, 0x117B0103, 0x117C00E8, 0x117D0104, 0x117E0105, 0x117F00E8, 0x11800054, + 0x118400FE, 0x11860054, 0x119000E8, 0x11910054, 0x11940809, 0x11950054, 0x119B0094, 0x11BD0054, + 0x11CA0094, 0x11CB0054, 0x11CD0019, 0x11DA00BF, 0x11DB0054, 0x11EE0078, 0x12000054, 0x12130078, + 0x12200054, 0x12250078, 0x123018C4, 0x123118C5, 0x123218C6, 0x123318C7, 0x1234191F, 0x1235191A, + 0x1236191B, 0x1237191C, 0x1238191D, 0x1239191E, 0x123A10C4, 0x123B10C5, 0x123C10C6, 0x123D10C7, + 0x123E111F, 0x123F111A, 0x1240111B, 0x1241111C, 0x1242111D, 0x1243111E, 0x1244105A, 0x124510E3, + 0x124610E4, 0x124710E5, 0x124811A8, 0x124911A9, 0x124A11AA, 0x124B11AB, 0x124C11AC, 0x124D11AD, + 0x124E1094, 0x125B1918, 0x12681919, 0x1275010B, 0x1276010C, 0x1277010D, 0x1278010E, 0x1279010F, + 0x127A0106, 0x127B0107, 0x127C0108, 0x127D0109, 0x127E010A, 0x127F00C3, 0x12800054, 0x12DB0019, + 0x12DC0054, 0x12E00019, 0x12E10054, 0x12FC0019, 0x13000054, 0x13370019, 0x13380054, 0x134E0078, + 0x13500054, 0x13590078, 0x13800054, 0x13820078, 0x13830054, 0x13850078, 0x13860054, 0x13A90078, + 0x13AC0054, 0x13AF0078, 0x13B00054, 0x13B4000A, 0x13BB00C4, 0x13BC00C5, 0x13BD00C6, 0x13BE00C7, + 0x13BF011F, 0x13C000C4, 0x13C100C5, 0x13C200C6, 0x13C300C7, 0x13C4011F, 0x13C500C4, 0x13C600C5, + 0x13C700C6, 0x13C800C7, 0x13C9011F, 0x13CA0078, 0x13CC0054, 0x13DF0078, 0x13E00019, 0x13E100FD, + 0x13E20009, 0x13E30078, 0x13E80019, 0x13E900E8, 0x13EA00FD, 0x13EB0019, 0x13EE00FD, 0x13EF0019, + 0x13F100FE, 0x13F3000A, 0x13F60078, 0x13F80019, 0x14000094, 0x14800019, 0x14C10009, 0x14C601AE, + 0x14C701AF, 0x14C80009, 0x14CC0019, 0x14CD00E8, 0x14D80019, 0x14E000FE, 0x14E100E8, 0x14E200FE, + 0x14E30019, 0x14E400E8, 0x14E50019, 0x14E700FD, 0x14E90019, 0x14EA00FE, 0x14EB0019, 0x14EC000A, + 0x14EE0019, 0x14F000E8, 0x14F30019, 0x14F400E8, 0x14F50019, 0x14FA01B0, 0x14FB00E8, 0x14FC00FE, + 0x14FD0019, 0x14FE000A, 0x14FF0019, 0x150500E8, 0x150E0019, 0x150F00E8, 0x15110019, 0x151400E8, + 0x151500FD, 0x15170019, 0x151A00FE, 0x151B0019, 0x151E00FE, 0x151F0019, 0x152B00E8, 0x152C0019, + 0x153200FE, 0x15330019, 0x153500E8, 0x15380019, 0x153900E8, 0x153A1019, 0x153B0019, 0x153C00FD, + 0x153D00E8, 0x153E00FD, 0x154200E8, 0x154500FD, 0x154600E8, 0x154800FD, 0x154E00E8, 0x155000FD, + 0x155100E8, 0x15520019, 0x155300FE, 0x155700FD, 0x155800E8, 0x155900FD, 0x155A00E8, 0x155D00FD, + 0x156300E8, 0x156600FD, 0x156B0019, 0x157101B1, 0x15730019, 0x157600FE, 0x15770019, 0x157900E8, + 0x157A0019, 0x157B00FD, 0x157D00E8, 0x157F0019, 0x15800054, 0x158A0078, 0x16000096, 0x16170078, + 0x16180098, 0x162F0078, 0x16400065, 0x16720054, 0x16750078, 0x167C0006, 0x167E005F, 0x167F0006, + 0x16800125, 0x16930078, 0x16980071, 0x16B30078, 0x16B77881, 0x16B80078, 0x16C00071, 0x16CB0078, + 0x16D00071, 0x16D30078, 0x16D40071, 0x16D70078, 0x16D80071, 0x16DB0078, 0x16DC0071, 0x16DF0078, + 0x16E00071, 0x16E30078, 0x16E40071, 0x16E70078, 0x16E80071, 0x16EB0078, 0x16EC0071, 0x16EF0078, + 0x17000006, 0x170100E0, 0x17030006, 0x17040126, 0x17050006, 0x170600E0, 0x17070006, 0x170B0099, + 0x170C0078, 0x170E00E0, 0x170F0078, 0x17400054, 0x174F1054, 0x17500054, 0x17791054, 0x177A0078, + 0x17801054, 0x17EB0078, 0x17F80054, 0x17FE0078, 0x18000006, 0x18020081, 0x180301B2, 0x1804000A, + 0x18090054, 0x180A000A, 0x180E00B4, 0x180F00BF, 0x181001B3, 0x181101B4, 0x181201B5, 0x181301B6, + 0x181401B7, 0x18150083, 0x18180081, 0x181B0054, 0x181C11B8, 0x181D0081, 0x181E0006, 0x181F0054, + 0x18200071, 0x18320871, 0x18350071, 0x18380871, 0x183A0071, 0x183B0871, 0x183D0071, 0x183E0871, + 0x183F0071, 0x184B0078, 0x184C0083, 0x184D1037, 0x184E0081, 0x184F8071, 0x18500071, 0x18620871, + 0x18650071, 0x18680871, 0x186A0071, 0x186B0871, 0x186D0071, 0x186E0871, 0x186F0071, 0x187B0871, + 0x187D0006, 0x187E0081, 0x187F8071, 0x18800078, 0x18820071, 0x18960078, 0x18981071, 0x18C70078, + 0x18C80094, 0x18C978B6, 0x18CA78B7, 0x18CB7894, 0x18D00071, 0x18DC0078, 0x18E00054, 0x18E80078, + 0x18F80071, 0x19001094, 0x190E1054, 0x190F0078, 0x191010B6, 0x191110B7, 0x191210B8, 0x191310B9, + 0x191410B0, 0x19151094, 0x19220078, 0x192819B9, 0x192919BA, 0x192A19BB, 0x192B19BC, 0x192C19BD, + 0x192D19BE, 0x192E19BF, 0x192F19C0, 0x19301894, 0x193E1854, 0x193F0094, 0x194018B6, 0x194118B7, + 0x194218B8, 0x194318B9, 0x194418B0, 0x19451894, 0x195819C1, 0x195919C2, 0x195A19C3, 0x195B19C4, + 0x195C19C5, 0x195D19C6, 0x195E19C7, 0x195F19C8, 0x19601094, 0x19666854, 0x19681894, 0x197F0078, + 0x19806894, 0x19AC1094, 0x19B86894, 0x19BB6854, 0x19BD6894, 0x19EF6854, 0x19F01094, 0x19FF6854, + 0x1A000071, 0x26DB0078, 0x26E00054, 0x27000071, 0x4FDE0078, 0x50000071, 0x500A0081, 0x500B0071, + 0x52460078, 0x52480054, 0x52630078, 0x53800037, 0x538B0078, 0x54000071, 0x54050083, 0x54060071, + 0x541100A7, 0x54120083, 0x541300A7, 0x54140054, 0x54160078, 0x56000071, 0x6BD20078, 0x6C00013E, + 0x7000013F, 0x7C800871, 0x7D070071, 0x7D0A0871, 0x7D0F0071, 0x7D120871, 0x7D130071, 0x7D150871, + 0x7D170078, 0x7D180871, 0x7D350078, 0x7D380871, 0x7D6D0078, 0x7D801055, 0x7D830078, 0x7D891055, + 0x7D8C0078, 0x7D8E089B, 0x7D90289B, 0x7D94280B, 0x7D95089B, 0x7D9B0078, 0x7D9C089B, 0x7D9E0078, + 0x7DA0089B, 0x7DA20078, 0x7DA3089B, 0x7DA7109B, 0x7DA8209E, 0x7DAA489E, 0x7DAB209E, 0x7DAC489E, + 0x7DAD209E, 0x7DAE489E, 0x7DAF209E, 0x7DB0489E, 0x7DB1209E, 0x7DB2489E, 0x7DB3209E, 0x7DB4489E, + 0x7DB5209E, 0x7DB6489E, 0x7DB7209E, 0x7DB8489E, 0x7DB9209E, 0x7DBA489E, 0x7DBB209E, 0x7DBC489E, + 0x7DBD209E, 0x7DBE489E, 0x7DBF209E, 0x7DC0489E, 0x7DC1209E, 0x7DC8489E, 0x7DC9209E, 0x7DCA489E, + 0x7DCB209E, 0x7DCC489E, 0x7DCD209E, 0x7DCE489E, 0x7DCF209E, 0x7DD1489E, 0x7DD2209E, 0x7DD4489E, + 0x7DD5209E, 0x7DD6489E, 0x7DD7209E, 0x7DD90078, 0x7DE9409E, 0x7DEA389E, 0x7DEB409E, 0x7DEF209E, + 0x7DF3489E, 0x7DF5209E, 0x7DFC409E, 0x7DFD389E, 0x7DFE209E, 0x7DFF489E, 0x7E00409E, 0x7E32209E, + 0x7E4B389E, 0x7E6F489E, 0x7E7A409E, 0x7E88209E, 0x7E96389E, 0x7E9A489E, 0x7E9E409E, 0x7E9F00BF, + 0x7EA00078, 0x7EA8209E, 0x7EA9389E, 0x7EAD209E, 0x7EAE389E, 0x7EAF209E, 0x7EB0389E, 0x7EB3209E, + 0x7EB5389E, 0x7EB7209E, 0x7EB9389E, 0x7EBA209E, 0x7EBB389E, 0x7EBC209E, 0x7EBE389E, 0x7EBF209E, + 0x7EC1389E, 0x7EC2209E, 0x7EC4389E, 0x7EC5209E, 0x7EC6389E, 0x7EC80078, 0x7EC9389E, 0x7ECB209E, + 0x7ECE389E, 0x7ECF209E, 0x7EDA389E, 0x7EDB209E, 0x7EE1389E, 0x7EE3209E, 0x7EE40078, 0x7EF8409E, + 0x7EFE0054, 0x7EFF0078, 0x7F000083, 0x7F088006, 0x7F0B80B4, 0x7F0C8006, 0x7F0D0078, 0x7F100083, + 0x7F120078, 0x7F188099, 0x7F198038, 0x7F1A80B4, 0x7F220006, 0x7F2380B4, 0x7F241006, 0x7F261038, + 0x7F286006, 0x7F290078, 0x7F2A600C, 0x7F2B6006, 0x7F2C60B4, 0x7F2F6007, 0x7F306006, 0x7F31600D, + 0x7F326019, 0x7F330078, 0x7F346008, 0x7F356006, 0x7F360078, 0x7F38489E, 0x7F39009E, 0x7F3A0078, + 0x7F3B489E, 0x7F40409E, 0x7F45389E, 0x7F46409E, 0x7F48389E, 0x7F49409E, 0x7F4B389E, 0x7F4C409E, + 0x7F4D389E, 0x7F4E409E, 0x7F4F389E, 0x7F50409E, 0x7F51389E, 0x7F52409E, 0x7F53389E, 0x7F54409E, + 0x7F59389E, 0x7F5A409E, 0x7F5B389E, 0x7F5C409E, 0x7F5D389E, 0x7F5E409E, 0x7F5F389E, 0x7F60409E, + 0x7F61389E, 0x7F62409E, 0x7F63389E, 0x7F64409E, 0x7F65389E, 0x7F66409E, 0x7F67389E, 0x7F68409E, + 0x7F69389E, 0x7F6A409E, 0x7F6B389E, 0x7F6C409E, 0x7F6D389E, 0x7F6E409E, 0x7F6F389E, 0x7F70409E, + 0x7F71389E, 0x7F72409E, 0x7F73389E, 0x7F74409E, 0x7F75389E, 0x7F76409E, 0x7F79389E, 0x7F7A409E, + 0x7F7E0078, 0x7F7F0057, 0x7F808806, 0x7F818807, 0x7F838806, 0x7F84880A, 0x7F85880B, 0x7F86880D, + 0x7F87880C, 0x7F88880F, 0x7F898811, 0x7F8A8813, 0x7F8B8815, 0x7F8C8817, 0x7F8D8806, 0x7F8E8819, + 0x7F8F8806, 0x7F908860, 0x7F9D8835, 0x7F9E8836, 0x7F9F8838, 0x7FA08861, 0x7FAD8835, 0x7FAE8836, + 0x7FAF8809, 0x7FB05006, 0x7FB1500A, 0x7FB25006, 0x7FB35071, 0x7FCF5081, 0x7FD05071, 0x7FDF0078, + 0x7FE15071, 0x7FE40078, 0x7FE55071, 0x7FE80078, 0x7FE95071, 0x7FEC0078, 0x7FED5071, 0x7FEE0078, + 0x7FF08808, 0x7FF18837, 0x7FF28808, 0x7FF30078, 0x7FF45019, 0x7FF65054, 0x7FF70078, 0x7FFC0141, + 0x7FFE0054, 0x7FFF0078, 0x80000071, 0x80130078, 0x80140071, 0x801D0078, 0x801E0071, 0x80270078, + 0x80280071, 0x802F0078, 0x80400071, 0x807D0078, 0x80800006, 0x80810078, 0x808300AD, 0x808400AE, + 0x8085012D, 0x8086012E, 0x8087012F, 0x80880185, 0x80890186, 0x808A0187, 0x808B0188, 0x808C0184, + 0x808D01C9, 0x808E01CA, 0x808F01CB, 0x809001CC, 0x809101CD, 0x809201CE, 0x809301CF, 0x809401D0, + 0x809500BE, 0x809601D1, 0x809701D2, 0x809801D3, 0x809901D4, 0x809A0078, 0x809B0094, 0x80A0014E, + 0x80A10152, 0x80A20153, 0x80A30157, 0x80A40154, 0x80A50155, 0x80A60156, 0x80A70152, 0x80A80150, + 0x80A90153, 0x80AA01D5, 0x80AB0154, 0x80AC014F, 0x80AD0158, 0x80AF0152, 0x80B00154, 0x80B201D6, + 0x80B30150, 0x80B501D7, 0x80B60153, 0x80B80156, 0x80B90152, 0x80BA005F, 0x80BC0054, 0x80C50078, + 0x81800071, 0x818F0078, 0x8190012D, 0x819100BB, 0x81920078, 0x81980071, 0x81A50078, 0x81C00071, + 0x81CF0097, 0x81D00071, 0x81E20078, 0x81E40071, 0x81E8014F, 0x81E90154, 0x81EA0155, 0x81EB0078, + 0x8200015B, 0x8214015C, 0x82280071, 0x824F0078, 0x8250017E, 0x8251017F, 0x82520180, 0x82530181, + 0x82540182, 0x82550078, 0x8400009B, 0x84030078, 0x8405009B, 0x841C0078, 0x841F009B, 0x84200078, + 0x85000083, 0x85030078, 0x85060083, 0x8508009B, 0x851A0078, 0x851C0083, 0x851D0078, 0x851F0083, + 0x852001D8, 0x852101D9, 0x852201DA, 0x852301DB, 0x85240078, 0x8528009A, 0x852C0078, 0xE8000094, + 0xE87B0078, 0xE8800094, 0xE8930078, 0xE8950094, 0xE8AF0894, 0xE8B200A7, 0xE8B30083, 0xE8B50094, + 0xE8B600A7, 0xE8B90057, 0xE8BD0083, 0xE8C10094, 0xE8C20083, 0xE8C60094, 0xE8D50083, 0xE8D70094, + 0xE8DD0894, 0xE8E00094, 0xE8EF0078, 0xE9000054, 0xE9210083, 0xE9220054, 0xE9230078, 0xE9800054, + 0xE9AB0078, 0xEA002877, 0xEA0D2855, 0xEA1A2877, 0xEA272855, 0xEA2A0078, 0xEA2B2855, 0xEA342877, + 0xEA412855, 0xEA4E0078, 0xEA4F2877, 0xEA500078, 0xEA522877, 0xEA530078, 0xEA542877, 0xEA560078, + 0xEA572877, 0xEA5B2855, 0xEA682877, 0xEA752855, 0xEA822877, 0xEA850078, 0xEA862877, 0xEA8A0078, + 0xEA8B2877, 0xEA8E0078, 0xEA8F2855, 0xEA9C2877, 0xEA9F0078, 0xEAA02877, 0xEAA20078, 0xEAA52877, + 0xEAA80078, 0xEAA92855, 0xEAB62877, 0xEAC32855, 0xEAD02877, 0xEADD2855, 0xEAEA2877, 0xEAF72855, + 0xEB042877, 0xEB112855, 0xEB1E2877, 0xEB2B2855, 0xEB382877, 0xEB452855, 0xEB530078, 0xEB542877, + 0xEB6029DC, 0xEB612855, 0xEB6D29DC, 0xEB6E2855, 0xEB712877, 0xEB7D29DC, 0xEB7E2855, 0xEB8A29DC, + 0xEB8B2855, 0xEB8E2877, 0xEB9A29DC, 0xEB9B2855, 0xEBA729DC, 0xEBA82855, 0xEBAB2877, 0xEBB729DC, + 0xEBB82855, 0xEBC429DC, 0xEBC52855, 0xEBC82877, 0xEBD429DC, 0xEBD52855, 0xEBE129DC, 0xEBE22855, + 0xEBE50078, 0xEBE7280F, 0xEBE82811, 0xEBE92813, 0xEBEA2815, 0xEBEB2817, 0xEBEC280F, 0xEBED2811, + 0xEBEE2813, 0xEBEF2815, 0xEBF02817, 0xEBF1280F, 0xEBF22811, 0xEBF32813, 0xEBF42815, 0xEBF52817, + 0xEBF6280F, 0xEBF72811, 0xEBF82813, 0xEBF92815, 0xEBFA2817, 0xEBFB280F, 0xEBFC2811, 0xEBFD2813, + 0xEBFE2815, 0xEBFF2817, 0xEC000078 + }; + + static const uint32_t a17[] = { + 0x00000071, 0x536B0078, 0x7C000871, 0x7D0F0078 + }; + + static const uint32_t a23[] = { + 0x00000057, 0x00010078, 0x00100057, 0x00400078, 0x00800083, 0x00F80078, 0x8000013F, 0xFFFF0078 + }; + + static const uint32_t a24[] = { + 0x0000013F, 0x7FFF0078 + }; + + + // The full set of all arrays to be searched. + static const Range FULL_DATA[] = { + {sizeof(a0)/sizeof(uint32_t), a0}, + {sizeof(a1)/sizeof(uint32_t), a1}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {sizeof(a7)/sizeof(uint32_t), a7}, + {sizeof(a8)/sizeof(uint32_t), a8}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {sizeof(a16)/sizeof(uint32_t), a16}, + {sizeof(a17)/sizeof(uint32_t), a17}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {sizeof(a23)/sizeof(uint32_t), a23}, + {sizeof(a24)/sizeof(uint32_t), a24}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0} + }; + + // Array of uppercase differences + static const short UCDIFF[] = { + 0, -32, 743, 121, -1, -232, -300, 97, + 163, 130, 56, -2, -79, -210, -206, -205, + -202, -203, -207, -209, -211, -213, -214, -218, + -217, -219, -83, 84, -38, -37, -31, -64, + -63, -62, -57, -47, -54, -86, -80, 7, + -96, -48, -59, 8, 74, 86, 100, 128, + 112, 126, 9, -7205, -16, -26, -7264, -40 + }; + + // Array of lowercase differences + static const short LCDIFF[] = { + 0, 32, 1, -199, -121, 210, 206, 205, + 79, 202, 203, 207, 211, 209, 213, 214, + 218, 217, 219, 2, -97, -56, -130, -163, + 83, 38, 37, 64, 63, -60, -7, 80, + 48, 7264, -8, -74, -9, -86, -100, -112, + -128, -126, -7517, -8383, -8262, 16, 26, 40 + }; + + // Array of titlecase differences + static const short TCDIFF[] = { + 3, 1, 0, -1 + }; + + // Array of mirrored character differences + static const short MIRROR_DIFF[] = { + 0, 1, -1, 2, -2, 16, -16, 3, + -3, 2016, 138, 1824, 2104, 2108, 2106, -138, + 8, 7, -8, -7, -1824, -2016, -2104, -2106, + -2108 + }; + + // Array of all possible numeric values + static const int NUMERICS[] = { + -1, 0, 1, 2, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, + 23, 24, 25, 26, 27, 28, 29, 30, + 31, 32, 33, 34, 35, -2, 100, 1000, + 40, 50, 60, 70, 80, 90, 10000, 500, + 5000, 36, 37, 38, 39, 41, 42, 43, + 44, 45, 46, 47, 48, 49, 200, 300, + 400, 600, 700, 800, 900, 2000, 3000, 4000, + 6000, 7000, 8000, 9000, 20000, 30000, 40000, 50000, + 60000, 70000, 80000, 90000 + }; + + // All possible packed data values, no duplicates + static const uint32_t PACKED_DATA[] = { + 0x00000000, 0x0000012F, 0x0000016F, 0x0000014F, 0x0000018F, 0x0000018C, 0x000001B8, 0x000000B8, + 0x000000BA, 0x020005B5, 0x040005B6, 0x00000099, 0x000000F8, 0x00000094, 0x02000069, 0x04000069, + 0x06000069, 0x08000069, 0x0A000069, 0x0C000069, 0x0E000069, 0x10000069, 0x12000069, 0x14000069, + 0x060005B9, 0x000001B9, 0x080005B9, 0x16020001, 0x18020001, 0x1A020001, 0x1C020001, 0x1E020001, + 0x20020001, 0x22020001, 0x24020001, 0x26020001, 0x28020001, 0x2A020001, 0x2C020001, 0x2E020001, + 0x30020001, 0x32020001, 0x34020001, 0x36020001, 0x38020001, 0x3A020001, 0x3C020001, 0x3E020001, + 0x40020001, 0x42020001, 0x44020001, 0x46020001, 0x48020001, 0x060005B5, 0x080005B6, 0x000001BB, + 0x000001B7, 0x16000802, 0x18000802, 0x1A000802, 0x1C000802, 0x1E000802, 0x20000802, 0x22000802, + 0x24000802, 0x26000802, 0x28000802, 0x2A000802, 0x2C000802, 0x2E000802, 0x30000802, 0x32000802, + 0x34000802, 0x36000802, 0x38000802, 0x3A000802, 0x3C000802, 0x3E000802, 0x40000802, 0x42000802, + 0x44000802, 0x46000802, 0x48000802, 0x000000EC, 0x000001BC, 0x00000002, 0x0A0005BD, 0x00000130, + 0x000000BC, 0x000000B9, 0x0600006B, 0x0800006B, 0x00001002, 0x0400006B, 0x0C0005BE, 0x4A0001AB, + 0x00020001, 0x00000802, 0x00001802, 0x00040001, 0x00060001, 0x00002002, 0x00080001, 0x000C0001, + 0x000E0001, 0x00100001, 0x00140001, 0x00160001, 0x00180001, 0x00004002, 0x00004802, 0x00200001, + 0x00220001, 0x00000005, 0x00A60001, 0x01805802, 0x01042003, 0x00280001, 0x002C0001, 0x00000001, + 0x00000000, 0x00007002, 0x00007802, 0x00009802, 0x0000A802, 0x0000B802, 0x0000C002, 0x0000C802, + 0x0000D002, 0x00000004, 0x000001A4, 0x00000106, 0x00320001, 0x00340001, 0x00360001, 0x00380001, + 0x0000E002, 0x0000E802, 0x0000F002, 0x0000F802, 0x00010002, 0x00010802, 0x00012002, 0x00012802, + 0x00013802, 0x003A0001, 0x003E0001, 0x00013002, 0x0000001C, 0x00000107, 0x00400001, 0x00000018, + 0x00014802, 0x000001B4, 0x00000038, 0x00000025, 0x00000050, 0x00000058, 0x00000045, 0x00000044, + 0x020000C9, 0x060000C9, 0x0A0000C9, 0x0E0000C9, 0x120000C9, 0x000000D8, 0x0000005C, 0x00000008, + 0x02000009, 0x06000009, 0x0A000009, 0x0E000009, 0x12000009, 0x0400000B, 0x0800000B, 0x0000000B, + 0x1600000B, 0x4E00000B, 0x00000006, 0x4A00000B, 0x000001B5, 0x00420001, 0x0600000B, 0x0A00000B, + 0x0E00000B, 0x1200000B, 0x3E00000B, 0x5200000B, 0x5600000B, 0x5A00000B, 0x5C00000B, 0x000001B6, + 0x2400000A, 0x2800000A, 0x00000010, 0x020001AB, 0x060001AB, 0x0A0001AB, 0x0E0001AB, 0x120001AB, + 0x00000108, 0x00015802, 0x00440001, 0x00016002, 0x00016802, 0x00017002, 0x00017802, 0x00018002, + 0x00018802, 0x00440003, 0x00460001, 0x00480003, 0x00019802, 0x004A0001, 0x004C0001, 0x004E0001, + 0x003C0001, 0x00500001, 0x00520001, 0x000001BD, 0x0000018D, 0x000001D0, 0x00000250, 0x00000230, + 0x040005BE, 0x000000F9, 0x0200006B, 0x0A00006B, 0x0E00006B, 0x1200006B, 0x00540001, 0x00560001, + 0x000005B9, 0x045A000A, 0x085A000A, 0x0C5A000A, 0x105A000A, 0x145A000A, 0x185A000A, 0x525A000A, + 0x5E5A000A, 0x0401A00A, 0x0801A00A, 0x0C01A00A, 0x1001A00A, 0x1401A00A, 0x1801A00A, 0x5201A00A, + 0x5E01A00A, 0x4E00000A, 0x5C00000A, 0x0E0005B9, 0x100005B9, 0x020005B9, 0x040005B9, 0x160005B9, + 0x180005B9, 0x1A0005B9, 0x200005B9, 0x220005B9, 0x240005B9, 0x260005B9, 0x040001AB, 0x080001AB, + 0x0C0001AB, 0x100001AB, 0x140001AB, 0x180001AB, 0x1C0001AB, 0x200001AB, 0x240001AB, 0x280001AB, + 0x0C00006B, 0x1000006B, 0x1400006B, 0x1800006B, 0x1C00006B, 0x2000006B, 0x2400006B, 0x2800006B, + 0x005C001C, 0x0001A81C, 0x1A0001AB, 0x1E0001AB, 0x220001AB, 0x260001AB, 0x2A0001AB, 0x160001AB, + 0x020005B6, 0x100005B6, 0x280005B9, 0x2C0005B9, 0x300005B9, 0x0001B002, 0x020005BD, 0x0600000A, + 0x0A00000A, 0x0E00000A, 0x1200000A, 0x1600000A, 0x3E00000A, 0x0C00000B, 0x1000000B, 0x1400000B, + 0x2E0001AB, 0x320001AB, 0x360001AB, 0x3A0001AB, 0x3E0001AB, 0x420001AB, 0x460001AB, 0x640001AB, + 0x680001AB, 0x6A0001AB, 0x6E0001AB, 0x720001AB, 0x760001AB, 0x7A0001AB, 0x00000013, 0x00000012, + 0x0000005A, 0x000001B0, 0x7C00000B, 0x8000000B, 0x8200000B, 0x8600000B, 0x8C00000B, 0x6000000B, + 0x9200000B, 0x9600000B, 0x9800000B, 0x9C00000B, 0xA000000B, 0xA400000B, 0x4A0001AA, 0x040001AA, + 0x520001AA, 0x600001AA, 0x0C0001AA, 0x5E0001AA, 0x160001AA, 0x4C0001AA, 0x4E0001AA, 0x9E0001AA, + 0x060001AA, 0x8800000A, 0x2A0001AA, 0x005E0001, 0x0001B802, 0x0400002B, 0x0800002B, 0x1600002B, + 0x4C00002B, 0x00002802, 0x00003002, 0x000A0001, 0x00120001, 0x00003802, 0x001A0001, 0x001C0001, + 0x001E0001, 0x00240001, 0x00005002, 0x00006002, 0x002A0001, 0x002E0001, 0x00300001, 0x00006802, + 0x00008002, 0x00008802, 0x00009002, 0x0000A002, 0x0000B002, 0x0000D906, 0x00011002, 0x00011802, + 0x00014002, 0x040000C9, 0x080000C9, 0x0C0000C9, 0x100000C9, 0x140000C9, 0x04000009, 0x08000009, + 0x0C000009, 0x10000009, 0x14000009, 0x2200000B, 0x4C00000B, 0x2A00000B, 0x5000000B, 0x5400000B, + 0x5800000B, 0x2600000A, 0x00015002, 0x00019002, 0x00000030, 0x000001BE, 0x0000014E, 0x00000210, + 0x000001F0, 0x00580001, 0x065A000A, 0x0A5A000A, 0x0E5A000A, 0x125A000A, 0x165A000A, 0x1A5A000A, + 0x4C5A000A, 0x4E5A000A, 0x0601A00A, 0x0A01A00A, 0x0E01A00A, 0x1201A00A, 0x1601A00A, 0x1A01A00A, + 0x4C01A00A, 0x4E01A00A, 0x6000000A, 0x0000000A, 0x120005B9, 0x140005B9, 0x1C0005B9, 0x1E0005B9, + 0x1600006B, 0x1A00006B, 0x1E00006B, 0x2200006B, 0x2600006B, 0x2A00006B, 0x0E0005B5, 0x040005B5, + 0x2A0005B9, 0x2E0005B9, 0x0200000A, 0x0400000A, 0x0800000A, 0x0C00000A, 0x1000000A, 0x1400000A, + 0x2A00000A, 0x2C0001AB, 0x300001AB, 0x340001AB, 0x380001AB, 0x3C0001AB, 0x400001AB, 0x440001AB, + 0x480001AB, 0x620001AB, 0x660001AB, 0x500001AB, 0x6C0001AB, 0x700001AB, 0x740001AB, 0x780001AB, + 0x520001AB, 0x7E00000B, 0x5E00000B, 0x8400000B, 0x8800000B, 0x8A00000B, 0x8E00000B, 0x9000000B, + 0x9400000B, 0x9A00000B, 0x9E00000B, 0xA200000B, 0xA600000B, 0x5C0001AA, 0x3E0001AA, 0x7E0001AA, + 0x0600002B, 0x0A00002B, 0x2A00002B, 0x4E00002B, 0x00000019 + }; +} diff --git a/libs/utils/executablepath_darwin.cpp b/libs/utils/executablepath_darwin.cpp new file mode 100644 index 000000000..2e3c3a01f --- /dev/null +++ b/libs/utils/executablepath_darwin.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#import +#include + +void executablepath(char s[PATH_MAX]) +{ + ProcessSerialNumber psn; + GetCurrentProcess(&psn); + CFDictionaryRef dict; + dict = ProcessInformationCopyDictionary(&psn, 0xffffffff); + CFStringRef value = (CFStringRef)CFDictionaryGetValue(dict, + CFSTR("CFBundleExecutable")); + CFStringGetCString(value, s, PATH_MAX+1, kCFStringEncodingUTF8); +} + diff --git a/libs/utils/executablepath_linux.cpp b/libs/utils/executablepath_linux.cpp new file mode 100644 index 000000000..b8d2a3d6f --- /dev/null +++ b/libs/utils/executablepath_linux.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include + +void executablepath(char exe[PATH_MAX]) +{ + char proc[100]; + sprintf(proc, "/proc/%d/exe", getpid()); + + int err = readlink(proc, exe, PATH_MAX); +} + diff --git a/libs/utils/futex_synchro.c b/libs/utils/futex_synchro.c new file mode 100644 index 000000000..c13760d77 --- /dev/null +++ b/libs/utils/futex_synchro.c @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include +#include + +#include + +#include + + +// This futex glue code is need on desktop linux, but is part of klibc on ARM +#if !defined(__arm__) + +#include +typedef unsigned int u32; +#define asmlinkage +#define __user +#include +#include + + +int futex (int *uaddr, int op, int val, const struct timespec *timeout, int *uaddr2, int val3) +{ + int err = syscall(SYS_futex, uaddr, op, val, timeout, uaddr2, val3); + return err == 0 ? 0 : -errno; +} + +int __futex_wait(volatile void *ftx, int val, const struct timespec *timeout) +{ + return futex((int*)ftx, FUTEX_WAIT, val, timeout, NULL, 0); +} + +int __futex_wake(volatile void *ftx, int count) +{ + return futex((int*)ftx, FUTEX_WAKE, count, NULL, NULL, 0); +} + +int __atomic_cmpxchg(int old, int _new, volatile int *ptr) +{ + return android_atomic_cmpxchg(old, _new, ptr); +} + +int __atomic_swap(int _new, volatile int *ptr) +{ + return android_atomic_swap(_new, ptr); +} + +int __atomic_dec(volatile int *ptr) +{ + return android_atomic_dec(ptr); +} + +#else // !defined(__arm__) + +int __futex_wait(volatile void *ftx, int val, const struct timespec *timeout); +int __futex_wake(volatile void *ftx, int count); + +int __atomic_cmpxchg(int old, int _new, volatile int *ptr); +int __atomic_swap(int _new, volatile int *ptr); +int __atomic_dec(volatile int *ptr); + +#endif // !defined(__arm__) + + +// lock states +// +// 0: unlocked +// 1: locked, no waiters +// 2: locked, maybe waiters + +void futex_mutex_init(futex_mutex_t *m) +{ + m->value = 0; +} + +int futex_mutex_lock(futex_mutex_t *m, unsigned msec) +{ + if(__atomic_cmpxchg(0, 1, &m->value) == 0) { + return 0; + } + if(msec == FUTEX_WAIT_INFINITE) { + while(__atomic_swap(2, &m->value) != 0) { + __futex_wait(&m->value, 2, 0); + } + } else { + struct timespec ts; + ts.tv_sec = msec / 1000; + ts.tv_nsec = (msec % 1000) * 1000000; + while(__atomic_swap(2, &m->value) != 0) { + if(__futex_wait(&m->value, 2, &ts) == -ETIMEDOUT) { + return -1; + } + } + } + return 0; +} + +int futex_mutex_trylock(futex_mutex_t *m) +{ + if(__atomic_cmpxchg(0, 1, &m->value) == 0) { + return 0; + } + return -1; +} + +void futex_mutex_unlock(futex_mutex_t *m) +{ + if(__atomic_dec(&m->value) != 1) { + m->value = 0; + __futex_wake(&m->value, 1); + } +} + +/* XXX *technically* there is a race condition that could allow + * XXX a signal to be missed. If thread A is preempted in _wait() + * XXX after unlocking the mutex and before waiting, and if other + * XXX threads call signal or broadcast UINT_MAX times (exactly), + * XXX before thread A is scheduled again and calls futex_wait(), + * XXX then the signal will be lost. + */ + +void futex_cond_init(futex_cond_t *c) +{ + c->value = 0; +} + +int futex_cond_wait(futex_cond_t *c, futex_mutex_t *m, unsigned msec) +{ + if(msec == FUTEX_WAIT_INFINITE){ + int oldvalue = c->value; + futex_mutex_unlock(m); + __futex_wait(&c->value, oldvalue, 0); + futex_mutex_lock(m, FUTEX_WAIT_INFINITE); + return 0; + } else { + int oldvalue = c->value; + struct timespec ts; + ts.tv_sec = msec / 1000; + ts.tv_nsec = (msec % 1000) * 1000000; + futex_mutex_unlock(m); + const int err = __futex_wait(&c->value, oldvalue, &ts); + futex_mutex_lock(m, FUTEX_WAIT_INFINITE); + return err; + } +} + +void futex_cond_signal(futex_cond_t *c) +{ + __atomic_dec(&c->value); + __futex_wake(&c->value, 1); +} + +void futex_cond_broadcast(futex_cond_t *c) +{ + __atomic_dec(&c->value); + __futex_wake(&c->value, INT_MAX); +} + diff --git a/libs/utils/misc.cpp b/libs/utils/misc.cpp new file mode 100644 index 000000000..dc89d15b6 --- /dev/null +++ b/libs/utils/misc.cpp @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2005 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. + */ + +// +// Miscellaneous utility functions. +// +#include + +#include +#include +#include +#include +#include + +using namespace android; + +namespace android { + +/* + * Like strdup(), but uses C++ "new" operator instead of malloc. + */ +char* strdupNew(const char* str) +{ + char* newStr; + int len; + + if (str == NULL) + return NULL; + + len = strlen(str); + newStr = new char[len+1]; + memcpy(newStr, str, len+1); + + return newStr; +} + +/* + * Concatenate an argument vector. + */ +char* concatArgv(int argc, const char* const argv[]) +{ + char* newStr = NULL; + int len, totalLen, posn, idx; + + /* + * First, figure out the total length. + */ + totalLen = idx = 0; + while (1) { + if (idx == argc || argv[idx] == NULL) + break; + if (idx) + totalLen++; // leave a space between args + totalLen += strlen(argv[idx]); + idx++; + } + + /* + * Alloc the string. + */ + newStr = new char[totalLen +1]; + if (newStr == NULL) + return NULL; + + /* + * Finally, allocate the string and copy data over. + */ + idx = posn = 0; + while (1) { + if (idx == argc || argv[idx] == NULL) + break; + if (idx) + newStr[posn++] = ' '; + + len = strlen(argv[idx]); + memcpy(&newStr[posn], argv[idx], len); + posn += len; + + idx++; + } + + assert(posn == totalLen); + newStr[posn] = '\0'; + + return newStr; +} + +/* + * Count the #of args in an argument vector. Don't count the final NULL. + */ +int countArgv(const char* const argv[]) +{ + int count = 0; + + while (argv[count] != NULL) + count++; + + return count; +} + + +#include +/* + * Get a file's type. + */ +FileType getFileType(const char* fileName) +{ + struct stat sb; + + if (stat(fileName, &sb) < 0) { + if (errno == ENOENT || errno == ENOTDIR) + return kFileTypeNonexistent; + else { + fprintf(stderr, "getFileType got errno=%d on '%s'\n", + errno, fileName); + return kFileTypeUnknown; + } + } else { + if (S_ISREG(sb.st_mode)) + return kFileTypeRegular; + else if (S_ISDIR(sb.st_mode)) + return kFileTypeDirectory; + else if (S_ISCHR(sb.st_mode)) + return kFileTypeCharDev; + else if (S_ISBLK(sb.st_mode)) + return kFileTypeBlockDev; + else if (S_ISFIFO(sb.st_mode)) + return kFileTypeFifo; +#ifdef HAVE_SYMLINKS + else if (S_ISLNK(sb.st_mode)) + return kFileTypeSymlink; + else if (S_ISSOCK(sb.st_mode)) + return kFileTypeSocket; +#endif + else + return kFileTypeUnknown; + } +} + +/* + * Get a file's modification date. + */ +time_t getFileModDate(const char* fileName) +{ + struct stat sb; + + if (stat(fileName, &sb) < 0) + return (time_t) -1; + + return sb.st_mtime; +} + +/* + * Round up to the next highest power of 2. + * + * Found on http://graphics.stanford.edu/~seander/bithacks.html. + */ +unsigned int roundUpPower2(unsigned int val) +{ + val--; + val |= val >> 1; + val |= val >> 2; + val |= val >> 4; + val |= val >> 8; + val |= val >> 16; + val++; + + return val; +} + +}; // namespace android + diff --git a/libs/utils/ported.cpp b/libs/utils/ported.cpp new file mode 100644 index 000000000..656e46f09 --- /dev/null +++ b/libs/utils/ported.cpp @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2005 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. + */ + +// +// Ports of standard functions that don't exist on a specific platform. +// +// Note these are NOT in the "android" namespace. +// +#include + +#if defined(NEED_GETTIMEOFDAY) || defined(NEED_USLEEP) +# include +# include +#endif + + +#if defined(NEED_GETTIMEOFDAY) +/* + * Replacement gettimeofday() for Windows environments (primarily MinGW). + * + * Ignores "tz". + */ +int gettimeofday(struct timeval* ptv, struct timezone* tz) +{ + long long nsTime; // time in 100ns units since Jan 1 1601 + FILETIME ft; + + if (tz != NULL) { + // oh well + } + + ::GetSystemTimeAsFileTime(&ft); + nsTime = (long long) ft.dwHighDateTime << 32 | + (long long) ft.dwLowDateTime; + // convert to time in usec since Jan 1 1970 + ptv->tv_usec = (long) ((nsTime / 10LL) % 1000000LL); + ptv->tv_sec = (long) ((nsTime - 116444736000000000LL) / 10000000LL); + + return 0; +} +#endif + +#if defined(NEED_USLEEP) +// +// Replacement usleep for Windows environments (primarily MinGW). +// +void usleep(unsigned long usec) +{ + // Win32 API function Sleep() takes milliseconds + ::Sleep((usec + 500) / 1000); +} +#endif + +#if 0 //defined(NEED_PIPE) +// +// Replacement pipe() command for MinGW +// +// The _O_NOINHERIT flag sets bInheritHandle to FALSE in the +// SecurityAttributes argument to CreatePipe(). This means the handles +// aren't inherited when a new process is created. The examples I've seen +// use it, possibly because there's a lot of junk going on behind the +// scenes. (I'm assuming "process" and "thread" are different here, so +// we should be okay spinning up a thread.) The recommended practice is +// to dup() the descriptor you want the child to have. +// +// It appears that unnamed pipes can't do non-blocking ("overlapped") I/O. +// You can't use select() either, since that only works on sockets. The +// Windows API calls that are useful here all operate on a HANDLE, not +// an integer file descriptor, and I don't think you can get there from +// here. The "named pipe" stuff is insane. +// +int pipe(int filedes[2]) +{ + return _pipe(filedes, 0, _O_BINARY | _O_NOINHERIT); +} +#endif + +#if defined(NEED_SETENV) +/* + * MinGW lacks these. For now, just stub them out so the code compiles. + */ +int setenv(const char* name, const char* value, int overwrite) +{ + return 0; +} +void unsetenv(const char* name) +{ +} +char* getenv(const char* name) +{ + return NULL; +} +#endif From 7810449ca1c0bbb7429188df96eab2dca8fbddca Mon Sep 17 00:00:00 2001 From: The Android Open Source Project Date: Wed, 17 Dec 2008 18:05:43 -0800 Subject: [PATCH 069/541] Code drop from //branches/cupcake/...@124589 --- include/utils/IPCThreadState.h | 3 ++ include/utils/MemoryHeapPmem.h | 25 ++++++++++++--- include/utils/ResourceTypes.h | 49 ++++++++++++++++++++++------ include/utils/TimeUtils.h | 5 +-- include/utils/string_array.h | 13 ++++++++ libs/utils/Android.mk | 8 +++++ libs/utils/CallStack.cpp | 58 +++++++++++++++++----------------- libs/utils/IPCThreadState.cpp | 27 ++++++++++++++-- libs/utils/LogSocket.cpp | 2 +- libs/utils/MemoryDealer.cpp | 22 +++++++------ libs/utils/MemoryHeapPmem.cpp | 54 +++++++++++++++++++++---------- libs/utils/ResourceTypes.cpp | 41 +++++++++++++++--------- libs/utils/futex_synchro.c | 6 ++-- 13 files changed, 221 insertions(+), 92 deletions(-) diff --git a/include/utils/IPCThreadState.h b/include/utils/IPCThreadState.h index 47043b817..0490fd3ec 100644 --- a/include/utils/IPCThreadState.h +++ b/include/utils/IPCThreadState.h @@ -20,6 +20,7 @@ #include #include #include +#include #ifdef HAVE_WIN32_PROC typedef int uid_t; @@ -92,6 +93,8 @@ private: void* cookie); const sp mProcess; + Vector mPendingStrongDerefs; + Vector mPendingWeakDerefs; Parcel mIn; Parcel mOut; diff --git a/include/utils/MemoryHeapPmem.h b/include/utils/MemoryHeapPmem.h index b694b202a..60335adae 100644 --- a/include/utils/MemoryHeapPmem.h +++ b/include/utils/MemoryHeapPmem.h @@ -23,7 +23,7 @@ #include #include #include -#include +#include namespace android { @@ -31,11 +31,21 @@ class MemoryHeapBase; // --------------------------------------------------------------------------- -class SubRegionMemory; - class MemoryHeapPmem : public HeapInterface, public MemoryHeapBase { public: + class MemoryPmem : public BnMemory { + public: + MemoryPmem(const sp& heap); + ~MemoryPmem(); + protected: + const sp& getHeap() const { return mClientHeap; } + private: + friend class MemoryHeapPmem; + virtual void revoke() = 0; + sp mClientHeap; + }; + MemoryHeapPmem(const sp& pmemHeap, uint32_t flags = IMemoryHeap::MAP_ONCE); ~MemoryHeapPmem(); @@ -51,11 +61,16 @@ public: /* revoke all allocations made by this heap */ virtual void revoke(); - + +private: + /* use this to create your own IMemory for mapMemory */ + virtual sp createMemory(size_t offset, size_t size); + void remove(const wp& memory); + private: sp mParentHeap; mutable Mutex mLock; - Vector< wp > mAllocations; + SortedVector< wp > mAllocations; }; diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h index 31b9aa84b..2d56e3e26 100644 --- a/include/utils/ResourceTypes.h +++ b/include/utils/ResourceTypes.h @@ -140,8 +140,6 @@ struct Res_png_9patch void serialize(void* outData); // Deserialize/Unmarshall the patch data static Res_png_9patch* deserialize(const void* data); - // Deserialize/Unmarshall the patch data into a newly malloc-ed block - static void deserialize(const void* data, Res_png_9patch* outData); // Compute the size of the serialized data structure size_t serializedSize(); }; @@ -860,6 +858,7 @@ struct ResTable_config KEYSHIDDEN_ANY = 0x0000, KEYSHIDDEN_NO = 0x0001, KEYSHIDDEN_YES = 0x0002, + KEYSHIDDEN_SOFT = 0x0003, }; union { @@ -989,11 +988,20 @@ struct ResTable_config return diffs; } - // Return true if 'this' is more specific than 'o'. + // Return true if 'this' is more specific than 'o'. Optionally, if + // 'requested' is null, then they will also be compared against the + // requested configuration and true will only be returned if 'this' + // is a better candidate than 'o' for the configuration. This assumes that + // match() has already been used to remove any configurations that don't + // match the requested configuration at all; if they are not first filtered, + // non-matching results can be considered better than matching ones. inline bool isBetterThan(const ResTable_config& o, const ResTable_config* requested = NULL) const { + // The order of the following tests defines the importance of one + // configuration parameter over another. Those tests first are more + // important, trumping any values in those following them. if (imsi != 0 && (!requested || requested->imsi != 0)) { - if (mcc != 0 && (!requested || requested->mcc!= 0)) { + if (mcc != 0 && (!requested || requested->mcc != 0)) { if (o.mcc == 0) { return true; } @@ -1034,9 +1042,24 @@ struct ResTable_config } } if (input != 0 && (!requested || requested->input != 0)) { - if ((inputFlags&MASK_KEYSHIDDEN) != 0 && (!requested - || (requested->inputFlags&MASK_KEYSHIDDEN) != 0)) { - if ((o.inputFlags&MASK_KEYSHIDDEN) == 0) { + const int keysHidden = inputFlags&MASK_KEYSHIDDEN; + const int reqKeysHidden = requested + ? requested->inputFlags&MASK_KEYSHIDDEN : 0; + if (keysHidden != 0 && reqKeysHidden != 0) { + const int oKeysHidden = o.inputFlags&MASK_KEYSHIDDEN; + //LOGI("isBetterThan keysHidden: cur=%d, given=%d, config=%d\n", + // keysHidden, oKeysHidden, reqKeysHidden); + if (oKeysHidden == 0) { + //LOGI("Better because 0!"); + return true; + } + // For compatibility, we count KEYSHIDDEN_NO as being + // the same as KEYSHIDDEN_SOFT. Here we disambiguate these + // may making an exact match more specific. + if (keysHidden == reqKeysHidden && oKeysHidden != reqKeysHidden) { + // The current configuration is an exact match, and + // the given one is not, so the current one is better. + //LOGI("Better because other not same!"); return true; } } @@ -1078,7 +1101,8 @@ struct ResTable_config return false; } - // Return true if 'this' matches the parameters in 'settings'. + // Return true if 'this' can be considered a match for the parameters in + // 'settings'. inline bool match(const ResTable_config& settings) const { if (imsi != 0) { if (settings.mcc != 0 && mcc != 0 @@ -1121,7 +1145,14 @@ struct ResTable_config const int setKeysHidden = settings.inputFlags&MASK_KEYSHIDDEN; if (setKeysHidden != 0 && keysHidden != 0 && keysHidden != setKeysHidden) { - return false; + // For compatibility, we count a request for KEYSHIDDEN_NO as also + // matching the more recent KEYSHIDDEN_SOFT. Basically + // KEYSHIDDEN_NO means there is some kind of keyboard available. + //LOGI("Matching keysHidden: have=%d, config=%d\n", keysHidden, setKeysHidden); + if (keysHidden != KEYSHIDDEN_NO || setKeysHidden != KEYSHIDDEN_SOFT) { + //LOGI("No match!"); + return false; + } } if (settings.keyboard != 0 && keyboard != 0 && keyboard != settings.keyboard) { diff --git a/include/utils/TimeUtils.h b/include/utils/TimeUtils.h index 30e533036..b19e02126 100644 --- a/include/utils/TimeUtils.h +++ b/include/utils/TimeUtils.h @@ -17,10 +17,11 @@ #ifndef ANDROID_TIME_H #define ANDROID_TIME_H +#include +#include #include #include #include -#include #include #include @@ -58,7 +59,7 @@ public: Time(); void switchTimezone(const char *timezone); - String8 format(const char *format) const; + String8 format(const char *format, const struct strftime_locale *locale) const; void format2445(short* buf, bool hasTime) const; String8 toString() const; void setToNow(); diff --git a/include/utils/string_array.h b/include/utils/string_array.h index ede0644cb..064dda224 100644 --- a/include/utils/string_array.h +++ b/include/utils/string_array.h @@ -111,6 +111,19 @@ public: return mArray[idx]; } + // + // Set entry N to specified string. + // [should use operator[] here] + // + void setEntry(int idx, const char* str) { + if (idx < 0 || idx >= mCurrent) + return; + delete[] mArray[idx]; + int len = strlen(str); + mArray[idx] = new char[len+1]; + memcpy(mArray[idx], str, len+1); + } + private: int mMax; int mCurrent; diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk index 4a68dc1f7..cdb8ca2d7 100644 --- a/libs/utils/Android.mk +++ b/libs/utils/Android.mk @@ -139,6 +139,14 @@ LOCAL_SHARED_LIBRARIES := \ liblog \ libcutils +ifneq ($(TARGET_SIMULATOR),true) +ifeq ($(TARGET_OS)-$(TARGET_ARCH),linux-x86) +# This is needed on x86 to bring in dl_iterate_phdr for CallStack.cpp +LOCAL_SHARED_LIBRARIES += \ + libdl +endif # linux-x86 +endif # sim + LOCAL_MODULE:= libutils #LOCAL_CFLAGS+= diff --git a/libs/utils/CallStack.cpp b/libs/utils/CallStack.cpp index 496866673..26fb22abc 100644 --- a/libs/utils/CallStack.cpp +++ b/libs/utils/CallStack.cpp @@ -79,35 +79,35 @@ int backtrace(const void** addrs, size_t ignore, size_t size) /*****************************************************************************/ static -const char *lookup_symbol(const void* addr, uint32_t *offset, char* name, size_t bufSize) +const char *lookup_symbol(const void* addr, void **offset, char* name, size_t bufSize) { #if HAVE_DLADDR - Dl_info info; - if (dladdr(addr, &info)) { - *offset = (uint32_t)info.dli_saddr; - return info.dli_sname; - } + Dl_info info; + if (dladdr(addr, &info)) { + *offset = info.dli_saddr; + return info.dli_sname; + } #endif - return NULL; + return NULL; } static int32_t linux_gcc_demangler(const char *mangled_name, char *unmangled_name, size_t buffersize) { - size_t out_len = 0; + size_t out_len = 0; #if HAVE_CXXABI - int status = 0; - char *demangled = abi::__cxa_demangle(mangled_name, 0, &out_len, &status); - if (status == 0) { - // OK - if (out_len < buffersize) memcpy(unmangled_name, demangled, out_len); - else out_len = 0; - free(demangled); - } else { - out_len = 0; - } + int status = 0; + char *demangled = abi::__cxa_demangle(mangled_name, 0, &out_len, &status); + if (status == 0) { + // OK + if (out_len < buffersize) memcpy(unmangled_name, demangled, out_len); + else out_len = 0; + free(demangled); + } else { + out_len = 0; + } #endif - return out_len; + return out_len; } /*****************************************************************************/ @@ -115,12 +115,12 @@ int32_t linux_gcc_demangler(const char *mangled_name, char *unmangled_name, size class MapInfo { struct mapinfo { struct mapinfo *next; - unsigned start; - unsigned end; + uint64_t start; + uint64_t end; char name[]; }; - const char *map_to_name(unsigned pc, const char* def) { + const char *map_to_name(uint64_t pc, const char* def) { mapinfo* mi = getMapInfoList(); while(mi) { if ((pc >= mi->start) && (pc < mi->end)) @@ -139,8 +139,8 @@ class MapInfo { if (line[20] != 'x') return 0; mi = (mapinfo*)malloc(sizeof(mapinfo) + (len - 47)); if (mi == 0) return 0; - mi->start = strtoul(line, 0, 16); - mi->end = strtoul(line + 9, 0, 16); + mi->start = strtoull(line, 0, 16); + mi->end = strtoull(line + 9, 0, 16); mi->next = 0; strcpy(mi->name, line + 49); return mi; @@ -184,7 +184,7 @@ public: } static const char *mapAddressToName(const void* pc, const char* def) { - return sMapInfo.map_to_name((unsigned)pc, def); + return sMapInfo.map_to_name((uint64_t)pc, def); } }; @@ -278,7 +278,7 @@ String8 CallStack::toStringSingleLevel(const char* prefix, int32_t level) const char tmp[256]; char tmp1[32]; char tmp2[32]; - uint32_t offs; + void *offs; const void* ip = mStack[level]; if (!ip) return res; @@ -291,14 +291,14 @@ String8 CallStack::toStringSingleLevel(const char* prefix, int32_t level) const if (name) { if (linux_gcc_demangler(name, tmp, 256) != 0) name = tmp; - snprintf(tmp1, 32, "0x%08x: <", (size_t)ip); - snprintf(tmp2, 32, ">+0x%08x", offs); + snprintf(tmp1, 32, "0x%p: <", ip); + snprintf(tmp2, 32, ">+0x%p", offs); res.append(tmp1); res.append(name); res.append(tmp2); } else { name = MapInfo::mapAddressToName(ip, ""); - snprintf(tmp, 256, "pc %08x %s", (size_t)ip, name); + snprintf(tmp, 256, "pc %p %s", ip, name); res.append(tmp); } res.append("\n"); diff --git a/libs/utils/IPCThreadState.cpp b/libs/utils/IPCThreadState.cpp index ca49d9a5b..04ae1424e 100644 --- a/libs/utils/IPCThreadState.cpp +++ b/libs/utils/IPCThreadState.cpp @@ -391,6 +391,29 @@ void IPCThreadState::joinThreadPool(bool isMain) status_t result; do { int32_t cmd; + + // When we've cleared the incoming command queue, process any pending derefs + if (mIn.dataPosition() >= mIn.dataSize()) { + size_t numPending = mPendingWeakDerefs.size(); + if (numPending > 0) { + for (size_t i = 0; i < numPending; i++) { + RefBase::weakref_type* refs = mPendingWeakDerefs[i]; + refs->decWeak(mProcess.get()); + } + mPendingWeakDerefs.clear(); + } + + numPending = mPendingStrongDerefs.size(); + if (numPending > 0) { + for (size_t i = 0; i < numPending; i++) { + BBinder* obj = mPendingStrongDerefs[i]; + obj->decStrong(mProcess.get()); + } + mPendingStrongDerefs.clear(); + } + } + + // now get the next command to be processed, waiting if necessary result = talkWithDriver(); if (result >= NO_ERROR) { size_t IN = mIn.dataAvail(); @@ -832,7 +855,7 @@ status_t IPCThreadState::executeCommand(int32_t cmd) LOG_REMOTEREFS("BR_RELEASE from driver on %p", obj); obj->printRefs(); } - obj->decStrong(mProcess.get()); + mPendingStrongDerefs.push(obj); break; case BR_INCREFS: @@ -853,7 +876,7 @@ status_t IPCThreadState::executeCommand(int32_t cmd) //LOG_ASSERT(refs->refBase() == obj, // "BR_DECREFS: object %p does not match cookie %p (expected %p)", // refs, obj, refs->refBase()); - refs->decWeak(mProcess.get()); + mPendingWeakDerefs.push(refs); break; case BR_ATTEMPT_ACQUIRE: diff --git a/libs/utils/LogSocket.cpp b/libs/utils/LogSocket.cpp index e64f794a8..55c1b99af 100644 --- a/libs/utils/LogSocket.cpp +++ b/libs/utils/LogSocket.cpp @@ -16,7 +16,7 @@ #ifndef HAVE_WINSOCK -#define SOCKETLOG +//#define SOCKETLOG #endif #ifdef SOCKETLOG diff --git a/libs/utils/MemoryDealer.cpp b/libs/utils/MemoryDealer.cpp index e6d1d180f..cf8201b85 100644 --- a/libs/utils/MemoryDealer.cpp +++ b/libs/utils/MemoryDealer.cpp @@ -387,21 +387,23 @@ SimpleMemory::~SimpleMemory() start = (start + pagesize-1) & ~(pagesize-1); end &= ~(pagesize-1); - void* const start_ptr = (void*)(intptr_t(getHeap()->base()) + start); - size_t size = end-start; + if (start < end) { + void* const start_ptr = (void*)(intptr_t(getHeap()->base()) + start); + size_t size = end-start; #ifndef NDEBUG - memset(start_ptr, 0xdf, size); + memset(start_ptr, 0xdf, size); #endif - -// MADV_REMOVE is not defined on Dapper based Goobuntu + + // MADV_REMOVE is not defined on Dapper based Goobuntu #ifdef MADV_REMOVE - if (size) { - int err = madvise(start_ptr, size, MADV_REMOVE); - LOGW_IF(err, "madvise(%p, %u, MADV_REMOVE) returned %s", - start_ptr, size, err<0 ? strerror(errno) : "Ok"); - } + if (size) { + int err = madvise(start_ptr, size, MADV_REMOVE); + LOGW_IF(err, "madvise(%p, %u, MADV_REMOVE) returned %s", + start_ptr, size, err<0 ? strerror(errno) : "Ok"); + } #endif + } } }; // namespace android diff --git a/libs/utils/MemoryHeapPmem.cpp b/libs/utils/MemoryHeapPmem.cpp index 1e5a1cc96..eba2b3055 100644 --- a/libs/utils/MemoryHeapPmem.cpp +++ b/libs/utils/MemoryHeapPmem.cpp @@ -38,9 +38,20 @@ namespace android { // --------------------------------------------------------------------------- -class MemoryHeapPmem; +MemoryHeapPmem::MemoryPmem::MemoryPmem(const sp& heap) + : BnMemory(), mClientHeap(heap) +{ +} -class SubRegionMemory : public BnMemory { +MemoryHeapPmem::MemoryPmem::~MemoryPmem() { + if (mClientHeap != NULL) { + mClientHeap->remove(this); + } +} + +// --------------------------------------------------------------------------- + +class SubRegionMemory : public MemoryHeapPmem::MemoryPmem { public: SubRegionMemory(const sp& heap, ssize_t offset, size_t size); virtual ~SubRegionMemory(); @@ -50,15 +61,14 @@ private: void revoke(); size_t mSize; ssize_t mOffset; - sp mClientHeap; }; SubRegionMemory::SubRegionMemory(const sp& heap, ssize_t offset, size_t size) - : mSize(size), mOffset(offset), mClientHeap(heap) + : MemoryHeapPmem::MemoryPmem(heap), mSize(size), mOffset(offset) { #ifndef NDEBUG - void* const start_ptr = (void*)(intptr_t(mClientHeap->base()) + offset); + void* const start_ptr = (void*)(intptr_t(getHeap()->base()) + offset); memset(start_ptr, 0xda, size); #endif @@ -80,7 +90,7 @@ sp SubRegionMemory::getMemory(ssize_t* offset, size_t* size) const { if (offset) *offset = mOffset; if (size) *size = mSize; - return mClientHeap; + return getHeap(); } SubRegionMemory::~SubRegionMemory() @@ -98,8 +108,9 @@ void SubRegionMemory::revoke() // promote() it. #if HAVE_ANDROID_OS - if (mClientHeap != NULL) { - int our_fd = mClientHeap->heapID(); + if (mSize != NULL) { + const sp& heap(getHeap()); + int our_fd = heap->heapID(); struct pmem_region sub; sub.offset = mOffset; sub.len = mSize; @@ -107,7 +118,7 @@ void SubRegionMemory::revoke() LOGE_IF(err<0, "PMEM_UNMAP failed (%s), " "mFD=%d, sub.offset=%lu, sub.size=%lu", strerror(errno), our_fd, sub.offset, sub.len); - mClientHeap.clear(); + mSize = 0; } #endif } @@ -157,10 +168,7 @@ MemoryHeapPmem::~MemoryHeapPmem() sp MemoryHeapPmem::mapMemory(size_t offset, size_t size) { - sp memory; - if (heapID() > 0) - memory = new SubRegionMemory(this, offset, size); - + sp memory = createMemory(offset, size); if (memory != 0) { Mutex::Autolock _l(mLock); mAllocations.add(memory); @@ -168,6 +176,15 @@ sp MemoryHeapPmem::mapMemory(size_t offset, size_t size) return memory; } +sp MemoryHeapPmem::createMemory( + size_t offset, size_t size) +{ + sp memory; + if (heapID() > 0) + memory = new SubRegionMemory(this, offset, size); + return memory; +} + status_t MemoryHeapPmem::slap() { #if HAVE_ANDROID_OS @@ -206,21 +223,26 @@ status_t MemoryHeapPmem::unslap() void MemoryHeapPmem::revoke() { - Vector< wp > allocations; + SortedVector< wp > allocations; { // scope for lock Mutex::Autolock _l(mLock); allocations = mAllocations; - mAllocations.clear(); } ssize_t count = allocations.size(); for (ssize_t i=0 ; i memory(allocations[i].promote()); + sp memory(allocations[i].promote()); if (memory != 0) memory->revoke(); } } +void MemoryHeapPmem::remove(const wp& memory) +{ + Mutex::Autolock _l(mLock); + mAllocations.remove(memory); +} + // --------------------------------------------------------------------------- }; // namespace android diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp index a5fe9fba5..5a09fb44a 100644 --- a/libs/utils/ResourceTypes.cpp +++ b/libs/utils/ResourceTypes.cpp @@ -164,7 +164,11 @@ void Res_png_9patch::fileToDevice() size_t Res_png_9patch::serializedSize() { - return sizeof(Res_png_9patch) + // The size of this struct is 32 bytes on the 32-bit target system + // 4 * int8_t + // 4 * int32_t + // 3 * pointer + return 32 + numXDivs * sizeof(int32_t) + numYDivs * sizeof(int32_t) + numColors * sizeof(uint32_t); @@ -180,8 +184,10 @@ void* Res_png_9patch::serialize() void Res_png_9patch::serialize(void * outData) { char* data = (char*) outData; - memmove(data, this, sizeof(Res_png_9patch)); - data += sizeof(Res_png_9patch); + memmove(data, &wasDeserialized, 4); // copy wasDeserialized, numXDivs, numYDivs, numColors + memmove(data + 12, &paddingLeft, 16); // copy paddingXXXX + data += 32; + memmove(data, this->xDivs, numXDivs * sizeof(int32_t)); data += numXDivs * sizeof(int32_t); memmove(data, this->yDivs, numYDivs * sizeof(int32_t)); @@ -189,27 +195,32 @@ void Res_png_9patch::serialize(void * outData) memmove(data, this->colors, numColors * sizeof(uint32_t)); } -Res_png_9patch* Res_png_9patch::deserialize(const void* inData) -{ - deserialize(inData, (Res_png_9patch*) inData); - return (Res_png_9patch*) inData; -} - -void Res_png_9patch::deserialize(const void* inData, Res_png_9patch* outData) { - Res_png_9patch* patch = (Res_png_9patch*) inData; +static void deserializeInternal(const void* inData, Res_png_9patch* outData) { + char* patch = (char*) inData; if (inData != outData) { - memcpy(outData, inData, patch->serializedSize()); + memmove(&outData->wasDeserialized, patch, 4); // copy wasDeserialized, numXDivs, numYDivs, numColors + memmove(&outData->paddingLeft, patch + 12, 4); // copy wasDeserialized, numXDivs, numYDivs, numColors } outData->wasDeserialized = true; char* data = (char*)outData; data += sizeof(Res_png_9patch); outData->xDivs = (int32_t*) data; - data += patch->numXDivs * sizeof(int32_t); + data += outData->numXDivs * sizeof(int32_t); outData->yDivs = (int32_t*) data; - data += patch->numYDivs * sizeof(int32_t); + data += outData->numYDivs * sizeof(int32_t); outData->colors = (uint32_t*) data; } +Res_png_9patch* Res_png_9patch::deserialize(const void* inData) +{ + if (sizeof(void*) != sizeof(int32_t)) { + LOGE("Cannot deserialize on non 32-bit system\n"); + return NULL; + } + deserializeInternal(inData, (Res_png_9patch*) inData); + return (Res_png_9patch*) inData; +} + // -------------------------------------------------------------------- // -------------------------------------------------------------------- // -------------------------------------------------------------------- @@ -3863,7 +3874,7 @@ void ResTable::print() const } for (size_t configIndex=0; configIndexconfigs[configIndex]; - if ((((int)type)&0x3) != 0) { + if ((((uint64_t)type)&0x3) != 0) { printf(" NON-INTEGER ResTable_type ADDRESS: %p\n", type); continue; } diff --git a/libs/utils/futex_synchro.c b/libs/utils/futex_synchro.c index c13760d77..ba1952033 100644 --- a/libs/utils/futex_synchro.c +++ b/libs/utils/futex_synchro.c @@ -25,8 +25,8 @@ #include -// This futex glue code is need on desktop linux, but is part of klibc on ARM -#if !defined(__arm__) +// This futex glue code is need on desktop linux, but is already part of bionic. +#if !defined(HAVE_FUTEX_WRAPPERS) #include typedef unsigned int u32; @@ -76,7 +76,7 @@ int __atomic_cmpxchg(int old, int _new, volatile int *ptr); int __atomic_swap(int _new, volatile int *ptr); int __atomic_dec(volatile int *ptr); -#endif // !defined(__arm__) +#endif // !defined(HAVE_FUTEX_WRAPPERS) // lock states From 046e40caa03beba3b8b03527959ce97df9372b6a Mon Sep 17 00:00:00 2001 From: The Android Open Source Project Date: Fri, 9 Jan 2009 17:51:23 -0800 Subject: [PATCH 070/541] auto import from //branches/cupcake/...@125939 --- include/utils/MemoryHeapBase.h | 5 ++++- include/utils/RefBase.h | 24 ++++++++++++++++++++++++ libs/utils/MemoryHeapBase.cpp | 25 +++++++++++++++---------- 3 files changed, 43 insertions(+), 11 deletions(-) diff --git a/include/utils/MemoryHeapBase.h b/include/utils/MemoryHeapBase.h index ff8973878..574acf4f9 100644 --- a/include/utils/MemoryHeapBase.h +++ b/include/utils/MemoryHeapBase.h @@ -32,7 +32,10 @@ class MemoryHeapBase : public virtual BnMemoryHeap public: enum { READ_ONLY = IMemoryHeap::READ_ONLY, - MAP_ONCE = IMemoryHeap::MAP_ONCE + MAP_ONCE = IMemoryHeap::MAP_ONCE, + // memory won't be mapped locally, but will be mapped in the remote + // process. + DONT_MAP_LOCALLY = 0x00000100 }; /* diff --git a/include/utils/RefBase.h b/include/utils/RefBase.h index e37b56f5b..cbda0fd80 100644 --- a/include/utils/RefBase.h +++ b/include/utils/RefBase.h @@ -17,6 +17,7 @@ #ifndef ANDROID_REF_BASE_H #define ANDROID_REF_BASE_H +#include #include #include @@ -142,6 +143,29 @@ private: // --------------------------------------------------------------------------- +template +class LightRefBase +{ +public: + inline LightRefBase() : mCount(0) { } + inline void incStrong(const void* id) const { + android_atomic_inc(&mCount); + } + inline void decStrong(const void* id) const { + if (android_atomic_dec(&mCount) == 1) { + delete static_cast(this); + } + } + +protected: + inline ~LightRefBase() { } + +private: + mutable volatile int32_t mCount; +}; + +// --------------------------------------------------------------------------- + template class sp { diff --git a/libs/utils/MemoryHeapBase.cpp b/libs/utils/MemoryHeapBase.cpp index 59963c92b..825172819 100644 --- a/libs/utils/MemoryHeapBase.cpp +++ b/libs/utils/MemoryHeapBase.cpp @@ -119,19 +119,24 @@ status_t MemoryHeapBase::mapfd(int fd, size_t size) // if it didn't work, let mmap() fail. } - void* base = (uint8_t*)mmap(0, size, - PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); - if (base == MAP_FAILED) { - LOGE("mmap(fd=%d, size=%u) failed (%s)", - fd, uint32_t(size), strerror(errno)); - close(fd); - return -errno; + if ((mFlags & DONT_MAP_LOCALLY) == 0) { + void* base = (uint8_t*)mmap(0, size, + PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); + if (base == MAP_FAILED) { + LOGE("mmap(fd=%d, size=%u) failed (%s)", + fd, uint32_t(size), strerror(errno)); + close(fd); + return -errno; + } + //LOGD("mmap(fd=%d, base=%p, size=%lu)", fd, base, size); + mBase = base; + mNeedUnmap = true; + } else { + mBase = 0; // not MAP_FAILED + mNeedUnmap = false; } - //LOGD("mmap(fd=%d, base=%p, size=%lu)", fd, base, size); mFD = fd; - mBase = base; mSize = size; - mNeedUnmap = true; return NO_ERROR; } From 7976caad7d3c1515b3a8f2e756fa62eb3d36722f Mon Sep 17 00:00:00 2001 From: The Android Open Source Project Date: Tue, 20 Jan 2009 14:03:58 -0800 Subject: [PATCH 071/541] auto import from //branches/cupcake/...@127101 --- include/utils/Parcel.h | 13 +++++++++ libs/utils/Parcel.cpp | 63 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 75 insertions(+), 1 deletion(-) diff --git a/include/utils/Parcel.h b/include/utils/Parcel.h index 7c451ab21..9087c4465 100644 --- a/include/utils/Parcel.h +++ b/include/utils/Parcel.h @@ -17,6 +17,7 @@ #ifndef ANDROID_PARCEL_H #define ANDROID_PARCEL_H +#include #include #include #include @@ -78,6 +79,9 @@ public: status_t writeString16(const char16_t* str, size_t len); status_t writeStrongBinder(const sp& val); status_t writeWeakBinder(const wp& val); + + // doesn't take ownership of the native_handle + status_t writeNativeHandle(const native_handle& handle); // Place a file descriptor into the parcel. The given fd must remain // valid for the lifetime of the parcel. @@ -108,6 +112,15 @@ public: const char16_t* readString16Inplace(size_t* outLen) const; sp readStrongBinder() const; wp readWeakBinder() const; + + + // if alloc is NULL, native_handle is allocated with malloc(), otherwise + // alloc is used. If the function fails, the effects of alloc() must be + // reverted by the caller. + native_handle* readNativeHandle( + native_handle* (*alloc)(void* cookie, int numFds, int ints), + void* cookie) const; + // Retrieve a file descriptor from the parcel. This returns the raw fd // in the parcel, which you do not own -- use dup() to get your own copy. diff --git a/libs/utils/Parcel.cpp b/libs/utils/Parcel.cpp index 3eca4b010..0eba0b07c 100644 --- a/libs/utils/Parcel.cpp +++ b/libs/utils/Parcel.cpp @@ -650,6 +650,26 @@ status_t Parcel::writeWeakBinder(const wp& val) return flatten_binder(ProcessState::self(), val, this); } +status_t Parcel::writeNativeHandle(const native_handle& handle) +{ + if (handle.version != sizeof(native_handle)) + return BAD_TYPE; + + status_t err; + err = writeInt32(handle.numFds); + if (err != NO_ERROR) return err; + + err = writeInt32(handle.numInts); + if (err != NO_ERROR) return err; + + for (int i=0 ; err==NO_ERROR && i Parcel::readWeakBinder() const return val; } + +native_handle* Parcel::readNativeHandle(native_handle* (*alloc)(void*, int, int), void* cookie) const +{ + int numFds, numInts; + status_t err; + err = readInt32(&numFds); + if (err != NO_ERROR) return 0; + err = readInt32(&numInts); + if (err != NO_ERROR) return 0; + + native_handle* h; + if (alloc == 0) { + size_t size = sizeof(native_handle) + sizeof(int)*(numFds + numInts); + h = (native_handle*)malloc(size); + h->version = sizeof(native_handle); + h->numFds = numFds; + h->numInts = numInts; + } else { + h = alloc(cookie, numFds, numInts); + if (h->version != sizeof(native_handle)) { + return 0; + } + } + + for (int i=0 ; err==NO_ERROR && idata[i] = readFileDescriptor(); + if (h->data[i] < 0) err = BAD_VALUE; + } + + err = read(h->data + numFds, sizeof(int)*numInts); + + if (err != NO_ERROR) { + if (alloc == 0) { + free(h); + } + h = 0; + } + return h; +} + + int Parcel::readFileDescriptor() const { const flat_binder_object* flat = readObject(true); @@ -923,7 +984,7 @@ const flat_binder_object* Parcel::readObject(bool nullMetaData) const = reinterpret_cast(mData+DPOS); mDataPos = DPOS + sizeof(flat_binder_object); if (!nullMetaData && (obj->cookie == NULL && obj->binder == NULL)) { - // When transfering a NULL object, we don't write it into + // When transferring a NULL object, we don't write it into // the object list, so we don't want to check for it when // reading. LOGV("readObject Setting data pos of %p to %d\n", this, mDataPos); From 13e4bed7dbfc5d785f774539021cf428ca851601 Mon Sep 17 00:00:00 2001 From: ralf Date: Thu, 5 Feb 2009 11:58:27 -0800 Subject: [PATCH 072/541] AAPT needs a buffer larger than 1 MB on the host to decode resources.arsc from SDK/android.jar. This leaves the asset unzip buffer to 1 MB on the device and 2 MB on the host. --- include/utils/Asset.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/utils/Asset.h b/include/utils/Asset.h index d8351f57c..453a2049a 100644 --- a/include/utils/Asset.h +++ b/include/utils/Asset.h @@ -62,7 +62,11 @@ public: enum { /* data larger than this does not get uncompressed into a buffer */ +#ifdef HAVE_ANDROID_OS UNCOMPRESS_DATA_MAX = 1 * 1024 * 1024 +#else + UNCOMPRESS_DATA_MAX = 2 * 1024 * 1024 +#endif }; /* From 60dcc00861a1810deea7b31968578010556d4f3a Mon Sep 17 00:00:00 2001 From: The Android Open Source Project Date: Tue, 10 Feb 2009 15:44:00 -0800 Subject: [PATCH 073/541] auto import from //branches/cupcake/...@130745 --- include/utils/ResourceTypes.h | 12 +++++------- libs/utils/Parcel.cpp | 15 ++++++++++----- libs/utils/ResourceTypes.cpp | 9 ++++++--- libs/utils/String8.cpp | 4 +++- 4 files changed, 24 insertions(+), 16 deletions(-) diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h index 2d56e3e26..d83a33cef 100644 --- a/include/utils/ResourceTypes.h +++ b/include/utils/ResourceTypes.h @@ -223,7 +223,7 @@ struct Res_value { // Number of bytes in this structure. uint16_t size; - + // Always set to 0. uint8_t res0; @@ -1131,10 +1131,8 @@ struct ResTable_config && orientation != settings.orientation) { return false; } - if (settings.density != 0 && density != 0 - && density != settings.density) { - return false; - } + // Density not taken into account, always match, no matter what + // density is specified for the resource if (settings.touchscreen != 0 && touchscreen != 0 && touchscreen != settings.touchscreen) { return false; @@ -1464,11 +1462,11 @@ public: * @return ssize_t Either a >= 0 table index or a negative error code. */ ssize_t getResource(uint32_t resID, Res_value* outValue, bool mayBeBag=false, - uint32_t* outSpecFlags=NULL) const; + uint32_t* outSpecFlags=NULL, ResTable_config* outConfig=NULL) const; inline ssize_t getResource(const ResTable_ref& res, Res_value* outValue, uint32_t* outSpecFlags=NULL) const { - return getResource(res.ident, outValue, outSpecFlags); + return getResource(res.ident, outValue, false, outSpecFlags, NULL); } ssize_t resolveReference(Res_value* inOutValue, diff --git a/libs/utils/Parcel.cpp b/libs/utils/Parcel.cpp index 0eba0b07c..0f4b64730 100644 --- a/libs/utils/Parcel.cpp +++ b/libs/utils/Parcel.cpp @@ -658,15 +658,20 @@ status_t Parcel::writeNativeHandle(const native_handle& handle) status_t err; err = writeInt32(handle.numFds); if (err != NO_ERROR) return err; - + err = writeInt32(handle.numInts); if (err != NO_ERROR) return err; - + for (int i=0 ; err==NO_ERROR && idata[i] = readFileDescriptor(); + h->data[i] = dup(readFileDescriptor()); if (h->data[i] < 0) err = BAD_VALUE; } diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp index 5a09fb44a..71e7cd722 100644 --- a/libs/utils/ResourceTypes.cpp +++ b/libs/utils/ResourceTypes.cpp @@ -1736,7 +1736,7 @@ bool ResTable::getResourceName(uint32_t resID, resource_name* outName) const } ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag, - uint32_t* outSpecFlags) const + uint32_t* outSpecFlags, ResTable_config* outConfig) const { if (mError != NO_ERROR) { return mError; @@ -1809,7 +1809,7 @@ ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag (const Res_value*)(((const uint8_t*)type) + offset); ResTable_config thisConfig; thisConfig.copyFromDtoH(type->config); - + if (outSpecFlags != NULL) { if (typeClass->typeSpecFlags != NULL) { *outSpecFlags |= dtohl(typeClass->typeSpecFlags[e]); @@ -1834,6 +1834,9 @@ ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag outValue->res0 = bestValue->res0; outValue->dataType = bestValue->dataType; outValue->data = dtohl(bestValue->data); + if (outConfig != NULL) { + *outConfig = bestItem; + } TABLE_NOISY(size_t len; printf("Found value: pkg=%d, type=%d, str=%s, int=%d\n", bestPackage->header->index, @@ -3484,7 +3487,7 @@ ssize_t ResTable::getEntry( ResTable_config thisConfig; thisConfig.copyFromDtoH(thisType->config); - + TABLE_GETENTRY(LOGI("Match entry 0x%x in type 0x%x (sz 0x%x): imsi:%d/%d=%d/%d lang:%c%c=%c%c cnt:%c%c=%c%c " "orien:%d=%d touch:%d=%d density:%d=%d key:%d=%d inp:%d=%d nav:%d=%d w:%d=%d h:%d=%d\n", entryIndex, typeIndex+1, dtohl(thisType->config.size), diff --git a/libs/utils/String8.cpp b/libs/utils/String8.cpp index ab843f6e7..c50d343a7 100644 --- a/libs/utils/String8.cpp +++ b/libs/utils/String8.cpp @@ -317,8 +317,10 @@ status_t String8::real_append(const char* other, size_t otherLen) ->editResize(myLen+otherLen+1); if (buf) { char* str = (char*)buf->data(); - memcpy(str+myLen, other, otherLen+1); mString = str; + str += myLen; + memcpy(str, other, otherLen); + str[otherLen] = '\0'; return NO_ERROR; } return NO_MEMORY; From dccb00bd4c9a5232f96a641b6749ab8d55b6c04d Mon Sep 17 00:00:00 2001 From: The Android Open Source Project Date: Fri, 13 Feb 2009 12:57:50 -0800 Subject: [PATCH 074/541] auto import from //branches/cupcake/...@131421 --- include/utils/logger.h | 46 ------------------------------------------ 1 file changed, 46 deletions(-) delete mode 100644 include/utils/logger.h diff --git a/include/utils/logger.h b/include/utils/logger.h deleted file mode 100644 index 3a08019a8..000000000 --- a/include/utils/logger.h +++ /dev/null @@ -1,46 +0,0 @@ -/* utils/logger.h -** -** Copyright 2007, The Android Open Source Project -** -** This file is dual licensed. It may be redistributed and/or modified -** under the terms of the Apache 2.0 License OR version 2 of the GNU -** General Public License. -*/ - -#ifndef _UTILS_LOGGER_H -#define _UTILS_LOGGER_H - -#include - -struct logger_entry { - uint16_t len; /* length of the payload */ - uint16_t __pad; /* no matter what, we get 2 bytes of padding */ - int32_t pid; /* generating process's pid */ - int32_t tid; /* generating process's tid */ - int32_t sec; /* seconds since Epoch */ - int32_t nsec; /* nanoseconds */ - char msg[0]; /* the entry's payload */ -}; - -#define LOGGER_LOG_MAIN "log/main" -#define LOGGER_LOG_RADIO "log/radio" -#define LOGGER_LOG_EVENTS "log/events" - -#define LOGGER_ENTRY_MAX_LEN (4*1024) -#define LOGGER_ENTRY_MAX_PAYLOAD \ - (LOGGER_ENTRY_MAX_LEN - sizeof(struct logger_entry)) - -#ifdef HAVE_IOCTL - -#include - -#define __LOGGERIO 0xAE - -#define LOGGER_GET_LOG_BUF_SIZE _IO(__LOGGERIO, 1) /* size of log */ -#define LOGGER_GET_LOG_LEN _IO(__LOGGERIO, 2) /* used log len */ -#define LOGGER_GET_NEXT_ENTRY_LEN _IO(__LOGGERIO, 3) /* next entry len */ -#define LOGGER_FLUSH_LOG _IO(__LOGGERIO, 4) /* flush log */ - -#endif // HAVE_IOCTL - -#endif /* _UTILS_LOGGER_H */ From 3fec108e6c2a90fbb1976f69407805ff653eb0e7 Mon Sep 17 00:00:00 2001 From: Rebecca Schultz Zavin Date: Fri, 13 Feb 2009 16:34:38 -0800 Subject: [PATCH 075/541] Need to dup file descriptor when reading from the binder for native handles When reading a native handle that has passed through the binder, the fds have to be duped to prevent them from getting closed when the binder object is destructed. Signed-off-by: Rebecca Schultz Zavin --- libs/utils/Parcel.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/libs/utils/Parcel.cpp b/libs/utils/Parcel.cpp index 0f4b64730..4225e6728 100644 --- a/libs/utils/Parcel.cpp +++ b/libs/utils/Parcel.cpp @@ -950,14 +950,13 @@ native_handle* Parcel::readNativeHandle(native_handle* (*alloc)(void*, int, int) return 0; } } - for (int i=0 ; err==NO_ERROR && idata[i] = dup(readFileDescriptor()); if (h->data[i] < 0) err = BAD_VALUE; } - + err = read(h->data + numFds, sizeof(int)*numInts); - + if (err != NO_ERROR) { if (alloc == 0) { free(h); From c739660fb7c06a845f829a86302bd4a91641318f Mon Sep 17 00:00:00 2001 From: The Android Open Source Project Date: Mon, 2 Mar 2009 22:54:33 -0800 Subject: [PATCH 076/541] auto import from //depot/cupcake/@137055 --- include/utils/ResourceTypes.h | 16 +++++++++++----- libs/utils/Threads.cpp | 4 +++- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h index d83a33cef..7d3fcf2a8 100644 --- a/include/utils/ResourceTypes.h +++ b/include/utils/ResourceTypes.h @@ -1101,16 +1101,22 @@ struct ResTable_config return false; } - // Return true if 'this' can be considered a match for the parameters in + // Return true if 'this' can be considered a match for the parameters in // 'settings'. + // Note this is asymetric. A default piece of data will match every request + // but a request for the default should not match odd specifics + // (ie, request with no mcc should not match a particular mcc's data) + // settings is the requested settings inline bool match(const ResTable_config& settings) const { if (imsi != 0) { - if (settings.mcc != 0 && mcc != 0 - && mcc != settings.mcc) { + if ((settings.mcc != 0 && mcc != 0 + && mcc != settings.mcc) || + (settings.mcc == 0 && mcc != 0)) { return false; } - if (settings.mnc != 0 && mnc != 0 - && mnc != settings.mnc) { + if ((settings.mnc != 0 && mnc != 0 + && mnc != settings.mnc) || + (settings.mnc == 0 && mnc != 0)) { return false; } } diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp index 74271ba3b..5f407a990 100644 --- a/libs/utils/Threads.cpp +++ b/libs/utils/Threads.cpp @@ -896,6 +896,7 @@ void ReadWriteLock::unlockForRead() { mLock.lock(); if (mNumReaders == 0) { + mLock.unlock(); LOG(LOG_WARN, "thread", "WARNING: unlockForRead requested, but not locked\n"); return; @@ -961,6 +962,7 @@ void ReadWriteLock::unlockForWrite() { mLock.lock(); if (mNumWriters == 0) { + mLock.unlock(); LOG(LOG_WARN, "thread", "WARNING: unlockForWrite requested, but not locked\n"); return; @@ -972,7 +974,7 @@ void ReadWriteLock::unlockForWrite() //printf(" wrlk held %.3f msec\n", // (double) mDebugTimer.durationUsecs() / 1000.0); #endif - // mWriteWaiter.signal(); // should other writers get first dibs? + mWriteWaiter.signal(); // should other writers get first dibs? //printf("+++ signaling readers (if any)\n"); mReadWaiter.broadcast(); // wake all readers (if any) mLock.unlock(); From 7aa707a5d654b7af67b133955c454c8e23a12abc Mon Sep 17 00:00:00 2001 From: The Android Open Source Project Date: Tue, 3 Mar 2009 14:04:24 -0800 Subject: [PATCH 077/541] auto import from //depot/cupcake/@132589 --- include/utils/ResourceTypes.h | 16 +++++----------- libs/utils/Threads.cpp | 4 +--- 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h index 7d3fcf2a8..d83a33cef 100644 --- a/include/utils/ResourceTypes.h +++ b/include/utils/ResourceTypes.h @@ -1101,22 +1101,16 @@ struct ResTable_config return false; } - // Return true if 'this' can be considered a match for the parameters in + // Return true if 'this' can be considered a match for the parameters in // 'settings'. - // Note this is asymetric. A default piece of data will match every request - // but a request for the default should not match odd specifics - // (ie, request with no mcc should not match a particular mcc's data) - // settings is the requested settings inline bool match(const ResTable_config& settings) const { if (imsi != 0) { - if ((settings.mcc != 0 && mcc != 0 - && mcc != settings.mcc) || - (settings.mcc == 0 && mcc != 0)) { + if (settings.mcc != 0 && mcc != 0 + && mcc != settings.mcc) { return false; } - if ((settings.mnc != 0 && mnc != 0 - && mnc != settings.mnc) || - (settings.mnc == 0 && mnc != 0)) { + if (settings.mnc != 0 && mnc != 0 + && mnc != settings.mnc) { return false; } } diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp index 5f407a990..74271ba3b 100644 --- a/libs/utils/Threads.cpp +++ b/libs/utils/Threads.cpp @@ -896,7 +896,6 @@ void ReadWriteLock::unlockForRead() { mLock.lock(); if (mNumReaders == 0) { - mLock.unlock(); LOG(LOG_WARN, "thread", "WARNING: unlockForRead requested, but not locked\n"); return; @@ -962,7 +961,6 @@ void ReadWriteLock::unlockForWrite() { mLock.lock(); if (mNumWriters == 0) { - mLock.unlock(); LOG(LOG_WARN, "thread", "WARNING: unlockForWrite requested, but not locked\n"); return; @@ -974,7 +972,7 @@ void ReadWriteLock::unlockForWrite() //printf(" wrlk held %.3f msec\n", // (double) mDebugTimer.durationUsecs() / 1000.0); #endif - mWriteWaiter.signal(); // should other writers get first dibs? + // mWriteWaiter.signal(); // should other writers get first dibs? //printf("+++ signaling readers (if any)\n"); mReadWaiter.broadcast(); // wake all readers (if any) mLock.unlock(); From cf59fa8dc7ddca5a172860223b06afed5d4ec0e0 Mon Sep 17 00:00:00 2001 From: The Android Open Source Project Date: Tue, 3 Mar 2009 18:28:45 -0800 Subject: [PATCH 078/541] auto import from //depot/cupcake/@135843 --- MODULE_LICENSE_APACHE2 | 0 NOTICE | 222 -- include/utils.h | 33 - include/utils/AndroidUnicode.h | 255 -- include/utils/Asset.h | 315 -- include/utils/AssetDir.h | 145 - include/utils/AssetManager.h | 323 -- include/utils/Atomic.h | 22 - include/utils/Binder.h | 103 - include/utils/BpBinder.h | 122 - include/utils/Buffer.h | 107 - include/utils/BufferedTextOutput.h | 67 - include/utils/ByteOrder.h | 69 - include/utils/CallStack.h | 76 - include/utils/Debug.h | 45 - include/utils/Endian.h | 40 - include/utils/Errors.h | 87 - include/utils/FileMap.h | 134 - include/utils/IBinder.h | 159 - include/utils/IInterface.h | 135 - include/utils/IMemory.h | 94 - include/utils/IPCThreadState.h | 110 - include/utils/IPermissionController.h | 56 - include/utils/IServiceManager.h | 98 - include/utils/KeyedVector.h | 201 -- include/utils/List.h | 280 -- include/utils/Log.h | 33 - include/utils/LogSocket.h | 20 - include/utils/MemoryBase.h | 51 - include/utils/MemoryDealer.h | 238 -- include/utils/MemoryHeapBase.h | 98 - include/utils/MemoryHeapPmem.h | 80 - include/utils/Parcel.h | 209 -- include/utils/Pipe.h | 108 - include/utils/ProcessState.h | 117 - include/utils/RefBase.h | 550 ---- include/utils/ResourceTypes.h | 1714 ----------- include/utils/SharedBuffer.h | 146 - include/utils/Socket.h | 80 - include/utils/SortedVector.h | 282 -- include/utils/StopWatch.h | 62 - include/utils/String16.h | 260 -- include/utils/String8.h | 353 --- include/utils/SystemClock.h | 32 - include/utils/TextOutput.h | 190 -- include/utils/TimeUtils.h | 89 - include/utils/TimerProbe.h | 72 - include/utils/Timers.h | 137 - include/utils/TypeHelpers.h | 254 -- include/utils/Vector.h | 359 --- include/utils/VectorImpl.h | 199 -- include/utils/ZipEntry.h | 345 --- include/utils/ZipFile.h | 269 -- include/utils/ZipFileCRO.h | 59 - include/utils/ZipFileRO.h | 222 -- include/utils/ZipUtils.h | 67 - include/utils/ashmem.h | 41 - include/utils/executablepath.h | 28 - include/utils/inet_address.h | 103 - include/utils/misc.h | 93 - include/utils/ported.h | 50 - include/utils/string_array.h | 135 - include/utils/threads.h | 347 --- libs/utils/Android.mk | 156 - libs/utils/Asset.cpp | 813 ----- libs/utils/AssetDir.cpp | 66 - libs/utils/AssetManager.cpp | 1637 ---------- libs/utils/Binder.cpp | 242 -- libs/utils/BpBinder.cpp | 348 --- libs/utils/BufferedTextOutput.cpp | 279 -- libs/utils/CallStack.cpp | 335 --- libs/utils/Debug.cpp | 318 -- libs/utils/FileMap.cpp | 222 -- libs/utils/IDataConnection.cpp | 89 - libs/utils/IInterface.cpp | 35 - libs/utils/IMemory.cpp | 486 --- libs/utils/IPCThreadState.cpp | 1030 ------- libs/utils/IPermissionController.cpp | 86 - libs/utils/IServiceManager.cpp | 230 -- libs/utils/InetAddress.cpp | 236 -- libs/utils/LogSocket.cpp | 129 - libs/utils/MODULE_LICENSE_APACHE2 | 0 libs/utils/MemoryBase.cpp | 46 - libs/utils/MemoryDealer.cpp | 409 --- libs/utils/MemoryHeapBase.cpp | 183 -- libs/utils/MemoryHeapPmem.cpp | 248 -- libs/utils/NOTICE | 190 -- libs/utils/Parcel.cpp | 1376 --------- libs/utils/Pipe.cpp | 465 --- libs/utils/ProcessState.cpp | 398 --- libs/utils/README | 14 - libs/utils/RefBase.cpp | 534 ---- libs/utils/ResourceTypes.cpp | 3983 ------------------------- libs/utils/SharedBuffer.cpp | 113 - libs/utils/Socket.cpp | 388 --- libs/utils/Static.cpp | 120 - libs/utils/StopWatch.cpp | 79 - libs/utils/String16.cpp | 609 ---- libs/utils/String8.cpp | 604 ---- libs/utils/SystemClock.cpp | 139 - libs/utils/TextOutput.cpp | 146 - libs/utils/Threads.cpp | 1126 ------- libs/utils/TimerProbe.cpp | 131 - libs/utils/Timers.cpp | 240 -- libs/utils/Unicode.cpp | 193 -- libs/utils/VectorImpl.cpp | 611 ---- libs/utils/ZipEntry.cpp | 696 ----- libs/utils/ZipFile.cpp | 1296 -------- libs/utils/ZipFileCRO.cpp | 54 - libs/utils/ZipFileRO.cpp | 724 ----- libs/utils/ZipUtils.cpp | 344 --- libs/utils/characterData.h | 730 ----- libs/utils/executablepath_darwin.cpp | 31 - libs/utils/executablepath_linux.cpp | 30 - libs/utils/futex_synchro.c | 175 -- libs/utils/misc.cpp | 185 -- libs/utils/ported.cpp | 106 - 117 files changed, 34243 deletions(-) delete mode 100644 MODULE_LICENSE_APACHE2 delete mode 100644 NOTICE delete mode 100644 include/utils.h delete mode 100644 include/utils/AndroidUnicode.h delete mode 100644 include/utils/Asset.h delete mode 100644 include/utils/AssetDir.h delete mode 100644 include/utils/AssetManager.h delete mode 100644 include/utils/Atomic.h delete mode 100644 include/utils/Binder.h delete mode 100644 include/utils/BpBinder.h delete mode 100644 include/utils/Buffer.h delete mode 100644 include/utils/BufferedTextOutput.h delete mode 100644 include/utils/ByteOrder.h delete mode 100644 include/utils/CallStack.h delete mode 100644 include/utils/Debug.h delete mode 100644 include/utils/Endian.h delete mode 100644 include/utils/Errors.h delete mode 100644 include/utils/FileMap.h delete mode 100644 include/utils/IBinder.h delete mode 100644 include/utils/IInterface.h delete mode 100644 include/utils/IMemory.h delete mode 100644 include/utils/IPCThreadState.h delete mode 100644 include/utils/IPermissionController.h delete mode 100644 include/utils/IServiceManager.h delete mode 100644 include/utils/KeyedVector.h delete mode 100644 include/utils/List.h delete mode 100644 include/utils/Log.h delete mode 100644 include/utils/LogSocket.h delete mode 100644 include/utils/MemoryBase.h delete mode 100644 include/utils/MemoryDealer.h delete mode 100644 include/utils/MemoryHeapBase.h delete mode 100644 include/utils/MemoryHeapPmem.h delete mode 100644 include/utils/Parcel.h delete mode 100644 include/utils/Pipe.h delete mode 100644 include/utils/ProcessState.h delete mode 100644 include/utils/RefBase.h delete mode 100644 include/utils/ResourceTypes.h delete mode 100644 include/utils/SharedBuffer.h delete mode 100644 include/utils/Socket.h delete mode 100644 include/utils/SortedVector.h delete mode 100644 include/utils/StopWatch.h delete mode 100644 include/utils/String16.h delete mode 100644 include/utils/String8.h delete mode 100644 include/utils/SystemClock.h delete mode 100644 include/utils/TextOutput.h delete mode 100644 include/utils/TimeUtils.h delete mode 100644 include/utils/TimerProbe.h delete mode 100644 include/utils/Timers.h delete mode 100644 include/utils/TypeHelpers.h delete mode 100644 include/utils/Vector.h delete mode 100644 include/utils/VectorImpl.h delete mode 100644 include/utils/ZipEntry.h delete mode 100644 include/utils/ZipFile.h delete mode 100644 include/utils/ZipFileCRO.h delete mode 100644 include/utils/ZipFileRO.h delete mode 100644 include/utils/ZipUtils.h delete mode 100644 include/utils/ashmem.h delete mode 100644 include/utils/executablepath.h delete mode 100644 include/utils/inet_address.h delete mode 100644 include/utils/misc.h delete mode 100644 include/utils/ported.h delete mode 100644 include/utils/string_array.h delete mode 100644 include/utils/threads.h delete mode 100644 libs/utils/Android.mk delete mode 100644 libs/utils/Asset.cpp delete mode 100644 libs/utils/AssetDir.cpp delete mode 100644 libs/utils/AssetManager.cpp delete mode 100644 libs/utils/Binder.cpp delete mode 100644 libs/utils/BpBinder.cpp delete mode 100644 libs/utils/BufferedTextOutput.cpp delete mode 100644 libs/utils/CallStack.cpp delete mode 100644 libs/utils/Debug.cpp delete mode 100644 libs/utils/FileMap.cpp delete mode 100644 libs/utils/IDataConnection.cpp delete mode 100644 libs/utils/IInterface.cpp delete mode 100644 libs/utils/IMemory.cpp delete mode 100644 libs/utils/IPCThreadState.cpp delete mode 100644 libs/utils/IPermissionController.cpp delete mode 100644 libs/utils/IServiceManager.cpp delete mode 100644 libs/utils/InetAddress.cpp delete mode 100644 libs/utils/LogSocket.cpp delete mode 100644 libs/utils/MODULE_LICENSE_APACHE2 delete mode 100644 libs/utils/MemoryBase.cpp delete mode 100644 libs/utils/MemoryDealer.cpp delete mode 100644 libs/utils/MemoryHeapBase.cpp delete mode 100644 libs/utils/MemoryHeapPmem.cpp delete mode 100644 libs/utils/NOTICE delete mode 100644 libs/utils/Parcel.cpp delete mode 100644 libs/utils/Pipe.cpp delete mode 100644 libs/utils/ProcessState.cpp delete mode 100644 libs/utils/README delete mode 100644 libs/utils/RefBase.cpp delete mode 100644 libs/utils/ResourceTypes.cpp delete mode 100644 libs/utils/SharedBuffer.cpp delete mode 100644 libs/utils/Socket.cpp delete mode 100644 libs/utils/Static.cpp delete mode 100644 libs/utils/StopWatch.cpp delete mode 100644 libs/utils/String16.cpp delete mode 100644 libs/utils/String8.cpp delete mode 100644 libs/utils/SystemClock.cpp delete mode 100644 libs/utils/TextOutput.cpp delete mode 100644 libs/utils/Threads.cpp delete mode 100644 libs/utils/TimerProbe.cpp delete mode 100644 libs/utils/Timers.cpp delete mode 100644 libs/utils/Unicode.cpp delete mode 100644 libs/utils/VectorImpl.cpp delete mode 100644 libs/utils/ZipEntry.cpp delete mode 100644 libs/utils/ZipFile.cpp delete mode 100644 libs/utils/ZipFileCRO.cpp delete mode 100644 libs/utils/ZipFileRO.cpp delete mode 100644 libs/utils/ZipUtils.cpp delete mode 100644 libs/utils/characterData.h delete mode 100644 libs/utils/executablepath_darwin.cpp delete mode 100644 libs/utils/executablepath_linux.cpp delete mode 100644 libs/utils/futex_synchro.c delete mode 100644 libs/utils/misc.cpp delete mode 100644 libs/utils/ported.cpp diff --git a/MODULE_LICENSE_APACHE2 b/MODULE_LICENSE_APACHE2 deleted file mode 100644 index e69de29bb..000000000 diff --git a/NOTICE b/NOTICE deleted file mode 100644 index 267a6aafd..000000000 --- a/NOTICE +++ /dev/null @@ -1,222 +0,0 @@ - ========================================================================= - == NOTICE file corresponding to the section 4 d of == - == the Apache License, Version 2.0, == - == in this case for the Android-specific code. == - ========================================================================= - -Android Code -Copyright 2005-2008 The Android Open Source Project - -This product includes software developed as part of -The Android Open Source Project (http://source.android.com). - - ========================================================================= - == NOTICE file corresponding to the section 4 d of == - == the Apache License, Version 2.0, == - == in this case for Apache Commons code. == - ========================================================================= - -Apache Commons -Copyright 1999-2004 The Apache Software Foundation - -This product includes software developed at -The Apache Software Foundation (http://www.apache.org/). - - ========================================================================= - == NOTICE file corresponding to the section 4 d of == - == the Apache License, Version 2.0, == - == in this case for Jakarta Commons Logging. == - ========================================================================= - -Jakarta Commons Logging (JCL) -Copyright 2005,2006 The Apache Software Foundation. - -This product includes software developed at -The Apache Software Foundation (http://www.apache.org/). - - ========================================================================= - == NOTICE file corresponding to the section 4 d of == - == the Apache License, Version 2.0, == - == in this case for the Nuance code. == - ========================================================================= - -These files are Copyright 2007 Nuance Communications, but released under -the Apache2 License. - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - diff --git a/include/utils.h b/include/utils.h deleted file mode 100644 index 30648b185..000000000 --- a/include/utils.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2005 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. - */ - -// -// Handy utility functions and portability code. This file includes all -// of the generally-useful headers in the "utils" directory. -// -#ifndef _LIBS_UTILS_H -#define _LIBS_UTILS_H - -#include -#include -#include -#include -#include -#include -#include -#include - -#endif // _LIBS_UTILS_H diff --git a/include/utils/AndroidUnicode.h b/include/utils/AndroidUnicode.h deleted file mode 100644 index 563fcd019..000000000 --- a/include/utils/AndroidUnicode.h +++ /dev/null @@ -1,255 +0,0 @@ -/* - * Copyright (C) 2006 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_UNICODE_H -#define ANDROID_UNICODE_H - -#include -#include - -#define REPLACEMENT_CHAR (0xFFFD) - -// this part of code is copied from umachine.h under ICU -/** - * Define UChar32 as a type for single Unicode code points. - * UChar32 is a signed 32-bit integer (same as int32_t). - * - * The Unicode code point range is 0..0x10ffff. - * All other values (negative or >=0x110000) are illegal as Unicode code points. - * They may be used as sentinel values to indicate "done", "error" - * or similar non-code point conditions. - * - * @stable ICU 2.4 - */ -typedef int32_t UChar32; - -namespace android { - - class Encoding; - /** - * \class Unicode - * - * Helper class for getting properties of Unicode characters. Characters - * can have one of the types listed in CharType and each character can have the - * directionality of Direction. - */ - class Unicode - { - public: - /** - * Directions specified in the Unicode standard. These directions map directly - * to java.lang.Character. - */ - enum Direction { - DIRECTIONALITY_UNDEFINED = -1, - DIRECTIONALITY_LEFT_TO_RIGHT, - DIRECTIONALITY_RIGHT_TO_LEFT, - DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC, - DIRECTIONALITY_EUROPEAN_NUMBER, - DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR, - DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR, - DIRECTIONALITY_ARABIC_NUMBER, - DIRECTIONALITY_COMMON_NUMBER_SEPARATOR, - DIRECTIONALITY_NONSPACING_MARK, - DIRECTIONALITY_BOUNDARY_NEUTRAL, - DIRECTIONALITY_PARAGRAPH_SEPARATOR, - DIRECTIONALITY_SEGMENT_SEPARATOR, - DIRECTIONALITY_WHITESPACE, - DIRECTIONALITY_OTHER_NEUTRALS, - DIRECTIONALITY_LEFT_TO_RIGHT_EMBEDDING, - DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE, - DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING, - DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE, - DIRECTIONALITY_POP_DIRECTIONAL_FORMAT - }; - - /** - * Character types as specified in the Unicode standard. These map directly to - * java.lang.Character. - */ - enum CharType { - CHARTYPE_UNASSIGNED = 0, - CHARTYPE_UPPERCASE_LETTER, - CHARTYPE_LOWERCASE_LETTER, - CHARTYPE_TITLECASE_LETTER, - CHARTYPE_MODIFIER_LETTER, - CHARTYPE_OTHER_LETTER, - CHARTYPE_NON_SPACING_MARK, - CHARTYPE_ENCLOSING_MARK, - CHARTYPE_COMBINING_SPACING_MARK, - CHARTYPE_DECIMAL_DIGIT_NUMBER, - CHARTYPE_LETTER_NUMBER, - CHARTYPE_OTHER_NUMBER, - CHARTYPE_SPACE_SEPARATOR, - CHARTYPE_LINE_SEPARATOR, - CHARTYPE_PARAGRAPH_SEPARATOR, - CHARTYPE_CONTROL, - CHARTYPE_FORMAT, - CHARTYPE_MISSING_VALUE_FOR_JAVA, /* This is the mysterious missing 17 value from the java constants */ - CHARTYPE_PRIVATE_USE, - CHARTYPE_SURROGATE, - CHARTYPE_DASH_PUNCTUATION, - CHARTYPE_START_PUNCTUATION, - CHARTYPE_END_PUNCTUATION, - CHARTYPE_CONNECTOR_PUNCTUATION, - CHARTYPE_OTHER_PUNCTUATION, - CHARTYPE_MATH_SYMBOL, - CHARTYPE_CURRENCY_SYMBOL, - CHARTYPE_MODIFIER_SYMBOL, - CHARTYPE_OTHER_SYMBOL, - CHARTYPE_INITIAL_QUOTE_PUNCTUATION, - CHARTYPE_FINAL_QUOTE_PUNCTUATION - }; - - /** - * Decomposition types as described by the unicode standard. These values map to - * the same values in uchar.h in ICU. - */ - enum DecompositionType { - DECOMPOSITION_NONE = 0, - DECOMPOSITION_CANONICAL, - DECOMPOSITION_COMPAT, - DECOMPOSITION_CIRCLE, - DECOMPOSITION_FINAL, - DECOMPOSITION_FONT, - DECOMPOSITION_FRACTION, - DECOMPOSITION_INITIAL, - DECOMPOSITION_ISOLATED, - DECOMPOSITION_MEDIAL, - DECOMPOSITION_NARROW, - DECOMPOSITION_NOBREAK, - DECOMPOSITION_SMALL, - DECOMPOSITION_SQUARE, - DECOMPOSITION_SUB, - DECOMPOSITION_SUPER, - DECOMPOSITION_VERTICAL, - DECOMPOSITION_WIDE - }; - - /** - * Returns the packed data for java calls - * @param c The unicode character. - * @return The packed data for the character. - * - * Copied from java.lang.Character implementation: - * 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 - * F E D C B A 9 8 7 6 5 4 3 2 1 0 F E D C B A 9 8 7 6 5 4 3 2 1 0 - * - * 31 types --------- - * 18 directionalities --------- - * 2 mirroreds - - * ----------- 56 toupper diffs - * ----------- 48 tolower diffs - * --- 4 totitlecase diffs - * ------------- 84 numeric values - * --------- 24 mirror char diffs - */ - static uint32_t getPackedData(UChar32 c); - - /** - * Get the Character type. - * @param c The unicode character. - * @return The character's type or CHARTYPE_UNASSIGNED if the character is invalid - * or has an unassigned class. - */ - static CharType getType(UChar32 c); - - /** - * Get the Character's decomposition type. - * @param c The unicode character. - * @return The character's decomposition type or DECOMPOSITION_NONE is there - * is no decomposition. - */ - static DecompositionType getDecompositionType(UChar32 c); - - /** - * Returns the digit value of a character or -1 if the character - * is not within the specified radix. - * - * The digit value is computed for integer characters and letters - * within the given radix. This function does not handle Roman Numerals, - * fractions, or any other characters that may represent numbers. - * - * @param c The unicode character - * @param radix The intended radix. - * @return The digit value or -1 if there is no digit value or if the value is outside the radix. - */ - static int getDigitValue(UChar32 c, int radix = 10); - - /** - * Return the numeric value of a character - * - * @param c The unicode character. - * @return The numeric value of the character. -1 if the character has no numeric value, - * -2 if the character has a numeric value that is not representable by an integer. - */ - static int getNumericValue(UChar32 c); - - /** - * Convert the character to lowercase - * @param c The unicode character. - * @return The lowercase character equivalent of c. If c does not have a lowercase equivalent, - * the original character is returned. - */ - static UChar32 toLower(UChar32 c); - - /** - * Convert the character to uppercase - * @param c The unicode character. - * @return The uppercase character equivalent of c. If c does not have an uppercase equivalent, - * the original character is returned. - */ - static UChar32 toUpper(UChar32 c); - - /** - * Get the directionality of the character. - * @param c The unicode character. - * @return The direction of the character or DIRECTIONALITY_UNDEFINED. - */ - static Direction getDirectionality(UChar32 c); - - /** - * Check if the character is a mirrored character. This means that the character - * has an equivalent character that is the mirror image of itself. - * @param c The unicode character. - * @return True iff c has a mirror equivalent. - */ - static bool isMirrored(UChar32 c); - - /** - * Return the mirror of the given character. - * @param c The unicode character. - * @return The mirror equivalent of c. If c does not have a mirror equivalent, - * the original character is returned. - * @see isMirrored - */ - static UChar32 toMirror(UChar32 c); - - /** - * Convert the character to title case. - * @param c The unicode character. - * @return The titlecase equivalent of c. If c does not have a titlecase equivalent, - * the original character is returned. - */ - static UChar32 toTitle(UChar32 c); - - }; - -} - -#endif diff --git a/include/utils/Asset.h b/include/utils/Asset.h deleted file mode 100644 index 453a2049a..000000000 --- a/include/utils/Asset.h +++ /dev/null @@ -1,315 +0,0 @@ -/* - * Copyright (C) 2006 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. - */ - -// -// Class providing access to a read-only asset. Asset objects are NOT -// thread-safe, and should not be shared across threads. -// -#ifndef __LIBS_ASSET_H -#define __LIBS_ASSET_H - -#include -#include -#include "FileMap.h" -#include "String8.h" -#include "Errors.h" - -namespace android { - -/* - * Instances of this class provide read-only operations on a byte stream. - * - * Access may be optimized for streaming, random, or whole buffer modes. All - * operations are supported regardless of how the file was opened, but some - * things will be less efficient. [pass that in??] - * - * "Asset" is the base class for all types of assets. The classes below - * provide most of the implementation. The AssetManager uses one of the - * static "create" functions defined here to create a new instance. - */ -class Asset { -public: - virtual ~Asset(void); - - static int32_t getGlobalCount(); - - /* used when opening an asset */ - typedef enum AccessMode { - ACCESS_UNKNOWN = 0, - - /* read chunks, and seek forward and backward */ - ACCESS_RANDOM, - - /* read sequentially, with an occasional forward seek */ - ACCESS_STREAMING, - - /* caller plans to ask for a read-only buffer with all data */ - ACCESS_BUFFER, - } AccessMode; - - enum { - /* data larger than this does not get uncompressed into a buffer */ -#ifdef HAVE_ANDROID_OS - UNCOMPRESS_DATA_MAX = 1 * 1024 * 1024 -#else - UNCOMPRESS_DATA_MAX = 2 * 1024 * 1024 -#endif - }; - - /* - * Read data from the current offset. Returns the actual number of - * bytes read, 0 on EOF, or -1 on error. - */ - virtual ssize_t read(void* buf, size_t count) = 0; - - /* - * Seek to the specified offset. "whence" uses the same values as - * lseek/fseek. Returns the new position on success, or (off_t) -1 - * on failure. - */ - virtual off_t seek(off_t offset, int whence) = 0; - - /* - * Close the asset, freeing all associated resources. - */ - virtual void close(void) = 0; - - /* - * Get a pointer to a buffer with the entire contents of the file. - */ - virtual const void* getBuffer(bool wordAligned) = 0; - - /* - * Get the total amount of data that can be read. - */ - virtual off_t getLength(void) const = 0; - - /* - * Get the total amount of data that can be read from the current position. - */ - virtual off_t getRemainingLength(void) const = 0; - - /* - * Open a new file descriptor that can be used to read this asset. - * Returns -1 if you can not use the file descriptor (for example if the - * asset is compressed). - */ - virtual int openFileDescriptor(off_t* outStart, off_t* outLength) const = 0; - - /* - * Get a string identifying the asset's source. This might be a full - * path, it might be a colon-separated list of identifiers. - * - * This is NOT intended to be used for anything except debug output. - * DO NOT try to parse this or use it to open a file. - */ - const char* getAssetSource(void) const { return mAssetSource.string(); } - -protected: - Asset(void); // constructor; only invoked indirectly - - /* handle common seek() housekeeping */ - off_t handleSeek(off_t offset, int whence, off_t curPosn, off_t maxPosn); - - /* set the asset source string */ - void setAssetSource(const String8& path) { mAssetSource = path; } - - AccessMode getAccessMode(void) const { return mAccessMode; } - -private: - /* these operations are not implemented */ - Asset(const Asset& src); - Asset& operator=(const Asset& src); - - /* AssetManager needs access to our "create" functions */ - friend class AssetManager; - - /* - * Create the asset from a named file on disk. - */ - static Asset* createFromFile(const char* fileName, AccessMode mode); - - /* - * Create the asset from a named, compressed file on disk (e.g. ".gz"). - */ - static Asset* createFromCompressedFile(const char* fileName, - AccessMode mode); - -#if 0 - /* - * Create the asset from a segment of an open file. This will fail - * if "offset" and "length" don't fit within the bounds of the file. - * - * The asset takes ownership of the file descriptor. - */ - static Asset* createFromFileSegment(int fd, off_t offset, size_t length, - AccessMode mode); - - /* - * Create from compressed data. "fd" should be seeked to the start of - * the compressed data. This could be inside a gzip file or part of a - * Zip archive. - * - * The asset takes ownership of the file descriptor. - * - * This may not verify the validity of the compressed data until first - * use. - */ - static Asset* createFromCompressedData(int fd, off_t offset, - int compressionMethod, size_t compressedLength, - size_t uncompressedLength, AccessMode mode); -#endif - - /* - * Create the asset from a memory-mapped file segment. - * - * The asset takes ownership of the FileMap. - */ - static Asset* createFromUncompressedMap(FileMap* dataMap, AccessMode mode); - - /* - * Create the asset from a memory-mapped file segment with compressed - * data. "method" is a Zip archive compression method constant. - * - * The asset takes ownership of the FileMap. - */ - static Asset* createFromCompressedMap(FileMap* dataMap, int method, - size_t uncompressedLen, AccessMode mode); - - - /* - * Create from a reference-counted chunk of shared memory. - */ - // TODO - - AccessMode mAccessMode; // how the asset was opened - String8 mAssetSource; // debug string -}; - - -/* - * =========================================================================== - * - * Innards follow. Do not use these classes directly. - */ - -/* - * An asset based on an uncompressed file on disk. It may encompass the - * entire file or just a piece of it. Access is through fread/fseek. - */ -class _FileAsset : public Asset { -public: - _FileAsset(void); - virtual ~_FileAsset(void); - - /* - * Use a piece of an already-open file. - * - * On success, the object takes ownership of "fd". - */ - status_t openChunk(const char* fileName, int fd, off_t offset, size_t length); - - /* - * Use a memory-mapped region. - * - * On success, the object takes ownership of "dataMap". - */ - status_t openChunk(FileMap* dataMap); - - /* - * Standard Asset interfaces. - */ - virtual ssize_t read(void* buf, size_t count); - virtual off_t seek(off_t offset, int whence); - virtual void close(void); - virtual const void* getBuffer(bool wordAligned); - virtual off_t getLength(void) const { return mLength; } - virtual off_t getRemainingLength(void) const { return mLength-mOffset; } - virtual int openFileDescriptor(off_t* outStart, off_t* outLength) const; - -private: - off_t mStart; // absolute file offset of start of chunk - off_t mLength; // length of the chunk - off_t mOffset; // current local offset, 0 == mStart - FILE* mFp; // for read/seek - char* mFileName; // for opening - - /* - * To support getBuffer() we either need to read the entire thing into - * a buffer or memory-map it. For small files it's probably best to - * just read them in. - */ - enum { kReadVsMapThreshold = 4096 }; - - FileMap* mMap; // for memory map - unsigned char* mBuf; // for read - - const void* ensureAlignment(FileMap* map); -}; - - -/* - * An asset based on compressed data in a file. - */ -class _CompressedAsset : public Asset { -public: - _CompressedAsset(void); - virtual ~_CompressedAsset(void); - - /* - * Use a piece of an already-open file. - * - * On success, the object takes ownership of "fd". - */ - status_t openChunk(int fd, off_t offset, int compressionMethod, - size_t uncompressedLen, size_t compressedLen); - - /* - * Use a memory-mapped region. - * - * On success, the object takes ownership of "fd". - */ - status_t openChunk(FileMap* dataMap, int compressionMethod, - size_t uncompressedLen); - - /* - * Standard Asset interfaces. - */ - virtual ssize_t read(void* buf, size_t count); - virtual off_t seek(off_t offset, int whence); - virtual void close(void); - virtual const void* getBuffer(bool wordAligned); - virtual off_t getLength(void) const { return mUncompressedLen; } - virtual off_t getRemainingLength(void) const { return mUncompressedLen-mOffset; } - virtual int openFileDescriptor(off_t* outStart, off_t* outLength) const { return -1; } - -private: - off_t mStart; // offset to start of compressed data - off_t mCompressedLen; // length of the compressed data - off_t mUncompressedLen; // length of the uncompressed data - off_t mOffset; // current offset, 0 == start of uncomp data - - FileMap* mMap; // for memory-mapped input - int mFd; // for file input - - unsigned char* mBuf; // for getBuffer() -}; - -// need: shared mmap version? - -}; // namespace android - -#endif // __LIBS_ASSET_H diff --git a/include/utils/AssetDir.h b/include/utils/AssetDir.h deleted file mode 100644 index abf8a3595..000000000 --- a/include/utils/AssetDir.h +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright (C) 2006 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. - */ - -// -// Access a chunk of the asset hierarchy as if it were a single directory. -// -#ifndef __LIBS_ASSETDIR_H -#define __LIBS_ASSETDIR_H - -#include -#include -#include -#include -#include - -namespace android { - -/* - * This provides vector-style access to a directory. We do this rather - * than modeling opendir/readdir access because it's simpler and the - * nature of the operation requires us to have all data on hand anyway. - * - * The list of files will be sorted in ascending order by ASCII value. - * - * The contents are populated by our friend, the AssetManager. - */ -class AssetDir { -public: - AssetDir(void) - : mFileInfo(NULL) - {} - virtual ~AssetDir(void) { - delete mFileInfo; - } - - /* - * Vector-style access. - */ - size_t getFileCount(void) { return mFileInfo->size(); } - const String8& getFileName(int idx) { - return mFileInfo->itemAt(idx).getFileName(); - } - const String8& getSourceName(int idx) { - return mFileInfo->itemAt(idx).getSourceName(); - } - - /* - * Get the type of a file (usually regular or directory). - */ - FileType getFileType(int idx) { - return mFileInfo->itemAt(idx).getFileType(); - } - -private: - /* these operations are not implemented */ - AssetDir(const AssetDir& src); - const AssetDir& operator=(const AssetDir& src); - - friend class AssetManager; - - /* - * This holds information about files in the asset hierarchy. - */ - class FileInfo { - public: - FileInfo(void) {} - FileInfo(const String8& path) // useful for e.g. svect.indexOf - : mFileName(path), mFileType(kFileTypeUnknown) - {} - ~FileInfo(void) {} - FileInfo(const FileInfo& src) { - copyMembers(src); - } - const FileInfo& operator= (const FileInfo& src) { - if (this != &src) - copyMembers(src); - return *this; - } - - void copyMembers(const FileInfo& src) { - mFileName = src.mFileName; - mFileType = src.mFileType; - mSourceName = src.mSourceName; - } - - /* need this for SortedVector; must compare only on file name */ - bool operator< (const FileInfo& rhs) const { - return mFileName < rhs.mFileName; - } - - /* used by AssetManager */ - bool operator== (const FileInfo& rhs) const { - return mFileName == rhs.mFileName; - } - - void set(const String8& path, FileType type) { - mFileName = path; - mFileType = type; - } - - const String8& getFileName(void) const { return mFileName; } - void setFileName(const String8& path) { mFileName = path; } - - FileType getFileType(void) const { return mFileType; } - void setFileType(FileType type) { mFileType = type; } - - const String8& getSourceName(void) const { return mSourceName; } - void setSourceName(const String8& path) { mSourceName = path; } - - /* - * Handy utility for finding an entry in a sorted vector of FileInfo. - * Returns the index of the matching entry, or -1 if none found. - */ - static int findEntry(const SortedVector* pVector, - const String8& fileName); - - private: - String8 mFileName; // filename only - FileType mFileType; // regular, directory, etc - - String8 mSourceName; // currently debug-only - }; - - /* AssetManager uses this to initialize us */ - void setFileList(SortedVector* list) { mFileInfo = list; } - - SortedVector* mFileInfo; -}; - -}; // namespace android - -#endif // __LIBS_ASSETDIR_H diff --git a/include/utils/AssetManager.h b/include/utils/AssetManager.h deleted file mode 100644 index e94c0e8fe..000000000 --- a/include/utils/AssetManager.h +++ /dev/null @@ -1,323 +0,0 @@ -/* - * Copyright (C) 2006 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. - */ - -// -// Asset management class. AssetManager objects are thread-safe. -// -#ifndef __LIBS_ASSETMANAGER_H -#define __LIBS_ASSETMANAGER_H - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace android { - -class Asset; // fwd decl for things that include Asset.h first -class ResTable; -struct ResTable_config; - -/* - * Every application that uses assets needs one instance of this. A - * single instance may be shared across multiple threads, and a single - * thread may have more than one instance (the latter is discouraged). - * - * The purpose of the AssetManager is to create Asset objects. To do - * this efficiently it may cache information about the locations of - * files it has seen. This can be controlled with the "cacheMode" - * argument. - * - * The asset hierarchy may be examined like a filesystem, using - * AssetDir objects to peruse a single directory. - */ -class AssetManager { -public: - typedef enum CacheMode { - CACHE_UNKNOWN = 0, - CACHE_OFF, // don't try to cache file locations - CACHE_DEFER, // construct cache as pieces are needed - //CACHE_SCAN, // scan full(!) asset hierarchy at init() time - } CacheMode; - - AssetManager(CacheMode cacheMode = CACHE_OFF); - virtual ~AssetManager(void); - - static int32_t getGlobalCount(); - - /* - * Add a new source for assets. This can be called multiple times to - * look in multiple places for assets. It can be either a directory (for - * finding assets as raw files on the disk) or a ZIP file. This newly - * added asset path will be examined first when searching for assets, - * before any that were previously added. - * - * Returns "true" on success, "false" on failure. If 'cookie' is non-NULL, - * then on success, *cookie is set to the value corresponding to the - * newly-added asset source. - */ - bool addAssetPath(const String8& path, void** cookie); - - /* - * Convenience for adding the standard system assets. Uses the - * ANDROID_ROOT environment variable to find them. - */ - bool addDefaultAssets(); - - /* - * Iterate over the asset paths in this manager. (Previously - * added via addAssetPath() and addDefaultAssets().) On first call, - * 'cookie' must be NULL, resulting in the first cookie being returned. - * Each next cookie will be returned there-after, until NULL indicating - * the end has been reached. - */ - void* nextAssetPath(void* cookie) const; - - /* - * Return an asset path in the manager. 'which' must be between 0 and - * countAssetPaths(). - */ - String8 getAssetPath(void* cookie) const; - - /* - * Set the current locale and vendor. The locale can change during - * the lifetime of an AssetManager if the user updates the device's - * language setting. The vendor is less likely to change. - * - * Pass in NULL to indicate no preference. - */ - void setLocale(const char* locale); - void setVendor(const char* vendor); - - /* - * Choose screen orientation for resources values returned. - */ - void setConfiguration(const ResTable_config& config, const char* locale = NULL); - - typedef Asset::AccessMode AccessMode; // typing shortcut - - /* - * Open an asset. - * - * This will search through locale-specific and vendor-specific - * directories and packages to find the file. - * - * The object returned does not depend on the AssetManager. It should - * be freed by calling Asset::close(). - */ - Asset* open(const char* fileName, AccessMode mode); - - /* - * Open a non-asset file as an asset. - * - * This is for opening files that are included in an asset package - * but aren't assets. These sit outside the usual "locale/vendor" - * path hierarchy, and will not be seen by "AssetDir" or included - * in our filename cache. - */ - Asset* openNonAsset(const char* fileName, AccessMode mode); - - /* - * Explicit non-asset file. The file explicitly named by the cookie (the - * resource set to look in) and fileName will be opened and returned. - */ - Asset* openNonAsset(void* cookie, const char* fileName, AccessMode mode); - - /* - * Open a directory within the asset hierarchy. - * - * The contents of the directory are an amalgam of vendor-specific, - * locale-specific, and generic assets stored loosely or in asset - * packages. Depending on the cache setting and previous accesses, - * this call may incur significant disk overhead. - * - * To open the top-level directory, pass in "". - */ - AssetDir* openDir(const char* dirName); - - /* - * Get the type of a file in the asset hierarchy. They will either - * be "regular" or "directory". [Currently only works for "regular".] - * - * Can also be used as a quick test for existence of a file. - */ - FileType getFileType(const char* fileName); - - /* - * Return the complete resource table to find things in the package. - */ - const ResTable& getResources(bool required = true) const; - - /* - * Discard cached filename information. This only needs to be called - * if somebody has updated the set of "loose" files, and we want to - * discard our cached notion of what's where. - */ - void purge(void) { purgeFileNameCacheLocked(); } - - /* - * Return true if the files this AssetManager references are all - * up-to-date (have not been changed since it was created). If false - * is returned, you will need to create a new AssetManager to get - * the current data. - */ - bool isUpToDate(); - - /** - * Get the known locales for this asset manager object. - */ - void getLocales(Vector* locales) const; - -private: - struct asset_path - { - String8 path; - FileType type; - }; - - Asset* openInPathLocked(const char* fileName, AccessMode mode, - const asset_path& path); - Asset* openNonAssetInPathLocked(const char* fileName, AccessMode mode, - const asset_path& path); - Asset* openInLocaleVendorLocked(const char* fileName, AccessMode mode, - const asset_path& path, const char* locale, const char* vendor); - String8 createPathNameLocked(const asset_path& path, const char* locale, - const char* vendor); - String8 createPathNameLocked(const asset_path& path, const char* rootDir); - String8 createZipSourceNameLocked(const String8& zipFileName, - const String8& dirName, const String8& fileName); - - ZipFileRO* getZipFileLocked(const asset_path& path); - Asset* openAssetFromFileLocked(const String8& fileName, AccessMode mode); - Asset* openAssetFromZipLocked(const ZipFileRO* pZipFile, - const ZipEntryRO entry, AccessMode mode, const String8& entryName); - - bool scanAndMergeDirLocked(SortedVector* pMergedInfo, - const asset_path& path, const char* rootDir, const char* dirName); - SortedVector* scanDirLocked(const String8& path); - bool scanAndMergeZipLocked(SortedVector* pMergedInfo, - const asset_path& path, const char* rootDir, const char* dirName); - void mergeInfoLocked(SortedVector* pMergedInfo, - const SortedVector* pContents); - - void loadFileNameCacheLocked(void); - void fncScanLocked(SortedVector* pMergedInfo, - const char* dirName); - bool fncScanAndMergeDirLocked( - SortedVector* pMergedInfo, - const asset_path& path, const char* locale, const char* vendor, - const char* dirName); - void purgeFileNameCacheLocked(void); - - const ResTable* getResTable(bool required = true) const; - void setLocaleLocked(const char* locale); - void updateResourceParamsLocked() const; - - class SharedZip : public RefBase { - public: - static sp get(const String8& path); - - ZipFileRO* getZip(); - - Asset* getResourceTableAsset(); - Asset* setResourceTableAsset(Asset* asset); - - bool isUpToDate(); - - protected: - ~SharedZip(); - - private: - SharedZip(const String8& path, time_t modWhen); - SharedZip(); // <-- not implemented - - String8 mPath; - ZipFileRO* mZipFile; - time_t mModWhen; - - Asset* mResourceTableAsset; - - static Mutex gLock; - static DefaultKeyedVector > gOpen; - }; - - /* - * Manage a set of Zip files. For each file we need a pointer to the - * ZipFile and a time_t with the file's modification date. - * - * We currently only have two zip files (current app, "common" app). - * (This was originally written for 8, based on app/locale/vendor.) - */ - class ZipSet { - public: - ZipSet(void); - ~ZipSet(void); - - /* - * Return a ZipFileRO structure for a ZipFileRO with the specified - * parameters. - */ - ZipFileRO* getZip(const String8& path); - - Asset* getZipResourceTable(const String8& path); - Asset* setZipResourceTable(const String8& path, Asset* asset); - - // generate path, e.g. "common/en-US-noogle.zip" - static String8 getPathName(const char* path); - - bool isUpToDate(); - - private: - void closeZip(int idx); - - int getIndex(const String8& zip) const; - mutable Vector mZipPath; - mutable Vector > mZipFile; - }; - - // Protect all internal state. - mutable Mutex mLock; - - ZipSet mZipSet; - - Vector mAssetPaths; - char* mLocale; - char* mVendor; - - mutable ResTable* mResources; - ResTable_config* mConfig; - - /* - * Cached data for "loose" files. This lets us avoid poking at the - * filesystem when searching for loose assets. Each entry is the - * "extended partial" path, e.g. "default/default/foo/bar.txt". The - * full set of files is present, including ".EXCLUDE" entries. - * - * We do not cache directory names. We don't retain the ".gz", - * because to our clients "foo" and "foo.gz" both look like "foo". - */ - CacheMode mCacheMode; // is the cache enabled? - bool mCacheValid; // clear when locale or vendor changes - SortedVector mCache; -}; - -}; // namespace android - -#endif // __LIBS_ASSETMANAGER_H diff --git a/include/utils/Atomic.h b/include/utils/Atomic.h deleted file mode 100644 index 7eb476c94..000000000 --- a/include/utils/Atomic.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (C) 2005 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_UTILS_ATOMIC_H -#define ANDROID_UTILS_ATOMIC_H - -#include - -#endif // ANDROID_UTILS_ATOMIC_H diff --git a/include/utils/Binder.h b/include/utils/Binder.h deleted file mode 100644 index b5b8d9851..000000000 --- a/include/utils/Binder.h +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_BINDER_H -#define ANDROID_BINDER_H - -#include - -// --------------------------------------------------------------------------- -namespace android { - -class BBinder : public IBinder -{ -public: - BBinder(); - - virtual String16 getInterfaceDescriptor() const; - virtual bool isBinderAlive() const; - virtual status_t pingBinder(); - virtual status_t dump(int fd, const Vector& args); - - virtual status_t transact( uint32_t code, - const Parcel& data, - Parcel* reply, - uint32_t flags = 0); - - virtual status_t linkToDeath(const sp& recipient, - void* cookie = NULL, - uint32_t flags = 0); - - virtual status_t unlinkToDeath( const wp& recipient, - void* cookie = NULL, - uint32_t flags = 0, - wp* outRecipient = NULL); - - virtual void attachObject( const void* objectID, - void* object, - void* cleanupCookie, - object_cleanup_func func); - virtual void* findObject(const void* objectID) const; - virtual void detachObject(const void* objectID); - - virtual BBinder* localBinder(); - -protected: - virtual ~BBinder(); - - virtual status_t onTransact( uint32_t code, - const Parcel& data, - Parcel* reply, - uint32_t flags = 0); - -private: - BBinder(const BBinder& o); - BBinder& operator=(const BBinder& o); - - class Extras; - - Extras* mExtras; - void* mReserved0; -}; - -// --------------------------------------------------------------------------- - -class BpRefBase : public virtual RefBase -{ -protected: - BpRefBase(const sp& o); - virtual ~BpRefBase(); - virtual void onFirstRef(); - virtual void onLastStrongRef(const void* id); - virtual bool onIncStrongAttempted(uint32_t flags, const void* id); - - inline IBinder* remote() { return mRemote; } - inline IBinder* remote() const { return mRemote; } - -private: - BpRefBase(const BpRefBase& o); - BpRefBase& operator=(const BpRefBase& o); - - IBinder* const mRemote; - RefBase::weakref_type* mRefs; - volatile int32_t mState; -}; - -}; // namespace android - -// --------------------------------------------------------------------------- - -#endif // ANDROID_BINDER_H diff --git a/include/utils/BpBinder.h b/include/utils/BpBinder.h deleted file mode 100644 index 7b96e296f..000000000 --- a/include/utils/BpBinder.h +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (C) 2005 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_BPBINDER_H -#define ANDROID_BPBINDER_H - -#include -#include -#include - -// --------------------------------------------------------------------------- -namespace android { - -class BpBinder : public IBinder -{ -public: - BpBinder(int32_t handle); - - inline int32_t handle() const { return mHandle; } - - virtual String16 getInterfaceDescriptor() const; - virtual bool isBinderAlive() const; - virtual status_t pingBinder(); - virtual status_t dump(int fd, const Vector& args); - - virtual status_t transact( uint32_t code, - const Parcel& data, - Parcel* reply, - uint32_t flags = 0); - - virtual status_t linkToDeath(const sp& recipient, - void* cookie = NULL, - uint32_t flags = 0); - virtual status_t unlinkToDeath( const wp& recipient, - void* cookie = NULL, - uint32_t flags = 0, - wp* outRecipient = NULL); - - virtual void attachObject( const void* objectID, - void* object, - void* cleanupCookie, - object_cleanup_func func); - virtual void* findObject(const void* objectID) const; - virtual void detachObject(const void* objectID); - - virtual BpBinder* remoteBinder(); - - status_t setConstantData(const void* data, size_t size); - void sendObituary(); - - class ObjectManager - { - public: - ObjectManager(); - ~ObjectManager(); - - void attach( const void* objectID, - void* object, - void* cleanupCookie, - IBinder::object_cleanup_func func); - void* find(const void* objectID) const; - void detach(const void* objectID); - - void kill(); - - private: - ObjectManager(const ObjectManager&); - ObjectManager& operator=(const ObjectManager&); - - struct entry_t - { - void* object; - void* cleanupCookie; - IBinder::object_cleanup_func func; - }; - - KeyedVector mObjects; - }; - -protected: - virtual ~BpBinder(); - virtual void onFirstRef(); - virtual void onLastStrongRef(const void* id); - virtual bool onIncStrongAttempted(uint32_t flags, const void* id); - -private: - const int32_t mHandle; - - struct Obituary { - wp recipient; - void* cookie; - uint32_t flags; - }; - - void reportOneDeath(const Obituary& obit); - - mutable Mutex mLock; - volatile int32_t mAlive; - volatile int32_t mObitsSent; - Vector* mObituaries; - ObjectManager mObjects; - Parcel* mConstantData; -}; - -}; // namespace android - -// --------------------------------------------------------------------------- - -#endif // ANDROID_BPBINDER_H diff --git a/include/utils/Buffer.h b/include/utils/Buffer.h deleted file mode 100644 index 8e22b0f21..000000000 --- a/include/utils/Buffer.h +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __UTILS_BUFFER_H__ -#define __UTILS_BUFFER_H__ 1 - -#include -#include -#include - -namespace android { - -class Buffer -{ -private: - char *buf; - int bufsiz; - int used; - void ensureCapacity(int len); - - void - makeRoomFor(int len) - { - if (len + used >= bufsiz) { - bufsiz = (len + used) * 3/2 + 2; - char *blah = new char[bufsiz]; - - memcpy(blah, buf, used); - delete[] buf; - buf = blah; - } - } - -public: - Buffer() - { - bufsiz = 16; - buf = new char[bufsiz]; - clear(); - } - - ~Buffer() - { - delete[] buf; - } - - void - clear() - { - buf[0] = '\0'; - used = 0; - } - - int - length() - { - return used; - } - - void - append(const char c) - { - makeRoomFor(1); - buf[used] = c; - used++; - buf[used] = '\0'; - } - - void - append(const char *s, int len) - { - makeRoomFor(len); - - memcpy(buf + used, s, len); - used += len; - buf[used] = '\0'; - } - - void - append(const char *s) - { - append(s, strlen(s)); - } - - char * - getBytes() - { - return buf; - } -}; - -}; // namespace android - -#endif diff --git a/include/utils/BufferedTextOutput.h b/include/utils/BufferedTextOutput.h deleted file mode 100644 index 69c624037..000000000 --- a/include/utils/BufferedTextOutput.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2006 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_BUFFEREDTEXTOUTPUT_H -#define ANDROID_BUFFEREDTEXTOUTPUT_H - -#include -#include -#include - -// --------------------------------------------------------------------------- -namespace android { - -class BufferedTextOutput : public TextOutput -{ -public: - //** Flags for constructor */ - enum { - MULTITHREADED = 0x0001 - }; - - BufferedTextOutput(uint32_t flags = 0); - virtual ~BufferedTextOutput(); - - virtual status_t print(const char* txt, size_t len); - virtual void moveIndent(int delta); - - virtual void pushBundle(); - virtual void popBundle(); - -protected: - virtual status_t writeLines(const struct iovec& vec, size_t N) = 0; - -private: - struct BufferState; - struct ThreadState; - - static ThreadState*getThreadState(); - static void threadDestructor(void *st); - - BufferState*getBuffer() const; - - uint32_t mFlags; - const int32_t mSeq; - const int32_t mIndex; - - Mutex mLock; - BufferState* mGlobalState; -}; - -// --------------------------------------------------------------------------- -}; // namespace android - -#endif // ANDROID_BUFFEREDTEXTOUTPUT_H diff --git a/include/utils/ByteOrder.h b/include/utils/ByteOrder.h deleted file mode 100644 index 4c0606763..000000000 --- a/include/utils/ByteOrder.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (C) 2006 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 _LIBS_UTILS_BYTE_ORDER_H -#define _LIBS_UTILS_BYTE_ORDER_H - -#include -#include -#ifdef HAVE_WINSOCK -#include -#else -#include -#endif - -/* - * These macros are like the hton/ntoh byte swapping macros, - * except they allow you to swap to and from the "device" byte - * order. The device byte order is the endianness of the target - * device -- for the ARM CPUs we use today, this is little endian. - * - * Note that the byte swapping functions have not been optimized - * much; performance is currently not an issue for them since the - * intent is to allow us to avoid byte swapping on the device. - */ - -#define DEVICE_BYTE_ORDER LITTLE_ENDIAN - -#if BYTE_ORDER == DEVICE_BYTE_ORDER - -#define dtohl(x) (x) -#define dtohs(x) (x) -#define htodl(x) (x) -#define htods(x) (x) - -#else - -static inline uint32_t android_swap_long(uint32_t v) -{ - return (v<<24) | ((v<<8)&0x00FF0000) | ((v>>8)&0x0000FF00) | (v>>24); -} - -static inline uint16_t android_swap_short(uint16_t v) -{ - return (v<<8) | (v>>8); -} - -#define dtohl(x) (android_swap_long(x)) -#define dtohs(x) (android_swap_short(x)) -#define htodl(x) (android_swap_long(x)) -#define htods(x) (android_swap_short(x)) - -#endif - -#endif // _LIBS_UTILS_BYTE_ORDER_H diff --git a/include/utils/CallStack.h b/include/utils/CallStack.h deleted file mode 100644 index c2c8ce514..000000000 --- a/include/utils/CallStack.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - * 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. - */ - -#ifndef ANDROID_CALLSTACK_H -#define ANDROID_CALLSTACK_H - -#include -#include - -#include - -// --------------------------------------------------------------------------- - -namespace android { - -class CallStack -{ -public: - enum { - MAX_DEPTH = 31 - }; - - CallStack(); - CallStack(const CallStack& rhs); - ~CallStack(); - - CallStack& operator = (const CallStack& rhs); - - bool operator == (const CallStack& rhs) const; - bool operator != (const CallStack& rhs) const; - bool operator < (const CallStack& rhs) const; - bool operator >= (const CallStack& rhs) const; - bool operator > (const CallStack& rhs) const; - bool operator <= (const CallStack& rhs) const; - - const void* operator [] (int index) const; - - void clear(); - - void update(int32_t ignoreDepth=0, int32_t maxDepth=MAX_DEPTH); - - // Dump a stack trace to the log - void dump(const char* prefix = 0) const; - - // Return a string (possibly very long) containing the complete stack trace - String8 toString(const char* prefix = 0) const; - - size_t size() const { return mCount; } - -private: - // Internal helper function - String8 toStringSingleLevel(const char* prefix, int32_t level) const; - - size_t mCount; - const void* mStack[MAX_DEPTH]; -}; - -}; // namespace android - - -// --------------------------------------------------------------------------- - -#endif // ANDROID_CALLSTACK_H diff --git a/include/utils/Debug.h b/include/utils/Debug.h deleted file mode 100644 index a662b9cc2..000000000 --- a/include/utils/Debug.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2005 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. - */ - -// -// Debugging tools. These should be able to be stripped -// in release builds. -// -#ifndef ANDROID_DEBUG_H -#define ANDROID_DEBUG_H - -#include -#include - -namespace android { - -template struct CompileTimeAssert; -template<> struct CompileTimeAssert {}; - -const char* stringForIndent(int32_t indentLevel); - -typedef void (*debugPrintFunc)(void* cookie, const char* txt); - -void printTypeCode(uint32_t typeCode, - debugPrintFunc func = 0, void* cookie = 0); -void printHexData(int32_t indent, const void *buf, size_t length, - size_t bytesPerLine=16, int32_t singleLineBytesCutoff=16, - size_t alignment=0, bool cArrayStyle=false, - debugPrintFunc func = 0, void* cookie = 0); - -}; // namespace android - -#endif // ANDROID_DEBUG_H diff --git a/include/utils/Endian.h b/include/utils/Endian.h deleted file mode 100644 index 19f250494..000000000 --- a/include/utils/Endian.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2005 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. - */ - -// -// Android endian-ness defines. -// -#ifndef _LIBS_UTILS_ENDIAN_H -#define _LIBS_UTILS_ENDIAN_H - -#if defined(HAVE_ENDIAN_H) - -#include - -#else /*not HAVE_ENDIAN_H*/ - -#define __BIG_ENDIAN 0x1000 -#define __LITTLE_ENDIAN 0x0001 - -#if defined(HAVE_LITTLE_ENDIAN) -# define __BYTE_ORDER __LITTLE_ENDIAN -#else -# define __BYTE_ORDER __BIG_ENDIAN -#endif - -#endif /*not HAVE_ENDIAN_H*/ - -#endif /*_LIBS_UTILS_ENDIAN_H*/ diff --git a/include/utils/Errors.h b/include/utils/Errors.h deleted file mode 100644 index 1bf9e6f2b..000000000 --- a/include/utils/Errors.h +++ /dev/null @@ -1,87 +0,0 @@ -/* - * 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. - */ - -#ifndef ANDROID_ERRORS_H -#define ANDROID_ERRORS_H - -#include -#include - -namespace android { - -// use this type to return error codes -#ifdef HAVE_MS_C_RUNTIME -typedef int status_t; -#else -typedef int32_t status_t; -#endif - -/* the MS C runtime lacks a few error codes */ - -/* - * Error codes. - * All error codes are negative values. - */ - -// Win32 #defines NO_ERROR as well. It has the same value, so there's no -// real conflict, though it's a bit awkward. -#ifdef _WIN32 -# undef NO_ERROR -#endif - -enum { - OK = 0, // Everything's swell. - NO_ERROR = 0, // No errors. - - UNKNOWN_ERROR = 0x80000000, - - NO_MEMORY = -ENOMEM, - INVALID_OPERATION = -ENOSYS, - BAD_VALUE = -EINVAL, - BAD_TYPE = 0x80000001, - NAME_NOT_FOUND = -ENOENT, - PERMISSION_DENIED = -EPERM, - NO_INIT = -ENODEV, - ALREADY_EXISTS = -EEXIST, - DEAD_OBJECT = -EPIPE, - FAILED_TRANSACTION = 0x80000002, - JPARKS_BROKE_IT = -EPIPE, -#if !defined(HAVE_MS_C_RUNTIME) - BAD_INDEX = -EOVERFLOW, - NOT_ENOUGH_DATA = -ENODATA, - WOULD_BLOCK = -EWOULDBLOCK, - TIMED_OUT = -ETIME, - UNKNOWN_TRANSACTION = -EBADMSG, -#else - BAD_INDEX = -E2BIG, - NOT_ENOUGH_DATA = 0x80000003, - WOULD_BLOCK = 0x80000004, - TIMED_OUT = 0x80000005, - UNKNOWN_TRANSACTION = 0x80000006, -#endif -}; - -// Restore define; enumeration is in "android" namespace, so the value defined -// there won't work for Win32 code in a different namespace. -#ifdef _WIN32 -# define NO_ERROR 0L -#endif - -}; // namespace android - -// --------------------------------------------------------------------------- - -#endif // ANDROID_ERRORS_H diff --git a/include/utils/FileMap.h b/include/utils/FileMap.h deleted file mode 100644 index 8dfd3bea6..000000000 --- a/include/utils/FileMap.h +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright (C) 2006 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. - */ - -// -// Encapsulate a shared file mapping. -// -#ifndef __LIBS_FILE_MAP_H -#define __LIBS_FILE_MAP_H - -#include - -#ifdef HAVE_WIN32_FILEMAP -#include -#endif - -namespace android { - -/* - * This represents a memory-mapped file. It might be the entire file or - * only part of it. This requires a little bookkeeping because the mapping - * needs to be aligned on page boundaries, and in some cases we'd like to - * have multiple references to the mapped area without creating additional - * maps. - * - * This always uses MAP_SHARED. - * - * TODO: we should be able to create a new FileMap that is a subset of - * an existing FileMap and shares the underlying mapped pages. Requires - * completing the refcounting stuff and possibly introducing the notion - * of a FileMap hierarchy. - */ -class FileMap { -public: - FileMap(void); - - /* - * Create a new mapping on an open file. - * - * Closing the file descriptor does not unmap the pages, so we don't - * claim ownership of the fd. - * - * Returns "false" on failure. - */ - bool create(const char* origFileName, int fd, - off_t offset, size_t length, bool readOnly); - - /* - * Return the name of the file this map came from, if known. - */ - const char* getFileName(void) const { return mFileName; } - - /* - * Get a pointer to the piece of the file we requested. - */ - void* getDataPtr(void) const { return mDataPtr; } - - /* - * Get the length we requested. - */ - size_t getDataLength(void) const { return mDataLength; } - - /* - * Get the data offset used to create this map. - */ - off_t getDataOffset(void) const { return mDataOffset; } - - /* - * Get a "copy" of the object. - */ - FileMap* acquire(void) { mRefCount++; return this; } - - /* - * Call this when mapping is no longer needed. - */ - void release(void) { - if (--mRefCount <= 0) - delete this; - } - - /* - * This maps directly to madvise() values, but allows us to avoid - * including everywhere. - */ - enum MapAdvice { - NORMAL, RANDOM, SEQUENTIAL, WILLNEED, DONTNEED - }; - - /* - * Apply an madvise() call to the entire file. - * - * Returns 0 on success, -1 on failure. - */ - int advise(MapAdvice advice); - -protected: - // don't delete objects; call release() - ~FileMap(void); - -private: - // these are not implemented - FileMap(const FileMap& src); - const FileMap& operator=(const FileMap& src); - - int mRefCount; // reference count - char* mFileName; // original file name, if known - void* mBasePtr; // base of mmap area; page aligned - size_t mBaseLength; // length, measured from "mBasePtr" - off_t mDataOffset; // offset used when map was created - void* mDataPtr; // start of requested data, offset from base - size_t mDataLength; // length, measured from "mDataPtr" -#ifdef HAVE_WIN32_FILEMAP - HANDLE mFileHandle; // Win32 file handle - HANDLE mFileMapping; // Win32 file mapping handle -#endif - - static long mPageSize; -}; - -}; // namespace android - -#endif // __LIBS_FILE_MAP_H diff --git a/include/utils/IBinder.h b/include/utils/IBinder.h deleted file mode 100644 index 737033090..000000000 --- a/include/utils/IBinder.h +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_IBINDER_H -#define ANDROID_IBINDER_H - -#include -#include -#include -#include - - -#define B_PACK_CHARS(c1, c2, c3, c4) \ - ((((c1)<<24)) | (((c2)<<16)) | (((c3)<<8)) | (c4)) - -// --------------------------------------------------------------------------- -namespace android { - -class BBinder; -class BpBinder; -class IInterface; -class Parcel; - -/** - * Base class and low-level protocol for a remotable object. - * You can derive from this class to create an object for which other - * processes can hold references to it. Communication between processes - * (method calls, property get and set) is down through a low-level - * protocol implemented on top of the transact() API. - */ -class IBinder : public virtual RefBase -{ -public: - enum { - FIRST_CALL_TRANSACTION = 0x00000001, - LAST_CALL_TRANSACTION = 0x00ffffff, - - PING_TRANSACTION = B_PACK_CHARS('_','P','N','G'), - DUMP_TRANSACTION = B_PACK_CHARS('_','D','M','P'), - INTERFACE_TRANSACTION = B_PACK_CHARS('_', 'N', 'T', 'F'), - - // Corresponds to tfOneWay -- an asynchronous call. - FLAG_ONEWAY = 0x00000001 - }; - - inline IBinder() { } - - /** - * Check if this IBinder implements the interface named by - * @a descriptor. If it does, the base pointer to it is returned, - * which you can safely static_cast<> to the concrete C++ interface. - */ - virtual sp queryLocalInterface(const String16& descriptor); - - /** - * Return the canonical name of the interface provided by this IBinder - * object. - */ - virtual String16 getInterfaceDescriptor() const = 0; - - virtual bool isBinderAlive() const = 0; - virtual status_t pingBinder() = 0; - virtual status_t dump(int fd, const Vector& args) = 0; - - virtual status_t transact( uint32_t code, - const Parcel& data, - Parcel* reply, - uint32_t flags = 0) = 0; - - /** - * This method allows you to add data that is transported through - * IPC along with your IBinder pointer. When implementing a Binder - * object, override it to write your desired data in to @a outData. - * You can then call getConstantData() on your IBinder to retrieve - * that data, from any process. You MUST return the number of bytes - * written in to the parcel (including padding). - */ - class DeathRecipient : public virtual RefBase - { - public: - virtual void binderDied(const wp& who) = 0; - }; - - /** - * Register the @a recipient for a notification if this binder - * goes away. If this binder object unexpectedly goes away - * (typically because its hosting process has been killed), - * then DeathRecipient::binderDied() will be called with a referene - * to this. - * - * The @a cookie is optional -- if non-NULL, it should be a - * memory address that you own (that is, you know it is unique). - * - * @note You will only receive death notifications for remote binders, - * as local binders by definition can't die without you dying as well. - * Trying to use this function on a local binder will result in an - * INVALID_OPERATION code being returned and nothing happening. - * - * @note This link always holds a weak reference to its recipient. - * - * @note You will only receive a weak reference to the dead - * binder. You should not try to promote this to a strong reference. - * (Nor should you need to, as there is nothing useful you can - * directly do with it now that it has passed on.) - */ - virtual status_t linkToDeath(const sp& recipient, - void* cookie = NULL, - uint32_t flags = 0) = 0; - - /** - * Remove a previously registered death notification. - * The @a recipient will no longer be called if this object - * dies. The @a cookie is optional. If non-NULL, you can - * supply a NULL @a recipient, and the recipient previously - * added with that cookie will be unlinked. - */ - virtual status_t unlinkToDeath( const wp& recipient, - void* cookie = NULL, - uint32_t flags = 0, - wp* outRecipient = NULL) = 0; - - virtual bool checkSubclass(const void* subclassID) const; - - typedef void (*object_cleanup_func)(const void* id, void* obj, void* cleanupCookie); - - virtual void attachObject( const void* objectID, - void* object, - void* cleanupCookie, - object_cleanup_func func) = 0; - virtual void* findObject(const void* objectID) const = 0; - virtual void detachObject(const void* objectID) = 0; - - virtual BBinder* localBinder(); - virtual BpBinder* remoteBinder(); - -protected: - inline virtual ~IBinder() { } - -private: -}; - -}; // namespace android - -// --------------------------------------------------------------------------- - -#endif // ANDROID_IBINDER_H diff --git a/include/utils/IInterface.h b/include/utils/IInterface.h deleted file mode 100644 index 959722a4d..000000000 --- a/include/utils/IInterface.h +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright (C) 2005 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_IINTERFACE_H -#define ANDROID_IINTERFACE_H - -#include - -namespace android { - -// ---------------------------------------------------------------------- - -class IInterface : public virtual RefBase -{ -public: - sp asBinder(); - sp asBinder() const; - -protected: - virtual IBinder* onAsBinder() = 0; -}; - -// ---------------------------------------------------------------------- - -template -inline sp interface_cast(const sp& obj) -{ - return INTERFACE::asInterface(obj); -} - -// ---------------------------------------------------------------------- - -template -class BnInterface : public INTERFACE, public BBinder -{ -public: - virtual sp queryLocalInterface(const String16& _descriptor); - virtual String16 getInterfaceDescriptor() const; - -protected: - virtual IBinder* onAsBinder(); -}; - -// ---------------------------------------------------------------------- - -template -class BpInterface : public INTERFACE, public BpRefBase -{ -public: - BpInterface(const sp& remote); - -protected: - virtual IBinder* onAsBinder(); -}; - -// ---------------------------------------------------------------------- - -#define DECLARE_META_INTERFACE(INTERFACE) \ - static const String16 descriptor; \ - static sp asInterface(const sp& obj); \ - virtual String16 getInterfaceDescriptor() const; \ - -#define IMPLEMENT_META_INTERFACE(INTERFACE, NAME) \ - const String16 I##INTERFACE::descriptor(NAME); \ - String16 I##INTERFACE::getInterfaceDescriptor() const { \ - return I##INTERFACE::descriptor; \ - } \ - sp I##INTERFACE::asInterface(const sp& obj) \ - { \ - sp intr; \ - if (obj != NULL) { \ - intr = static_cast( \ - obj->queryLocalInterface( \ - I##INTERFACE::descriptor).get()); \ - if (intr == NULL) { \ - intr = new Bp##INTERFACE(obj); \ - } \ - } \ - return intr; \ - } \ - -// ---------------------------------------------------------------------- -// No user-servicable parts after this... - -template -inline sp BnInterface::queryLocalInterface( - const String16& _descriptor) -{ - if (_descriptor == INTERFACE::descriptor) return this; - return NULL; -} - -template -inline String16 BnInterface::getInterfaceDescriptor() const -{ - return INTERFACE::getInterfaceDescriptor(); -} - -template -IBinder* BnInterface::onAsBinder() -{ - return this; -} - -template -inline BpInterface::BpInterface(const sp& remote) - : BpRefBase(remote) -{ -} - -template -inline IBinder* BpInterface::onAsBinder() -{ - return remote(); -} - -// ---------------------------------------------------------------------- - -}; // namespace android - -#endif // ANDROID_IINTERFACE_H diff --git a/include/utils/IMemory.h b/include/utils/IMemory.h deleted file mode 100644 index 35a3fd7da..000000000 --- a/include/utils/IMemory.h +++ /dev/null @@ -1,94 +0,0 @@ -/* - * 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. - */ - -#ifndef ANDROID_IMEMORY_H -#define ANDROID_IMEMORY_H - -#include -#include -#include - -#include -#include -#include - -namespace android { - -// ---------------------------------------------------------------------------- - -class IMemoryHeap : public IInterface -{ -public: - DECLARE_META_INTERFACE(MemoryHeap); - - // flags returned by getFlags() - enum { - READ_ONLY = 0x00000001, - MAP_ONCE = 0x00000002 - }; - - virtual int getHeapID() const = 0; - virtual void* getBase() const = 0; - virtual size_t getSize() const = 0; - virtual uint32_t getFlags() const = 0; - - // these are there just for backward source compatibility - int32_t heapID() const { return getHeapID(); } - void* base() const { return getBase(); } - size_t virtualSize() const { return getSize(); } -}; - -class BnMemoryHeap : public BnInterface -{ -public: - virtual status_t onTransact( - uint32_t code, - const Parcel& data, - Parcel* reply, - uint32_t flags = 0); -}; - -// ---------------------------------------------------------------------------- - -class IMemory : public IInterface -{ -public: - DECLARE_META_INTERFACE(Memory); - - virtual sp getMemory(ssize_t* offset=0, size_t* size=0) const = 0; - - // helpers - void* fastPointer(const sp& heap, ssize_t offset) const; - void* pointer() const; - size_t size() const; - ssize_t offset() const; -}; - -class BnMemory : public BnInterface -{ -public: - virtual status_t onTransact( - uint32_t code, - const Parcel& data, - Parcel* reply, - uint32_t flags = 0); -}; - -// ---------------------------------------------------------------------------- - -}; // namespace android - -#endif // ANDROID_IMEMORY_H diff --git a/include/utils/IPCThreadState.h b/include/utils/IPCThreadState.h deleted file mode 100644 index 0490fd3ec..000000000 --- a/include/utils/IPCThreadState.h +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (C) 2005 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_IPC_THREAD_STATE_H -#define ANDROID_IPC_THREAD_STATE_H - -#include -#include -#include -#include - -#ifdef HAVE_WIN32_PROC -typedef int uid_t; -#endif - -// --------------------------------------------------------------------------- -namespace android { - -class IPCThreadState -{ -public: - static IPCThreadState* self(); - - sp process(); - - status_t clearLastError(); - - int getCallingPid(); - int getCallingUid(); - - int64_t clearCallingIdentity(); - void restoreCallingIdentity(int64_t token); - - void flushCommands(); - - void joinThreadPool(bool isMain = true); - - // Stop the local process. - void stopProcess(bool immediate = true); - - status_t transact(int32_t handle, - uint32_t code, const Parcel& data, - Parcel* reply, uint32_t flags); - - void incStrongHandle(int32_t handle); - void decStrongHandle(int32_t handle); - void incWeakHandle(int32_t handle); - void decWeakHandle(int32_t handle); - status_t attemptIncStrongHandle(int32_t handle); - static void expungeHandle(int32_t handle, IBinder* binder); - status_t requestDeathNotification( int32_t handle, - BpBinder* proxy); - status_t clearDeathNotification( int32_t handle, - BpBinder* proxy); - - static void shutdown(); - -private: - IPCThreadState(); - ~IPCThreadState(); - - status_t sendReply(const Parcel& reply, uint32_t flags); - status_t waitForResponse(Parcel *reply, - status_t *acquireResult=NULL); - status_t talkWithDriver(bool doReceive=true); - status_t writeTransactionData(int32_t cmd, - uint32_t binderFlags, - int32_t handle, - uint32_t code, - const Parcel& data, - status_t* statusBuffer); - status_t executeCommand(int32_t command); - - void clearCaller(); - - static void threadDestructor(void *st); - static void freeBuffer(Parcel* parcel, - const uint8_t* data, size_t dataSize, - const size_t* objects, size_t objectsSize, - void* cookie); - - const sp mProcess; - Vector mPendingStrongDerefs; - Vector mPendingWeakDerefs; - - Parcel mIn; - Parcel mOut; - status_t mLastError; - pid_t mCallingPid; - uid_t mCallingUid; -}; - -}; // namespace android - -// --------------------------------------------------------------------------- - -#endif // ANDROID_IPC_THREAD_STATE_H diff --git a/include/utils/IPermissionController.h b/include/utils/IPermissionController.h deleted file mode 100644 index cb1dd345d..000000000 --- a/include/utils/IPermissionController.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2005 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_IPERMISSION_CONTROLLER_H -#define ANDROID_IPERMISSION_CONTROLLER_H - -#include - -namespace android { - -// ---------------------------------------------------------------------- - -class IPermissionController : public IInterface -{ -public: - DECLARE_META_INTERFACE(PermissionController); - - virtual bool checkPermission(const String16& permission, - int32_t pid, int32_t uid) = 0; - - enum { - CHECK_PERMISSION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION - }; -}; - -// ---------------------------------------------------------------------- - -class BnPermissionController : public BnInterface -{ -public: - virtual status_t onTransact( uint32_t code, - const Parcel& data, - Parcel* reply, - uint32_t flags = 0); -}; - -// ---------------------------------------------------------------------- - -}; // namespace android - -#endif // ANDROID_IPERMISSION_CONTROLLER_H - diff --git a/include/utils/IServiceManager.h b/include/utils/IServiceManager.h deleted file mode 100644 index e3d99fe7e..000000000 --- a/include/utils/IServiceManager.h +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (C) 2005 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_ISERVICE_MANAGER_H -#define ANDROID_ISERVICE_MANAGER_H - -#include -#include -#include -#include - -namespace android { - -// ---------------------------------------------------------------------- - -class IServiceManager : public IInterface -{ -public: - DECLARE_META_INTERFACE(ServiceManager); - - /** - * Retrieve an existing service, blocking for a few seconds - * if it doesn't yet exist. - */ - virtual sp getService( const String16& name) const = 0; - - /** - * Retrieve an existing service, non-blocking. - */ - virtual sp checkService( const String16& name) const = 0; - - /** - * Register a service. - */ - virtual status_t addService( const String16& name, - const sp& service) = 0; - - /** - * Return list of all existing services. - */ - virtual Vector listServices() = 0; - - enum { - GET_SERVICE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION, - CHECK_SERVICE_TRANSACTION, - ADD_SERVICE_TRANSACTION, - LIST_SERVICES_TRANSACTION, - }; -}; - -sp defaultServiceManager(); - -template -status_t getService(const String16& name, sp* outService) -{ - const sp sm = defaultServiceManager(); - if (sm != NULL) { - *outService = interface_cast(sm->getService(name)); - if ((*outService) != NULL) return NO_ERROR; - } - return NAME_NOT_FOUND; -} - -bool checkCallingPermission(const String16& permission); -bool checkCallingPermission(const String16& permission, - int32_t* outPid, int32_t* outUid); - -// ---------------------------------------------------------------------- - -class BnServiceManager : public BnInterface -{ -public: - virtual status_t onTransact( uint32_t code, - const Parcel& data, - Parcel* reply, - uint32_t flags = 0); -}; - -// ---------------------------------------------------------------------- - -}; // namespace android - -#endif // ANDROID_ISERVICE_MANAGER_H - diff --git a/include/utils/KeyedVector.h b/include/utils/KeyedVector.h deleted file mode 100644 index f4513ee20..000000000 --- a/include/utils/KeyedVector.h +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Copyright (C) 2005 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_KEYED_VECTOR_H -#define ANDROID_KEYED_VECTOR_H - -#include -#include -#include - -#include -#include -#include - -// --------------------------------------------------------------------------- - -namespace android { - -template -class KeyedVector -{ -public: - typedef KEY key_type; - typedef VALUE value_type; - - inline KeyedVector(); - - /* - * empty the vector - */ - - inline void clear() { mVector.clear(); } - - /*! - * vector stats - */ - - //! returns number of items in the vector - inline size_t size() const { return mVector.size(); } - //! returns wether or not the vector is empty - inline bool isEmpty() const { return mVector.isEmpty(); } - //! returns how many items can be stored without reallocating the backing store - inline size_t capacity() const { return mVector.capacity(); } - //! setst the capacity. capacity can never be reduced less than size() - inline ssize_t setCapacity(size_t size) { return mVector.setCapacity(size); } - - /*! - * accessors - */ - const VALUE& valueFor(const KEY& key) const; - const VALUE& valueAt(size_t index) const; - const KEY& keyAt(size_t index) const; - ssize_t indexOfKey(const KEY& key) const; - - /*! - * modifing the array - */ - - VALUE& editValueFor(const KEY& key); - VALUE& editValueAt(size_t index); - - /*! - * add/insert/replace items - */ - - ssize_t add(const KEY& key, const VALUE& item); - ssize_t replaceValueFor(const KEY& key, const VALUE& item); - ssize_t replaceValueAt(size_t index, const VALUE& item); - - /*! - * remove items - */ - - ssize_t removeItem(const KEY& key); - ssize_t removeItemsAt(size_t index, size_t count = 1); - -private: - SortedVector< key_value_pair_t > mVector; -}; - -// --------------------------------------------------------------------------- - -/** - * Variation of KeyedVector that holds a default value to return when - * valueFor() is called with a key that doesn't exist. - */ -template -class DefaultKeyedVector : public KeyedVector -{ -public: - inline DefaultKeyedVector(const VALUE& defValue = VALUE()); - const VALUE& valueFor(const KEY& key) const; - -private: - VALUE mDefault; -}; - -// --------------------------------------------------------------------------- - -template inline -KeyedVector::KeyedVector() -{ -} - -template inline -ssize_t KeyedVector::indexOfKey(const KEY& key) const { - return mVector.indexOf( key_value_pair_t(key) ); -} - -template inline -const VALUE& KeyedVector::valueFor(const KEY& key) const { - ssize_t i = indexOfKey(key); - assert(i>=0); - return mVector.itemAt(i).value; -} - -template inline -const VALUE& KeyedVector::valueAt(size_t index) const { - return mVector.itemAt(index).value; -} - -template inline -const KEY& KeyedVector::keyAt(size_t index) const { - return mVector.itemAt(index).key; -} - -template inline -VALUE& KeyedVector::editValueFor(const KEY& key) { - ssize_t i = indexOfKey(key); - assert(i>=0); - return mVector.editItemAt(i).value; -} - -template inline -VALUE& KeyedVector::editValueAt(size_t index) { - return mVector.editItemAt(index).value; -} - -template inline -ssize_t KeyedVector::add(const KEY& key, const VALUE& value) { - return mVector.add( key_value_pair_t(key, value) ); -} - -template inline -ssize_t KeyedVector::replaceValueFor(const KEY& key, const VALUE& value) { - key_value_pair_t pair(key, value); - mVector.remove(pair); - return mVector.add(pair); -} - -template inline -ssize_t KeyedVector::replaceValueAt(size_t index, const VALUE& item) { - if (index inline -ssize_t KeyedVector::removeItem(const KEY& key) { - return mVector.remove(key_value_pair_t(key)); -} - -template inline -ssize_t KeyedVector::removeItemsAt(size_t index, size_t count) { - return mVector.removeItemsAt(index, count); -} - -// --------------------------------------------------------------------------- - -template inline -DefaultKeyedVector::DefaultKeyedVector(const VALUE& defValue) - : mDefault(defValue) -{ -} - -template inline -const VALUE& DefaultKeyedVector::valueFor(const KEY& key) const { - ssize_t i = indexOfKey(key); - return i >= 0 ? KeyedVector::valueAt(i) : mDefault; -} - -}; // namespace android - -// --------------------------------------------------------------------------- - -#endif // ANDROID_KEYED_VECTOR_H diff --git a/include/utils/List.h b/include/utils/List.h deleted file mode 100644 index 1a6be9ac9..000000000 --- a/include/utils/List.h +++ /dev/null @@ -1,280 +0,0 @@ -/* - * Copyright (C) 2005 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. - */ - -// -// Templated list class. Normally we'd use STL, but we don't have that. -// This class mimics STL's interfaces. -// -// Objects are copied into the list with the '=' operator or with copy- -// construction, so if the compiler's auto-generated versions won't work for -// you, define your own. -// -// The only class you want to use from here is "List". Do not use classes -// starting with "_" directly. -// -#ifndef _LIBS_UTILS_LIST_H -#define _LIBS_UTILS_LIST_H - -namespace android { - -/* - * One element in the list. - */ -template class _ListNode { -public: - typedef _ListNode _Node; - - _ListNode(const T& val) : mVal(val) {} - ~_ListNode(void) {} - - T& getRef(void) { return mVal; } - void setVal(const T& val) { mVal = val; } - - _Node* getPrev(void) const { return mpPrev; } - void setPrev(_Node* ptr) { mpPrev = ptr; } - _Node* getNext(void) const { return mpNext; } - void setNext(_Node* ptr) { mpNext = ptr; } - -private: - T mVal; - _Node* mpPrev; - _Node* mpNext; -}; - -/* - * Iterator for walking through the list. - */ -template class _ListIterator { -public: - typedef _ListIterator _Iter; - typedef _ListNode _Node; - - _ListIterator(void) {} - _ListIterator(_Node* ptr) : mpNode(ptr) {} - ~_ListIterator(void) {} - - /* - * Dereference operator. Used to get at the juicy insides. - */ - Tref operator*() const { return mpNode->getRef(); } - - /* - * Iterator comparison. - */ - bool operator==(const _Iter& right) const { return mpNode == right.mpNode; } - bool operator!=(const _Iter& right) const { return mpNode != right.mpNode; } - - /* - * Incr/decr, used to move through the list. - */ - _Iter& operator++(void) { // pre-increment - mpNode = mpNode->getNext(); - return *this; - } - _Iter operator++(int) { // post-increment - _Iter tmp = *this; - ++*this; - return tmp; - } - _Iter& operator--(void) { // pre-increment - mpNode = mpNode->getPrev(); - return *this; - } - _Iter operator--(int) { // post-increment - _Iter tmp = *this; - --*this; - return tmp; - } - - _Node* getNode(void) const { return mpNode; } - -private: - _Node* mpNode; -}; - - -/* - * Doubly-linked list. Instantiate with "List myList". - * - * Objects added to the list are copied using the assignment operator, - * so this must be defined. - */ -template class List { -public: - typedef _ListNode _Node; - - List(void) { - prep(); - } - List(const List& src) { // copy-constructor - prep(); - insert(begin(), src.begin(), src.end()); - } - virtual ~List(void) { - clear(); - delete[] (unsigned char*) mpMiddle; - } - - typedef _ListIterator iterator; - typedef _ListIterator const_iterator; - - List& operator=(const List& right); - - /* returns true if the list is empty */ - bool empty(void) const { return mpMiddle->getNext() == mpMiddle; } - - /* return #of elements in list */ - unsigned int size(void) const { - return distance(begin(), end()); - } - - /* - * Return the first element or one past the last element. The - * _ListNode* we're returning is converted to an "iterator" by a - * constructor in _ListIterator. - */ - iterator begin() { return mpMiddle->getNext(); } - const_iterator begin() const { return mpMiddle->getNext(); } - iterator end() { return mpMiddle; } - const_iterator end() const { return mpMiddle; } - - /* add the object to the head or tail of the list */ - void push_front(const T& val) { insert(begin(), val); } - void push_back(const T& val) { insert(end(), val); } - - /* insert before the current node; returns iterator at new node */ - iterator insert(iterator posn, const T& val) { - _Node* newNode = new _Node(val); // alloc & copy-construct - newNode->setNext(posn.getNode()); - newNode->setPrev(posn.getNode()->getPrev()); - posn.getNode()->getPrev()->setNext(newNode); - posn.getNode()->setPrev(newNode); - return newNode; - } - - /* insert a range of elements before the current node */ - void insert(iterator posn, const_iterator first, const_iterator last) { - for ( ; first != last; ++first) - insert(posn, *first); - } - - /* remove one entry; returns iterator at next node */ - iterator erase(iterator posn) { - _Node* pNext = posn.getNode()->getNext(); - _Node* pPrev = posn.getNode()->getPrev(); - pPrev->setNext(pNext); - pNext->setPrev(pPrev); - delete posn.getNode(); - return pNext; - } - - /* remove a range of elements */ - iterator erase(iterator first, iterator last) { - while (first != last) - erase(first++); // don't erase than incr later! - return last; - } - - /* remove all contents of the list */ - void clear(void) { - _Node* pCurrent = mpMiddle->getNext(); - _Node* pNext; - - while (pCurrent != mpMiddle) { - pNext = pCurrent->getNext(); - delete pCurrent; - pCurrent = pNext; - } - mpMiddle->setPrev(mpMiddle); - mpMiddle->setNext(mpMiddle); - } - - /* - * Measure the distance between two iterators. On exist, "first" - * will be equal to "last". The iterators must refer to the same - * list. - * - * (This is actually a generic iterator function. It should be part - * of some other class, possibly an iterator base class. It needs to - * know the difference between a list, which has to march through, - * and a vector, which can just do pointer math.) - */ - unsigned int distance(iterator first, iterator last) { - unsigned int count = 0; - while (first != last) { - ++first; - ++count; - } - return count; - } - unsigned int distance(const_iterator first, const_iterator last) const { - unsigned int count = 0; - while (first != last) { - ++first; - ++count; - } - return count; - } - -private: - /* - * I want a _ListNode but don't need it to hold valid data. More - * to the point, I don't want T's constructor to fire, since it - * might have side-effects or require arguments. So, we do this - * slightly uncouth storage alloc. - */ - void prep(void) { - mpMiddle = (_Node*) new unsigned char[sizeof(_Node)]; - mpMiddle->setPrev(mpMiddle); - mpMiddle->setNext(mpMiddle); - } - - /* - * This node plays the role of "pointer to head" and "pointer to tail". - * It sits in the middle of a circular list of nodes. The iterator - * runs around the circle until it encounters this one. - */ - _Node* mpMiddle; -}; - -/* - * Assignment operator. - * - * The simplest way to do this would be to clear out the target list and - * fill it with the source. However, we can speed things along by - * re-using existing elements. - */ -template -List& List::operator=(const List& right) -{ - if (this == &right) - return *this; // self-assignment - iterator firstDst = begin(); - iterator lastDst = end(); - const_iterator firstSrc = right.begin(); - const_iterator lastSrc = right.end(); - while (firstSrc != lastSrc && firstDst != lastDst) - *firstDst++ = *firstSrc++; - if (firstSrc == lastSrc) // ran out of elements in source? - erase(firstDst, lastDst); // yes, erase any extras - else - insert(lastDst, firstSrc, lastSrc); // copy remaining over - return *this; -} - -}; // namespace android - -#endif // _LIBS_UTILS_LIST_H diff --git a/include/utils/Log.h b/include/utils/Log.h deleted file mode 100644 index 3c6cc8bdc..000000000 --- a/include/utils/Log.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2005 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. - */ - -// -// C/C++ logging functions. See the logging documentation for API details. -// -// We'd like these to be available from C code (in case we import some from -// somewhere), so this has a C interface. -// -// The output will be correct when the log file is shared between multiple -// threads and/or multiple processes so long as the operating system -// supports O_APPEND. These calls have mutex-protected data structures -// and so are NOT reentrant. Do not use LOG in a signal handler. -// -#ifndef _LIBS_UTILS_LOG_H -#define _LIBS_UTILS_LOG_H - -#include - -#endif // _LIBS_UTILS_LOG_H diff --git a/include/utils/LogSocket.h b/include/utils/LogSocket.h deleted file mode 100644 index 01fbfb50e..000000000 --- a/include/utils/LogSocket.h +++ /dev/null @@ -1,20 +0,0 @@ -/* utils/LogSocket.h -** -** Copyright 2008, The Android Open Source Project -** -** This file is dual licensed. It may be redistributed and/or modified -** under the terms of the Apache 2.0 License OR version 2 of the GNU -** General Public License. -*/ - -#ifndef _UTILS_LOGSOCKET_H -#define _UTILS_LOGSOCKET_H - -#define SOCKET_CLOSE_LOCAL 0 - -void add_send_stats(int fd, int send); -void add_recv_stats(int fd, int recv); -void log_socket_close(int fd, short reason); -void log_socket_connect(int fd, unsigned int ip, unsigned short port); - -#endif /* _UTILS_LOGSOCKET_H */ diff --git a/include/utils/MemoryBase.h b/include/utils/MemoryBase.h deleted file mode 100644 index eb5a9d275..000000000 --- a/include/utils/MemoryBase.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_MEMORY_BASE_H -#define ANDROID_MEMORY_BASE_H - -#include -#include - -#include - - -namespace android { - -// --------------------------------------------------------------------------- - -class MemoryBase : public BnMemory -{ -public: - MemoryBase(const sp& heap, ssize_t offset, size_t size); - virtual ~MemoryBase(); - virtual sp getMemory(ssize_t* offset, size_t* size) const; - -protected: - size_t getSize() const { return mSize; } - ssize_t getOffset() const { return mOffset; } - const sp& getHeap() const { return mHeap; } - -private: - size_t mSize; - ssize_t mOffset; - sp mHeap; -}; - -// --------------------------------------------------------------------------- -}; // namespace android - -#endif // ANDROID_MEMORY_BASE_H diff --git a/include/utils/MemoryDealer.h b/include/utils/MemoryDealer.h deleted file mode 100644 index 454b6270e..000000000 --- a/include/utils/MemoryDealer.h +++ /dev/null @@ -1,238 +0,0 @@ -/* - * 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. - */ - -#ifndef ANDROID_MEMORY_DEALER_H -#define ANDROID_MEMORY_DEALER_H - - -#include -#include - -#include -#include -#include - -namespace android { -// ---------------------------------------------------------------------------- -class String8; - -/* - * interface for implementing a "heap". A heap basically provides - * the IMemoryHeap interface for cross-process sharing and the - * ability to map/unmap pages within the heap. - */ -class HeapInterface : public virtual BnMemoryHeap -{ -public: - // all values must be page-aligned - virtual sp mapMemory(size_t offset, size_t size) = 0; -}; - -// ---------------------------------------------------------------------------- - -/* - * interface for implementing an allocator. An allocator provides - * methods for allocating and freeing memory blocks and dumping - * its state. - */ -class AllocatorInterface : public RefBase -{ -public: - enum { - PAGE_ALIGNED = 0x00000001 - }; - - virtual size_t allocate(size_t size, uint32_t flags = 0) = 0; - virtual status_t deallocate(size_t offset) = 0; - virtual size_t size() const = 0; - virtual void dump(const char* what, uint32_t flags = 0) const = 0; - virtual void dump(String8& res, - const char* what, uint32_t flags = 0) const = 0; -}; - -// ---------------------------------------------------------------------------- - -/* - * concrete implementation of HeapInterface on top of mmap() - */ -class SharedHeap : public HeapInterface, public MemoryHeapBase -{ -public: - SharedHeap(size_t size, uint32_t flags = 0, char const * name = NULL); - virtual ~SharedHeap(); - virtual sp mapMemory(size_t offset, size_t size); -}; - -// ---------------------------------------------------------------------------- - -/* - * A simple templatized doubly linked-list implementation - */ - -template -class LinkedList -{ - NODE* mFirst; - NODE* mLast; - -public: - LinkedList() : mFirst(0), mLast(0) { } - bool isEmpty() const { return mFirst == 0; } - NODE const* head() const { return mFirst; } - NODE* head() { return mFirst; } - NODE const* tail() const { return mLast; } - NODE* tail() { return mLast; } - - void insertAfter(NODE* node, NODE* newNode) { - newNode->prev = node; - newNode->next = node->next; - if (node->next == 0) mLast = newNode; - else node->next->prev = newNode; - node->next = newNode; - } - - void insertBefore(NODE* node, NODE* newNode) { - newNode->prev = node->prev; - newNode->next = node; - if (node->prev == 0) mFirst = newNode; - else node->prev->next = newNode; - node->prev = newNode; - } - - void insertHead(NODE* newNode) { - if (mFirst == 0) { - mFirst = mLast = newNode; - newNode->prev = newNode->next = 0; - } else { - insertBefore(mFirst, newNode); - } - } - - void insertTail(NODE* newNode) { - if (mLast == 0) insertBeginning(newNode); - else insertAfter(mLast, newNode); - } - - NODE* remove(NODE* node) { - if (node->prev == 0) mFirst = node->next; - else node->prev->next = node->next; - if (node->next == 0) mLast = node->prev; - else node->next->prev = node->prev; - return node; - } -}; - - -/* - * concrete implementation of AllocatorInterface using a simple - * best-fit allocation scheme - */ -class SimpleBestFitAllocator : public AllocatorInterface -{ -public: - - SimpleBestFitAllocator(size_t size); - virtual ~SimpleBestFitAllocator(); - - virtual size_t allocate(size_t size, uint32_t flags = 0); - virtual status_t deallocate(size_t offset); - virtual size_t size() const; - virtual void dump(const char* what, uint32_t flags = 0) const; - virtual void dump(String8& res, - const char* what, uint32_t flags = 0) const; - -private: - - struct chunk_t { - chunk_t(size_t start, size_t size) - : start(start), size(size), free(1), prev(0), next(0) { - } - size_t start; - size_t size : 28; - int free : 4; - mutable chunk_t* prev; - mutable chunk_t* next; - }; - - ssize_t alloc(size_t size, uint32_t flags); - chunk_t* dealloc(size_t start); - void dump_l(const char* what, uint32_t flags = 0) const; - void dump_l(String8& res, const char* what, uint32_t flags = 0) const; - - static const int kMemoryAlign; - mutable Mutex mLock; - LinkedList mList; - size_t mHeapSize; -}; - -// ---------------------------------------------------------------------------- - -class MemoryDealer : public RefBase -{ -public: - - enum { - READ_ONLY = MemoryHeapBase::READ_ONLY, - PAGE_ALIGNED = AllocatorInterface::PAGE_ALIGNED - }; - - // creates a memory dealer with the SharedHeap and SimpleBestFitAllocator - MemoryDealer(size_t size, uint32_t flags = 0, const char* name = 0); - - // provide a custom heap but use the SimpleBestFitAllocator - MemoryDealer(const sp& heap); - - // provide both custom heap and allocotar - MemoryDealer( - const sp& heap, - const sp& allocator); - - virtual ~MemoryDealer(); - - virtual sp allocate(size_t size, uint32_t flags = 0); - virtual void deallocate(size_t offset); - virtual void dump(const char* what, uint32_t flags = 0) const; - - - sp getMemoryHeap() const { return heap(); } - sp getAllocator() const { return allocator(); } - -private: - const sp& heap() const; - const sp& allocator() const; - - class Allocation : public BnMemory { - public: - Allocation(const sp& dealer, - ssize_t offset, size_t size, const sp& memory); - virtual ~Allocation(); - virtual sp getMemory(ssize_t* offset, size_t* size) const; - private: - sp mDealer; - ssize_t mOffset; - size_t mSize; - sp mMemory; - }; - - sp mHeap; - sp mAllocator; -}; - - -// ---------------------------------------------------------------------------- -}; // namespace android - -#endif // ANDROID_MEMORY_DEALER_H diff --git a/include/utils/MemoryHeapBase.h b/include/utils/MemoryHeapBase.h deleted file mode 100644 index 574acf4f9..000000000 --- a/include/utils/MemoryHeapBase.h +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_MEMORY_HEAP_BASE_H -#define ANDROID_MEMORY_HEAP_BASE_H - -#include -#include - -#include - - -namespace android { - -// --------------------------------------------------------------------------- - -class MemoryHeapBase : public virtual BnMemoryHeap -{ -public: - enum { - READ_ONLY = IMemoryHeap::READ_ONLY, - MAP_ONCE = IMemoryHeap::MAP_ONCE, - // memory won't be mapped locally, but will be mapped in the remote - // process. - DONT_MAP_LOCALLY = 0x00000100 - }; - - /* - * maps the memory referenced by fd. but DOESN'T take ownership - * of the filedescriptor (it makes a copy with dup() - */ - MemoryHeapBase(int fd, size_t size, uint32_t flags = 0); - - /* - * maps memory from the given device - */ - MemoryHeapBase(const char* device, size_t size = 0, uint32_t flags = 0); - - /* - * maps memory from ashmem, with the given name for debugging - */ - MemoryHeapBase(size_t size, uint32_t flags = 0, char const* name = NULL); - - virtual ~MemoryHeapBase(); - - /* implement IMemoryHeap interface */ - virtual int getHeapID() const; - virtual void* getBase() const; - virtual size_t getSize() const; - virtual uint32_t getFlags() const; - - const char* getDevice() const; - - /* this closes this heap -- use carefully */ - void dispose(); - - /* this is only needed as a workaround, use only if you know - * what you are doing */ - status_t setDevice(const char* device) { - if (mDevice == 0) - mDevice = device; - return mDevice ? NO_ERROR : ALREADY_EXISTS; - } - -protected: - MemoryHeapBase(); - // init() takes ownership of fd - status_t init(int fd, void *base, int size, - int flags = 0, const char* device = NULL); - -private: - status_t mapfd(int fd, size_t size); - - int mFD; - size_t mSize; - void* mBase; - uint32_t mFlags; - const char* mDevice; - bool mNeedUnmap; -}; - -// --------------------------------------------------------------------------- -}; // namespace android - -#endif // ANDROID_MEMORY_HEAP_BASE_H diff --git a/include/utils/MemoryHeapPmem.h b/include/utils/MemoryHeapPmem.h deleted file mode 100644 index 60335adae..000000000 --- a/include/utils/MemoryHeapPmem.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_MEMORY_HEAP_PMEM_H -#define ANDROID_MEMORY_HEAP_PMEM_H - -#include -#include - -#include -#include -#include -#include - -namespace android { - -class MemoryHeapBase; - -// --------------------------------------------------------------------------- - -class MemoryHeapPmem : public HeapInterface, public MemoryHeapBase -{ -public: - class MemoryPmem : public BnMemory { - public: - MemoryPmem(const sp& heap); - ~MemoryPmem(); - protected: - const sp& getHeap() const { return mClientHeap; } - private: - friend class MemoryHeapPmem; - virtual void revoke() = 0; - sp mClientHeap; - }; - - MemoryHeapPmem(const sp& pmemHeap, - uint32_t flags = IMemoryHeap::MAP_ONCE); - ~MemoryHeapPmem(); - - /* HeapInterface additions */ - virtual sp mapMemory(size_t offset, size_t size); - - /* make the whole heap visible (you know who you are) */ - virtual status_t slap(); - - /* hide (revoke) the whole heap (the client will see the garbage page) */ - virtual status_t unslap(); - - /* revoke all allocations made by this heap */ - virtual void revoke(); - -private: - /* use this to create your own IMemory for mapMemory */ - virtual sp createMemory(size_t offset, size_t size); - void remove(const wp& memory); - -private: - sp mParentHeap; - mutable Mutex mLock; - SortedVector< wp > mAllocations; -}; - - -// --------------------------------------------------------------------------- -}; // namespace android - -#endif // ANDROID_MEMORY_HEAP_PMEM_H diff --git a/include/utils/Parcel.h b/include/utils/Parcel.h deleted file mode 100644 index 9087c4465..000000000 --- a/include/utils/Parcel.h +++ /dev/null @@ -1,209 +0,0 @@ -/* - * Copyright (C) 2005 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_PARCEL_H -#define ANDROID_PARCEL_H - -#include -#include -#include -#include -#include - -// --------------------------------------------------------------------------- -namespace android { - -class IBinder; -class ProcessState; -class String8; -class TextOutput; - -struct flat_binder_object; // defined in support_p/binder_module.h - -class Parcel -{ -public: - Parcel(); - ~Parcel(); - - const uint8_t* data() const; - size_t dataSize() const; - size_t dataAvail() const; - size_t dataPosition() const; - size_t dataCapacity() const; - - status_t setDataSize(size_t size); - void setDataPosition(size_t pos) const; - status_t setDataCapacity(size_t size); - - status_t setData(const uint8_t* buffer, size_t len); - - status_t appendFrom(Parcel *parcel, size_t start, size_t len); - - bool hasFileDescriptors() const; - - status_t writeInterfaceToken(const String16& interface); - bool enforceInterface(const String16& interface) const; - - void freeData(); - - const size_t* objects() const; - size_t objectsCount() const; - - status_t errorCheck() const; - void setError(status_t err); - - status_t write(const void* data, size_t len); - void* writeInplace(size_t len); - status_t writeUnpadded(const void* data, size_t len); - status_t writeInt32(int32_t val); - status_t writeInt64(int64_t val); - status_t writeFloat(float val); - status_t writeDouble(double val); - status_t writeCString(const char* str); - status_t writeString8(const String8& str); - status_t writeString16(const String16& str); - status_t writeString16(const char16_t* str, size_t len); - status_t writeStrongBinder(const sp& val); - status_t writeWeakBinder(const wp& val); - - // doesn't take ownership of the native_handle - status_t writeNativeHandle(const native_handle& handle); - - // Place a file descriptor into the parcel. The given fd must remain - // valid for the lifetime of the parcel. - status_t writeFileDescriptor(int fd); - - // Place a file descriptor into the parcel. A dup of the fd is made, which - // will be closed once the parcel is destroyed. - status_t writeDupFileDescriptor(int fd); - - status_t writeObject(const flat_binder_object& val, bool nullMetaData); - - void remove(size_t start, size_t amt); - - status_t read(void* outData, size_t len) const; - const void* readInplace(size_t len) const; - int32_t readInt32() const; - status_t readInt32(int32_t *pArg) const; - int64_t readInt64() const; - status_t readInt64(int64_t *pArg) const; - float readFloat() const; - status_t readFloat(float *pArg) const; - double readDouble() const; - status_t readDouble(double *pArg) const; - - const char* readCString() const; - String8 readString8() const; - String16 readString16() const; - const char16_t* readString16Inplace(size_t* outLen) const; - sp readStrongBinder() const; - wp readWeakBinder() const; - - - // if alloc is NULL, native_handle is allocated with malloc(), otherwise - // alloc is used. If the function fails, the effects of alloc() must be - // reverted by the caller. - native_handle* readNativeHandle( - native_handle* (*alloc)(void* cookie, int numFds, int ints), - void* cookie) const; - - - // Retrieve a file descriptor from the parcel. This returns the raw fd - // in the parcel, which you do not own -- use dup() to get your own copy. - int readFileDescriptor() const; - - const flat_binder_object* readObject(bool nullMetaData) const; - - // Explicitly close all file descriptors in the parcel. - void closeFileDescriptors(); - - typedef void (*release_func)(Parcel* parcel, - const uint8_t* data, size_t dataSize, - const size_t* objects, size_t objectsSize, - void* cookie); - - const uint8_t* ipcData() const; - size_t ipcDataSize() const; - const size_t* ipcObjects() const; - size_t ipcObjectsCount() const; - void ipcSetDataReference(const uint8_t* data, size_t dataSize, - const size_t* objects, size_t objectsCount, - release_func relFunc, void* relCookie); - - void print(TextOutput& to, uint32_t flags = 0) const; - -private: - Parcel(const Parcel& o); - Parcel& operator=(const Parcel& o); - - status_t finishWrite(size_t len); - void releaseObjects(); - void acquireObjects(); - status_t growData(size_t len); - status_t restartWrite(size_t desired); - status_t continueWrite(size_t desired); - void freeDataNoInit(); - void initState(); - void scanForFds() const; - - status_t mError; - uint8_t* mData; - size_t mDataSize; - size_t mDataCapacity; - mutable size_t mDataPos; - size_t* mObjects; - size_t mObjectsSize; - size_t mObjectsCapacity; - mutable size_t mNextObjectHint; - - mutable bool mFdsKnown; - mutable bool mHasFds; - - release_func mOwner; - void* mOwnerCookie; -}; - -// --------------------------------------------------------------------------- - -inline TextOutput& operator<<(TextOutput& to, const Parcel& parcel) -{ - parcel.print(to); - return to; -} - -// --------------------------------------------------------------------------- - -// Generic acquire and release of objects. -void acquire_object(const sp& proc, - const flat_binder_object& obj, const void* who); -void release_object(const sp& proc, - const flat_binder_object& obj, const void* who); - -void flatten_binder(const sp& proc, - const sp& binder, flat_binder_object* out); -void flatten_binder(const sp& proc, - const wp& binder, flat_binder_object* out); -status_t unflatten_binder(const sp& proc, - const flat_binder_object& flat, sp* out); -status_t unflatten_binder(const sp& proc, - const flat_binder_object& flat, wp* out); - -}; // namespace android - -// --------------------------------------------------------------------------- - -#endif // ANDROID_PARCEL_H diff --git a/include/utils/Pipe.h b/include/utils/Pipe.h deleted file mode 100644 index 6404168a2..000000000 --- a/include/utils/Pipe.h +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright (C) 2005 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. - */ - -// -// FIFO I/O. -// -#ifndef _LIBS_UTILS_PIPE_H -#define _LIBS_UTILS_PIPE_H - -#ifdef HAVE_ANDROID_OS -#error DO NOT USE THIS FILE IN THE DEVICE BUILD -#endif - -namespace android { - -/* - * Simple anonymous unidirectional pipe. - * - * The primary goal is to create an implementation with minimal overhead - * under Linux. Making Windows, Mac OS X, and Linux all work the same way - * is a secondary goal. Part of this goal is to have something that can - * be fed to a select() call, so that the application can sleep in the - * kernel until something interesting happens. - */ -class Pipe { -public: - Pipe(void); - virtual ~Pipe(void); - - /* Create the pipe */ - bool create(void); - - /* Create a read-only pipe, using the supplied handle as read handle */ - bool createReader(unsigned long handle); - /* Create a write-only pipe, using the supplied handle as write handle */ - bool createWriter(unsigned long handle); - - /* Is this object ready to go? */ - bool isCreated(void); - - /* - * Read "count" bytes from the pipe. Returns the amount of data read, - * or 0 if no data available and we're non-blocking. - * Returns -1 on error. - */ - int read(void* buf, int count); - - /* - * Write "count" bytes into the pipe. Returns number of bytes written, - * or 0 if there's no room for more data and we're non-blocking. - * Returns -1 on error. - */ - int write(const void* buf, int count); - - /* Returns "true" if data is available to read */ - bool readReady(void); - - /* Enable or disable non-blocking I/O for reads */ - bool setReadNonBlocking(bool val); - /* Enable or disable non-blocking I/O for writes. Only works on Linux. */ - bool setWriteNonBlocking(bool val); - - /* - * Get the handle. Only useful in some platform-specific situations. - */ - unsigned long getReadHandle(void); - unsigned long getWriteHandle(void); - - /* - * Modify inheritance, i.e. whether or not a child process will get - * copies of the descriptors. Systems with fork+exec allow us to close - * the descriptors before launching the child process, but Win32 - * doesn't allow it. - */ - bool disallowReadInherit(void); - bool disallowWriteInherit(void); - - /* - * Close one side or the other. Useful in the parent after launching - * a child process. - */ - bool closeRead(void); - bool closeWrite(void); - -private: - bool mReadNonBlocking; - bool mWriteNonBlocking; - - unsigned long mReadHandle; - unsigned long mWriteHandle; -}; - -}; // android - -#endif // _LIBS_UTILS_PIPE_H diff --git a/include/utils/ProcessState.h b/include/utils/ProcessState.h deleted file mode 100644 index 39584f42c..000000000 --- a/include/utils/ProcessState.h +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (C) 2005 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_PROCESS_STATE_H -#define ANDROID_PROCESS_STATE_H - -#include -#include -#include -#include - -#include - -// --------------------------------------------------------------------------- -namespace android { - -// Global variables -extern int mArgC; -extern const char* const* mArgV; -extern int mArgLen; - -class IPCThreadState; - -class ProcessState : public virtual RefBase -{ -public: - static sp self(); - - static void setSingleProcess(bool singleProcess); - - void setContextObject(const sp& object); - sp getContextObject(const sp& caller); - - void setContextObject(const sp& object, - const String16& name); - sp getContextObject(const String16& name, - const sp& caller); - - bool supportsProcesses() const; - - void startThreadPool(); - - typedef bool (*context_check_func)(const String16& name, - const sp& caller, - void* userData); - - bool isContextManager(void) const; - bool becomeContextManager( - context_check_func checkFunc, - void* userData); - - sp getStrongProxyForHandle(int32_t handle); - wp getWeakProxyForHandle(int32_t handle); - void expungeHandle(int32_t handle, IBinder* binder); - - void setArgs(int argc, const char* const argv[]); - int getArgC() const; - const char* const* getArgV() const; - - void setArgV0(const char* txt); - - void spawnPooledThread(bool isMain); - -private: - friend class IPCThreadState; - - ProcessState(); - ~ProcessState(); - - ProcessState(const ProcessState& o); - ProcessState& operator=(const ProcessState& o); - - struct handle_entry { - IBinder* binder; - RefBase::weakref_type* refs; - }; - - handle_entry* lookupHandleLocked(int32_t handle); - - int mDriverFD; - void* mVMStart; - - mutable Mutex mLock; // protects everything below. - - VectormHandleToObject; - - bool mManagesContexts; - context_check_func mBinderContextCheckFunc; - void* mBinderContextUserData; - - KeyedVector > - mContexts; - - - String8 mRootDir; - bool mThreadPoolStarted; - volatile int32_t mThreadPoolSeq; -}; - -}; // namespace android - -// --------------------------------------------------------------------------- - -#endif // ANDROID_PROCESS_STATE_H diff --git a/include/utils/RefBase.h b/include/utils/RefBase.h deleted file mode 100644 index cbda0fd80..000000000 --- a/include/utils/RefBase.h +++ /dev/null @@ -1,550 +0,0 @@ -/* - * Copyright (C) 2005 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_REF_BASE_H -#define ANDROID_REF_BASE_H - -#include -#include - -#include -#include -#include - -// --------------------------------------------------------------------------- -namespace android { - -template class wp; - -// --------------------------------------------------------------------------- - -#define COMPARE(_op_) \ -inline bool operator _op_ (const sp& o) const { \ - return m_ptr _op_ o.m_ptr; \ -} \ -inline bool operator _op_ (const wp& o) const { \ - return m_ptr _op_ o.m_ptr; \ -} \ -inline bool operator _op_ (const T* o) const { \ - return m_ptr _op_ o; \ -} \ -template \ -inline bool operator _op_ (const sp& o) const { \ - return m_ptr _op_ o.m_ptr; \ -} \ -template \ -inline bool operator _op_ (const wp& o) const { \ - return m_ptr _op_ o.m_ptr; \ -} \ -template \ -inline bool operator _op_ (const U* o) const { \ - return m_ptr _op_ o; \ -} - -// --------------------------------------------------------------------------- - -class RefBase -{ -public: - void incStrong(const void* id) const; - void decStrong(const void* id) const; - - void forceIncStrong(const void* id) const; - - //! DEBUGGING ONLY: Get current strong ref count. - int32_t getStrongCount() const; - - class weakref_type - { - public: - RefBase* refBase() const; - - void incWeak(const void* id); - void decWeak(const void* id); - - bool attemptIncStrong(const void* id); - - //! This is only safe if you have set OBJECT_LIFETIME_FOREVER. - bool attemptIncWeak(const void* id); - - //! DEBUGGING ONLY: Get current weak ref count. - int32_t getWeakCount() const; - - //! DEBUGGING ONLY: Print references held on object. - void printRefs() const; - - //! DEBUGGING ONLY: Enable tracking for this object. - // enable -- enable/disable tracking - // retain -- when tracking is enable, if true, then we save a stack trace - // for each reference and dereference; when retain == false, we - // match up references and dereferences and keep only the - // outstanding ones. - - void trackMe(bool enable, bool retain); - }; - - weakref_type* createWeak(const void* id) const; - - weakref_type* getWeakRefs() const; - - //! DEBUGGING ONLY: Print references held on object. - inline void printRefs() const { getWeakRefs()->printRefs(); } - - //! DEBUGGING ONLY: Enable tracking of object. - inline void trackMe(bool enable, bool retain) - { - getWeakRefs()->trackMe(enable, retain); - } - -protected: - RefBase(); - virtual ~RefBase(); - - //! Flags for extendObjectLifetime() - enum { - OBJECT_LIFETIME_WEAK = 0x0001, - OBJECT_LIFETIME_FOREVER = 0x0003 - }; - - void extendObjectLifetime(int32_t mode); - - //! Flags for onIncStrongAttempted() - enum { - FIRST_INC_STRONG = 0x0001 - }; - - virtual void onFirstRef(); - virtual void onLastStrongRef(const void* id); - virtual bool onIncStrongAttempted(uint32_t flags, const void* id); - virtual void onLastWeakRef(const void* id); - -private: - friend class weakref_type; - class weakref_impl; - - RefBase(const RefBase& o); - RefBase& operator=(const RefBase& o); - - weakref_impl* const mRefs; -}; - -// --------------------------------------------------------------------------- - -template -class LightRefBase -{ -public: - inline LightRefBase() : mCount(0) { } - inline void incStrong(const void* id) const { - android_atomic_inc(&mCount); - } - inline void decStrong(const void* id) const { - if (android_atomic_dec(&mCount) == 1) { - delete static_cast(this); - } - } - -protected: - inline ~LightRefBase() { } - -private: - mutable volatile int32_t mCount; -}; - -// --------------------------------------------------------------------------- - -template -class sp -{ -public: - typedef typename RefBase::weakref_type weakref_type; - - inline sp() : m_ptr(0) { } - - sp(T* other); - sp(const sp& other); - template sp(U* other); - template sp(const sp& other); - - ~sp(); - - // Assignment - - sp& operator = (T* other); - sp& operator = (const sp& other); - - template sp& operator = (const sp& other); - template sp& operator = (U* other); - - //! Special optimization for use by ProcessState (and nobody else). - void force_set(T* other); - - // Reset - - void clear(); - - // Accessors - - inline T& operator* () const { return *m_ptr; } - inline T* operator-> () const { return m_ptr; } - inline T* get() const { return m_ptr; } - - // Operators - - COMPARE(==) - COMPARE(!=) - COMPARE(>) - COMPARE(<) - COMPARE(<=) - COMPARE(>=) - -private: - template friend class sp; - template friend class wp; - - // Optimization for wp::promote(). - sp(T* p, weakref_type* refs); - - T* m_ptr; -}; - -template -TextOutput& operator<<(TextOutput& to, const sp& val); - -// --------------------------------------------------------------------------- - -template -class wp -{ -public: - typedef typename RefBase::weakref_type weakref_type; - - inline wp() : m_ptr(0) { } - - wp(T* other); - wp(const wp& other); - wp(const sp& other); - template wp(U* other); - template wp(const sp& other); - template wp(const wp& other); - - ~wp(); - - // Assignment - - wp& operator = (T* other); - wp& operator = (const wp& other); - wp& operator = (const sp& other); - - template wp& operator = (U* other); - template wp& operator = (const wp& other); - template wp& operator = (const sp& other); - - void set_object_and_refs(T* other, weakref_type* refs); - - // promotion to sp - - sp promote() const; - - // Reset - - void clear(); - - // Accessors - - inline weakref_type* get_refs() const { return m_refs; } - - inline T* unsafe_get() const { return m_ptr; } - - // Operators - - COMPARE(==) - COMPARE(!=) - COMPARE(>) - COMPARE(<) - COMPARE(<=) - COMPARE(>=) - -private: - template friend class sp; - template friend class wp; - - T* m_ptr; - weakref_type* m_refs; -}; - -template -TextOutput& operator<<(TextOutput& to, const wp& val); - -#undef COMPARE - -// --------------------------------------------------------------------------- -// No user serviceable parts below here. - -template -sp::sp(T* other) - : m_ptr(other) -{ - if (other) other->incStrong(this); -} - -template -sp::sp(const sp& other) - : m_ptr(other.m_ptr) -{ - if (m_ptr) m_ptr->incStrong(this); -} - -template template -sp::sp(U* other) : m_ptr(other) -{ - if (other) other->incStrong(this); -} - -template template -sp::sp(const sp& other) - : m_ptr(other.m_ptr) -{ - if (m_ptr) m_ptr->incStrong(this); -} - -template -sp::~sp() -{ - if (m_ptr) m_ptr->decStrong(this); -} - -template -sp& sp::operator = (const sp& other) { - if (other.m_ptr) other.m_ptr->incStrong(this); - if (m_ptr) m_ptr->decStrong(this); - m_ptr = other.m_ptr; - return *this; -} - -template -sp& sp::operator = (T* other) -{ - if (other) other->incStrong(this); - if (m_ptr) m_ptr->decStrong(this); - m_ptr = other; - return *this; -} - -template template -sp& sp::operator = (const sp& other) -{ - if (other.m_ptr) other.m_ptr->incStrong(this); - if (m_ptr) m_ptr->decStrong(this); - m_ptr = other.m_ptr; - return *this; -} - -template template -sp& sp::operator = (U* other) -{ - if (other) other->incStrong(this); - if (m_ptr) m_ptr->decStrong(this); - m_ptr = other; - return *this; -} - -template -void sp::force_set(T* other) -{ - other->forceIncStrong(this); - m_ptr = other; -} - -template -void sp::clear() -{ - if (m_ptr) { - m_ptr->decStrong(this); - m_ptr = 0; - } -} - -template -sp::sp(T* p, weakref_type* refs) - : m_ptr((p && refs->attemptIncStrong(this)) ? p : 0) -{ -} - -template -inline TextOutput& operator<<(TextOutput& to, const sp& val) -{ - to << "sp<>(" << val.get() << ")"; - return to; -} - -// --------------------------------------------------------------------------- - -template -wp::wp(T* other) - : m_ptr(other) -{ - if (other) m_refs = other->createWeak(this); -} - -template -wp::wp(const wp& other) - : m_ptr(other.m_ptr), m_refs(other.m_refs) -{ - if (m_ptr) m_refs->incWeak(this); -} - -template -wp::wp(const sp& other) - : m_ptr(other.m_ptr) -{ - if (m_ptr) { - m_refs = m_ptr->createWeak(this); - } -} - -template template -wp::wp(U* other) - : m_ptr(other) -{ - if (other) m_refs = other->createWeak(this); -} - -template template -wp::wp(const wp& other) - : m_ptr(other.m_ptr) -{ - if (m_ptr) { - m_refs = other.m_refs; - m_refs->incWeak(this); - } -} - -template template -wp::wp(const sp& other) - : m_ptr(other.m_ptr) -{ - if (m_ptr) { - m_refs = m_ptr->createWeak(this); - } -} - -template -wp::~wp() -{ - if (m_ptr) m_refs->decWeak(this); -} - -template -wp& wp::operator = (T* other) -{ - weakref_type* newRefs = - other ? other->createWeak(this) : 0; - if (m_ptr) m_refs->decWeak(this); - m_ptr = other; - m_refs = newRefs; - return *this; -} - -template -wp& wp::operator = (const wp& other) -{ - if (other.m_ptr) other.m_refs->incWeak(this); - if (m_ptr) m_refs->decWeak(this); - m_ptr = other.m_ptr; - m_refs = other.m_refs; - return *this; -} - -template -wp& wp::operator = (const sp& other) -{ - weakref_type* newRefs = - other != NULL ? other->createWeak(this) : 0; - if (m_ptr) m_refs->decWeak(this); - m_ptr = other.get(); - m_refs = newRefs; - return *this; -} - -template template -wp& wp::operator = (U* other) -{ - weakref_type* newRefs = - other ? other->createWeak(this) : 0; - if (m_ptr) m_refs->decWeak(this); - m_ptr = other; - m_refs = newRefs; - return *this; -} - -template template -wp& wp::operator = (const wp& other) -{ - if (other.m_ptr) other.m_refs->incWeak(this); - if (m_ptr) m_refs->decWeak(this); - m_ptr = other.m_ptr; - m_refs = other.m_refs; - return *this; -} - -template template -wp& wp::operator = (const sp& other) -{ - weakref_type* newRefs = - other != NULL ? other->createWeak(this) : 0; - if (m_ptr) m_refs->decWeak(this); - m_ptr = other.get(); - m_refs = newRefs; - return *this; -} - -template -void wp::set_object_and_refs(T* other, weakref_type* refs) -{ - if (other) refs->incWeak(this); - if (m_ptr) m_refs->decWeak(this); - m_ptr = other; - m_refs = refs; -} - -template -sp wp::promote() const -{ - return sp(m_ptr, m_refs); -} - -template -void wp::clear() -{ - if (m_ptr) { - m_refs->decWeak(this); - m_ptr = 0; - } -} - -template -inline TextOutput& operator<<(TextOutput& to, const wp& val) -{ - to << "wp<>(" << val.unsafe_get() << ")"; - return to; -} - -}; // namespace android - -// --------------------------------------------------------------------------- - -#endif // ANDROID_REF_BASE_H diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h deleted file mode 100644 index d83a33cef..000000000 --- a/include/utils/ResourceTypes.h +++ /dev/null @@ -1,1714 +0,0 @@ -/* - * Copyright (C) 2005 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. - */ - -// -// Definitions of resource data structures. -// -#ifndef _LIBS_UTILS_RESOURCE_TYPES_H -#define _LIBS_UTILS_RESOURCE_TYPES_H - -#include -#include -#include -#include -#include - -#include - -#include -#include - -namespace android { - -/** ******************************************************************** - * PNG Extensions - * - * New private chunks that may be placed in PNG images. - * - *********************************************************************** */ - -/** - * This chunk specifies how to split an image into segments for - * scaling. - * - * There are J horizontal and K vertical segments. These segments divide - * the image into J*K regions as follows (where J=4 and K=3): - * - * F0 S0 F1 S1 - * +-----+----+------+-------+ - * S2| 0 | 1 | 2 | 3 | - * +-----+----+------+-------+ - * | | | | | - * | | | | | - * F2| 4 | 5 | 6 | 7 | - * | | | | | - * | | | | | - * +-----+----+------+-------+ - * S3| 8 | 9 | 10 | 11 | - * +-----+----+------+-------+ - * - * Each horizontal and vertical segment is considered to by either - * stretchable (marked by the Sx labels) or fixed (marked by the Fy - * labels), in the horizontal or vertical axis, respectively. In the - * above example, the first is horizontal segment (F0) is fixed, the - * next is stretchable and then they continue to alternate. Note that - * the segment list for each axis can begin or end with a stretchable - * or fixed segment. - * - * The relative sizes of the stretchy segments indicates the relative - * amount of stretchiness of the regions bordered by the segments. For - * example, regions 3, 7 and 11 above will take up more horizontal space - * than regions 1, 5 and 9 since the horizonal segment associated with - * the first set of regions is larger than the other set of regions. The - * ratios of the amount of horizontal (or vertical) space taken by any - * two stretchable slices is exactly the ratio of their corresponding - * segment lengths. - * - * xDivs and yDivs point to arrays of horizontal and vertical pixel - * indices. The first pair of Divs (in either array) indicate the - * starting and ending points of the first stretchable segment in that - * axis. The next pair specifies the next stretchable segment, etc. So - * in the above example xDiv[0] and xDiv[1] specify the horizontal - * coordinates for the regions labeled 1, 5 and 9. xDiv[2] and - * xDiv[3] specify the coordinates for regions 3, 7 and 11. Note that - * the leftmost slices always start at x=0 and the rightmost slices - * always end at the end of the image. So, for example, the regions 0, - * 4 and 8 (which are fixed along the X axis) start at x value 0 and - * go to xDiv[0] amd slices 2, 6 and 10 start at xDiv[1] and end at - * xDiv[2]. - * - * The array pointed to by the colors field lists contains hints for - * each of the regions. They are ordered according left-to-right and - * top-to-bottom as indicated above. For each segment that is a solid - * color the array entry will contain that color value; otherwise it - * will contain NO_COLOR. Segments that are completely transparent - * will always have the value TRANSPARENT_COLOR. - * - * The PNG chunk type is "npTc". - */ -struct Res_png_9patch -{ - Res_png_9patch() : wasDeserialized(false), xDivs(NULL), - yDivs(NULL), colors(NULL) { } - - int8_t wasDeserialized; - int8_t numXDivs; - int8_t numYDivs; - int8_t numColors; - - // These tell where the next section of a patch starts. - // For example, the first patch includes the pixels from - // 0 to xDivs[0]-1 and the second patch includes the pixels - // from xDivs[0] to xDivs[1]-1. - // Note: allocation/free of these pointers is left to the caller. - int32_t* xDivs; - int32_t* yDivs; - - int32_t paddingLeft, paddingRight; - int32_t paddingTop, paddingBottom; - - enum { - // The 9 patch segment is not a solid color. - NO_COLOR = 0x00000001, - - // The 9 patch segment is completely transparent. - TRANSPARENT_COLOR = 0x00000000 - }; - // Note: allocation/free of this pointer is left to the caller. - uint32_t* colors; - - // Convert data from device representation to PNG file representation. - void deviceToFile(); - // Convert data from PNG file representation to device representation. - void fileToDevice(); - // Serialize/Marshall the patch data into a newly malloc-ed block - void* serialize(); - // Serialize/Marshall the patch data - void serialize(void* outData); - // Deserialize/Unmarshall the patch data - static Res_png_9patch* deserialize(const void* data); - // Compute the size of the serialized data structure - size_t serializedSize(); -}; - -/** ******************************************************************** - * Base Types - * - * These are standard types that are shared between multiple specific - * resource types. - * - *********************************************************************** */ - -/** - * Header that appears at the front of every data chunk in a resource. - */ -struct ResChunk_header -{ - // Type identifier for this chunk. The meaning of this value depends - // on the containing chunk. - uint16_t type; - - // Size of the chunk header (in bytes). Adding this value to - // the address of the chunk allows you to find its associated data - // (if any). - uint16_t headerSize; - - // Total size of this chunk (in bytes). This is the chunkSize plus - // the size of any data associated with the chunk. Adding this value - // to the chunk allows you to completely skip its contents (including - // any child chunks). If this value is the same as chunkSize, there is - // no data associated with the chunk. - uint32_t size; -}; - -enum { - RES_NULL_TYPE = 0x0000, - RES_STRING_POOL_TYPE = 0x0001, - RES_TABLE_TYPE = 0x0002, - RES_XML_TYPE = 0x0003, - - // Chunk types in RES_XML_TYPE - RES_XML_FIRST_CHUNK_TYPE = 0x0100, - RES_XML_START_NAMESPACE_TYPE= 0x0100, - RES_XML_END_NAMESPACE_TYPE = 0x0101, - RES_XML_START_ELEMENT_TYPE = 0x0102, - RES_XML_END_ELEMENT_TYPE = 0x0103, - RES_XML_CDATA_TYPE = 0x0104, - RES_XML_LAST_CHUNK_TYPE = 0x017f, - // This contains a uint32_t array mapping strings in the string - // pool back to resource identifiers. It is optional. - RES_XML_RESOURCE_MAP_TYPE = 0x0180, - - // Chunk types in RES_TABLE_TYPE - RES_TABLE_PACKAGE_TYPE = 0x0200, - RES_TABLE_TYPE_TYPE = 0x0201, - RES_TABLE_TYPE_SPEC_TYPE = 0x0202 -}; - -/** - * Macros for building/splitting resource identifiers. - */ -#define Res_VALIDID(resid) (resid != 0) -#define Res_CHECKID(resid) ((resid&0xFFFF0000) != 0) -#define Res_MAKEID(package, type, entry) \ - (((package+1)<<24) | (((type+1)&0xFF)<<16) | (entry&0xFFFF)) -#define Res_GETPACKAGE(id) ((id>>24)-1) -#define Res_GETTYPE(id) (((id>>16)&0xFF)-1) -#define Res_GETENTRY(id) (id&0xFFFF) - -#define Res_INTERNALID(resid) ((resid&0xFFFF0000) != 0 && (resid&0xFF0000) == 0) -#define Res_MAKEINTERNAL(entry) (0x01000000 | (entry&0xFFFF)) -#define Res_MAKEARRAY(entry) (0x02000000 | (entry&0xFFFF)) - -#define Res_MAXPACKAGE 255 - -/** - * Representation of a value in a resource, supplying type - * information. - */ -struct Res_value -{ - // Number of bytes in this structure. - uint16_t size; - - // Always set to 0. - uint8_t res0; - - // Type of the data value. - enum { - // Contains no data. - TYPE_NULL = 0x00, - // The 'data' holds a ResTable_ref, a reference to another resource - // table entry. - TYPE_REFERENCE = 0x01, - // The 'data' holds an attribute resource identifier. - TYPE_ATTRIBUTE = 0x02, - // The 'data' holds an index into the containing resource table's - // global value string pool. - TYPE_STRING = 0x03, - // The 'data' holds a single-precision floating point number. - TYPE_FLOAT = 0x04, - // The 'data' holds a complex number encoding a dimension value, - // such as "100in". - TYPE_DIMENSION = 0x05, - // The 'data' holds a complex number encoding a fraction of a - // container. - TYPE_FRACTION = 0x06, - - // Beginning of integer flavors... - TYPE_FIRST_INT = 0x10, - - // The 'data' is a raw integer value of the form n..n. - TYPE_INT_DEC = 0x10, - // The 'data' is a raw integer value of the form 0xn..n. - TYPE_INT_HEX = 0x11, - // The 'data' is either 0 or 1, for input "false" or "true" respectively. - TYPE_INT_BOOLEAN = 0x12, - - // Beginning of color integer flavors... - TYPE_FIRST_COLOR_INT = 0x1c, - - // The 'data' is a raw integer value of the form #aarrggbb. - TYPE_INT_COLOR_ARGB8 = 0x1c, - // The 'data' is a raw integer value of the form #rrggbb. - TYPE_INT_COLOR_RGB8 = 0x1d, - // The 'data' is a raw integer value of the form #argb. - TYPE_INT_COLOR_ARGB4 = 0x1e, - // The 'data' is a raw integer value of the form #rgb. - TYPE_INT_COLOR_RGB4 = 0x1f, - - // ...end of integer flavors. - TYPE_LAST_COLOR_INT = 0x1f, - - // ...end of integer flavors. - TYPE_LAST_INT = 0x1f - }; - uint8_t dataType; - - // Structure of complex data values (TYPE_UNIT and TYPE_FRACTION) - enum { - // Where the unit type information is. This gives us 16 possible - // types, as defined below. - COMPLEX_UNIT_SHIFT = 0, - COMPLEX_UNIT_MASK = 0xf, - - // TYPE_DIMENSION: Value is raw pixels. - COMPLEX_UNIT_PX = 0, - // TYPE_DIMENSION: Value is Device Independent Pixels. - COMPLEX_UNIT_DIP = 1, - // TYPE_DIMENSION: Value is a Scaled device independent Pixels. - COMPLEX_UNIT_SP = 2, - // TYPE_DIMENSION: Value is in points. - COMPLEX_UNIT_PT = 3, - // TYPE_DIMENSION: Value is in inches. - COMPLEX_UNIT_IN = 4, - // TYPE_DIMENSION: Value is in millimeters. - COMPLEX_UNIT_MM = 5, - - // TYPE_FRACTION: A basic fraction of the overall size. - COMPLEX_UNIT_FRACTION = 0, - // TYPE_FRACTION: A fraction of the parent size. - COMPLEX_UNIT_FRACTION_PARENT = 1, - - // Where the radix information is, telling where the decimal place - // appears in the mantissa. This give us 4 possible fixed point - // representations as defined below. - COMPLEX_RADIX_SHIFT = 4, - COMPLEX_RADIX_MASK = 0x3, - - // The mantissa is an integral number -- i.e., 0xnnnnnn.0 - COMPLEX_RADIX_23p0 = 0, - // The mantissa magnitude is 16 bits -- i.e, 0xnnnn.nn - COMPLEX_RADIX_16p7 = 1, - // The mantissa magnitude is 8 bits -- i.e, 0xnn.nnnn - COMPLEX_RADIX_8p15 = 2, - // The mantissa magnitude is 0 bits -- i.e, 0x0.nnnnnn - COMPLEX_RADIX_0p23 = 3, - - // Where the actual value is. This gives us 23 bits of - // precision. The top bit is the sign. - COMPLEX_MANTISSA_SHIFT = 8, - COMPLEX_MANTISSA_MASK = 0xffffff - }; - - // The data for this item, as interpreted according to dataType. - uint32_t data; - - void copyFrom_dtoh(const Res_value& src); -}; - -/** - * This is a reference to a unique entry (a ResTable_entry structure) - * in a resource table. The value is structured as: 0xpptteeee, - * where pp is the package index, tt is the type index in that - * package, and eeee is the entry index in that type. The package - * and type values start at 1 for the first item, to help catch cases - * where they have not been supplied. - */ -struct ResTable_ref -{ - uint32_t ident; -}; - -/** - * Reference to a string in a string pool. - */ -struct ResStringPool_ref -{ - // Index into the string pool table (uint32_t-offset from the indices - // immediately after ResStringPool_header) at which to find the location - // of the string data in the pool. - uint32_t index; -}; - -/** ******************************************************************** - * String Pool - * - * A set of strings that can be references by others through a - * ResStringPool_ref. - * - *********************************************************************** */ - -/** - * Definition for a pool of strings. The data of this chunk is an - * array of uint32_t providing indices into the pool, relative to - * stringsStart. At stringsStart are all of the UTF-16 strings - * concatenated together; each starts with a uint16_t of the string's - * length and each ends with a 0x0000 terminator. If a string is > - * 32767 characters, the high bit of the length is set meaning to take - * those 15 bits as a high word and it will be followed by another - * uint16_t containing the low word. - * - * If styleCount is not zero, then immediately following the array of - * uint32_t indices into the string table is another array of indices - * into a style table starting at stylesStart. Each entry in the - * style table is an array of ResStringPool_span structures. - */ -struct ResStringPool_header -{ - struct ResChunk_header header; - - // Number of strings in this pool (number of uint32_t indices that follow - // in the data). - uint32_t stringCount; - - // Number of style span arrays in the pool (number of uint32_t indices - // follow the string indices). - uint32_t styleCount; - - // Flags. - enum { - // If set, the string index is sorted by the string values (based - // on strcmp16()). - SORTED_FLAG = 1<<0 - }; - uint32_t flags; - - // Index from header of the string data. - uint32_t stringsStart; - - // Index from header of the style data. - uint32_t stylesStart; -}; - -/** - * This structure defines a span of style information associated with - * a string in the pool. - */ -struct ResStringPool_span -{ - enum { - END = 0xFFFFFFFF - }; - - // This is the name of the span -- that is, the name of the XML - // tag that defined it. The special value END (0xFFFFFFFF) indicates - // the end of an array of spans. - ResStringPool_ref name; - - // The range of characters in the string that this span applies to. - uint32_t firstChar, lastChar; -}; - -/** - * Convenience class for accessing data in a ResStringPool resource. - */ -class ResStringPool -{ -public: - ResStringPool(); - ResStringPool(const void* data, size_t size, bool copyData=false); - ~ResStringPool(); - - status_t setTo(const void* data, size_t size, bool copyData=false); - - status_t getError() const; - - void uninit(); - - inline const char16_t* stringAt(const ResStringPool_ref& ref, size_t* outLen) const { - return stringAt(ref.index, outLen); - } - const char16_t* stringAt(size_t idx, size_t* outLen) const; - - const ResStringPool_span* styleAt(const ResStringPool_ref& ref) const; - const ResStringPool_span* styleAt(size_t idx) const; - - ssize_t indexOfString(const char16_t* str, size_t strLen) const; - - size_t size() const; - -private: - status_t mError; - void* mOwnedData; - const ResStringPool_header* mHeader; - size_t mSize; - const uint32_t* mEntries; - const uint32_t* mEntryStyles; - const char16_t* mStrings; - uint32_t mStringPoolSize; // number of uint16_t - const uint32_t* mStyles; - uint32_t mStylePoolSize; // number of uint32_t -}; - -/** ******************************************************************** - * XML Tree - * - * Binary representation of an XML document. This is designed to - * express everything in an XML document, in a form that is much - * easier to parse on the device. - * - *********************************************************************** */ - -/** - * XML tree header. This appears at the front of an XML tree, - * describing its content. It is followed by a flat array of - * ResXMLTree_node structures; the hierarchy of the XML document - * is described by the occurrance of RES_XML_START_ELEMENT_TYPE - * and corresponding RES_XML_END_ELEMENT_TYPE nodes in the array. - */ -struct ResXMLTree_header -{ - struct ResChunk_header header; -}; - -/** - * Basic XML tree node. A single item in the XML document. Extended info - * about the node can be found after header.headerSize. - */ -struct ResXMLTree_node -{ - struct ResChunk_header header; - - // Line number in original source file at which this element appeared. - uint32_t lineNumber; - - // Optional XML comment that was associated with this element; -1 if none. - struct ResStringPool_ref comment; -}; - -/** - * Extended XML tree node for CDATA tags -- includes the CDATA string. - * Appears header.headerSize bytes after a ResXMLTree_node. - */ -struct ResXMLTree_cdataExt -{ - // The raw CDATA character data. - struct ResStringPool_ref data; - - // The typed value of the character data if this is a CDATA node. - struct Res_value typedData; -}; - -/** - * Extended XML tree node for namespace start/end nodes. - * Appears header.headerSize bytes after a ResXMLTree_node. - */ -struct ResXMLTree_namespaceExt -{ - // The prefix of the namespace. - struct ResStringPool_ref prefix; - - // The URI of the namespace. - struct ResStringPool_ref uri; -}; - -/** - * Extended XML tree node for element start/end nodes. - * Appears header.headerSize bytes after a ResXMLTree_node. - */ -struct ResXMLTree_endElementExt -{ - // String of the full namespace of this element. - struct ResStringPool_ref ns; - - // String name of this node if it is an ELEMENT; the raw - // character data if this is a CDATA node. - struct ResStringPool_ref name; -}; - -/** - * Extended XML tree node for start tags -- includes attribute - * information. - * Appears header.headerSize bytes after a ResXMLTree_node. - */ -struct ResXMLTree_attrExt -{ - // String of the full namespace of this element. - struct ResStringPool_ref ns; - - // String name of this node if it is an ELEMENT; the raw - // character data if this is a CDATA node. - struct ResStringPool_ref name; - - // Byte offset from the start of this structure where the attributes start. - uint16_t attributeStart; - - // Size of the ResXMLTree_attribute structures that follow. - uint16_t attributeSize; - - // Number of attributes associated with an ELEMENT. These are - // available as an array of ResXMLTree_attribute structures - // immediately following this node. - uint16_t attributeCount; - - // Index (1-based) of the "id" attribute. 0 if none. - uint16_t idIndex; - - // Index (1-based) of the "class" attribute. 0 if none. - uint16_t classIndex; - - // Index (1-based) of the "style" attribute. 0 if none. - uint16_t styleIndex; -}; - -struct ResXMLTree_attribute -{ - // Namespace of this attribute. - struct ResStringPool_ref ns; - - // Name of this attribute. - struct ResStringPool_ref name; - - // The original raw string value of this attribute. - struct ResStringPool_ref rawValue; - - // Processesd typed value of this attribute. - struct Res_value typedValue; -}; - -class ResXMLTree; - -class ResXMLParser -{ -public: - ResXMLParser(const ResXMLTree& tree); - - enum event_code_t { - BAD_DOCUMENT = -1, - START_DOCUMENT = 0, - END_DOCUMENT = 1, - - FIRST_CHUNK_CODE = RES_XML_FIRST_CHUNK_TYPE, - - START_NAMESPACE = RES_XML_START_NAMESPACE_TYPE, - END_NAMESPACE = RES_XML_END_NAMESPACE_TYPE, - START_TAG = RES_XML_START_ELEMENT_TYPE, - END_TAG = RES_XML_END_ELEMENT_TYPE, - TEXT = RES_XML_CDATA_TYPE - }; - - struct ResXMLPosition - { - event_code_t eventCode; - const ResXMLTree_node* curNode; - const void* curExt; - }; - - void restart(); - - event_code_t getEventType() const; - // Note, unlike XmlPullParser, the first call to next() will return - // START_TAG of the first element. - event_code_t next(); - - // These are available for all nodes: - const int32_t getCommentID() const; - const uint16_t* getComment(size_t* outLen) const; - const uint32_t getLineNumber() const; - - // This is available for TEXT: - const int32_t getTextID() const; - const uint16_t* getText(size_t* outLen) const; - ssize_t getTextValue(Res_value* outValue) const; - - // These are available for START_NAMESPACE and END_NAMESPACE: - const int32_t getNamespacePrefixID() const; - const uint16_t* getNamespacePrefix(size_t* outLen) const; - const int32_t getNamespaceUriID() const; - const uint16_t* getNamespaceUri(size_t* outLen) const; - - // These are available for START_TAG and END_TAG: - const int32_t getElementNamespaceID() const; - const uint16_t* getElementNamespace(size_t* outLen) const; - const int32_t getElementNameID() const; - const uint16_t* getElementName(size_t* outLen) const; - - // Remaining methods are for retrieving information about attributes - // associated with a START_TAG: - - size_t getAttributeCount() const; - - // Returns -1 if no namespace, -2 if idx out of range. - const int32_t getAttributeNamespaceID(size_t idx) const; - const uint16_t* getAttributeNamespace(size_t idx, size_t* outLen) const; - - const int32_t getAttributeNameID(size_t idx) const; - const uint16_t* getAttributeName(size_t idx, size_t* outLen) const; - const uint32_t getAttributeNameResID(size_t idx) const; - - const int32_t getAttributeValueStringID(size_t idx) const; - const uint16_t* getAttributeStringValue(size_t idx, size_t* outLen) const; - - int32_t getAttributeDataType(size_t idx) const; - int32_t getAttributeData(size_t idx) const; - ssize_t getAttributeValue(size_t idx, Res_value* outValue) const; - - ssize_t indexOfAttribute(const char* ns, const char* attr) const; - ssize_t indexOfAttribute(const char16_t* ns, size_t nsLen, - const char16_t* attr, size_t attrLen) const; - - ssize_t indexOfID() const; - ssize_t indexOfClass() const; - ssize_t indexOfStyle() const; - - void getPosition(ResXMLPosition* pos) const; - void setPosition(const ResXMLPosition& pos); - -private: - friend class ResXMLTree; - - event_code_t nextNode(); - - const ResXMLTree& mTree; - event_code_t mEventCode; - const ResXMLTree_node* mCurNode; - const void* mCurExt; -}; - -/** - * Convenience class for accessing data in a ResXMLTree resource. - */ -class ResXMLTree : public ResXMLParser -{ -public: - ResXMLTree(); - ResXMLTree(const void* data, size_t size, bool copyData=false); - ~ResXMLTree(); - - status_t setTo(const void* data, size_t size, bool copyData=false); - - status_t getError() const; - - void uninit(); - - const ResStringPool& getStrings() const; - -private: - friend class ResXMLParser; - - status_t validateNode(const ResXMLTree_node* node) const; - - status_t mError; - void* mOwnedData; - const ResXMLTree_header* mHeader; - size_t mSize; - const uint8_t* mDataEnd; - ResStringPool mStrings; - const uint32_t* mResIds; - size_t mNumResIds; - const ResXMLTree_node* mRootNode; - const void* mRootExt; - event_code_t mRootCode; -}; - -/** ******************************************************************** - * RESOURCE TABLE - * - *********************************************************************** */ - -/** - * Header for a resource table. Its data contains a series of - * additional chunks: - * * A ResStringPool_header containing all table values. - * * One or more ResTable_package chunks. - * - * Specific entries within a resource table can be uniquely identified - * with a single integer as defined by the ResTable_ref structure. - */ -struct ResTable_header -{ - struct ResChunk_header header; - - // The number of ResTable_package structures. - uint32_t packageCount; -}; - -/** - * A collection of resource data types within a package. Followed by - * one or more ResTable_type and ResTable_typeSpec structures containing the - * entry values for each resource type. - */ -struct ResTable_package -{ - struct ResChunk_header header; - - // If this is a base package, its ID. Package IDs start - // at 1 (corresponding to the value of the package bits in a - // resource identifier). 0 means this is not a base package. - uint32_t id; - - // Actual name of this package, \0-terminated. - char16_t name[128]; - - // Offset to a ResStringPool_header defining the resource - // type symbol table. If zero, this package is inheriting from - // another base package (overriding specific values in it). - uint32_t typeStrings; - - // Last index into typeStrings that is for public use by others. - uint32_t lastPublicType; - - // Offset to a ResStringPool_header defining the resource - // key symbol table. If zero, this package is inheriting from - // another base package (overriding specific values in it). - uint32_t keyStrings; - - // Last index into keyStrings that is for public use by others. - uint32_t lastPublicKey; -}; - -/** - * Describes a particular resource configuration. - */ -struct ResTable_config -{ - // Number of bytes in this structure. - uint32_t size; - - union { - struct { - // Mobile country code (from SIM). 0 means "any". - uint16_t mcc; - // Mobile network code (from SIM). 0 means "any". - uint16_t mnc; - }; - uint32_t imsi; - }; - - union { - struct { - // \0\0 means "any". Otherwise, en, fr, etc. - char language[2]; - - // \0\0 means "any". Otherwise, US, CA, etc. - char country[2]; - }; - uint32_t locale; - }; - - enum { - ORIENTATION_ANY = 0x0000, - ORIENTATION_PORT = 0x0001, - ORIENTATION_LAND = 0x0002, - ORIENTATION_SQUARE = 0x0002, - }; - - enum { - TOUCHSCREEN_ANY = 0x0000, - TOUCHSCREEN_NOTOUCH = 0x0001, - TOUCHSCREEN_STYLUS = 0x0002, - TOUCHSCREEN_FINGER = 0x0003, - }; - - enum { - DENSITY_ANY = 0 - }; - - union { - struct { - uint8_t orientation; - uint8_t touchscreen; - uint16_t density; - }; - uint32_t screenType; - }; - - enum { - KEYBOARD_ANY = 0x0000, - KEYBOARD_NOKEYS = 0x0001, - KEYBOARD_QWERTY = 0x0002, - KEYBOARD_12KEY = 0x0003, - }; - - enum { - NAVIGATION_ANY = 0x0000, - NAVIGATION_NONAV = 0x0001, - NAVIGATION_DPAD = 0x0002, - NAVIGATION_TRACKBALL = 0x0003, - NAVIGATION_WHEEL = 0x0004, - }; - - enum { - MASK_KEYSHIDDEN = 0x0003, - SHIFT_KEYSHIDDEN = 0, - KEYSHIDDEN_ANY = 0x0000, - KEYSHIDDEN_NO = 0x0001, - KEYSHIDDEN_YES = 0x0002, - KEYSHIDDEN_SOFT = 0x0003, - }; - - union { - struct { - uint8_t keyboard; - uint8_t navigation; - uint8_t inputFlags; - uint8_t pad0; - }; - uint32_t input; - }; - - enum { - SCREENWIDTH_ANY = 0 - }; - - enum { - SCREENHEIGHT_ANY = 0 - }; - - union { - struct { - uint16_t screenWidth; - uint16_t screenHeight; - }; - uint32_t screenSize; - }; - - enum { - SDKVERSION_ANY = 0 - }; - - enum { - MINORVERSION_ANY = 0 - }; - - union { - struct { - uint16_t sdkVersion; - // For now minorVersion must always be 0!!! Its meaning - // is currently undefined. - uint16_t minorVersion; - }; - uint32_t version; - }; - - inline void copyFromDeviceNoSwap(const ResTable_config& o) { - const size_t size = dtohl(o.size); - if (size >= sizeof(ResTable_config)) { - *this = o; - } else { - memcpy(this, &o, size); - memset(((uint8_t*)this)+size, 0, sizeof(ResTable_config)-size); - } - } - - inline void copyFromDtoH(const ResTable_config& o) { - copyFromDeviceNoSwap(o); - size = sizeof(ResTable_config); - mcc = dtohs(mcc); - mnc = dtohs(mnc); - density = dtohs(density); - screenWidth = dtohs(screenWidth); - screenHeight = dtohs(screenHeight); - sdkVersion = dtohs(sdkVersion); - minorVersion = dtohs(minorVersion); - } - - inline void swapHtoD() { - size = htodl(size); - mcc = htods(mcc); - mnc = htods(mnc); - density = htods(density); - screenWidth = htods(screenWidth); - screenHeight = htods(screenHeight); - sdkVersion = htods(sdkVersion); - minorVersion = htods(minorVersion); - } - - inline int compare(const ResTable_config& o) const { - int32_t diff = (int32_t)(imsi - o.imsi); - if (diff != 0) return diff; - diff = (int32_t)(locale - o.locale); - if (diff != 0) return diff; - diff = (int32_t)(screenType - o.screenType); - if (diff != 0) return diff; - diff = (int32_t)(input - o.input); - if (diff != 0) return diff; - diff = (int32_t)(screenSize - o.screenSize); - if (diff != 0) return diff; - diff = (int32_t)(version - o.version); - return (int)diff; - } - - // Flags indicating a set of config values. These flag constants must - // match the corresponding ones in android.content.pm.ActivityInfo and - // attrs_manifest.xml. - enum { - CONFIG_MCC = 0x0001, - CONFIG_MNC = 0x0002, - CONFIG_LOCALE = 0x0004, - CONFIG_TOUCHSCREEN = 0x0008, - CONFIG_KEYBOARD = 0x0010, - CONFIG_KEYBOARD_HIDDEN = 0x0020, - CONFIG_NAVIGATION = 0x0040, - CONFIG_ORIENTATION = 0x0080, - CONFIG_DENSITY = 0x0100, - CONFIG_SCREEN_SIZE = 0x0200, - CONFIG_VERSION = 0x0400 - }; - - // Compare two configuration, returning CONFIG_* flags set for each value - // that is different. - inline int diff(const ResTable_config& o) const { - int diffs = 0; - if (mcc != o.mcc) diffs |= CONFIG_MCC; - if (mnc != o.mnc) diffs |= CONFIG_MNC; - if (locale != o.locale) diffs |= CONFIG_LOCALE; - if (orientation != o.orientation) diffs |= CONFIG_ORIENTATION; - if (density != o.density) diffs |= CONFIG_DENSITY; - if (touchscreen != o.touchscreen) diffs |= CONFIG_TOUCHSCREEN; - if (((inputFlags^o.inputFlags)&MASK_KEYSHIDDEN) != 0) diffs |= CONFIG_KEYBOARD_HIDDEN; - if (keyboard != o.keyboard) diffs |= CONFIG_KEYBOARD; - if (navigation != o.navigation) diffs |= CONFIG_NAVIGATION; - if (screenSize != o.screenSize) diffs |= CONFIG_SCREEN_SIZE; - if (version != o.version) diffs |= CONFIG_VERSION; - return diffs; - } - - // Return true if 'this' is more specific than 'o'. Optionally, if - // 'requested' is null, then they will also be compared against the - // requested configuration and true will only be returned if 'this' - // is a better candidate than 'o' for the configuration. This assumes that - // match() has already been used to remove any configurations that don't - // match the requested configuration at all; if they are not first filtered, - // non-matching results can be considered better than matching ones. - inline bool - isBetterThan(const ResTable_config& o, const ResTable_config* requested = NULL) const { - // The order of the following tests defines the importance of one - // configuration parameter over another. Those tests first are more - // important, trumping any values in those following them. - if (imsi != 0 && (!requested || requested->imsi != 0)) { - if (mcc != 0 && (!requested || requested->mcc != 0)) { - if (o.mcc == 0) { - return true; - } - } - if (mnc != 0 && (!requested || requested->mnc != 0)) { - if (o.mnc == 0) { - return true; - } - } - } - if (locale != 0 && (!requested || requested->locale != 0)) { - if (language[0] != 0 && (!requested || requested->language[0] != 0)) { - if (o.language[0] == 0) { - return true; - } - } - if (country[0] != 0 && (!requested || requested->country[0] != 0)) { - if (o.country[0] == 0) { - return true; - } - } - } - if (screenType != 0 && (!requested || requested->screenType != 0)) { - if (orientation != 0 && (!requested || requested->orientation != 0)) { - if (o.orientation == 0) { - return true; - } - } - if (density != 0 && (!requested || requested->density != 0)) { - if (o.density == 0) { - return true; - } - } - if (touchscreen != 0 && (!requested || requested->touchscreen != 0)) { - if (o.touchscreen == 0) { - return true; - } - } - } - if (input != 0 && (!requested || requested->input != 0)) { - const int keysHidden = inputFlags&MASK_KEYSHIDDEN; - const int reqKeysHidden = requested - ? requested->inputFlags&MASK_KEYSHIDDEN : 0; - if (keysHidden != 0 && reqKeysHidden != 0) { - const int oKeysHidden = o.inputFlags&MASK_KEYSHIDDEN; - //LOGI("isBetterThan keysHidden: cur=%d, given=%d, config=%d\n", - // keysHidden, oKeysHidden, reqKeysHidden); - if (oKeysHidden == 0) { - //LOGI("Better because 0!"); - return true; - } - // For compatibility, we count KEYSHIDDEN_NO as being - // the same as KEYSHIDDEN_SOFT. Here we disambiguate these - // may making an exact match more specific. - if (keysHidden == reqKeysHidden && oKeysHidden != reqKeysHidden) { - // The current configuration is an exact match, and - // the given one is not, so the current one is better. - //LOGI("Better because other not same!"); - return true; - } - } - if (keyboard != 0 && (!requested || requested->keyboard != 0)) { - if (o.keyboard == 0) { - return true; - } - } - if (navigation != 0 && (!requested || requested->navigation != 0)) { - if (o.navigation == 0) { - return true; - } - } - } - if (screenSize != 0 && (!requested || requested->screenSize != 0)) { - if (screenWidth != 0 && (!requested || requested->screenWidth != 0)) { - if (o.screenWidth == 0) { - return true; - } - } - if (screenHeight != 0 && (!requested || requested->screenHeight != 0)) { - if (o.screenHeight == 0) { - return true; - } - } - } - if (version != 0 && (!requested || requested->version != 0)) { - if (sdkVersion != 0 && (!requested || requested->sdkVersion != 0)) { - if (o.sdkVersion == 0) { - return true; - } - } - if (minorVersion != 0 && (!requested || requested->minorVersion != 0)) { - if (o.minorVersion == 0) { - return true; - } - } - } - return false; - } - - // Return true if 'this' can be considered a match for the parameters in - // 'settings'. - inline bool match(const ResTable_config& settings) const { - if (imsi != 0) { - if (settings.mcc != 0 && mcc != 0 - && mcc != settings.mcc) { - return false; - } - if (settings.mnc != 0 && mnc != 0 - && mnc != settings.mnc) { - return false; - } - } - if (locale != 0) { - if (settings.language[0] != 0 && language[0] != 0 - && (language[0] != settings.language[0] - || language[1] != settings.language[1])) { - return false; - } - if (settings.country[0] != 0 && country[0] != 0 - && (country[0] != settings.country[0] - || country[1] != settings.country[1])) { - return false; - } - } - if (screenType != 0) { - if (settings.orientation != 0 && orientation != 0 - && orientation != settings.orientation) { - return false; - } - // Density not taken into account, always match, no matter what - // density is specified for the resource - if (settings.touchscreen != 0 && touchscreen != 0 - && touchscreen != settings.touchscreen) { - return false; - } - } - if (input != 0) { - const int keysHidden = inputFlags&MASK_KEYSHIDDEN; - const int setKeysHidden = settings.inputFlags&MASK_KEYSHIDDEN; - if (setKeysHidden != 0 && keysHidden != 0 - && keysHidden != setKeysHidden) { - // For compatibility, we count a request for KEYSHIDDEN_NO as also - // matching the more recent KEYSHIDDEN_SOFT. Basically - // KEYSHIDDEN_NO means there is some kind of keyboard available. - //LOGI("Matching keysHidden: have=%d, config=%d\n", keysHidden, setKeysHidden); - if (keysHidden != KEYSHIDDEN_NO || setKeysHidden != KEYSHIDDEN_SOFT) { - //LOGI("No match!"); - return false; - } - } - if (settings.keyboard != 0 && keyboard != 0 - && keyboard != settings.keyboard) { - return false; - } - if (settings.navigation != 0 && navigation != 0 - && navigation != settings.navigation) { - return false; - } - } - if (screenSize != 0) { - if (settings.screenWidth != 0 && screenWidth != 0 - && screenWidth != settings.screenWidth) { - return false; - } - if (settings.screenHeight != 0 && screenHeight != 0 - && screenHeight != settings.screenHeight) { - return false; - } - } - if (version != 0) { - if (settings.sdkVersion != 0 && sdkVersion != 0 - && sdkVersion != settings.sdkVersion) { - return false; - } - if (settings.minorVersion != 0 && minorVersion != 0 - && minorVersion != settings.minorVersion) { - return false; - } - } - return true; - } - - void getLocale(char str[6]) const { - memset(str, 0, 6); - if (language[0]) { - str[0] = language[0]; - str[1] = language[1]; - if (country[0]) { - str[2] = '_'; - str[3] = country[0]; - str[4] = country[1]; - } - } - } - - String8 toString() const { - char buf[200]; - sprintf(buf, "imsi=%d/%d lang=%c%c reg=%c%c orient=0x%02x touch=0x%02x dens=0x%02x " - "kbd=0x%02x nav=0x%02x input=0x%02x screenW=0x%04x screenH=0x%04x vers=%d.%d", - mcc, mnc, - language[0] ? language[0] : '-', language[1] ? language[1] : '-', - country[0] ? country[0] : '-', country[1] ? country[1] : '-', - orientation, touchscreen, density, keyboard, navigation, inputFlags, - screenWidth, screenHeight, sdkVersion, minorVersion); - return String8(buf); - } -}; - -/** - * A specification of the resources defined by a particular type. - * - * There should be one of these chunks for each resource type. - * - * This structure is followed by an array of integers providing the set of - * configuation change flags (ResTable_config::CONFIG_*) that have multiple - * resources for that configuration. In addition, the high bit is set if that - * resource has been made public. - */ -struct ResTable_typeSpec -{ - struct ResChunk_header header; - - // The type identifier this chunk is holding. Type IDs start - // at 1 (corresponding to the value of the type bits in a - // resource identifier). 0 is invalid. - uint8_t id; - - // Must be 0. - uint8_t res0; - // Must be 0. - uint16_t res1; - - // Number of uint32_t entry configuration masks that follow. - uint32_t entryCount; - - enum { - // Additional flag indicating an entry is public. - SPEC_PUBLIC = 0x40000000 - }; -}; - -/** - * A collection of resource entries for a particular resource data - * type. Followed by an array of uint32_t defining the resource - * values, corresponding to the array of type strings in the - * ResTable_package::typeStrings string block. Each of these hold an - * index from entriesStart; a value of NO_ENTRY means that entry is - * not defined. - * - * There may be multiple of these chunks for a particular resource type, - * supply different configuration variations for the resource values of - * that type. - * - * It would be nice to have an additional ordered index of entries, so - * we can do a binary search if trying to find a resource by string name. - */ -struct ResTable_type -{ - struct ResChunk_header header; - - enum { - NO_ENTRY = 0xFFFFFFFF - }; - - // The type identifier this chunk is holding. Type IDs start - // at 1 (corresponding to the value of the type bits in a - // resource identifier). 0 is invalid. - uint8_t id; - - // Must be 0. - uint8_t res0; - // Must be 0. - uint16_t res1; - - // Number of uint32_t entry indices that follow. - uint32_t entryCount; - - // Offset from header where ResTable_entry data starts. - uint32_t entriesStart; - - // Configuration this collection of entries is designed for. - ResTable_config config; -}; - -/** - * 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. - * * An array of ResTable_map structures, if FLAG_COMPLEX is set. - * These supply a set of name/value mappings of data. - */ -struct ResTable_entry -{ - // Number of bytes in this structure. - uint16_t size; - - enum { - // If set, this is a complex entry, holding a set of name/value - // mappings. It is followed by an array of ResTable_map structures. - FLAG_COMPLEX = 0x0001, - // If set, this resource has been declared public, so libraries - // are allowed to reference it. - FLAG_PUBLIC = 0x0002 - }; - uint16_t flags; - - // Reference into ResTable_package::keyStrings identifying this entry. - struct ResStringPool_ref key; -}; - -/** - * Extended form of a ResTable_entry for map entries, defining a parent map - * resource from which to inherit values. - */ -struct ResTable_map_entry : public ResTable_entry -{ - // Resource identifier of the parent mapping, or 0 if there is none. - ResTable_ref parent; - // Number of name/value pairs that follow for FLAG_COMPLEX. - uint32_t count; -}; - -/** - * A single name/value mapping that is part of a complex resource - * entry. - */ -struct ResTable_map -{ - // The resource identifier defining this mapping's name. For attribute - // resources, 'name' can be one of the following special resource types - // to supply meta-data about the attribute; for all other resource types - // it must be an attribute resource. - ResTable_ref name; - - // Special values for 'name' when defining attribute resources. - enum { - // This entry holds the attribute's type code. - ATTR_TYPE = Res_MAKEINTERNAL(0), - - // For integral attributes, this is the minimum value it can hold. - ATTR_MIN = Res_MAKEINTERNAL(1), - - // For integral attributes, this is the maximum value it can hold. - ATTR_MAX = Res_MAKEINTERNAL(2), - - // Localization of this resource is can be encouraged or required with - // an aapt flag if this is set - ATTR_L10N = Res_MAKEINTERNAL(3), - - // for plural support, see android.content.res.PluralRules#attrForQuantity(int) - ATTR_OTHER = Res_MAKEINTERNAL(4), - ATTR_ZERO = Res_MAKEINTERNAL(5), - ATTR_ONE = Res_MAKEINTERNAL(6), - ATTR_TWO = Res_MAKEINTERNAL(7), - ATTR_FEW = Res_MAKEINTERNAL(8), - ATTR_MANY = Res_MAKEINTERNAL(9) - - }; - - // Bit mask of allowed types, for use with ATTR_TYPE. - enum { - // No type has been defined for this attribute, use generic - // type handling. The low 16 bits are for types that can be - // handled generically; the upper 16 require additional information - // in the bag so can not be handled generically for TYPE_ANY. - TYPE_ANY = 0x0000FFFF, - - // Attribute holds a references to another resource. - TYPE_REFERENCE = 1<<0, - - // Attribute holds a generic string. - TYPE_STRING = 1<<1, - - // Attribute holds an integer value. ATTR_MIN and ATTR_MIN can - // optionally specify a constrained range of possible integer values. - TYPE_INTEGER = 1<<2, - - // Attribute holds a boolean integer. - TYPE_BOOLEAN = 1<<3, - - // Attribute holds a color value. - TYPE_COLOR = 1<<4, - - // Attribute holds a floating point value. - TYPE_FLOAT = 1<<5, - - // Attribute holds a dimension value, such as "20px". - TYPE_DIMENSION = 1<<6, - - // Attribute holds a fraction value, such as "20%". - TYPE_FRACTION = 1<<7, - - // Attribute holds an enumeration. The enumeration values are - // supplied as additional entries in the map. - TYPE_ENUM = 1<<16, - - // Attribute holds a bitmaks of flags. The flag bit values are - // supplied as additional entries in the map. - TYPE_FLAGS = 1<<17 - }; - - // Enum of localization modes, for use with ATTR_L10N. - enum { - L10N_NOT_REQUIRED = 0, - L10N_SUGGESTED = 1 - }; - - // This mapping's value. - Res_value value; -}; - -/** - * Convenience class for accessing data in a ResTable resource. - */ -class ResTable -{ -public: - ResTable(); - ResTable(const void* data, size_t size, void* cookie, - bool copyData=false); - ~ResTable(); - - status_t add(const void* data, size_t size, void* cookie, - bool copyData=false); - status_t add(Asset* asset, void* cookie, - bool copyData=false); - - status_t getError() const; - - void uninit(); - - struct resource_name - { - const char16_t* package; - size_t packageLen; - const char16_t* type; - size_t typeLen; - const char16_t* name; - size_t nameLen; - }; - - bool getResourceName(uint32_t resID, resource_name* outName) const; - - /** - * Retrieve the value of a resource. If the resource is found, returns a - * value >= 0 indicating the table it is in (for use with - * getTableStringBlock() and getTableCookie()) and fills in 'outValue'. If - * not found, returns a negative error code. - * - * Note that this function does not do reference traversal. If you want - * to follow references to other resources to get the "real" value to - * use, you need to call resolveReference() after this function. - * - * @param resID The desired resoruce identifier. - * @param outValue Filled in with the resource data that was found. - * - * @return ssize_t Either a >= 0 table index or a negative error code. - */ - ssize_t getResource(uint32_t resID, Res_value* outValue, bool mayBeBag=false, - uint32_t* outSpecFlags=NULL, ResTable_config* outConfig=NULL) const; - - inline ssize_t getResource(const ResTable_ref& res, Res_value* outValue, - uint32_t* outSpecFlags=NULL) const { - return getResource(res.ident, outValue, false, outSpecFlags, NULL); - } - - ssize_t resolveReference(Res_value* inOutValue, - ssize_t blockIndex, - uint32_t* outLastRef = NULL, - uint32_t* inoutTypeSpecFlags = NULL) const; - - enum { - TMP_BUFFER_SIZE = 16 - }; - const char16_t* valueToString(const Res_value* value, size_t stringBlock, - char16_t tmpBuffer[TMP_BUFFER_SIZE], - size_t* outLen); - - struct bag_entry { - ssize_t stringBlock; - ResTable_map map; - }; - - /** - * Retrieve the bag of a resource. If the resoruce is found, returns the - * number of bags it contains and 'outBag' points to an array of their - * values. If not found, a negative error code is returned. - * - * Note that this function -does- do reference traversal of the bag data. - * - * @param resID The desired resource identifier. - * @param outBag Filled inm with a pointer to the bag mappings. - * - * @return ssize_t Either a >= 0 bag count of negative error code. - */ - ssize_t lockBag(uint32_t resID, const bag_entry** outBag) const; - - void unlockBag(const bag_entry* bag) const; - - void lock() const; - - ssize_t getBagLocked(uint32_t resID, const bag_entry** outBag, - uint32_t* outTypeSpecFlags=NULL) const; - - void unlock() const; - - class Theme { - public: - Theme(const ResTable& table); - ~Theme(); - - inline const ResTable& getResTable() const { return mTable; } - - status_t applyStyle(uint32_t resID, bool force=false); - status_t setTo(const Theme& other); - - /** - * Retrieve a value in the theme. If the theme defines this - * value, returns a value >= 0 indicating the table it is in - * (for use with getTableStringBlock() and getTableCookie) and - * fills in 'outValue'. If not found, returns a negative error - * code. - * - * Note that this function does not do reference traversal. If you want - * to follow references to other resources to get the "real" value to - * use, you need to call resolveReference() after this function. - * - * @param resID A resource identifier naming the desired theme - * attribute. - * @param outValue Filled in with the theme value that was - * found. - * - * @return ssize_t Either a >= 0 table index or a negative error code. - */ - ssize_t getAttribute(uint32_t resID, Res_value* outValue, - uint32_t* outTypeSpecFlags = NULL) const; - - /** - * This is like ResTable::resolveReference(), but also takes - * care of resolving attribute references to the theme. - */ - ssize_t resolveAttributeReference(Res_value* inOutValue, - ssize_t blockIndex, uint32_t* outLastRef = NULL, - uint32_t* inoutTypeSpecFlags = NULL) const; - - void dumpToLog() const; - - private: - Theme(const Theme&); - Theme& operator=(const Theme&); - - struct theme_entry { - ssize_t stringBlock; - uint32_t typeSpecFlags; - Res_value value; - }; - struct type_info { - size_t numEntries; - theme_entry* entries; - }; - struct package_info { - size_t numTypes; - type_info types[]; - }; - - void free_package(package_info* pi); - package_info* copy_package(package_info* pi); - - const ResTable& mTable; - package_info* mPackages[Res_MAXPACKAGE]; - }; - - void setParameters(const ResTable_config* params); - void getParameters(ResTable_config* params) const; - - // Retrieve an identifier (which can be passed to getResource) - // for a given resource name. The 'name' can be fully qualified - // (:.) or the package or type components - // can be dropped if default values are supplied here. - // - // Returns 0 if no such resource was found, else a valid resource ID. - uint32_t identifierForName(const char16_t* name, size_t nameLen, - const char16_t* type = 0, size_t typeLen = 0, - const char16_t* defPackage = 0, - size_t defPackageLen = 0, - uint32_t* outTypeSpecFlags = NULL) const; - - static bool expandResourceRef(const uint16_t* refStr, size_t refLen, - String16* outPackage, - String16* outType, - String16* outName, - const String16* defType = NULL, - const String16* defPackage = NULL, - const char** outErrorMsg = NULL); - - static bool stringToInt(const char16_t* s, size_t len, Res_value* outValue); - static bool stringToFloat(const char16_t* s, size_t len, Res_value* outValue); - - // Used with stringToValue. - class Accessor - { - public: - inline virtual ~Accessor() { } - - virtual uint32_t getCustomResource(const String16& package, - const String16& type, - const String16& name) const = 0; - virtual uint32_t getCustomResourceWithCreation(const String16& package, - const String16& type, - const String16& name, - const bool createIfNeeded = false) = 0; - virtual uint32_t getRemappedPackage(uint32_t origPackage) const = 0; - virtual bool getAttributeType(uint32_t attrID, uint32_t* outType) = 0; - virtual bool getAttributeMin(uint32_t attrID, uint32_t* outMin) = 0; - virtual bool getAttributeMax(uint32_t attrID, uint32_t* outMax) = 0; - virtual bool getAttributeEnum(uint32_t attrID, - const char16_t* name, size_t nameLen, - Res_value* outValue) = 0; - virtual bool getAttributeFlags(uint32_t attrID, - const char16_t* name, size_t nameLen, - Res_value* outValue) = 0; - virtual uint32_t getAttributeL10N(uint32_t attrID) = 0; - virtual bool getLocalizationSetting() = 0; - virtual void reportError(void* accessorCookie, const char* fmt, ...) = 0; - }; - - // Convert a string to a resource value. Handles standard "@res", - // "#color", "123", and "0x1bd" types; performs escaping of strings. - // The resulting value is placed in 'outValue'; if it is a string type, - // 'outString' receives the string. If 'attrID' is supplied, the value is - // type checked against this attribute and it is used to perform enum - // evaluation. If 'acccessor' is supplied, it will be used to attempt to - // resolve resources that do not exist in this ResTable. If 'attrType' is - // supplied, the value will be type checked for this format if 'attrID' - // is not supplied or found. - bool stringToValue(Res_value* outValue, String16* outString, - const char16_t* s, size_t len, - bool preserveSpaces, bool coerceType, - uint32_t attrID = 0, - const String16* defType = NULL, - const String16* defPackage = NULL, - Accessor* accessor = NULL, - void* accessorCookie = NULL, - uint32_t attrType = ResTable_map::TYPE_ANY, - bool enforcePrivate = true) const; - - // Perform processing of escapes and quotes in a string. - static bool collectString(String16* outString, - const char16_t* s, size_t len, - bool preserveSpaces, - const char** outErrorMsg = NULL, - bool append = false); - - size_t getBasePackageCount() const; - const char16_t* getBasePackageName(size_t idx) const; - uint32_t getBasePackageId(size_t idx) const; - - size_t getTableCount() const; - const ResStringPool* getTableStringBlock(size_t index) const; - void* getTableCookie(size_t index) const; - - // Return the configurations (ResTable_config) that we know about - void getConfigurations(Vector* configs) const; - - void getLocales(Vector* locales) const; - -#ifndef HAVE_ANDROID_OS - void print() const; -#endif - -private: - struct Header; - struct Type; - struct Package; - struct PackageGroup; - struct bag_set; - - status_t add(const void* data, size_t size, void* cookie, - Asset* asset, bool copyData); - - ssize_t getResourcePackageIndex(uint32_t resID) const; - ssize_t getEntry( - const Package* package, int typeIndex, int entryIndex, - const ResTable_config* config, - const ResTable_type** outType, const ResTable_entry** outEntry, - const Type** outTypeClass) const; - status_t parsePackage( - const ResTable_package* const pkg, const Header* const header); - - mutable Mutex mLock; - - status_t mError; - - ResTable_config mParams; - - // Array of all resource tables. - Vector mHeaders; - - // Array of packages in all resource tables. - Vector mPackageGroups; - - // Mapping from resource package IDs to indices into the internal - // package array. - uint8_t mPackageMap[256]; -}; - -} // namespace android - -#endif // _LIBS_UTILS_RESOURCE_TYPES_H diff --git a/include/utils/SharedBuffer.h b/include/utils/SharedBuffer.h deleted file mode 100644 index 24508b0f7..000000000 --- a/include/utils/SharedBuffer.h +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright (C) 2005 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_SHARED_BUFFER_H -#define ANDROID_SHARED_BUFFER_H - -#include -#include - -// --------------------------------------------------------------------------- - -namespace android { - -class SharedBuffer -{ -public: - - /* flags to use with release() */ - enum { - eKeepStorage = 0x00000001 - }; - - /*! allocate a buffer of size 'size' and acquire() it. - * call release() to free it. - */ - static SharedBuffer* alloc(size_t size); - - /*! free the memory associated with the SharedBuffer. - * Fails if there are any users associated with this SharedBuffer. - * In other words, the buffer must have been release by all its - * users. - */ - static ssize_t dealloc(const SharedBuffer* released); - - //! get the SharedBuffer from the data pointer - static inline const SharedBuffer* sharedBuffer(const void* data); - - //! access the data for read - inline const void* data() const; - - //! access the data for read/write - inline void* data(); - - //! get size of the buffer - inline size_t size() const; - - //! get back a SharedBuffer object from its data - static inline SharedBuffer* bufferFromData(void* data); - - //! get back a SharedBuffer object from its data - static inline const SharedBuffer* bufferFromData(const void* data); - - //! get the size of a SharedBuffer object from its data - static inline size_t sizeFromData(const void* data); - - //! edit the buffer (get a writtable, or non-const, version of it) - SharedBuffer* edit() const; - - //! edit the buffer, resizing if needed - SharedBuffer* editResize(size_t size) const; - - //! like edit() but fails if a copy is required - SharedBuffer* attemptEdit() const; - - //! resize and edit the buffer, loose it's content. - SharedBuffer* reset(size_t size) const; - - //! acquire/release a reference on this buffer - void acquire() const; - - /*! release a reference on this buffer, with the option of not - * freeing the memory associated with it if it was the last reference - * returns the previous reference count - */ - int32_t release(uint32_t flags = 0) const; - - //! returns wether or not we're the only owner - inline bool onlyOwner() const; - - -private: - inline SharedBuffer() { } - inline ~SharedBuffer() { } - inline SharedBuffer(const SharedBuffer&); - - // 16 bytes. must be sized to preserve correct alingment. - mutable int32_t mRefs; - size_t mSize; - uint32_t mReserved[2]; -}; - -// --------------------------------------------------------------------------- - -const SharedBuffer* SharedBuffer::sharedBuffer(const void* data) { - return data ? reinterpret_cast(data)-1 : 0; -} - -const void* SharedBuffer::data() const { - return this + 1; -} - -void* SharedBuffer::data() { - return this + 1; -} - -size_t SharedBuffer::size() const { - return mSize; -} - -SharedBuffer* SharedBuffer::bufferFromData(void* data) -{ - return ((SharedBuffer*)data)-1; -} - -const SharedBuffer* SharedBuffer::bufferFromData(const void* data) -{ - return ((const SharedBuffer*)data)-1; -} - -size_t SharedBuffer::sizeFromData(const void* data) -{ - return (((const SharedBuffer*)data)-1)->mSize; -} - -bool SharedBuffer::onlyOwner() const { - return (mRefs == 1); -} - -}; // namespace android - -// --------------------------------------------------------------------------- - -#endif // ANDROID_VECTOR_H diff --git a/include/utils/Socket.h b/include/utils/Socket.h deleted file mode 100644 index 8b7f40617..000000000 --- a/include/utils/Socket.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (C) 2005 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. - */ - -// -// Socket class. Modeled after Java classes. -// -#ifndef _RUNTIME_SOCKET_H -#define _RUNTIME_SOCKET_H - -#include -#include - -namespace android { - -/* - * Basic socket class, needed to abstract away the differences between - * BSD sockets and WinSock. This establishes a streaming network - * connection (TCP/IP) to somebody. - */ -class Socket { -public: - Socket(void); - ~Socket(void); - - // Create a connection to somewhere. - // Return 0 on success. - int connect(const char* host, int port); - int connect(const InetAddress* addr, int port); - - - // Close the socket. Don't try to use this object again after - // calling this. Returns false on failure. - bool close(void); - - // If we created the socket without an address, we can use these - // to finish the connection. Returns 0 on success. - int bind(const SocketAddress& bindPoint); - int connect(const SocketAddress& endPoint); - - // Here we deviate from the traditional object-oriented fanciness - // and just provide read/write operators instead of getters for - // objects that abstract a stream. - // - // Standard read/write semantics. - int read(void* buf, ssize_t len) const; - int write(const void* buf, ssize_t len) const; - - // This must be called once, at program startup. - static bool bootInit(void); - static void finalShutdown(void); - -private: - // Internal function that establishes a connection. - int doConnect(const InetSocketAddress& addr); - - unsigned long mSock; // holds SOCKET or int - - static bool mBootInitialized; -}; - - -// debug -- unit tests -void TestSockets(void); - -}; // namespace android - -#endif // _RUNTIME_SOCKET_H diff --git a/include/utils/SortedVector.h b/include/utils/SortedVector.h deleted file mode 100644 index c8a61531f..000000000 --- a/include/utils/SortedVector.h +++ /dev/null @@ -1,282 +0,0 @@ -/* - * Copyright (C) 2005 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_SORTED_VECTOR_H -#define ANDROID_SORTED_VECTOR_H - -#include -#include -#include - -#include -#include -#include - -// --------------------------------------------------------------------------- - -namespace android { - -template -class SortedVector : private SortedVectorImpl -{ -public: - typedef TYPE value_type; - - /*! - * Constructors and destructors - */ - - SortedVector(); - SortedVector(const SortedVector& rhs); - virtual ~SortedVector(); - - /*! copy operator */ - const SortedVector& operator = (const SortedVector& rhs) const; - SortedVector& operator = (const SortedVector& rhs); - - /* - * empty the vector - */ - - inline void clear() { VectorImpl::clear(); } - - /*! - * vector stats - */ - - //! returns number of items in the vector - inline size_t size() const { return VectorImpl::size(); } - //! returns wether or not the vector is empty - inline bool isEmpty() const { return VectorImpl::isEmpty(); } - //! returns how many items can be stored without reallocating the backing store - inline size_t capacity() const { return VectorImpl::capacity(); } - //! setst the capacity. capacity can never be reduced less than size() - inline ssize_t setCapacity(size_t size) { return VectorImpl::setCapacity(size); } - - /*! - * C-style array access - */ - - //! read-only C-style access - inline const TYPE* array() const; - - //! read-write C-style access. BE VERY CAREFUL when modifying the array - //! you ust keep it sorted! You usually don't use this function. - TYPE* editArray(); - - //! finds the index of an item - ssize_t indexOf(const TYPE& item) const; - - //! finds where this item should be inserted - size_t orderOf(const TYPE& item) const; - - - /*! - * accessors - */ - - //! read-only access to an item at a given index - inline const TYPE& operator [] (size_t index) const; - //! alternate name for operator [] - inline const TYPE& itemAt(size_t index) const; - //! stack-usage of the vector. returns the top of the stack (last element) - const TYPE& top() const; - //! same as operator [], but allows to access the vector backward (from the end) with a negative index - const TYPE& mirrorItemAt(ssize_t index) const; - - /*! - * modifing the array - */ - - //! add an item in the right place (and replace the one that is there) - ssize_t add(const TYPE& item); - - //! editItemAt() MUST NOT change the order of this item - TYPE& editItemAt(size_t index) { - return *( static_cast(VectorImpl::editItemLocation(index)) ); - } - - //! merges a vector into this one - ssize_t merge(const Vector& vector); - ssize_t merge(const SortedVector& vector); - - //! removes an item - ssize_t remove(const TYPE&); - - //! remove several items - inline ssize_t removeItemsAt(size_t index, size_t count = 1); - //! remove one item - inline ssize_t removeAt(size_t index) { return removeItemsAt(index); } - -protected: - virtual void do_construct(void* storage, size_t num) const; - virtual void do_destroy(void* storage, size_t num) const; - virtual void do_copy(void* dest, const void* from, size_t num) const; - virtual void do_splat(void* dest, const void* item, size_t num) const; - virtual void do_move_forward(void* dest, const void* from, size_t num) const; - virtual void do_move_backward(void* dest, const void* from, size_t num) const; - virtual int do_compare(const void* lhs, const void* rhs) const; -}; - - -// --------------------------------------------------------------------------- -// No user serviceable parts from here... -// --------------------------------------------------------------------------- - -template inline -SortedVector::SortedVector() - : SortedVectorImpl(sizeof(TYPE), - ((traits::has_trivial_ctor ? HAS_TRIVIAL_CTOR : 0) - |(traits::has_trivial_dtor ? HAS_TRIVIAL_DTOR : 0) - |(traits::has_trivial_copy ? HAS_TRIVIAL_COPY : 0) - |(traits::has_trivial_assign ? HAS_TRIVIAL_ASSIGN : 0)) - ) -{ -} - -template inline -SortedVector::SortedVector(const SortedVector& rhs) - : SortedVectorImpl(rhs) { -} - -template inline -SortedVector::~SortedVector() { - finish_vector(); -} - -template inline -SortedVector& SortedVector::operator = (const SortedVector& rhs) { - SortedVectorImpl::operator = (rhs); - return *this; -} - -template inline -const SortedVector& SortedVector::operator = (const SortedVector& rhs) const { - SortedVectorImpl::operator = (rhs); - return *this; -} - -template inline -const TYPE* SortedVector::array() const { - return static_cast(arrayImpl()); -} - -template inline -TYPE* SortedVector::editArray() { - return static_cast(editArrayImpl()); -} - - -template inline -const TYPE& SortedVector::operator[](size_t index) const { - assert( index inline -const TYPE& SortedVector::itemAt(size_t index) const { - return operator[](index); -} - -template inline -const TYPE& SortedVector::mirrorItemAt(ssize_t index) const { - assert( (index>0 ? index : -index) inline -const TYPE& SortedVector::top() const { - return *(array() + size() - 1); -} - -template inline -ssize_t SortedVector::add(const TYPE& item) { - return SortedVectorImpl::add(&item); -} - -template inline -ssize_t SortedVector::indexOf(const TYPE& item) const { - return SortedVectorImpl::indexOf(&item); -} - -template inline -size_t SortedVector::orderOf(const TYPE& item) const { - return SortedVectorImpl::orderOf(&item); -} - -template inline -ssize_t SortedVector::merge(const Vector& vector) { - return SortedVectorImpl::merge(reinterpret_cast(vector)); -} - -template inline -ssize_t SortedVector::merge(const SortedVector& vector) { - return SortedVectorImpl::merge(reinterpret_cast(vector)); -} - -template inline -ssize_t SortedVector::remove(const TYPE& item) { - return SortedVectorImpl::remove(&item); -} - -template inline -ssize_t SortedVector::removeItemsAt(size_t index, size_t count) { - return VectorImpl::removeItemsAt(index, count); -} - -// --------------------------------------------------------------------------- - -template -void SortedVector::do_construct(void* storage, size_t num) const { - construct_type( reinterpret_cast(storage), num ); -} - -template -void SortedVector::do_destroy(void* storage, size_t num) const { - destroy_type( reinterpret_cast(storage), num ); -} - -template -void SortedVector::do_copy(void* dest, const void* from, size_t num) const { - copy_type( reinterpret_cast(dest), reinterpret_cast(from), num ); -} - -template -void SortedVector::do_splat(void* dest, const void* item, size_t num) const { - splat_type( reinterpret_cast(dest), reinterpret_cast(item), num ); -} - -template -void SortedVector::do_move_forward(void* dest, const void* from, size_t num) const { - move_forward_type( reinterpret_cast(dest), reinterpret_cast(from), num ); -} - -template -void SortedVector::do_move_backward(void* dest, const void* from, size_t num) const { - move_backward_type( reinterpret_cast(dest), reinterpret_cast(from), num ); -} - -template -int SortedVector::do_compare(const void* lhs, const void* rhs) const { - return compare_type( *reinterpret_cast(lhs), *reinterpret_cast(rhs) ); -} - -}; // namespace android - - -// --------------------------------------------------------------------------- - -#endif // ANDROID_SORTED_VECTOR_H diff --git a/include/utils/StopWatch.h b/include/utils/StopWatch.h deleted file mode 100644 index cc0bebc40..000000000 --- a/include/utils/StopWatch.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2005 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_STOPWATCH_H -#define ANDROID_STOPWATCH_H - -#include -#include - -#include - -// --------------------------------------------------------------------------- - -namespace android { - -class StopWatch -{ -public: - StopWatch( const char *name, - int clock = SYSTEM_TIME_MONOTONIC, - uint32_t flags = 0); - ~StopWatch(); - - const char* name() const; - nsecs_t lap(); - nsecs_t elapsedTime() const; - -private: - const char* mName; - int mClock; - uint32_t mFlags; - - struct lap_t { - nsecs_t soFar; - nsecs_t thisLap; - }; - - nsecs_t mStartTime; - lap_t mLaps[8]; - int mNumLaps; -}; - - -}; // namespace android - - -// --------------------------------------------------------------------------- - -#endif // ANDROID_STOPWATCH_H diff --git a/include/utils/String16.h b/include/utils/String16.h deleted file mode 100644 index a2d22eea9..000000000 --- a/include/utils/String16.h +++ /dev/null @@ -1,260 +0,0 @@ -/* - * Copyright (C) 2005 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_STRING16_H -#define ANDROID_STRING16_H - -#include -#include - -#include -#include - -// --------------------------------------------------------------------------- - -extern "C" { - -typedef uint16_t char16_t; - -// Standard string functions on char16 strings. -int strcmp16(const char16_t *, const char16_t *); -int strncmp16(const char16_t *s1, const char16_t *s2, size_t n); -size_t strlen16(const char16_t *); -size_t strnlen16(const char16_t *, size_t); -char16_t *strcpy16(char16_t *, const char16_t *); -char16_t *strncpy16(char16_t *, const char16_t *, size_t); - -// Version of comparison that supports embedded nulls. -// This is different than strncmp() because we don't stop -// at a nul character and consider the strings to be different -// if the lengths are different (thus we need to supply the -// lengths of both strings). This can also be used when -// your string is not nul-terminated as it will have the -// equivalent result as strcmp16 (unlike strncmp16). -int strzcmp16(const char16_t *s1, size_t n1, const char16_t *s2, size_t n2); - -// Version of strzcmp16 for comparing strings in different endianness. -int strzcmp16_h_n(const char16_t *s1H, size_t n1, const char16_t *s2N, size_t n2); - -} - -// --------------------------------------------------------------------------- - -namespace android { - -class String8; -class TextOutput; - -//! This is a string holding UTF-16 characters. -class String16 -{ -public: - String16(); - String16(const String16& o); - String16(const String16& o, - size_t len, - size_t begin=0); - explicit String16(const char16_t* o); - explicit String16(const char16_t* o, size_t len); - explicit String16(const String8& o); - explicit String16(const char* o); - explicit String16(const char* o, size_t len); - - ~String16(); - - inline const char16_t* string() const; - inline size_t size() const; - - inline const SharedBuffer* sharedBuffer() const; - - void setTo(const String16& other); - status_t setTo(const char16_t* other); - status_t setTo(const char16_t* other, size_t len); - status_t setTo(const String16& other, - size_t len, - size_t begin=0); - - status_t append(const String16& other); - status_t append(const char16_t* other, size_t len); - - inline String16& operator=(const String16& other); - - inline String16& operator+=(const String16& other); - inline String16 operator+(const String16& other) const; - - status_t insert(size_t pos, const char16_t* chrs); - status_t insert(size_t pos, - const char16_t* chrs, size_t len); - - ssize_t findFirst(char16_t c) const; - ssize_t findLast(char16_t c) const; - - bool startsWith(const String16& prefix) const; - bool startsWith(const char16_t* prefix) const; - - status_t makeLower(); - - status_t replaceAll(char16_t replaceThis, - char16_t withThis); - - status_t remove(size_t len, size_t begin=0); - - inline int compare(const String16& other) const; - - inline bool operator<(const String16& other) const; - inline bool operator<=(const String16& other) const; - inline bool operator==(const String16& other) const; - inline bool operator!=(const String16& other) const; - inline bool operator>=(const String16& other) const; - inline bool operator>(const String16& other) const; - - inline bool operator<(const char16_t* other) const; - inline bool operator<=(const char16_t* other) const; - inline bool operator==(const char16_t* other) const; - inline bool operator!=(const char16_t* other) const; - inline bool operator>=(const char16_t* other) const; - inline bool operator>(const char16_t* other) const; - - inline operator const char16_t*() const; - -private: - const char16_t* mString; -}; - -TextOutput& operator<<(TextOutput& to, const String16& val); - -// --------------------------------------------------------------------------- -// No user servicable parts below. - -inline int compare_type(const String16& lhs, const String16& rhs) -{ - return lhs.compare(rhs); -} - -inline int strictly_order_type(const String16& lhs, const String16& rhs) -{ - return compare_type(lhs, rhs) < 0; -} - -inline const char16_t* String16::string() const -{ - return mString; -} - -inline size_t String16::size() const -{ - return SharedBuffer::sizeFromData(mString)/sizeof(char16_t)-1; -} - -inline const SharedBuffer* String16::sharedBuffer() const -{ - return SharedBuffer::bufferFromData(mString); -} - -inline String16& String16::operator=(const String16& other) -{ - setTo(other); - return *this; -} - -inline String16& String16::operator+=(const String16& other) -{ - append(other); - return *this; -} - -inline String16 String16::operator+(const String16& other) const -{ - String16 tmp; - tmp += other; - return tmp; -} - -inline int String16::compare(const String16& other) const -{ - return strzcmp16(mString, size(), other.mString, other.size()); -} - -inline bool String16::operator<(const String16& other) const -{ - return strzcmp16(mString, size(), other.mString, other.size()) < 0; -} - -inline bool String16::operator<=(const String16& other) const -{ - return strzcmp16(mString, size(), other.mString, other.size()) <= 0; -} - -inline bool String16::operator==(const String16& other) const -{ - return strzcmp16(mString, size(), other.mString, other.size()) == 0; -} - -inline bool String16::operator!=(const String16& other) const -{ - return strzcmp16(mString, size(), other.mString, other.size()) != 0; -} - -inline bool String16::operator>=(const String16& other) const -{ - return strzcmp16(mString, size(), other.mString, other.size()) >= 0; -} - -inline bool String16::operator>(const String16& other) const -{ - return strzcmp16(mString, size(), other.mString, other.size()) > 0; -} - -inline bool String16::operator<(const char16_t* other) const -{ - return strcmp16(mString, other) < 0; -} - -inline bool String16::operator<=(const char16_t* other) const -{ - return strcmp16(mString, other) <= 0; -} - -inline bool String16::operator==(const char16_t* other) const -{ - return strcmp16(mString, other) == 0; -} - -inline bool String16::operator!=(const char16_t* other) const -{ - return strcmp16(mString, other) != 0; -} - -inline bool String16::operator>=(const char16_t* other) const -{ - return strcmp16(mString, other) >= 0; -} - -inline bool String16::operator>(const char16_t* other) const -{ - return strcmp16(mString, other) > 0; -} - -inline String16::operator const char16_t*() const -{ - return mString; -} - -}; // namespace android - -// --------------------------------------------------------------------------- - -#endif // ANDROID_STRING16_H diff --git a/include/utils/String8.h b/include/utils/String8.h deleted file mode 100644 index c49faf6fe..000000000 --- a/include/utils/String8.h +++ /dev/null @@ -1,353 +0,0 @@ -/* - * Copyright (C) 2005 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_STRING8_H -#define ANDROID_STRING8_H - -#include - -// Need this for the char16_t type; String8.h should not -// be depedent on the String16 class. -#include - -#include -#include -#include - -// --------------------------------------------------------------------------- - -namespace android { - -class TextOutput; - -//! This is a string holding UTF-8 characters. -class String8 -{ -public: - String8(); - String8(const String8& o); - explicit String8(const char* o); - explicit String8(const char* o, size_t numChars); - - explicit String8(const String16& o); - explicit String8(const char16_t* o); - explicit String8(const char16_t* o, size_t numChars); - - ~String8(); - - inline const char* string() const; - inline size_t size() const; - inline size_t length() const; - inline size_t bytes() const; - - inline const SharedBuffer* sharedBuffer() const; - - void setTo(const String8& other); - status_t setTo(const char* other); - status_t setTo(const char* other, size_t numChars); - status_t setTo(const char16_t* other, size_t numChars); - - status_t append(const String8& other); - status_t append(const char* other); - status_t append(const char* other, size_t numChars); - - inline String8& operator=(const String8& other); - inline String8& operator=(const char* other); - - inline String8& operator+=(const String8& other); - inline String8 operator+(const String8& other) const; - - inline String8& operator+=(const char* other); - inline String8 operator+(const char* other) const; - - inline int compare(const String8& other) const; - - inline bool operator<(const String8& other) const; - inline bool operator<=(const String8& other) const; - inline bool operator==(const String8& other) const; - inline bool operator!=(const String8& other) const; - inline bool operator>=(const String8& other) const; - inline bool operator>(const String8& other) const; - - inline bool operator<(const char* other) const; - inline bool operator<=(const char* other) const; - inline bool operator==(const char* other) const; - inline bool operator!=(const char* other) const; - inline bool operator>=(const char* other) const; - inline bool operator>(const char* other) const; - - inline operator const char*() const; - - char* lockBuffer(size_t size); - void unlockBuffer(); - status_t unlockBuffer(size_t size); - - // return the index of the first byte of other in this at or after - // start, or -1 if not found - ssize_t find(const char* other, size_t start = 0) const; - - void toLower(); - void toLower(size_t start, size_t numChars); - void toUpper(); - void toUpper(size_t start, size_t numChars); - - /* - * These methods operate on the string as if it were a path name. - */ - - /* - * Set the filename field to a specific value. - * - * Normalizes the filename, removing a trailing '/' if present. - */ - void setPathName(const char* name); - void setPathName(const char* name, size_t numChars); - - /* - * Get just the filename component. - * - * "/tmp/foo/bar.c" --> "bar.c" - */ - String8 getPathLeaf(void) const; - - /* - * Remove the last (file name) component, leaving just the directory - * name. - * - * "/tmp/foo/bar.c" --> "/tmp/foo" - * "/tmp" --> "" // ????? shouldn't this be "/" ???? XXX - * "bar.c" --> "" - */ - String8 getPathDir(void) const; - - /* - * Retrieve the front (root dir) component. Optionally also return the - * remaining components. - * - * "/tmp/foo/bar.c" --> "tmp" (remain = "foo/bar.c") - * "/tmp" --> "tmp" (remain = "") - * "bar.c" --> "bar.c" (remain = "") - */ - String8 walkPath(String8* outRemains = NULL) const; - - /* - * Return the filename extension. This is the last '.' and up to - * four characters that follow it. The '.' is included in case we - * decide to expand our definition of what constitutes an extension. - * - * "/tmp/foo/bar.c" --> ".c" - * "/tmp" --> "" - * "/tmp/foo.bar/baz" --> "" - * "foo.jpeg" --> ".jpeg" - * "foo." --> "" - */ - String8 getPathExtension(void) const; - - /* - * Return the path without the extension. Rules for what constitutes - * an extension are described in the comment for getPathExtension(). - * - * "/tmp/foo/bar.c" --> "/tmp/foo/bar" - */ - String8 getBasePath(void) const; - - /* - * Add a component to the pathname. We guarantee that there is - * exactly one path separator between the old path and the new. - * If there is no existing name, we just copy the new name in. - * - * If leaf is a fully qualified path (i.e. starts with '/', it - * replaces whatever was there before. - */ - String8& appendPath(const char* leaf); - String8& appendPath(const String8& leaf) { return appendPath(leaf.string()); } - - /* - * Like appendPath(), but does not affect this string. Returns a new one instead. - */ - String8 appendPathCopy(const char* leaf) const - { String8 p(*this); p.appendPath(leaf); return p; } - String8 appendPathCopy(const String8& leaf) const { return appendPathCopy(leaf.string()); } - - /* - * Converts all separators in this string to /, the default path separator. - * - * If the default OS separator is backslash, this converts all - * backslashes to slashes, in-place. Otherwise it does nothing. - * Returns self. - */ - String8& convertToResPath(); - -private: - status_t real_append(const char* other, size_t numChars); - char* find_extension(void) const; - - const char* mString; -}; - -TextOutput& operator<<(TextOutput& to, const String16& val); - -// --------------------------------------------------------------------------- -// No user servicable parts below. - -inline int compare_type(const String8& lhs, const String8& rhs) -{ - return lhs.compare(rhs); -} - -inline int strictly_order_type(const String8& lhs, const String8& rhs) -{ - return compare_type(lhs, rhs) < 0; -} - -inline const char* String8::string() const -{ - return mString; -} - -inline size_t String8::length() const -{ - return SharedBuffer::sizeFromData(mString)-1; -} - -inline size_t String8::size() const -{ - return length(); -} - -inline size_t String8::bytes() const -{ - return SharedBuffer::sizeFromData(mString)-1; -} - -inline const SharedBuffer* String8::sharedBuffer() const -{ - return SharedBuffer::bufferFromData(mString); -} - -inline String8& String8::operator=(const String8& other) -{ - setTo(other); - return *this; -} - -inline String8& String8::operator=(const char* other) -{ - setTo(other); - return *this; -} - -inline String8& String8::operator+=(const String8& other) -{ - append(other); - return *this; -} - -inline String8 String8::operator+(const String8& other) const -{ - String8 tmp; - tmp += other; - return tmp; -} - -inline String8& String8::operator+=(const char* other) -{ - append(other); - return *this; -} - -inline String8 String8::operator+(const char* other) const -{ - String8 tmp; - tmp += other; - return tmp; -} - -inline int String8::compare(const String8& other) const -{ - return strcmp(mString, other.mString); -} - -inline bool String8::operator<(const String8& other) const -{ - return strcmp(mString, other.mString) < 0; -} - -inline bool String8::operator<=(const String8& other) const -{ - return strcmp(mString, other.mString) <= 0; -} - -inline bool String8::operator==(const String8& other) const -{ - return strcmp(mString, other.mString) == 0; -} - -inline bool String8::operator!=(const String8& other) const -{ - return strcmp(mString, other.mString) != 0; -} - -inline bool String8::operator>=(const String8& other) const -{ - return strcmp(mString, other.mString) >= 0; -} - -inline bool String8::operator>(const String8& other) const -{ - return strcmp(mString, other.mString) > 0; -} - -inline bool String8::operator<(const char* other) const -{ - return strcmp(mString, other) < 0; -} - -inline bool String8::operator<=(const char* other) const -{ - return strcmp(mString, other) <= 0; -} - -inline bool String8::operator==(const char* other) const -{ - return strcmp(mString, other) == 0; -} - -inline bool String8::operator!=(const char* other) const -{ - return strcmp(mString, other) != 0; -} - -inline bool String8::operator>=(const char* other) const -{ - return strcmp(mString, other) >= 0; -} - -inline bool String8::operator>(const char* other) const -{ - return strcmp(mString, other) > 0; -} - -inline String8::operator const char*() const -{ - return mString; -} - -}; // namespace android - -// --------------------------------------------------------------------------- - -#endif // ANDROID_STRING8_H diff --git a/include/utils/SystemClock.h b/include/utils/SystemClock.h deleted file mode 100644 index 7c319be13..000000000 --- a/include/utils/SystemClock.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_UTILS_SYSTEMCLOCK_H -#define ANDROID_UTILS_SYSTEMCLOCK_H - -#include -#include - -namespace android { - -int setCurrentTimeMillis(int64_t millis); -int64_t uptimeMillis(); -int64_t elapsedRealtime(); - -}; // namespace android - -#endif // ANDROID_UTILS_SYSTEMCLOCK_H - diff --git a/include/utils/TextOutput.h b/include/utils/TextOutput.h deleted file mode 100644 index d8d86ba82..000000000 --- a/include/utils/TextOutput.h +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Copyright (C) 2006 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_TEXTOUTPUT_H -#define ANDROID_TEXTOUTPUT_H - -#include - -#include -#include - -// --------------------------------------------------------------------------- -namespace android { - -class TextOutput -{ -public: - TextOutput() { } - virtual ~TextOutput() { } - - virtual status_t print(const char* txt, size_t len) = 0; - virtual void moveIndent(int delta) = 0; - - class Bundle { - public: - inline Bundle(TextOutput& to) : mTO(to) { to.pushBundle(); } - inline ~Bundle() { mTO.popBundle(); } - private: - TextOutput& mTO; - }; - - virtual void pushBundle() = 0; - virtual void popBundle() = 0; -}; - -// --------------------------------------------------------------------------- - -// Text output stream for printing to the log (via utils/Log.h). -extern TextOutput& alog; - -// Text output stream for printing to stdout. -extern TextOutput& aout; - -// Text output stream for printing to stderr. -extern TextOutput& aerr; - -typedef TextOutput& (*TextOutputManipFunc)(TextOutput&); - -TextOutput& endl(TextOutput& to); -TextOutput& indent(TextOutput& to); -TextOutput& dedent(TextOutput& to); - -TextOutput& operator<<(TextOutput& to, const char* str); -TextOutput& operator<<(TextOutput& to, char); // writes raw character -TextOutput& operator<<(TextOutput& to, bool); -TextOutput& operator<<(TextOutput& to, int); -TextOutput& operator<<(TextOutput& to, long); -TextOutput& operator<<(TextOutput& to, unsigned int); -TextOutput& operator<<(TextOutput& to, unsigned long); -TextOutput& operator<<(TextOutput& to, long long); -TextOutput& operator<<(TextOutput& to, unsigned long long); -TextOutput& operator<<(TextOutput& to, float); -TextOutput& operator<<(TextOutput& to, double); -TextOutput& operator<<(TextOutput& to, TextOutputManipFunc func); -TextOutput& operator<<(TextOutput& to, const void*); - -class TypeCode -{ -public: - inline TypeCode(uint32_t code); - inline ~TypeCode(); - - inline uint32_t typeCode() const; - -private: - uint32_t mCode; -}; - -TextOutput& operator<<(TextOutput& to, const TypeCode& val); - -class HexDump -{ -public: - HexDump(const void *buf, size_t size, size_t bytesPerLine=16); - inline ~HexDump(); - - inline HexDump& setBytesPerLine(size_t bytesPerLine); - inline HexDump& setSingleLineCutoff(int32_t bytes); - inline HexDump& setAlignment(size_t alignment); - inline HexDump& setCArrayStyle(bool enabled); - - inline const void* buffer() const; - inline size_t size() const; - inline size_t bytesPerLine() const; - inline int32_t singleLineCutoff() const; - inline size_t alignment() const; - inline bool carrayStyle() const; - -private: - const void* mBuffer; - size_t mSize; - size_t mBytesPerLine; - int32_t mSingleLineCutoff; - size_t mAlignment; - bool mCArrayStyle; -}; - -TextOutput& operator<<(TextOutput& to, const HexDump& val); - -// --------------------------------------------------------------------------- -// No user servicable parts below. - -inline TextOutput& endl(TextOutput& to) -{ - to.print("\n", 1); - return to; -} - -inline TextOutput& indent(TextOutput& to) -{ - to.moveIndent(1); - return to; -} - -inline TextOutput& dedent(TextOutput& to) -{ - to.moveIndent(-1); - return to; -} - -inline TextOutput& operator<<(TextOutput& to, const char* str) -{ - to.print(str, strlen(str)); - return to; -} - -inline TextOutput& operator<<(TextOutput& to, char c) -{ - to.print(&c, 1); - return to; -} - -inline TextOutput& operator<<(TextOutput& to, TextOutputManipFunc func) -{ - return (*func)(to); -} - -inline TypeCode::TypeCode(uint32_t code) : mCode(code) { } -inline TypeCode::~TypeCode() { } -inline uint32_t TypeCode::typeCode() const { return mCode; } - -inline HexDump::~HexDump() { } - -inline HexDump& HexDump::setBytesPerLine(size_t bytesPerLine) { - mBytesPerLine = bytesPerLine; return *this; -} -inline HexDump& HexDump::setSingleLineCutoff(int32_t bytes) { - mSingleLineCutoff = bytes; return *this; -} -inline HexDump& HexDump::setAlignment(size_t alignment) { - mAlignment = alignment; return *this; -} -inline HexDump& HexDump::setCArrayStyle(bool enabled) { - mCArrayStyle = enabled; return *this; -} - -inline const void* HexDump::buffer() const { return mBuffer; } -inline size_t HexDump::size() const { return mSize; } -inline size_t HexDump::bytesPerLine() const { return mBytesPerLine; } -inline int32_t HexDump::singleLineCutoff() const { return mSingleLineCutoff; } -inline size_t HexDump::alignment() const { return mAlignment; } -inline bool HexDump::carrayStyle() const { return mCArrayStyle; } - -// --------------------------------------------------------------------------- -}; // namespace android - -#endif // ANDROID_TEXTOUTPUT_H diff --git a/include/utils/TimeUtils.h b/include/utils/TimeUtils.h deleted file mode 100644 index b19e02126..000000000 --- a/include/utils/TimeUtils.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (C) 2005 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_TIME_H -#define ANDROID_TIME_H - -#include -#include -#include -#include -#include -#include -#include - -namespace android { - -/* - * This class is the core implementation of the android.util.Time java - * class. It doesn't implement some of the methods that are implemented - * in Java. They could be done here, but it's not expected that this class - * will be used. If that assumption is incorrect, feel free to update this - * file. The reason to do it here is to not mix the implementation of this - * class and the jni glue code. - */ -class Time -{ -public: - struct tm t; - - // this object doesn't own this string - const char *timezone; - - enum { - SEC = 1, - MIN = 2, - HOUR = 3, - MDAY = 4, - MON = 5, - YEAR = 6, - WDAY = 7, - YDAY = 8 - }; - - static int compare(Time& a, Time& b); - - Time(); - - void switchTimezone(const char *timezone); - String8 format(const char *format, const struct strftime_locale *locale) const; - void format2445(short* buf, bool hasTime) const; - String8 toString() const; - void setToNow(); - int64_t toMillis(bool ignoreDst); - void set(int64_t millis); - - inline void set(int sec, int min, int hour, int mday, int mon, int year, - int isdst) - { - this->t.tm_sec = sec; - this->t.tm_min = min; - this->t.tm_hour = hour; - this->t.tm_mday = mday; - this->t.tm_mon = mon; - this->t.tm_year = year; - this->t.tm_isdst = isdst; -#ifdef HAVE_TM_GMTOFF - this->t.tm_gmtoff = 0; -#endif - this->t.tm_wday = 0; - this->t.tm_yday = 0; - } -}; - -}; // namespace android - -#endif // ANDROID_TIME_H diff --git a/include/utils/TimerProbe.h b/include/utils/TimerProbe.h deleted file mode 100644 index f2e32b21a..000000000 --- a/include/utils/TimerProbe.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * 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. - */ - -#ifndef ANDROID_TIMER_PROBE_H -#define ANDROID_TIMER_PROBE_H - -#if 0 && defined(HAVE_POSIX_CLOCKS) -#define ENABLE_TIMER_PROBE 1 -#else -#define ENABLE_TIMER_PROBE 0 -#endif - -#if ENABLE_TIMER_PROBE - -#include -#include -#include - -#define TIMER_PROBE(tag) \ - static int _timer_slot_; \ - android::TimerProbe probe(tag, &_timer_slot_) -#define TIMER_PROBE_END() probe.end() -#else -#define TIMER_PROBE(tag) -#define TIMER_PROBE_END() -#endif - -#if ENABLE_TIMER_PROBE -namespace android { - -class TimerProbe { -public: - TimerProbe(const char tag[], int* slot); - void end(); - ~TimerProbe(); -private: - struct Bucket { - int mStart, mReal, mProcess, mThread, mCount; - const char* mTag; - int* mSlotPtr; - int mIndent; - }; - static Vector gBuckets; - static TimerProbe* gExecuteChain; - static int gIndent; - static timespec gRealBase; - TimerProbe* mNext; - static uint32_t ElapsedTime(const timespec& start, const timespec& end); - void print(const timespec& r, const timespec& p, const timespec& t) const; - timespec mRealStart, mPStart, mTStart; - const char* mTag; - int mIndent; - int mBucket; -}; - -}; // namespace android - -#endif -#endif diff --git a/include/utils/Timers.h b/include/utils/Timers.h deleted file mode 100644 index 96103995b..000000000 --- a/include/utils/Timers.h +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright (C) 2005 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. - */ - -// -// Timer functions. -// -#ifndef _LIBS_UTILS_TIMERS_H -#define _LIBS_UTILS_TIMERS_H - -#include -#include -#include - -// ------------------------------------------------------------------ -// C API - -#ifdef __cplusplus -extern "C" { -#endif - -typedef int64_t nsecs_t; // nano-seconds - -static inline nsecs_t seconds_to_nanoseconds(nsecs_t secs) -{ - return secs*1000000000; -} - -static inline nsecs_t milliseconds_to_nanoseconds(nsecs_t secs) -{ - return secs*1000000; -} - -static inline nsecs_t microseconds_to_nanoseconds(nsecs_t secs) -{ - return secs*1000; -} - -static inline nsecs_t nanoseconds_to_seconds(nsecs_t secs) -{ - return secs/1000000000; -} - -static inline nsecs_t nanoseconds_to_milliseconds(nsecs_t secs) -{ - return secs/1000000; -} - -static inline nsecs_t nanoseconds_to_microseconds(nsecs_t secs) -{ - return secs/1000; -} - -static inline nsecs_t s2ns(nsecs_t v) {return seconds_to_nanoseconds(v);} -static inline nsecs_t ms2ns(nsecs_t v) {return milliseconds_to_nanoseconds(v);} -static inline nsecs_t us2ns(nsecs_t v) {return microseconds_to_nanoseconds(v);} -static inline nsecs_t ns2s(nsecs_t v) {return nanoseconds_to_seconds(v);} -static inline nsecs_t ns2ms(nsecs_t v) {return nanoseconds_to_milliseconds(v);} -static inline nsecs_t ns2us(nsecs_t v) {return nanoseconds_to_microseconds(v);} - -static inline nsecs_t seconds(nsecs_t v) { return s2ns(v); } -static inline nsecs_t milliseconds(nsecs_t v) { return ms2ns(v); } -static inline nsecs_t microseconds(nsecs_t v) { return us2ns(v); } - -enum { - SYSTEM_TIME_REALTIME = 0, // system-wide realtime clock - SYSTEM_TIME_MONOTONIC = 1, // monotonic time since unspecified starting point - SYSTEM_TIME_PROCESS = 2, // high-resolution per-process clock - SYSTEM_TIME_THREAD = 3 // high-resolution per-thread clock -}; - -// return the system-time according to the specified clock -#ifdef __cplusplus -nsecs_t systemTime(int clock = SYSTEM_TIME_MONOTONIC); -#else -nsecs_t systemTime(int clock); -#endif // def __cplusplus - -// return the system-time according to the specified clock -int sleepForInterval(long interval, struct timeval* pNextTick); - -#ifdef __cplusplus -} // extern "C" -#endif - -// ------------------------------------------------------------------ -// C++ API - -#ifdef __cplusplus - -namespace android { -/* - * Time the duration of something. - * - * Includes some timeval manipulation functions. - */ -class DurationTimer { -public: - DurationTimer(void) {} - ~DurationTimer(void) {} - - // Start the timer. - void start(void); - // Stop the timer. - void stop(void); - // Get the duration in microseconds. - long long durationUsecs(void) const; - - // Subtract two timevals. Returns the difference (ptv1-ptv2) in - // microseconds. - static long long subtractTimevals(const struct timeval* ptv1, - const struct timeval* ptv2); - - // Add the specified amount of time to the timeval. - static void addToTimeval(struct timeval* ptv, long usec); - -private: - struct timeval mStartWhen; - struct timeval mStopWhen; -}; - -}; // android -#endif // def __cplusplus - -#endif // _LIBS_UTILS_TIMERS_H diff --git a/include/utils/TypeHelpers.h b/include/utils/TypeHelpers.h deleted file mode 100644 index c04c37fa9..000000000 --- a/include/utils/TypeHelpers.h +++ /dev/null @@ -1,254 +0,0 @@ -/* - * Copyright (C) 2005 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_TYPE_HELPERS_H -#define ANDROID_TYPE_HELPERS_H - -#include -#include -#include -#include - -// --------------------------------------------------------------------------- - -namespace android { - -/* - * Types traits - */ - -template struct trait_trivial_ctor { enum { value = false }; }; -template struct trait_trivial_dtor { enum { value = false }; }; -template struct trait_trivial_copy { enum { value = false }; }; -template struct trait_trivial_assign{ enum { value = false }; }; - -template struct trait_pointer { enum { value = false }; }; -template struct trait_pointer { enum { value = true }; }; - -#define ANDROID_BASIC_TYPES_TRAITS( T ) \ - template<> struct trait_trivial_ctor< T > { enum { value = true }; }; \ - template<> struct trait_trivial_dtor< T > { enum { value = true }; }; \ - template<> struct trait_trivial_copy< T > { enum { value = true }; }; \ - template<> struct trait_trivial_assign< T >{ enum { value = true }; }; - -#define ANDROID_TYPE_TRAITS( T, ctor, dtor, copy, assign ) \ - template<> struct trait_trivial_ctor< T > { enum { value = ctor }; }; \ - template<> struct trait_trivial_dtor< T > { enum { value = dtor }; }; \ - template<> struct trait_trivial_copy< T > { enum { value = copy }; }; \ - template<> struct trait_trivial_assign< T >{ enum { value = assign }; }; - -template -struct traits { - enum { - is_pointer = trait_pointer::value, - has_trivial_ctor = is_pointer || trait_trivial_ctor::value, - has_trivial_dtor = is_pointer || trait_trivial_dtor::value, - has_trivial_copy = is_pointer || trait_trivial_copy::value, - has_trivial_assign = is_pointer || trait_trivial_assign::value - }; -}; - -template -struct aggregate_traits { - enum { - is_pointer = false, - has_trivial_ctor = traits::has_trivial_ctor && traits::has_trivial_ctor, - has_trivial_dtor = traits::has_trivial_dtor && traits::has_trivial_dtor, - has_trivial_copy = traits::has_trivial_copy && traits::has_trivial_copy, - has_trivial_assign = traits::has_trivial_assign && traits::has_trivial_assign - }; -}; - -// --------------------------------------------------------------------------- - -/* - * basic types traits - */ - -ANDROID_BASIC_TYPES_TRAITS( void ); -ANDROID_BASIC_TYPES_TRAITS( bool ); -ANDROID_BASIC_TYPES_TRAITS( char ); -ANDROID_BASIC_TYPES_TRAITS( unsigned char ); -ANDROID_BASIC_TYPES_TRAITS( short ); -ANDROID_BASIC_TYPES_TRAITS( unsigned short ); -ANDROID_BASIC_TYPES_TRAITS( int ); -ANDROID_BASIC_TYPES_TRAITS( unsigned int ); -ANDROID_BASIC_TYPES_TRAITS( long ); -ANDROID_BASIC_TYPES_TRAITS( unsigned long ); -ANDROID_BASIC_TYPES_TRAITS( long long ); -ANDROID_BASIC_TYPES_TRAITS( unsigned long long ); -ANDROID_BASIC_TYPES_TRAITS( float ); -ANDROID_BASIC_TYPES_TRAITS( double ); - -// --------------------------------------------------------------------------- - - -/* - * compare and order types - */ - -template inline -int strictly_order_type(const TYPE& lhs, const TYPE& rhs) { - return (lhs < rhs) ? 1 : 0; -} - -template inline -int compare_type(const TYPE& lhs, const TYPE& rhs) { - return strictly_order_type(rhs, lhs) - strictly_order_type(lhs, rhs); -} - -/* - * create, destroy, copy and assign types... - */ - -template inline -void construct_type(TYPE* p, size_t n) { - if (!traits::has_trivial_ctor) { - while (n--) { - new(p++) TYPE; - } - } -} - -template inline -void destroy_type(TYPE* p, size_t n) { - if (!traits::has_trivial_dtor) { - while (n--) { - p->~TYPE(); - p++; - } - } -} - -template inline -void copy_type(TYPE* d, const TYPE* s, size_t n) { - if (!traits::has_trivial_copy) { - while (n--) { - new(d) TYPE(*s); - d++, s++; - } - } else { - memcpy(d,s,n*sizeof(TYPE)); - } -} - -template inline -void assign_type(TYPE* d, const TYPE* s, size_t n) { - if (!traits::has_trivial_assign) { - while (n--) { - *d++ = *s++; - } - } else { - memcpy(d,s,n*sizeof(TYPE)); - } -} - -template inline -void splat_type(TYPE* where, const TYPE* what, size_t n) { - if (!traits::has_trivial_copy) { - while (n--) { - new(where) TYPE(*what); - where++; - } - } else { - while (n--) { - *where++ = *what; - } - } -} - -template inline -void move_forward_type(TYPE* d, const TYPE* s, size_t n = 1) { - if (!traits::has_trivial_copy || !traits::has_trivial_dtor) { - d += n; - s += n; - while (n--) { - --d, --s; - if (!traits::has_trivial_copy) { - new(d) TYPE(*s); - } else { - *d = *s; - } - if (!traits::has_trivial_dtor) { - s->~TYPE(); - } - } - } else { - memmove(d,s,n*sizeof(TYPE)); - } -} - -template inline -void move_backward_type(TYPE* d, const TYPE* s, size_t n = 1) { - if (!traits::has_trivial_copy || !traits::has_trivial_dtor) { - while (n--) { - if (!traits::has_trivial_copy) { - new(d) TYPE(*s); - } else { - *d = *s; - } - if (!traits::has_trivial_dtor) { - s->~TYPE(); - } - d++, s++; - } - } else { - memmove(d,s,n*sizeof(TYPE)); - } -} -// --------------------------------------------------------------------------- - -/* - * a key/value pair - */ - -template -struct key_value_pair_t { - KEY key; - VALUE value; - key_value_pair_t() { } - key_value_pair_t(const key_value_pair_t& o) : key(o.key), value(o.value) { } - key_value_pair_t(const KEY& k, const VALUE& v) : key(k), value(v) { } - key_value_pair_t(const KEY& k) : key(k) { } - inline bool operator < (const key_value_pair_t& o) const { - return strictly_order_type(key, o.key); - } -}; - -template<> -template -struct trait_trivial_ctor< key_value_pair_t > -{ enum { value = aggregate_traits::has_trivial_ctor }; }; -template<> -template -struct trait_trivial_dtor< key_value_pair_t > -{ enum { value = aggregate_traits::has_trivial_dtor }; }; -template<> -template -struct trait_trivial_copy< key_value_pair_t > -{ enum { value = aggregate_traits::has_trivial_copy }; }; -template<> -template -struct trait_trivial_assign< key_value_pair_t > -{ enum { value = aggregate_traits::has_trivial_assign};}; - -// --------------------------------------------------------------------------- - -}; // namespace android - -// --------------------------------------------------------------------------- - -#endif // ANDROID_TYPE_HELPERS_H diff --git a/include/utils/Vector.h b/include/utils/Vector.h deleted file mode 100644 index be365d83e..000000000 --- a/include/utils/Vector.h +++ /dev/null @@ -1,359 +0,0 @@ -/* - * Copyright (C) 2005 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_VECTOR_H -#define ANDROID_VECTOR_H - -#include -#include -#include - -#include -#include -#include - -// --------------------------------------------------------------------------- - -namespace android { - -/*! - * The main templated vector class ensuring type safety - * while making use of VectorImpl. - * This is the class users want to use. - */ - -template -class Vector : private VectorImpl -{ -public: - typedef TYPE value_type; - - /*! - * Constructors and destructors - */ - - Vector(); - Vector(const Vector& rhs); - virtual ~Vector(); - - /*! copy operator */ - const Vector& operator = (const Vector& rhs) const; - Vector& operator = (const Vector& rhs); - - /* - * empty the vector - */ - - inline void clear() { VectorImpl::clear(); } - - /*! - * vector stats - */ - - //! returns number of items in the vector - inline size_t size() const { return VectorImpl::size(); } - //! returns wether or not the vector is empty - inline bool isEmpty() const { return VectorImpl::isEmpty(); } - //! returns how many items can be stored without reallocating the backing store - inline size_t capacity() const { return VectorImpl::capacity(); } - //! setst the capacity. capacity can never be reduced less than size() - inline ssize_t setCapacity(size_t size) { return VectorImpl::setCapacity(size); } - - /*! - * C-style array access - */ - - //! read-only C-style access - inline const TYPE* array() const; - //! read-write C-style access - TYPE* editArray(); - - /*! - * accessors - */ - - //! read-only access to an item at a given index - inline const TYPE& operator [] (size_t index) const; - //! alternate name for operator [] - inline const TYPE& itemAt(size_t index) const; - //! stack-usage of the vector. returns the top of the stack (last element) - const TYPE& top() const; - //! same as operator [], but allows to access the vector backward (from the end) with a negative index - const TYPE& mirrorItemAt(ssize_t index) const; - - /*! - * modifing the array - */ - - //! copy-on write support, grants write access to an item - TYPE& editItemAt(size_t index); - //! grants right acces to the top of the stack (last element) - TYPE& editTop(); - - /*! - * append/insert another vector - */ - - //! insert another vector at a given index - ssize_t insertVectorAt(const Vector& vector, size_t index); - - //! append another vector at the end of this one - ssize_t appendVector(const Vector& vector); - - - /*! - * add/insert/replace items - */ - - //! insert one or several items initialized with their default constructor - inline ssize_t insertAt(size_t index, size_t numItems = 1); - //! insert on onr several items initialized from a prototype item - ssize_t insertAt(const TYPE& prototype_item, size_t index, size_t numItems = 1); - //! pop the top of the stack (removes the last element). No-op if the stack's empty - inline void pop(); - //! pushes an item initialized with its default constructor - inline void push(); - //! pushes an item on the top of the stack - void push(const TYPE& item); - //! same as push() but returns the index the item was added at (or an error) - inline ssize_t add(); - //! same as push() but returns the index the item was added at (or an error) - ssize_t add(const TYPE& item); - //! replace an item with a new one initialized with its default constructor - inline ssize_t replaceAt(size_t index); - //! replace an item with a new one - ssize_t replaceAt(const TYPE& item, size_t index); - - /*! - * remove items - */ - - //! remove several items - inline ssize_t removeItemsAt(size_t index, size_t count = 1); - //! remove one item - inline ssize_t removeAt(size_t index) { return removeItemsAt(index); } - - /*! - * sort (stable) the array - */ - - typedef int (*compar_t)(const TYPE* lhs, const TYPE* rhs); - typedef int (*compar_r_t)(const TYPE* lhs, const TYPE* rhs, void* state); - - inline status_t sort(compar_t cmp); - inline status_t sort(compar_r_t cmp, void* state); - -protected: - virtual void do_construct(void* storage, size_t num) const; - virtual void do_destroy(void* storage, size_t num) const; - virtual void do_copy(void* dest, const void* from, size_t num) const; - virtual void do_splat(void* dest, const void* item, size_t num) const; - virtual void do_move_forward(void* dest, const void* from, size_t num) const; - virtual void do_move_backward(void* dest, const void* from, size_t num) const; -}; - - -// --------------------------------------------------------------------------- -// No user serviceable parts from here... -// --------------------------------------------------------------------------- - -template inline -Vector::Vector() - : VectorImpl(sizeof(TYPE), - ((traits::has_trivial_ctor ? HAS_TRIVIAL_CTOR : 0) - |(traits::has_trivial_dtor ? HAS_TRIVIAL_DTOR : 0) - |(traits::has_trivial_copy ? HAS_TRIVIAL_COPY : 0) - |(traits::has_trivial_assign ? HAS_TRIVIAL_ASSIGN : 0)) - ) -{ -} - -template inline -Vector::Vector(const Vector& rhs) - : VectorImpl(rhs) { -} - -template inline -Vector::~Vector() { - finish_vector(); -} - -template inline -Vector& Vector::operator = (const Vector& rhs) { - VectorImpl::operator = (rhs); - return *this; -} - -template inline -const Vector& Vector::operator = (const Vector& rhs) const { - VectorImpl::operator = (rhs); - return *this; -} - -template inline -const TYPE* Vector::array() const { - return static_cast(arrayImpl()); -} - -template inline -TYPE* Vector::editArray() { - return static_cast(editArrayImpl()); -} - - -template inline -const TYPE& Vector::operator[](size_t index) const { - LOG_FATAL_IF( index>=size(), - "itemAt: index %d is past size %d", (int)index, (int)size() ); - return *(array() + index); -} - -template inline -const TYPE& Vector::itemAt(size_t index) const { - return operator[](index); -} - -template inline -const TYPE& Vector::mirrorItemAt(ssize_t index) const { - LOG_FATAL_IF( (index>0 ? index : -index)>=size(), - "mirrorItemAt: index %d is past size %d", - (int)index, (int)size() ); - return *(array() + ((index<0) ? (size()-index) : index)); -} - -template inline -const TYPE& Vector::top() const { - return *(array() + size() - 1); -} - -template inline -TYPE& Vector::editItemAt(size_t index) { - return *( static_cast(editItemLocation(index)) ); -} - -template inline -TYPE& Vector::editTop() { - return *( static_cast(editItemLocation(size()-1)) ); -} - -template inline -ssize_t Vector::insertVectorAt(const Vector& vector, size_t index) { - return VectorImpl::insertVectorAt(reinterpret_cast(vector), index); -} - -template inline -ssize_t Vector::appendVector(const Vector& vector) { - return VectorImpl::appendVector(reinterpret_cast(vector)); -} - -template inline -ssize_t Vector::insertAt(const TYPE& item, size_t index, size_t numItems) { - return VectorImpl::insertAt(&item, index, numItems); -} - -template inline -void Vector::push(const TYPE& item) { - return VectorImpl::push(&item); -} - -template inline -ssize_t Vector::add(const TYPE& item) { - return VectorImpl::add(&item); -} - -template inline -ssize_t Vector::replaceAt(const TYPE& item, size_t index) { - return VectorImpl::replaceAt(&item, index); -} - -template inline -ssize_t Vector::insertAt(size_t index, size_t numItems) { - return VectorImpl::insertAt(index, numItems); -} - -template inline -void Vector::pop() { - VectorImpl::pop(); -} - -template inline -void Vector::push() { - VectorImpl::push(); -} - -template inline -ssize_t Vector::add() { - return VectorImpl::add(); -} - -template inline -ssize_t Vector::replaceAt(size_t index) { - return VectorImpl::replaceAt(index); -} - -template inline -ssize_t Vector::removeItemsAt(size_t index, size_t count) { - return VectorImpl::removeItemsAt(index, count); -} - -template inline -status_t Vector::sort(Vector::compar_t cmp) { - return VectorImpl::sort((VectorImpl::compar_t)cmp); -} - -template inline -status_t Vector::sort(Vector::compar_r_t cmp, void* state) { - return VectorImpl::sort((VectorImpl::compar_r_t)cmp, state); -} - -// --------------------------------------------------------------------------- - -template -void Vector::do_construct(void* storage, size_t num) const { - construct_type( reinterpret_cast(storage), num ); -} - -template -void Vector::do_destroy(void* storage, size_t num) const { - destroy_type( reinterpret_cast(storage), num ); -} - -template -void Vector::do_copy(void* dest, const void* from, size_t num) const { - copy_type( reinterpret_cast(dest), reinterpret_cast(from), num ); -} - -template -void Vector::do_splat(void* dest, const void* item, size_t num) const { - splat_type( reinterpret_cast(dest), reinterpret_cast(item), num ); -} - -template -void Vector::do_move_forward(void* dest, const void* from, size_t num) const { - move_forward_type( reinterpret_cast(dest), reinterpret_cast(from), num ); -} - -template -void Vector::do_move_backward(void* dest, const void* from, size_t num) const { - move_backward_type( reinterpret_cast(dest), reinterpret_cast(from), num ); -} - -}; // namespace android - - -// --------------------------------------------------------------------------- - -#endif // ANDROID_VECTOR_H diff --git a/include/utils/VectorImpl.h b/include/utils/VectorImpl.h deleted file mode 100644 index 2525229be..000000000 --- a/include/utils/VectorImpl.h +++ /dev/null @@ -1,199 +0,0 @@ -/* - * Copyright (C) 2005 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_VECTOR_IMPL_H -#define ANDROID_VECTOR_IMPL_H - -#include -#include -#include -#include - -// --------------------------------------------------------------------------- -// No user serviceable parts in here... -// --------------------------------------------------------------------------- - -namespace android { - -/*! - * Implementation of the guts of the vector<> class - * this ensures backward binary compatibility and - * reduces code size. - * For performance reasons, we expose mStorage and mCount - * so these fields are set in stone. - * - */ - -class VectorImpl -{ -public: - enum { // flags passed to the ctor - HAS_TRIVIAL_CTOR = 0x00000001, - HAS_TRIVIAL_DTOR = 0x00000002, - HAS_TRIVIAL_COPY = 0x00000004, - HAS_TRIVIAL_ASSIGN = 0x00000008 - }; - - VectorImpl(size_t itemSize, uint32_t flags); - VectorImpl(const VectorImpl& rhs); - virtual ~VectorImpl(); - - /*! must be called from subclasses destructor */ - void finish_vector(); - - VectorImpl& operator = (const VectorImpl& rhs); - - /*! C-style array access */ - inline const void* arrayImpl() const { return mStorage; } - void* editArrayImpl(); - - /*! vector stats */ - inline size_t size() const { return mCount; } - inline bool isEmpty() const { return mCount == 0; } - size_t capacity() const; - ssize_t setCapacity(size_t size); - - /*! append/insert another vector */ - ssize_t insertVectorAt(const VectorImpl& vector, size_t index); - ssize_t appendVector(const VectorImpl& vector); - - /*! add/insert/replace items */ - ssize_t insertAt(size_t where, size_t numItems = 1); - ssize_t insertAt(const void* item, size_t where, size_t numItems = 1); - void pop(); - void push(); - void push(const void* item); - ssize_t add(); - ssize_t add(const void* item); - ssize_t replaceAt(size_t index); - ssize_t replaceAt(const void* item, size_t index); - - /*! remove items */ - ssize_t removeItemsAt(size_t index, size_t count = 1); - void clear(); - - const void* itemLocation(size_t index) const; - void* editItemLocation(size_t index); - - typedef int (*compar_t)(const void* lhs, const void* rhs); - typedef int (*compar_r_t)(const void* lhs, const void* rhs, void* state); - status_t sort(compar_t cmp); - status_t sort(compar_r_t cmp, void* state); - -protected: - size_t itemSize() const; - void release_storage(); - - virtual void do_construct(void* storage, size_t num) const = 0; - virtual void do_destroy(void* storage, size_t num) const = 0; - virtual void do_copy(void* dest, const void* from, size_t num) const = 0; - virtual void do_splat(void* dest, const void* item, size_t num) const = 0; - virtual void do_move_forward(void* dest, const void* from, size_t num) const = 0; - virtual void do_move_backward(void* dest, const void* from, size_t num) const = 0; - - // take care of FBC... - virtual void reservedVectorImpl1(); - virtual void reservedVectorImpl2(); - virtual void reservedVectorImpl3(); - virtual void reservedVectorImpl4(); - virtual void reservedVectorImpl5(); - virtual void reservedVectorImpl6(); - virtual void reservedVectorImpl7(); - virtual void reservedVectorImpl8(); - -private: - void* _grow(size_t where, size_t amount); - void _shrink(size_t where, size_t amount); - - inline void _do_construct(void* storage, size_t num) const; - inline void _do_destroy(void* storage, size_t num) const; - inline void _do_copy(void* dest, const void* from, size_t num) const; - inline void _do_splat(void* dest, const void* item, size_t num) const; - inline void _do_move_forward(void* dest, const void* from, size_t num) const; - inline void _do_move_backward(void* dest, const void* from, size_t num) const; - - // These 2 fields are exposed in the inlines below, - // so they're set in stone. - void * mStorage; // base address of the vector - size_t mCount; // number of items - - const uint32_t mFlags; - const size_t mItemSize; -}; - - - -class SortedVectorImpl : public VectorImpl -{ -public: - SortedVectorImpl(size_t itemSize, uint32_t flags); - SortedVectorImpl(const VectorImpl& rhs); - virtual ~SortedVectorImpl(); - - SortedVectorImpl& operator = (const SortedVectorImpl& rhs); - - //! finds the index of an item - ssize_t indexOf(const void* item) const; - - //! finds where this item should be inserted - size_t orderOf(const void* item) const; - - //! add an item in the right place (or replaces it if there is one) - ssize_t add(const void* item); - - //! merges a vector into this one - ssize_t merge(const VectorImpl& vector); - ssize_t merge(const SortedVectorImpl& vector); - - //! removes an item - ssize_t remove(const void* item); - -protected: - virtual int do_compare(const void* lhs, const void* rhs) const = 0; - - // take care of FBC... - virtual void reservedSortedVectorImpl1(); - virtual void reservedSortedVectorImpl2(); - virtual void reservedSortedVectorImpl3(); - virtual void reservedSortedVectorImpl4(); - virtual void reservedSortedVectorImpl5(); - virtual void reservedSortedVectorImpl6(); - virtual void reservedSortedVectorImpl7(); - virtual void reservedSortedVectorImpl8(); - -private: - ssize_t _indexOrderOf(const void* item, size_t* order = 0) const; - - // these are made private, because they can't be used on a SortedVector - // (they don't have an implementation either) - ssize_t add(); - void pop(); - void push(); - void push(const void* item); - ssize_t insertVectorAt(const VectorImpl& vector, size_t index); - ssize_t appendVector(const VectorImpl& vector); - ssize_t insertAt(size_t where, size_t numItems = 1); - ssize_t insertAt(const void* item, size_t where, size_t numItems = 1); - ssize_t replaceAt(size_t index); - ssize_t replaceAt(const void* item, size_t index); -}; - -}; // namespace android - - -// --------------------------------------------------------------------------- - -#endif // ANDROID_VECTOR_IMPL_H diff --git a/include/utils/ZipEntry.h b/include/utils/ZipEntry.h deleted file mode 100644 index e4698dfbb..000000000 --- a/include/utils/ZipEntry.h +++ /dev/null @@ -1,345 +0,0 @@ -/* - * Copyright (C) 2006 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. - */ - -// -// Zip archive entries. -// -// The ZipEntry class is tightly meshed with the ZipFile class. -// -#ifndef __LIBS_ZIPENTRY_H -#define __LIBS_ZIPENTRY_H - -#include "Errors.h" - -#include -#include - -namespace android { - -class ZipFile; - -/* - * ZipEntry objects represent a single entry in a Zip archive. - * - * You can use one of these to get or set information about an entry, but - * there are no functions here for accessing the data itself. (We could - * tuck a pointer to the ZipFile in here for convenience, but that raises - * the likelihood of using ZipEntry objects after discarding the ZipFile.) - * - * File information is stored in two places: next to the file data (the Local - * File Header, and possibly a Data Descriptor), and at the end of the file - * (the Central Directory Entry). The two must be kept in sync. - */ -class ZipEntry { -public: - friend class ZipFile; - - ZipEntry(void) - : mDeleted(false), mMarked(false) - {} - ~ZipEntry(void) {} - - /* - * Returns "true" if the data is compressed. - */ - bool isCompressed(void) const { - return mCDE.mCompressionMethod != kCompressStored; - } - int getCompressionMethod(void) const { return mCDE.mCompressionMethod; } - - /* - * Return the uncompressed length. - */ - off_t getUncompressedLen(void) const { return mCDE.mUncompressedSize; } - - /* - * Return the compressed length. For uncompressed data, this returns - * the same thing as getUncompresesdLen(). - */ - off_t getCompressedLen(void) const { return mCDE.mCompressedSize; } - - /* - * Return the absolute file offset of the start of the compressed or - * uncompressed data. - */ - off_t getFileOffset(void) const { - return mCDE.mLocalHeaderRelOffset + - LocalFileHeader::kLFHLen + - mLFH.mFileNameLength + - mLFH.mExtraFieldLength; - } - - /* - * Return the data CRC. - */ - unsigned long getCRC32(void) const { return mCDE.mCRC32; } - - /* - * Return file modification time in UNIX seconds-since-epoch. - */ - time_t getModWhen(void) const; - - /* - * Return the archived file name. - */ - const char* getFileName(void) const { return (const char*) mCDE.mFileName; } - - /* - * Application-defined "mark". Can be useful when synchronizing the - * contents of an archive with contents on disk. - */ - bool getMarked(void) const { return mMarked; } - void setMarked(bool val) { mMarked = val; } - - /* - * Some basic functions for raw data manipulation. "LE" means - * Little Endian. - */ - static inline unsigned short getShortLE(const unsigned char* buf) { - return buf[0] | (buf[1] << 8); - } - static inline unsigned long getLongLE(const unsigned char* buf) { - return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); - } - static inline void putShortLE(unsigned char* buf, short val) { - buf[0] = (unsigned char) val; - buf[1] = (unsigned char) (val >> 8); - } - static inline void putLongLE(unsigned char* buf, long val) { - buf[0] = (unsigned char) val; - buf[1] = (unsigned char) (val >> 8); - buf[2] = (unsigned char) (val >> 16); - buf[3] = (unsigned char) (val >> 24); - } - - /* defined for Zip archives */ - enum { - kCompressStored = 0, // no compression - // shrunk = 1, - // reduced 1 = 2, - // reduced 2 = 3, - // reduced 3 = 4, - // reduced 4 = 5, - // imploded = 6, - // tokenized = 7, - kCompressDeflated = 8, // standard deflate - // Deflate64 = 9, - // lib imploded = 10, - // reserved = 11, - // bzip2 = 12, - }; - - /* - * Deletion flag. If set, the entry will be removed on the next - * call to "flush". - */ - bool getDeleted(void) const { return mDeleted; } - -protected: - /* - * Initialize the structure from the file, which is pointing at - * our Central Directory entry. - */ - status_t initFromCDE(FILE* fp); - - /* - * Initialize the structure for a new file. We need the filename - * and comment so that we can properly size the LFH area. The - * filename is mandatory, the comment is optional. - */ - void initNew(const char* fileName, const char* comment); - - /* - * Initialize the structure with the contents of a ZipEntry from - * another file. - */ - status_t initFromExternal(const ZipFile* pZipFile, const ZipEntry* pEntry); - - /* - * Add some pad bytes to the LFH. We do this by adding or resizing - * the "extra" field. - */ - status_t addPadding(int padding); - - /* - * Set information about the data for this entry. - */ - void setDataInfo(long uncompLen, long compLen, unsigned long crc32, - int compressionMethod); - - /* - * Set the modification date. - */ - void setModWhen(time_t when); - - /* - * Return the offset of the local file header. - */ - off_t getLFHOffset(void) const { return mCDE.mLocalHeaderRelOffset; } - - /* - * Set the offset of the local file header, relative to the start of - * the current file. - */ - void setLFHOffset(off_t offset) { - mCDE.mLocalHeaderRelOffset = (long) offset; - } - - /* mark for deletion; used by ZipFile::remove() */ - void setDeleted(void) { mDeleted = true; } - -private: - /* these are private and not defined */ - ZipEntry(const ZipEntry& src); - ZipEntry& operator=(const ZipEntry& src); - - /* returns "true" if the CDE and the LFH agree */ - bool compareHeaders(void) const; - void copyCDEtoLFH(void); - - bool mDeleted; // set if entry is pending deletion - bool mMarked; // app-defined marker - - /* - * Every entry in the Zip archive starts off with one of these. - */ - class LocalFileHeader { - public: - LocalFileHeader(void) : - mVersionToExtract(0), - mGPBitFlag(0), - mCompressionMethod(0), - mLastModFileTime(0), - mLastModFileDate(0), - mCRC32(0), - mCompressedSize(0), - mUncompressedSize(0), - mFileNameLength(0), - mExtraFieldLength(0), - mFileName(NULL), - mExtraField(NULL) - {} - virtual ~LocalFileHeader(void) { - delete[] mFileName; - delete[] mExtraField; - } - - status_t read(FILE* fp); - status_t write(FILE* fp); - - // unsigned long mSignature; - unsigned short mVersionToExtract; - unsigned short mGPBitFlag; - unsigned short mCompressionMethod; - unsigned short mLastModFileTime; - unsigned short mLastModFileDate; - unsigned long mCRC32; - unsigned long mCompressedSize; - unsigned long mUncompressedSize; - unsigned short mFileNameLength; - unsigned short mExtraFieldLength; - unsigned char* mFileName; - unsigned char* mExtraField; - - enum { - kSignature = 0x04034b50, - kLFHLen = 30, // LocalFileHdr len, excl. var fields - }; - - void dump(void) const; - }; - - /* - * Every entry in the Zip archive has one of these in the "central - * directory" at the end of the file. - */ - class CentralDirEntry { - public: - CentralDirEntry(void) : - mVersionMadeBy(0), - mVersionToExtract(0), - mGPBitFlag(0), - mCompressionMethod(0), - mLastModFileTime(0), - mLastModFileDate(0), - mCRC32(0), - mCompressedSize(0), - mUncompressedSize(0), - mFileNameLength(0), - mExtraFieldLength(0), - mFileCommentLength(0), - mDiskNumberStart(0), - mInternalAttrs(0), - mExternalAttrs(0), - mLocalHeaderRelOffset(0), - mFileName(NULL), - mExtraField(NULL), - mFileComment(NULL) - {} - virtual ~CentralDirEntry(void) { - delete[] mFileName; - delete[] mExtraField; - delete[] mFileComment; - } - - status_t read(FILE* fp); - status_t write(FILE* fp); - - // unsigned long mSignature; - unsigned short mVersionMadeBy; - unsigned short mVersionToExtract; - unsigned short mGPBitFlag; - unsigned short mCompressionMethod; - unsigned short mLastModFileTime; - unsigned short mLastModFileDate; - unsigned long mCRC32; - unsigned long mCompressedSize; - unsigned long mUncompressedSize; - unsigned short mFileNameLength; - unsigned short mExtraFieldLength; - unsigned short mFileCommentLength; - unsigned short mDiskNumberStart; - unsigned short mInternalAttrs; - unsigned long mExternalAttrs; - unsigned long mLocalHeaderRelOffset; - unsigned char* mFileName; - unsigned char* mExtraField; - unsigned char* mFileComment; - - void dump(void) const; - - enum { - kSignature = 0x02014b50, - kCDELen = 46, // CentralDirEnt len, excl. var fields - }; - }; - - enum { - //kDataDescriptorSignature = 0x08074b50, // currently unused - kDataDescriptorLen = 16, // four 32-bit fields - - kDefaultVersion = 20, // need deflate, nothing much else - kDefaultMadeBy = 0x0317, // 03=UNIX, 17=spec v2.3 - kUsesDataDescr = 0x0008, // GPBitFlag bit 3 - }; - - LocalFileHeader mLFH; - CentralDirEntry mCDE; -}; - -}; // namespace android - -#endif // __LIBS_ZIPENTRY_H diff --git a/include/utils/ZipFile.h b/include/utils/ZipFile.h deleted file mode 100644 index 44df5bbaa..000000000 --- a/include/utils/ZipFile.h +++ /dev/null @@ -1,269 +0,0 @@ -/* - * Copyright (C) 2006 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. - */ - -// -// General-purpose Zip archive access. This class allows both reading and -// writing to Zip archives, including deletion of existing entries. -// -#ifndef __LIBS_ZIPFILE_H -#define __LIBS_ZIPFILE_H - -#include "ZipEntry.h" -#include "Vector.h" -#include "Errors.h" -#include - -namespace android { - -/* - * Manipulate a Zip archive. - * - * Some changes will not be visible in the until until "flush" is called. - * - * The correct way to update a file archive is to make all changes to a - * copy of the archive in a temporary file, and then unlink/rename over - * the original after everything completes. Because we're only interested - * in using this for packaging, we don't worry about such things. Crashing - * after making changes and before flush() completes could leave us with - * an unusable Zip archive. - */ -class ZipFile { -public: - ZipFile(void) - : mZipFp(NULL), mReadOnly(false), mNeedCDRewrite(false) - {} - ~ZipFile(void) { - if (!mReadOnly) - flush(); - if (mZipFp != NULL) - fclose(mZipFp); - discardEntries(); - } - - /* - * Open a new or existing archive. - */ - typedef enum { - kOpenReadOnly = 0x01, - kOpenReadWrite = 0x02, - kOpenCreate = 0x04, // create if it doesn't exist - kOpenTruncate = 0x08, // if it exists, empty it - }; - status_t open(const char* zipFileName, int flags); - - /* - * Add a file to the end of the archive. Specify whether you want the - * library to try to store it compressed. - * - * If "storageName" is specified, the archive will use that instead - * of "fileName". - * - * If there is already an entry with the same name, the call fails. - * Existing entries with the same name must be removed first. - * - * If "ppEntry" is non-NULL, a pointer to the new entry will be returned. - */ - status_t add(const char* fileName, int compressionMethod, - ZipEntry** ppEntry) - { - return add(fileName, fileName, compressionMethod, ppEntry); - } - status_t add(const char* fileName, const char* storageName, - int compressionMethod, ZipEntry** ppEntry) - { - return addCommon(fileName, NULL, 0, storageName, - ZipEntry::kCompressStored, - compressionMethod, ppEntry); - } - - /* - * Add a file that is already compressed with gzip. - * - * If "ppEntry" is non-NULL, a pointer to the new entry will be returned. - */ - status_t addGzip(const char* fileName, const char* storageName, - ZipEntry** ppEntry) - { - return addCommon(fileName, NULL, 0, storageName, - ZipEntry::kCompressDeflated, - ZipEntry::kCompressDeflated, ppEntry); - } - - /* - * Add a file from an in-memory data buffer. - * - * If "ppEntry" is non-NULL, a pointer to the new entry will be returned. - */ - status_t add(const void* data, size_t size, const char* storageName, - int compressionMethod, ZipEntry** ppEntry) - { - return addCommon(NULL, data, size, storageName, - ZipEntry::kCompressStored, - compressionMethod, ppEntry); - } - - /* - * Add an entry by copying it from another zip file. If "padding" is - * nonzero, the specified number of bytes will be added to the "extra" - * field in the header. - * - * If "ppEntry" is non-NULL, a pointer to the new entry will be returned. - */ - status_t add(const ZipFile* pSourceZip, const ZipEntry* pSourceEntry, - int padding, ZipEntry** ppEntry); - - /* - * Mark an entry as having been removed. It is not actually deleted - * from the archive or our internal data structures until flush() is - * called. - */ - status_t remove(ZipEntry* pEntry); - - /* - * Flush changes. If mNeedCDRewrite is set, this writes the central dir. - */ - status_t flush(void); - - /* - * Expand the data into the buffer provided. The buffer must hold - * at least bytes. Variation expands directly - * to a file. - * - * Returns "false" if an error was encountered in the compressed data. - */ - //bool uncompress(const ZipEntry* pEntry, void* buf) const; - //bool uncompress(const ZipEntry* pEntry, FILE* fp) const; - void* uncompress(const ZipEntry* pEntry); - - /* - * Get an entry, by name. Returns NULL if not found. - * - * Does not return entries pending deletion. - */ - ZipEntry* getEntryByName(const char* fileName) const; - - /* - * Get the Nth entry in the archive. - * - * This will return an entry that is pending deletion. - */ - int getNumEntries(void) const { return mEntries.size(); } - ZipEntry* getEntryByIndex(int idx) const; - -private: - /* these are private and not defined */ - ZipFile(const ZipFile& src); - ZipFile& operator=(const ZipFile& src); - - class EndOfCentralDir { - public: - EndOfCentralDir(void) : - mDiskNumber(0), - mDiskWithCentralDir(0), - mNumEntries(0), - mTotalNumEntries(0), - mCentralDirSize(0), - mCentralDirOffset(0), - mCommentLen(0), - mComment(NULL) - {} - virtual ~EndOfCentralDir(void) { - delete[] mComment; - } - - status_t readBuf(const unsigned char* buf, int len); - status_t write(FILE* fp); - - //unsigned long mSignature; - unsigned short mDiskNumber; - unsigned short mDiskWithCentralDir; - unsigned short mNumEntries; - unsigned short mTotalNumEntries; - unsigned long mCentralDirSize; - unsigned long mCentralDirOffset; // offset from first disk - unsigned short mCommentLen; - unsigned char* mComment; - - enum { - kSignature = 0x06054b50, - kEOCDLen = 22, // EndOfCentralDir len, excl. comment - - kMaxCommentLen = 65535, // longest possible in ushort - kMaxEOCDSearch = kMaxCommentLen + EndOfCentralDir::kEOCDLen, - - }; - - void dump(void) const; - }; - - - /* read all entries in the central dir */ - status_t readCentralDir(void); - - /* crunch deleted entries out */ - status_t crunchArchive(void); - - /* clean up mEntries */ - void discardEntries(void); - - /* common handler for all "add" functions */ - status_t addCommon(const char* fileName, const void* data, size_t size, - const char* storageName, int sourceType, int compressionMethod, - ZipEntry** ppEntry); - - /* copy all of "srcFp" into "dstFp" */ - status_t copyFpToFp(FILE* dstFp, FILE* srcFp, unsigned long* pCRC32); - /* copy all of "data" into "dstFp" */ - status_t copyDataToFp(FILE* dstFp, - const void* data, size_t size, unsigned long* pCRC32); - /* copy some of "srcFp" into "dstFp" */ - status_t copyPartialFpToFp(FILE* dstFp, FILE* srcFp, long length, - unsigned long* pCRC32); - /* like memmove(), but on parts of a single file */ - status_t filemove(FILE* fp, off_t dest, off_t src, size_t n); - /* compress all of "srcFp" into "dstFp", using Deflate */ - status_t compressFpToFp(FILE* dstFp, FILE* srcFp, - const void* data, size_t size, unsigned long* pCRC32); - - /* get modification date from a file descriptor */ - time_t getModTime(int fd); - - /* - * We use stdio FILE*, which gives us buffering but makes dealing - * with files >2GB awkward. Until we support Zip64, we're fine. - */ - FILE* mZipFp; // Zip file pointer - - /* one of these per file */ - EndOfCentralDir mEOCD; - - /* did we open this read-only? */ - bool mReadOnly; - - /* set this when we trash the central dir */ - bool mNeedCDRewrite; - - /* - * One ZipEntry per entry in the zip file. I'm using pointers instead - * of objects because it's easier than making operator= work for the - * classes and sub-classes. - */ - Vector mEntries; -}; - -}; // namespace android - -#endif // __LIBS_ZIPFILE_H diff --git a/include/utils/ZipFileCRO.h b/include/utils/ZipFileCRO.h deleted file mode 100644 index 30e00368e..000000000 --- a/include/utils/ZipFileCRO.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// -// C API for ead-only access to Zip archives, with minimal heap allocation. -// -#ifndef __LIBS_ZIPFILECRO_H -#define __LIBS_ZIPFILECRO_H - -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/* - * Trivial typedef to ensure that ZipFileCRO is not treated as a simple integer. - */ -typedef void* ZipFileCRO; - -/* - * Trivial typedef to ensure that ZipEntryCRO is not treated as a simple - * integer. We use NULL to indicate an invalid value. - */ -typedef void* ZipEntryCRO; - -extern ZipFileCRO ZipFileXRO_open(const char* path); - -extern void ZipFileCRO_destroy(ZipFileCRO zip); - -extern ZipEntryCRO ZipFileCRO_findEntryByName(ZipFileCRO zip, - const char* fileName); - -extern bool ZipFileCRO_getEntryInfo(ZipFileCRO zip, ZipEntryCRO entry, - int* pMethod, long* pUncompLen, - long* pCompLen, off_t* pOffset, long* pModWhen, long* pCrc32); - -extern bool ZipFileCRO_uncompressEntry(ZipFileCRO zip, ZipEntryCRO entry, int fd); - -#ifdef __cplusplus -} -#endif - -#endif /*__LIBS_ZIPFILECRO_H*/ diff --git a/include/utils/ZipFileRO.h b/include/utils/ZipFileRO.h deleted file mode 100644 index 51c4f2fb6..000000000 --- a/include/utils/ZipFileRO.h +++ /dev/null @@ -1,222 +0,0 @@ -/* - * 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. - */ - -// -// Read-only access to Zip archives, with minimal heap allocation. -// -// This is similar to the more-complete ZipFile class, but no attempt -// has been made to make them interchangeable. This class operates under -// a very different set of assumptions and constraints. -// -#ifndef __LIBS_ZIPFILERO_H -#define __LIBS_ZIPFILERO_H - -#include "Errors.h" -#include "FileMap.h" - -#include -#include -#include - -namespace android { - -/* - * Trivial typedef to ensure that ZipEntryRO is not treated as a simple - * integer. We use NULL to indicate an invalid value. - */ -typedef void* ZipEntryRO; - -/* - * Open a Zip archive for reading. - * - * We want "open" and "find entry by name" to be fast operations, and we - * want to use as little memory as possible. We memory-map the file, - * and load a hash table with pointers to the filenames (which aren't - * null-terminated). The other fields are at a fixed offset from the - * filename, so we don't need to extract those (but we do need to byte-read - * and endian-swap them every time we want them). - * - * To speed comparisons when doing a lookup by name, we could make the mapping - * "private" (copy-on-write) and null-terminate the filenames after verifying - * the record structure. However, this requires a private mapping of - * every page that the Central Directory touches. Easier to tuck a copy - * of the string length into the hash table entry. - */ -class ZipFileRO { -public: - ZipFileRO() - : mFd(-1), mFileMap(NULL), mHashTableSize(-1), mHashTable(NULL) - {} - ~ZipFileRO() { - free(mHashTable); - if (mFileMap) - mFileMap->release(); - if (mFd >= 0) - close(mFd); - } - - /* - * Open an archive. - */ - status_t open(const char* zipFileName); - - /* - * Find an entry, by name. Returns the entry identifier, or NULL if - * not found. - * - * If two entries have the same name, one will be chosen at semi-random. - */ - ZipEntryRO findEntryByName(const char* fileName) const; - - /* - * Return the #of entries in the Zip archive. - */ - int getNumEntries(void) const { - return mNumEntries; - } - - /* - * Return the Nth entry. Zip file entries are not stored in sorted - * order, and updated entries may appear at the end, so anyone walking - * the archive needs to avoid making ordering assumptions. We take - * that further by returning the Nth non-empty entry in the hash table - * rather than the Nth entry in the archive. - * - * Valid values are [0..numEntries). - * - * [This is currently O(n). If it needs to be fast we can allocate an - * additional data structure or provide an iterator interface.] - */ - ZipEntryRO findEntryByIndex(int idx) const; - - /* - * Copy the filename into the supplied buffer. Returns 0 on success, - * -1 if "entry" is invalid, or the filename length if it didn't fit. The - * length, and the returned string, include the null-termination. - */ - int getEntryFileName(ZipEntryRO entry, char* buffer, int bufLen) const; - - /* - * Get the vital stats for an entry. Pass in NULL pointers for anything - * you don't need. - * - * "*pOffset" holds the Zip file offset of the entry's data. - * - * Returns "false" if "entry" is bogus or if the data in the Zip file - * appears to be bad. - */ - bool getEntryInfo(ZipEntryRO entry, int* pMethod, long* pUncompLen, - long* pCompLen, off_t* pOffset, long* pModWhen, long* pCrc32) const; - - /* - * Create a new FileMap object that maps a subset of the archive. For - * an uncompressed entry this effectively provides a pointer to the - * actual data, for a compressed entry this provides the input buffer - * for inflate(). - */ - FileMap* createEntryFileMap(ZipEntryRO entry) const; - - /* - * Uncompress the data into a buffer. Depending on the compression - * format, this is either an "inflate" operation or a memcpy. - * - * Use "uncompLen" from getEntryInfo() to determine the required - * buffer size. - * - * Returns "true" on success. - */ - bool uncompressEntry(ZipEntryRO entry, void* buffer) const; - - /* - * Uncompress the data to an open file descriptor. - */ - bool uncompressEntry(ZipEntryRO entry, int fd) const; - - /* Zip compression methods we support */ - enum { - kCompressStored = 0, // no compression - kCompressDeflated = 8, // standard deflate - }; - - /* - * Utility function: uncompress deflated data, buffer to buffer. - */ - static bool inflateBuffer(void* outBuf, const void* inBuf, - long uncompLen, long compLen); - - /* - * Utility function: uncompress deflated data, buffer to fd. - */ - static bool inflateBuffer(int fd, const void* inBuf, - long uncompLen, long compLen); - - /* - * Some basic functions for raw data manipulation. "LE" means - * Little Endian. - */ - static inline unsigned short get2LE(const unsigned char* buf) { - return buf[0] | (buf[1] << 8); - } - static inline unsigned long get4LE(const unsigned char* buf) { - return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); - } - -private: - /* these are private and not defined */ - ZipFileRO(const ZipFileRO& src); - ZipFileRO& operator=(const ZipFileRO& src); - - /* parse the archive, prepping internal structures */ - bool parseZipArchive(void); - - /* add a new entry to the hash table */ - void addToHash(const char* str, int strLen, unsigned int hash); - - /* compute string hash code */ - static unsigned int computeHash(const char* str, int len); - - /* convert a ZipEntryRO back to a hash table index */ - int entryToIndex(const ZipEntryRO entry) const; - - /* - * One entry in the hash table. - */ - typedef struct HashEntry { - const char* name; - unsigned short nameLen; - //unsigned int hash; - } HashEntry; - - /* open Zip archive */ - int mFd; - - /* mapped file */ - FileMap* mFileMap; - - /* number of entries in the Zip archive */ - int mNumEntries; - - /* - * We know how many entries are in the Zip archive, so we have a - * fixed-size hash table. We probe for an empty slot. - */ - int mHashTableSize; - HashEntry* mHashTable; -}; - -}; // namespace android - -#endif /*__LIBS_ZIPFILERO_H*/ diff --git a/include/utils/ZipUtils.h b/include/utils/ZipUtils.h deleted file mode 100644 index 42c42b6c0..000000000 --- a/include/utils/ZipUtils.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * 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. - */ - -// -// Miscellaneous zip/gzip utility functions. -// -#ifndef __LIBS_ZIPUTILS_H -#define __LIBS_ZIPUTILS_H - -#include - -namespace android { - -/* - * Container class for utility functions, primarily for namespace reasons. - */ -class ZipUtils { -public: - /* - * General utility function for uncompressing "deflate" data from a file - * to a buffer. - */ - static bool inflateToBuffer(int fd, void* buf, long uncompressedLen, - long compressedLen); - static bool inflateToBuffer(FILE* fp, void* buf, long uncompressedLen, - long compressedLen); - - /* - * Someday we might want to make this generic and handle bzip2 ".bz2" - * files too. - * - * We could declare gzip to be a sub-class of zip that has exactly - * one always-compressed entry, but we currently want to treat Zip - * and gzip as distinct, so there's no value. - * - * The zlib library has some gzip utilities, but it has no interface - * for extracting the uncompressed length of the file (you do *not* - * want to gzseek to the end). - * - * Pass in a seeked file pointer for the gzip file. If this is a gzip - * file, we set our return values appropriately and return "true" with - * the file seeked to the start of the compressed data. - */ - static bool examineGzip(FILE* fp, int* pCompressionMethod, - long* pUncompressedLen, long* pCompressedLen, unsigned long* pCRC32); - -private: - ZipUtils() {} - ~ZipUtils() {} -}; - -}; // namespace android - -#endif /*__LIBS_ZIPUTILS_H*/ diff --git a/include/utils/ashmem.h b/include/utils/ashmem.h deleted file mode 100644 index 085477578..000000000 --- a/include/utils/ashmem.h +++ /dev/null @@ -1,41 +0,0 @@ -/* utils/ashmem.h - ** - ** Copyright 2008 The Android Open Source Project - ** - ** This file is dual licensed. It may be redistributed and/or modified - ** under the terms of the Apache 2.0 License OR version 2 of the GNU - ** General Public License. - */ - -#ifndef _UTILS_ASHMEM_H -#define _UTILS_ASHMEM_H - -#include -#include - -#define ASHMEM_NAME_LEN 256 - -#define ASHMEM_NAME_DEF "dev/ashmem" - -/* Return values from ASHMEM_PIN: Was the mapping purged while unpinned? */ -#define ASHMEM_NOT_REAPED 0 -#define ASHMEM_WAS_REAPED 1 - -/* Return values from ASHMEM_UNPIN: Is the mapping now pinned or unpinned? */ -#define ASHMEM_NOW_UNPINNED 0 -#define ASHMEM_NOW_PINNED 1 - -#define __ASHMEMIOC 0x77 - -#define ASHMEM_SET_NAME _IOW(__ASHMEMIOC, 1, char[ASHMEM_NAME_LEN]) -#define ASHMEM_GET_NAME _IOR(__ASHMEMIOC, 2, char[ASHMEM_NAME_LEN]) -#define ASHMEM_SET_SIZE _IOW(__ASHMEMIOC, 3, size_t) -#define ASHMEM_GET_SIZE _IO(__ASHMEMIOC, 4) -#define ASHMEM_SET_PROT_MASK _IOW(__ASHMEMIOC, 5, unsigned long) -#define ASHMEM_GET_PROT_MASK _IO(__ASHMEMIOC, 6) -#define ASHMEM_PIN _IO(__ASHMEMIOC, 7) -#define ASHMEM_UNPIN _IO(__ASHMEMIOC, 8) -#define ASHMEM_ISPINNED _IO(__ASHMEMIOC, 9) -#define ASHMEM_PURGE_ALL_CACHES _IO(__ASHMEMIOC, 10) - -#endif /* _UTILS_ASHMEM_H */ diff --git a/include/utils/executablepath.h b/include/utils/executablepath.h deleted file mode 100644 index c979432ba..000000000 --- a/include/utils/executablepath.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef _UTILS_EXECUTABLEPATH_H -#define _UTILS_EXECUTABLEPATH_H - -#include - -// returns the path to this executable -#if __cplusplus -extern "C" -#endif -void executablepath(char s[PATH_MAX]); - -#endif // _UTILS_EXECUTABLEPATH_H diff --git a/include/utils/inet_address.h b/include/utils/inet_address.h deleted file mode 100644 index dbd8672e0..000000000 --- a/include/utils/inet_address.h +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (C) 2005 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. - */ - -// -// Internet address classes. Modeled after Java classes. -// -#ifndef _RUNTIME_INET_ADDRESS_H -#define _RUNTIME_INET_ADDRESS_H - -#ifdef HAVE_ANDROID_OS -#error DO NOT USE THIS FILE IN THE DEVICE BUILD -#endif - - -namespace android { - -/* - * This class holds Internet addresses. Perhaps more useful is its - * ability to look up addresses by name. - * - * Invoke one of the static factory methods to create a new object. - */ -class InetAddress { -public: - virtual ~InetAddress(void); - - // create from w.x.y.z or foo.bar.com notation - static InetAddress* getByName(const char* host); - - // copy-construction - InetAddress(const InetAddress& orig); - - const void* getAddress(void) const { return mAddress; } - int getAddressLength(void) const { return mLength; } - const char* getHostName(void) const { return mName; } - -private: - InetAddress(void); - // assignment (private) - InetAddress& operator=(const InetAddress& addr); - - // use a void* here so we don't have to expose actual socket headers - void* mAddress; // this is really a ptr to sockaddr_in - int mLength; - char* mName; -}; - - -/* - * Base class for socket addresses. - */ -class SocketAddress { -public: - SocketAddress() {} - virtual ~SocketAddress() {} -}; - - -/* - * Internet address class. This combines an InetAddress with a port. - */ -class InetSocketAddress : public SocketAddress { -public: - InetSocketAddress() : - mAddress(0), mPort(-1) - {} - ~InetSocketAddress(void) { - delete mAddress; - } - - // Create an address with a host wildcard (useful for servers). - bool create(int port); - // Create an address with the specified host and port. - bool create(const InetAddress* addr, int port); - // Create an address with the specified host and port. Does the - // hostname lookup. - bool create(const char* host, int port); - - const InetAddress* getAddress(void) const { return mAddress; } - const int getPort(void) const { return mPort; } - const char* getHostName(void) const { return mAddress->getHostName(); } - -private: - InetAddress* mAddress; - int mPort; -}; - -}; // namespace android - -#endif // _RUNTIME_INET_ADDRESS_H diff --git a/include/utils/misc.h b/include/utils/misc.h deleted file mode 100644 index 62e84b489..000000000 --- a/include/utils/misc.h +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (C) 2005 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. - */ - -// -// Handy utility functions and portability code. -// -#ifndef _LIBS_UTILS_MISC_H -#define _LIBS_UTILS_MISC_H - -#include -#include "utils/Endian.h" - -namespace android { - -/* get #of elements in a static array */ -#ifndef NELEM -# define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0]))) -#endif - -/* - * Make a copy of the string, using "new[]" instead of "malloc". Free the - * string with delete[]. - * - * Returns NULL if "str" is NULL. - */ -char* strdupNew(const char* str); - -/* - * Concatenate an argument vector into a single string. If argc is >= 0 - * it will be used; if it's < 0 then the last element in the arg vector - * must be NULL. - * - * This inserts a space between each argument. - * - * This does not automatically add double quotes around arguments with - * spaces in them. This practice is necessary for Win32, because Win32's - * CreateProcess call is stupid. - * - * The caller should delete[] the returned string. - */ -char* concatArgv(int argc, const char* const argv[]); - -/* - * Count up the number of arguments in "argv". The count does not include - * the final NULL entry. - */ -int countArgv(const char* const argv[]); - -/* - * Some utility functions for working with files. These could be made - * part of a "File" class. - */ -typedef enum FileType { - kFileTypeUnknown = 0, - kFileTypeNonexistent, // i.e. ENOENT - kFileTypeRegular, - kFileTypeDirectory, - kFileTypeCharDev, - kFileTypeBlockDev, - kFileTypeFifo, - kFileTypeSymlink, - kFileTypeSocket, -} FileType; -/* get the file's type; follows symlinks */ -FileType getFileType(const char* fileName); -/* get the file's modification date; returns -1 w/errno set on failure */ -time_t getFileModDate(const char* fileName); - -/* - * Round up to the nearest power of 2. Handy for hash tables. - */ -unsigned int roundUpPower2(unsigned int val); - -void strreverse(char* begin, char* end); -void k_itoa(int value, char* str, int base); -char* itoa(int val, int base); - -}; // namespace android - -#endif // _LIBS_UTILS_MISC_H diff --git a/include/utils/ported.h b/include/utils/ported.h deleted file mode 100644 index eb3be01e9..000000000 --- a/include/utils/ported.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2005 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. - */ - -// -// Standard functions ported to the current platform. Note these are NOT -// in the "android" namespace. -// -#ifndef _LIBS_UTILS_PORTED_H -#define _LIBS_UTILS_PORTED_H - -#include // for timeval - -#ifdef __cplusplus -extern "C" { -#endif - -/* library replacement functions */ -#if defined(NEED_GETTIMEOFDAY) -int gettimeofday(struct timeval* tv, struct timezone* tz); -#endif -#if defined(NEED_USLEEP) -void usleep(unsigned long usec); -#endif -#if defined(NEED_PIPE) -int pipe(int filedes[2]); -#endif -#if defined(NEED_SETENV) -int setenv(const char* name, const char* value, int overwrite); -void unsetenv(const char* name); -char* getenv(const char* name); -#endif - -#ifdef __cplusplus -} -#endif - -#endif // _LIBS_UTILS_PORTED_H diff --git a/include/utils/string_array.h b/include/utils/string_array.h deleted file mode 100644 index 064dda224..000000000 --- a/include/utils/string_array.h +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright (C) 2005 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. - */ - -// -// Sortable array of strings. STL-ish, but STL-free. -// -#ifndef _LIBS_UTILS_STRING_ARRAY_H -#define _LIBS_UTILS_STRING_ARRAY_H - -#include -#include - -namespace android { - -// -// An expanding array of strings. Add, get, sort, delete. -// -class StringArray { -public: - StringArray() - : mMax(0), mCurrent(0), mArray(NULL) - {} - virtual ~StringArray() { - for (int i = 0; i < mCurrent; i++) - delete[] mArray[i]; - delete[] mArray; - } - - // - // Add a string. A copy of the string is made. - // - bool push_back(const char* str) { - if (mCurrent >= mMax) { - char** tmp; - - if (mMax == 0) - mMax = 16; // initial storage - else - mMax *= 2; - - tmp = new char*[mMax]; - if (tmp == NULL) - return false; - - memcpy(tmp, mArray, mCurrent * sizeof(char*)); - delete[] mArray; - mArray = tmp; - } - - int len = strlen(str); - mArray[mCurrent] = new char[len+1]; - memcpy(mArray[mCurrent], str, len+1); - mCurrent++; - - return true; - } - - // - // Delete an entry. - // - void erase(int idx) { - if (idx < 0 || idx >= mCurrent) - return; - delete[] mArray[idx]; - if (idx < mCurrent-1) { - memmove(&mArray[idx], &mArray[idx+1], - (mCurrent-1 - idx) * sizeof(char*)); - } - mCurrent--; - } - - // - // Sort the array. - // - void sort(int (*compare)(const void*, const void*)) { - qsort(mArray, mCurrent, sizeof(char*), compare); - } - - // - // Pass this to the sort routine to do an ascending alphabetical sort. - // - static int cmpAscendingAlpha(const void* pstr1, const void* pstr2) { - return strcmp(*(const char**)pstr1, *(const char**)pstr2); - } - - // - // Get the #of items in the array. - // - inline int size(void) const { return mCurrent; } - - // - // Return entry N. - // [should use operator[] here] - // - const char* getEntry(int idx) const { - if (idx < 0 || idx >= mCurrent) - return NULL; - return mArray[idx]; - } - - // - // Set entry N to specified string. - // [should use operator[] here] - // - void setEntry(int idx, const char* str) { - if (idx < 0 || idx >= mCurrent) - return; - delete[] mArray[idx]; - int len = strlen(str); - mArray[idx] = new char[len+1]; - memcpy(mArray[idx], str, len+1); - } - -private: - int mMax; - int mCurrent; - char** mArray; -}; - -}; // namespace android - -#endif // _LIBS_UTILS_STRING_ARRAY_H diff --git a/include/utils/threads.h b/include/utils/threads.h deleted file mode 100644 index 7dca81004..000000000 --- a/include/utils/threads.h +++ /dev/null @@ -1,347 +0,0 @@ -/* - * 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. - */ - -#ifndef _LIBS_UTILS_THREADS_H -#define _LIBS_UTILS_THREADS_H - -#include -#include -#include - -// ------------------------------------------------------------------ -// C API - -#ifdef __cplusplus -extern "C" { -#endif - -typedef void* android_thread_id_t; - -typedef int (*android_thread_func_t)(void*); - -enum { - /* - * *********************************************** - * ** Keep in sync with android.os.Process.java ** - * *********************************************** - * - * This maps directly to the "nice" priorites we use in Android. - * A thread priority should be chosen inverse-proportinally to - * the amount of work the thread is expected to do. The more work - * a thread will do, the less favorable priority it should get so that - * it doesn't starve the system. Threads not behaving properly might - * be "punished" by the kernel. - * Use the levels below when appropriate. Intermediate values are - * acceptable, preferably use the {MORE|LESS}_FAVORABLE constants below. - */ - ANDROID_PRIORITY_LOWEST = 19, - - /* use for background tasks */ - ANDROID_PRIORITY_BACKGROUND = 10, - - /* most threads run at normal priority */ - ANDROID_PRIORITY_NORMAL = 0, - - /* threads currently running a UI that the user is interacting with */ - ANDROID_PRIORITY_FOREGROUND = -2, - - /* the main UI thread has a slightly more favorable priority */ - ANDROID_PRIORITY_DISPLAY = -4, - - /* ui service treads might want to run at a urgent display (uncommon) */ - ANDROID_PRIORITY_URGENT_DISPLAY = -8, - - /* all normal audio threads */ - ANDROID_PRIORITY_AUDIO = -16, - - /* service audio threads (uncommon) */ - ANDROID_PRIORITY_URGENT_AUDIO = -19, - - /* should never be used in practice. regular process might not - * be allowed to use this level */ - ANDROID_PRIORITY_HIGHEST = -20, - - ANDROID_PRIORITY_DEFAULT = ANDROID_PRIORITY_NORMAL, - ANDROID_PRIORITY_MORE_FAVORABLE = -1, - ANDROID_PRIORITY_LESS_FAVORABLE = +1, -}; - -// Create and run a new thread. -extern int androidCreateThread(android_thread_func_t, void *); - -// Create thread with lots of parameters -extern int androidCreateThreadEtc(android_thread_func_t entryFunction, - void *userData, - const char* threadName, - int32_t threadPriority, - size_t threadStackSize, - android_thread_id_t *threadId); - -// Get some sort of unique identifier for the current thread. -extern android_thread_id_t androidGetThreadId(); - -// Low-level thread creation -- never creates threads that can -// interact with the Java VM. -extern int androidCreateRawThreadEtc(android_thread_func_t entryFunction, - void *userData, - const char* threadName, - int32_t threadPriority, - size_t threadStackSize, - android_thread_id_t *threadId); - -// Used by the Java Runtime to control how threads are created, so that -// they can be proper and lovely Java threads. -typedef int (*android_create_thread_fn)(android_thread_func_t entryFunction, - void *userData, - const char* threadName, - int32_t threadPriority, - size_t threadStackSize, - android_thread_id_t *threadId); - -extern void androidSetCreateThreadFunc(android_create_thread_fn func); - -#ifdef __cplusplus -} -#endif - -// ------------------------------------------------------------------ -// C++ API - -#ifdef __cplusplus - -#include -#include -#include - -namespace android { - -typedef android_thread_id_t thread_id_t; - -typedef android_thread_func_t thread_func_t; - -enum { - PRIORITY_LOWEST = ANDROID_PRIORITY_LOWEST, - PRIORITY_BACKGROUND = ANDROID_PRIORITY_BACKGROUND, - PRIORITY_NORMAL = ANDROID_PRIORITY_NORMAL, - PRIORITY_FOREGROUND = ANDROID_PRIORITY_FOREGROUND, - PRIORITY_DISPLAY = ANDROID_PRIORITY_DISPLAY, - PRIORITY_URGENT_DISPLAY = ANDROID_PRIORITY_URGENT_DISPLAY, - PRIORITY_AUDIO = ANDROID_PRIORITY_AUDIO, - PRIORITY_URGENT_AUDIO = ANDROID_PRIORITY_URGENT_AUDIO, - PRIORITY_HIGHEST = ANDROID_PRIORITY_HIGHEST, - PRIORITY_DEFAULT = ANDROID_PRIORITY_DEFAULT, - PRIORITY_MORE_FAVORABLE = ANDROID_PRIORITY_MORE_FAVORABLE, - PRIORITY_LESS_FAVORABLE = ANDROID_PRIORITY_LESS_FAVORABLE, -}; - -// Create and run a new thread. -inline bool createThread(thread_func_t f, void *a) { - return androidCreateThread(f, a) ? true : false; -} - -// Create thread with lots of parameters -inline bool createThreadEtc(thread_func_t entryFunction, - void *userData, - const char* threadName = "android:unnamed_thread", - int32_t threadPriority = PRIORITY_DEFAULT, - size_t threadStackSize = 0, - thread_id_t *threadId = 0) -{ - return androidCreateThreadEtc(entryFunction, userData, threadName, - threadPriority, threadStackSize, threadId) ? true : false; -} - -// Get some sort of unique identifier for the current thread. -inline thread_id_t getThreadId() { - return androidGetThreadId(); -} - -/* - * Simple mutex class. The implementation is system-dependent. - * - * The mutex must be unlocked by the thread that locked it. They are not - * recursive, i.e. the same thread can't lock it multiple times. - */ -class Mutex { -public: - Mutex(); - Mutex(const char* name); - ~Mutex(); - - // lock or unlock the mutex - status_t lock(); - void unlock(); - - // lock if possible; returns 0 on success, error otherwise - status_t tryLock(); - - // Manages the mutex automatically. It'll be locked when Autolock is - // constructed and released when Autolock goes out of scope. - class Autolock { - public: - inline Autolock(Mutex& mutex) : mpMutex(&mutex) { mutex.lock(); } - inline Autolock(Mutex* mutex) : mpMutex(mutex) { mutex->lock(); } - inline ~Autolock() { mpMutex->unlock(); } - private: - Mutex* mpMutex; - }; - -private: - friend class Condition; - - // A mutex cannot be copied - Mutex(const Mutex&); - Mutex& operator = (const Mutex&); - void _init(); - - void* mState; -}; - -/* - * Automatic mutex. Declare one of these at the top of a function. - * When the function returns, it will go out of scope, and release the - * mutex. - */ - -typedef Mutex::Autolock AutoMutex; - - -/* - * Condition variable class. The implementation is system-dependent. - * - * Condition variables are paired up with mutexes. Lock the mutex, - * call wait(), then either re-wait() if things aren't quite what you want, - * or unlock the mutex and continue. All threads calling wait() must - * use the same mutex for a given Condition. - */ -class Condition { -public: - Condition(); - ~Condition(); - // Wait on the condition variable. Lock the mutex before calling. - status_t wait(Mutex& mutex); - // Wait on the condition variable until the given time. Lock the mutex - // before calling. - status_t wait(Mutex& mutex, nsecs_t abstime); - // same with relative timeout - status_t waitRelative(Mutex& mutex, nsecs_t reltime); - // Signal the condition variable, allowing one thread to continue. - void signal(); - // Signal the condition variable, allowing all threads to continue. - void broadcast(); - -private: - void* mState; -}; - - -/* - * Read/write lock. The resource can have multiple readers or one writer, - * but can't be read and written at the same time. - * - * The same thread should not call a lock function while it already has - * a lock. (Should be okay for multiple readers.) - */ -class ReadWriteLock { -public: - ReadWriteLock() - : mNumReaders(0), mNumWriters(0) - {} - ~ReadWriteLock() {} - - void lockForRead(); - bool tryLockForRead(); - void unlockForRead(); - - void lockForWrite(); - bool tryLockForWrite(); - void unlockForWrite(); - -private: - int mNumReaders; - int mNumWriters; - - Mutex mLock; - Condition mReadWaiter; - Condition mWriteWaiter; -#if defined(PRINT_RENDER_TIMES) - DurationTimer mDebugTimer; -#endif -}; - - -/* - * This is our spiffy thread object! - */ - -class Thread : virtual public RefBase -{ -public: - // Create a Thread object, but doesn't create or start the associated - // thread. See the run() method. - Thread(bool canCallJava = true); - virtual ~Thread(); - - // Start the thread in threadLoop() which needs to be implemented. - virtual status_t run( const char* name = 0, - int32_t priority = PRIORITY_DEFAULT, - size_t stack = 0); - - // Ask this object's thread to exit. This function is asynchronous, when the - // function returns the thread might still be running. Of course, this - // function can be called from a different thread. - virtual void requestExit(); - - // Good place to do one-time initializations - virtual status_t readyToRun(); - - // Call requestExit() and wait until this object's thread exits. - // BE VERY CAREFUL of deadlocks. In particular, it would be silly to call - // this function from this object's thread. Will return WOULD_BLOCK in - // that case. - status_t requestExitAndWait(); - -protected: - // exitPending() returns true if requestExit() has been called. - bool exitPending() const; - -private: - // Derived class must implemtent threadLoop(). The thread starts its life - // here. There are two ways of using the Thread object: - // 1) loop: if threadLoop() returns true, it will be called again if - // requestExit() wasn't called. - // 2) once: if threadLoop() returns false, the thread will exit upon return. - virtual bool threadLoop() = 0; - -private: - Thread& operator=(const Thread&); - static int _threadLoop(void* user); - const bool mCanCallJava; - thread_id_t mThread; - Mutex mLock; - Condition mThreadExitedCondition; - status_t mStatus; - volatile bool mExitPending; - volatile bool mRunning; - sp mHoldSelf; -}; - - -}; // namespace android - -#endif // __cplusplus - -#endif // _LIBS_UTILS_THREADS_H diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk deleted file mode 100644 index cdb8ca2d7..000000000 --- a/libs/utils/Android.mk +++ /dev/null @@ -1,156 +0,0 @@ -# Copyright (C) 2008 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -LOCAL_PATH:= $(call my-dir) - -# libutils is a little unique: It's built twice, once for the host -# and once for the device. - -commonSources:= \ - Asset.cpp \ - AssetDir.cpp \ - AssetManager.cpp \ - BufferedTextOutput.cpp \ - CallStack.cpp \ - Debug.cpp \ - FileMap.cpp \ - RefBase.cpp \ - ResourceTypes.cpp \ - SharedBuffer.cpp \ - Static.cpp \ - StopWatch.cpp \ - String8.cpp \ - String16.cpp \ - SystemClock.cpp \ - TextOutput.cpp \ - Threads.cpp \ - TimerProbe.cpp \ - Timers.cpp \ - VectorImpl.cpp \ - ZipFileCRO.cpp \ - ZipFileRO.cpp \ - ZipUtils.cpp \ - misc.cpp \ - ported.cpp \ - LogSocket.cpp - -# -# The cpp files listed here do not belong in the device -# build. Consult with the swetland before even thinking about -# putting them in commonSources. -# -# They're used by the simulator runtime and by host-side tools like -# aapt and the simulator front-end. -# -hostSources:= \ - InetAddress.cpp \ - Pipe.cpp \ - Socket.cpp \ - ZipEntry.cpp \ - ZipFile.cpp - -# For the host -# ===================================================== - -include $(CLEAR_VARS) - -LOCAL_SRC_FILES:= $(commonSources) $(hostSources) - -ifeq ($(HOST_OS),linux) -# Use the futex based mutex and condition variable -# implementation from android-arm because it's shared mem safe - LOCAL_SRC_FILES += \ - futex_synchro.c \ - executablepath_linux.cpp -endif -ifeq ($(HOST_OS),darwin) - LOCAL_SRC_FILES += \ - executablepath_darwin.cpp -endif - -LOCAL_MODULE:= libutils - -LOCAL_CFLAGS += -DLIBUTILS_NATIVE=1 $(TOOL_CFLAGS) -LOCAL_C_INCLUDES += external/zlib - -ifeq ($(HOST_OS),windows) -ifeq ($(strip $(USE_CYGWIN),),) -# Under MinGW, ctype.h doesn't need multi-byte support -LOCAL_CFLAGS += -DMB_CUR_MAX=1 -endif -endif - -include $(BUILD_HOST_STATIC_LIBRARY) - - - -# For the device -# ===================================================== -include $(CLEAR_VARS) - - -# we have the common sources, plus some device-specific stuff -LOCAL_SRC_FILES:= \ - $(commonSources) \ - Binder.cpp \ - BpBinder.cpp \ - IInterface.cpp \ - IMemory.cpp \ - IPCThreadState.cpp \ - MemoryDealer.cpp \ - MemoryBase.cpp \ - MemoryHeapBase.cpp \ - MemoryHeapPmem.cpp \ - Parcel.cpp \ - ProcessState.cpp \ - IPermissionController.cpp \ - IServiceManager.cpp \ - Unicode.cpp - -ifeq ($(TARGET_SIMULATOR),true) -LOCAL_SRC_FILES += $(hostSources) -endif - -ifeq ($(TARGET_OS),linux) -# Use the futex based mutex and condition variable -# implementation from android-arm because it's shared mem safe -LOCAL_SRC_FILES += futex_synchro.c -LOCAL_LDLIBS += -lrt -ldl -endif - -LOCAL_C_INCLUDES += \ - external/zlib \ - external/icu4c/common -LOCAL_LDLIBS += -lpthread - -LOCAL_SHARED_LIBRARIES := \ - libz \ - liblog \ - libcutils - -ifneq ($(TARGET_SIMULATOR),true) -ifeq ($(TARGET_OS)-$(TARGET_ARCH),linux-x86) -# This is needed on x86 to bring in dl_iterate_phdr for CallStack.cpp -LOCAL_SHARED_LIBRARIES += \ - libdl -endif # linux-x86 -endif # sim - -LOCAL_MODULE:= libutils - -#LOCAL_CFLAGS+= -#LOCAL_LDFLAGS:= - -include $(BUILD_SHARED_LIBRARY) - diff --git a/libs/utils/Asset.cpp b/libs/utils/Asset.cpp deleted file mode 100644 index 91203ddb4..000000000 --- a/libs/utils/Asset.cpp +++ /dev/null @@ -1,813 +0,0 @@ -/* - * Copyright (C) 2006 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. - */ - -// -// Provide access to a read-only asset. -// - -#define LOG_TAG "asset" -//#define NDEBUG 0 - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -using namespace android; - -#ifndef O_BINARY -# define O_BINARY 0 -#endif - -static volatile int32_t gCount = 0; - -int32_t Asset::getGlobalCount() -{ - return gCount; -} - -Asset::Asset(void) - : mAccessMode(ACCESS_UNKNOWN) -{ - int count = android_atomic_inc(&gCount)+1; - //LOGI("Creating Asset %p #%d\n", this, count); -} - -Asset::~Asset(void) -{ - int count = android_atomic_dec(&gCount); - //LOGI("Destroying Asset in %p #%d\n", this, count); -} - -/* - * Create a new Asset from a file on disk. There is a fair chance that - * the file doesn't actually exist. - * - * We can use "mode" to decide how we want to go about it. - */ -/*static*/ Asset* Asset::createFromFile(const char* fileName, AccessMode mode) -{ - _FileAsset* pAsset; - status_t result; - off_t length; - int fd; - - fd = open(fileName, O_RDONLY | O_BINARY); - if (fd < 0) - return NULL; - - /* - * Under Linux, the lseek fails if we actually opened a directory. To - * be correct we should test the file type explicitly, but since we - * always open things read-only it doesn't really matter, so there's - * no value in incurring the extra overhead of an fstat() call. - */ - length = lseek(fd, 0, SEEK_END); - if (length < 0) { - ::close(fd); - return NULL; - } - (void) lseek(fd, 0, SEEK_SET); - - pAsset = new _FileAsset; - result = pAsset->openChunk(fileName, fd, 0, length); - if (result != NO_ERROR) { - delete pAsset; - return NULL; - } - - pAsset->mAccessMode = mode; - return pAsset; -} - - -/* - * Create a new Asset from a compressed file on disk. There is a fair chance - * that the file doesn't actually exist. - * - * We currently support gzip files. We might want to handle .bz2 someday. - */ -/*static*/ Asset* Asset::createFromCompressedFile(const char* fileName, - AccessMode mode) -{ - _CompressedAsset* pAsset; - status_t result; - off_t fileLen; - bool scanResult; - long offset; - int method; - long uncompressedLen, compressedLen; - int fd; - - fd = open(fileName, O_RDONLY | O_BINARY); - if (fd < 0) - return NULL; - - fileLen = lseek(fd, 0, SEEK_END); - if (fileLen < 0) { - ::close(fd); - return NULL; - } - (void) lseek(fd, 0, SEEK_SET); - - /* want buffered I/O for the file scan; must dup so fclose() is safe */ - FILE* fp = fdopen(dup(fd), "rb"); - if (fp == NULL) { - ::close(fd); - return NULL; - } - - unsigned long crc32; - scanResult = ZipUtils::examineGzip(fp, &method, &uncompressedLen, - &compressedLen, &crc32); - offset = ftell(fp); - fclose(fp); - if (!scanResult) { - LOGD("File '%s' is not in gzip format\n", fileName); - ::close(fd); - return NULL; - } - - pAsset = new _CompressedAsset; - result = pAsset->openChunk(fd, offset, method, uncompressedLen, - compressedLen); - if (result != NO_ERROR) { - delete pAsset; - return NULL; - } - - pAsset->mAccessMode = mode; - return pAsset; -} - - -#if 0 -/* - * Create a new Asset from part of an open file. - */ -/*static*/ Asset* Asset::createFromFileSegment(int fd, off_t offset, - size_t length, AccessMode mode) -{ - _FileAsset* pAsset; - status_t result; - - pAsset = new _FileAsset; - result = pAsset->openChunk(NULL, fd, offset, length); - if (result != NO_ERROR) - return NULL; - - pAsset->mAccessMode = mode; - return pAsset; -} - -/* - * Create a new Asset from compressed data in an open file. - */ -/*static*/ Asset* Asset::createFromCompressedData(int fd, off_t offset, - int compressionMethod, size_t uncompressedLen, size_t compressedLen, - AccessMode mode) -{ - _CompressedAsset* pAsset; - status_t result; - - pAsset = new _CompressedAsset; - result = pAsset->openChunk(fd, offset, compressionMethod, - uncompressedLen, compressedLen); - if (result != NO_ERROR) - return NULL; - - pAsset->mAccessMode = mode; - return pAsset; -} -#endif - -/* - * Create a new Asset from a memory mapping. - */ -/*static*/ Asset* Asset::createFromUncompressedMap(FileMap* dataMap, - AccessMode mode) -{ - _FileAsset* pAsset; - status_t result; - - pAsset = new _FileAsset; - result = pAsset->openChunk(dataMap); - if (result != NO_ERROR) - return NULL; - - pAsset->mAccessMode = mode; - return pAsset; -} - -/* - * Create a new Asset from compressed data in a memory mapping. - */ -/*static*/ Asset* Asset::createFromCompressedMap(FileMap* dataMap, - int method, size_t uncompressedLen, AccessMode mode) -{ - _CompressedAsset* pAsset; - status_t result; - - pAsset = new _CompressedAsset; - result = pAsset->openChunk(dataMap, method, uncompressedLen); - if (result != NO_ERROR) - return NULL; - - pAsset->mAccessMode = mode; - return pAsset; -} - - -/* - * Do generic seek() housekeeping. Pass in the offset/whence values from - * the seek request, along with the current chunk offset and the chunk - * length. - * - * Returns the new chunk offset, or -1 if the seek is illegal. - */ -off_t Asset::handleSeek(off_t offset, int whence, off_t curPosn, off_t maxPosn) -{ - off_t newOffset; - - switch (whence) { - case SEEK_SET: - newOffset = offset; - break; - case SEEK_CUR: - newOffset = curPosn + offset; - break; - case SEEK_END: - newOffset = maxPosn + offset; - break; - default: - LOGW("unexpected whence %d\n", whence); - // this was happening due to an off_t size mismatch - assert(false); - return (off_t) -1; - } - - if (newOffset < 0 || newOffset > maxPosn) { - LOGW("seek out of range: want %ld, end=%ld\n", - (long) newOffset, (long) maxPosn); - return (off_t) -1; - } - - return newOffset; -} - - -/* - * =========================================================================== - * _FileAsset - * =========================================================================== - */ - -/* - * Constructor. - */ -_FileAsset::_FileAsset(void) - : mStart(0), mLength(0), mOffset(0), mFp(NULL), mFileName(NULL), mMap(NULL), mBuf(NULL) -{ -} - -/* - * Destructor. Release resources. - */ -_FileAsset::~_FileAsset(void) -{ - close(); -} - -/* - * Operate on a chunk of an uncompressed file. - * - * Zero-length chunks are allowed. - */ -status_t _FileAsset::openChunk(const char* fileName, int fd, off_t offset, size_t length) -{ - assert(mFp == NULL); // no reopen - assert(mMap == NULL); - assert(fd >= 0); - assert(offset >= 0); - - /* - * Seek to end to get file length. - */ - off_t fileLength; - fileLength = lseek(fd, 0, SEEK_END); - if (fileLength == (off_t) -1) { - // probably a bad file descriptor - LOGD("failed lseek (errno=%d)\n", errno); - return UNKNOWN_ERROR; - } - - if ((off_t) (offset + length) > fileLength) { - LOGD("start (%ld) + len (%ld) > end (%ld)\n", - (long) offset, (long) length, (long) fileLength); - return BAD_INDEX; - } - - /* after fdopen, the fd will be closed on fclose() */ - mFp = fdopen(fd, "rb"); - if (mFp == NULL) - return UNKNOWN_ERROR; - - mStart = offset; - mLength = length; - assert(mOffset == 0); - - /* seek the FILE* to the start of chunk */ - if (fseek(mFp, mStart, SEEK_SET) != 0) { - assert(false); - } - - mFileName = fileName != NULL ? strdup(fileName) : NULL; - - return NO_ERROR; -} - -/* - * Create the chunk from the map. - */ -status_t _FileAsset::openChunk(FileMap* dataMap) -{ - assert(mFp == NULL); // no reopen - assert(mMap == NULL); - assert(dataMap != NULL); - - mMap = dataMap; - mStart = -1; // not used - mLength = dataMap->getDataLength(); - assert(mOffset == 0); - - return NO_ERROR; -} - -/* - * Read a chunk of data. - */ -ssize_t _FileAsset::read(void* buf, size_t count) -{ - size_t maxLen; - size_t actual; - - assert(mOffset >= 0 && mOffset <= mLength); - - if (getAccessMode() == ACCESS_BUFFER) { - /* - * On first access, read or map the entire file. The caller has - * requested buffer access, either because they're going to be - * using the buffer or because what they're doing has appropriate - * performance needs and access patterns. - */ - if (mBuf == NULL) - getBuffer(false); - } - - /* adjust count if we're near EOF */ - maxLen = mLength - mOffset; - if (count > maxLen) - count = maxLen; - - if (!count) - return 0; - - if (mMap != NULL) { - /* copy from mapped area */ - //printf("map read\n"); - memcpy(buf, (char*)mMap->getDataPtr() + mOffset, count); - actual = count; - } else if (mBuf != NULL) { - /* copy from buffer */ - //printf("buf read\n"); - memcpy(buf, (char*)mBuf + mOffset, count); - actual = count; - } else { - /* read from the file */ - //printf("file read\n"); - if (ftell(mFp) != mStart + mOffset) { - LOGE("Hosed: %ld != %ld+%ld\n", - ftell(mFp), (long) mStart, (long) mOffset); - assert(false); - } - - /* - * This returns 0 on error or eof. We need to use ferror() or feof() - * to tell the difference, but we don't currently have those on the - * device. However, we know how much data is *supposed* to be in the - * file, so if we don't read the full amount we know something is - * hosed. - */ - actual = fread(buf, 1, count, mFp); - if (actual == 0) // something failed -- I/O error? - return -1; - - assert(actual == count); - } - - mOffset += actual; - return actual; -} - -/* - * Seek to a new position. - */ -off_t _FileAsset::seek(off_t offset, int whence) -{ - off_t newPosn; - long actualOffset; - - // compute new position within chunk - newPosn = handleSeek(offset, whence, mOffset, mLength); - if (newPosn == (off_t) -1) - return newPosn; - - actualOffset = (long) (mStart + newPosn); - - if (mFp != NULL) { - if (fseek(mFp, (long) actualOffset, SEEK_SET) != 0) - return (off_t) -1; - } - - mOffset = actualOffset - mStart; - return mOffset; -} - -/* - * Close the asset. - */ -void _FileAsset::close(void) -{ - if (mMap != NULL) { - mMap->release(); - mMap = NULL; - } - if (mBuf != NULL) { - delete[] mBuf; - mBuf = NULL; - } - - if (mFileName != NULL) { - free(mFileName); - mFileName = NULL; - } - - if (mFp != NULL) { - // can only be NULL when called from destructor - // (otherwise we would never return this object) - fclose(mFp); - mFp = NULL; - } -} - -/* - * Return a read-only pointer to a buffer. - * - * We can either read the whole thing in or map the relevant piece of - * the source file. Ideally a map would be established at a higher - * level and we'd be using a different object, but we didn't, so we - * deal with it here. - */ -const void* _FileAsset::getBuffer(bool wordAligned) -{ - /* subsequent requests just use what we did previously */ - if (mBuf != NULL) - return mBuf; - if (mMap != NULL) { - if (!wordAligned) { - return mMap->getDataPtr(); - } - return ensureAlignment(mMap); - } - - assert(mFp != NULL); - - if (mLength < kReadVsMapThreshold) { - unsigned char* buf; - long allocLen; - - /* zero-length files are allowed; not sure about zero-len allocs */ - /* (works fine with gcc + x86linux) */ - allocLen = mLength; - if (mLength == 0) - allocLen = 1; - - buf = new unsigned char[allocLen]; - if (buf == NULL) { - LOGE("alloc of %ld bytes failed\n", (long) allocLen); - return NULL; - } - - LOGV("Asset %p allocating buffer size %d (smaller than threshold)", this, (int)allocLen); - if (mLength > 0) { - long oldPosn = ftell(mFp); - fseek(mFp, mStart, SEEK_SET); - if (fread(buf, 1, mLength, mFp) != (size_t) mLength) { - LOGE("failed reading %ld bytes\n", (long) mLength); - delete[] buf; - return NULL; - } - fseek(mFp, oldPosn, SEEK_SET); - } - - LOGV(" getBuffer: loaded into buffer\n"); - - mBuf = buf; - return mBuf; - } else { - FileMap* map; - - map = new FileMap; - if (!map->create(NULL, fileno(mFp), mStart, mLength, true)) { - map->release(); - return NULL; - } - - LOGV(" getBuffer: mapped\n"); - - mMap = map; - if (!wordAligned) { - return mMap->getDataPtr(); - } - return ensureAlignment(mMap); - } -} - -int _FileAsset::openFileDescriptor(off_t* outStart, off_t* outLength) const -{ - if (mMap != NULL) { - const char* fname = mMap->getFileName(); - if (fname == NULL) { - fname = mFileName; - } - if (fname == NULL) { - return -1; - } - *outStart = mMap->getDataOffset(); - *outLength = mMap->getDataLength(); - return open(fname, O_RDONLY | O_BINARY); - } - if (mFileName == NULL) { - return -1; - } - *outStart = mStart; - *outLength = mLength; - return open(mFileName, O_RDONLY | O_BINARY); -} - -const void* _FileAsset::ensureAlignment(FileMap* map) -{ - void* data = map->getDataPtr(); - if ((((size_t)data)&0x3) == 0) { - // We can return this directly if it is aligned on a word - // boundary. - return data; - } - // If not aligned on a word boundary, then we need to copy it into - // our own buffer. - LOGV("Copying FileAsset %p to buffer size %d to make it aligned.", this, (int)mLength); - unsigned char* buf = new unsigned char[mLength]; - if (buf == NULL) { - LOGE("alloc of %ld bytes failed\n", (long) mLength); - return NULL; - } - memcpy(buf, data, mLength); - mBuf = buf; - return buf; -} - -/* - * =========================================================================== - * _CompressedAsset - * =========================================================================== - */ - -/* - * Constructor. - */ -_CompressedAsset::_CompressedAsset(void) - : mStart(0), mCompressedLen(0), mUncompressedLen(0), mOffset(0), - mMap(NULL), mFd(-1), mBuf(NULL) -{ -} - -/* - * Destructor. Release resources. - */ -_CompressedAsset::~_CompressedAsset(void) -{ - close(); -} - -/* - * Open a chunk of compressed data inside a file. - * - * This currently just sets up some values and returns. On the first - * read, we expand the entire file into a buffer and return data from it. - */ -status_t _CompressedAsset::openChunk(int fd, off_t offset, - int compressionMethod, size_t uncompressedLen, size_t compressedLen) -{ - assert(mFd < 0); // no re-open - assert(mMap == NULL); - assert(fd >= 0); - assert(offset >= 0); - assert(compressedLen > 0); - - if (compressionMethod != ZipFileRO::kCompressDeflated) { - assert(false); - return UNKNOWN_ERROR; - } - - mStart = offset; - mCompressedLen = compressedLen; - mUncompressedLen = uncompressedLen; - assert(mOffset == 0); - mFd = fd; - assert(mBuf == NULL); - - return NO_ERROR; -} - -/* - * Open a chunk of compressed data in a mapped region. - * - * Nothing is expanded until the first read call. - */ -status_t _CompressedAsset::openChunk(FileMap* dataMap, int compressionMethod, - size_t uncompressedLen) -{ - assert(mFd < 0); // no re-open - assert(mMap == NULL); - assert(dataMap != NULL); - - if (compressionMethod != ZipFileRO::kCompressDeflated) { - assert(false); - return UNKNOWN_ERROR; - } - - mMap = dataMap; - mStart = -1; // not used - mCompressedLen = dataMap->getDataLength(); - mUncompressedLen = uncompressedLen; - assert(mOffset == 0); - - return NO_ERROR; -} - -/* - * Read data from a chunk of compressed data. - * - * [For now, that's just copying data out of a buffer.] - */ -ssize_t _CompressedAsset::read(void* buf, size_t count) -{ - size_t maxLen; - size_t actual; - - assert(mOffset >= 0 && mOffset <= mUncompressedLen); - - // TODO: if mAccessMode == ACCESS_STREAMING, use zlib more cleverly - - if (mBuf == NULL) { - if (getBuffer(false) == NULL) - return -1; - } - assert(mBuf != NULL); - - /* adjust count if we're near EOF */ - maxLen = mUncompressedLen - mOffset; - if (count > maxLen) - count = maxLen; - - if (!count) - return 0; - - /* copy from buffer */ - //printf("comp buf read\n"); - memcpy(buf, (char*)mBuf + mOffset, count); - actual = count; - - mOffset += actual; - return actual; -} - -/* - * Handle a seek request. - * - * If we're working in a streaming mode, this is going to be fairly - * expensive, because it requires plowing through a bunch of compressed - * data. - */ -off_t _CompressedAsset::seek(off_t offset, int whence) -{ - off_t newPosn; - - // compute new position within chunk - newPosn = handleSeek(offset, whence, mOffset, mUncompressedLen); - if (newPosn == (off_t) -1) - return newPosn; - - mOffset = newPosn; - return mOffset; -} - -/* - * Close the asset. - */ -void _CompressedAsset::close(void) -{ - if (mMap != NULL) { - mMap->release(); - mMap = NULL; - } - if (mBuf != NULL) { - delete[] mBuf; - mBuf = NULL; - } - - if (mFd > 0) { - ::close(mFd); - mFd = -1; - } -} - -/* - * Get a pointer to a read-only buffer of data. - * - * The first time this is called, we expand the compressed data into a - * buffer. - */ -const void* _CompressedAsset::getBuffer(bool wordAligned) -{ - unsigned char* buf = NULL; - - if (mBuf != NULL) - return mBuf; - - if (mUncompressedLen > UNCOMPRESS_DATA_MAX) { - LOGD("Data exceeds UNCOMPRESS_DATA_MAX (%ld vs %d)\n", - (long) mUncompressedLen, UNCOMPRESS_DATA_MAX); - goto bail; - } - - /* - * Allocate a buffer and read the file into it. - */ - buf = new unsigned char[mUncompressedLen]; - if (buf == NULL) { - LOGW("alloc %ld bytes failed\n", (long) mUncompressedLen); - goto bail; - } - - if (mMap != NULL) { - if (!ZipFileRO::inflateBuffer(buf, mMap->getDataPtr(), - mUncompressedLen, mCompressedLen)) - goto bail; - } else { - assert(mFd >= 0); - - /* - * Seek to the start of the compressed data. - */ - if (lseek(mFd, mStart, SEEK_SET) != mStart) - goto bail; - - /* - * Expand the data into it. - */ - if (!ZipUtils::inflateToBuffer(mFd, buf, mUncompressedLen, - mCompressedLen)) - goto bail; - } - - /* success! */ - mBuf = buf; - buf = NULL; - -bail: - delete[] buf; - return mBuf; -} - diff --git a/libs/utils/AssetDir.cpp b/libs/utils/AssetDir.cpp deleted file mode 100644 index c5f664ecc..000000000 --- a/libs/utils/AssetDir.cpp +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2006 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. - */ - -// -// Provide access to a virtual directory in "asset space". Most of the -// implementation is in the header file or in friend functions in -// AssetManager. -// -#include - -using namespace android; - - -/* - * Find a matching entry in a vector of FileInfo. Because it's sorted, we - * can use a binary search. - * - * Assumes the vector is sorted in ascending order. - */ -/*static*/ int AssetDir::FileInfo::findEntry(const SortedVector* pVector, - const String8& fileName) -{ - FileInfo tmpInfo; - - tmpInfo.setFileName(fileName); - return pVector->indexOf(tmpInfo); - -#if 0 // don't need this after all (uses 1/2 compares of SortedVector though) - int lo, hi, cur; - - lo = 0; - hi = pVector->size() -1; - while (lo <= hi) { - int cmp; - - cur = (hi + lo) / 2; - cmp = strcmp(pVector->itemAt(cur).getFileName(), fileName); - if (cmp == 0) { - /* match, bail */ - return cur; - } else if (cmp < 0) { - /* too low */ - lo = cur + 1; - } else { - /* too high */ - hi = cur -1; - } - } - - return -1; -#endif -} - diff --git a/libs/utils/AssetManager.cpp b/libs/utils/AssetManager.cpp deleted file mode 100644 index 447b80193..000000000 --- a/libs/utils/AssetManager.cpp +++ /dev/null @@ -1,1637 +0,0 @@ -/* - * Copyright (C) 2006 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. - */ - -// -// Provide access to read-only assets. -// - -#define LOG_TAG "asset" -//#define LOG_NDEBUG 0 - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -using namespace android; - -/* - * Names for default app, locale, and vendor. We might want to change - * these to be an actual locale, e.g. always use en-US as the default. - */ -static const char* kDefaultLocale = "default"; -static const char* kDefaultVendor = "default"; -static const char* kAssetsRoot = "assets"; -static const char* kAppZipName = NULL; //"classes.jar"; -static const char* kSystemAssets = "framework/framework-res.apk"; - -static const char* kExcludeExtension = ".EXCLUDE"; - -static Asset* const kExcludedAsset = (Asset*) 0xd000000d; - -static volatile int32_t gCount = 0; - - -/* - * =========================================================================== - * AssetManager - * =========================================================================== - */ - -int32_t AssetManager::getGlobalCount() -{ - return gCount; -} - -AssetManager::AssetManager(CacheMode cacheMode) - : mLocale(NULL), mVendor(NULL), - mResources(NULL), mConfig(new ResTable_config), - mCacheMode(cacheMode), mCacheValid(false) -{ - int count = android_atomic_inc(&gCount)+1; - //LOGI("Creating AssetManager %p #%d\n", this, count); - memset(mConfig, 0, sizeof(ResTable_config)); -} - -AssetManager::~AssetManager(void) -{ - int count = android_atomic_dec(&gCount); - //LOGI("Destroying AssetManager in %p #%d\n", this, count); - - delete mConfig; - delete mResources; - - // don't have a String class yet, so make sure we clean up - delete[] mLocale; - delete[] mVendor; -} - -bool AssetManager::addAssetPath(const String8& path, void** cookie) -{ - AutoMutex _l(mLock); - - asset_path ap; - - String8 realPath(path); - if (kAppZipName) { - realPath.appendPath(kAppZipName); - } - ap.type = ::getFileType(realPath.string()); - if (ap.type == kFileTypeRegular) { - ap.path = realPath; - } else { - ap.path = path; - ap.type = ::getFileType(path.string()); - if (ap.type != kFileTypeDirectory && ap.type != kFileTypeRegular) { - LOGW("Asset path %s is neither a directory nor file (type=%d).", - path.string(), (int)ap.type); - return false; - } - } - - // Skip if we have it already. - for (size_t i=0; i mAssetPaths.size() ? NULL : (void*)next; -} - -String8 AssetManager::getAssetPath(void* cookie) const -{ - AutoMutex _l(mLock); - const size_t which = ((size_t)cookie)-1; - if (which < mAssetPaths.size()) { - return mAssetPaths[which].path; - } - return String8(); -} - -/* - * Set the current locale. Use NULL to indicate no locale. - * - * Close and reopen Zip archives as appropriate, and reset cached - * information in the locale-specific sections of the tree. - */ -void AssetManager::setLocale(const char* locale) -{ - AutoMutex _l(mLock); - setLocaleLocked(locale); -} - -void AssetManager::setLocaleLocked(const char* locale) -{ - if (mLocale != NULL) { - /* previously set, purge cached data */ - purgeFileNameCacheLocked(); - //mZipSet.purgeLocale(); - delete[] mLocale; - } - mLocale = strdupNew(locale); - - updateResourceParamsLocked(); -} - -/* - * Set the current vendor. Use NULL to indicate no vendor. - * - * Close and reopen Zip archives as appropriate, and reset cached - * information in the vendor-specific sections of the tree. - */ -void AssetManager::setVendor(const char* vendor) -{ - AutoMutex _l(mLock); - - if (mVendor != NULL) { - /* previously set, purge cached data */ - purgeFileNameCacheLocked(); - //mZipSet.purgeVendor(); - delete[] mVendor; - } - mVendor = strdupNew(vendor); -} - -void AssetManager::setConfiguration(const ResTable_config& config, const char* locale) -{ - AutoMutex _l(mLock); - *mConfig = config; - if (locale) { - setLocaleLocked(locale); - } else if (config.language[0] != 0) { - char spec[9]; - spec[0] = config.language[0]; - spec[1] = config.language[1]; - if (config.country[0] != 0) { - spec[2] = '_'; - spec[3] = config.country[0]; - spec[4] = config.country[1]; - spec[5] = 0; - } else { - spec[3] = 0; - } - setLocaleLocked(spec); - } else { - updateResourceParamsLocked(); - } -} - -/* - * Open an asset. - * - * The data could be; - * - In a file on disk (assetBase + fileName). - * - In a compressed file on disk (assetBase + fileName.gz). - * - In a Zip archive, uncompressed or compressed. - * - * It can be in a number of different directories and Zip archives. - * The search order is: - * - [appname] - * - locale + vendor - * - "default" + vendor - * - locale + "default" - * - "default + "default" - * - "common" - * - (same as above) - * - * To find a particular file, we have to try up to eight paths with - * all three forms of data. - * - * We should probably reject requests for "illegal" filenames, e.g. those - * with illegal characters or "../" backward relative paths. - */ -Asset* AssetManager::open(const char* fileName, AccessMode mode) -{ - AutoMutex _l(mLock); - - LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager"); - - - if (mCacheMode != CACHE_OFF && !mCacheValid) - loadFileNameCacheLocked(); - - String8 assetName(kAssetsRoot); - assetName.appendPath(fileName); - - /* - * For each top-level asset path, search for the asset. - */ - - size_t i = mAssetPaths.size(); - while (i > 0) { - i--; - LOGV("Looking for asset '%s' in '%s'\n", - assetName.string(), mAssetPaths.itemAt(i).path.string()); - Asset* pAsset = openNonAssetInPathLocked(assetName.string(), mode, mAssetPaths.itemAt(i)); - if (pAsset != NULL) { - return pAsset != kExcludedAsset ? pAsset : NULL; - } - } - - return NULL; -} - -/* - * Open a non-asset file as if it were an asset. - * - * The "fileName" is the partial path starting from the application - * name. - */ -Asset* AssetManager::openNonAsset(const char* fileName, AccessMode mode) -{ - AutoMutex _l(mLock); - - LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager"); - - - if (mCacheMode != CACHE_OFF && !mCacheValid) - loadFileNameCacheLocked(); - - /* - * For each top-level asset path, search for the asset. - */ - - size_t i = mAssetPaths.size(); - while (i > 0) { - i--; - LOGV("Looking for non-asset '%s' in '%s'\n", fileName, mAssetPaths.itemAt(i).path.string()); - Asset* pAsset = openNonAssetInPathLocked( - fileName, mode, mAssetPaths.itemAt(i)); - if (pAsset != NULL) { - return pAsset != kExcludedAsset ? pAsset : NULL; - } - } - - return NULL; -} - -Asset* AssetManager::openNonAsset(void* cookie, const char* fileName, AccessMode mode) -{ - const size_t which = ((size_t)cookie)-1; - - AutoMutex _l(mLock); - - LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager"); - - - if (mCacheMode != CACHE_OFF && !mCacheValid) - loadFileNameCacheLocked(); - - if (which < mAssetPaths.size()) { - LOGV("Looking for non-asset '%s' in '%s'\n", fileName, - mAssetPaths.itemAt(which).path.string()); - Asset* pAsset = openNonAssetInPathLocked( - fileName, mode, mAssetPaths.itemAt(which)); - if (pAsset != NULL) { - return pAsset != kExcludedAsset ? pAsset : NULL; - } - } - - return NULL; -} - -/* - * Get the type of a file in the asset namespace. - * - * This currently only works for regular files. All others (including - * directories) will return kFileTypeNonexistent. - */ -FileType AssetManager::getFileType(const char* fileName) -{ - Asset* pAsset = NULL; - - /* - * Open the asset. This is less efficient than simply finding the - * file, but it's not too bad (we don't uncompress or mmap data until - * the first read() call). - */ - pAsset = open(fileName, Asset::ACCESS_STREAMING); - delete pAsset; - - if (pAsset == NULL) - return kFileTypeNonexistent; - else - return kFileTypeRegular; -} - -const ResTable* AssetManager::getResTable(bool required) const -{ - ResTable* rt = mResources; - if (rt) { - return rt; - } - - // Iterate through all asset packages, collecting resources from each. - - AutoMutex _l(mLock); - - if (mResources != NULL) { - return mResources; - } - - if (required) { - LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager"); - } - - if (mCacheMode != CACHE_OFF && !mCacheValid) - const_cast(this)->loadFileNameCacheLocked(); - - const size_t N = mAssetPaths.size(); - for (size_t i=0; i(this)-> - mZipSet.getZipResourceTable(ap.path); - if (ass == NULL) { - LOGV("loading resource table %s\n", ap.path.string()); - ass = const_cast(this)-> - openNonAssetInPathLocked("resources.arsc", - Asset::ACCESS_BUFFER, - ap); - if (ass != NULL && ass != kExcludedAsset) { - ass = const_cast(this)-> - mZipSet.setZipResourceTable(ap.path, ass); - } - } - } else { - LOGV("loading resource table %s\n", ap.path.string()); - Asset* ass = const_cast(this)-> - openNonAssetInPathLocked("resources.arsc", - Asset::ACCESS_BUFFER, - ap); - shared = false; - } - if (ass != NULL && ass != kExcludedAsset) { - if (rt == NULL) { - mResources = rt = new ResTable(); - updateResourceParamsLocked(); - } - LOGV("Installing resource asset %p in to table %p\n", ass, mResources); - rt->add(ass, (void*)(i+1), !shared); - - if (!shared) { - delete ass; - } - } - } - - if (required && !rt) LOGW("Unable to find resources file resources.arsc"); - if (!rt) { - mResources = rt = new ResTable(); - } - return rt; -} - -void AssetManager::updateResourceParamsLocked() const -{ - ResTable* res = mResources; - if (!res) { - return; - } - - size_t llen = mLocale ? strlen(mLocale) : 0; - mConfig->language[0] = 0; - mConfig->language[1] = 0; - mConfig->country[0] = 0; - mConfig->country[1] = 0; - if (llen >= 2) { - mConfig->language[0] = mLocale[0]; - mConfig->language[1] = mLocale[1]; - } - if (llen >= 5) { - mConfig->country[0] = mLocale[3]; - mConfig->country[1] = mLocale[4]; - } - mConfig->size = sizeof(*mConfig); - - res->setParameters(mConfig); -} - -const ResTable& AssetManager::getResources(bool required) const -{ - const ResTable* rt = getResTable(required); - return *rt; -} - -bool AssetManager::isUpToDate() -{ - AutoMutex _l(mLock); - return mZipSet.isUpToDate(); -} - -void AssetManager::getLocales(Vector* locales) const -{ - ResTable* res = mResources; - if (res != NULL) { - res->getLocales(locales); - } -} - -/* - * Open a non-asset file as if it were an asset, searching for it in the - * specified app. - * - * Pass in a NULL values for "appName" if the common app directory should - * be used. - */ -Asset* AssetManager::openNonAssetInPathLocked(const char* fileName, AccessMode mode, - const asset_path& ap) -{ - Asset* pAsset = NULL; - - /* look at the filesystem on disk */ - if (ap.type == kFileTypeDirectory) { - String8 path(ap.path); - path.appendPath(fileName); - - pAsset = openAssetFromFileLocked(path, mode); - - if (pAsset == NULL) { - /* try again, this time with ".gz" */ - path.append(".gz"); - pAsset = openAssetFromFileLocked(path, mode); - } - - if (pAsset != NULL) { - //printf("FOUND NA '%s' on disk\n", fileName); - pAsset->setAssetSource(path); - } - - /* look inside the zip file */ - } else { - String8 path(fileName); - - /* check the appropriate Zip file */ - ZipFileRO* pZip; - ZipEntryRO entry; - - pZip = getZipFileLocked(ap); - if (pZip != NULL) { - //printf("GOT zip, checking NA '%s'\n", (const char*) path); - entry = pZip->findEntryByName(path.string()); - if (entry != NULL) { - //printf("FOUND NA in Zip file for %s\n", appName ? appName : kAppCommon); - pAsset = openAssetFromZipLocked(pZip, entry, mode, path); - } - } - - if (pAsset != NULL) { - /* create a "source" name, for debug/display */ - pAsset->setAssetSource( - createZipSourceNameLocked(ZipSet::getPathName(ap.path.string()), String8(""), - String8(fileName))); - } - } - - return pAsset; -} - -/* - * Open an asset, searching for it in the directory hierarchy for the - * specified app. - * - * Pass in a NULL values for "appName" if the common app directory should - * be used. - */ -Asset* AssetManager::openInPathLocked(const char* fileName, AccessMode mode, - const asset_path& ap) -{ - Asset* pAsset = NULL; - - /* - * Try various combinations of locale and vendor. - */ - if (mLocale != NULL && mVendor != NULL) - pAsset = openInLocaleVendorLocked(fileName, mode, ap, mLocale, mVendor); - if (pAsset == NULL && mVendor != NULL) - pAsset = openInLocaleVendorLocked(fileName, mode, ap, NULL, mVendor); - if (pAsset == NULL && mLocale != NULL) - pAsset = openInLocaleVendorLocked(fileName, mode, ap, mLocale, NULL); - if (pAsset == NULL) - pAsset = openInLocaleVendorLocked(fileName, mode, ap, NULL, NULL); - - return pAsset; -} - -/* - * Open an asset, searching for it in the directory hierarchy for the - * specified locale and vendor. - * - * We also search in "app.jar". - * - * Pass in NULL values for "appName", "locale", and "vendor" if the - * defaults should be used. - */ -Asset* AssetManager::openInLocaleVendorLocked(const char* fileName, AccessMode mode, - const asset_path& ap, const char* locale, const char* vendor) -{ - Asset* pAsset = NULL; - - if (ap.type == kFileTypeDirectory) { - if (mCacheMode == CACHE_OFF) { - /* look at the filesystem on disk */ - String8 path(createPathNameLocked(ap, locale, vendor)); - path.appendPath(fileName); - - String8 excludeName(path); - excludeName.append(kExcludeExtension); - if (::getFileType(excludeName.string()) != kFileTypeNonexistent) { - /* say no more */ - //printf("+++ excluding '%s'\n", (const char*) excludeName); - return kExcludedAsset; - } - - pAsset = openAssetFromFileLocked(path, mode); - - if (pAsset == NULL) { - /* try again, this time with ".gz" */ - path.append(".gz"); - pAsset = openAssetFromFileLocked(path, mode); - } - - if (pAsset != NULL) - pAsset->setAssetSource(path); - } else { - /* find in cache */ - String8 path(createPathNameLocked(ap, locale, vendor)); - path.appendPath(fileName); - - AssetDir::FileInfo tmpInfo; - bool found = false; - - String8 excludeName(path); - excludeName.append(kExcludeExtension); - - if (mCache.indexOf(excludeName) != NAME_NOT_FOUND) { - /* go no farther */ - //printf("+++ Excluding '%s'\n", (const char*) excludeName); - return kExcludedAsset; - } - - /* - * File compression extensions (".gz") don't get stored in the - * name cache, so we have to try both here. - */ - if (mCache.indexOf(path) != NAME_NOT_FOUND) { - found = true; - pAsset = openAssetFromFileLocked(path, mode); - if (pAsset == NULL) { - /* try again, this time with ".gz" */ - path.append(".gz"); - pAsset = openAssetFromFileLocked(path, mode); - } - } - - if (pAsset != NULL) - pAsset->setAssetSource(path); - - /* - * Don't continue the search into the Zip files. Our cached info - * said it was a file on disk; to be consistent with openDir() - * we want to return the loose asset. If the cached file gets - * removed, we fail. - * - * The alternative is to update our cache when files get deleted, - * or make some sort of "best effort" promise, but for now I'm - * taking the hard line. - */ - if (found) { - if (pAsset == NULL) - LOGD("Expected file not found: '%s'\n", path.string()); - return pAsset; - } - } - } - - /* - * Either it wasn't found on disk or on the cached view of the disk. - * Dig through the currently-opened set of Zip files. If caching - * is disabled, the Zip file may get reopened. - */ - if (pAsset == NULL && ap.type == kFileTypeRegular) { - String8 path; - - path.appendPath((locale != NULL) ? locale : kDefaultLocale); - path.appendPath((vendor != NULL) ? vendor : kDefaultVendor); - path.appendPath(fileName); - - /* check the appropriate Zip file */ - ZipFileRO* pZip; - ZipEntryRO entry; - - pZip = getZipFileLocked(ap); - if (pZip != NULL) { - //printf("GOT zip, checking '%s'\n", (const char*) path); - entry = pZip->findEntryByName(path.string()); - if (entry != NULL) { - //printf("FOUND in Zip file for %s/%s-%s\n", - // appName, locale, vendor); - pAsset = openAssetFromZipLocked(pZip, entry, mode, path); - } - } - - if (pAsset != NULL) { - /* create a "source" name, for debug/display */ - pAsset->setAssetSource(createZipSourceNameLocked(ZipSet::getPathName(ap.path.string()), - String8(""), String8(fileName))); - } - } - - return pAsset; -} - -/* - * Create a "source name" for a file from a Zip archive. - */ -String8 AssetManager::createZipSourceNameLocked(const String8& zipFileName, - const String8& dirName, const String8& fileName) -{ - String8 sourceName("zip:"); - sourceName.append(zipFileName); - sourceName.append(":"); - if (dirName.length() > 0) { - sourceName.appendPath(dirName); - } - sourceName.appendPath(fileName); - return sourceName; -} - -/* - * Create a path to a loose asset (asset-base/app/locale/vendor). - */ -String8 AssetManager::createPathNameLocked(const asset_path& ap, const char* locale, - const char* vendor) -{ - String8 path(ap.path); - path.appendPath((locale != NULL) ? locale : kDefaultLocale); - path.appendPath((vendor != NULL) ? vendor : kDefaultVendor); - return path; -} - -/* - * Create a path to a loose asset (asset-base/app/rootDir). - */ -String8 AssetManager::createPathNameLocked(const asset_path& ap, const char* rootDir) -{ - String8 path(ap.path); - if (rootDir != NULL) path.appendPath(rootDir); - return path; -} - -/* - * Return a pointer to one of our open Zip archives. Returns NULL if no - * matching Zip file exists. - * - * Right now we have 2 possible Zip files (1 each in app/"common"). - * - * If caching is set to CACHE_OFF, to get the expected behavior we - * need to reopen the Zip file on every request. That would be silly - * and expensive, so instead we just check the file modification date. - * - * Pass in NULL values for "appName", "locale", and "vendor" if the - * generics should be used. - */ -ZipFileRO* AssetManager::getZipFileLocked(const asset_path& ap) -{ - LOGV("getZipFileLocked() in %p\n", this); - - return mZipSet.getZip(ap.path); -} - -/* - * Try to open an asset from a file on disk. - * - * If the file is compressed with gzip, we seek to the start of the - * deflated data and pass that in (just like we would for a Zip archive). - * - * For uncompressed data, we may already have an mmap()ed version sitting - * around. If so, we want to hand that to the Asset instead. - * - * This returns NULL if the file doesn't exist, couldn't be opened, or - * claims to be a ".gz" but isn't. - */ -Asset* AssetManager::openAssetFromFileLocked(const String8& pathName, - AccessMode mode) -{ - Asset* pAsset = NULL; - - if (strcasecmp(pathName.getPathExtension().string(), ".gz") == 0) { - //printf("TRYING '%s'\n", (const char*) pathName); - pAsset = Asset::createFromCompressedFile(pathName.string(), mode); - } else { - //printf("TRYING '%s'\n", (const char*) pathName); - pAsset = Asset::createFromFile(pathName.string(), mode); - } - - return pAsset; -} - -/* - * Given an entry in a Zip archive, create a new Asset object. - * - * If the entry is uncompressed, we may want to create or share a - * slice of shared memory. - */ -Asset* AssetManager::openAssetFromZipLocked(const ZipFileRO* pZipFile, - const ZipEntryRO entry, AccessMode mode, const String8& entryName) -{ - Asset* pAsset = NULL; - - // TODO: look for previously-created shared memory slice? - int method; - long uncompressedLen; - - //printf("USING Zip '%s'\n", pEntry->getFileName()); - - //pZipFile->getEntryInfo(entry, &method, &uncompressedLen, &compressedLen, - // &offset); - if (!pZipFile->getEntryInfo(entry, &method, &uncompressedLen, NULL, NULL, - NULL, NULL)) - { - LOGW("getEntryInfo failed\n"); - return NULL; - } - - FileMap* dataMap = pZipFile->createEntryFileMap(entry); - if (dataMap == NULL) { - LOGW("create map from entry failed\n"); - return NULL; - } - - if (method == ZipFileRO::kCompressStored) { - pAsset = Asset::createFromUncompressedMap(dataMap, mode); - LOGV("Opened uncompressed entry %s in zip %s mode %d: %p", entryName.string(), - dataMap->getFileName(), mode, pAsset); - } else { - pAsset = Asset::createFromCompressedMap(dataMap, method, - uncompressedLen, mode); - LOGV("Opened compressed entry %s in zip %s mode %d: %p", entryName.string(), - dataMap->getFileName(), mode, pAsset); - } - if (pAsset == NULL) { - /* unexpected */ - LOGW("create from segment failed\n"); - } - - return pAsset; -} - - - -/* - * Open a directory in the asset namespace. - * - * An "asset directory" is simply the combination of all files in all - * locations, with ".gz" stripped for loose files. With app, locale, and - * vendor defined, we have 8 directories and 2 Zip archives to scan. - * - * Pass in "" for the root dir. - */ -AssetDir* AssetManager::openDir(const char* dirName) -{ - AutoMutex _l(mLock); - - AssetDir* pDir = NULL; - SortedVector* pMergedInfo = NULL; - - LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager"); - assert(dirName != NULL); - - //printf("+++ openDir(%s) in '%s'\n", dirName, (const char*) mAssetBase); - - if (mCacheMode != CACHE_OFF && !mCacheValid) - loadFileNameCacheLocked(); - - pDir = new AssetDir; - - /* - * Scan the various directories, merging what we find into a single - * vector. We want to scan them in reverse priority order so that - * the ".EXCLUDE" processing works correctly. Also, if we decide we - * want to remember where the file is coming from, we'll get the right - * version. - * - * We start with Zip archives, then do loose files. - */ - pMergedInfo = new SortedVector; - - size_t i = mAssetPaths.size(); - while (i > 0) { - i--; - const asset_path& ap = mAssetPaths.itemAt(i); - if (ap.type == kFileTypeRegular) { - LOGV("Adding directory %s from zip %s", dirName, ap.path.string()); - scanAndMergeZipLocked(pMergedInfo, ap, kAssetsRoot, dirName); - } else { - LOGV("Adding directory %s from dir %s", dirName, ap.path.string()); - scanAndMergeDirLocked(pMergedInfo, ap, kAssetsRoot, dirName); - } - } - -#if 0 - printf("FILE LIST:\n"); - for (i = 0; i < (size_t) pMergedInfo->size(); i++) { - printf(" %d: (%d) '%s'\n", i, - pMergedInfo->itemAt(i).getFileType(), - (const char*) pMergedInfo->itemAt(i).getFileName()); - } -#endif - - pDir->setFileList(pMergedInfo); - return pDir; -} - -/* - * Scan the contents of the specified directory and merge them into the - * "pMergedInfo" vector, removing previous entries if we find "exclude" - * directives. - * - * Returns "false" if we found nothing to contribute. - */ -bool AssetManager::scanAndMergeDirLocked(SortedVector* pMergedInfo, - const asset_path& ap, const char* rootDir, const char* dirName) -{ - SortedVector* pContents; - String8 path; - - assert(pMergedInfo != NULL); - - //printf("scanAndMergeDir: %s %s %s %s\n", appName, locale, vendor,dirName); - - if (mCacheValid) { - int i, start, count; - - pContents = new SortedVector; - - /* - * Get the basic partial path and find it in the cache. That's - * the start point for the search. - */ - path = createPathNameLocked(ap, rootDir); - if (dirName[0] != '\0') - path.appendPath(dirName); - - start = mCache.indexOf(path); - if (start == NAME_NOT_FOUND) { - //printf("+++ not found in cache: dir '%s'\n", (const char*) path); - delete pContents; - return false; - } - - /* - * The match string looks like "common/default/default/foo/bar/". - * The '/' on the end ensures that we don't match on the directory - * itself or on ".../foo/barfy/". - */ - path.append("/"); - - count = mCache.size(); - - /* - * Pick out the stuff in the current dir by examining the pathname. - * It needs to match the partial pathname prefix, and not have a '/' - * (fssep) anywhere after the prefix. - */ - for (i = start+1; i < count; i++) { - if (mCache[i].getFileName().length() > path.length() && - strncmp(mCache[i].getFileName().string(), path.string(), path.length()) == 0) - { - const char* name = mCache[i].getFileName().string(); - // XXX THIS IS BROKEN! Looks like we need to store the full - // path prefix separately from the file path. - if (strchr(name + path.length(), '/') == NULL) { - /* grab it, reducing path to just the filename component */ - AssetDir::FileInfo tmp = mCache[i]; - tmp.setFileName(tmp.getFileName().getPathLeaf()); - pContents->add(tmp); - } - } else { - /* no longer in the dir or its subdirs */ - break; - } - - } - } else { - path = createPathNameLocked(ap, rootDir); - if (dirName[0] != '\0') - path.appendPath(dirName); - pContents = scanDirLocked(path); - if (pContents == NULL) - return false; - } - - // if we wanted to do an incremental cache fill, we would do it here - - /* - * Process "exclude" directives. If we find a filename that ends with - * ".EXCLUDE", we look for a matching entry in the "merged" set, and - * remove it if we find it. We also delete the "exclude" entry. - */ - int i, count, exclExtLen; - - count = pContents->size(); - exclExtLen = strlen(kExcludeExtension); - for (i = 0; i < count; i++) { - const char* name; - int nameLen; - - name = pContents->itemAt(i).getFileName().string(); - nameLen = strlen(name); - if (nameLen > exclExtLen && - strcmp(name + (nameLen - exclExtLen), kExcludeExtension) == 0) - { - String8 match(name, nameLen - exclExtLen); - int matchIdx; - - matchIdx = AssetDir::FileInfo::findEntry(pMergedInfo, match); - if (matchIdx > 0) { - LOGV("Excluding '%s' [%s]\n", - pMergedInfo->itemAt(matchIdx).getFileName().string(), - pMergedInfo->itemAt(matchIdx).getSourceName().string()); - pMergedInfo->removeAt(matchIdx); - } else { - //printf("+++ no match on '%s'\n", (const char*) match); - } - - LOGD("HEY: size=%d removing %d\n", (int)pContents->size(), i); - pContents->removeAt(i); - i--; // adjust "for" loop - count--; // and loop limit - } - } - - mergeInfoLocked(pMergedInfo, pContents); - - delete pContents; - - return true; -} - -/* - * Scan the contents of the specified directory, and stuff what we find - * into a newly-allocated vector. - * - * Files ending in ".gz" will have their extensions removed. - * - * We should probably think about skipping files with "illegal" names, - * e.g. illegal characters (/\:) or excessive length. - * - * Returns NULL if the specified directory doesn't exist. - */ -SortedVector* AssetManager::scanDirLocked(const String8& path) -{ - SortedVector* pContents = NULL; - DIR* dir; - struct dirent* entry; - FileType fileType; - - LOGV("Scanning dir '%s'\n", path.string()); - - dir = opendir(path.string()); - if (dir == NULL) - return NULL; - - pContents = new SortedVector; - - while (1) { - entry = readdir(dir); - if (entry == NULL) - break; - - if (strcmp(entry->d_name, ".") == 0 || - strcmp(entry->d_name, "..") == 0) - continue; - -#ifdef _DIRENT_HAVE_D_TYPE - if (entry->d_type == DT_REG) - fileType = kFileTypeRegular; - else if (entry->d_type == DT_DIR) - fileType = kFileTypeDirectory; - else - fileType = kFileTypeUnknown; -#else - // stat the file - fileType = ::getFileType(path.appendPathCopy(entry->d_name).string()); -#endif - - if (fileType != kFileTypeRegular && fileType != kFileTypeDirectory) - continue; - - AssetDir::FileInfo info; - info.set(String8(entry->d_name), fileType); - if (strcasecmp(info.getFileName().getPathExtension().string(), ".gz") == 0) - info.setFileName(info.getFileName().getBasePath()); - info.setSourceName(path.appendPathCopy(info.getFileName())); - pContents->add(info); - } - - closedir(dir); - return pContents; -} - -/* - * Scan the contents out of the specified Zip archive, and merge what we - * find into "pMergedInfo". If the Zip archive in question doesn't exist, - * we return immediately. - * - * Returns "false" if we found nothing to contribute. - */ -bool AssetManager::scanAndMergeZipLocked(SortedVector* pMergedInfo, - const asset_path& ap, const char* rootDir, const char* baseDirName) -{ - ZipFileRO* pZip; - Vector dirs; - AssetDir::FileInfo info; - SortedVector contents; - String8 sourceName, zipName, dirName; - - pZip = mZipSet.getZip(ap.path); - if (pZip == NULL) { - LOGW("Failure opening zip %s\n", ap.path.string()); - return false; - } - - zipName = ZipSet::getPathName(ap.path.string()); - - /* convert "sounds" to "rootDir/sounds" */ - if (rootDir != NULL) dirName = rootDir; - dirName.appendPath(baseDirName); - - /* - * Scan through the list of files, looking for a match. The files in - * the Zip table of contents are not in sorted order, so we have to - * process the entire list. We're looking for a string that begins - * with the characters in "dirName", is followed by a '/', and has no - * subsequent '/' in the stuff that follows. - * - * What makes this especially fun is that directories are not stored - * explicitly in Zip archives, so we have to infer them from context. - * When we see "sounds/foo.wav" we have to leave a note to ourselves - * to insert a directory called "sounds" into the list. We store - * these in temporary vector so that we only return each one once. - * - * Name comparisons are case-sensitive to match UNIX filesystem - * semantics. - */ - int dirNameLen = dirName.length(); - for (int i = 0; i < pZip->getNumEntries(); i++) { - ZipEntryRO entry; - char nameBuf[256]; - - entry = pZip->findEntryByIndex(i); - if (pZip->getEntryFileName(entry, nameBuf, sizeof(nameBuf)) != 0) { - // TODO: fix this if we expect to have long names - LOGE("ARGH: name too long?\n"); - continue; - } - if (dirNameLen == 0 || - (strncmp(nameBuf, dirName.string(), dirNameLen) == 0 && - nameBuf[dirNameLen] == '/')) - { - const char* cp; - const char* nextSlash; - - cp = nameBuf + dirNameLen; - if (dirNameLen != 0) - cp++; // advance past the '/' - - nextSlash = strchr(cp, '/'); -//xxx this may break if there are bare directory entries - if (nextSlash == NULL) { - /* this is a file in the requested directory */ - - info.set(String8(nameBuf).getPathLeaf(), kFileTypeRegular); - - info.setSourceName( - createZipSourceNameLocked(zipName, dirName, info.getFileName())); - - contents.add(info); - //printf("FOUND: file '%s'\n", (const char*) info.mFileName); - } else { - /* this is a subdir; add it if we don't already have it*/ - String8 subdirName(cp, nextSlash - cp); - size_t j; - size_t N = dirs.size(); - - for (j = 0; j < N; j++) { - if (subdirName == dirs[j]) { - break; - } - } - if (j == N) { - dirs.add(subdirName); - } - - //printf("FOUND: dir '%s'\n", (const char*) subdirName); - } - } - } - - /* - * Add the set of unique directories. - */ - for (int i = 0; i < (int) dirs.size(); i++) { - info.set(dirs[i], kFileTypeDirectory); - info.setSourceName( - createZipSourceNameLocked(zipName, dirName, info.getFileName())); - contents.add(info); - } - - mergeInfoLocked(pMergedInfo, &contents); - - return true; -} - - -/* - * Merge two vectors of FileInfo. - * - * The merged contents will be stuffed into *pMergedInfo. - * - * If an entry for a file exists in both "pMergedInfo" and "pContents", - * we use the newer "pContents" entry. - */ -void AssetManager::mergeInfoLocked(SortedVector* pMergedInfo, - const SortedVector* pContents) -{ - /* - * Merge what we found in this directory with what we found in - * other places. - * - * Two basic approaches: - * (1) Create a new array that holds the unique values of the two - * arrays. - * (2) Take the elements from pContents and shove them into pMergedInfo. - * - * Because these are vectors of complex objects, moving elements around - * inside the vector requires constructing new objects and allocating - * storage for members. With approach #1, we're always adding to the - * end, whereas with #2 we could be inserting multiple elements at the - * front of the vector. Approach #1 requires a full copy of the - * contents of pMergedInfo, but approach #2 requires the same copy for - * every insertion at the front of pMergedInfo. - * - * (We should probably use a SortedVector interface that allows us to - * just stuff items in, trusting us to maintain the sort order.) - */ - SortedVector* pNewSorted; - int mergeMax, contMax; - int mergeIdx, contIdx; - - pNewSorted = new SortedVector; - mergeMax = pMergedInfo->size(); - contMax = pContents->size(); - mergeIdx = contIdx = 0; - - while (mergeIdx < mergeMax || contIdx < contMax) { - if (mergeIdx == mergeMax) { - /* hit end of "merge" list, copy rest of "contents" */ - pNewSorted->add(pContents->itemAt(contIdx)); - contIdx++; - } else if (contIdx == contMax) { - /* hit end of "cont" list, copy rest of "merge" */ - pNewSorted->add(pMergedInfo->itemAt(mergeIdx)); - mergeIdx++; - } else if (pMergedInfo->itemAt(mergeIdx) == pContents->itemAt(contIdx)) - { - /* items are identical, add newer and advance both indices */ - pNewSorted->add(pContents->itemAt(contIdx)); - mergeIdx++; - contIdx++; - } else if (pMergedInfo->itemAt(mergeIdx) < pContents->itemAt(contIdx)) - { - /* "merge" is lower, add that one */ - pNewSorted->add(pMergedInfo->itemAt(mergeIdx)); - mergeIdx++; - } else { - /* "cont" is lower, add that one */ - assert(pContents->itemAt(contIdx) < pMergedInfo->itemAt(mergeIdx)); - pNewSorted->add(pContents->itemAt(contIdx)); - contIdx++; - } - } - - /* - * Overwrite the "merged" list with the new stuff. - */ - *pMergedInfo = *pNewSorted; - delete pNewSorted; - -#if 0 // for Vector, rather than SortedVector - int i, j; - for (i = pContents->size() -1; i >= 0; i--) { - bool add = true; - - for (j = pMergedInfo->size() -1; j >= 0; j--) { - /* case-sensitive comparisons, to behave like UNIX fs */ - if (strcmp(pContents->itemAt(i).mFileName, - pMergedInfo->itemAt(j).mFileName) == 0) - { - /* match, don't add this entry */ - add = false; - break; - } - } - - if (add) - pMergedInfo->add(pContents->itemAt(i)); - } -#endif -} - - -/* - * Load all files into the file name cache. We want to do this across - * all combinations of { appname, locale, vendor }, performing a recursive - * directory traversal. - * - * This is not the most efficient data structure. Also, gathering the - * information as we needed it (file-by-file or directory-by-directory) - * would be faster. However, on the actual device, 99% of the files will - * live in Zip archives, so this list will be very small. The trouble - * is that we have to check the "loose" files first, so it's important - * that we don't beat the filesystem silly looking for files that aren't - * there. - * - * Note on thread safety: this is the only function that causes updates - * to mCache, and anybody who tries to use it will call here if !mCacheValid, - * so we need to employ a mutex here. - */ -void AssetManager::loadFileNameCacheLocked(void) -{ - assert(!mCacheValid); - assert(mCache.size() == 0); - -#ifdef DO_TIMINGS // need to link against -lrt for this now - DurationTimer timer; - timer.start(); -#endif - - fncScanLocked(&mCache, ""); - -#ifdef DO_TIMINGS - timer.stop(); - LOGD("Cache scan took %.3fms\n", - timer.durationUsecs() / 1000.0); -#endif - -#if 0 - int i; - printf("CACHED FILE LIST (%d entries):\n", mCache.size()); - for (i = 0; i < (int) mCache.size(); i++) { - printf(" %d: (%d) '%s'\n", i, - mCache.itemAt(i).getFileType(), - (const char*) mCache.itemAt(i).getFileName()); - } -#endif - - mCacheValid = true; -} - -/* - * Scan up to 8 versions of the specified directory. - */ -void AssetManager::fncScanLocked(SortedVector* pMergedInfo, - const char* dirName) -{ - size_t i = mAssetPaths.size(); - while (i > 0) { - i--; - const asset_path& ap = mAssetPaths.itemAt(i); - fncScanAndMergeDirLocked(pMergedInfo, ap, NULL, NULL, dirName); - if (mLocale != NULL) - fncScanAndMergeDirLocked(pMergedInfo, ap, mLocale, NULL, dirName); - if (mVendor != NULL) - fncScanAndMergeDirLocked(pMergedInfo, ap, NULL, mVendor, dirName); - if (mLocale != NULL && mVendor != NULL) - fncScanAndMergeDirLocked(pMergedInfo, ap, mLocale, mVendor, dirName); - } -} - -/* - * Recursively scan this directory and all subdirs. - * - * This is similar to scanAndMergeDir, but we don't remove the .EXCLUDE - * files, and we prepend the extended partial path to the filenames. - */ -bool AssetManager::fncScanAndMergeDirLocked( - SortedVector* pMergedInfo, - const asset_path& ap, const char* locale, const char* vendor, - const char* dirName) -{ - SortedVector* pContents; - String8 partialPath; - String8 fullPath; - - // XXX This is broken -- the filename cache needs to hold the base - // asset path separately from its filename. - - partialPath = createPathNameLocked(ap, locale, vendor); - if (dirName[0] != '\0') { - partialPath.appendPath(dirName); - } - - fullPath = partialPath; - pContents = scanDirLocked(fullPath); - if (pContents == NULL) { - return false; // directory did not exist - } - - /* - * Scan all subdirectories of the current dir, merging what we find - * into "pMergedInfo". - */ - for (int i = 0; i < (int) pContents->size(); i++) { - if (pContents->itemAt(i).getFileType() == kFileTypeDirectory) { - String8 subdir(dirName); - subdir.appendPath(pContents->itemAt(i).getFileName()); - - fncScanAndMergeDirLocked(pMergedInfo, ap, locale, vendor, subdir.string()); - } - } - - /* - * To be consistent, we want entries for the root directory. If - * we're the root, add one now. - */ - if (dirName[0] == '\0') { - AssetDir::FileInfo tmpInfo; - - tmpInfo.set(String8(""), kFileTypeDirectory); - tmpInfo.setSourceName(createPathNameLocked(ap, locale, vendor)); - pContents->add(tmpInfo); - } - - /* - * We want to prepend the extended partial path to every entry in - * "pContents". It's the same value for each entry, so this will - * not change the sorting order of the vector contents. - */ - for (int i = 0; i < (int) pContents->size(); i++) { - const AssetDir::FileInfo& info = pContents->itemAt(i); - pContents->editItemAt(i).setFileName(partialPath.appendPathCopy(info.getFileName())); - } - - mergeInfoLocked(pMergedInfo, pContents); - return true; -} - -/* - * Trash the cache. - */ -void AssetManager::purgeFileNameCacheLocked(void) -{ - mCacheValid = false; - mCache.clear(); -} - -/* - * =========================================================================== - * AssetManager::SharedZip - * =========================================================================== - */ - - -Mutex AssetManager::SharedZip::gLock; -DefaultKeyedVector > AssetManager::SharedZip::gOpen; - -AssetManager::SharedZip::SharedZip(const String8& path, time_t modWhen) - : mPath(path), mZipFile(NULL), mModWhen(modWhen), mResourceTableAsset(NULL) -{ - //LOGI("Creating SharedZip %p %s\n", this, (const char*)mPath); - mZipFile = new ZipFileRO; - LOGV("+++ opening zip '%s'\n", mPath.string()); - if (mZipFile->open(mPath.string()) != NO_ERROR) { - LOGD("failed to open Zip archive '%s'\n", mPath.string()); - delete mZipFile; - mZipFile = NULL; - } -} - -sp AssetManager::SharedZip::get(const String8& path) -{ - AutoMutex _l(gLock); - time_t modWhen = getFileModDate(path); - sp zip = gOpen.valueFor(path).promote(); - if (zip != NULL && zip->mModWhen == modWhen) { - return zip; - } - zip = new SharedZip(path, modWhen); - gOpen.add(path, zip); - return zip; - -} - -ZipFileRO* AssetManager::SharedZip::getZip() -{ - return mZipFile; -} - -Asset* AssetManager::SharedZip::getResourceTableAsset() -{ - LOGV("Getting from SharedZip %p resource asset %p\n", this, mResourceTableAsset); - return mResourceTableAsset; -} - -Asset* AssetManager::SharedZip::setResourceTableAsset(Asset* asset) -{ - { - AutoMutex _l(gLock); - if (mResourceTableAsset == NULL) { - mResourceTableAsset = asset; - // This is not thread safe the first time it is called, so - // do it here with the global lock held. - asset->getBuffer(true); - return asset; - } - } - delete asset; - return mResourceTableAsset; -} - -bool AssetManager::SharedZip::isUpToDate() -{ - time_t modWhen = getFileModDate(mPath.string()); - return mModWhen == modWhen; -} - -AssetManager::SharedZip::~SharedZip() -{ - //LOGI("Destroying SharedZip %p %s\n", this, (const char*)mPath); - if (mResourceTableAsset != NULL) { - delete mResourceTableAsset; - } - if (mZipFile != NULL) { - delete mZipFile; - LOGV("Closed '%s'\n", mPath.string()); - } -} - -/* - * =========================================================================== - * AssetManager::ZipSet - * =========================================================================== - */ - -/* - * Constructor. - */ -AssetManager::ZipSet::ZipSet(void) -{ -} - -/* - * Destructor. Close any open archives. - */ -AssetManager::ZipSet::~ZipSet(void) -{ - size_t N = mZipFile.size(); - for (size_t i = 0; i < N; i++) - closeZip(i); -} - -/* - * Close a Zip file and reset the entry. - */ -void AssetManager::ZipSet::closeZip(int idx) -{ - mZipFile.editItemAt(idx) = NULL; -} - - -/* - * Retrieve the appropriate Zip file from the set. - */ -ZipFileRO* AssetManager::ZipSet::getZip(const String8& path) -{ - int idx = getIndex(path); - sp zip = mZipFile[idx]; - if (zip == NULL) { - zip = SharedZip::get(path); - mZipFile.editItemAt(idx) = zip; - } - return zip->getZip(); -} - -Asset* AssetManager::ZipSet::getZipResourceTable(const String8& path) -{ - int idx = getIndex(path); - sp zip = mZipFile[idx]; - if (zip == NULL) { - zip = SharedZip::get(path); - mZipFile.editItemAt(idx) = zip; - } - return zip->getResourceTableAsset(); -} - -Asset* AssetManager::ZipSet::setZipResourceTable(const String8& path, - Asset* asset) -{ - int idx = getIndex(path); - sp zip = mZipFile[idx]; - // doesn't make sense to call before previously accessing. - return zip->setResourceTableAsset(asset); -} - -/* - * Generate the partial pathname for the specified archive. The caller - * gets to prepend the asset root directory. - * - * Returns something like "common/en-US-noogle.jar". - */ -/*static*/ String8 AssetManager::ZipSet::getPathName(const char* zipPath) -{ - return String8(zipPath); -} - -bool AssetManager::ZipSet::isUpToDate() -{ - const size_t N = mZipFile.size(); - for (size_t i=0; iisUpToDate()) { - return false; - } - } - return true; -} - -/* - * Compute the zip file's index. - * - * "appName", "locale", and "vendor" should be set to NULL to indicate the - * default directory. - */ -int AssetManager::ZipSet::getIndex(const String8& zip) const -{ - const size_t N = mZipPath.size(); - for (size_t i=0; i - -#include -#include -#include -#include - -#include - -namespace android { - -// --------------------------------------------------------------------------- - -sp IBinder::queryLocalInterface(const String16& descriptor) -{ - return NULL; -} - -BBinder* IBinder::localBinder() -{ - return NULL; -} - -BpBinder* IBinder::remoteBinder() -{ - return NULL; -} - -bool IBinder::checkSubclass(const void* /*subclassID*/) const -{ - return false; -} - -// --------------------------------------------------------------------------- - -class BBinder::Extras -{ -public: - Mutex mLock; - BpBinder::ObjectManager mObjects; -}; - -// --------------------------------------------------------------------------- - -BBinder::BBinder() - : mExtras(NULL) -{ -} - -bool BBinder::isBinderAlive() const -{ - return true; -} - -status_t BBinder::pingBinder() -{ - return NO_ERROR; -} - -String16 BBinder::getInterfaceDescriptor() const -{ - LOGW("reached BBinder::getInterfaceDescriptor (this=%p)", this); - return String16(); -} - -status_t BBinder::transact( - uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) -{ - data.setDataPosition(0); - - status_t err = NO_ERROR; - switch (code) { - case PING_TRANSACTION: - reply->writeInt32(pingBinder()); - break; - default: - err = onTransact(code, data, reply, flags); - break; - } - - if (reply != NULL) { - reply->setDataPosition(0); - } - - return err; -} - -status_t BBinder::linkToDeath( - const sp& recipient, void* cookie, uint32_t flags) -{ - return INVALID_OPERATION; -} - -status_t BBinder::unlinkToDeath( - const wp& recipient, void* cookie, uint32_t flags, - wp* outRecipient) -{ - return INVALID_OPERATION; -} - -status_t BBinder::dump(int fd, const Vector& args) -{ - return NO_ERROR; -} - -void BBinder::attachObject( - const void* objectID, void* object, void* cleanupCookie, - object_cleanup_func func) -{ - Extras* e = mExtras; - - if (!e) { - e = new Extras; - if (android_atomic_cmpxchg(0, reinterpret_cast(e), - reinterpret_cast(&mExtras)) != 0) { - delete e; - e = mExtras; - } - if (e == 0) return; // out of memory - } - - AutoMutex _l(e->mLock); - e->mObjects.attach(objectID, object, cleanupCookie, func); -} - -void* BBinder::findObject(const void* objectID) const -{ - Extras* e = mExtras; - if (!e) return NULL; - - AutoMutex _l(e->mLock); - return e->mObjects.find(objectID); -} - -void BBinder::detachObject(const void* objectID) -{ - Extras* e = mExtras; - if (!e) return; - - AutoMutex _l(e->mLock); - e->mObjects.detach(objectID); -} - -BBinder* BBinder::localBinder() -{ - return this; -} - -BBinder::~BBinder() -{ - if (mExtras) delete mExtras; -} - - -status_t BBinder::onTransact( - uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) -{ - switch (code) { - case INTERFACE_TRANSACTION: - reply->writeString16(getInterfaceDescriptor()); - return NO_ERROR; - - case DUMP_TRANSACTION: { - int fd = data.readFileDescriptor(); - int argc = data.readInt32(); - Vector args; - for (int i = 0; i < argc && data.dataAvail() > 0; i++) { - args.add(data.readString16()); - } - return dump(fd, args); - } - default: - return UNKNOWN_TRANSACTION; - } -} - -// --------------------------------------------------------------------------- - -enum { - // This is used to transfer ownership of the remote binder from - // the BpRefBase object holding it (when it is constructed), to the - // owner of the BpRefBase object when it first acquires that BpRefBase. - kRemoteAcquired = 0x00000001 -}; - -BpRefBase::BpRefBase(const sp& o) - : mRemote(o.get()), mRefs(NULL), mState(0) -{ - extendObjectLifetime(OBJECT_LIFETIME_WEAK); - - if (mRemote) { - mRemote->incStrong(this); // Removed on first IncStrong(). - mRefs = mRemote->createWeak(this); // Held for our entire lifetime. - } -} - -BpRefBase::~BpRefBase() -{ - if (mRemote) { - if (!(mState&kRemoteAcquired)) { - mRemote->decStrong(this); - } - mRefs->decWeak(this); - } -} - -void BpRefBase::onFirstRef() -{ - android_atomic_or(kRemoteAcquired, &mState); -} - -void BpRefBase::onLastStrongRef(const void* id) -{ - if (mRemote) { - mRemote->decStrong(this); - } -} - -bool BpRefBase::onIncStrongAttempted(uint32_t flags, const void* id) -{ - return mRemote ? mRefs->attemptIncStrong(this) : false; -} - -// --------------------------------------------------------------------------- - -}; // namespace android diff --git a/libs/utils/BpBinder.cpp b/libs/utils/BpBinder.cpp deleted file mode 100644 index 69ab19574..000000000 --- a/libs/utils/BpBinder.cpp +++ /dev/null @@ -1,348 +0,0 @@ -/* - * Copyright (C) 2005 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 "BpBinder" -//#define LOG_NDEBUG 0 - -#include - -#include -#include - -#include - -//#undef LOGV -//#define LOGV(...) fprintf(stderr, __VA_ARGS__) - -namespace android { - -// --------------------------------------------------------------------------- - -BpBinder::ObjectManager::ObjectManager() -{ -} - -BpBinder::ObjectManager::~ObjectManager() -{ - kill(); -} - -void BpBinder::ObjectManager::attach( - const void* objectID, void* object, void* cleanupCookie, - IBinder::object_cleanup_func func) -{ - entry_t e; - e.object = object; - e.cleanupCookie = cleanupCookie; - e.func = func; - - if (mObjects.indexOfKey(objectID) >= 0) { - LOGE("Trying to attach object ID %p to binder ObjectManager %p with object %p, but object ID already in use", - objectID, this, object); - return; - } - - mObjects.add(objectID, e); -} - -void* BpBinder::ObjectManager::find(const void* objectID) const -{ - const ssize_t i = mObjects.indexOfKey(objectID); - if (i < 0) return NULL; - return mObjects.valueAt(i).object; -} - -void BpBinder::ObjectManager::detach(const void* objectID) -{ - mObjects.removeItem(objectID); -} - -void BpBinder::ObjectManager::kill() -{ - const size_t N = mObjects.size(); - LOGV("Killing %d objects in manager %p", N, this); - for (size_t i=0; iincWeakHandle(handle); -} - -String16 BpBinder::getInterfaceDescriptor() const -{ - String16 res; - Parcel send, reply; - status_t err = const_cast(this)->transact( - INTERFACE_TRANSACTION, send, &reply); - if (err == NO_ERROR) { - res = reply.readString16(); - } - return res; -} - -bool BpBinder::isBinderAlive() const -{ - return mAlive != 0; -} - -status_t BpBinder::pingBinder() -{ - Parcel send; - Parcel reply; - status_t err = transact(PING_TRANSACTION, send, &reply); - if (err != NO_ERROR) return err; - if (reply.dataSize() < sizeof(status_t)) return NOT_ENOUGH_DATA; - return (status_t)reply.readInt32(); -} - -status_t BpBinder::dump(int fd, const Vector& args) -{ - Parcel send; - Parcel reply; - send.writeFileDescriptor(fd); - const size_t numArgs = args.size(); - send.writeInt32(numArgs); - for (size_t i = 0; i < numArgs; i++) { - send.writeString16(args[i]); - } - status_t err = transact(DUMP_TRANSACTION, send, &reply); - return err; -} - -status_t BpBinder::transact( - uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) -{ - // Once a binder has died, it will never come back to life. - if (mAlive) { - status_t status = IPCThreadState::self()->transact( - mHandle, code, data, reply, flags); - if (status == DEAD_OBJECT) mAlive = 0; - return status; - } - - return DEAD_OBJECT; -} - -status_t BpBinder::linkToDeath( - const sp& recipient, void* cookie, uint32_t flags) -{ - Obituary ob; - ob.recipient = recipient; - ob.cookie = cookie; - ob.flags = flags; - - LOG_ALWAYS_FATAL_IF(recipient == NULL, - "linkToDeath(): recipient must be non-NULL"); - - { - AutoMutex _l(mLock); - - if (!mObitsSent) { - if (!mObituaries) { - mObituaries = new Vector; - if (!mObituaries) { - return NO_MEMORY; - } - LOGV("Requesting death notification: %p handle %d\n", this, mHandle); - getWeakRefs()->incWeak(this); - IPCThreadState* self = IPCThreadState::self(); - self->requestDeathNotification(mHandle, this); - self->flushCommands(); - } - ssize_t res = mObituaries->add(ob); - return res >= (ssize_t)NO_ERROR ? (status_t)NO_ERROR : res; - } - } - - return DEAD_OBJECT; -} - -status_t BpBinder::unlinkToDeath( - const wp& recipient, void* cookie, uint32_t flags, - wp* outRecipient) -{ - AutoMutex _l(mLock); - - if (mObitsSent) { - return DEAD_OBJECT; - } - - const size_t N = mObituaries ? mObituaries->size() : 0; - for (size_t i=0; iitemAt(i); - if ((obit.recipient == recipient - || (recipient == NULL && obit.cookie == cookie)) - && obit.flags == flags) { - const uint32_t allFlags = obit.flags|flags; - if (outRecipient != NULL) { - *outRecipient = mObituaries->itemAt(i).recipient; - } - mObituaries->removeAt(i); - if (mObituaries->size() == 0) { - LOGV("Clearing death notification: %p handle %d\n", this, mHandle); - IPCThreadState* self = IPCThreadState::self(); - self->clearDeathNotification(mHandle, this); - self->flushCommands(); - delete mObituaries; - mObituaries = NULL; - } - return NO_ERROR; - } - } - - return NAME_NOT_FOUND; -} - -void BpBinder::sendObituary() -{ - LOGV("Sending obituary for proxy %p handle %d, mObitsSent=%s\n", - this, mHandle, mObitsSent ? "true" : "false"); - - mAlive = 0; - if (mObitsSent) return; - - mLock.lock(); - Vector* obits = mObituaries; - if(obits != NULL) { - LOGV("Clearing sent death notification: %p handle %d\n", this, mHandle); - IPCThreadState* self = IPCThreadState::self(); - self->clearDeathNotification(mHandle, this); - self->flushCommands(); - mObituaries = NULL; - } - mObitsSent = 1; - mLock.unlock(); - - LOGV("Reporting death of proxy %p for %d recipients\n", - this, obits ? obits->size() : 0); - - if (obits != NULL) { - const size_t N = obits->size(); - for (size_t i=0; iitemAt(i)); - } - - delete obits; - } -} - -void BpBinder::reportOneDeath(const Obituary& obit) -{ - sp recipient = obit.recipient.promote(); - LOGV("Reporting death to recipient: %p\n", recipient.get()); - if (recipient == NULL) return; - - recipient->binderDied(this); -} - - -void BpBinder::attachObject( - const void* objectID, void* object, void* cleanupCookie, - object_cleanup_func func) -{ - AutoMutex _l(mLock); - LOGV("Attaching object %p to binder %p (manager=%p)", object, this, &mObjects); - mObjects.attach(objectID, object, cleanupCookie, func); -} - -void* BpBinder::findObject(const void* objectID) const -{ - AutoMutex _l(mLock); - return mObjects.find(objectID); -} - -void BpBinder::detachObject(const void* objectID) -{ - AutoMutex _l(mLock); - mObjects.detach(objectID); -} - -BpBinder* BpBinder::remoteBinder() -{ - return this; -} - -BpBinder::~BpBinder() -{ - LOGV("Destroying BpBinder %p handle %d\n", this, mHandle); - - IPCThreadState* ipc = IPCThreadState::self(); - - mLock.lock(); - Vector* obits = mObituaries; - if(obits != NULL) { - if (ipc) ipc->clearDeathNotification(mHandle, this); - mObituaries = NULL; - } - mLock.unlock(); - - if (obits != NULL) { - // XXX Should we tell any remaining DeathRecipient - // objects that the last strong ref has gone away, so they - // are no longer linked? - delete obits; - } - - if (ipc) { - ipc->expungeHandle(mHandle, this); - ipc->decWeakHandle(mHandle); - } -} - -void BpBinder::onFirstRef() -{ - LOGV("onFirstRef BpBinder %p handle %d\n", this, mHandle); - IPCThreadState* ipc = IPCThreadState::self(); - if (ipc) ipc->incStrongHandle(mHandle); -} - -void BpBinder::onLastStrongRef(const void* id) -{ - LOGV("onLastStrongRef BpBinder %p handle %d\n", this, mHandle); - IF_LOGV() { - printRefs(); - } - IPCThreadState* ipc = IPCThreadState::self(); - if (ipc) ipc->decStrongHandle(mHandle); -} - -bool BpBinder::onIncStrongAttempted(uint32_t flags, const void* id) -{ - LOGV("onIncStrongAttempted BpBinder %p handle %d\n", this, mHandle); - IPCThreadState* ipc = IPCThreadState::self(); - return ipc ? ipc->attemptIncStrongHandle(mHandle) == NO_ERROR : false; -} - -// --------------------------------------------------------------------------- - -}; // namespace android diff --git a/libs/utils/BufferedTextOutput.cpp b/libs/utils/BufferedTextOutput.cpp deleted file mode 100644 index 989662e84..000000000 --- a/libs/utils/BufferedTextOutput.cpp +++ /dev/null @@ -1,279 +0,0 @@ -/* - * Copyright (C) 2006 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 - -#include -#include -#include -#include -#include -#include - -#include - -#include -#include - -// --------------------------------------------------------------------------- - -namespace android { - -struct BufferedTextOutput::BufferState : public RefBase -{ - BufferState(int32_t _seq) - : seq(_seq) - , buffer(NULL) - , bufferPos(0) - , bufferSize(0) - , atFront(true) - , indent(0) - , bundle(0) { - } - ~BufferState() { - free(buffer); - } - - status_t append(const char* txt, size_t len) { - if ((len+bufferPos) > bufferSize) { - void* b = realloc(buffer, ((len+bufferPos)*3)/2); - if (!b) return NO_MEMORY; - buffer = (char*)b; - } - memcpy(buffer+bufferPos, txt, len); - bufferPos += len; - return NO_ERROR; - } - - void restart() { - bufferPos = 0; - atFront = true; - if (bufferSize > 256) { - void* b = realloc(buffer, 256); - if (b) { - buffer = (char*)b; - bufferSize = 256; - } - } - } - - const int32_t seq; - char* buffer; - size_t bufferPos; - size_t bufferSize; - bool atFront; - int32_t indent; - int32_t bundle; -}; - -struct BufferedTextOutput::ThreadState -{ - Vector > states; -}; - -static mutex_t gMutex; - -static thread_store_t tls; - -BufferedTextOutput::ThreadState* BufferedTextOutput::getThreadState() -{ - ThreadState* ts = (ThreadState*) thread_store_get( &tls ); - if (ts) return ts; - ts = new ThreadState; - thread_store_set( &tls, ts, threadDestructor ); - return ts; -} - -void BufferedTextOutput::threadDestructor(void *st) -{ - delete ((ThreadState*)st); -} - -static volatile int32_t gSequence = 0; - -static volatile int32_t gFreeBufferIndex = -1; - -static int32_t allocBufferIndex() -{ - int32_t res = -1; - - mutex_lock(&gMutex); - - if (gFreeBufferIndex >= 0) { - res = gFreeBufferIndex; - gFreeBufferIndex = gTextBuffers[res]; - gTextBuffers.editItemAt(res) = -1; - - } else { - res = gTextBuffers.size(); - gTextBuffers.add(-1); - } - - mutex_unlock(&gMutex); - - return res; -} - -static void freeBufferIndex(int32_t idx) -{ - mutex_lock(&gMutex); - gTextBuffers.editItemAt(idx) = gFreeBufferIndex; - gFreeBufferIndex = idx; - mutex_unlock(&gMutex); -} - -// --------------------------------------------------------------------------- - -BufferedTextOutput::BufferedTextOutput(uint32_t flags) - : mFlags(flags) - , mSeq(android_atomic_inc(&gSequence)) - , mIndex(allocBufferIndex()) -{ - mGlobalState = new BufferState(mSeq); - if (mGlobalState) mGlobalState->incStrong(this); -} - -BufferedTextOutput::~BufferedTextOutput() -{ - if (mGlobalState) mGlobalState->decStrong(this); - freeBufferIndex(mIndex); -} - -status_t BufferedTextOutput::print(const char* txt, size_t len) -{ - //printf("BufferedTextOutput: printing %d\n", len); - - AutoMutex _l(mLock); - BufferState* b = getBuffer(); - - const char* const end = txt+len; - - status_t err; - - while (txt < end) { - // Find the next line. - const char* first = txt; - while (txt < end && *txt != '\n') txt++; - - // Include this and all following empty lines. - while (txt < end && *txt == '\n') txt++; - - // Special cases for first data on a line. - if (b->atFront) { - if (b->indent > 0) { - // If this is the start of a line, add the indent. - const char* prefix = stringForIndent(b->indent); - err = b->append(prefix, strlen(prefix)); - if (err != NO_ERROR) return err; - - } else if (*(txt-1) == '\n' && !b->bundle) { - // Fast path: if we are not indenting or bundling, and - // have been given one or more complete lines, just write - // them out without going through the buffer. - - // Slurp up all of the lines. - const char* lastLine = txt+1; - while (txt < end) { - if (*txt++ == '\n') lastLine = txt; - } - struct iovec vec; - vec.iov_base = (void*)first; - vec.iov_len = lastLine-first; - //printf("Writing %d bytes of data!\n", vec.iov_len); - writeLines(vec, 1); - txt = lastLine; - continue; - } - } - - // Append the new text to the buffer. - err = b->append(first, txt-first); - if (err != NO_ERROR) return err; - b->atFront = *(txt-1) == '\n'; - - // If we have finished a line and are not bundling, write - // it out. - //printf("Buffer is now %d bytes\n", b->bufferPos); - if (b->atFront && !b->bundle) { - struct iovec vec; - vec.iov_base = b->buffer; - vec.iov_len = b->bufferPos; - //printf("Writing %d bytes of data!\n", vec.iov_len); - writeLines(vec, 1); - b->restart(); - } - } - - return NO_ERROR; -} - -void BufferedTextOutput::moveIndent(int delta) -{ - AutoMutex _l(mLock); - BufferState* b = getBuffer(); - b->indent += delta; - if (b->indent < 0) b->indent = 0; -} - -void BufferedTextOutput::pushBundle() -{ - AutoMutex _l(mLock); - BufferState* b = getBuffer(); - b->bundle++; -} - -void BufferedTextOutput::popBundle() -{ - AutoMutex _l(mLock); - BufferState* b = getBuffer(); - b->bundle--; - LOG_FATAL_IF(b->bundle < 0, - "TextOutput::popBundle() called more times than pushBundle()"); - if (b->bundle < 0) b->bundle = 0; - - if (b->bundle == 0) { - // Last bundle, write out data if it is complete. If it is not - // complete, don't write until the last line is done... this may - // or may not be the write thing to do, but it's the easiest. - if (b->bufferPos > 0 && b->atFront) { - struct iovec vec; - vec.iov_base = b->buffer; - vec.iov_len = b->bufferPos; - writeLines(vec, 1); - b->restart(); - } - } -} - -BufferedTextOutput::BufferState* BufferedTextOutput::getBuffer() const -{ - if ((mFlags&MULTITHREADED) != 0) { - ThreadState* ts = getThreadState(); - if (ts) { - while (ts->states.size() <= (size_t)mIndex) ts->states.add(NULL); - BufferState* bs = ts->states[mIndex].get(); - if (bs != NULL && bs->seq == mSeq) return bs; - - ts->states.editItemAt(mIndex) = new BufferState(mIndex); - bs = ts->states[mIndex].get(); - if (bs != NULL) return bs; - } - } - - return mGlobalState; -} - -}; // namespace android diff --git a/libs/utils/CallStack.cpp b/libs/utils/CallStack.cpp deleted file mode 100644 index 26fb22abc..000000000 --- a/libs/utils/CallStack.cpp +++ /dev/null @@ -1,335 +0,0 @@ -/* - * 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. - */ - -#define LOG_TAG "CallStack" - -#include -#include -#include - -#if HAVE_DLADDR -#include -#endif - -#if HAVE_CXXABI -#include -#endif - -#include - -#include -#include -#include -#include - - -/*****************************************************************************/ -namespace android { - - -typedef struct { - size_t count; - size_t ignore; - const void** addrs; -} stack_crawl_state_t; - -static -_Unwind_Reason_Code trace_function(_Unwind_Context *context, void *arg) -{ - stack_crawl_state_t* state = (stack_crawl_state_t*)arg; - if (state->count) { - void* ip = (void*)_Unwind_GetIP(context); - if (ip) { - if (state->ignore) { - state->ignore--; - } else { - state->addrs[0] = ip; - state->addrs++; - state->count--; - } - } - } - return _URC_NO_REASON; -} - -static -int backtrace(const void** addrs, size_t ignore, size_t size) -{ - stack_crawl_state_t state; - state.count = size; - state.ignore = ignore; - state.addrs = addrs; - _Unwind_Backtrace(trace_function, (void*)&state); - return size - state.count; -} - -/*****************************************************************************/ - -static -const char *lookup_symbol(const void* addr, void **offset, char* name, size_t bufSize) -{ -#if HAVE_DLADDR - Dl_info info; - if (dladdr(addr, &info)) { - *offset = info.dli_saddr; - return info.dli_sname; - } -#endif - return NULL; -} - -static -int32_t linux_gcc_demangler(const char *mangled_name, char *unmangled_name, size_t buffersize) -{ - size_t out_len = 0; -#if HAVE_CXXABI - int status = 0; - char *demangled = abi::__cxa_demangle(mangled_name, 0, &out_len, &status); - if (status == 0) { - // OK - if (out_len < buffersize) memcpy(unmangled_name, demangled, out_len); - else out_len = 0; - free(demangled); - } else { - out_len = 0; - } -#endif - return out_len; -} - -/*****************************************************************************/ - -class MapInfo { - struct mapinfo { - struct mapinfo *next; - uint64_t start; - uint64_t end; - char name[]; - }; - - const char *map_to_name(uint64_t pc, const char* def) { - mapinfo* mi = getMapInfoList(); - while(mi) { - if ((pc >= mi->start) && (pc < mi->end)) - return mi->name; - mi = mi->next; - } - return def; - } - - mapinfo *parse_maps_line(char *line) { - mapinfo *mi; - int len = strlen(line); - if (len < 1) return 0; - line[--len] = 0; - if (len < 50) return 0; - if (line[20] != 'x') return 0; - mi = (mapinfo*)malloc(sizeof(mapinfo) + (len - 47)); - if (mi == 0) return 0; - mi->start = strtoull(line, 0, 16); - mi->end = strtoull(line + 9, 0, 16); - mi->next = 0; - strcpy(mi->name, line + 49); - return mi; - } - - mapinfo* getMapInfoList() { - Mutex::Autolock _l(mLock); - if (milist == 0) { - char data[1024]; - FILE *fp; - sprintf(data, "/proc/%d/maps", getpid()); - fp = fopen(data, "r"); - if (fp) { - while(fgets(data, 1024, fp)) { - mapinfo *mi = parse_maps_line(data); - if(mi) { - mi->next = milist; - milist = mi; - } - } - fclose(fp); - } - } - return milist; - } - mapinfo* milist; - Mutex mLock; - static MapInfo sMapInfo; - -public: - MapInfo() - : milist(0) { - } - - ~MapInfo() { - while (milist) { - mapinfo *next = milist->next; - free(milist); - milist = next; - } - } - - static const char *mapAddressToName(const void* pc, const char* def) { - return sMapInfo.map_to_name((uint64_t)pc, def); - } - -}; - -/*****************************************************************************/ - -MapInfo MapInfo::sMapInfo; - -/*****************************************************************************/ - -CallStack::CallStack() - : mCount(0) -{ -} - -CallStack::CallStack(const CallStack& rhs) - : mCount(rhs.mCount) -{ - if (mCount) { - memcpy(mStack, rhs.mStack, mCount*sizeof(void*)); - } -} - -CallStack::~CallStack() -{ -} - -CallStack& CallStack::operator = (const CallStack& rhs) -{ - mCount = rhs.mCount; - if (mCount) { - memcpy(mStack, rhs.mStack, mCount*sizeof(void*)); - } - return *this; -} - -bool CallStack::operator == (const CallStack& rhs) const { - if (mCount != rhs.mCount) - return false; - return !mCount || (memcmp(mStack, rhs.mStack, mCount*sizeof(void*)) == 0); -} - -bool CallStack::operator != (const CallStack& rhs) const { - return !operator == (rhs); -} - -bool CallStack::operator < (const CallStack& rhs) const { - if (mCount != rhs.mCount) - return mCount < rhs.mCount; - return memcmp(mStack, rhs.mStack, mCount*sizeof(void*)) < 0; -} - -bool CallStack::operator >= (const CallStack& rhs) const { - return !operator < (rhs); -} - -bool CallStack::operator > (const CallStack& rhs) const { - if (mCount != rhs.mCount) - return mCount > rhs.mCount; - return memcmp(mStack, rhs.mStack, mCount*sizeof(void*)) > 0; -} - -bool CallStack::operator <= (const CallStack& rhs) const { - return !operator > (rhs); -} - -const void* CallStack::operator [] (int index) const { - if (index >= int(mCount)) - return 0; - return mStack[index]; -} - - -void CallStack::clear() -{ - mCount = 0; -} - -void CallStack::update(int32_t ignoreDepth, int32_t maxDepth) -{ - if (maxDepth > MAX_DEPTH) - maxDepth = MAX_DEPTH; - mCount = backtrace(mStack, ignoreDepth, maxDepth); -} - -// Return the stack frame name on the designated level -String8 CallStack::toStringSingleLevel(const char* prefix, int32_t level) const -{ - String8 res; - char namebuf[1024]; - char tmp[256]; - char tmp1[32]; - char tmp2[32]; - void *offs; - - const void* ip = mStack[level]; - if (!ip) return res; - - if (prefix) res.append(prefix); - snprintf(tmp1, 32, "#%02d ", level); - res.append(tmp1); - - const char* name = lookup_symbol(ip, &offs, namebuf, sizeof(namebuf)); - if (name) { - if (linux_gcc_demangler(name, tmp, 256) != 0) - name = tmp; - snprintf(tmp1, 32, "0x%p: <", ip); - snprintf(tmp2, 32, ">+0x%p", offs); - res.append(tmp1); - res.append(name); - res.append(tmp2); - } else { - name = MapInfo::mapAddressToName(ip, ""); - snprintf(tmp, 256, "pc %p %s", ip, name); - res.append(tmp); - } - res.append("\n"); - - return res; -} - -// Dump a stack trace to the log -void CallStack::dump(const char* prefix) const -{ - /* - * Sending a single long log may be truncated since the stack levels can - * get very deep. So we request function names of each frame individually. - */ - for (int i=0; i - -#include - -#include -#include -#include - -namespace android { - -// --------------------------------------------------------------------- - -static const char indentStr[] = -" " -" "; - -const char* stringForIndent(int32_t indentLevel) -{ - ssize_t off = sizeof(indentStr)-1-(indentLevel*2); - return indentStr + (off < 0 ? 0 : off); -} - -// --------------------------------------------------------------------- - -static void defaultPrintFunc(void* cookie, const char* txt) -{ - printf("%s", txt); -} - -// --------------------------------------------------------------------- - -static inline int isident(int c) -{ - return isalnum(c) || c == '_'; -} - -static inline bool isasciitype(char c) -{ - if( c >= ' ' && c < 127 && c != '\'' && c != '\\' ) return true; - return false; -} - -static inline char makehexdigit(uint32_t val) -{ - return "0123456789abcdef"[val&0xF]; -} - -static char* appendhexnum(uint32_t val, char* out) -{ - for( int32_t i=28; i>=0; i-=4 ) { - *out++ = makehexdigit( val>>i ); - } - *out = 0; - return out; -} - -static inline char makeupperhexdigit(uint32_t val) -{ - return "0123456789ABCDEF"[val&0xF]; -} - -static char* appendupperhexnum(uint32_t val, char* out) -{ - for( int32_t i=28; i>=0; i-=4 ) { - *out++ = makeupperhexdigit( val>>i ); - } - *out = 0; - return out; -} - -static char* appendcharornum(char c, char* out, bool skipzero = true) -{ - if (skipzero && c == 0) return out; - - if (isasciitype(c)) { - *out++ = c; - return out; - } - - *out++ = '\\'; - *out++ = 'x'; - *out++ = makehexdigit(c>>4); - *out++ = makehexdigit(c); - return out; -} - -static char* typetostring(uint32_t type, char* out, - bool fullContext = true, - bool strict = false) -{ - char* pos = out; - char c[4]; - c[0] = (char)((type>>24)&0xFF); - c[1] = (char)((type>>16)&0xFF); - c[2] = (char)((type>>8)&0xFF); - c[3] = (char)(type&0xFF); - bool valid; - if( !strict ) { - // now even less strict! - // valid = isasciitype(c[3]); - valid = true; - int32_t i = 0; - bool zero = true; - while (valid && i<3) { - if (c[i] == 0) { - if (!zero) valid = false; - } else { - zero = false; - //if (!isasciitype(c[i])) valid = false; - } - i++; - } - // if all zeros, not a valid type code. - if (zero) valid = false; - } else { - valid = isident(c[3]) ? true : false; - int32_t i = 0; - bool zero = true; - while (valid && i<3) { - if (c[i] == 0) { - if (!zero) valid = false; - } else { - zero = false; - if (!isident(c[i])) valid = false; - } - i++; - } - } - if( valid && (!fullContext || c[0] != '0' || c[1] != 'x') ) { - if( fullContext ) *pos++ = '\''; - pos = appendcharornum(c[0], pos); - pos = appendcharornum(c[1], pos); - pos = appendcharornum(c[2], pos); - pos = appendcharornum(c[3], pos); - if( fullContext ) *pos++ = '\''; - *pos = 0; - return pos; - } - - if( fullContext ) { - *pos++ = '0'; - *pos++ = 'x'; - } - return appendhexnum(type, pos); -} - -void printTypeCode(uint32_t typeCode, debugPrintFunc func, void* cookie) -{ - char buffer[32]; - char* end = typetostring(typeCode, buffer); - *end = 0; - func ? (*func)(cookie, buffer) : defaultPrintFunc(cookie, buffer); -} - -void printHexData(int32_t indent, const void *buf, size_t length, - size_t bytesPerLine, int32_t singleLineBytesCutoff, - size_t alignment, bool cStyle, - debugPrintFunc func, void* cookie) -{ - if (alignment == 0) { - if (bytesPerLine >= 16) alignment = 4; - else if (bytesPerLine >= 8) alignment = 2; - else alignment = 1; - } - if (func == NULL) func = defaultPrintFunc; - - size_t offset; - - unsigned char *pos = (unsigned char *)buf; - - if (pos == NULL) { - if (singleLineBytesCutoff < 0) func(cookie, "\n"); - func(cookie, "(NULL)"); - return; - } - - if (length == 0) { - if (singleLineBytesCutoff < 0) func(cookie, "\n"); - func(cookie, "(empty)"); - return; - } - - if ((int32_t)length < 0) { - if (singleLineBytesCutoff < 0) func(cookie, "\n"); - char buf[64]; - sprintf(buf, "(bad length: %d)", length); - func(cookie, buf); - return; - } - - char buffer[256]; - static const size_t maxBytesPerLine = (sizeof(buffer)-1-11-4)/(3+1); - - if (bytesPerLine > maxBytesPerLine) bytesPerLine = maxBytesPerLine; - - const bool oneLine = (int32_t)length <= singleLineBytesCutoff; - bool newLine = false; - if (cStyle) { - indent++; - func(cookie, "{\n"); - newLine = true; - } else if (!oneLine) { - func(cookie, "\n"); - newLine = true; - } - - for (offset = 0; ; offset += bytesPerLine, pos += bytesPerLine) { - long remain = length; - - char* c = buffer; - if (!oneLine && !cStyle) { - sprintf(c, "0x%08x: ", (int)offset); - c += 12; - } - - size_t index; - size_t word; - - for (word = 0; word < bytesPerLine; ) { - -#ifdef HAVE_LITTLE_ENDIAN - const size_t startIndex = word+(alignment-(alignment?1:0)); - const ssize_t dir = -1; -#else - const size_t startIndex = word; - const ssize_t dir = 1; -#endif - - for (index = 0; index < alignment || (alignment == 0 && index < bytesPerLine); index++) { - - if (!cStyle) { - if (index == 0 && word > 0 && alignment > 0) { - *c++ = ' '; - } - - if (remain-- > 0) { - const unsigned char val = *(pos+startIndex+(index*dir)); - *c++ = makehexdigit(val>>4); - *c++ = makehexdigit(val); - } else if (!oneLine) { - *c++ = ' '; - *c++ = ' '; - } - } else { - if (remain > 0) { - if (index == 0 && word > 0) { - *c++ = ','; - *c++ = ' '; - } - if (index == 0) { - *c++ = '0'; - *c++ = 'x'; - } - const unsigned char val = *(pos+startIndex+(index*dir)); - *c++ = makehexdigit(val>>4); - *c++ = makehexdigit(val); - remain--; - } - } - } - - word += index; - } - - if (!cStyle) { - remain = length; - *c++ = ' '; - *c++ = '\''; - for (index = 0; index < bytesPerLine; index++) { - - if (remain-- > 0) { - const unsigned char val = pos[index]; - *c++ = (val >= ' ' && val < 127) ? val : '.'; - } else if (!oneLine) { - *c++ = ' '; - } - } - - *c++ = '\''; - if (length > bytesPerLine) *c++ = '\n'; - } else { - if (remain > 0) *c++ = ','; - *c++ = '\n'; - } - - if (newLine && indent) func(cookie, stringForIndent(indent)); - *c = 0; - func(cookie, buffer); - newLine = true; - - if (length <= bytesPerLine) break; - length -= bytesPerLine; - } - - if (cStyle) { - if (indent > 0) func(cookie, stringForIndent(indent-1)); - func(cookie, "};"); - } -} - -}; // namespace android - diff --git a/libs/utils/FileMap.cpp b/libs/utils/FileMap.cpp deleted file mode 100644 index e1ba9b238..000000000 --- a/libs/utils/FileMap.cpp +++ /dev/null @@ -1,222 +0,0 @@ -/* - * Copyright (C) 2006 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. - */ - -// -// Shared file mapping class. -// - -#define LOG_TAG "filemap" - -#include -#include - -#include -#include - -#ifdef HAVE_POSIX_FILEMAP -#include -#endif - -#include -#include -#include -#include - -using namespace android; - -/*static*/ long FileMap::mPageSize = -1; - - -/* - * Constructor. Create an empty object. - */ -FileMap::FileMap(void) - : mRefCount(1), mFileName(NULL), mBasePtr(NULL), mBaseLength(0), - mDataPtr(NULL), mDataLength(0) -{ -} - -/* - * Destructor. - */ -FileMap::~FileMap(void) -{ - assert(mRefCount == 0); - - //printf("+++ removing FileMap %p %u\n", mDataPtr, mDataLength); - - mRefCount = -100; // help catch double-free - if (mFileName != NULL) { - free(mFileName); - } -#ifdef HAVE_POSIX_FILEMAP - if (munmap(mBasePtr, mBaseLength) != 0) { - LOGD("munmap(%p, %d) failed\n", mBasePtr, (int) mBaseLength); - } -#endif -#ifdef HAVE_WIN32_FILEMAP - if ( UnmapViewOfFile(mBasePtr) == 0) { - LOGD("UnmapViewOfFile(%p) failed, error = %ld\n", mBasePtr, - GetLastError() ); - } - CloseHandle(mFileMapping); - CloseHandle(mFileHandle); -#endif -} - - -/* - * Create a new mapping on an open file. - * - * Closing the file descriptor does not unmap the pages, so we don't - * claim ownership of the fd. - * - * Returns "false" on failure. - */ -bool FileMap::create(const char* origFileName, int fd, off_t offset, size_t length, bool readOnly) -{ -#ifdef HAVE_WIN32_FILEMAP - int adjust; - off_t adjOffset; - size_t adjLength; - - if (mPageSize == -1) { - SYSTEM_INFO si; - - GetSystemInfo( &si ); - mPageSize = si.dwAllocationGranularity; - } - - DWORD protect = readOnly ? PAGE_READONLY : PAGE_READWRITE; - - mFileHandle = (HANDLE) _get_osfhandle(fd); - mFileMapping = CreateFileMapping( mFileHandle, NULL, protect, 0, 0, NULL); - if (mFileMapping == NULL) { - LOGE("CreateFileMapping(%p, %lx) failed with error %ld\n", - mFileHandle, protect, GetLastError() ); - return false; - } - - adjust = offset % mPageSize; - adjOffset = offset - adjust; - adjLength = length + adjust; - - mBasePtr = MapViewOfFile( mFileMapping, - readOnly ? FILE_MAP_READ : FILE_MAP_ALL_ACCESS, - 0, - (DWORD)(adjOffset), - adjLength ); - if (mBasePtr == NULL) { - LOGE("MapViewOfFile(%ld, %ld) failed with error %ld\n", - adjOffset, adjLength, GetLastError() ); - CloseHandle(mFileMapping); - mFileMapping = INVALID_HANDLE_VALUE; - return false; - } -#endif -#ifdef HAVE_POSIX_FILEMAP - int prot, flags, adjust; - off_t adjOffset; - size_t adjLength; - - void* ptr; - - assert(mRefCount == 1); - assert(fd >= 0); - assert(offset >= 0); - assert(length > 0); - - /* init on first use */ - if (mPageSize == -1) { -#if NOT_USING_KLIBC - mPageSize = sysconf(_SC_PAGESIZE); - if (mPageSize == -1) { - LOGE("could not get _SC_PAGESIZE\n"); - return false; - } -#else - /* this holds for Linux, Darwin, Cygwin, and doesn't pain the ARM */ - mPageSize = 4096; -#endif - } - - adjust = offset % mPageSize; -try_again: - adjOffset = offset - adjust; - adjLength = length + adjust; - - flags = MAP_SHARED; - prot = PROT_READ; - if (!readOnly) - prot |= PROT_WRITE; - - ptr = mmap(NULL, adjLength, prot, flags, fd, adjOffset); - if (ptr == MAP_FAILED) { - // Cygwin does not seem to like file mapping files from an offset. - // So if we fail, try again with offset zero - if (adjOffset > 0) { - adjust = offset; - goto try_again; - } - - LOGE("mmap(%ld,%ld) failed: %s\n", - (long) adjOffset, (long) adjLength, strerror(errno)); - return false; - } - mBasePtr = ptr; -#endif /* HAVE_POSIX_FILEMAP */ - - mFileName = origFileName != NULL ? strdup(origFileName) : NULL; - mBaseLength = adjLength; - mDataOffset = offset; - mDataPtr = (char*) mBasePtr + adjust; - mDataLength = length; - - assert(mBasePtr != NULL); - - LOGV("MAP: base %p/%d data %p/%d\n", - mBasePtr, (int) mBaseLength, mDataPtr, (int) mDataLength); - - return true; -} - -/* - * Provide guidance to the system. - */ -int FileMap::advise(MapAdvice advice) -{ -#if HAVE_MADVISE - int cc, sysAdvice; - - switch (advice) { - case NORMAL: sysAdvice = MADV_NORMAL; break; - case RANDOM: sysAdvice = MADV_RANDOM; break; - case SEQUENTIAL: sysAdvice = MADV_SEQUENTIAL; break; - case WILLNEED: sysAdvice = MADV_WILLNEED; break; - case DONTNEED: sysAdvice = MADV_DONTNEED; break; - default: - assert(false); - return -1; - } - - cc = madvise(mBasePtr, mBaseLength, sysAdvice); - if (cc != 0) - LOGW("madvise(%d) failed: %s\n", sysAdvice, strerror(errno)); - return cc; -#else - return -1; -#endif // HAVE_MADVISE -} diff --git a/libs/utils/IDataConnection.cpp b/libs/utils/IDataConnection.cpp deleted file mode 100644 index c6d49aa48..000000000 --- a/libs/utils/IDataConnection.cpp +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (C) 2006 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 -#include - -#include - -#include - -namespace android { - -// --------------------------------------------------------------------------- - -enum -{ - CONNECT_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION, - DISCONNECT_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION + 1 -}; - -class BpDataConnection : public BpInterface -{ -public: - BpDataConnection::BpDataConnection(const sp& impl) - : BpInterface(impl) - { - } - - virtual void connect() - { - Parcel data, reply; - data.writeInterfaceToken(IDataConnection::descriptor()); - remote()->transact(CONNECT_TRANSACTION, data, &reply); - } - - virtual void disconnect() - { - Parcel data, reply; - remote()->transact(DISCONNECT_TRANSACTION, data, &reply); - } -}; - -IMPLEMENT_META_INTERFACE(DataConnection, "android.utils.IDataConnection"); - -#define CHECK_INTERFACE(interface, data, reply) \ - do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ - LOGW("Call incorrectly routed to " #interface); \ - return PERMISSION_DENIED; \ - } } while (0) - -status_t BnDataConnection::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) -{ - switch(code) - { - case CONNECT_TRANSACTION: - { - CHECK_INTERFACE(IDataConnection, data, reply); - connect(); - return NO_ERROR; - } - - case DISCONNECT_TRANSACTION: - { - CHECK_INTERFACE(IDataConnection, data, reply); - disconnect(); - return NO_ERROR; - } - - default: - return BBinder::onTransact(code, data, reply, flags); - } -} - -// ---------------------------------------------------------------------------- - -}; // namespace android diff --git a/libs/utils/IInterface.cpp b/libs/utils/IInterface.cpp deleted file mode 100644 index 6ea817887..000000000 --- a/libs/utils/IInterface.cpp +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2005 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 - -namespace android { - -// --------------------------------------------------------------------------- - -sp IInterface::asBinder() -{ - return this ? onAsBinder() : NULL; -} - -sp IInterface::asBinder() const -{ - return this ? const_cast(this)->onAsBinder() : NULL; -} - -// --------------------------------------------------------------------------- - -}; // namespace android diff --git a/libs/utils/IMemory.cpp b/libs/utils/IMemory.cpp deleted file mode 100644 index 429bc2b94..000000000 --- a/libs/utils/IMemory.cpp +++ /dev/null @@ -1,486 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "IMemory" - -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include - -#define VERBOSE 0 - -namespace android { -// --------------------------------------------------------------------------- - -class HeapCache : public IBinder::DeathRecipient -{ -public: - HeapCache(); - virtual ~HeapCache(); - - virtual void binderDied(const wp& who); - - sp find_heap(const sp& binder); - void pin_heap(const sp& binder); - void free_heap(const sp& binder); - sp get_heap(const sp& binder); - void dump_heaps(); - -private: - // For IMemory.cpp - struct heap_info_t { - sp heap; - int32_t count; - }; - - void free_heap(const wp& binder); - - Mutex mHeapCacheLock; - KeyedVector< wp, heap_info_t > mHeapCache; -}; - -static sp gHeapCache = new HeapCache(); - -/******************************************************************************/ - -enum { - HEAP_ID = IBinder::FIRST_CALL_TRANSACTION -}; - -class BpMemoryHeap : public BpInterface -{ -public: - BpMemoryHeap(const sp& impl); - virtual ~BpMemoryHeap(); - - virtual int getHeapID() const; - virtual void* getBase() const; - virtual size_t getSize() const; - virtual uint32_t getFlags() const; - -private: - friend class IMemory; - friend class HeapCache; - - // for debugging in this module - static inline sp find_heap(const sp& binder) { - return gHeapCache->find_heap(binder); - } - static inline void free_heap(const sp& binder) { - gHeapCache->free_heap(binder); - } - static inline sp get_heap(const sp& binder) { - return gHeapCache->get_heap(binder); - } - static inline void dump_heaps() { - gHeapCache->dump_heaps(); - } - void inline pin_heap() const { - gHeapCache->pin_heap(const_cast(this)->asBinder()); - } - - void assertMapped() const; - void assertReallyMapped() const; - void pinHeap() const; - - mutable volatile int32_t mHeapId; - mutable void* mBase; - mutable size_t mSize; - mutable uint32_t mFlags; - mutable bool mRealHeap; - mutable Mutex mLock; -}; - -// ---------------------------------------------------------------------------- - -enum { - GET_MEMORY = IBinder::FIRST_CALL_TRANSACTION -}; - -class BpMemory : public BpInterface -{ -public: - BpMemory(const sp& impl); - virtual ~BpMemory(); - virtual sp getMemory(ssize_t* offset=0, size_t* size=0) const; - -private: - mutable sp mHeap; - mutable ssize_t mOffset; - mutable size_t mSize; -}; - -/******************************************************************************/ - -void* IMemory::fastPointer(const sp& binder, ssize_t offset) const -{ - sp realHeap = BpMemoryHeap::get_heap(binder); - void* const base = realHeap->base(); - if (base == MAP_FAILED) - return 0; - return static_cast(base) + offset; -} - -void* IMemory::pointer() const { - ssize_t offset; - sp heap = getMemory(&offset); - void* const base = heap!=0 ? heap->base() : MAP_FAILED; - if (base == MAP_FAILED) - return 0; - return static_cast(base) + offset; -} - -size_t IMemory::size() const { - size_t size; - getMemory(NULL, &size); - return size; -} - -ssize_t IMemory::offset() const { - ssize_t offset; - getMemory(&offset); - return offset; -} - -/******************************************************************************/ - -BpMemory::BpMemory(const sp& impl) - : BpInterface(impl), mOffset(0), mSize(0) -{ -} - -BpMemory::~BpMemory() -{ -} - -sp BpMemory::getMemory(ssize_t* offset, size_t* size) const -{ - if (mHeap == 0) { - Parcel data, reply; - data.writeInterfaceToken(IMemory::getInterfaceDescriptor()); - if (remote()->transact(GET_MEMORY, data, &reply) == NO_ERROR) { - sp heap = reply.readStrongBinder(); - ssize_t o = reply.readInt32(); - size_t s = reply.readInt32(); - if (heap != 0) { - mHeap = interface_cast(heap); - if (mHeap != 0) { - mOffset = o; - mSize = s; - } - } - } - } - if (offset) *offset = mOffset; - if (size) *size = mSize; - return mHeap; -} - -// --------------------------------------------------------------------------- - -IMPLEMENT_META_INTERFACE(Memory, "android.utils.IMemory"); - -#define CHECK_INTERFACE(interface, data, reply) \ - do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ - LOGW("Call incorrectly routed to " #interface); \ - return PERMISSION_DENIED; \ - } } while (0) - -status_t BnMemory::onTransact( - uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) -{ - switch(code) { - case GET_MEMORY: { - CHECK_INTERFACE(IMemory, data, reply); - ssize_t offset; - size_t size; - reply->writeStrongBinder( getMemory(&offset, &size)->asBinder() ); - reply->writeInt32(offset); - reply->writeInt32(size); - return NO_ERROR; - } break; - default: - return BBinder::onTransact(code, data, reply, flags); - } -} - - -/******************************************************************************/ - -BpMemoryHeap::BpMemoryHeap(const sp& impl) - : BpInterface(impl), - mHeapId(-1), mBase(MAP_FAILED), mSize(0), mFlags(0), mRealHeap(false) -{ -} - -BpMemoryHeap::~BpMemoryHeap() { - if (mHeapId != -1) { - close(mHeapId); - if (mRealHeap) { - // by construction we're the last one - if (mBase != MAP_FAILED) { - sp binder = const_cast(this)->asBinder(); - - if (VERBOSE) { - LOGD("UNMAPPING binder=%p, heap=%p, size=%d, fd=%d", - binder.get(), this, mSize, mHeapId); - CallStack stack; - stack.update(); - stack.dump("callstack"); - } - - munmap(mBase, mSize); - } - } else { - // remove from list only if it was mapped before - sp binder = const_cast(this)->asBinder(); - free_heap(binder); - } - } -} - -void BpMemoryHeap::assertMapped() const -{ - if (mHeapId == -1) { - sp binder(const_cast(this)->asBinder()); - sp heap(static_cast(find_heap(binder).get())); - heap->assertReallyMapped(); - if (heap->mBase != MAP_FAILED) { - Mutex::Autolock _l(mLock); - if (mHeapId == -1) { - mBase = heap->mBase; - mSize = heap->mSize; - android_atomic_write( dup( heap->mHeapId ), &mHeapId ); - } - } else { - // something went wrong - free_heap(binder); - } - } -} - -void BpMemoryHeap::assertReallyMapped() const -{ - if (mHeapId == -1) { - - // remote call without mLock held, worse case scenario, we end up - // calling transact() from multiple threads, but that's not a problem, - // only mmap below must be in the critical section. - - Parcel data, reply; - data.writeInterfaceToken(IMemoryHeap::getInterfaceDescriptor()); - status_t err = remote()->transact(HEAP_ID, data, &reply); - int parcel_fd = reply.readFileDescriptor(); - ssize_t size = reply.readInt32(); - uint32_t flags = reply.readInt32(); - - LOGE_IF(err, "binder=%p transaction failed fd=%d, size=%d, err=%d (%s)", - asBinder().get(), parcel_fd, size, err, strerror(-err)); - - int fd = dup( parcel_fd ); - LOGE_IF(fd==-1, "cannot dup fd=%d, size=%d, err=%d (%s)", - parcel_fd, size, err, strerror(errno)); - - int access = PROT_READ; - if (!(flags & READ_ONLY)) { - access |= PROT_WRITE; - } - - Mutex::Autolock _l(mLock); - if (mHeapId == -1) { - mRealHeap = true; - mBase = mmap(0, size, access, MAP_SHARED, fd, 0); - if (mBase == MAP_FAILED) { - LOGE("cannot map BpMemoryHeap (binder=%p), size=%d, fd=%d (%s)", - asBinder().get(), size, fd, strerror(errno)); - close(fd); - } else { - if (flags & MAP_ONCE) { - //LOGD("pinning heap (binder=%p, size=%d, fd=%d", - // asBinder().get(), size, fd); - pin_heap(); - } - mSize = size; - mFlags = flags; - android_atomic_write(fd, &mHeapId); - } - } - } -} - -int BpMemoryHeap::getHeapID() const { - assertMapped(); - return mHeapId; -} - -void* BpMemoryHeap::getBase() const { - assertMapped(); - return mBase; -} - -size_t BpMemoryHeap::getSize() const { - assertMapped(); - return mSize; -} - -uint32_t BpMemoryHeap::getFlags() const { - assertMapped(); - return mFlags; -} - -// --------------------------------------------------------------------------- - -IMPLEMENT_META_INTERFACE(MemoryHeap, "android.utils.IMemoryHeap"); - -status_t BnMemoryHeap::onTransact( - uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) -{ - switch(code) { - case HEAP_ID: { - CHECK_INTERFACE(IMemoryHeap, data, reply); - reply->writeFileDescriptor(getHeapID()); - reply->writeInt32(getSize()); - reply->writeInt32(getFlags()); - return NO_ERROR; - } break; - default: - return BBinder::onTransact(code, data, reply, flags); - } -} - -/*****************************************************************************/ - -HeapCache::HeapCache() - : DeathRecipient() -{ -} - -HeapCache::~HeapCache() -{ -} - -void HeapCache::binderDied(const wp& binder) -{ - //LOGD("binderDied binder=%p", binder.unsafe_get()); - free_heap(binder); -} - -sp HeapCache::find_heap(const sp& binder) -{ - Mutex::Autolock _l(mHeapCacheLock); - ssize_t i = mHeapCache.indexOfKey(binder); - if (i>=0) { - heap_info_t& info = mHeapCache.editValueAt(i); - LOGD_IF(VERBOSE, - "found binder=%p, heap=%p, size=%d, fd=%d, count=%d", - binder.get(), info.heap.get(), - static_cast(info.heap.get())->mSize, - static_cast(info.heap.get())->mHeapId, - info.count); - android_atomic_inc(&info.count); - return info.heap; - } else { - heap_info_t info; - info.heap = interface_cast(binder); - info.count = 1; - //LOGD("adding binder=%p, heap=%p, count=%d", - // binder.get(), info.heap.get(), info.count); - mHeapCache.add(binder, info); - return info.heap; - } -} - -void HeapCache::pin_heap(const sp& binder) -{ - Mutex::Autolock _l(mHeapCacheLock); - ssize_t i = mHeapCache.indexOfKey(binder); - if (i>=0) { - heap_info_t& info(mHeapCache.editValueAt(i)); - android_atomic_inc(&info.count); - binder->linkToDeath(this); - } else { - LOGE("pin_heap binder=%p not found!!!", binder.get()); - } -} - -void HeapCache::free_heap(const sp& binder) { - free_heap( wp(binder) ); -} - -void HeapCache::free_heap(const wp& binder) -{ - sp rel; - { - Mutex::Autolock _l(mHeapCacheLock); - ssize_t i = mHeapCache.indexOfKey(binder); - if (i>=0) { - heap_info_t& info(mHeapCache.editValueAt(i)); - int32_t c = android_atomic_dec(&info.count); - if (c == 1) { - LOGD_IF(VERBOSE, - "removing binder=%p, heap=%p, size=%d, fd=%d, count=%d", - binder.unsafe_get(), info.heap.get(), - static_cast(info.heap.get())->mSize, - static_cast(info.heap.get())->mHeapId, - info.count); - rel = mHeapCache.valueAt(i).heap; - mHeapCache.removeItemsAt(i); - } - } else { - LOGE("free_heap binder=%p not found!!!", binder.unsafe_get()); - } - } -} - -sp HeapCache::get_heap(const sp& binder) -{ - sp realHeap; - Mutex::Autolock _l(mHeapCacheLock); - ssize_t i = mHeapCache.indexOfKey(binder); - if (i>=0) realHeap = mHeapCache.valueAt(i).heap; - else realHeap = interface_cast(binder); - return realHeap; -} - -void HeapCache::dump_heaps() -{ - Mutex::Autolock _l(mHeapCacheLock); - int c = mHeapCache.size(); - for (int i=0 ; i(info.heap.get())); - LOGD("hey=%p, heap=%p, count=%d, (fd=%d, base=%p, size=%d)", - mHeapCache.keyAt(i).unsafe_get(), - info.heap.get(), info.count, - h->mHeapId, h->mBase, h->mSize); - } -} - - -// --------------------------------------------------------------------------- -}; // namespace android diff --git a/libs/utils/IPCThreadState.cpp b/libs/utils/IPCThreadState.cpp deleted file mode 100644 index 04ae1424e..000000000 --- a/libs/utils/IPCThreadState.cpp +++ /dev/null @@ -1,1030 +0,0 @@ -/* - * Copyright (C) 2005 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 - -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include - -#ifdef HAVE_PTHREADS -#include -#include -#include -#endif -#ifdef HAVE_WIN32_THREADS -#include -#endif - - -#if LOG_NDEBUG - -#define IF_LOG_TRANSACTIONS() if (false) -#define IF_LOG_COMMANDS() if (false) -#define LOG_REMOTEREFS(...) -#define IF_LOG_REMOTEREFS() if (false) -#define LOG_THREADPOOL(...) -#define LOG_ONEWAY(...) - -#else - -#define IF_LOG_TRANSACTIONS() IF_LOG(LOG_VERBOSE, "transact") -#define IF_LOG_COMMANDS() IF_LOG(LOG_VERBOSE, "ipc") -#define LOG_REMOTEREFS(...) LOG(LOG_DEBUG, "remoterefs", __VA_ARGS__) -#define IF_LOG_REMOTEREFS() IF_LOG(LOG_DEBUG, "remoterefs") -#define LOG_THREADPOOL(...) LOG(LOG_DEBUG, "threadpool", __VA_ARGS__) -#define LOG_ONEWAY(...) LOG(LOG_DEBUG, "ipc", __VA_ARGS__) - -#endif - -// --------------------------------------------------------------------------- - -namespace android { - -static const char* getReturnString(size_t idx); -static const char* getCommandString(size_t idx); -static const void* printReturnCommand(TextOutput& out, const void* _cmd); -static const void* printCommand(TextOutput& out, const void* _cmd); - -// This will result in a missing symbol failure if the IF_LOG_COMMANDS() -// conditionals don't get stripped... but that is probably what we want. -#if !LOG_NDEBUG -static const char *kReturnStrings[] = { -#if 1 /* TODO: error update strings */ - "unknown", -#else - "BR_OK", - "BR_TIMEOUT", - "BR_WAKEUP", - "BR_TRANSACTION", - "BR_REPLY", - "BR_ACQUIRE_RESULT", - "BR_DEAD_REPLY", - "BR_TRANSACTION_COMPLETE", - "BR_INCREFS", - "BR_ACQUIRE", - "BR_RELEASE", - "BR_DECREFS", - "BR_ATTEMPT_ACQUIRE", - "BR_EVENT_OCCURRED", - "BR_NOOP", - "BR_SPAWN_LOOPER", - "BR_FINISHED", - "BR_DEAD_BINDER", - "BR_CLEAR_DEATH_NOTIFICATION_DONE" -#endif -}; - -static const char *kCommandStrings[] = { -#if 1 /* TODO: error update strings */ - "unknown", -#else - "BC_NOOP", - "BC_TRANSACTION", - "BC_REPLY", - "BC_ACQUIRE_RESULT", - "BC_FREE_BUFFER", - "BC_TRANSACTION_COMPLETE", - "BC_INCREFS", - "BC_ACQUIRE", - "BC_RELEASE", - "BC_DECREFS", - "BC_INCREFS_DONE", - "BC_ACQUIRE_DONE", - "BC_ATTEMPT_ACQUIRE", - "BC_RETRIEVE_ROOT_OBJECT", - "BC_SET_THREAD_ENTRY", - "BC_REGISTER_LOOPER", - "BC_ENTER_LOOPER", - "BC_EXIT_LOOPER", - "BC_SYNC", - "BC_STOP_PROCESS", - "BC_STOP_SELF", - "BC_REQUEST_DEATH_NOTIFICATION", - "BC_CLEAR_DEATH_NOTIFICATION", - "BC_DEAD_BINDER_DONE" -#endif -}; - -static const char* getReturnString(size_t idx) -{ - if (idx < sizeof(kReturnStrings) / sizeof(kReturnStrings[0])) - return kReturnStrings[idx]; - else - return "unknown"; -} - -static const char* getCommandString(size_t idx) -{ - if (idx < sizeof(kCommandStrings) / sizeof(kCommandStrings[0])) - return kCommandStrings[idx]; - else - return "unknown"; -} - -static const void* printBinderTransactionData(TextOutput& out, const void* data) -{ - const binder_transaction_data* btd = - (const binder_transaction_data*)data; - out << "target=" << btd->target.ptr << " (cookie " << btd->cookie << ")" << endl - << "code=" << TypeCode(btd->code) << ", flags=" << (void*)btd->flags << endl - << "data=" << btd->data.ptr.buffer << " (" << (void*)btd->data_size - << " bytes)" << endl - << "offsets=" << btd->data.ptr.offsets << " (" << (void*)btd->offsets_size - << " bytes)" << endl; - return btd+1; -} - -static const void* printReturnCommand(TextOutput& out, const void* _cmd) -{ - static const int32_t N = sizeof(kReturnStrings)/sizeof(kReturnStrings[0]); - - const int32_t* cmd = (const int32_t*)_cmd; - int32_t code = *cmd++; - if (code == BR_ERROR) { - out << "BR_ERROR: " << (void*)(*cmd++) << endl; - return cmd; - } else if (code < 0 || code >= N) { - out << "Unknown reply: " << code << endl; - return cmd; - } - - out << kReturnStrings[code]; - switch (code) { - case BR_TRANSACTION: - case BR_REPLY: { - out << ": " << indent; - cmd = (const int32_t *)printBinderTransactionData(out, cmd); - out << dedent; - } break; - - case BR_ACQUIRE_RESULT: { - const int32_t res = *cmd++; - out << ": " << res << (res ? " (SUCCESS)" : " (FAILURE)"); - } break; - - case BR_INCREFS: - case BR_ACQUIRE: - case BR_RELEASE: - case BR_DECREFS: { - const int32_t b = *cmd++; - const int32_t c = *cmd++; - out << ": target=" << (void*)b << " (cookie " << (void*)c << ")"; - } break; - - case BR_ATTEMPT_ACQUIRE: { - const int32_t p = *cmd++; - const int32_t b = *cmd++; - const int32_t c = *cmd++; - out << ": target=" << (void*)b << " (cookie " << (void*)c - << "), pri=" << p; - } break; - - case BR_DEAD_BINDER: - case BR_CLEAR_DEATH_NOTIFICATION_DONE: { - const int32_t c = *cmd++; - out << ": death cookie " << (void*)c; - } break; - } - - out << endl; - return cmd; -} - -static const void* printCommand(TextOutput& out, const void* _cmd) -{ - static const int32_t N = sizeof(kCommandStrings)/sizeof(kCommandStrings[0]); - - const int32_t* cmd = (const int32_t*)_cmd; - int32_t code = *cmd++; - if (code < 0 || code >= N) { - out << "Unknown command: " << code << endl; - return cmd; - } - - out << kCommandStrings[code]; - switch (code) { - case BC_TRANSACTION: - case BC_REPLY: { - out << ": " << indent; - cmd = (const int32_t *)printBinderTransactionData(out, cmd); - out << dedent; - } break; - - case BC_ACQUIRE_RESULT: { - const int32_t res = *cmd++; - out << ": " << res << (res ? " (SUCCESS)" : " (FAILURE)"); - } break; - - case BC_FREE_BUFFER: { - const int32_t buf = *cmd++; - out << ": buffer=" << (void*)buf; - } break; - - case BC_INCREFS: - case BC_ACQUIRE: - case BC_RELEASE: - case BC_DECREFS: { - const int32_t d = *cmd++; - out << ": descriptor=" << (void*)d; - } break; - - case BC_INCREFS_DONE: - case BC_ACQUIRE_DONE: { - const int32_t b = *cmd++; - const int32_t c = *cmd++; - out << ": target=" << (void*)b << " (cookie " << (void*)c << ")"; - } break; - - case BC_ATTEMPT_ACQUIRE: { - const int32_t p = *cmd++; - const int32_t d = *cmd++; - out << ": decriptor=" << (void*)d << ", pri=" << p; - } break; - - case BC_REQUEST_DEATH_NOTIFICATION: - case BC_CLEAR_DEATH_NOTIFICATION: { - const int32_t h = *cmd++; - const int32_t c = *cmd++; - out << ": handle=" << h << " (death cookie " << (void*)c << ")"; - } break; - - case BC_DEAD_BINDER_DONE: { - const int32_t c = *cmd++; - out << ": death cookie " << (void*)c; - } break; - } - - out << endl; - return cmd; -} -#endif - -static pthread_mutex_t gTLSMutex = PTHREAD_MUTEX_INITIALIZER; -static bool gHaveTLS = false; -static pthread_key_t gTLS = 0; -static bool gShutdown = false; - -IPCThreadState* IPCThreadState::self() -{ - if (gHaveTLS) { -restart: - const pthread_key_t k = gTLS; - IPCThreadState* st = (IPCThreadState*)pthread_getspecific(k); - if (st) return st; - return new IPCThreadState; - } - - if (gShutdown) return NULL; - - pthread_mutex_lock(&gTLSMutex); - if (!gHaveTLS) { - if (pthread_key_create(&gTLS, threadDestructor) != 0) { - pthread_mutex_unlock(&gTLSMutex); - return NULL; - } - gHaveTLS = true; - } - pthread_mutex_unlock(&gTLSMutex); - goto restart; -} - -void IPCThreadState::shutdown() -{ - gShutdown = true; - - if (gHaveTLS) { - // XXX Need to wait for all thread pool threads to exit! - IPCThreadState* st = (IPCThreadState*)pthread_getspecific(gTLS); - if (st) { - delete st; - pthread_setspecific(gTLS, NULL); - } - gHaveTLS = false; - } -} - -sp IPCThreadState::process() -{ - return mProcess; -} - -status_t IPCThreadState::clearLastError() -{ - const status_t err = mLastError; - mLastError = NO_ERROR; - return err; -} - -int IPCThreadState::getCallingPid() -{ - return mCallingPid; -} - -int IPCThreadState::getCallingUid() -{ - return mCallingUid; -} - -int64_t IPCThreadState::clearCallingIdentity() -{ - int64_t token = ((int64_t)mCallingUid<<32) | mCallingPid; - clearCaller(); - return token; -} - -void IPCThreadState::restoreCallingIdentity(int64_t token) -{ - mCallingUid = (int)(token>>32); - mCallingPid = (int)token; -} - -void IPCThreadState::clearCaller() -{ - if (mProcess->supportsProcesses()) { - mCallingPid = getpid(); - mCallingUid = getuid(); - } else { - mCallingPid = -1; - mCallingUid = -1; - } -} - -void IPCThreadState::flushCommands() -{ - if (mProcess->mDriverFD <= 0) - return; - talkWithDriver(false); -} - -void IPCThreadState::joinThreadPool(bool isMain) -{ - LOG_THREADPOOL("**** THREAD %p (PID %d) IS JOINING THE THREAD POOL\n", (void*)pthread_self(), getpid()); - - mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER); - - status_t result; - do { - int32_t cmd; - - // When we've cleared the incoming command queue, process any pending derefs - if (mIn.dataPosition() >= mIn.dataSize()) { - size_t numPending = mPendingWeakDerefs.size(); - if (numPending > 0) { - for (size_t i = 0; i < numPending; i++) { - RefBase::weakref_type* refs = mPendingWeakDerefs[i]; - refs->decWeak(mProcess.get()); - } - mPendingWeakDerefs.clear(); - } - - numPending = mPendingStrongDerefs.size(); - if (numPending > 0) { - for (size_t i = 0; i < numPending; i++) { - BBinder* obj = mPendingStrongDerefs[i]; - obj->decStrong(mProcess.get()); - } - mPendingStrongDerefs.clear(); - } - } - - // now get the next command to be processed, waiting if necessary - result = talkWithDriver(); - if (result >= NO_ERROR) { - size_t IN = mIn.dataAvail(); - if (IN < sizeof(int32_t)) continue; - cmd = mIn.readInt32(); - IF_LOG_COMMANDS() { - alog << "Processing top-level Command: " - << getReturnString(cmd) << endl; - } - result = executeCommand(cmd); - } - - // Let this thread exit the thread pool if it is no longer - // needed and it is not the main process thread. - if(result == TIMED_OUT && !isMain) { - break; - } - } while (result != -ECONNREFUSED && result != -EBADF); - - LOG_THREADPOOL("**** THREAD %p (PID %d) IS LEAVING THE THREAD POOL err=%p\n", - (void*)pthread_self(), getpid(), (void*)result); - - mOut.writeInt32(BC_EXIT_LOOPER); - talkWithDriver(false); -} - -void IPCThreadState::stopProcess(bool immediate) -{ - //LOGI("**** STOPPING PROCESS"); - flushCommands(); - int fd = mProcess->mDriverFD; - mProcess->mDriverFD = -1; - close(fd); - //kill(getpid(), SIGKILL); -} - -status_t IPCThreadState::transact(int32_t handle, - uint32_t code, const Parcel& data, - Parcel* reply, uint32_t flags) -{ - status_t err = data.errorCheck(); - - flags |= TF_ACCEPT_FDS; - - IF_LOG_TRANSACTIONS() { - TextOutput::Bundle _b(alog); - alog << "BC_TRANSACTION thr " << (void*)pthread_self() << " / hand " - << handle << " / code " << TypeCode(code) << ": " - << indent << data << dedent << endl; - } - - if (err == NO_ERROR) { - LOG_ONEWAY(">>>> SEND from pid %d uid %d %s", getpid(), getuid(), - (flags & TF_ONE_WAY) == 0 ? "READ REPLY" : "ONE WAY"); - err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL); - } - - if (err != NO_ERROR) { - if (reply) reply->setError(err); - return (mLastError = err); - } - - if ((flags & TF_ONE_WAY) == 0) { - if (reply) { - err = waitForResponse(reply); - } else { - Parcel fakeReply; - err = waitForResponse(&fakeReply); - } - - IF_LOG_TRANSACTIONS() { - TextOutput::Bundle _b(alog); - alog << "BR_REPLY thr " << (void*)pthread_self() << " / hand " - << handle << ": "; - if (reply) alog << indent << *reply << dedent << endl; - else alog << "(none requested)" << endl; - } - } else { - err = waitForResponse(NULL, NULL); - } - - return err; -} - -void IPCThreadState::incStrongHandle(int32_t handle) -{ - LOG_REMOTEREFS("IPCThreadState::incStrongHandle(%d)\n", handle); - mOut.writeInt32(BC_ACQUIRE); - mOut.writeInt32(handle); -} - -void IPCThreadState::decStrongHandle(int32_t handle) -{ - LOG_REMOTEREFS("IPCThreadState::decStrongHandle(%d)\n", handle); - mOut.writeInt32(BC_RELEASE); - mOut.writeInt32(handle); -} - -void IPCThreadState::incWeakHandle(int32_t handle) -{ - LOG_REMOTEREFS("IPCThreadState::incWeakHandle(%d)\n", handle); - mOut.writeInt32(BC_INCREFS); - mOut.writeInt32(handle); -} - -void IPCThreadState::decWeakHandle(int32_t handle) -{ - LOG_REMOTEREFS("IPCThreadState::decWeakHandle(%d)\n", handle); - mOut.writeInt32(BC_DECREFS); - mOut.writeInt32(handle); -} - -status_t IPCThreadState::attemptIncStrongHandle(int32_t handle) -{ - mOut.writeInt32(BC_ATTEMPT_ACQUIRE); - mOut.writeInt32(0); // xxx was thread priority - mOut.writeInt32(handle); - status_t result = UNKNOWN_ERROR; - - waitForResponse(NULL, &result); - -#if LOG_REFCOUNTS - printf("IPCThreadState::attemptIncStrongHandle(%ld) = %s\n", - handle, result == NO_ERROR ? "SUCCESS" : "FAILURE"); -#endif - - return result; -} - -void IPCThreadState::expungeHandle(int32_t handle, IBinder* binder) -{ -#if LOG_REFCOUNTS - printf("IPCThreadState::expungeHandle(%ld)\n", handle); -#endif - self()->mProcess->expungeHandle(handle, binder); -} - -status_t IPCThreadState::requestDeathNotification(int32_t handle, BpBinder* proxy) -{ - mOut.writeInt32(BC_REQUEST_DEATH_NOTIFICATION); - mOut.writeInt32((int32_t)handle); - mOut.writeInt32((int32_t)proxy); - return NO_ERROR; -} - -status_t IPCThreadState::clearDeathNotification(int32_t handle, BpBinder* proxy) -{ - mOut.writeInt32(BC_CLEAR_DEATH_NOTIFICATION); - mOut.writeInt32((int32_t)handle); - mOut.writeInt32((int32_t)proxy); - return NO_ERROR; -} - -IPCThreadState::IPCThreadState() - : mProcess(ProcessState::self()) -{ - pthread_setspecific(gTLS, this); - clearCaller(); - mIn.setDataCapacity(256); - mOut.setDataCapacity(256); -} - -IPCThreadState::~IPCThreadState() -{ -} - -status_t IPCThreadState::sendReply(const Parcel& reply, uint32_t flags) -{ - status_t err; - status_t statusBuffer; - err = writeTransactionData(BC_REPLY, flags, -1, 0, reply, &statusBuffer); - if (err < NO_ERROR) return err; - - return waitForResponse(NULL, NULL); -} - -status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult) -{ - int32_t cmd; - int32_t err; - - while (1) { - if ((err=talkWithDriver()) < NO_ERROR) break; - err = mIn.errorCheck(); - if (err < NO_ERROR) break; - if (mIn.dataAvail() == 0) continue; - - cmd = mIn.readInt32(); - - IF_LOG_COMMANDS() { - alog << "Processing waitForResponse Command: " - << getReturnString(cmd) << endl; - } - - switch (cmd) { - case BR_TRANSACTION_COMPLETE: - if (!reply && !acquireResult) goto finish; - break; - - case BR_DEAD_REPLY: - err = DEAD_OBJECT; - goto finish; - - case BR_FAILED_REPLY: - err = FAILED_TRANSACTION; - goto finish; - - case BR_ACQUIRE_RESULT: - { - LOG_ASSERT(acquireResult != NULL, "Unexpected brACQUIRE_RESULT"); - const int32_t result = mIn.readInt32(); - if (!acquireResult) continue; - *acquireResult = result ? NO_ERROR : INVALID_OPERATION; - } - goto finish; - - case BR_REPLY: - { - binder_transaction_data tr; - err = mIn.read(&tr, sizeof(tr)); - LOG_ASSERT(err == NO_ERROR, "Not enough command data for brREPLY"); - if (err != NO_ERROR) goto finish; - - if (reply) { - if ((tr.flags & TF_STATUS_CODE) == 0) { - reply->ipcSetDataReference( - reinterpret_cast(tr.data.ptr.buffer), - tr.data_size, - reinterpret_cast(tr.data.ptr.offsets), - tr.offsets_size/sizeof(size_t), - freeBuffer, this); - } else { - err = *static_cast(tr.data.ptr.buffer); - freeBuffer(NULL, - reinterpret_cast(tr.data.ptr.buffer), - tr.data_size, - reinterpret_cast(tr.data.ptr.offsets), - tr.offsets_size/sizeof(size_t), this); - } - } else { - freeBuffer(NULL, - reinterpret_cast(tr.data.ptr.buffer), - tr.data_size, - reinterpret_cast(tr.data.ptr.offsets), - tr.offsets_size/sizeof(size_t), this); - continue; - } - } - goto finish; - - default: - err = executeCommand(cmd); - if (err != NO_ERROR) goto finish; - break; - } - } - -finish: - if (err != NO_ERROR) { - if (acquireResult) *acquireResult = err; - if (reply) reply->setError(err); - mLastError = err; - } - - return err; -} - -status_t IPCThreadState::talkWithDriver(bool doReceive) -{ - LOG_ASSERT(mProcess->mDriverFD >= 0, "Binder driver is not opened"); - - binder_write_read bwr; - - // Is the read buffer empty? - const bool needRead = mIn.dataPosition() >= mIn.dataSize(); - - // We don't want to write anything if we are still reading - // from data left in the input buffer and the caller - // has requested to read the next data. - const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : 0; - - bwr.write_size = outAvail; - bwr.write_buffer = (long unsigned int)mOut.data(); - - // This is what we'll read. - if (doReceive && needRead) { - bwr.read_size = mIn.dataCapacity(); - bwr.read_buffer = (long unsigned int)mIn.data(); - } else { - bwr.read_size = 0; - } - - IF_LOG_COMMANDS() { - TextOutput::Bundle _b(alog); - if (outAvail != 0) { - alog << "Sending commands to driver: " << indent; - const void* cmds = (const void*)bwr.write_buffer; - const void* end = ((const uint8_t*)cmds)+bwr.write_size; - alog << HexDump(cmds, bwr.write_size) << endl; - while (cmds < end) cmds = printCommand(alog, cmds); - alog << dedent; - } - alog << "Size of receive buffer: " << bwr.read_size - << ", needRead: " << needRead << ", doReceive: " << doReceive << endl; - } - - // Return immediately if there is nothing to do. - if ((bwr.write_size == 0) && (bwr.read_size == 0)) return NO_ERROR; - - bwr.write_consumed = 0; - bwr.read_consumed = 0; - status_t err; - do { - IF_LOG_COMMANDS() { - alog << "About to read/write, write size = " << mOut.dataSize() << endl; - } -#if defined(HAVE_ANDROID_OS) - if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0) - err = NO_ERROR; - else - err = -errno; -#else - err = INVALID_OPERATION; -#endif - IF_LOG_COMMANDS() { - alog << "Finished read/write, write size = " << mOut.dataSize() << endl; - } - } while (err == -EINTR); - - IF_LOG_COMMANDS() { - alog << "Our err: " << (void*)err << ", write consumed: " - << bwr.write_consumed << " (of " << mOut.dataSize() - << "), read consumed: " << bwr.read_consumed << endl; - } - - if (err >= NO_ERROR) { - if (bwr.write_consumed > 0) { - if (bwr.write_consumed < (ssize_t)mOut.dataSize()) - mOut.remove(0, bwr.write_consumed); - else - mOut.setDataSize(0); - } - if (bwr.read_consumed > 0) { - mIn.setDataSize(bwr.read_consumed); - mIn.setDataPosition(0); - } - IF_LOG_COMMANDS() { - TextOutput::Bundle _b(alog); - alog << "Remaining data size: " << mOut.dataSize() << endl; - alog << "Received commands from driver: " << indent; - const void* cmds = mIn.data(); - const void* end = mIn.data() + mIn.dataSize(); - alog << HexDump(cmds, mIn.dataSize()) << endl; - while (cmds < end) cmds = printReturnCommand(alog, cmds); - alog << dedent; - } - return NO_ERROR; - } - - return err; -} - -status_t IPCThreadState::writeTransactionData(int32_t cmd, uint32_t binderFlags, - int32_t handle, uint32_t code, const Parcel& data, status_t* statusBuffer) -{ - binder_transaction_data tr; - - tr.target.handle = handle; - tr.code = code; - tr.flags = binderFlags; - - const status_t err = data.errorCheck(); - if (err == NO_ERROR) { - tr.data_size = data.ipcDataSize(); - tr.data.ptr.buffer = data.ipcData(); - tr.offsets_size = data.ipcObjectsCount()*sizeof(size_t); - tr.data.ptr.offsets = data.ipcObjects(); - } else if (statusBuffer) { - tr.flags |= TF_STATUS_CODE; - *statusBuffer = err; - tr.data_size = sizeof(status_t); - tr.data.ptr.buffer = statusBuffer; - tr.offsets_size = 0; - tr.data.ptr.offsets = NULL; - } else { - return (mLastError = err); - } - - mOut.writeInt32(cmd); - mOut.write(&tr, sizeof(tr)); - - return NO_ERROR; -} - -sp the_context_object; - -void setTheContextObject(sp obj) -{ - the_context_object = obj; -} - -status_t IPCThreadState::executeCommand(int32_t cmd) -{ - BBinder* obj; - RefBase::weakref_type* refs; - status_t result = NO_ERROR; - - switch (cmd) { - case BR_ERROR: - result = mIn.readInt32(); - break; - - case BR_OK: - break; - - case BR_ACQUIRE: - refs = (RefBase::weakref_type*)mIn.readInt32(); - obj = (BBinder*)mIn.readInt32(); - LOG_ASSERT(refs->refBase() == obj, - "BR_ACQUIRE: object %p does not match cookie %p (expected %p)", - refs, obj, refs->refBase()); - obj->incStrong(mProcess.get()); - IF_LOG_REMOTEREFS() { - LOG_REMOTEREFS("BR_ACQUIRE from driver on %p", obj); - obj->printRefs(); - } - mOut.writeInt32(BC_ACQUIRE_DONE); - mOut.writeInt32((int32_t)refs); - mOut.writeInt32((int32_t)obj); - break; - - case BR_RELEASE: - refs = (RefBase::weakref_type*)mIn.readInt32(); - obj = (BBinder*)mIn.readInt32(); - LOG_ASSERT(refs->refBase() == obj, - "BR_RELEASE: object %p does not match cookie %p (expected %p)", - refs, obj, refs->refBase()); - IF_LOG_REMOTEREFS() { - LOG_REMOTEREFS("BR_RELEASE from driver on %p", obj); - obj->printRefs(); - } - mPendingStrongDerefs.push(obj); - break; - - case BR_INCREFS: - refs = (RefBase::weakref_type*)mIn.readInt32(); - obj = (BBinder*)mIn.readInt32(); - refs->incWeak(mProcess.get()); - mOut.writeInt32(BC_INCREFS_DONE); - mOut.writeInt32((int32_t)refs); - mOut.writeInt32((int32_t)obj); - break; - - case BR_DECREFS: - refs = (RefBase::weakref_type*)mIn.readInt32(); - obj = (BBinder*)mIn.readInt32(); - // NOTE: This assertion is not valid, because the object may no - // longer exist (thus the (BBinder*)cast above resulting in a different - // memory address). - //LOG_ASSERT(refs->refBase() == obj, - // "BR_DECREFS: object %p does not match cookie %p (expected %p)", - // refs, obj, refs->refBase()); - mPendingWeakDerefs.push(refs); - break; - - case BR_ATTEMPT_ACQUIRE: - refs = (RefBase::weakref_type*)mIn.readInt32(); - obj = (BBinder*)mIn.readInt32(); - - { - const bool success = refs->attemptIncStrong(mProcess.get()); - LOG_ASSERT(success && refs->refBase() == obj, - "BR_ATTEMPT_ACQUIRE: object %p does not match cookie %p (expected %p)", - refs, obj, refs->refBase()); - - mOut.writeInt32(BC_ACQUIRE_RESULT); - mOut.writeInt32((int32_t)success); - } - break; - - case BR_TRANSACTION: - { - binder_transaction_data tr; - result = mIn.read(&tr, sizeof(tr)); - LOG_ASSERT(result == NO_ERROR, - "Not enough command data for brTRANSACTION"); - if (result != NO_ERROR) break; - - Parcel buffer; - buffer.ipcSetDataReference( - reinterpret_cast(tr.data.ptr.buffer), - tr.data_size, - reinterpret_cast(tr.data.ptr.offsets), - tr.offsets_size/sizeof(size_t), freeBuffer, this); - - const pid_t origPid = mCallingPid; - const uid_t origUid = mCallingUid; - - mCallingPid = tr.sender_pid; - mCallingUid = tr.sender_euid; - - //LOGI(">>>> TRANSACT from pid %d uid %d\n", mCallingPid, mCallingUid); - - Parcel reply; - IF_LOG_TRANSACTIONS() { - TextOutput::Bundle _b(alog); - alog << "BR_TRANSACTION thr " << (void*)pthread_self() - << " / obj " << tr.target.ptr << " / code " - << TypeCode(tr.code) << ": " << indent << buffer - << dedent << endl - << "Data addr = " - << reinterpret_cast(tr.data.ptr.buffer) - << ", offsets addr=" - << reinterpret_cast(tr.data.ptr.offsets) << endl; - } - if (tr.target.ptr) { - sp b((BBinder*)tr.cookie); - const status_t error = b->transact(tr.code, buffer, &reply, 0); - if (error < NO_ERROR) reply.setError(error); - - } else { - const status_t error = the_context_object->transact(tr.code, buffer, &reply, 0); - if (error < NO_ERROR) reply.setError(error); - } - - //LOGI("<<<< TRANSACT from pid %d restore pid %d uid %d\n", - // mCallingPid, origPid, origUid); - - if ((tr.flags & TF_ONE_WAY) == 0) { - LOG_ONEWAY("Sending reply to %d!", mCallingPid); - sendReply(reply, 0); - } else { - LOG_ONEWAY("NOT sending reply to %d!", mCallingPid); - } - - mCallingPid = origPid; - mCallingUid = origUid; - - IF_LOG_TRANSACTIONS() { - TextOutput::Bundle _b(alog); - alog << "BC_REPLY thr " << (void*)pthread_self() << " / obj " - << tr.target.ptr << ": " << indent << reply << dedent << endl; - } - - } - break; - - case BR_DEAD_BINDER: - { - BpBinder *proxy = (BpBinder*)mIn.readInt32(); - proxy->sendObituary(); - mOut.writeInt32(BC_DEAD_BINDER_DONE); - mOut.writeInt32((int32_t)proxy); - } break; - - case BR_CLEAR_DEATH_NOTIFICATION_DONE: - { - BpBinder *proxy = (BpBinder*)mIn.readInt32(); - proxy->getWeakRefs()->decWeak(proxy); - } break; - - case BR_FINISHED: - result = TIMED_OUT; - break; - - case BR_NOOP: - break; - - case BR_SPAWN_LOOPER: - mProcess->spawnPooledThread(false); - break; - - default: - printf("*** BAD COMMAND %d received from Binder driver\n", cmd); - result = UNKNOWN_ERROR; - break; - } - - if (result != NO_ERROR) { - mLastError = result; - } - - return result; -} - -void IPCThreadState::threadDestructor(void *st) -{ - IPCThreadState* const self = static_cast(st); - if (self) { - self->flushCommands(); -#if defined(HAVE_ANDROID_OS) - ioctl(self->mProcess->mDriverFD, BINDER_THREAD_EXIT, 0); -#endif - delete self; - } -} - - -void IPCThreadState::freeBuffer(Parcel* parcel, const uint8_t* data, size_t dataSize, - const size_t* objects, size_t objectsSize, - void* cookie) -{ - //LOGI("Freeing parcel %p", &parcel); - IF_LOG_COMMANDS() { - alog << "Writing BC_FREE_BUFFER for " << data << endl; - } - LOG_ASSERT(data != NULL, "Called with NULL data"); - if (parcel != NULL) parcel->closeFileDescriptors(); - IPCThreadState* state = self(); - state->mOut.writeInt32(BC_FREE_BUFFER); - state->mOut.writeInt32((int32_t)data); -} - -}; // namespace android diff --git a/libs/utils/IPermissionController.cpp b/libs/utils/IPermissionController.cpp deleted file mode 100644 index f01d38fd1..000000000 --- a/libs/utils/IPermissionController.cpp +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (C) 2005 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 "PermissionController" - -#include - -#include -#include -#include -#include - -#include - -namespace android { - -// ---------------------------------------------------------------------- - -class BpPermissionController : public BpInterface -{ -public: - BpPermissionController(const sp& impl) - : BpInterface(impl) - { - } - - virtual bool checkPermission(const String16& permission, int32_t pid, int32_t uid) - { - Parcel data, reply; - data.writeInterfaceToken(IPermissionController::getInterfaceDescriptor()); - data.writeString16(permission); - data.writeInt32(pid); - data.writeInt32(uid); - remote()->transact(CHECK_PERMISSION_TRANSACTION, data, &reply); - // fail on exception - if (reply.readInt32() != 0) return 0; - return reply.readInt32() != 0; - } -}; - -IMPLEMENT_META_INTERFACE(PermissionController, "android.os.IPermissionController"); - -// ---------------------------------------------------------------------- - -#define CHECK_INTERFACE(interface, data, reply) \ - do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ - LOGW("Call incorrectly routed to " #interface); \ - return PERMISSION_DENIED; \ - } } while (0) - -status_t BnPermissionController::onTransact( - uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) -{ - //printf("PermissionController received: "); data.print(); - switch(code) { - case CHECK_PERMISSION_TRANSACTION: { - CHECK_INTERFACE(IPermissionController, data, reply); - String16 permission = data.readString16(); - int32_t pid = data.readInt32(); - int32_t uid = data.readInt32(); - bool res = checkPermission(permission, pid, uid); - // write exception - reply->writeInt32(0); - reply->writeInt32(res ? 1 : 0); - return NO_ERROR; - } break; - default: - return BBinder::onTransact(code, data, reply, flags); - } -} - -}; // namespace android - diff --git a/libs/utils/IServiceManager.cpp b/libs/utils/IServiceManager.cpp deleted file mode 100644 index 9beeaddde..000000000 --- a/libs/utils/IServiceManager.cpp +++ /dev/null @@ -1,230 +0,0 @@ -/* - * Copyright (C) 2005 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 "ServiceManager" - -#include - -#include -#include -#include -#include -#include -#include - -#include - -#include - -namespace android { - -sp defaultServiceManager() -{ - if (gDefaultServiceManager != NULL) return gDefaultServiceManager; - - { - AutoMutex _l(gDefaultServiceManagerLock); - if (gDefaultServiceManager == NULL) { - gDefaultServiceManager = interface_cast( - ProcessState::self()->getContextObject(NULL)); - } - } - - return gDefaultServiceManager; -} - -bool checkCallingPermission(const String16& permission) -{ - return checkCallingPermission(permission, NULL, NULL); -} - -static String16 _permission("permission"); - -bool checkCallingPermission(const String16& permission, int32_t* outPid, int32_t* outUid) -{ - IPCThreadState* ipcState = IPCThreadState::self(); - int32_t pid = ipcState->getCallingPid(); - int32_t uid = ipcState->getCallingUid(); - if (outPid) *outPid = pid; - if (outUid) *outUid= uid; - - sp pc; - gDefaultServiceManagerLock.lock(); - pc = gPermissionController; - gDefaultServiceManagerLock.unlock(); - - int64_t startTime = 0; - - while (true) { - if (pc != NULL) { - bool res = pc->checkPermission(permission, pid, uid); - if (res) { - if (startTime != 0) { - LOGI("Check passed after %d seconds for %s from uid=%d pid=%d", - (int)((uptimeMillis()-startTime)/1000), - String8(permission).string(), uid, pid); - } - return res; - } - - // Is this a permission failure, or did the controller go away? - if (pc->asBinder()->isBinderAlive()) { - LOGW("Permission failure: %s from uid=%d pid=%d", - String8(permission).string(), uid, pid); - return false; - } - - // Object is dead! - gDefaultServiceManagerLock.lock(); - if (gPermissionController == pc) { - gPermissionController = NULL; - } - gDefaultServiceManagerLock.unlock(); - } - - // Need to retrieve the permission controller. - sp binder = defaultServiceManager()->checkService(_permission); - if (binder == NULL) { - // Wait for the permission controller to come back... - if (startTime == 0) { - startTime = uptimeMillis(); - LOGI("Waiting to check permission %s from uid=%d pid=%d", - String8(permission).string(), uid, pid); - } - sleep(1); - } else { - pc = interface_cast(binder); - // Install the new permission controller, and try again. - gDefaultServiceManagerLock.lock(); - gPermissionController = pc; - gDefaultServiceManagerLock.unlock(); - } - } -} - -// ---------------------------------------------------------------------- - -class BpServiceManager : public BpInterface -{ -public: - BpServiceManager(const sp& impl) - : BpInterface(impl) - { - } - - virtual sp getService(const String16& name) const - { - unsigned n; - for (n = 0; n < 5; n++){ - sp svc = checkService(name); - if (svc != NULL) return svc; - LOGI("Waiting for sevice %s...\n", String8(name).string()); - sleep(1); - } - return NULL; - } - - virtual sp checkService( const String16& name) const - { - Parcel data, reply; - data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor()); - data.writeString16(name); - remote()->transact(CHECK_SERVICE_TRANSACTION, data, &reply); - return reply.readStrongBinder(); - } - - virtual status_t addService(const String16& name, const sp& service) - { - Parcel data, reply; - data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor()); - data.writeString16(name); - data.writeStrongBinder(service); - status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply); - return err == NO_ERROR ? reply.readInt32() : err; - } - - virtual Vector listServices() - { - Vector res; - int n = 0; - - for (;;) { - Parcel data, reply; - data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor()); - data.writeInt32(n++); - status_t err = remote()->transact(LIST_SERVICES_TRANSACTION, data, &reply); - if (err != NO_ERROR) - break; - res.add(reply.readString16()); - } - return res; - } -}; - -IMPLEMENT_META_INTERFACE(ServiceManager, "android.os.IServiceManager"); - -// ---------------------------------------------------------------------- - -#define CHECK_INTERFACE(interface, data, reply) \ - do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ - LOGW("Call incorrectly routed to " #interface); \ - return PERMISSION_DENIED; \ - } } while (0) - -status_t BnServiceManager::onTransact( - uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) -{ - //printf("ServiceManager received: "); data.print(); - switch(code) { - case GET_SERVICE_TRANSACTION: { - CHECK_INTERFACE(IServiceManager, data, reply); - String16 which = data.readString16(); - sp b = const_cast(this)->getService(which); - reply->writeStrongBinder(b); - return NO_ERROR; - } break; - case CHECK_SERVICE_TRANSACTION: { - CHECK_INTERFACE(IServiceManager, data, reply); - String16 which = data.readString16(); - sp b = const_cast(this)->checkService(which); - reply->writeStrongBinder(b); - return NO_ERROR; - } break; - case ADD_SERVICE_TRANSACTION: { - CHECK_INTERFACE(IServiceManager, data, reply); - String16 which = data.readString16(); - sp b = data.readStrongBinder(); - status_t err = addService(which, b); - reply->writeInt32(err); - return NO_ERROR; - } break; - case LIST_SERVICES_TRANSACTION: { - CHECK_INTERFACE(IServiceManager, data, reply); - Vector list = listServices(); - const size_t N = list.size(); - reply->writeInt32(N); - for (size_t i=0; iwriteString16(list[i]); - } - return NO_ERROR; - } break; - default: - return BBinder::onTransact(code, data, reply, flags); - } -} - -}; // namespace android - diff --git a/libs/utils/InetAddress.cpp b/libs/utils/InetAddress.cpp deleted file mode 100644 index 39a0a6839..000000000 --- a/libs/utils/InetAddress.cpp +++ /dev/null @@ -1,236 +0,0 @@ -/* - * Copyright (C) 2005 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. - */ - -// -// Internet address class. -// -#ifdef HAVE_WINSOCK -# include -#else -# include -# include -# include -//# include -# include -#endif - -#include -#include -#include - -#include -#include -#include -#include -#include - -using namespace android; - - -/* - * =========================================================================== - * InetAddress - * =========================================================================== - */ - -// lock for the next couple of functions; could tuck into InetAddress -static Mutex* gGHBNLock; - -/* - * Lock/unlock access to the hostent struct returned by gethostbyname(). - */ -static inline void lock_gethostbyname(void) -{ - if (gGHBNLock == NULL) - gGHBNLock = new Mutex; - gGHBNLock->lock(); -} -static inline void unlock_gethostbyname(void) -{ - assert(gGHBNLock != NULL); - gGHBNLock->unlock(); -} - - -/* - * Constructor -- just init members. This is private so that callers - * are required to use getByName(). - */ -InetAddress::InetAddress(void) - : mAddress(NULL), mLength(-1), mName(NULL) -{ -} - -/* - * Destructor -- free address storage. - */ -InetAddress::~InetAddress(void) -{ - delete[] (char*) mAddress; - delete[] mName; -} - -/* - * Copy constructor. - */ -InetAddress::InetAddress(const InetAddress& orig) -{ - *this = orig; // use assignment code -} - -/* - * Assignment operator. - */ -InetAddress& InetAddress::operator=(const InetAddress& addr) -{ - // handle self-assignment - if (this == &addr) - return *this; - // copy mLength and mAddress - mLength = addr.mLength; - if (mLength > 0) { - mAddress = new char[mLength]; - memcpy(mAddress, addr.mAddress, mLength); - LOG(LOG_DEBUG, "socket", - "HEY: copied %d bytes in assignment operator\n", mLength); - } else { - mAddress = NULL; - } - // copy mName - mName = new char[strlen(addr.mName)+1]; - strcpy(mName, addr.mName); - - return *this; -} - -/* - * Create a new object from a name or a dotted-number IP notation. - * - * Returns NULL on failure. - */ -InetAddress* -InetAddress::getByName(const char* host) -{ - InetAddress* newAddr = NULL; - struct sockaddr_in addr; - struct hostent* he; - DurationTimer hostTimer, lockTimer; - - // gethostbyname() isn't reentrant, so we need to lock things until - // we can copy the data out. - lockTimer.start(); - lock_gethostbyname(); - hostTimer.start(); - - he = gethostbyname(host); - if (he == NULL) { - LOG(LOG_WARN, "socket", "WARNING: cannot resolve host %s\n", host); - unlock_gethostbyname(); - return NULL; - } - - memcpy(&addr.sin_addr, he->h_addr, he->h_length); - addr.sin_family = he->h_addrtype; - addr.sin_port = 0; - - // got it, unlock us - hostTimer.stop(); - he = NULL; - unlock_gethostbyname(); - - lockTimer.stop(); - if ((long) lockTimer.durationUsecs() > 100000) { - long lockTime = (long) lockTimer.durationUsecs(); - long hostTime = (long) hostTimer.durationUsecs(); - LOG(LOG_DEBUG, "socket", - "Lookup of %s took %.3fs (gethostbyname=%.3fs lock=%.3fs)\n", - host, lockTime / 1000000.0, hostTime / 1000000.0, - (lockTime - hostTime) / 1000000.0); - } - - // Alloc storage and copy it over. - newAddr = new InetAddress(); - if (newAddr == NULL) - return NULL; - - newAddr->mLength = sizeof(struct sockaddr_in); - newAddr->mAddress = new char[sizeof(struct sockaddr_in)]; - if (newAddr->mAddress == NULL) { - delete newAddr; - return NULL; - } - memcpy(newAddr->mAddress, &addr, newAddr->mLength); - - // Keep this for debug messages. - newAddr->mName = new char[strlen(host)+1]; - if (newAddr->mName == NULL) { - delete newAddr; - return NULL; - } - strcpy(newAddr->mName, host); - - return newAddr; -} - - -/* - * =========================================================================== - * InetSocketAddress - * =========================================================================== - */ - -/* - * Create an address with the host wildcard (INADDR_ANY). - */ -bool InetSocketAddress::create(int port) -{ - assert(mAddress == NULL); - - mAddress = InetAddress::getByName("0.0.0.0"); - if (mAddress == NULL) - return false; - mPort = port; - return true; -} - -/* - * Create address with host and port specified. - */ -bool InetSocketAddress::create(const InetAddress* addr, int port) -{ - assert(mAddress == NULL); - - mAddress = new InetAddress(*addr); // make a copy - if (mAddress == NULL) - return false; - mPort = port; - return true; -} - -/* - * Create address with host and port specified. - */ -bool InetSocketAddress::create(const char* host, int port) -{ - assert(mAddress == NULL); - - mAddress = InetAddress::getByName(host); - if (mAddress == NULL) - return false; - mPort = port; - return true; -} - diff --git a/libs/utils/LogSocket.cpp b/libs/utils/LogSocket.cpp deleted file mode 100644 index 55c1b99af..000000000 --- a/libs/utils/LogSocket.cpp +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#ifndef HAVE_WINSOCK -//#define SOCKETLOG -#endif - -#ifdef SOCKETLOG - -#define LOG_TAG "SOCKETLOG" - -#include -#include -#include "utils/LogSocket.h" -#include "utils/logger.h" -#include "cutils/hashmap.h" - -// defined in //device/data/etc/event-log-tags -#define SOCKET_CLOSE_LOG 51000 - -static Hashmap* statsMap = NULL; - -#define LOG_LIST_NUMBER 5 - -typedef struct SocketStats { - int fd; - unsigned int send; - unsigned int recv; - unsigned int ip; - unsigned short port; - short reason; -}SocketStats; - -SocketStats *get_socket_stats(int fd) { - if (statsMap == NULL) { - statsMap = hashmapCreate(8, &hashmapIntHash, &hashmapIntEquals); - } - - SocketStats *s = (SocketStats*) hashmapGet(statsMap, &fd); - if (s == NULL) { - // LOGD("create SocketStats for fd %d", fd); - s = (SocketStats*) malloc(sizeof(SocketStats)); - memset(s, 0, sizeof(SocketStats)); - s->fd = fd; - hashmapPut(statsMap, &s->fd, s); - } - return s; -} - -void log_socket_connect(int fd, unsigned int ip, unsigned short port) { - // LOGD("log_socket_connect for fd %d ip %d port%d", fd, ip, port); - SocketStats *s = get_socket_stats(fd); - s->ip = ip; - s->port = port; -} - -void add_send_stats(int fd, int send) { - if (send <=0) { - LOGE("add_send_stats send %d", send); - return; - } - SocketStats *s = get_socket_stats(fd); - s->send += send; - // LOGD("add_send_stats for fd %d ip %d port%d", fd, s->ip, s->port); -} - -void add_recv_stats(int fd, int recv) { - if (recv <=0) { - LOGE("add_recv_stats recv %d", recv); - return; - } - SocketStats *s = get_socket_stats(fd); - s->recv += recv; - // LOGD("add_recv_stats for fd %d ip %d port%d", fd, s->ip, s->port); -} - -char* put_int(char* buf, int value) { - *buf = EVENT_TYPE_INT; - buf++; - memcpy(buf, &value, sizeof(int)); - return buf + sizeof(int); -} - -void log_socket_close(int fd, short reason) { - if (statsMap) { - SocketStats *s = (SocketStats*) hashmapGet(statsMap, &fd); - if (s != NULL) { - if (s->send != 0 || s->recv != 0) { - s->reason = reason; - // 5 int + list type need 2 bytes - char buf[LOG_LIST_NUMBER * 5 + 2]; - buf[0] = EVENT_TYPE_LIST; - buf[1] = LOG_LIST_NUMBER; - char* writePos = buf + 2; - writePos = put_int(writePos, s->send); - writePos = put_int(writePos, s->recv); - writePos = put_int(writePos, s->ip); - writePos = put_int(writePos, s->port); - writePos = put_int(writePos, s->reason); - - android_bWriteLog(SOCKET_CLOSE_LOG, buf, sizeof(buf)); - // LOGD("send %d recv %d reason %d", s->send, s->recv, s->reason); - } - hashmapRemove(statsMap, &s->fd); - free(s); - } - } -} - -#else -void add_send_stats(int fd, int send) {} -void add_recv_stats(int fd, int recv) {} -void log_socket_close(int fd, short reason) {} -void log_socket_connect(int fd, unsigned int ip, unsigned short port) {} -#endif diff --git a/libs/utils/MODULE_LICENSE_APACHE2 b/libs/utils/MODULE_LICENSE_APACHE2 deleted file mode 100644 index e69de29bb..000000000 diff --git a/libs/utils/MemoryBase.cpp b/libs/utils/MemoryBase.cpp deleted file mode 100644 index f25e11c6b..000000000 --- a/libs/utils/MemoryBase.cpp +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#include -#include - -#include - - -namespace android { - -// --------------------------------------------------------------------------- - -MemoryBase::MemoryBase(const sp& heap, - ssize_t offset, size_t size) - : mSize(size), mOffset(offset), mHeap(heap) -{ -} - -sp MemoryBase::getMemory(ssize_t* offset, size_t* size) const -{ - if (offset) *offset = mOffset; - if (size) *size = mSize; - return mHeap; -} - -MemoryBase::~MemoryBase() -{ -} - -// --------------------------------------------------------------------------- -}; // namespace android diff --git a/libs/utils/MemoryDealer.cpp b/libs/utils/MemoryDealer.cpp deleted file mode 100644 index cf8201b85..000000000 --- a/libs/utils/MemoryDealer.cpp +++ /dev/null @@ -1,409 +0,0 @@ -/* - * 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. - */ - -#define LOG_TAG "MemoryDealer" - -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -namespace android { - - -// ---------------------------------------------------------------------------- - -class SimpleMemory : public MemoryBase { -public: - SimpleMemory(const sp& heap, ssize_t offset, size_t size); - virtual ~SimpleMemory(); -}; - - -// ---------------------------------------------------------------------------- - -MemoryDealer::Allocation::Allocation( - const sp& dealer, ssize_t offset, size_t size, - const sp& memory) - : mDealer(dealer), mOffset(offset), mSize(size), mMemory(memory) -{ -} - -MemoryDealer::Allocation::~Allocation() -{ - if (mSize) { - /* NOTE: it's VERY important to not free allocations of size 0 because - * they're special as they don't have any record in the allocator - * and could alias some real allocation (their offset is zero). */ - mDealer->deallocate(mOffset); - } -} - -sp MemoryDealer::Allocation::getMemory( - ssize_t* offset, size_t* size) const -{ - return mMemory->getMemory(offset, size); -} - -// ---------------------------------------------------------------------------- - -MemoryDealer::MemoryDealer(size_t size, uint32_t flags, const char* name) - : mHeap(new SharedHeap(size, flags, name)), - mAllocator(new SimpleBestFitAllocator(size)) -{ -} - -MemoryDealer::MemoryDealer(const sp& heap) - : mHeap(heap), - mAllocator(new SimpleBestFitAllocator(heap->virtualSize())) -{ -} - -MemoryDealer::MemoryDealer( const sp& heap, - const sp& allocator) - : mHeap(heap), mAllocator(allocator) -{ -} - -MemoryDealer::~MemoryDealer() -{ -} - -sp MemoryDealer::allocate(size_t size, uint32_t flags) -{ - sp memory; - const ssize_t offset = allocator()->allocate(size, flags); - if (offset >= 0) { - sp new_memory = heap()->mapMemory(offset, size); - if (new_memory != 0) { - memory = new Allocation(this, offset, size, new_memory); - } else { - LOGE("couldn't map [%8x, %d]", offset, size); - if (size) { - /* NOTE: it's VERY important to not free allocations of size 0 - * because they're special as they don't have any record in the - * allocator and could alias some real allocation - * (their offset is zero). */ - allocator()->deallocate(offset); - } - } - } - return memory; -} - -void MemoryDealer::deallocate(size_t offset) -{ - allocator()->deallocate(offset); -} - -void MemoryDealer::dump(const char* what, uint32_t flags) const -{ - allocator()->dump(what, flags); -} - -const sp& MemoryDealer::heap() const { - return mHeap; -} - -const sp& MemoryDealer::allocator() const { - return mAllocator; -} - -// ---------------------------------------------------------------------------- - -// align all the memory blocks on a cache-line boundary -const int SimpleBestFitAllocator::kMemoryAlign = 32; - -SimpleBestFitAllocator::SimpleBestFitAllocator(size_t size) -{ - size_t pagesize = getpagesize(); - mHeapSize = ((size + pagesize-1) & ~(pagesize-1)); - - chunk_t* node = new chunk_t(0, mHeapSize / kMemoryAlign); - mList.insertHead(node); -} - -SimpleBestFitAllocator::~SimpleBestFitAllocator() -{ - while(!mList.isEmpty()) { - delete mList.remove(mList.head()); - } -} - -size_t SimpleBestFitAllocator::size() const -{ - return mHeapSize; -} - -size_t SimpleBestFitAllocator::allocate(size_t size, uint32_t flags) -{ - Mutex::Autolock _l(mLock); - ssize_t offset = alloc(size, flags); - return offset; -} - -status_t SimpleBestFitAllocator::deallocate(size_t offset) -{ - Mutex::Autolock _l(mLock); - chunk_t const * const freed = dealloc(offset); - if (freed) { - return NO_ERROR; - } - return NAME_NOT_FOUND; -} - -ssize_t SimpleBestFitAllocator::alloc(size_t size, uint32_t flags) -{ - if (size == 0) { - return 0; - } - size = (size + kMemoryAlign-1) / kMemoryAlign; - chunk_t* free_chunk = 0; - chunk_t* cur = mList.head(); - - size_t pagesize = getpagesize(); - while (cur) { - int extra = 0; - if (flags & PAGE_ALIGNED) - extra = ( -cur->start & ((pagesize/kMemoryAlign)-1) ) ; - - // best fit - if (cur->free && (cur->size >= (size+extra))) { - if ((!free_chunk) || (cur->size < free_chunk->size)) { - free_chunk = cur; - } - if (cur->size == size) { - break; - } - } - cur = cur->next; - } - - if (free_chunk) { - const size_t free_size = free_chunk->size; - free_chunk->free = 0; - free_chunk->size = size; - if (free_size > size) { - int extra = 0; - if (flags & PAGE_ALIGNED) - extra = ( -free_chunk->start & ((pagesize/kMemoryAlign)-1) ) ; - if (extra) { - chunk_t* split = new chunk_t(free_chunk->start, extra); - free_chunk->start += extra; - mList.insertBefore(free_chunk, split); - } - - LOGE_IF((flags&PAGE_ALIGNED) && - ((free_chunk->start*kMemoryAlign)&(pagesize-1)), - "PAGE_ALIGNED requested, but page is not aligned!!!"); - - const ssize_t tail_free = free_size - (size+extra); - if (tail_free > 0) { - chunk_t* split = new chunk_t( - free_chunk->start + free_chunk->size, tail_free); - mList.insertAfter(free_chunk, split); - } - } - return (free_chunk->start)*kMemoryAlign; - } - return NO_MEMORY; -} - -SimpleBestFitAllocator::chunk_t* SimpleBestFitAllocator::dealloc(size_t start) -{ - start = start / kMemoryAlign; - chunk_t* cur = mList.head(); - while (cur) { - if (cur->start == start) { - LOG_FATAL_IF(cur->free, - "block at offset 0x%08lX of size 0x%08lX already freed", - cur->start*kMemoryAlign, cur->size*kMemoryAlign); - - // merge freed blocks together - chunk_t* freed = cur; - cur->free = 1; - do { - chunk_t* const p = cur->prev; - chunk_t* const n = cur->next; - if (p && (p->free || !cur->size)) { - freed = p; - p->size += cur->size; - mList.remove(cur); - delete cur; - } - cur = n; - } while (cur && cur->free); - - #ifndef NDEBUG - if (!freed->free) { - dump_l("dealloc (!freed->free)"); - } - #endif - LOG_FATAL_IF(!freed->free, - "freed block at offset 0x%08lX of size 0x%08lX is not free!", - freed->start * kMemoryAlign, freed->size * kMemoryAlign); - - return freed; - } - cur = cur->next; - } - return 0; -} - -void SimpleBestFitAllocator::dump(const char* what, uint32_t flags) const -{ - Mutex::Autolock _l(mLock); - dump_l(what, flags); -} - -void SimpleBestFitAllocator::dump_l(const char* what, uint32_t flags) const -{ - String8 result; - dump_l(result, what, flags); - LOGD("%s", result.string()); -} - -void SimpleBestFitAllocator::dump(String8& result, - const char* what, uint32_t flags) const -{ - Mutex::Autolock _l(mLock); - dump_l(result, what, flags); -} - -void SimpleBestFitAllocator::dump_l(String8& result, - const char* what, uint32_t flags) const -{ - size_t size = 0; - int32_t i = 0; - chunk_t const* cur = mList.head(); - - const size_t SIZE = 256; - char buffer[SIZE]; - snprintf(buffer, SIZE, " %s (%p, size=%u)\n", - what, this, (unsigned int)mHeapSize); - - result.append(buffer); - - while (cur) { - const char* errs[] = {"", "| link bogus NP", - "| link bogus PN", "| link bogus NP+PN" }; - int np = ((cur->next) && cur->next->prev != cur) ? 1 : 0; - int pn = ((cur->prev) && cur->prev->next != cur) ? 2 : 0; - - snprintf(buffer, SIZE, " %3u: %08x | 0x%08X | 0x%08X | %s %s\n", - i, int(cur), int(cur->start*kMemoryAlign), - int(cur->size*kMemoryAlign), - int(cur->free) ? "F" : "A", - errs[np|pn]); - - result.append(buffer); - - if (!cur->free) - size += cur->size*kMemoryAlign; - - i++; - cur = cur->next; - } - snprintf(buffer, SIZE, " size allocated: %u (%u KB)\n", int(size), int(size/1024)); - result.append(buffer); -} - -// ---------------------------------------------------------------------------- - - -SharedHeap::SharedHeap(size_t size, uint32_t flags, char const * name) - : MemoryHeapBase(size, flags, name) -{ -} - -SharedHeap::~SharedHeap() -{ -} - -sp SharedHeap::mapMemory(size_t offset, size_t size) -{ - return new SimpleMemory(this, offset, size); -} - - -SimpleMemory::SimpleMemory(const sp& heap, - ssize_t offset, size_t size) - : MemoryBase(heap, offset, size) -{ -#ifndef NDEBUG - void* const start_ptr = (void*)(intptr_t(heap->base()) + offset); - memset(start_ptr, 0xda, size); -#endif -} - -SimpleMemory::~SimpleMemory() -{ - size_t freedOffset = getOffset(); - size_t freedSize = getSize(); - - // keep the size to unmap in excess - size_t pagesize = getpagesize(); - size_t start = freedOffset; - size_t end = start + freedSize; - start &= ~(pagesize-1); - end = (end + pagesize-1) & ~(pagesize-1); - - // give back to the kernel the pages we don't need - size_t free_start = freedOffset; - size_t free_end = free_start + freedSize; - if (start < free_start) - start = free_start; - if (end > free_end) - end = free_end; - start = (start + pagesize-1) & ~(pagesize-1); - end &= ~(pagesize-1); - - if (start < end) { - void* const start_ptr = (void*)(intptr_t(getHeap()->base()) + start); - size_t size = end-start; - -#ifndef NDEBUG - memset(start_ptr, 0xdf, size); -#endif - - // MADV_REMOVE is not defined on Dapper based Goobuntu -#ifdef MADV_REMOVE - if (size) { - int err = madvise(start_ptr, size, MADV_REMOVE); - LOGW_IF(err, "madvise(%p, %u, MADV_REMOVE) returned %s", - start_ptr, size, err<0 ? strerror(errno) : "Ok"); - } -#endif - } -} - -}; // namespace android diff --git a/libs/utils/MemoryHeapBase.cpp b/libs/utils/MemoryHeapBase.cpp deleted file mode 100644 index 825172819..000000000 --- a/libs/utils/MemoryHeapBase.cpp +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "MemoryHeapBase" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -#if HAVE_ANDROID_OS -#include -#endif - - -namespace android { - -// --------------------------------------------------------------------------- - -MemoryHeapBase::MemoryHeapBase() - : mFD(-1), mSize(0), mBase(MAP_FAILED), - mDevice(NULL), mNeedUnmap(false) -{ -} - -MemoryHeapBase::MemoryHeapBase(size_t size, uint32_t flags, char const * name) - : mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags), - mDevice(0), mNeedUnmap(false) -{ - const size_t pagesize = getpagesize(); - size = ((size + pagesize-1) & ~(pagesize-1)); - int fd = ashmem_create_region(name == NULL ? "MemoryHeapBase" : name, size); - LOGE_IF(fd<0, "error creating ashmem region: %s", strerror(errno)); - if (fd >= 0) { - if (mapfd(fd, size) == NO_ERROR) { - if (flags & READ_ONLY) { - ashmem_set_prot_region(fd, PROT_READ); - } - } - } -} - -MemoryHeapBase::MemoryHeapBase(const char* device, size_t size, uint32_t flags) - : mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags), - mDevice(0), mNeedUnmap(false) -{ - int fd = open(device, O_RDWR); - LOGE_IF(fd<0, "error opening %s: %s", device, strerror(errno)); - if (fd >= 0) { - const size_t pagesize = getpagesize(); - size = ((size + pagesize-1) & ~(pagesize-1)); - if (mapfd(fd, size) == NO_ERROR) { - mDevice = device; - } - } -} - -MemoryHeapBase::MemoryHeapBase(int fd, size_t size, uint32_t flags) - : mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags), - mDevice(0), mNeedUnmap(false) -{ - const size_t pagesize = getpagesize(); - size = ((size + pagesize-1) & ~(pagesize-1)); - mapfd(dup(fd), size); -} - -status_t MemoryHeapBase::init(int fd, void *base, int size, int flags, const char* device) -{ - if (mFD != -1) { - return INVALID_OPERATION; - } - mFD = fd; - mBase = base; - mSize = size; - mFlags = flags; - mDevice = device; - return NO_ERROR; -} - -status_t MemoryHeapBase::mapfd(int fd, size_t size) -{ - if (size == 0) { - // try to figure out the size automatically -#if HAVE_ANDROID_OS - // first try the PMEM ioctl - pmem_region reg; - int err = ioctl(fd, PMEM_GET_TOTAL_SIZE, ®); - if (err == 0) - size = reg.len; -#endif - if (size == 0) { // try fstat - struct stat sb; - if (fstat(fd, &sb) == 0) - size = sb.st_size; - } - // if it didn't work, let mmap() fail. - } - - if ((mFlags & DONT_MAP_LOCALLY) == 0) { - void* base = (uint8_t*)mmap(0, size, - PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); - if (base == MAP_FAILED) { - LOGE("mmap(fd=%d, size=%u) failed (%s)", - fd, uint32_t(size), strerror(errno)); - close(fd); - return -errno; - } - //LOGD("mmap(fd=%d, base=%p, size=%lu)", fd, base, size); - mBase = base; - mNeedUnmap = true; - } else { - mBase = 0; // not MAP_FAILED - mNeedUnmap = false; - } - mFD = fd; - mSize = size; - return NO_ERROR; -} - -MemoryHeapBase::~MemoryHeapBase() -{ - dispose(); -} - -void MemoryHeapBase::dispose() -{ - int fd = android_atomic_or(-1, &mFD); - if (fd >= 0) { - if (mNeedUnmap) { - //LOGD("munmap(fd=%d, base=%p, size=%lu)", fd, mBase, mSize); - munmap(mBase, mSize); - } - mBase = 0; - mSize = 0; - close(fd); - } -} - -int MemoryHeapBase::getHeapID() const { - return mFD; -} - -void* MemoryHeapBase::getBase() const { - return mBase; -} - -size_t MemoryHeapBase::getSize() const { - return mSize; -} - -uint32_t MemoryHeapBase::getFlags() const { - return mFlags; -} - -const char* MemoryHeapBase::getDevice() const { - return mDevice; -} - -// --------------------------------------------------------------------------- -}; // namespace android diff --git a/libs/utils/MemoryHeapPmem.cpp b/libs/utils/MemoryHeapPmem.cpp deleted file mode 100644 index eba2b3055..000000000 --- a/libs/utils/MemoryHeapPmem.cpp +++ /dev/null @@ -1,248 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "MemoryHeapPmem" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include - -#if HAVE_ANDROID_OS -#include -#endif - -namespace android { - -// --------------------------------------------------------------------------- - -MemoryHeapPmem::MemoryPmem::MemoryPmem(const sp& heap) - : BnMemory(), mClientHeap(heap) -{ -} - -MemoryHeapPmem::MemoryPmem::~MemoryPmem() { - if (mClientHeap != NULL) { - mClientHeap->remove(this); - } -} - -// --------------------------------------------------------------------------- - -class SubRegionMemory : public MemoryHeapPmem::MemoryPmem { -public: - SubRegionMemory(const sp& heap, ssize_t offset, size_t size); - virtual ~SubRegionMemory(); - virtual sp getMemory(ssize_t* offset, size_t* size) const; -private: - friend class MemoryHeapPmem; - void revoke(); - size_t mSize; - ssize_t mOffset; -}; - -SubRegionMemory::SubRegionMemory(const sp& heap, - ssize_t offset, size_t size) - : MemoryHeapPmem::MemoryPmem(heap), mSize(size), mOffset(offset) -{ -#ifndef NDEBUG - void* const start_ptr = (void*)(intptr_t(getHeap()->base()) + offset); - memset(start_ptr, 0xda, size); -#endif - -#if HAVE_ANDROID_OS - if (size > 0) { - const size_t pagesize = getpagesize(); - size = (size + pagesize-1) & ~(pagesize-1); - int our_fd = heap->heapID(); - struct pmem_region sub = { offset, size }; - int err = ioctl(our_fd, PMEM_MAP, &sub); - LOGE_IF(err<0, "PMEM_MAP failed (%s), " - "mFD=%d, sub.offset=%lu, sub.size=%lu", - strerror(errno), our_fd, sub.offset, sub.len); -} -#endif -} - -sp SubRegionMemory::getMemory(ssize_t* offset, size_t* size) const -{ - if (offset) *offset = mOffset; - if (size) *size = mSize; - return getHeap(); -} - -SubRegionMemory::~SubRegionMemory() -{ - revoke(); -} - - -void SubRegionMemory::revoke() -{ - // NOTE: revoke() doesn't need to be protected by a lock because it - // can only be called from MemoryHeapPmem::revoke(), which means - // that we can't be in ~SubRegionMemory(), or in ~SubRegionMemory(), - // which means MemoryHeapPmem::revoke() wouldn't have been able to - // promote() it. - -#if HAVE_ANDROID_OS - if (mSize != NULL) { - const sp& heap(getHeap()); - int our_fd = heap->heapID(); - struct pmem_region sub; - sub.offset = mOffset; - sub.len = mSize; - int err = ioctl(our_fd, PMEM_UNMAP, &sub); - LOGE_IF(err<0, "PMEM_UNMAP failed (%s), " - "mFD=%d, sub.offset=%lu, sub.size=%lu", - strerror(errno), our_fd, sub.offset, sub.len); - mSize = 0; - } -#endif -} - -// --------------------------------------------------------------------------- - -MemoryHeapPmem::MemoryHeapPmem(const sp& pmemHeap, - uint32_t flags) - : HeapInterface(), MemoryHeapBase() -{ - char const * const device = pmemHeap->getDevice(); -#if HAVE_ANDROID_OS - if (device) { - int fd = open(device, O_RDWR); - LOGE_IF(fd<0, "couldn't open %s (%s)", device, strerror(errno)); - if (fd >= 0) { - int err = ioctl(fd, PMEM_CONNECT, pmemHeap->heapID()); - if (err < 0) { - LOGE("PMEM_CONNECT failed (%s), mFD=%d, sub-fd=%d", - strerror(errno), fd, pmemHeap->heapID()); - close(fd); - } else { - // everything went well... - mParentHeap = pmemHeap; - MemoryHeapBase::init(fd, - pmemHeap->getBase(), - pmemHeap->getSize(), - pmemHeap->getFlags() | flags, - device); - } - } - } -#else - mParentHeap = pmemHeap; - MemoryHeapBase::init( - dup(pmemHeap->heapID()), - pmemHeap->getBase(), - pmemHeap->getSize(), - pmemHeap->getFlags() | flags, - device); -#endif -} - -MemoryHeapPmem::~MemoryHeapPmem() -{ -} - -sp MemoryHeapPmem::mapMemory(size_t offset, size_t size) -{ - sp memory = createMemory(offset, size); - if (memory != 0) { - Mutex::Autolock _l(mLock); - mAllocations.add(memory); - } - return memory; -} - -sp MemoryHeapPmem::createMemory( - size_t offset, size_t size) -{ - sp memory; - if (heapID() > 0) - memory = new SubRegionMemory(this, offset, size); - return memory; -} - -status_t MemoryHeapPmem::slap() -{ -#if HAVE_ANDROID_OS - size_t size = getSize(); - const size_t pagesize = getpagesize(); - size = (size + pagesize-1) & ~(pagesize-1); - int our_fd = getHeapID(); - struct pmem_region sub = { 0, size }; - int err = ioctl(our_fd, PMEM_MAP, &sub); - LOGE_IF(err<0, "PMEM_MAP failed (%s), " - "mFD=%d, sub.offset=%lu, sub.size=%lu", - strerror(errno), our_fd, sub.offset, sub.len); - return -errno; -#else - return NO_ERROR; -#endif -} - -status_t MemoryHeapPmem::unslap() -{ -#if HAVE_ANDROID_OS - size_t size = getSize(); - const size_t pagesize = getpagesize(); - size = (size + pagesize-1) & ~(pagesize-1); - int our_fd = getHeapID(); - struct pmem_region sub = { 0, size }; - int err = ioctl(our_fd, PMEM_UNMAP, &sub); - LOGE_IF(err<0, "PMEM_UNMAP failed (%s), " - "mFD=%d, sub.offset=%lu, sub.size=%lu", - strerror(errno), our_fd, sub.offset, sub.len); - return -errno; -#else - return NO_ERROR; -#endif -} - -void MemoryHeapPmem::revoke() -{ - SortedVector< wp > allocations; - - { // scope for lock - Mutex::Autolock _l(mLock); - allocations = mAllocations; - } - - ssize_t count = allocations.size(); - for (ssize_t i=0 ; i memory(allocations[i].promote()); - if (memory != 0) - memory->revoke(); - } -} - -void MemoryHeapPmem::remove(const wp& memory) -{ - Mutex::Autolock _l(mLock); - mAllocations.remove(memory); -} - -// --------------------------------------------------------------------------- -}; // namespace android diff --git a/libs/utils/NOTICE b/libs/utils/NOTICE deleted file mode 100644 index c5b1efa7a..000000000 --- a/libs/utils/NOTICE +++ /dev/null @@ -1,190 +0,0 @@ - - Copyright (c) 2005-2008, The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - - 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. - - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - diff --git a/libs/utils/Parcel.cpp b/libs/utils/Parcel.cpp deleted file mode 100644 index 4225e6728..000000000 --- a/libs/utils/Parcel.cpp +++ /dev/null @@ -1,1376 +0,0 @@ -/* - * Copyright (C) 2005 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 "Parcel" -//#define LOG_NDEBUG 0 - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include - -#ifndef INT32_MAX -#define INT32_MAX ((int32_t)(2147483647)) -#endif - -#define LOG_REFS(...) -//#define LOG_REFS(...) LOG(LOG_DEBUG, "Parcel", __VA_ARGS__) - -// --------------------------------------------------------------------------- - -#define PAD_SIZE(s) (((s)+3)&~3) - -// XXX This can be made public if we want to provide -// support for typed data. -struct small_flat_data -{ - uint32_t type; - uint32_t data; -}; - -namespace android { - -void acquire_object(const sp& proc, - const flat_binder_object& obj, const void* who) -{ - switch (obj.type) { - case BINDER_TYPE_BINDER: - if (obj.binder) { - LOG_REFS("Parcel %p acquiring reference on local %p", who, obj.cookie); - static_cast(obj.cookie)->incStrong(who); - } - return; - case BINDER_TYPE_WEAK_BINDER: - if (obj.binder) - static_cast(obj.binder)->incWeak(who); - return; - case BINDER_TYPE_HANDLE: { - const sp b = proc->getStrongProxyForHandle(obj.handle); - if (b != NULL) { - LOG_REFS("Parcel %p acquiring reference on remote %p", who, b.get()); - b->incStrong(who); - } - return; - } - case BINDER_TYPE_WEAK_HANDLE: { - const wp b = proc->getWeakProxyForHandle(obj.handle); - if (b != NULL) b.get_refs()->incWeak(who); - return; - } - case BINDER_TYPE_FD: { - // intentionally blank -- nothing to do to acquire this, but we do - // recognize it as a legitimate object type. - return; - } - } - - LOGD("Invalid object type 0x%08lx", obj.type); -} - -void release_object(const sp& proc, - const flat_binder_object& obj, const void* who) -{ - switch (obj.type) { - case BINDER_TYPE_BINDER: - if (obj.binder) { - LOG_REFS("Parcel %p releasing reference on local %p", who, obj.cookie); - static_cast(obj.cookie)->decStrong(who); - } - return; - case BINDER_TYPE_WEAK_BINDER: - if (obj.binder) - static_cast(obj.binder)->decWeak(who); - return; - case BINDER_TYPE_HANDLE: { - const sp b = proc->getStrongProxyForHandle(obj.handle); - if (b != NULL) { - LOG_REFS("Parcel %p releasing reference on remote %p", who, b.get()); - b->decStrong(who); - } - return; - } - case BINDER_TYPE_WEAK_HANDLE: { - const wp b = proc->getWeakProxyForHandle(obj.handle); - if (b != NULL) b.get_refs()->decWeak(who); - return; - } - case BINDER_TYPE_FD: { - if (obj.cookie != (void*)0) close(obj.handle); - return; - } - } - - LOGE("Invalid object type 0x%08lx", obj.type); -} - -inline static status_t finish_flatten_binder( - const sp& binder, const flat_binder_object& flat, Parcel* out) -{ - return out->writeObject(flat, false); -} - -status_t flatten_binder(const sp& proc, - const sp& binder, Parcel* out) -{ - flat_binder_object obj; - - obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS; - if (binder != NULL) { - IBinder *local = binder->localBinder(); - if (!local) { - BpBinder *proxy = binder->remoteBinder(); - if (proxy == NULL) { - LOGE("null proxy"); - } - const int32_t handle = proxy ? proxy->handle() : 0; - obj.type = BINDER_TYPE_HANDLE; - obj.handle = handle; - obj.cookie = NULL; - } else { - obj.type = BINDER_TYPE_BINDER; - obj.binder = local->getWeakRefs(); - obj.cookie = local; - } - } else { - obj.type = BINDER_TYPE_BINDER; - obj.binder = NULL; - obj.cookie = NULL; - } - - return finish_flatten_binder(binder, obj, out); -} - -status_t flatten_binder(const sp& proc, - const wp& binder, Parcel* out) -{ - flat_binder_object obj; - - obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS; - if (binder != NULL) { - sp real = binder.promote(); - if (real != NULL) { - IBinder *local = real->localBinder(); - if (!local) { - BpBinder *proxy = real->remoteBinder(); - if (proxy == NULL) { - LOGE("null proxy"); - } - const int32_t handle = proxy ? proxy->handle() : 0; - obj.type = BINDER_TYPE_WEAK_HANDLE; - obj.handle = handle; - obj.cookie = NULL; - } else { - obj.type = BINDER_TYPE_WEAK_BINDER; - obj.binder = binder.get_refs(); - obj.cookie = binder.unsafe_get(); - } - return finish_flatten_binder(real, obj, out); - } - - // XXX How to deal? In order to flatten the given binder, - // we need to probe it for information, which requires a primary - // reference... but we don't have one. - // - // The OpenBinder implementation uses a dynamic_cast<> here, - // but we can't do that with the different reference counting - // implementation we are using. - LOGE("Unable to unflatten Binder weak reference!"); - obj.type = BINDER_TYPE_BINDER; - obj.binder = NULL; - obj.cookie = NULL; - return finish_flatten_binder(NULL, obj, out); - - } else { - obj.type = BINDER_TYPE_BINDER; - obj.binder = NULL; - obj.cookie = NULL; - return finish_flatten_binder(NULL, obj, out); - } -} - -inline static status_t finish_unflatten_binder( - BpBinder* proxy, const flat_binder_object& flat, const Parcel& in) -{ - return NO_ERROR; -} - -status_t unflatten_binder(const sp& proc, - const Parcel& in, sp* out) -{ - const flat_binder_object* flat = in.readObject(false); - - if (flat) { - switch (flat->type) { - case BINDER_TYPE_BINDER: - *out = static_cast(flat->cookie); - return finish_unflatten_binder(NULL, *flat, in); - case BINDER_TYPE_HANDLE: - *out = proc->getStrongProxyForHandle(flat->handle); - return finish_unflatten_binder( - static_cast(out->get()), *flat, in); - } - } - return BAD_TYPE; -} - -status_t unflatten_binder(const sp& proc, - const Parcel& in, wp* out) -{ - const flat_binder_object* flat = in.readObject(false); - - if (flat) { - switch (flat->type) { - case BINDER_TYPE_BINDER: - *out = static_cast(flat->cookie); - return finish_unflatten_binder(NULL, *flat, in); - case BINDER_TYPE_WEAK_BINDER: - if (flat->binder != NULL) { - out->set_object_and_refs( - static_cast(flat->cookie), - static_cast(flat->binder)); - } else { - *out = NULL; - } - return finish_unflatten_binder(NULL, *flat, in); - case BINDER_TYPE_HANDLE: - case BINDER_TYPE_WEAK_HANDLE: - *out = proc->getWeakProxyForHandle(flat->handle); - return finish_unflatten_binder( - static_cast(out->unsafe_get()), *flat, in); - } - } - return BAD_TYPE; -} - -// --------------------------------------------------------------------------- - -Parcel::Parcel() -{ - initState(); -} - -Parcel::~Parcel() -{ - freeDataNoInit(); -} - -const uint8_t* Parcel::data() const -{ - return mData; -} - -size_t Parcel::dataSize() const -{ - return (mDataSize > mDataPos ? mDataSize : mDataPos); -} - -size_t Parcel::dataAvail() const -{ - // TODO: decide what to do about the possibility that this can - // report an available-data size that exceeds a Java int's max - // positive value, causing havoc. Fortunately this will only - // happen if someone constructs a Parcel containing more than two - // gigabytes of data, which on typical phone hardware is simply - // not possible. - return dataSize() - dataPosition(); -} - -size_t Parcel::dataPosition() const -{ - return mDataPos; -} - -size_t Parcel::dataCapacity() const -{ - return mDataCapacity; -} - -status_t Parcel::setDataSize(size_t size) -{ - status_t err; - err = continueWrite(size); - if (err == NO_ERROR) { - mDataSize = size; - LOGV("setDataSize Setting data size of %p to %d\n", this, mDataSize); - } - return err; -} - -void Parcel::setDataPosition(size_t pos) const -{ - mDataPos = pos; - mNextObjectHint = 0; -} - -status_t Parcel::setDataCapacity(size_t size) -{ - if (size > mDataSize) return continueWrite(size); - return NO_ERROR; -} - -status_t Parcel::setData(const uint8_t* buffer, size_t len) -{ - status_t err = restartWrite(len); - if (err == NO_ERROR) { - memcpy(const_cast(data()), buffer, len); - mDataSize = len; - mFdsKnown = false; - } - return err; -} - -status_t Parcel::appendFrom(Parcel *parcel, size_t offset, size_t len) -{ - const sp proc(ProcessState::self()); - status_t err; - uint8_t *data = parcel->mData; - size_t *objects = parcel->mObjects; - size_t size = parcel->mObjectsSize; - int startPos = mDataPos; - int firstIndex = -1, lastIndex = -2; - - if (len == 0) { - return NO_ERROR; - } - - // range checks against the source parcel size - if ((offset > parcel->mDataSize) - || (len > parcel->mDataSize) - || (offset + len > parcel->mDataSize)) { - return BAD_VALUE; - } - - // Count objects in range - for (int i = 0; i < (int) size; i++) { - size_t off = objects[i]; - if ((off >= offset) && (off < offset + len)) { - if (firstIndex == -1) { - firstIndex = i; - } - lastIndex = i; - } - } - int numObjects = lastIndex - firstIndex + 1; - - // grow data - err = growData(len); - if (err != NO_ERROR) { - return err; - } - - // append data - memcpy(mData + mDataPos, data + offset, len); - mDataPos += len; - mDataSize += len; - - if (numObjects > 0) { - // grow objects - if (mObjectsCapacity < mObjectsSize + numObjects) { - int newSize = ((mObjectsSize + numObjects)*3)/2; - size_t *objects = - (size_t*)realloc(mObjects, newSize*sizeof(size_t)); - if (objects == (size_t*)0) { - return NO_MEMORY; - } - mObjects = objects; - mObjectsCapacity = newSize; - } - - // append and acquire objects - int idx = mObjectsSize; - for (int i = firstIndex; i <= lastIndex; i++) { - size_t off = objects[i] - offset + startPos; - mObjects[idx++] = off; - mObjectsSize++; - - const flat_binder_object* flat - = reinterpret_cast(mData + off); - acquire_object(proc, *flat, this); - - // take note if the object is a file descriptor - if (flat->type == BINDER_TYPE_FD) { - mHasFds = mFdsKnown = true; - } - } - } - - return NO_ERROR; -} - -bool Parcel::hasFileDescriptors() const -{ - if (!mFdsKnown) { - scanForFds(); - } - return mHasFds; -} - -status_t Parcel::writeInterfaceToken(const String16& interface) -{ - // currently the interface identification token is just its name as a string - return writeString16(interface); -} - -bool Parcel::enforceInterface(const String16& interface) const -{ - String16 str = readString16(); - if (str == interface) { - return true; - } else { - LOGW("**** enforceInterface() expected '%s' but read '%s'\n", - String8(interface).string(), String8(str).string()); - return false; - } -} - -const size_t* Parcel::objects() const -{ - return mObjects; -} - -size_t Parcel::objectsCount() const -{ - return mObjectsSize; -} - -status_t Parcel::errorCheck() const -{ - return mError; -} - -void Parcel::setError(status_t err) -{ - mError = err; -} - -status_t Parcel::finishWrite(size_t len) -{ - //printf("Finish write of %d\n", len); - mDataPos += len; - LOGV("finishWrite Setting data pos of %p to %d\n", this, mDataPos); - if (mDataPos > mDataSize) { - mDataSize = mDataPos; - LOGV("finishWrite Setting data size of %p to %d\n", this, mDataSize); - } - //printf("New pos=%d, size=%d\n", mDataPos, mDataSize); - return NO_ERROR; -} - -status_t Parcel::writeUnpadded(const void* data, size_t len) -{ - size_t end = mDataPos + len; - if (end < mDataPos) { - // integer overflow - return BAD_VALUE; - } - - if (end <= mDataCapacity) { -restart_write: - memcpy(mData+mDataPos, data, len); - return finishWrite(len); - } - - status_t err = growData(len); - if (err == NO_ERROR) goto restart_write; - return err; -} - -status_t Parcel::write(const void* data, size_t len) -{ - void* const d = writeInplace(len); - if (d) { - memcpy(d, data, len); - return NO_ERROR; - } - return mError; -} - -void* Parcel::writeInplace(size_t len) -{ - const size_t padded = PAD_SIZE(len); - - // sanity check for integer overflow - if (mDataPos+padded < mDataPos) { - return NULL; - } - - if ((mDataPos+padded) <= mDataCapacity) { -restart_write: - //printf("Writing %ld bytes, padded to %ld\n", len, padded); - uint8_t* const data = mData+mDataPos; - - // Need to pad at end? - if (padded != len) { -#if BYTE_ORDER == BIG_ENDIAN - static const uint32_t mask[4] = { - 0x00000000, 0xffffff00, 0xffff0000, 0xff000000 - }; -#endif -#if BYTE_ORDER == LITTLE_ENDIAN - static const uint32_t mask[4] = { - 0x00000000, 0x00ffffff, 0x0000ffff, 0x000000ff - }; -#endif - //printf("Applying pad mask: %p to %p\n", (void*)mask[padded-len], - // *reinterpret_cast(data+padded-4)); - *reinterpret_cast(data+padded-4) &= mask[padded-len]; - } - - finishWrite(padded); - return data; - } - - status_t err = growData(padded); - if (err == NO_ERROR) goto restart_write; - return NULL; -} - -status_t Parcel::writeInt32(int32_t val) -{ - if ((mDataPos+sizeof(val)) <= mDataCapacity) { -restart_write: - *reinterpret_cast(mData+mDataPos) = val; - return finishWrite(sizeof(val)); - } - - status_t err = growData(sizeof(val)); - if (err == NO_ERROR) goto restart_write; - return err; -} - -status_t Parcel::writeInt64(int64_t val) -{ - if ((mDataPos+sizeof(val)) <= mDataCapacity) { -restart_write: - *reinterpret_cast(mData+mDataPos) = val; - return finishWrite(sizeof(val)); - } - - status_t err = growData(sizeof(val)); - if (err == NO_ERROR) goto restart_write; - return err; -} - -status_t Parcel::writeFloat(float val) -{ - if ((mDataPos+sizeof(val)) <= mDataCapacity) { -restart_write: - *reinterpret_cast(mData+mDataPos) = val; - return finishWrite(sizeof(val)); - } - - status_t err = growData(sizeof(val)); - if (err == NO_ERROR) goto restart_write; - return err; -} - -status_t Parcel::writeDouble(double val) -{ - if ((mDataPos+sizeof(val)) <= mDataCapacity) { -restart_write: - *reinterpret_cast(mData+mDataPos) = val; - return finishWrite(sizeof(val)); - } - - status_t err = growData(sizeof(val)); - if (err == NO_ERROR) goto restart_write; - return err; -} - -status_t Parcel::writeCString(const char* str) -{ - return write(str, strlen(str)+1); -} - -status_t Parcel::writeString8(const String8& str) -{ - status_t err = writeInt32(str.bytes()); - if (err == NO_ERROR) { - err = write(str.string(), str.bytes()+1); - } - return err; -} - -status_t Parcel::writeString16(const String16& str) -{ - return writeString16(str.string(), str.size()); -} - -status_t Parcel::writeString16(const char16_t* str, size_t len) -{ - if (str == NULL) return writeInt32(-1); - - status_t err = writeInt32(len); - if (err == NO_ERROR) { - len *= sizeof(char16_t); - uint8_t* data = (uint8_t*)writeInplace(len+sizeof(char16_t)); - if (data) { - memcpy(data, str, len); - *reinterpret_cast(data+len) = 0; - return NO_ERROR; - } - err = mError; - } - return err; -} - -status_t Parcel::writeStrongBinder(const sp& val) -{ - return flatten_binder(ProcessState::self(), val, this); -} - -status_t Parcel::writeWeakBinder(const wp& val) -{ - return flatten_binder(ProcessState::self(), val, this); -} - -status_t Parcel::writeNativeHandle(const native_handle& handle) -{ - if (handle.version != sizeof(native_handle)) - return BAD_TYPE; - - status_t err; - err = writeInt32(handle.numFds); - if (err != NO_ERROR) return err; - - err = writeInt32(handle.numInts); - if (err != NO_ERROR) return err; - - for (int i=0 ; err==NO_ERROR && i(mData+mDataPos) = val; - - // Need to write meta-data? - if (nullMetaData || val.binder != NULL) { - mObjects[mObjectsSize] = mDataPos; - acquire_object(ProcessState::self(), val, this); - mObjectsSize++; - } - - // remember if it's a file descriptor - if (val.type == BINDER_TYPE_FD) { - mHasFds = mFdsKnown = true; - } - - return finishWrite(sizeof(flat_binder_object)); - } - - if (!enoughData) { - const status_t err = growData(sizeof(val)); - if (err != NO_ERROR) return err; - } - if (!enoughObjects) { - size_t newSize = ((mObjectsSize+2)*3)/2; - size_t* objects = (size_t*)realloc(mObjects, newSize*sizeof(size_t)); - if (objects == NULL) return NO_MEMORY; - mObjects = objects; - mObjectsCapacity = newSize; - } - - goto restart_write; -} - - -void Parcel::remove(size_t start, size_t amt) -{ - LOG_ALWAYS_FATAL("Parcel::remove() not yet implemented!"); -} - -status_t Parcel::read(void* outData, size_t len) const -{ - if ((mDataPos+PAD_SIZE(len)) >= mDataPos && (mDataPos+PAD_SIZE(len)) <= mDataSize) { - memcpy(outData, mData+mDataPos, len); - mDataPos += PAD_SIZE(len); - LOGV("read Setting data pos of %p to %d\n", this, mDataPos); - return NO_ERROR; - } - return NOT_ENOUGH_DATA; -} - -const void* Parcel::readInplace(size_t len) const -{ - if ((mDataPos+PAD_SIZE(len)) >= mDataPos && (mDataPos+PAD_SIZE(len)) <= mDataSize) { - const void* data = mData+mDataPos; - mDataPos += PAD_SIZE(len); - LOGV("readInplace Setting data pos of %p to %d\n", this, mDataPos); - return data; - } - return NULL; -} - -status_t Parcel::readInt32(int32_t *pArg) const -{ - if ((mDataPos+sizeof(int32_t)) <= mDataSize) { - const void* data = mData+mDataPos; - mDataPos += sizeof(int32_t); - *pArg = *reinterpret_cast(data); - return NO_ERROR; - } else { - return NOT_ENOUGH_DATA; - } -} - -int32_t Parcel::readInt32() const -{ - if ((mDataPos+sizeof(int32_t)) <= mDataSize) { - const void* data = mData+mDataPos; - mDataPos += sizeof(int32_t); - LOGV("readInt32 Setting data pos of %p to %d\n", this, mDataPos); - return *reinterpret_cast(data); - } - return 0; -} - - -status_t Parcel::readInt64(int64_t *pArg) const -{ - if ((mDataPos+sizeof(int64_t)) <= mDataSize) { - const void* data = mData+mDataPos; - mDataPos += sizeof(int64_t); - *pArg = *reinterpret_cast(data); - LOGV("readInt64 Setting data pos of %p to %d\n", this, mDataPos); - return NO_ERROR; - } else { - return NOT_ENOUGH_DATA; - } -} - - -int64_t Parcel::readInt64() const -{ - if ((mDataPos+sizeof(int64_t)) <= mDataSize) { - const void* data = mData+mDataPos; - mDataPos += sizeof(int64_t); - LOGV("readInt64 Setting data pos of %p to %d\n", this, mDataPos); - return *reinterpret_cast(data); - } - return 0; -} - -status_t Parcel::readFloat(float *pArg) const -{ - if ((mDataPos+sizeof(float)) <= mDataSize) { - const void* data = mData+mDataPos; - mDataPos += sizeof(float); - LOGV("readFloat Setting data pos of %p to %d\n", this, mDataPos); - *pArg = *reinterpret_cast(data); - return NO_ERROR; - } else { - return NOT_ENOUGH_DATA; - } -} - - -float Parcel::readFloat() const -{ - if ((mDataPos+sizeof(float)) <= mDataSize) { - const void* data = mData+mDataPos; - mDataPos += sizeof(float); - LOGV("readFloat Setting data pos of %p to %d\n", this, mDataPos); - return *reinterpret_cast(data); - } - return 0; -} - -status_t Parcel::readDouble(double *pArg) const -{ - if ((mDataPos+sizeof(double)) <= mDataSize) { - const void* data = mData+mDataPos; - mDataPos += sizeof(double); - LOGV("readDouble Setting data pos of %p to %d\n", this, mDataPos); - *pArg = *reinterpret_cast(data); - return NO_ERROR; - } else { - return NOT_ENOUGH_DATA; - } -} - - -double Parcel::readDouble() const -{ - if ((mDataPos+sizeof(double)) <= mDataSize) { - const void* data = mData+mDataPos; - mDataPos += sizeof(double); - LOGV("readDouble Setting data pos of %p to %d\n", this, mDataPos); - return *reinterpret_cast(data); - } - return 0; -} - - -const char* Parcel::readCString() const -{ - const size_t avail = mDataSize-mDataPos; - if (avail > 0) { - const char* str = reinterpret_cast(mData+mDataPos); - // is the string's trailing NUL within the parcel's valid bounds? - const char* eos = reinterpret_cast(memchr(str, 0, avail)); - if (eos) { - const size_t len = eos - str; - mDataPos += PAD_SIZE(len+1); - LOGV("readCString Setting data pos of %p to %d\n", this, mDataPos); - return str; - } - } - return NULL; -} - -String8 Parcel::readString8() const -{ - int32_t size = readInt32(); - // watch for potential int overflow adding 1 for trailing NUL - if (size > 0 && size < INT32_MAX) { - const char* str = (const char*)readInplace(size+1); - if (str) return String8(str, size); - } - return String8(); -} - -String16 Parcel::readString16() const -{ - size_t len; - const char16_t* str = readString16Inplace(&len); - if (str) return String16(str, len); - LOGE("Reading a NULL string not supported here."); - return String16(); -} - -const char16_t* Parcel::readString16Inplace(size_t* outLen) const -{ - int32_t size = readInt32(); - // watch for potential int overflow from size+1 - if (size >= 0 && size < INT32_MAX) { - *outLen = size; - const char16_t* str = (const char16_t*)readInplace((size+1)*sizeof(char16_t)); - if (str != NULL) { - return str; - } - } - *outLen = 0; - return NULL; -} - -sp Parcel::readStrongBinder() const -{ - sp val; - unflatten_binder(ProcessState::self(), *this, &val); - return val; -} - -wp Parcel::readWeakBinder() const -{ - wp val; - unflatten_binder(ProcessState::self(), *this, &val); - return val; -} - - -native_handle* Parcel::readNativeHandle(native_handle* (*alloc)(void*, int, int), void* cookie) const -{ - int numFds, numInts; - status_t err; - err = readInt32(&numFds); - if (err != NO_ERROR) return 0; - err = readInt32(&numInts); - if (err != NO_ERROR) return 0; - - native_handle* h; - if (alloc == 0) { - size_t size = sizeof(native_handle) + sizeof(int)*(numFds + numInts); - h = (native_handle*)malloc(size); - h->version = sizeof(native_handle); - h->numFds = numFds; - h->numInts = numInts; - } else { - h = alloc(cookie, numFds, numInts); - if (h->version != sizeof(native_handle)) { - return 0; - } - } - for (int i=0 ; err==NO_ERROR && idata[i] = dup(readFileDescriptor()); - if (h->data[i] < 0) err = BAD_VALUE; - } - - err = read(h->data + numFds, sizeof(int)*numInts); - - if (err != NO_ERROR) { - if (alloc == 0) { - free(h); - } - h = 0; - } - return h; -} - - -int Parcel::readFileDescriptor() const -{ - const flat_binder_object* flat = readObject(true); - if (flat) { - switch (flat->type) { - case BINDER_TYPE_FD: - //LOGI("Returning file descriptor %ld from parcel %p\n", flat->handle, this); - return flat->handle; - } - } - return BAD_TYPE; -} - -const flat_binder_object* Parcel::readObject(bool nullMetaData) const -{ - const size_t DPOS = mDataPos; - if ((DPOS+sizeof(flat_binder_object)) <= mDataSize) { - const flat_binder_object* obj - = reinterpret_cast(mData+DPOS); - mDataPos = DPOS + sizeof(flat_binder_object); - if (!nullMetaData && (obj->cookie == NULL && obj->binder == NULL)) { - // When transferring a NULL object, we don't write it into - // the object list, so we don't want to check for it when - // reading. - LOGV("readObject Setting data pos of %p to %d\n", this, mDataPos); - return obj; - } - - // Ensure that this object is valid... - size_t* const OBJS = mObjects; - const size_t N = mObjectsSize; - size_t opos = mNextObjectHint; - - if (N > 0) { - LOGV("Parcel %p looking for obj at %d, hint=%d\n", - this, DPOS, opos); - - // Start at the current hint position, looking for an object at - // the current data position. - if (opos < N) { - while (opos < (N-1) && OBJS[opos] < DPOS) { - opos++; - } - } else { - opos = N-1; - } - if (OBJS[opos] == DPOS) { - // Found it! - LOGV("Parcel found obj %d at index %d with forward search", - this, DPOS, opos); - mNextObjectHint = opos+1; - LOGV("readObject Setting data pos of %p to %d\n", this, mDataPos); - return obj; - } - - // Look backwards for it... - while (opos > 0 && OBJS[opos] > DPOS) { - opos--; - } - if (OBJS[opos] == DPOS) { - // Found it! - LOGV("Parcel found obj %d at index %d with backward search", - this, DPOS, opos); - mNextObjectHint = opos+1; - LOGV("readObject Setting data pos of %p to %d\n", this, mDataPos); - return obj; - } - } - LOGW("Attempt to read object from Parcel %p at offset %d that is not in the object list", - this, DPOS); - } - return NULL; -} - -void Parcel::closeFileDescriptors() -{ - size_t i = mObjectsSize; - if (i > 0) { - //LOGI("Closing file descriptors for %d objects...", mObjectsSize); - } - while (i > 0) { - i--; - const flat_binder_object* flat - = reinterpret_cast(mData+mObjects[i]); - if (flat->type == BINDER_TYPE_FD) { - //LOGI("Closing fd: %ld\n", flat->handle); - close(flat->handle); - } - } -} - -const uint8_t* Parcel::ipcData() const -{ - return mData; -} - -size_t Parcel::ipcDataSize() const -{ - return (mDataSize > mDataPos ? mDataSize : mDataPos); -} - -const size_t* Parcel::ipcObjects() const -{ - return mObjects; -} - -size_t Parcel::ipcObjectsCount() const -{ - return mObjectsSize; -} - -void Parcel::ipcSetDataReference(const uint8_t* data, size_t dataSize, - const size_t* objects, size_t objectsCount, release_func relFunc, void* relCookie) -{ - freeDataNoInit(); - mError = NO_ERROR; - mData = const_cast(data); - mDataSize = mDataCapacity = dataSize; - //LOGI("setDataReference Setting data size of %p to %lu (pid=%d)\n", this, mDataSize, getpid()); - mDataPos = 0; - LOGV("setDataReference Setting data pos of %p to %d\n", this, mDataPos); - mObjects = const_cast(objects); - mObjectsSize = mObjectsCapacity = objectsCount; - mNextObjectHint = 0; - mOwner = relFunc; - mOwnerCookie = relCookie; - scanForFds(); -} - -void Parcel::print(TextOutput& to, uint32_t flags) const -{ - to << "Parcel("; - - if (errorCheck() != NO_ERROR) { - const status_t err = errorCheck(); - to << "Error: " << (void*)err << " \"" << strerror(-err) << "\""; - } else if (dataSize() > 0) { - const uint8_t* DATA = data(); - to << indent << HexDump(DATA, dataSize()) << dedent; - const size_t* OBJS = objects(); - const size_t N = objectsCount(); - for (size_t i=0; i(DATA+OBJS[i]); - to << endl << "Object #" << i << " @ " << (void*)OBJS[i] << ": " - << TypeCode(flat->type & 0x7f7f7f00) - << " = " << flat->binder; - } - } else { - to << "NULL"; - } - - to << ")"; -} - -void Parcel::releaseObjects() -{ - const sp proc(ProcessState::self()); - size_t i = mObjectsSize; - uint8_t* const data = mData; - size_t* const objects = mObjects; - while (i > 0) { - i--; - const flat_binder_object* flat - = reinterpret_cast(data+objects[i]); - release_object(proc, *flat, this); - } -} - -void Parcel::acquireObjects() -{ - const sp proc(ProcessState::self()); - size_t i = mObjectsSize; - uint8_t* const data = mData; - size_t* const objects = mObjects; - while (i > 0) { - i--; - const flat_binder_object* flat - = reinterpret_cast(data+objects[i]); - acquire_object(proc, *flat, this); - } -} - -void Parcel::freeData() -{ - freeDataNoInit(); - initState(); -} - -void Parcel::freeDataNoInit() -{ - if (mOwner) { - //LOGI("Freeing data ref of %p (pid=%d)\n", this, getpid()); - mOwner(this, mData, mDataSize, mObjects, mObjectsSize, mOwnerCookie); - } else { - releaseObjects(); - if (mData) free(mData); - if (mObjects) free(mObjects); - } -} - -status_t Parcel::growData(size_t len) -{ - size_t newSize = ((mDataSize+len)*3)/2; - return (newSize <= mDataSize) - ? (status_t) NO_MEMORY - : continueWrite(newSize); -} - -status_t Parcel::restartWrite(size_t desired) -{ - if (mOwner) { - freeData(); - return continueWrite(desired); - } - - uint8_t* data = (uint8_t*)realloc(mData, desired); - if (!data && desired > mDataCapacity) { - mError = NO_MEMORY; - return NO_MEMORY; - } - - releaseObjects(); - - if (data) { - mData = data; - mDataCapacity = desired; - } - - mDataSize = mDataPos = 0; - LOGV("restartWrite Setting data size of %p to %d\n", this, mDataSize); - LOGV("restartWrite Setting data pos of %p to %d\n", this, mDataPos); - - free(mObjects); - mObjects = NULL; - mObjectsSize = mObjectsCapacity = 0; - mNextObjectHint = 0; - mHasFds = false; - mFdsKnown = true; - - return NO_ERROR; -} - -status_t Parcel::continueWrite(size_t desired) -{ - // If shrinking, first adjust for any objects that appear - // after the new data size. - size_t objectsSize = mObjectsSize; - if (desired < mDataSize) { - if (desired == 0) { - objectsSize = 0; - } else { - while (objectsSize > 0) { - if (mObjects[objectsSize-1] < desired) - break; - objectsSize--; - } - } - } - - if (mOwner) { - // If the size is going to zero, just release the owner's data. - if (desired == 0) { - freeData(); - return NO_ERROR; - } - - // If there is a different owner, we need to take - // posession. - uint8_t* data = (uint8_t*)malloc(desired); - if (!data) { - mError = NO_MEMORY; - return NO_MEMORY; - } - size_t* objects = NULL; - - if (objectsSize) { - objects = (size_t*)malloc(objectsSize*sizeof(size_t)); - if (!objects) { - mError = NO_MEMORY; - return NO_MEMORY; - } - - // Little hack to only acquire references on objects - // we will be keeping. - size_t oldObjectsSize = mObjectsSize; - mObjectsSize = objectsSize; - acquireObjects(); - mObjectsSize = oldObjectsSize; - } - - if (mData) { - memcpy(data, mData, mDataSize < desired ? mDataSize : desired); - } - if (objects && mObjects) { - memcpy(objects, mObjects, objectsSize*sizeof(size_t)); - } - //LOGI("Freeing data ref of %p (pid=%d)\n", this, getpid()); - mOwner(this, mData, mDataSize, mObjects, mObjectsSize, mOwnerCookie); - mOwner = NULL; - - mData = data; - mObjects = objects; - mDataSize = (mDataSize < desired) ? mDataSize : desired; - LOGV("continueWrite Setting data size of %p to %d\n", this, mDataSize); - mDataCapacity = desired; - mObjectsSize = mObjectsCapacity = objectsSize; - mNextObjectHint = 0; - - } else if (mData) { - if (objectsSize < mObjectsSize) { - // Need to release refs on any objects we are dropping. - const sp proc(ProcessState::self()); - for (size_t i=objectsSize; i(mData+mObjects[i]); - if (flat->type == BINDER_TYPE_FD) { - // will need to rescan because we may have lopped off the only FDs - mFdsKnown = false; - } - release_object(proc, *flat, this); - } - size_t* objects = - (size_t*)realloc(mObjects, objectsSize*sizeof(size_t)); - if (objects) { - mObjects = objects; - } - mObjectsSize = objectsSize; - mNextObjectHint = 0; - } - - // We own the data, so we can just do a realloc(). - if (desired > mDataCapacity) { - uint8_t* data = (uint8_t*)realloc(mData, desired); - if (data) { - mData = data; - mDataCapacity = desired; - } else if (desired > mDataCapacity) { - mError = NO_MEMORY; - return NO_MEMORY; - } - } else { - mDataSize = desired; - LOGV("continueWrite Setting data size of %p to %d\n", this, mDataSize); - if (mDataPos > desired) { - mDataPos = desired; - LOGV("continueWrite Setting data pos of %p to %d\n", this, mDataPos); - } - } - - } else { - // This is the first data. Easy! - uint8_t* data = (uint8_t*)malloc(desired); - if (!data) { - mError = NO_MEMORY; - return NO_MEMORY; - } - - if(!(mDataCapacity == 0 && mObjects == NULL - && mObjectsCapacity == 0)) { - LOGE("continueWrite: %d/%p/%d/%d", mDataCapacity, mObjects, mObjectsCapacity, desired); - } - - mData = data; - mDataSize = mDataPos = 0; - LOGV("continueWrite Setting data size of %p to %d\n", this, mDataSize); - LOGV("continueWrite Setting data pos of %p to %d\n", this, mDataPos); - mDataCapacity = desired; - } - - return NO_ERROR; -} - -void Parcel::initState() -{ - mError = NO_ERROR; - mData = 0; - mDataSize = 0; - mDataCapacity = 0; - mDataPos = 0; - LOGV("initState Setting data size of %p to %d\n", this, mDataSize); - LOGV("initState Setting data pos of %p to %d\n", this, mDataPos); - mObjects = NULL; - mObjectsSize = 0; - mObjectsCapacity = 0; - mNextObjectHint = 0; - mHasFds = false; - mFdsKnown = true; - mOwner = NULL; -} - -void Parcel::scanForFds() const -{ - bool hasFds = false; - for (size_t i=0; i(mData + mObjects[i]); - if (flat->type == BINDER_TYPE_FD) { - hasFds = true; - break; - } - } - mHasFds = hasFds; - mFdsKnown = true; -} - -}; // namespace android diff --git a/libs/utils/Pipe.cpp b/libs/utils/Pipe.cpp deleted file mode 100644 index 613906bed..000000000 --- a/libs/utils/Pipe.cpp +++ /dev/null @@ -1,465 +0,0 @@ -/* - * Copyright (C) 2005 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. - */ - -// -// Unidirectional pipe. -// - -#include -#include - -#if defined(HAVE_WIN32_IPC) -# include -#else -# include -# include -# include -#endif - -#include -#include -#include -#include - -using namespace android; - -const unsigned long kInvalidHandle = (unsigned long) -1; - - -/* - * Constructor. Do little. - */ -Pipe::Pipe(void) - : mReadNonBlocking(false), mReadHandle(kInvalidHandle), - mWriteHandle(kInvalidHandle) -{ -} - -/* - * Destructor. Use the system-appropriate close call. - */ -Pipe::~Pipe(void) -{ -#if defined(HAVE_WIN32_IPC) - if (mReadHandle != kInvalidHandle) { - if (!CloseHandle((HANDLE)mReadHandle)) - LOG(LOG_WARN, "pipe", "failed closing read handle (%ld)\n", - mReadHandle); - } - if (mWriteHandle != kInvalidHandle) { - FlushFileBuffers((HANDLE)mWriteHandle); - if (!CloseHandle((HANDLE)mWriteHandle)) - LOG(LOG_WARN, "pipe", "failed closing write handle (%ld)\n", - mWriteHandle); - } -#else - if (mReadHandle != kInvalidHandle) { - if (close((int) mReadHandle) != 0) - LOG(LOG_WARN, "pipe", "failed closing read fd (%d)\n", - (int) mReadHandle); - } - if (mWriteHandle != kInvalidHandle) { - if (close((int) mWriteHandle) != 0) - LOG(LOG_WARN, "pipe", "failed closing write fd (%d)\n", - (int) mWriteHandle); - } -#endif -} - -/* - * Create the pipe. - * - * Use the POSIX stuff for everything but Windows. - */ -bool Pipe::create(void) -{ - assert(mReadHandle == kInvalidHandle); - assert(mWriteHandle == kInvalidHandle); - -#if defined(HAVE_WIN32_IPC) - /* we use this across processes, so they need to be inheritable */ - HANDLE handles[2]; - SECURITY_ATTRIBUTES saAttr; - - saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); - saAttr.bInheritHandle = TRUE; - saAttr.lpSecurityDescriptor = NULL; - - if (!CreatePipe(&handles[0], &handles[1], &saAttr, 0)) { - LOG(LOG_ERROR, "pipe", "unable to create pipe\n"); - return false; - } - mReadHandle = (unsigned long) handles[0]; - mWriteHandle = (unsigned long) handles[1]; - return true; -#else - int fds[2]; - - if (pipe(fds) != 0) { - LOG(LOG_ERROR, "pipe", "unable to create pipe\n"); - return false; - } - mReadHandle = fds[0]; - mWriteHandle = fds[1]; - return true; -#endif -} - -/* - * Create a "half pipe". Please, no Segway riding. - */ -bool Pipe::createReader(unsigned long handle) -{ - mReadHandle = handle; - assert(mWriteHandle == kInvalidHandle); - return true; -} - -/* - * Create a "half pipe" for writing. - */ -bool Pipe::createWriter(unsigned long handle) -{ - mWriteHandle = handle; - assert(mReadHandle == kInvalidHandle); - return true; -} - -/* - * Return "true" if create() has been called successfully. - */ -bool Pipe::isCreated(void) -{ - // one or the other should be open - return (mReadHandle != kInvalidHandle || mWriteHandle != kInvalidHandle); -} - - -/* - * Read data from the pipe. - * - * For Linux and Darwin, just call read(). For Windows, implement - * non-blocking reads by calling PeekNamedPipe first. - */ -int Pipe::read(void* buf, int count) -{ - assert(mReadHandle != kInvalidHandle); - -#if defined(HAVE_WIN32_IPC) - DWORD totalBytesAvail = count; - DWORD bytesRead; - - if (mReadNonBlocking) { - // use PeekNamedPipe to adjust read count expectations - if (!PeekNamedPipe((HANDLE) mReadHandle, NULL, 0, NULL, - &totalBytesAvail, NULL)) - { - LOG(LOG_ERROR, "pipe", "PeekNamedPipe failed\n"); - return -1; - } - - if (totalBytesAvail == 0) - return 0; - } - - if (!ReadFile((HANDLE) mReadHandle, buf, totalBytesAvail, &bytesRead, - NULL)) - { - DWORD err = GetLastError(); - if (err == ERROR_HANDLE_EOF || err == ERROR_BROKEN_PIPE) - return 0; - LOG(LOG_ERROR, "pipe", "ReadFile failed (err=%ld)\n", err); - return -1; - } - - return (int) bytesRead; -#else - int cc; - cc = ::read(mReadHandle, buf, count); - if (cc < 0 && errno == EAGAIN) - return 0; - return cc; -#endif -} - -/* - * Write data to the pipe. - * - * POSIX systems are trivial, Windows uses a different call and doesn't - * handle non-blocking writes. - * - * If we add non-blocking support here, we probably want to make it an - * all-or-nothing write. - * - * DO NOT use LOG() here, we could be writing a log message. - */ -int Pipe::write(const void* buf, int count) -{ - assert(mWriteHandle != kInvalidHandle); - -#if defined(HAVE_WIN32_IPC) - DWORD bytesWritten; - - if (mWriteNonBlocking) { - // BUG: can't use PeekNamedPipe() to get the amount of space - // left. Looks like we need to use "overlapped I/O" functions. - // I just don't care that much. - } - - if (!WriteFile((HANDLE) mWriteHandle, buf, count, &bytesWritten, NULL)) { - // can't LOG, use stderr - fprintf(stderr, "WriteFile failed (err=%ld)\n", GetLastError()); - return -1; - } - - return (int) bytesWritten; -#else - int cc; - cc = ::write(mWriteHandle, buf, count); - if (cc < 0 && errno == EAGAIN) - return 0; - return cc; -#endif -} - -/* - * Figure out if there is data available on the read fd. - * - * We return "true" on error because we want the caller to try to read - * from the pipe. They'll notice the read failure and do something - * appropriate. - */ -bool Pipe::readReady(void) -{ - assert(mReadHandle != kInvalidHandle); - -#if defined(HAVE_WIN32_IPC) - DWORD totalBytesAvail; - - if (!PeekNamedPipe((HANDLE) mReadHandle, NULL, 0, NULL, - &totalBytesAvail, NULL)) - { - LOG(LOG_ERROR, "pipe", "PeekNamedPipe failed\n"); - return true; - } - - return (totalBytesAvail != 0); -#else - errno = 0; - fd_set readfds; - struct timeval tv = { 0, 0 }; - int cc; - - FD_ZERO(&readfds); - FD_SET(mReadHandle, &readfds); - - cc = select(mReadHandle+1, &readfds, NULL, NULL, &tv); - if (cc < 0) { - LOG(LOG_ERROR, "pipe", "select() failed\n"); - return true; - } else if (cc == 0) { - /* timed out, nothing available */ - return false; - } else if (cc == 1) { - /* our fd is ready */ - return true; - } else { - LOG(LOG_ERROR, "pipe", "HUH? select() returned > 1\n"); - return true; - } -#endif -} - -/* - * Enable or disable non-blocking mode for the read descriptor. - * - * NOTE: the calls succeed under Mac OS X, but the pipe doesn't appear to - * actually be in non-blocking mode. If this matters -- i.e. you're not - * using a select() call -- put a call to readReady() in front of the - * ::read() call, with a PIPE_NONBLOCK_BROKEN #ifdef in the Makefile for - * Darwin. - */ -bool Pipe::setReadNonBlocking(bool val) -{ - assert(mReadHandle != kInvalidHandle); - -#if defined(HAVE_WIN32_IPC) - // nothing to do -#else - int flags; - - if (fcntl(mReadHandle, F_GETFL, &flags) == -1) { - LOG(LOG_ERROR, "pipe", "couldn't get flags for pipe read fd\n"); - return false; - } - if (val) - flags |= O_NONBLOCK; - else - flags &= ~(O_NONBLOCK); - if (fcntl(mReadHandle, F_SETFL, &flags) == -1) { - LOG(LOG_ERROR, "pipe", "couldn't set flags for pipe read fd\n"); - return false; - } -#endif - - mReadNonBlocking = val; - return true; -} - -/* - * Enable or disable non-blocking mode for the write descriptor. - * - * As with setReadNonBlocking(), this does not work on the Mac. - */ -bool Pipe::setWriteNonBlocking(bool val) -{ - assert(mWriteHandle != kInvalidHandle); - -#if defined(HAVE_WIN32_IPC) - // nothing to do -#else - int flags; - - if (fcntl(mWriteHandle, F_GETFL, &flags) == -1) { - LOG(LOG_WARN, "pipe", - "Warning: couldn't get flags for pipe write fd (errno=%d)\n", - errno); - return false; - } - if (val) - flags |= O_NONBLOCK; - else - flags &= ~(O_NONBLOCK); - if (fcntl(mWriteHandle, F_SETFL, &flags) == -1) { - LOG(LOG_WARN, "pipe", - "Warning: couldn't set flags for pipe write fd (errno=%d)\n", - errno); - return false; - } -#endif - - mWriteNonBlocking = val; - return true; -} - -/* - * Specify whether a file descriptor can be inherited by a child process. - * Under Linux this means setting the close-on-exec flag, under Windows - * this is SetHandleInformation(HANDLE_FLAG_INHERIT). - */ -bool Pipe::disallowReadInherit(void) -{ - if (mReadHandle == kInvalidHandle) - return false; - -#if defined(HAVE_WIN32_IPC) - if (SetHandleInformation((HANDLE) mReadHandle, HANDLE_FLAG_INHERIT, 0) == 0) - return false; -#else - if (fcntl((int) mReadHandle, F_SETFD, FD_CLOEXEC) != 0) - return false; -#endif - return true; -} -bool Pipe::disallowWriteInherit(void) -{ - if (mWriteHandle == kInvalidHandle) - return false; - -#if defined(HAVE_WIN32_IPC) - if (SetHandleInformation((HANDLE) mWriteHandle, HANDLE_FLAG_INHERIT, 0) == 0) - return false; -#else - if (fcntl((int) mWriteHandle, F_SETFD, FD_CLOEXEC) != 0) - return false; -#endif - return true; -} - -/* - * Close read descriptor. - */ -bool Pipe::closeRead(void) -{ - if (mReadHandle == kInvalidHandle) - return false; - -#if defined(HAVE_WIN32_IPC) - if (mReadHandle != kInvalidHandle) { - if (!CloseHandle((HANDLE)mReadHandle)) { - LOG(LOG_WARN, "pipe", "failed closing read handle\n"); - return false; - } - } -#else - if (mReadHandle != kInvalidHandle) { - if (close((int) mReadHandle) != 0) { - LOG(LOG_WARN, "pipe", "failed closing read fd\n"); - return false; - } - } -#endif - mReadHandle = kInvalidHandle; - return true; -} - -/* - * Close write descriptor. - */ -bool Pipe::closeWrite(void) -{ - if (mWriteHandle == kInvalidHandle) - return false; - -#if defined(HAVE_WIN32_IPC) - if (mWriteHandle != kInvalidHandle) { - if (!CloseHandle((HANDLE)mWriteHandle)) { - LOG(LOG_WARN, "pipe", "failed closing write handle\n"); - return false; - } - } -#else - if (mWriteHandle != kInvalidHandle) { - if (close((int) mWriteHandle) != 0) { - LOG(LOG_WARN, "pipe", "failed closing write fd\n"); - return false; - } - } -#endif - mWriteHandle = kInvalidHandle; - return true; -} - -/* - * Get the read handle. - */ -unsigned long Pipe::getReadHandle(void) -{ - assert(mReadHandle != kInvalidHandle); - - return mReadHandle; -} - -/* - * Get the write handle. - */ -unsigned long Pipe::getWriteHandle(void) -{ - assert(mWriteHandle != kInvalidHandle); - - return mWriteHandle; -} - diff --git a/libs/utils/ProcessState.cpp b/libs/utils/ProcessState.cpp deleted file mode 100644 index 4567df60f..000000000 --- a/libs/utils/ProcessState.cpp +++ /dev/null @@ -1,398 +0,0 @@ -/* - * Copyright (C) 2005 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 "ProcessState" - -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#define BINDER_VM_SIZE (1*1024*1024) - -static bool gSingleProcess = false; - - -// --------------------------------------------------------------------------- - -namespace android { - -// Global variables -int mArgC; -const char* const* mArgV; -int mArgLen; - -class PoolThread : public Thread -{ -public: - PoolThread(bool isMain) - : mIsMain(isMain) - { - } - -protected: - virtual bool threadLoop() - { - IPCThreadState::self()->joinThreadPool(mIsMain); - return false; - } - - const bool mIsMain; -}; - -sp ProcessState::self() -{ - if (gProcess != NULL) return gProcess; - - AutoMutex _l(gProcessMutex); - if (gProcess == NULL) gProcess = new ProcessState; - return gProcess; -} - -void ProcessState::setSingleProcess(bool singleProcess) -{ - gSingleProcess = singleProcess; -} - - -void ProcessState::setContextObject(const sp& object) -{ - setContextObject(object, String16("default")); -} - -sp ProcessState::getContextObject(const sp& caller) -{ - if (supportsProcesses()) { - return getStrongProxyForHandle(0); - } else { - return getContextObject(String16("default"), caller); - } -} - -void ProcessState::setContextObject(const sp& object, const String16& name) -{ - AutoMutex _l(mLock); - mContexts.add(name, object); -} - -sp ProcessState::getContextObject(const String16& name, const sp& caller) -{ - mLock.lock(); - sp object( - mContexts.indexOfKey(name) >= 0 ? mContexts.valueFor(name) : NULL); - mLock.unlock(); - - //printf("Getting context object %s for %p\n", String8(name).string(), caller.get()); - - if (object != NULL) return object; - - // Don't attempt to retrieve contexts if we manage them - if (mManagesContexts) { - LOGE("getContextObject(%s) failed, but we manage the contexts!\n", - String8(name).string()); - return NULL; - } - - IPCThreadState* ipc = IPCThreadState::self(); - { - Parcel data, reply; - // no interface token on this magic transaction - data.writeString16(name); - data.writeStrongBinder(caller); - status_t result = ipc->transact(0 /*magic*/, 0, data, &reply, 0); - if (result == NO_ERROR) { - object = reply.readStrongBinder(); - } - } - - ipc->flushCommands(); - - if (object != NULL) setContextObject(object, name); - return object; -} - -bool ProcessState::supportsProcesses() const -{ - return mDriverFD >= 0; -} - -void ProcessState::startThreadPool() -{ - AutoMutex _l(mLock); - if (!mThreadPoolStarted) { - mThreadPoolStarted = true; - spawnPooledThread(true); - } -} - -bool ProcessState::isContextManager(void) const -{ - return mManagesContexts; -} - -bool ProcessState::becomeContextManager(context_check_func checkFunc, void* userData) -{ - if (!mManagesContexts) { - AutoMutex _l(mLock); - mBinderContextCheckFunc = checkFunc; - mBinderContextUserData = userData; - if (mDriverFD >= 0) { - int dummy = 0; -#if defined(HAVE_ANDROID_OS) - status_t result = ioctl(mDriverFD, BINDER_SET_CONTEXT_MGR, &dummy); -#else - status_t result = INVALID_OPERATION; -#endif - if (result == 0) { - mManagesContexts = true; - } else if (result == -1) { - mBinderContextCheckFunc = NULL; - mBinderContextUserData = NULL; - LOGE("Binder ioctl to become context manager failed: %s\n", strerror(errno)); - } - } else { - // If there is no driver, our only world is the local - // process so we can always become the context manager there. - mManagesContexts = true; - } - } - return mManagesContexts; -} - -ProcessState::handle_entry* ProcessState::lookupHandleLocked(int32_t handle) -{ - const size_t N=mHandleToObject.size(); - if (N <= (size_t)handle) { - handle_entry e; - e.binder = NULL; - e.refs = NULL; - status_t err = mHandleToObject.insertAt(e, N, handle+1-N); - if (err < NO_ERROR) return NULL; - } - return &mHandleToObject.editItemAt(handle); -} - -sp ProcessState::getStrongProxyForHandle(int32_t handle) -{ - sp result; - - AutoMutex _l(mLock); - - handle_entry* e = lookupHandleLocked(handle); - - if (e != NULL) { - // We need to create a new BpBinder if there isn't currently one, OR we - // are unable to acquire a weak reference on this current one. See comment - // in getWeakProxyForHandle() for more info about this. - IBinder* b = e->binder; - if (b == NULL || !e->refs->attemptIncWeak(this)) { - b = new BpBinder(handle); - e->binder = b; - if (b) e->refs = b->getWeakRefs(); - result = b; - } else { - // This little bit of nastyness is to allow us to add a primary - // reference to the remote proxy when this team doesn't have one - // but another team is sending the handle to us. - result.force_set(b); - e->refs->decWeak(this); - } - } - - return result; -} - -wp ProcessState::getWeakProxyForHandle(int32_t handle) -{ - wp result; - - AutoMutex _l(mLock); - - handle_entry* e = lookupHandleLocked(handle); - - if (e != NULL) { - // We need to create a new BpBinder if there isn't currently one, OR we - // are unable to acquire a weak reference on this current one. The - // attemptIncWeak() is safe because we know the BpBinder destructor will always - // call expungeHandle(), which acquires the same lock we are holding now. - // We need to do this because there is a race condition between someone - // releasing a reference on this BpBinder, and a new reference on its handle - // arriving from the driver. - IBinder* b = e->binder; - if (b == NULL || !e->refs->attemptIncWeak(this)) { - b = new BpBinder(handle); - result = b; - e->binder = b; - if (b) e->refs = b->getWeakRefs(); - } else { - result = b; - e->refs->decWeak(this); - } - } - - return result; -} - -void ProcessState::expungeHandle(int32_t handle, IBinder* binder) -{ - AutoMutex _l(mLock); - - handle_entry* e = lookupHandleLocked(handle); - - // This handle may have already been replaced with a new BpBinder - // (if someone failed the AttemptIncWeak() above); we don't want - // to overwrite it. - if (e && e->binder == binder) e->binder = NULL; -} - -void ProcessState::setArgs(int argc, const char* const argv[]) -{ - mArgC = argc; - mArgV = (const char **)argv; - - mArgLen = 0; - for (int i=0; i t = new PoolThread(isMain); - t->run(buf); - } -} - -static int open_driver() -{ - if (gSingleProcess) { - return -1; - } - - int fd = open("/dev/binder", O_RDWR); - if (fd >= 0) { - fcntl(fd, F_SETFD, FD_CLOEXEC); - int vers; -#if defined(HAVE_ANDROID_OS) - status_t result = ioctl(fd, BINDER_VERSION, &vers); -#else - status_t result = -1; - errno = EPERM; -#endif - if (result == -1) { - LOGE("Binder ioctl to obtain version failed: %s", strerror(errno)); - close(fd); - fd = -1; - } - if (result != 0 || vers != BINDER_CURRENT_PROTOCOL_VERSION) { - LOGE("Binder driver protocol does not match user space protocol!"); - close(fd); - fd = -1; - } -#if defined(HAVE_ANDROID_OS) - size_t maxThreads = 15; - result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads); - if (result == -1) { - LOGE("Binder ioctl to set max threads failed: %s", strerror(errno)); - } -#endif - - } else { - LOGW("Opening '/dev/binder' failed: %s\n", strerror(errno)); - } - return fd; -} - -ProcessState::ProcessState() - : mDriverFD(open_driver()) - , mVMStart(MAP_FAILED) - , mManagesContexts(false) - , mBinderContextCheckFunc(NULL) - , mBinderContextUserData(NULL) - , mThreadPoolStarted(false) - , mThreadPoolSeq(1) -{ - if (mDriverFD >= 0) { - // XXX Ideally, there should be a specific define for whether we - // have mmap (or whether we could possibly have the kernel module - // availabla). -#if !defined(HAVE_WIN32_IPC) - // mmap the binder, providing a chunk of virtual address space to receive transactions. - mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0); - if (mVMStart == MAP_FAILED) { - // *sigh* - LOGE("Using /dev/binder failed: unable to mmap transaction memory.\n"); - close(mDriverFD); - mDriverFD = -1; - } -#else - mDriverFD = -1; -#endif - } - if (mDriverFD < 0) { - // Need to run without the driver, starting our own thread pool. - } -} - -ProcessState::~ProcessState() -{ -} - -}; // namespace android diff --git a/libs/utils/README b/libs/utils/README deleted file mode 100644 index 36a706d5c..000000000 --- a/libs/utils/README +++ /dev/null @@ -1,14 +0,0 @@ -Android Utility Function Library - -If you need a feature that is native to Linux but not present on other -platforms, construct a platform-dependent implementation that shares -the Linux interface. That way the actual device runs as "light" as -possible. - -If that isn't feasible, create a system-independent interface and hide -the details. - -The ultimate goal is *not* to create a super-duper platform abstraction -layer. The goal is to provide an optimized solution for Linux with -reasonable implementations for other platforms. - diff --git a/libs/utils/RefBase.cpp b/libs/utils/RefBase.cpp deleted file mode 100644 index 0bd1af4eb..000000000 --- a/libs/utils/RefBase.cpp +++ /dev/null @@ -1,534 +0,0 @@ -/* - * Copyright (C) 2005 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 "RefBase" - -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -// compile with refcounting debugging enabled -#define DEBUG_REFS 0 -#define DEBUG_REFS_ENABLED_BY_DEFAULT 1 -#define DEBUG_REFS_CALLSTACK_ENABLED 1 - -// log all reference counting operations -#define PRINT_REFS 0 - -// --------------------------------------------------------------------------- - -namespace android { - -#define INITIAL_STRONG_VALUE (1<<28) - -// --------------------------------------------------------------------------- - -class RefBase::weakref_impl : public RefBase::weakref_type -{ -public: - volatile int32_t mStrong; - volatile int32_t mWeak; - RefBase* const mBase; - volatile int32_t mFlags; - - -#if !DEBUG_REFS - - weakref_impl(RefBase* base) - : mStrong(INITIAL_STRONG_VALUE) - , mWeak(0) - , mBase(base) - , mFlags(0) - { - } - - void addStrongRef(const void* /*id*/) { } - void removeStrongRef(const void* /*id*/) { } - void addWeakRef(const void* /*id*/) { } - void removeWeakRef(const void* /*id*/) { } - void printRefs() const { } - void trackMe(bool, bool) { } - -#else - - weakref_impl(RefBase* base) - : mStrong(INITIAL_STRONG_VALUE) - , mWeak(0) - , mBase(base) - , mFlags(0) - , mStrongRefs(NULL) - , mWeakRefs(NULL) - , mTrackEnabled(!!DEBUG_REFS_ENABLED_BY_DEFAULT) - , mRetain(false) - { - //LOGI("NEW weakref_impl %p for RefBase %p", this, base); - } - - ~weakref_impl() - { - LOG_ALWAYS_FATAL_IF(!mRetain && mStrongRefs != NULL, "Strong references remain!"); - LOG_ALWAYS_FATAL_IF(!mRetain && mWeakRefs != NULL, "Weak references remain!"); - } - - void addStrongRef(const void* id) - { - addRef(&mStrongRefs, id, mStrong); - } - - void removeStrongRef(const void* id) - { - if (!mRetain) - removeRef(&mStrongRefs, id); - else - addRef(&mStrongRefs, id, -mStrong); - } - - void addWeakRef(const void* id) - { - addRef(&mWeakRefs, id, mWeak); - } - - void removeWeakRef(const void* id) - { - if (!mRetain) - removeRef(&mWeakRefs, id); - else - addRef(&mWeakRefs, id, -mWeak); - } - - void trackMe(bool track, bool retain) - { - mTrackEnabled = track; - mRetain = retain; - } - - void printRefs() const - { - String8 text; - - { - AutoMutex _l(const_cast(this)->mMutex); - - char buf[128]; - sprintf(buf, "Strong references on RefBase %p (weakref_type %p):\n", mBase, this); - text.append(buf); - printRefsLocked(&text, mStrongRefs); - sprintf(buf, "Weak references on RefBase %p (weakref_type %p):\n", mBase, this); - text.append(buf); - printRefsLocked(&text, mWeakRefs); - } - - { - char name[100]; - snprintf(name, 100, "/data/%p.stack", this); - int rc = open(name, O_RDWR | O_CREAT | O_APPEND); - if (rc >= 0) { - write(rc, text.string(), text.length()); - close(rc); - LOGD("STACK TRACE for %p saved in %s", this, name); - } - else LOGE("FAILED TO PRINT STACK TRACE for %p in %s: %s", this, - name, strerror(errno)); - } - } - -private: - struct ref_entry - { - ref_entry* next; - const void* id; -#if DEBUG_REFS_CALLSTACK_ENABLED - CallStack stack; -#endif - int32_t ref; - }; - - void addRef(ref_entry** refs, const void* id, int32_t mRef) - { - if (mTrackEnabled) { - AutoMutex _l(mMutex); - ref_entry* ref = new ref_entry; - // Reference count at the time of the snapshot, but before the - // update. Positive value means we increment, negative--we - // decrement the reference count. - ref->ref = mRef; - ref->id = id; -#if DEBUG_REFS_CALLSTACK_ENABLED - ref->stack.update(2); -#endif - - ref->next = *refs; - *refs = ref; - } - } - - void removeRef(ref_entry** refs, const void* id) - { - if (mTrackEnabled) { - AutoMutex _l(mMutex); - - ref_entry* ref = *refs; - while (ref != NULL) { - if (ref->id == id) { - *refs = ref->next; - delete ref; - return; - } - - refs = &ref->next; - ref = *refs; - } - - LOG_ALWAYS_FATAL("RefBase: removing id %p on RefBase %p (weakref_type %p) that doesn't exist!", - id, mBase, this); - } - } - - void printRefsLocked(String8* out, const ref_entry* refs) const - { - char buf[128]; - while (refs) { - char inc = refs->ref >= 0 ? '+' : '-'; - sprintf(buf, "\t%c ID %p (ref %d):\n", - inc, refs->id, refs->ref); - out->append(buf); -#if DEBUG_REFS_CALLSTACK_ENABLED - out->append(refs->stack.toString("\t\t")); -#else - out->append("\t\t(call stacks disabled)"); -#endif - refs = refs->next; - } - } - - Mutex mMutex; - ref_entry* mStrongRefs; - ref_entry* mWeakRefs; - - bool mTrackEnabled; - // Collect stack traces on addref and removeref, instead of deleting the stack references - // on removeref that match the address ones. - bool mRetain; - -#if 0 - void addRef(KeyedVector* refs, const void* id) - { - AutoMutex _l(mMutex); - ssize_t i = refs->indexOfKey(id); - if (i >= 0) { - ++(refs->editValueAt(i)); - } else { - i = refs->add(id, 1); - } - } - - void removeRef(KeyedVector* refs, const void* id) - { - AutoMutex _l(mMutex); - ssize_t i = refs->indexOfKey(id); - LOG_ALWAYS_FATAL_IF(i < 0, "RefBase: removing id %p that doesn't exist!", id); - if (i >= 0) { - int32_t val = --(refs->editValueAt(i)); - if (val == 0) { - refs->removeItemsAt(i); - } - } - } - - void printRefs(const KeyedVector& refs) - { - const size_t N=refs.size(); - for (size_t i=0; i mStrongRefs; - KeyedVector mWeakRefs; -#endif - -#endif -}; - -// --------------------------------------------------------------------------- - -void RefBase::incStrong(const void* id) const -{ - weakref_impl* const refs = mRefs; - refs->addWeakRef(id); - refs->incWeak(id); - - refs->addStrongRef(id); - const int32_t c = android_atomic_inc(&refs->mStrong); - LOG_ASSERT(c > 0, "incStrong() called on %p after last strong ref", refs); -#if PRINT_REFS - LOGD("incStrong of %p from %p: cnt=%d\n", this, id, c); -#endif - if (c != INITIAL_STRONG_VALUE) { - return; - } - - android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong); - const_cast(this)->onFirstRef(); -} - -void RefBase::decStrong(const void* id) const -{ - weakref_impl* const refs = mRefs; - refs->removeStrongRef(id); - const int32_t c = android_atomic_dec(&refs->mStrong); -#if PRINT_REFS - LOGD("decStrong of %p from %p: cnt=%d\n", this, id, c); -#endif - LOG_ASSERT(c >= 1, "decStrong() called on %p too many times", refs); - if (c == 1) { - const_cast(this)->onLastStrongRef(id); - if ((refs->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) { - delete this; - } - } - refs->removeWeakRef(id); - refs->decWeak(id); -} - -void RefBase::forceIncStrong(const void* id) const -{ - weakref_impl* const refs = mRefs; - refs->addWeakRef(id); - refs->incWeak(id); - - refs->addStrongRef(id); - const int32_t c = android_atomic_inc(&refs->mStrong); - LOG_ASSERT(c >= 0, "forceIncStrong called on %p after ref count underflow", - refs); -#if PRINT_REFS - LOGD("forceIncStrong of %p from %p: cnt=%d\n", this, id, c); -#endif - - switch (c) { - case INITIAL_STRONG_VALUE: - android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong); - // fall through... - case 0: - const_cast(this)->onFirstRef(); - } -} - -int32_t RefBase::getStrongCount() const -{ - return mRefs->mStrong; -} - - - -RefBase* RefBase::weakref_type::refBase() const -{ - return static_cast(this)->mBase; -} - -void RefBase::weakref_type::incWeak(const void* id) -{ - weakref_impl* const impl = static_cast(this); - impl->addWeakRef(id); - const int32_t c = android_atomic_inc(&impl->mWeak); - LOG_ASSERT(c >= 0, "incWeak called on %p after last weak ref", this); -} - -void RefBase::weakref_type::decWeak(const void* id) -{ - weakref_impl* const impl = static_cast(this); - impl->removeWeakRef(id); - const int32_t c = android_atomic_dec(&impl->mWeak); - LOG_ASSERT(c >= 1, "decWeak called on %p too many times", this); - if (c != 1) return; - - if ((impl->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) { - if (impl->mStrong == INITIAL_STRONG_VALUE) - delete impl->mBase; - else { -// LOGV("Freeing refs %p of old RefBase %p\n", this, impl->mBase); - delete impl; - } - } else { - impl->mBase->onLastWeakRef(id); - if ((impl->mFlags&OBJECT_LIFETIME_FOREVER) != OBJECT_LIFETIME_FOREVER) { - delete impl->mBase; - } - } -} - -bool RefBase::weakref_type::attemptIncStrong(const void* id) -{ - incWeak(id); - - weakref_impl* const impl = static_cast(this); - - int32_t curCount = impl->mStrong; - LOG_ASSERT(curCount >= 0, "attemptIncStrong called on %p after underflow", - this); - while (curCount > 0 && curCount != INITIAL_STRONG_VALUE) { - if (android_atomic_cmpxchg(curCount, curCount+1, &impl->mStrong) == 0) { - break; - } - curCount = impl->mStrong; - } - - if (curCount <= 0 || curCount == INITIAL_STRONG_VALUE) { - bool allow; - if (curCount == INITIAL_STRONG_VALUE) { - // Attempting to acquire first strong reference... this is allowed - // if the object does NOT have a longer lifetime (meaning the - // implementation doesn't need to see this), or if the implementation - // allows it to happen. - allow = (impl->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK - || impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id); - } else { - // Attempting to revive the object... this is allowed - // if the object DOES have a longer lifetime (so we can safely - // call the object with only a weak ref) and the implementation - // allows it to happen. - allow = (impl->mFlags&OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_WEAK - && impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id); - } - if (!allow) { - decWeak(id); - return false; - } - curCount = android_atomic_inc(&impl->mStrong); - - // If the strong reference count has already been incremented by - // someone else, the implementor of onIncStrongAttempted() is holding - // an unneeded reference. So call onLastStrongRef() here to remove it. - // (No, this is not pretty.) Note that we MUST NOT do this if we - // are in fact acquiring the first reference. - if (curCount > 0 && curCount < INITIAL_STRONG_VALUE) { - impl->mBase->onLastStrongRef(id); - } - } - - impl->addWeakRef(id); - impl->addStrongRef(id); - -#if PRINT_REFS - LOGD("attemptIncStrong of %p from %p: cnt=%d\n", this, id, curCount); -#endif - - if (curCount == INITIAL_STRONG_VALUE) { - android_atomic_add(-INITIAL_STRONG_VALUE, &impl->mStrong); - impl->mBase->onFirstRef(); - } - - return true; -} - -bool RefBase::weakref_type::attemptIncWeak(const void* id) -{ - weakref_impl* const impl = static_cast(this); - - int32_t curCount = impl->mWeak; - LOG_ASSERT(curCount >= 0, "attemptIncWeak called on %p after underflow", - this); - while (curCount > 0) { - if (android_atomic_cmpxchg(curCount, curCount+1, &impl->mWeak) == 0) { - break; - } - curCount = impl->mWeak; - } - - if (curCount > 0) { - impl->addWeakRef(id); - } - - return curCount > 0; -} - -int32_t RefBase::weakref_type::getWeakCount() const -{ - return static_cast(this)->mWeak; -} - -void RefBase::weakref_type::printRefs() const -{ - static_cast(this)->printRefs(); -} - -void RefBase::weakref_type::trackMe(bool enable, bool retain) -{ - static_cast(this)->trackMe(enable, retain); -} - -RefBase::weakref_type* RefBase::createWeak(const void* id) const -{ - mRefs->incWeak(id); - return mRefs; -} - -RefBase::weakref_type* RefBase::getWeakRefs() const -{ - return mRefs; -} - -RefBase::RefBase() - : mRefs(new weakref_impl(this)) -{ -// LOGV("Creating refs %p with RefBase %p\n", mRefs, this); -} - -RefBase::~RefBase() -{ -// LOGV("Destroying RefBase %p (refs %p)\n", this, mRefs); - if (mRefs->mWeak == 0) { -// LOGV("Freeing refs %p of old RefBase %p\n", mRefs, this); - delete mRefs; - } -} - -void RefBase::extendObjectLifetime(int32_t mode) -{ - android_atomic_or(mode, &mRefs->mFlags); -} - -void RefBase::onFirstRef() -{ -} - -void RefBase::onLastStrongRef(const void* /*id*/) -{ -} - -bool RefBase::onIncStrongAttempted(uint32_t flags, const void* id) -{ - return (flags&FIRST_INC_STRONG) ? true : false; -} - -void RefBase::onLastWeakRef(const void* /*id*/) -{ -} - -}; // namespace android diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp deleted file mode 100644 index 71e7cd722..000000000 --- a/libs/utils/ResourceTypes.cpp +++ /dev/null @@ -1,3983 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "ResourceType" -//#define LOG_NDEBUG 0 - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#ifndef INT32_MAX -#define INT32_MAX ((int32_t)(2147483647)) -#endif - -#define POOL_NOISY(x) //x -#define XML_NOISY(x) //x -#define TABLE_NOISY(x) //x -#define TABLE_GETENTRY(x) //x -#define TABLE_SUPER_NOISY(x) //x -#define LOAD_TABLE_NOISY(x) //x - -namespace android { - -#ifdef HAVE_WINSOCK -#undef nhtol -#undef htonl - -#ifdef HAVE_LITTLE_ENDIAN -#define ntohl(x) ( ((x) << 24) | (((x) >> 24) & 255) | (((x) << 8) & 0xff0000) | (((x) >> 8) & 0xff00) ) -#define htonl(x) ntohl(x) -#define ntohs(x) ( (((x) << 8) & 0xff00) | (((x) >> 8) & 255) ) -#define htons(x) ntohs(x) -#else -#define ntohl(x) (x) -#define htonl(x) (x) -#define ntohs(x) (x) -#define htons(x) (x) -#endif -#endif - -static void printToLogFunc(void* cookie, const char* txt) -{ - LOGV("%s", txt); -} - -// Standard C isspace() is only required to look at the low byte of its input, so -// produces incorrect results for UTF-16 characters. For safety's sake, assume that -// any high-byte UTF-16 code point is not whitespace. -inline int isspace16(char16_t c) { - return (c < 0x0080 && isspace(c)); -} - -// range checked; guaranteed to NUL-terminate within the stated number of available slots -// NOTE: if this truncates the dst string due to running out of space, no attempt is -// made to avoid splitting surrogate pairs. -static void strcpy16_dtoh(uint16_t* dst, const uint16_t* src, size_t avail) -{ - uint16_t* last = dst + avail - 1; - while (*src && (dst < last)) { - char16_t s = dtohs(*src); - *dst++ = s; - src++; - } - *dst = 0; -} - -static status_t validate_chunk(const ResChunk_header* chunk, - size_t minSize, - const uint8_t* dataEnd, - const char* name) -{ - const uint16_t headerSize = dtohs(chunk->headerSize); - const uint32_t size = dtohl(chunk->size); - - if (headerSize >= minSize) { - if (headerSize <= size) { - if (((headerSize|size)&0x3) == 0) { - if ((ssize_t)size <= (dataEnd-((const uint8_t*)chunk))) { - return NO_ERROR; - } - LOGW("%s data size %p extends beyond resource end %p.", - name, (void*)size, - (void*)(dataEnd-((const uint8_t*)chunk))); - return BAD_TYPE; - } - LOGW("%s size 0x%x or headerSize 0x%x is not on an integer boundary.", - name, (int)size, (int)headerSize); - return BAD_TYPE; - } - LOGW("%s size %p is smaller than header size %p.", - name, (void*)size, (void*)(int)headerSize); - return BAD_TYPE; - } - LOGW("%s header size %p is too small.", - name, (void*)(int)headerSize); - return BAD_TYPE; -} - -inline void Res_value::copyFrom_dtoh(const Res_value& src) -{ - size = dtohs(src.size); - res0 = src.res0; - dataType = src.dataType; - data = dtohl(src.data); -} - -void Res_png_9patch::deviceToFile() -{ - for (int i = 0; i < numXDivs; i++) { - xDivs[i] = htonl(xDivs[i]); - } - for (int i = 0; i < numYDivs; i++) { - yDivs[i] = htonl(yDivs[i]); - } - paddingLeft = htonl(paddingLeft); - paddingRight = htonl(paddingRight); - paddingTop = htonl(paddingTop); - paddingBottom = htonl(paddingBottom); - for (int i=0; ixDivs, numXDivs * sizeof(int32_t)); - data += numXDivs * sizeof(int32_t); - memmove(data, this->yDivs, numYDivs * sizeof(int32_t)); - data += numYDivs * sizeof(int32_t); - memmove(data, this->colors, numColors * sizeof(uint32_t)); -} - -static void deserializeInternal(const void* inData, Res_png_9patch* outData) { - char* patch = (char*) inData; - if (inData != outData) { - memmove(&outData->wasDeserialized, patch, 4); // copy wasDeserialized, numXDivs, numYDivs, numColors - memmove(&outData->paddingLeft, patch + 12, 4); // copy wasDeserialized, numXDivs, numYDivs, numColors - } - outData->wasDeserialized = true; - char* data = (char*)outData; - data += sizeof(Res_png_9patch); - outData->xDivs = (int32_t*) data; - data += outData->numXDivs * sizeof(int32_t); - outData->yDivs = (int32_t*) data; - data += outData->numYDivs * sizeof(int32_t); - outData->colors = (uint32_t*) data; -} - -Res_png_9patch* Res_png_9patch::deserialize(const void* inData) -{ - if (sizeof(void*) != sizeof(int32_t)) { - LOGE("Cannot deserialize on non 32-bit system\n"); - return NULL; - } - deserializeInternal(inData, (Res_png_9patch*) inData); - return (Res_png_9patch*) inData; -} - -// -------------------------------------------------------------------- -// -------------------------------------------------------------------- -// -------------------------------------------------------------------- - -ResStringPool::ResStringPool() - : mError(NO_INIT), mOwnedData(NULL) -{ -} - -ResStringPool::ResStringPool(const void* data, size_t size, bool copyData) - : mError(NO_INIT), mOwnedData(NULL) -{ - setTo(data, size, copyData); -} - -ResStringPool::~ResStringPool() -{ - uninit(); -} - -status_t ResStringPool::setTo(const void* data, size_t size, bool copyData) -{ - if (!data || !size) { - return (mError=BAD_TYPE); - } - - uninit(); - - const bool notDeviceEndian = htods(0xf0) != 0xf0; - - if (copyData || notDeviceEndian) { - mOwnedData = malloc(size); - if (mOwnedData == NULL) { - return (mError=NO_MEMORY); - } - memcpy(mOwnedData, data, size); - data = mOwnedData; - } - - mHeader = (const ResStringPool_header*)data; - - if (notDeviceEndian) { - ResStringPool_header* h = const_cast(mHeader); - h->header.headerSize = dtohs(mHeader->header.headerSize); - h->header.type = dtohs(mHeader->header.type); - h->header.size = dtohl(mHeader->header.size); - h->stringCount = dtohl(mHeader->stringCount); - h->styleCount = dtohl(mHeader->styleCount); - h->flags = dtohl(mHeader->flags); - h->stringsStart = dtohl(mHeader->stringsStart); - h->stylesStart = dtohl(mHeader->stylesStart); - } - - if (mHeader->header.headerSize > mHeader->header.size - || mHeader->header.size > size) { - LOGW("Bad string block: header size %d or total size %d is larger than data size %d\n", - (int)mHeader->header.headerSize, (int)mHeader->header.size, (int)size); - return (mError=BAD_TYPE); - } - mSize = mHeader->header.size; - mEntries = (const uint32_t*) - (((const uint8_t*)data)+mHeader->header.headerSize); - - if (mHeader->stringCount > 0) { - if ((mHeader->stringCount*sizeof(uint32_t) < mHeader->stringCount) // uint32 overflow? - || (mHeader->header.headerSize+(mHeader->stringCount*sizeof(uint32_t))) - > size) { - LOGW("Bad string block: entry of %d items extends past data size %d\n", - (int)(mHeader->header.headerSize+(mHeader->stringCount*sizeof(uint32_t))), - (int)size); - return (mError=BAD_TYPE); - } - mStrings = (const char16_t*) - (((const uint8_t*)data)+mHeader->stringsStart); - if (mHeader->stringsStart >= (mHeader->header.size-sizeof(uint16_t))) { - LOGW("Bad string block: string pool starts at %d, after total size %d\n", - (int)mHeader->stringsStart, (int)mHeader->header.size); - return (mError=BAD_TYPE); - } - if (mHeader->styleCount == 0) { - mStringPoolSize = - (mHeader->header.size-mHeader->stringsStart)/sizeof(uint16_t); - } else { - // check invariant: styles follow the strings - if (mHeader->stylesStart <= mHeader->stringsStart) { - LOGW("Bad style block: style block starts at %d, before strings at %d\n", - (int)mHeader->stylesStart, (int)mHeader->stringsStart); - return (mError=BAD_TYPE); - } - mStringPoolSize = - (mHeader->stylesStart-mHeader->stringsStart)/sizeof(uint16_t); - } - - // check invariant: stringCount > 0 requires a string pool to exist - if (mStringPoolSize == 0) { - LOGW("Bad string block: stringCount is %d but pool size is 0\n", (int)mHeader->stringCount); - return (mError=BAD_TYPE); - } - - if (notDeviceEndian) { - size_t i; - uint32_t* e = const_cast(mEntries); - for (i=0; istringCount; i++) { - e[i] = dtohl(mEntries[i]); - } - char16_t* s = const_cast(mStrings); - for (i=0; istyleCount > 0) { - mEntryStyles = mEntries + mHeader->stringCount; - // invariant: integer overflow in calculating mEntryStyles - if (mEntryStyles < mEntries) { - LOGW("Bad string block: integer overflow finding styles\n"); - return (mError=BAD_TYPE); - } - - if (((const uint8_t*)mEntryStyles-(const uint8_t*)mHeader) > (int)size) { - LOGW("Bad string block: entry of %d styles extends past data size %d\n", - (int)((const uint8_t*)mEntryStyles-(const uint8_t*)mHeader), - (int)size); - return (mError=BAD_TYPE); - } - mStyles = (const uint32_t*) - (((const uint8_t*)data)+mHeader->stylesStart); - if (mHeader->stylesStart >= mHeader->header.size) { - LOGW("Bad string block: style pool starts %d, after total size %d\n", - (int)mHeader->stylesStart, (int)mHeader->header.size); - return (mError=BAD_TYPE); - } - mStylePoolSize = - (mHeader->header.size-mHeader->stylesStart)/sizeof(uint32_t); - - if (notDeviceEndian) { - size_t i; - uint32_t* e = const_cast(mEntryStyles); - for (i=0; istyleCount; i++) { - e[i] = dtohl(mEntryStyles[i]); - } - uint32_t* s = const_cast(mStyles); - for (i=0; istringCount) { - const uint32_t off = (mEntries[idx]/sizeof(uint16_t)); - if (off < (mStringPoolSize-1)) { - const char16_t* str = mStrings+off; - *outLen = *str; - if ((*str)&0x8000) { - str++; - *outLen = (((*outLen)&0x7fff)<<16) + *str; - } - if ((uint32_t)(str+1+*outLen-mStrings) < mStringPoolSize) { - return str+1; - } else { - LOGW("Bad string block: string #%d extends to %d, past end at %d\n", - (int)idx, (int)(str+1+*outLen-mStrings), (int)mStringPoolSize); - } - } else { - LOGW("Bad string block: string #%d entry is at %d, past end at %d\n", - (int)idx, (int)(off*sizeof(uint16_t)), - (int)(mStringPoolSize*sizeof(uint16_t))); - } - } - return NULL; -} - -const ResStringPool_span* ResStringPool::styleAt(const ResStringPool_ref& ref) const -{ - return styleAt(ref.index); -} - -const ResStringPool_span* ResStringPool::styleAt(size_t idx) const -{ - if (mError == NO_ERROR && idx < mHeader->styleCount) { - const uint32_t off = (mEntryStyles[idx]/sizeof(uint32_t)); - if (off < mStylePoolSize) { - return (const ResStringPool_span*)(mStyles+off); - } else { - LOGW("Bad string block: style #%d entry is at %d, past end at %d\n", - (int)idx, (int)(off*sizeof(uint32_t)), - (int)(mStylePoolSize*sizeof(uint32_t))); - } - } - return NULL; -} - -ssize_t ResStringPool::indexOfString(const char16_t* str, size_t strLen) const -{ - if (mError != NO_ERROR) { - return mError; - } - - size_t len; - - if (mHeader->flags&ResStringPool_header::SORTED_FLAG) { - // Do a binary search for the string... - ssize_t l = 0; - ssize_t h = mHeader->stringCount-1; - - ssize_t mid; - while (l <= h) { - mid = l + (h - l)/2; - const char16_t* s = stringAt(mid, &len); - int c = s ? strzcmp16(s, len, str, strLen) : -1; - POOL_NOISY(printf("Looking for %s, at %s, cmp=%d, l/mid/h=%d/%d/%d\n", - String8(str).string(), - String8(s).string(), - c, (int)l, (int)mid, (int)h)); - if (c == 0) { - return mid; - } else if (c < 0) { - l = mid + 1; - } else { - h = mid - 1; - } - } - } else { - // It is unusual to get the ID from an unsorted string block... - // most often this happens because we want to get IDs for style - // span tags; since those always appear at the end of the string - // block, start searching at the back. - for (int i=mHeader->stringCount-1; i>=0; i--) { - const char16_t* s = stringAt(i, &len); - POOL_NOISY(printf("Looking for %s, at %s, i=%d\n", - String8(str, strLen).string(), - String8(s).string(), - i)); - if (s && strzcmp16(s, len, str, strLen) == 0) { - return i; - } - } - } - - return NAME_NOT_FOUND; -} - -size_t ResStringPool::size() const -{ - return (mError == NO_ERROR) ? mHeader->stringCount : 0; -} - -// -------------------------------------------------------------------- -// -------------------------------------------------------------------- -// -------------------------------------------------------------------- - -ResXMLParser::ResXMLParser(const ResXMLTree& tree) - : mTree(tree), mEventCode(BAD_DOCUMENT) -{ -} - -void ResXMLParser::restart() -{ - mCurNode = NULL; - mEventCode = mTree.mError == NO_ERROR ? START_DOCUMENT : BAD_DOCUMENT; -} - -ResXMLParser::event_code_t ResXMLParser::getEventType() const -{ - return mEventCode; -} - -ResXMLParser::event_code_t ResXMLParser::next() -{ - if (mEventCode == START_DOCUMENT) { - mCurNode = mTree.mRootNode; - mCurExt = mTree.mRootExt; - return (mEventCode=mTree.mRootCode); - } else if (mEventCode >= FIRST_CHUNK_CODE) { - return nextNode(); - } - return mEventCode; -} - -const int32_t ResXMLParser::getCommentID() const -{ - return mCurNode != NULL ? dtohl(mCurNode->comment.index) : -1; -} - -const uint16_t* ResXMLParser::getComment(size_t* outLen) const -{ - int32_t id = getCommentID(); - return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; -} - -const uint32_t ResXMLParser::getLineNumber() const -{ - return mCurNode != NULL ? dtohl(mCurNode->lineNumber) : -1; -} - -const int32_t ResXMLParser::getTextID() const -{ - if (mEventCode == TEXT) { - return dtohl(((const ResXMLTree_cdataExt*)mCurExt)->data.index); - } - return -1; -} - -const uint16_t* ResXMLParser::getText(size_t* outLen) const -{ - int32_t id = getTextID(); - return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; -} - -ssize_t ResXMLParser::getTextValue(Res_value* outValue) const -{ - if (mEventCode == TEXT) { - outValue->copyFrom_dtoh(((const ResXMLTree_cdataExt*)mCurExt)->typedData); - return sizeof(Res_value); - } - return BAD_TYPE; -} - -const int32_t ResXMLParser::getNamespacePrefixID() const -{ - if (mEventCode == START_NAMESPACE || mEventCode == END_NAMESPACE) { - return dtohl(((const ResXMLTree_namespaceExt*)mCurExt)->prefix.index); - } - return -1; -} - -const uint16_t* ResXMLParser::getNamespacePrefix(size_t* outLen) const -{ - int32_t id = getNamespacePrefixID(); - //printf("prefix=%d event=%p\n", id, mEventCode); - return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; -} - -const int32_t ResXMLParser::getNamespaceUriID() const -{ - if (mEventCode == START_NAMESPACE || mEventCode == END_NAMESPACE) { - return dtohl(((const ResXMLTree_namespaceExt*)mCurExt)->uri.index); - } - return -1; -} - -const uint16_t* ResXMLParser::getNamespaceUri(size_t* outLen) const -{ - int32_t id = getNamespaceUriID(); - //printf("uri=%d event=%p\n", id, mEventCode); - return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; -} - -const int32_t ResXMLParser::getElementNamespaceID() const -{ - if (mEventCode == START_TAG) { - return dtohl(((const ResXMLTree_attrExt*)mCurExt)->ns.index); - } - if (mEventCode == END_TAG) { - return dtohl(((const ResXMLTree_endElementExt*)mCurExt)->ns.index); - } - return -1; -} - -const uint16_t* ResXMLParser::getElementNamespace(size_t* outLen) const -{ - int32_t id = getElementNamespaceID(); - return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; -} - -const int32_t ResXMLParser::getElementNameID() const -{ - if (mEventCode == START_TAG) { - return dtohl(((const ResXMLTree_attrExt*)mCurExt)->name.index); - } - if (mEventCode == END_TAG) { - return dtohl(((const ResXMLTree_endElementExt*)mCurExt)->name.index); - } - return -1; -} - -const uint16_t* ResXMLParser::getElementName(size_t* outLen) const -{ - int32_t id = getElementNameID(); - return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; -} - -size_t ResXMLParser::getAttributeCount() const -{ - if (mEventCode == START_TAG) { - return dtohs(((const ResXMLTree_attrExt*)mCurExt)->attributeCount); - } - return 0; -} - -const int32_t ResXMLParser::getAttributeNamespaceID(size_t idx) const -{ - if (mEventCode == START_TAG) { - const ResXMLTree_attrExt* tag = (const ResXMLTree_attrExt*)mCurExt; - if (idx < dtohs(tag->attributeCount)) { - const ResXMLTree_attribute* attr = (const ResXMLTree_attribute*) - (((const uint8_t*)tag) - + dtohs(tag->attributeStart) - + (dtohs(tag->attributeSize)*idx)); - return dtohl(attr->ns.index); - } - } - return -2; -} - -const uint16_t* ResXMLParser::getAttributeNamespace(size_t idx, size_t* outLen) const -{ - int32_t id = getAttributeNamespaceID(idx); - //printf("attribute namespace=%d idx=%d event=%p\n", id, idx, mEventCode); - //XML_NOISY(printf("getAttributeNamespace 0x%x=0x%x\n", idx, id)); - return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; -} - -const int32_t ResXMLParser::getAttributeNameID(size_t idx) const -{ - if (mEventCode == START_TAG) { - const ResXMLTree_attrExt* tag = (const ResXMLTree_attrExt*)mCurExt; - if (idx < dtohs(tag->attributeCount)) { - const ResXMLTree_attribute* attr = (const ResXMLTree_attribute*) - (((const uint8_t*)tag) - + dtohs(tag->attributeStart) - + (dtohs(tag->attributeSize)*idx)); - return dtohl(attr->name.index); - } - } - return -1; -} - -const uint16_t* ResXMLParser::getAttributeName(size_t idx, size_t* outLen) const -{ - int32_t id = getAttributeNameID(idx); - //printf("attribute name=%d idx=%d event=%p\n", id, idx, mEventCode); - //XML_NOISY(printf("getAttributeName 0x%x=0x%x\n", idx, id)); - return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; -} - -const uint32_t ResXMLParser::getAttributeNameResID(size_t idx) const -{ - int32_t id = getAttributeNameID(idx); - if (id >= 0 && (size_t)id < mTree.mNumResIds) { - return dtohl(mTree.mResIds[id]); - } - return 0; -} - -const int32_t ResXMLParser::getAttributeValueStringID(size_t idx) const -{ - if (mEventCode == START_TAG) { - const ResXMLTree_attrExt* tag = (const ResXMLTree_attrExt*)mCurExt; - if (idx < dtohs(tag->attributeCount)) { - const ResXMLTree_attribute* attr = (const ResXMLTree_attribute*) - (((const uint8_t*)tag) - + dtohs(tag->attributeStart) - + (dtohs(tag->attributeSize)*idx)); - return dtohl(attr->rawValue.index); - } - } - return -1; -} - -const uint16_t* ResXMLParser::getAttributeStringValue(size_t idx, size_t* outLen) const -{ - int32_t id = getAttributeValueStringID(idx); - //XML_NOISY(printf("getAttributeValue 0x%x=0x%x\n", idx, id)); - return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; -} - -int32_t ResXMLParser::getAttributeDataType(size_t idx) const -{ - if (mEventCode == START_TAG) { - const ResXMLTree_attrExt* tag = (const ResXMLTree_attrExt*)mCurExt; - if (idx < dtohs(tag->attributeCount)) { - const ResXMLTree_attribute* attr = (const ResXMLTree_attribute*) - (((const uint8_t*)tag) - + dtohs(tag->attributeStart) - + (dtohs(tag->attributeSize)*idx)); - return attr->typedValue.dataType; - } - } - return Res_value::TYPE_NULL; -} - -int32_t ResXMLParser::getAttributeData(size_t idx) const -{ - if (mEventCode == START_TAG) { - const ResXMLTree_attrExt* tag = (const ResXMLTree_attrExt*)mCurExt; - if (idx < dtohs(tag->attributeCount)) { - const ResXMLTree_attribute* attr = (const ResXMLTree_attribute*) - (((const uint8_t*)tag) - + dtohs(tag->attributeStart) - + (dtohs(tag->attributeSize)*idx)); - return dtohl(attr->typedValue.data); - } - } - return 0; -} - -ssize_t ResXMLParser::getAttributeValue(size_t idx, Res_value* outValue) const -{ - if (mEventCode == START_TAG) { - const ResXMLTree_attrExt* tag = (const ResXMLTree_attrExt*)mCurExt; - if (idx < dtohs(tag->attributeCount)) { - const ResXMLTree_attribute* attr = (const ResXMLTree_attribute*) - (((const uint8_t*)tag) - + dtohs(tag->attributeStart) - + (dtohs(tag->attributeSize)*idx)); - outValue->copyFrom_dtoh(attr->typedValue); - return sizeof(Res_value); - } - } - return BAD_TYPE; -} - -ssize_t ResXMLParser::indexOfAttribute(const char* ns, const char* attr) const -{ - String16 nsStr(ns != NULL ? ns : ""); - String16 attrStr(attr); - return indexOfAttribute(ns ? nsStr.string() : NULL, ns ? nsStr.size() : 0, - attrStr.string(), attrStr.size()); -} - -ssize_t ResXMLParser::indexOfAttribute(const char16_t* ns, size_t nsLen, - const char16_t* attr, size_t attrLen) const -{ - if (mEventCode == START_TAG) { - const size_t N = getAttributeCount(); - for (size_t i=0; i attr=%s, curAttr=%s\n", - // String8(attr).string(), String8(curAttr).string()); - if (attr && curAttr && (strzcmp16(attr, attrLen, curAttr, curAttrLen) == 0)) { - if (ns == NULL) { - if (curNs == NULL) return i; - } else if (curNs != NULL) { - //printf(" --> ns=%s, curNs=%s\n", - // String8(ns).string(), String8(curNs).string()); - if (strzcmp16(ns, nsLen, curNs, curNsLen) == 0) return i; - } - } - } - } - - return NAME_NOT_FOUND; -} - -ssize_t ResXMLParser::indexOfID() const -{ - if (mEventCode == START_TAG) { - const ssize_t idx = dtohs(((const ResXMLTree_attrExt*)mCurExt)->idIndex); - if (idx > 0) return (idx-1); - } - return NAME_NOT_FOUND; -} - -ssize_t ResXMLParser::indexOfClass() const -{ - if (mEventCode == START_TAG) { - const ssize_t idx = dtohs(((const ResXMLTree_attrExt*)mCurExt)->classIndex); - if (idx > 0) return (idx-1); - } - return NAME_NOT_FOUND; -} - -ssize_t ResXMLParser::indexOfStyle() const -{ - if (mEventCode == START_TAG) { - const ssize_t idx = dtohs(((const ResXMLTree_attrExt*)mCurExt)->styleIndex); - if (idx > 0) return (idx-1); - } - return NAME_NOT_FOUND; -} - -ResXMLParser::event_code_t ResXMLParser::nextNode() -{ - if (mEventCode < 0) { - return mEventCode; - } - - do { - const ResXMLTree_node* next = (const ResXMLTree_node*) - (((const uint8_t*)mCurNode) + dtohl(mCurNode->header.size)); - //LOGW("Next node: prev=%p, next=%p\n", mCurNode, next); - - if (((const uint8_t*)next) >= mTree.mDataEnd) { - mCurNode = NULL; - return (mEventCode=END_DOCUMENT); - } - - if (mTree.validateNode(next) != NO_ERROR) { - mCurNode = NULL; - return (mEventCode=BAD_DOCUMENT); - } - - mCurNode = next; - const uint16_t headerSize = dtohs(next->header.headerSize); - const uint32_t totalSize = dtohl(next->header.size); - mCurExt = ((const uint8_t*)next) + headerSize; - size_t minExtSize = 0; - event_code_t eventCode = (event_code_t)dtohs(next->header.type); - switch ((mEventCode=eventCode)) { - case RES_XML_START_NAMESPACE_TYPE: - case RES_XML_END_NAMESPACE_TYPE: - minExtSize = sizeof(ResXMLTree_namespaceExt); - break; - case RES_XML_START_ELEMENT_TYPE: - minExtSize = sizeof(ResXMLTree_attrExt); - break; - case RES_XML_END_ELEMENT_TYPE: - minExtSize = sizeof(ResXMLTree_endElementExt); - break; - case RES_XML_CDATA_TYPE: - minExtSize = sizeof(ResXMLTree_cdataExt); - break; - default: - LOGW("Unknown XML block: header type %d in node at %d\n", - (int)dtohs(next->header.type), - (int)(((const uint8_t*)next)-((const uint8_t*)mTree.mHeader))); - continue; - } - - if ((totalSize-headerSize) < minExtSize) { - LOGW("Bad XML block: header type 0x%x in node at 0x%x has size %d, need %d\n", - (int)dtohs(next->header.type), - (int)(((const uint8_t*)next)-((const uint8_t*)mTree.mHeader)), - (int)(totalSize-headerSize), (int)minExtSize); - return (mEventCode=BAD_DOCUMENT); - } - - //printf("CurNode=%p, CurExt=%p, headerSize=%d, minExtSize=%d\n", - // mCurNode, mCurExt, headerSize, minExtSize); - - return eventCode; - } while (true); -} - -void ResXMLParser::getPosition(ResXMLParser::ResXMLPosition* pos) const -{ - pos->eventCode = mEventCode; - pos->curNode = mCurNode; - pos->curExt = mCurExt; -} - -void ResXMLParser::setPosition(const ResXMLParser::ResXMLPosition& pos) -{ - mEventCode = pos.eventCode; - mCurNode = pos.curNode; - mCurExt = pos.curExt; -} - - -// -------------------------------------------------------------------- - -static volatile int32_t gCount = 0; - -ResXMLTree::ResXMLTree() - : ResXMLParser(*this) - , mError(NO_INIT), mOwnedData(NULL) -{ - //LOGI("Creating ResXMLTree %p #%d\n", this, android_atomic_inc(&gCount)+1); - restart(); -} - -ResXMLTree::ResXMLTree(const void* data, size_t size, bool copyData) - : ResXMLParser(*this) - , mError(NO_INIT), mOwnedData(NULL) -{ - //LOGI("Creating ResXMLTree %p #%d\n", this, android_atomic_inc(&gCount)+1); - setTo(data, size, copyData); -} - -ResXMLTree::~ResXMLTree() -{ - //LOGI("Destroying ResXMLTree in %p #%d\n", this, android_atomic_dec(&gCount)-1); - uninit(); -} - -status_t ResXMLTree::setTo(const void* data, size_t size, bool copyData) -{ - uninit(); - mEventCode = START_DOCUMENT; - - if (copyData) { - mOwnedData = malloc(size); - if (mOwnedData == NULL) { - return (mError=NO_MEMORY); - } - memcpy(mOwnedData, data, size); - data = mOwnedData; - } - - mHeader = (const ResXMLTree_header*)data; - mSize = dtohl(mHeader->header.size); - if (dtohs(mHeader->header.headerSize) > mSize || mSize > size) { - LOGW("Bad XML block: header size %d or total size %d is larger than data size %d\n", - (int)dtohs(mHeader->header.headerSize), - (int)dtohl(mHeader->header.size), (int)size); - mError = BAD_TYPE; - restart(); - return mError; - } - mDataEnd = ((const uint8_t*)mHeader) + mSize; - - mStrings.uninit(); - mRootNode = NULL; - mResIds = NULL; - mNumResIds = 0; - - // First look for a couple interesting chunks: the string block - // and first XML node. - const ResChunk_header* chunk = - (const ResChunk_header*)(((const uint8_t*)mHeader) + dtohs(mHeader->header.headerSize)); - const ResChunk_header* lastChunk = chunk; - while (((const uint8_t*)chunk) < (mDataEnd-sizeof(ResChunk_header)) && - ((const uint8_t*)chunk) < (mDataEnd-dtohl(chunk->size))) { - status_t err = validate_chunk(chunk, sizeof(ResChunk_header), mDataEnd, "XML"); - if (err != NO_ERROR) { - mError = err; - goto done; - } - const uint16_t type = dtohs(chunk->type); - const size_t size = dtohl(chunk->size); - XML_NOISY(printf("Scanning @ %p: type=0x%x, size=0x%x\n", - (void*)(((uint32_t)chunk)-((uint32_t)mHeader)), type, size)); - if (type == RES_STRING_POOL_TYPE) { - mStrings.setTo(chunk, size); - } else if (type == RES_XML_RESOURCE_MAP_TYPE) { - mResIds = (const uint32_t*) - (((const uint8_t*)chunk)+dtohs(chunk->headerSize)); - mNumResIds = (dtohl(chunk->size)-dtohs(chunk->headerSize))/sizeof(uint32_t); - } else if (type >= RES_XML_FIRST_CHUNK_TYPE - && type <= RES_XML_LAST_CHUNK_TYPE) { - if (validateNode((const ResXMLTree_node*)chunk) != NO_ERROR) { - mError = BAD_TYPE; - goto done; - } - mCurNode = (const ResXMLTree_node*)lastChunk; - if (nextNode() == BAD_DOCUMENT) { - mError = BAD_TYPE; - goto done; - } - mRootNode = mCurNode; - mRootExt = mCurExt; - mRootCode = mEventCode; - break; - } else { - XML_NOISY(printf("Skipping unknown chunk!\n")); - } - lastChunk = chunk; - chunk = (const ResChunk_header*) - (((const uint8_t*)chunk) + size); - } - - if (mRootNode == NULL) { - LOGW("Bad XML block: no root element node found\n"); - mError = BAD_TYPE; - goto done; - } - - mError = mStrings.getError(); - -done: - restart(); - return mError; -} - -status_t ResXMLTree::getError() const -{ - return mError; -} - -void ResXMLTree::uninit() -{ - mError = NO_INIT; - if (mOwnedData) { - free(mOwnedData); - mOwnedData = NULL; - } - restart(); -} - -const ResStringPool& ResXMLTree::getStrings() const -{ - return mStrings; -} - -status_t ResXMLTree::validateNode(const ResXMLTree_node* node) const -{ - const uint16_t eventCode = dtohs(node->header.type); - - status_t err = validate_chunk( - &node->header, sizeof(ResXMLTree_node), - mDataEnd, "ResXMLTree_node"); - - if (err >= NO_ERROR) { - // Only perform additional validation on START nodes - if (eventCode != RES_XML_START_ELEMENT_TYPE) { - return NO_ERROR; - } - - const uint16_t headerSize = dtohs(node->header.headerSize); - const uint32_t size = dtohl(node->header.size); - const ResXMLTree_attrExt* attrExt = (const ResXMLTree_attrExt*) - (((const uint8_t*)node) + headerSize); - // check for sensical values pulled out of the stream so far... - if ((size >= headerSize + sizeof(ResXMLTree_attrExt)) - && ((void*)attrExt > (void*)node)) { - const size_t attrSize = ((size_t)dtohs(attrExt->attributeSize)) - * dtohs(attrExt->attributeCount); - if ((dtohs(attrExt->attributeStart)+attrSize) <= (size-headerSize)) { - return NO_ERROR; - } - LOGW("Bad XML block: node attributes use 0x%x bytes, only have 0x%x bytes\n", - (unsigned int)(dtohs(attrExt->attributeStart)+attrSize), - (unsigned int)(size-headerSize)); - } - else { - LOGW("Bad XML start block: node header size 0x%x, size 0x%x\n", - (unsigned int)headerSize, (unsigned int)size); - } - return BAD_TYPE; - } - - return err; - -#if 0 - const bool isStart = dtohs(node->header.type) == RES_XML_START_ELEMENT_TYPE; - - const uint16_t headerSize = dtohs(node->header.headerSize); - const uint32_t size = dtohl(node->header.size); - - if (headerSize >= (isStart ? sizeof(ResXMLTree_attrNode) : sizeof(ResXMLTree_node))) { - if (size >= headerSize) { - if (((const uint8_t*)node) <= (mDataEnd-size)) { - if (!isStart) { - return NO_ERROR; - } - if ((((size_t)dtohs(node->attributeSize))*dtohs(node->attributeCount)) - <= (size-headerSize)) { - return NO_ERROR; - } - LOGW("Bad XML block: node attributes use 0x%x bytes, only have 0x%x bytes\n", - ((int)dtohs(node->attributeSize))*dtohs(node->attributeCount), - (int)(size-headerSize)); - return BAD_TYPE; - } - LOGW("Bad XML block: node at 0x%x extends beyond data end 0x%x\n", - (int)(((const uint8_t*)node)-((const uint8_t*)mHeader)), (int)mSize); - return BAD_TYPE; - } - LOGW("Bad XML block: node at 0x%x header size 0x%x smaller than total size 0x%x\n", - (int)(((const uint8_t*)node)-((const uint8_t*)mHeader)), - (int)headerSize, (int)size); - return BAD_TYPE; - } - LOGW("Bad XML block: node at 0x%x header size 0x%x too small\n", - (int)(((const uint8_t*)node)-((const uint8_t*)mHeader)), - (int)headerSize); - return BAD_TYPE; -#endif -} - -// -------------------------------------------------------------------- -// -------------------------------------------------------------------- -// -------------------------------------------------------------------- - -struct ResTable::Header -{ - Header() : ownedData(NULL), header(NULL) { } - - void* ownedData; - const ResTable_header* header; - size_t size; - const uint8_t* dataEnd; - size_t index; - void* cookie; - - ResStringPool values; -}; - -struct ResTable::Type -{ - Type(const Header* _header, const Package* _package, size_t count) - : header(_header), package(_package), entryCount(count), - typeSpec(NULL), typeSpecFlags(NULL) { } - const Header* const header; - const Package* const package; - const size_t entryCount; - const ResTable_typeSpec* typeSpec; - const uint32_t* typeSpecFlags; - Vector configs; -}; - -struct ResTable::Package -{ - Package(const Header* _header, const ResTable_package* _package) - : header(_header), package(_package) { } - ~Package() - { - size_t i = types.size(); - while (i > 0) { - i--; - delete types[i]; - } - } - - const Header* const header; - const ResTable_package* const package; - Vector types; - - const Type* getType(size_t idx) const { - return idx < types.size() ? types[idx] : NULL; - } -}; - -// A group of objects describing a particular resource package. -// The first in 'package' is always the root object (from the resource -// table that defined the package); the ones after are skins on top of it. -struct ResTable::PackageGroup -{ - PackageGroup(const String16& _name, uint32_t _id) - : name(_name), id(_id), typeCount(0), bags(NULL) { } - ~PackageGroup() { - clearBagCache(); - const size_t N = packages.size(); - for (size_t i=0; igetType(i); - if (type != NULL) { - bag_set** typeBags = bags[i]; - TABLE_NOISY(printf("typeBags=%p\n", typeBags)); - if (typeBags) { - TABLE_NOISY(printf("type->entryCount=%x\n", type->entryCount)); - const size_t N = type->entryCount; - for (size_t j=0; j packages; - - // Taken from the root package. - ResStringPool typeStrings; - ResStringPool keyStrings; - size_t typeCount; - - // Computed attribute bags, first indexed by the type and second - // by the entry in that type. - bag_set*** bags; -}; - -struct ResTable::bag_set -{ - size_t numAttrs; // number in array - size_t availAttrs; // total space in array - uint32_t typeSpecFlags; - // Followed by 'numAttr' bag_entry structures. -}; - -ResTable::Theme::Theme(const ResTable& table) - : mTable(table) -{ - memset(mPackages, 0, sizeof(mPackages)); -} - -ResTable::Theme::~Theme() -{ - for (size_t i=0; inumTypes; j++) { - theme_entry* te = pi->types[j].entries; - if (te != NULL) { - free(te); - } - } - free(pi); -} - -ResTable::Theme::package_info* ResTable::Theme::copy_package(package_info* pi) -{ - package_info* newpi = (package_info*)malloc( - sizeof(package_info) + (pi->numTypes*sizeof(type_info))); - newpi->numTypes = pi->numTypes; - for (size_t j=0; jnumTypes; j++) { - size_t cnt = pi->types[j].numEntries; - newpi->types[j].numEntries = cnt; - theme_entry* te = pi->types[j].entries; - if (te != NULL) { - theme_entry* newte = (theme_entry*)malloc(cnt*sizeof(theme_entry)); - newpi->types[j].entries = newte; - memcpy(newte, te, cnt*sizeof(theme_entry)); - } else { - newpi->types[j].entries = NULL; - } - } - return newpi; -} - -status_t ResTable::Theme::applyStyle(uint32_t resID, bool force) -{ - const bag_entry* bag; - uint32_t bagTypeSpecFlags = 0; - mTable.lock(); - const ssize_t N = mTable.getBagLocked(resID, &bag, &bagTypeSpecFlags); - TABLE_NOISY(LOGV("Applying style 0x%08x to theme %p, count=%d", resID, this, N)); - if (N < 0) { - mTable.unlock(); - return N; - } - - uint32_t curPackage = 0xffffffff; - ssize_t curPackageIndex = 0; - package_info* curPI = NULL; - uint32_t curType = 0xffffffff; - size_t numEntries = 0; - theme_entry* curEntries = NULL; - - const bag_entry* end = bag + N; - while (bag < end) { - const uint32_t attrRes = bag->map.name.ident; - const uint32_t p = Res_GETPACKAGE(attrRes); - const uint32_t t = Res_GETTYPE(attrRes); - const uint32_t e = Res_GETENTRY(attrRes); - - if (curPackage != p) { - const ssize_t pidx = mTable.getResourcePackageIndex(attrRes); - if (pidx < 0) { - LOGE("Style contains key with bad package: 0x%08x\n", attrRes); - bag++; - continue; - } - curPackage = p; - curPackageIndex = pidx; - curPI = mPackages[pidx]; - if (curPI == NULL) { - PackageGroup* const grp = mTable.mPackageGroups[pidx]; - int cnt = grp->typeCount; - curPI = (package_info*)malloc( - sizeof(package_info) + (cnt*sizeof(type_info))); - curPI->numTypes = cnt; - memset(curPI->types, 0, cnt*sizeof(type_info)); - mPackages[pidx] = curPI; - } - curType = 0xffffffff; - } - if (curType != t) { - if (t >= curPI->numTypes) { - LOGE("Style contains key with bad type: 0x%08x\n", attrRes); - bag++; - continue; - } - curType = t; - curEntries = curPI->types[t].entries; - if (curEntries == NULL) { - PackageGroup* const grp = mTable.mPackageGroups[curPackageIndex]; - const Type* type = grp->packages[0]->getType(t); - int cnt = type != NULL ? type->entryCount : 0; - curEntries = (theme_entry*)malloc(cnt*sizeof(theme_entry)); - memset(curEntries, Res_value::TYPE_NULL, cnt*sizeof(theme_entry)); - curPI->types[t].numEntries = cnt; - curPI->types[t].entries = curEntries; - } - numEntries = curPI->types[t].numEntries; - } - if (e >= numEntries) { - LOGE("Style contains key with bad entry: 0x%08x\n", attrRes); - bag++; - continue; - } - theme_entry* curEntry = curEntries + e; - TABLE_NOISY(LOGV("Attr 0x%08x: type=0x%x, data=0x%08x; curType=0x%x", - attrRes, bag->map.value.dataType, bag->map.value.data, - curEntry->value.dataType)); - if (force || curEntry->value.dataType == Res_value::TYPE_NULL) { - curEntry->stringBlock = bag->stringBlock; - curEntry->typeSpecFlags |= bagTypeSpecFlags; - curEntry->value = bag->map.value; - } - - bag++; - } - - mTable.unlock(); - - //LOGI("Applying style 0x%08x (force=%d) theme %p...\n", resID, force, this); - //dumpToLog(); - - return NO_ERROR; -} - -status_t ResTable::Theme::setTo(const Theme& other) -{ - //LOGI("Setting theme %p from theme %p...\n", this, &other); - //dumpToLog(); - //other.dumpToLog(); - - if (&mTable == &other.mTable) { - for (size_t i=0; i= 0) { - const package_info* const pi = mPackages[p]; - if (pi != NULL) { - if (t < pi->numTypes) { - const type_info& ti = pi->types[t]; - if (e < ti.numEntries) { - const theme_entry& te = ti.entries[e]; - if (outTypeSpecFlags != NULL) { - *outTypeSpecFlags |= te.typeSpecFlags; - } - const uint8_t type = te.value.dataType; - if (type == Res_value::TYPE_ATTRIBUTE) { - if (cnt > 0) { - cnt--; - resID = te.value.data; - continue; - } - LOGW("Too many attribute references, stopped at: 0x%08x\n", resID); - return BAD_INDEX; - } else if (type != Res_value::TYPE_NULL) { - *outValue = te.value; - return te.stringBlock; - } - return BAD_INDEX; - } - } - } - } - break; - - } while (true); - - return BAD_INDEX; -} - -ssize_t ResTable::Theme::resolveAttributeReference(Res_value* inOutValue, - ssize_t blockIndex, uint32_t* outLastRef, - uint32_t* inoutTypeSpecFlags) const -{ - //printf("Resolving type=0x%x\n", inOutValue->dataType); - if (inOutValue->dataType == Res_value::TYPE_ATTRIBUTE) { - uint32_t newTypeSpecFlags; - blockIndex = getAttribute(inOutValue->data, inOutValue, &newTypeSpecFlags); - if (inoutTypeSpecFlags != NULL) *inoutTypeSpecFlags |= newTypeSpecFlags; - //printf("Retrieved attribute new type=0x%x\n", inOutValue->dataType); - if (blockIndex < 0) { - return blockIndex; - } - } - return mTable.resolveReference(inOutValue, blockIndex, outLastRef); -} - -void ResTable::Theme::dumpToLog() const -{ - LOGI("Theme %p:\n", this); - for (size_t i=0; inumTypes; j++) { - type_info& ti = pi->types[j]; - if (ti.numEntries == 0) continue; - - LOGI(" Type #0x%02x:\n", (int)(j+1)); - for (size_t k=0; kgetBuffer(true); - if (data == NULL) { - LOGW("Unable to get buffer of resource asset file"); - return UNKNOWN_ERROR; - } - size_t size = (size_t)asset->getLength(); - return add(data, size, cookie, asset, copyData); -} - -status_t ResTable::add(const void* data, size_t size, void* cookie, - Asset* asset, bool copyData) -{ - if (!data) return NO_ERROR; - Header* header = new Header; - header->index = mHeaders.size(); - header->cookie = cookie; - mHeaders.add(header); - - const bool notDeviceEndian = htods(0xf0) != 0xf0; - - LOAD_TABLE_NOISY( - LOGV("Adding resources to ResTable: data=%p, size=0x%x, cookie=%p, asset=%p, copy=%d\n", - data, size, cookie, asset, copyData)); - - if (copyData || notDeviceEndian) { - header->ownedData = malloc(size); - if (header->ownedData == NULL) { - return (mError=NO_MEMORY); - } - memcpy(header->ownedData, data, size); - data = header->ownedData; - } - - header->header = (const ResTable_header*)data; - header->size = dtohl(header->header->header.size); - //LOGI("Got size 0x%x, again size 0x%x, raw size 0x%x\n", header->size, - // dtohl(header->header->header.size), header->header->header.size); - LOAD_TABLE_NOISY(LOGV("Loading ResTable @%p:\n", header->header)); - LOAD_TABLE_NOISY(printHexData(2, header->header, header->size < 256 ? header->size : 256, - 16, 16, 0, false, printToLogFunc)); - if (dtohs(header->header->header.headerSize) > header->size - || header->size > size) { - LOGW("Bad resource table: header size 0x%x or total size 0x%x is larger than data size 0x%x\n", - (int)dtohs(header->header->header.headerSize), - (int)header->size, (int)size); - return (mError=BAD_TYPE); - } - if (((dtohs(header->header->header.headerSize)|header->size)&0x3) != 0) { - LOGW("Bad resource table: header size 0x%x or total size 0x%x is not on an integer boundary\n", - (int)dtohs(header->header->header.headerSize), - (int)header->size); - return (mError=BAD_TYPE); - } - header->dataEnd = ((const uint8_t*)header->header) + header->size; - - // Iterate through all chunks. - size_t curPackage = 0; - - const ResChunk_header* chunk = - (const ResChunk_header*)(((const uint8_t*)header->header) - + dtohs(header->header->header.headerSize)); - while (((const uint8_t*)chunk) <= (header->dataEnd-sizeof(ResChunk_header)) && - ((const uint8_t*)chunk) <= (header->dataEnd-dtohl(chunk->size))) { - status_t err = validate_chunk(chunk, sizeof(ResChunk_header), header->dataEnd, "ResTable"); - if (err != NO_ERROR) { - return (mError=err); - } - TABLE_NOISY(LOGV("Chunk: type=0x%x, headerSize=0x%x, size=0x%x, pos=%p\n", - dtohs(chunk->type), dtohs(chunk->headerSize), dtohl(chunk->size), - (void*)(((const uint8_t*)chunk) - ((const uint8_t*)header->header)))); - const size_t csize = dtohl(chunk->size); - const uint16_t ctype = dtohs(chunk->type); - if (ctype == RES_STRING_POOL_TYPE) { - if (header->values.getError() != NO_ERROR) { - // Only use the first string chunk; ignore any others that - // may appear. - status_t err = header->values.setTo(chunk, csize); - if (err != NO_ERROR) { - return (mError=err); - } - } else { - LOGW("Multiple string chunks found in resource table."); - } - } else if (ctype == RES_TABLE_PACKAGE_TYPE) { - if (curPackage >= dtohl(header->header->packageCount)) { - LOGW("More package chunks were found than the %d declared in the header.", - dtohl(header->header->packageCount)); - return (mError=BAD_TYPE); - } - if (parsePackage((ResTable_package*)chunk, header) != NO_ERROR) { - return mError; - } - curPackage++; - } else { - LOGW("Unknown chunk type %p in table at %p.\n", - (void*)(int)(ctype), - (void*)(((const uint8_t*)chunk) - ((const uint8_t*)header->header))); - } - chunk = (const ResChunk_header*) - (((const uint8_t*)chunk) + csize); - } - - if (curPackage < dtohl(header->header->packageCount)) { - LOGW("Fewer package chunks (%d) were found than the %d declared in the header.", - (int)curPackage, dtohl(header->header->packageCount)); - return (mError=BAD_TYPE); - } - mError = header->values.getError(); - if (mError != NO_ERROR) { - LOGW("No string values found in resource table!"); - } - TABLE_NOISY(LOGV("Returning from add with mError=%d\n", mError)); - return mError; -} - -status_t ResTable::getError() const -{ - return mError; -} - -void ResTable::uninit() -{ - mError = NO_INIT; - size_t N = mPackageGroups.size(); - for (size_t i=0; iownedData) { - free(header->ownedData); - } - delete header; - } - - mPackageGroups.clear(); - mHeaders.clear(); -} - -bool ResTable::getResourceName(uint32_t resID, resource_name* outName) const -{ - if (mError != NO_ERROR) { - return false; - } - - const ssize_t p = getResourcePackageIndex(resID); - const int t = Res_GETTYPE(resID); - const int e = Res_GETENTRY(resID); - - if (p < 0) { - LOGW("No package identifier when getting name for resource number 0x%08x", resID); - return false; - } - if (t < 0) { - LOGW("No type identifier when getting name for resource number 0x%08x", resID); - return false; - } - - const PackageGroup* const grp = mPackageGroups[p]; - if (grp == NULL) { - LOGW("Bad identifier when getting name for resource number 0x%08x", resID); - return false; - } - if (grp->packages.size() > 0) { - const Package* const package = grp->packages[0]; - - const ResTable_type* type; - const ResTable_entry* entry; - ssize_t offset = getEntry(package, t, e, NULL, &type, &entry, NULL); - if (offset <= 0) { - return false; - } - - outName->package = grp->name.string(); - outName->packageLen = grp->name.size(); - outName->type = grp->typeStrings.stringAt(t, &outName->typeLen); - outName->name = grp->keyStrings.stringAt( - dtohl(entry->key.index), &outName->nameLen); - return true; - } - - return false; -} - -ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag, - uint32_t* outSpecFlags, ResTable_config* outConfig) const -{ - if (mError != NO_ERROR) { - return mError; - } - - const ssize_t p = getResourcePackageIndex(resID); - const int t = Res_GETTYPE(resID); - const int e = Res_GETENTRY(resID); - - if (p < 0) { - LOGW("No package identifier when getting value for resource number 0x%08x", resID); - return BAD_INDEX; - } - if (t < 0) { - LOGW("No type identifier when getting value for resource number 0x%08x", resID); - return BAD_INDEX; - } - - const Res_value* bestValue = NULL; - const Package* bestPackage = NULL; - ResTable_config bestItem; - memset(&bestItem, 0, sizeof(bestItem)); // make the compiler shut up - - if (outSpecFlags != NULL) *outSpecFlags = 0; - - // Look through all resource packages, starting with the most - // recently added. - const PackageGroup* const grp = mPackageGroups[p]; - if (grp == NULL) { - LOGW("Bad identifier when getting value for resource number 0x%08x", resID); - return false; - } - size_t ip = grp->packages.size(); - while (ip > 0) { - ip--; - - const Package* const package = grp->packages[ip]; - - const ResTable_type* type; - const ResTable_entry* entry; - const Type* typeClass; - ssize_t offset = getEntry(package, t, e, &mParams, &type, &entry, &typeClass); - if (offset <= 0) { - if (offset < 0) { - LOGW("Failure getting entry for 0x%08x (t=%d e=%d) in package %d: 0x%08x\n", - resID, t, e, (int)ip, (int)offset); - return offset; - } - continue; - } - - if ((dtohs(entry->flags)&entry->FLAG_COMPLEX) != 0) { - if (!mayBeBag) { - LOGW("Requesting resource %p failed because it is complex\n", - (void*)resID); - } - continue; - } - - TABLE_NOISY(aout << "Resource type data: " - << HexDump(type, dtohl(type->header.size)) << endl); - - if ((size_t)offset > (dtohl(type->header.size)-sizeof(Res_value))) { - LOGW("ResTable_item at %d is beyond type chunk data %d", - (int)offset, dtohl(type->header.size)); - return BAD_TYPE; - } - - const Res_value* item = - (const Res_value*)(((const uint8_t*)type) + offset); - ResTable_config thisConfig; - thisConfig.copyFromDtoH(type->config); - - if (outSpecFlags != NULL) { - if (typeClass->typeSpecFlags != NULL) { - *outSpecFlags |= dtohl(typeClass->typeSpecFlags[e]); - } else { - *outSpecFlags = -1; - } - } - - if (bestPackage != NULL && bestItem.isBetterThan(thisConfig)) { - continue; - } - - bestItem = thisConfig; - bestValue = item; - bestPackage = package; - } - - TABLE_NOISY(printf("Found result: package %p\n", bestPackage)); - - if (bestValue) { - outValue->size = dtohs(bestValue->size); - outValue->res0 = bestValue->res0; - outValue->dataType = bestValue->dataType; - outValue->data = dtohl(bestValue->data); - if (outConfig != NULL) { - *outConfig = bestItem; - } - TABLE_NOISY(size_t len; - printf("Found value: pkg=%d, type=%d, str=%s, int=%d\n", - bestPackage->header->index, - outValue->dataType, - outValue->dataType == bestValue->TYPE_STRING - ? String8(bestPackage->header->values.stringAt( - outValue->data, &len)).string() - : "", - outValue->data)); - return bestPackage->header->index; - } - - return BAD_INDEX; -} - -ssize_t ResTable::resolveReference(Res_value* value, ssize_t blockIndex, - uint32_t* outLastRef, uint32_t* inoutTypeSpecFlags) const -{ - int count=0; - while (blockIndex >= 0 && value->dataType == value->TYPE_REFERENCE - && value->data != 0 && count < 20) { - if (outLastRef) *outLastRef = value->data; - uint32_t lastRef = value->data; - uint32_t newFlags = 0; - const ssize_t newIndex = getResource(value->data, value, true, &newFlags); - //LOGI("Resolving reference d=%p: newIndex=%d, t=0x%02x, d=%p\n", - // (void*)lastRef, (int)newIndex, (int)value->dataType, (void*)value->data); - //printf("Getting reference 0x%08x: newIndex=%d\n", value->data, newIndex); - if (inoutTypeSpecFlags != NULL) *inoutTypeSpecFlags |= newFlags; - if (newIndex < 0) { - // This can fail if the resource being referenced is a style... - // in this case, just return the reference, and expect the - // caller to deal with. - return blockIndex; - } - blockIndex = newIndex; - count++; - } - return blockIndex; -} - -const char16_t* ResTable::valueToString( - const Res_value* value, size_t stringBlock, - char16_t tmpBuffer[TMP_BUFFER_SIZE], size_t* outLen) -{ - if (!value) { - return NULL; - } - if (value->dataType == value->TYPE_STRING) { - return getTableStringBlock(stringBlock)->stringAt(value->data, outLen); - } - // XXX do int to string conversions. - return NULL; -} - -ssize_t ResTable::lockBag(uint32_t resID, const bag_entry** outBag) const -{ - mLock.lock(); - ssize_t err = getBagLocked(resID, outBag); - if (err < NO_ERROR) { - //printf("*** get failed! unlocking\n"); - mLock.unlock(); - } - return err; -} - -void ResTable::unlockBag(const bag_entry* bag) const -{ - //printf("<<< unlockBag %p\n", this); - mLock.unlock(); -} - -void ResTable::lock() const -{ - mLock.lock(); -} - -void ResTable::unlock() const -{ - mLock.unlock(); -} - -ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag, - uint32_t* outTypeSpecFlags) const -{ - if (mError != NO_ERROR) { - return mError; - } - - const ssize_t p = getResourcePackageIndex(resID); - const int t = Res_GETTYPE(resID); - const int e = Res_GETENTRY(resID); - - if (p < 0) { - LOGW("Invalid package identifier when getting bag for resource number 0x%08x", resID); - return BAD_INDEX; - } - if (t < 0) { - LOGW("No type identifier when getting bag for resource number 0x%08x", resID); - return BAD_INDEX; - } - - //printf("Get bag: id=0x%08x, p=%d, t=%d\n", resID, p, t); - PackageGroup* const grp = mPackageGroups[p]; - if (grp == NULL) { - LOGW("Bad identifier when getting bag for resource number 0x%08x", resID); - return false; - } - - if (t >= (int)grp->typeCount) { - LOGW("Type identifier 0x%x is larger than type count 0x%x", - t+1, (int)grp->typeCount); - return BAD_INDEX; - } - - const Package* const basePackage = grp->packages[0]; - - const Type* const typeConfigs = basePackage->getType(t); - - const size_t NENTRY = typeConfigs->entryCount; - if (e >= (int)NENTRY) { - LOGW("Entry identifier 0x%x is larger than entry count 0x%x", - e, (int)typeConfigs->entryCount); - return BAD_INDEX; - } - - // First see if we've already computed this bag... - if (grp->bags) { - bag_set** typeSet = grp->bags[t]; - if (typeSet) { - bag_set* set = typeSet[e]; - if (set) { - if (set != (bag_set*)0xFFFFFFFF) { - if (outTypeSpecFlags != NULL) { - *outTypeSpecFlags = set->typeSpecFlags; - } - *outBag = (bag_entry*)(set+1); - //LOGI("Found existing bag for: %p\n", (void*)resID); - return set->numAttrs; - } - LOGW("Attempt to retrieve bag 0x%08x which is invalid or in a cycle.", - resID); - return BAD_INDEX; - } - } - } - - // Bag not found, we need to compute it! - if (!grp->bags) { - grp->bags = (bag_set***)malloc(sizeof(bag_set*)*grp->typeCount); - if (!grp->bags) return NO_MEMORY; - memset(grp->bags, 0, sizeof(bag_set*)*grp->typeCount); - } - - bag_set** typeSet = grp->bags[t]; - if (!typeSet) { - typeSet = (bag_set**)malloc(sizeof(bag_set*)*NENTRY); - if (!typeSet) return NO_MEMORY; - memset(typeSet, 0, sizeof(bag_set*)*NENTRY); - grp->bags[t] = typeSet; - } - - // Mark that we are currently working on this one. - typeSet[e] = (bag_set*)0xFFFFFFFF; - - // This is what we are building. - bag_set* set = NULL; - - TABLE_NOISY(LOGI("Building bag: %p\n", (void*)resID)); - - // Now collect all bag attributes from all packages. - size_t ip = grp->packages.size(); - while (ip > 0) { - ip--; - - const Package* const package = grp->packages[ip]; - - const ResTable_type* type; - const ResTable_entry* entry; - const Type* typeClass; - LOGV("Getting entry pkg=%p, t=%d, e=%d\n", package, t, e); - ssize_t offset = getEntry(package, t, e, &mParams, &type, &entry, &typeClass); - LOGV("Resulting offset=%d\n", offset); - if (offset <= 0) { - if (offset < 0) { - if (set) free(set); - return offset; - } - continue; - } - - if ((dtohs(entry->flags)&entry->FLAG_COMPLEX) == 0) { - LOGW("Skipping entry %p in package table %d because it is not complex!\n", - (void*)resID, (int)ip); - continue; - } - - const uint16_t entrySize = dtohs(entry->size); - const uint32_t parent = entrySize >= sizeof(ResTable_map_entry) - ? dtohl(((const ResTable_map_entry*)entry)->parent.ident) : 0; - const uint32_t count = entrySize >= sizeof(ResTable_map_entry) - ? dtohl(((const ResTable_map_entry*)entry)->count) : 0; - - size_t N = count; - - TABLE_NOISY(LOGI("Found map: size=%p parent=%p count=%d\n", - entrySize, parent, count)); - - if (set == NULL) { - // If this map inherits from another, we need to start - // with its parent's values. Otherwise start out empty. - TABLE_NOISY(printf("Creating new bag, entrySize=0x%08x, parent=0x%08x\n", - entrySize, parent)); - if (parent) { - const bag_entry* parentBag; - uint32_t parentTypeSpecFlags = 0; - const ssize_t NP = getBagLocked(parent, &parentBag, &parentTypeSpecFlags); - const size_t NT = ((NP >= 0) ? NP : 0) + N; - set = (bag_set*)malloc(sizeof(bag_set)+sizeof(bag_entry)*NT); - if (set == NULL) { - return NO_MEMORY; - } - if (NP > 0) { - memcpy(set+1, parentBag, NP*sizeof(bag_entry)); - set->numAttrs = NP; - TABLE_NOISY(LOGI("Initialized new bag with %d inherited attributes.\n", NP)); - } else { - TABLE_NOISY(LOGI("Initialized new bag with no inherited attributes.\n")); - set->numAttrs = 0; - } - set->availAttrs = NT; - set->typeSpecFlags = parentTypeSpecFlags; - } else { - set = (bag_set*)malloc(sizeof(bag_set)+sizeof(bag_entry)*N); - if (set == NULL) { - return NO_MEMORY; - } - set->numAttrs = 0; - set->availAttrs = N; - set->typeSpecFlags = 0; - } - } - - if (typeClass->typeSpecFlags != NULL) { - set->typeSpecFlags |= dtohl(typeClass->typeSpecFlags[e]); - } else { - set->typeSpecFlags = -1; - } - - // Now merge in the new attributes... - ssize_t curOff = offset; - const ResTable_map* map; - bag_entry* entries = (bag_entry*)(set+1); - size_t curEntry = 0; - uint32_t pos = 0; - TABLE_NOISY(LOGI("Starting with set %p, entries=%p, avail=%d\n", - set, entries, set->availAttrs)); - while (pos < count) { - TABLE_NOISY(printf("Now at %p\n", (void*)curOff)); - - if ((size_t)curOff > (dtohl(type->header.size)-sizeof(ResTable_map))) { - LOGW("ResTable_map at %d is beyond type chunk data %d", - (int)curOff, dtohl(type->header.size)); - return BAD_TYPE; - } - map = (const ResTable_map*)(((const uint8_t*)type) + curOff); - N++; - - const uint32_t newName = htodl(map->name.ident); - bool isInside; - uint32_t oldName = 0; - while ((isInside=(curEntry < set->numAttrs)) - && (oldName=entries[curEntry].map.name.ident) < newName) { - TABLE_NOISY(printf("#%d: Keeping existing attribute: 0x%08x\n", - curEntry, entries[curEntry].map.name.ident)); - curEntry++; - } - - if ((!isInside) || oldName != newName) { - // This is a new attribute... figure out what to do with it. - if (set->numAttrs >= set->availAttrs) { - // Need to alloc more memory... - const size_t newAvail = set->availAttrs+N; - set = (bag_set*)realloc(set, - sizeof(bag_set) - + sizeof(bag_entry)*newAvail); - if (set == NULL) { - return NO_MEMORY; - } - set->availAttrs = newAvail; - entries = (bag_entry*)(set+1); - TABLE_NOISY(printf("Reallocated set %p, entries=%p, avail=%d\n", - set, entries, set->availAttrs)); - } - if (isInside) { - // Going in the middle, need to make space. - memmove(entries+curEntry+1, entries+curEntry, - sizeof(bag_entry)*(set->numAttrs-curEntry)); - set->numAttrs++; - } - TABLE_NOISY(printf("#%d: Inserting new attribute: 0x%08x\n", - curEntry, newName)); - } else { - TABLE_NOISY(printf("#%d: Replacing existing attribute: 0x%08x\n", - curEntry, oldName)); - } - - bag_entry* cur = entries+curEntry; - - cur->stringBlock = package->header->index; - cur->map.name.ident = newName; - cur->map.value.copyFrom_dtoh(map->value); - TABLE_NOISY(printf("Setting entry #%d %p: block=%d, name=0x%08x, type=%d, data=0x%08x\n", - curEntry, cur, cur->stringBlock, cur->map.name.ident, - cur->map.value.dataType, cur->map.value.data)); - - // On to the next! - curEntry++; - pos++; - const size_t size = dtohs(map->value.size); - curOff += size + sizeof(*map)-sizeof(map->value); - }; - if (curEntry > set->numAttrs) { - set->numAttrs = curEntry; - } - } - - // And this is it... - typeSet[e] = set; - if (set) { - if (outTypeSpecFlags != NULL) { - *outTypeSpecFlags = set->typeSpecFlags; - } - *outBag = (bag_entry*)(set+1); - TABLE_NOISY(LOGI("Returning %d attrs\n", set->numAttrs)); - return set->numAttrs; - } - return BAD_INDEX; -} - -void ResTable::setParameters(const ResTable_config* params) -{ - mLock.lock(); - TABLE_GETENTRY(LOGI("Setting parameters: imsi:%d/%d lang:%c%c cnt:%c%c " - "orien:%d touch:%d density:%d key:%d inp:%d nav:%d w:%d h:%d\n", - params->mcc, params->mnc, - params->language[0] ? params->language[0] : '-', - params->language[1] ? params->language[1] : '-', - params->country[0] ? params->country[0] : '-', - params->country[1] ? params->country[1] : '-', - params->orientation, - params->touchscreen, - params->density, - params->keyboard, - params->inputFlags, - params->navigation, - params->screenWidth, - params->screenHeight)); - mParams = *params; - for (size_t i=0; iclearBagCache(); - } - mLock.unlock(); -} - -void ResTable::getParameters(ResTable_config* params) const -{ - mLock.lock(); - *params = mParams; - mLock.unlock(); -} - -struct id_name_map { - uint32_t id; - size_t len; - char16_t name[6]; -}; - -const static id_name_map ID_NAMES[] = { - { ResTable_map::ATTR_TYPE, 5, { '^', 't', 'y', 'p', 'e' } }, - { ResTable_map::ATTR_L10N, 5, { '^', 'l', '1', '0', 'n' } }, - { ResTable_map::ATTR_MIN, 4, { '^', 'm', 'i', 'n' } }, - { ResTable_map::ATTR_MAX, 4, { '^', 'm', 'a', 'x' } }, - { ResTable_map::ATTR_OTHER, 6, { '^', 'o', 't', 'h', 'e', 'r' } }, - { ResTable_map::ATTR_ZERO, 5, { '^', 'z', 'e', 'r', 'o' } }, - { ResTable_map::ATTR_ONE, 4, { '^', 'o', 'n', 'e' } }, - { ResTable_map::ATTR_TWO, 4, { '^', 't', 'w', 'o' } }, - { ResTable_map::ATTR_FEW, 4, { '^', 'f', 'e', 'w' } }, - { ResTable_map::ATTR_MANY, 5, { '^', 'm', 'a', 'n', 'y' } }, -}; - -uint32_t ResTable::identifierForName(const char16_t* name, size_t nameLen, - const char16_t* type, size_t typeLen, - const char16_t* package, - size_t packageLen, - uint32_t* outTypeSpecFlags) const -{ - TABLE_SUPER_NOISY(printf("Identifier for name: error=%d\n", mError)); - - // Check for internal resource identifier as the very first thing, so - // that we will always find them even when there are no resources. - if (name[0] == '^') { - const int N = (sizeof(ID_NAMES)/sizeof(ID_NAMES[0])); - size_t len; - for (int i=0; ilen; - if (len != nameLen) { - continue; - } - for (size_t j=1; jname[j] != name[j]) { - goto nope; - } - } - return m->id; -nope: - ; - } - if (nameLen > 7) { - if (name[1] == 'i' && name[2] == 'n' - && name[3] == 'd' && name[4] == 'e' && name[5] == 'x' - && name[6] == '_') { - int index = atoi(String8(name + 7, nameLen - 7).string()); - if (Res_CHECKID(index)) { - LOGW("Array resource index: %d is too large.", - index); - return 0; - } - return Res_MAKEARRAY(index); - } - } - return 0; - } - - if (mError != NO_ERROR) { - return 0; - } - - // Figure out the package and type we are looking in... - - const char16_t* packageEnd = NULL; - const char16_t* typeEnd = NULL; - const char16_t* const nameEnd = name+nameLen; - const char16_t* p = name; - while (p < nameEnd) { - if (*p == ':') packageEnd = p; - else if (*p == '/') typeEnd = p; - p++; - } - if (*name == '@') name++; - if (name >= nameEnd) { - return 0; - } - - if (packageEnd) { - package = name; - packageLen = packageEnd-name; - name = packageEnd+1; - } else if (!package) { - return 0; - } - - if (typeEnd) { - type = name; - typeLen = typeEnd-name; - name = typeEnd+1; - } else if (!type) { - return 0; - } - - if (name >= nameEnd) { - return 0; - } - nameLen = nameEnd-name; - - TABLE_NOISY(printf("Looking for identifier: type=%s, name=%s, package=%s\n", - String8(type, typeLen).string(), - String8(name, nameLen).string(), - String8(package, packageLen).string())); - - const size_t NG = mPackageGroups.size(); - for (size_t ig=0; igname.string(), group->name.size())) { - TABLE_NOISY(printf("Skipping package group: %s\n", String8(group->name).string())); - continue; - } - - const ssize_t ti = group->typeStrings.indexOfString(type, typeLen); - if (ti < 0) { - TABLE_NOISY(printf("Type not found in package %s\n", String8(group->name).string())); - continue; - } - - const ssize_t ei = group->keyStrings.indexOfString(name, nameLen); - if (ei < 0) { - TABLE_NOISY(printf("Name not found in package %s\n", String8(group->name).string())); - continue; - } - - TABLE_NOISY(printf("Search indices: type=%d, name=%d\n", ti, ei)); - - const Type* const typeConfigs = group->packages[0]->getType(ti); - if (typeConfigs == NULL || typeConfigs->configs.size() <= 0) { - TABLE_NOISY(printf("Expected type structure not found in package %s for idnex %d\n", - String8(group->name).string(), ti)); - } - - size_t NTC = typeConfigs->configs.size(); - for (size_t tci=0; tciconfigs[tci]; - const uint32_t typeOffset = dtohl(ty->entriesStart); - - const uint8_t* const end = ((const uint8_t*)ty) + dtohl(ty->header.size); - const uint32_t* const eindex = (const uint32_t*) - (((const uint8_t*)ty) + dtohs(ty->header.headerSize)); - - const size_t NE = dtohl(ty->entryCount); - for (size_t i=0; i (dtohl(ty->header.size)-sizeof(ResTable_entry))) { - LOGW("ResTable_entry at %d is beyond type chunk data %d", - offset, dtohl(ty->header.size)); - return 0; - } - if ((offset&0x3) != 0) { - LOGW("ResTable_entry at %d (pkg=%d type=%d ent=%d) is not on an integer boundary when looking for %s:%s/%s", - (int)offset, (int)group->id, (int)ti+1, (int)i, - String8(package, packageLen).string(), - String8(type, typeLen).string(), - String8(name, nameLen).string()); - return 0; - } - - const ResTable_entry* const entry = (const ResTable_entry*) - (((const uint8_t*)ty) + offset); - if (dtohs(entry->size) < sizeof(*entry)) { - LOGW("ResTable_entry size %d is too small", dtohs(entry->size)); - return BAD_TYPE; - } - - TABLE_SUPER_NOISY(printf("Looking at entry #%d: want str %d, have %d\n", - i, ei, dtohl(entry->key.index))); - if (dtohl(entry->key.index) == (size_t)ei) { - if (outTypeSpecFlags) { - *outTypeSpecFlags = typeConfigs->typeSpecFlags[i]; - } - return Res_MAKEID(group->id-1, ti, i); - } - } - } - } - - return 0; -} - -bool ResTable::expandResourceRef(const uint16_t* refStr, size_t refLen, - String16* outPackage, - String16* outType, - String16* outName, - const String16* defType, - const String16* defPackage, - const char** outErrorMsg) -{ - const char16_t* packageEnd = NULL; - const char16_t* typeEnd = NULL; - const char16_t* p = refStr; - const char16_t* const end = p + refLen; - while (p < end) { - if (*p == ':') packageEnd = p; - else if (*p == '/') { - typeEnd = p; - break; - } - p++; - } - p = refStr; - if (*p == '@') p++; - - if (packageEnd) { - *outPackage = String16(p, packageEnd-p); - p = packageEnd+1; - } else { - if (!defPackage) { - if (outErrorMsg) { - *outErrorMsg = "No resource package specified"; - } - return false; - } - *outPackage = *defPackage; - } - if (typeEnd) { - *outType = String16(p, typeEnd-p); - p = typeEnd+1; - } else { - if (!defType) { - if (outErrorMsg) { - *outErrorMsg = "No resource type specified"; - } - return false; - } - *outType = *defType; - } - *outName = String16(p, end-p); - return true; -} - -static uint32_t get_hex(char c, bool* outError) -{ - if (c >= '0' && c <= '9') { - return c - '0'; - } else if (c >= 'a' && c <= 'f') { - return c - 'a' + 0xa; - } else if (c >= 'A' && c <= 'F') { - return c - 'A' + 0xa; - } - *outError = true; - return 0; -} - -struct unit_entry -{ - const char* name; - size_t len; - uint8_t type; - uint32_t unit; - float scale; -}; - -static const unit_entry unitNames[] = { - { "px", strlen("px"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_PX, 1.0f }, - { "dip", strlen("dip"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_DIP, 1.0f }, - { "dp", strlen("dp"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_DIP, 1.0f }, - { "sp", strlen("sp"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_SP, 1.0f }, - { "pt", strlen("pt"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_PT, 1.0f }, - { "in", strlen("in"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_IN, 1.0f }, - { "mm", strlen("mm"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_MM, 1.0f }, - { "%", strlen("%"), Res_value::TYPE_FRACTION, Res_value::COMPLEX_UNIT_FRACTION, 1.0f/100 }, - { "%p", strlen("%p"), Res_value::TYPE_FRACTION, Res_value::COMPLEX_UNIT_FRACTION_PARENT, 1.0f/100 }, - { NULL, 0, 0, 0, 0 } -}; - -static bool parse_unit(const char* str, Res_value* outValue, - float* outScale, const char** outEnd) -{ - const char* end = str; - while (*end != 0 && !isspace((unsigned char)*end)) { - end++; - } - const size_t len = end-str; - - const char* realEnd = end; - while (*realEnd != 0 && isspace((unsigned char)*realEnd)) { - realEnd++; - } - if (*realEnd != 0) { - return false; - } - - const unit_entry* cur = unitNames; - while (cur->name) { - if (len == cur->len && strncmp(cur->name, str, len) == 0) { - outValue->dataType = cur->type; - outValue->data = cur->unit << Res_value::COMPLEX_UNIT_SHIFT; - *outScale = cur->scale; - *outEnd = end; - //printf("Found unit %s for %s\n", cur->name, str); - return true; - } - cur++; - } - - return false; -} - - -bool ResTable::stringToInt(const char16_t* s, size_t len, Res_value* outValue) -{ - while (len > 0 && isspace16(*s)) { - s++; - len--; - } - - if (len <= 0) { - return false; - } - - size_t i = 0; - int32_t val = 0; - bool neg = false; - - if (*s == '-') { - neg = true; - i++; - } - - if (s[i] < '0' || s[i] > '9') { - return false; - } - - // Decimal or hex? - if (s[i] == '0' && s[i+1] == 'x') { - if (outValue) - outValue->dataType = outValue->TYPE_INT_HEX; - i += 2; - bool error = false; - while (i < len && !error) { - val = (val*16) + get_hex(s[i], &error); - i++; - } - if (error) { - return false; - } - } else { - if (outValue) - outValue->dataType = outValue->TYPE_INT_DEC; - while (i < len) { - if (s[i] < '0' || s[i] > '9') { - return false; - } - val = (val*10) + s[i]-'0'; - i++; - } - } - - if (neg) val = -val; - - while (i < len && isspace16(s[i])) { - i++; - } - - if (i == len) { - if (outValue) - outValue->data = val; - return true; - } - - return false; -} - -bool ResTable::stringToFloat(const char16_t* s, size_t len, Res_value* outValue) -{ - while (len > 0 && isspace16(*s)) { - s++; - len--; - } - - if (len <= 0) { - return false; - } - - char buf[128]; - int i=0; - while (len > 0 && *s != 0 && i < 126) { - if (*s > 255) { - return false; - } - buf[i++] = *s++; - len--; - } - - if (len > 0) { - return false; - } - if (buf[0] < '0' && buf[0] > '9' && buf[0] != '.') { - return false; - } - - buf[i] = 0; - const char* end; - float f = strtof(buf, (char**)&end); - - if (*end != 0 && !isspace((unsigned char)*end)) { - // Might be a unit... - float scale; - if (parse_unit(end, outValue, &scale, &end)) { - f *= scale; - const bool neg = f < 0; - if (neg) f = -f; - uint64_t bits = (uint64_t)(f*(1<<23)+.5f); - uint32_t radix; - uint32_t shift; - if ((bits&0x7fffff) == 0) { - // Always use 23p0 if there is no fraction, just to make - // things easier to read. - radix = Res_value::COMPLEX_RADIX_23p0; - shift = 23; - } else if ((bits&0xffffffffff800000LL) == 0) { - // Magnitude is zero -- can fit in 0 bits of precision. - radix = Res_value::COMPLEX_RADIX_0p23; - shift = 0; - } else if ((bits&0xffffffff80000000LL) == 0) { - // Magnitude can fit in 8 bits of precision. - radix = Res_value::COMPLEX_RADIX_8p15; - shift = 8; - } else if ((bits&0xffffff8000000000LL) == 0) { - // Magnitude can fit in 16 bits of precision. - radix = Res_value::COMPLEX_RADIX_16p7; - shift = 16; - } else { - // Magnitude needs entire range, so no fractional part. - radix = Res_value::COMPLEX_RADIX_23p0; - shift = 23; - } - int32_t mantissa = (int32_t)( - (bits>>shift) & Res_value::COMPLEX_MANTISSA_MASK); - if (neg) { - mantissa = (-mantissa) & Res_value::COMPLEX_MANTISSA_MASK; - } - outValue->data |= - (radix<data); - return true; - } - return false; - } - - while (*end != 0 && isspace((unsigned char)*end)) { - end++; - } - - if (*end == 0) { - if (outValue) { - outValue->dataType = outValue->TYPE_FLOAT; - *(float*)(&outValue->data) = f; - return true; - } - } - - return false; -} - -bool ResTable::stringToValue(Res_value* outValue, String16* outString, - const char16_t* s, size_t len, - bool preserveSpaces, bool coerceType, - uint32_t attrID, - const String16* defType, - const String16* defPackage, - Accessor* accessor, - void* accessorCookie, - uint32_t attrType, - bool enforcePrivate) const -{ - bool localizationSetting = accessor != NULL && accessor->getLocalizationSetting(); - const char* errorMsg = NULL; - - outValue->size = sizeof(Res_value); - outValue->res0 = 0; - - // First strip leading/trailing whitespace. Do this before handling - // escapes, so they can be used to force whitespace into the string. - if (!preserveSpaces) { - while (len > 0 && isspace16(*s)) { - s++; - len--; - } - while (len > 0 && isspace16(s[len-1])) { - len--; - } - // If the string ends with '\', then we keep the space after it. - if (len > 0 && s[len-1] == '\\' && s[len] != 0) { - len++; - } - } - - //printf("Value for: %s\n", String8(s, len).string()); - - uint32_t l10nReq = ResTable_map::L10N_NOT_REQUIRED; - uint32_t attrMin = 0x80000000, attrMax = 0x7fffffff; - bool fromAccessor = false; - if (attrID != 0 && !Res_INTERNALID(attrID)) { - const ssize_t p = getResourcePackageIndex(attrID); - const bag_entry* bag; - ssize_t cnt = p >= 0 ? lockBag(attrID, &bag) : -1; - //printf("For attr 0x%08x got bag of %d\n", attrID, cnt); - if (cnt >= 0) { - while (cnt > 0) { - //printf("Entry 0x%08x = 0x%08x\n", bag->map.name.ident, bag->map.value.data); - switch (bag->map.name.ident) { - case ResTable_map::ATTR_TYPE: - attrType = bag->map.value.data; - break; - case ResTable_map::ATTR_MIN: - attrMin = bag->map.value.data; - break; - case ResTable_map::ATTR_MAX: - attrMax = bag->map.value.data; - break; - case ResTable_map::ATTR_L10N: - l10nReq = bag->map.value.data; - break; - } - bag++; - cnt--; - } - unlockBag(bag); - } else if (accessor && accessor->getAttributeType(attrID, &attrType)) { - fromAccessor = true; - if (attrType == ResTable_map::TYPE_ENUM - || attrType == ResTable_map::TYPE_FLAGS - || attrType == ResTable_map::TYPE_INTEGER) { - accessor->getAttributeMin(attrID, &attrMin); - accessor->getAttributeMax(attrID, &attrMax); - } - if (localizationSetting) { - l10nReq = accessor->getAttributeL10N(attrID); - } - } - } - - const bool canStringCoerce = - coerceType && (attrType&ResTable_map::TYPE_STRING) != 0; - - if (*s == '@') { - outValue->dataType = outValue->TYPE_REFERENCE; - - // Note: we don't check attrType here because the reference can - // be to any other type; we just need to count on the client making - // sure the referenced type is correct. - - //printf("Looking up ref: %s\n", String8(s, len).string()); - - // It's a reference! - if (len == 5 && s[1]=='n' && s[2]=='u' && s[3]=='l' && s[4]=='l') { - outValue->data = 0; - return true; - } else { - bool createIfNotFound = false; - const char16_t* resourceRefName; - int resourceNameLen; - if (len > 2 && s[1] == '+') { - createIfNotFound = true; - resourceRefName = s + 2; - resourceNameLen = len - 2; - } else if (len > 2 && s[1] == '*') { - enforcePrivate = false; - resourceRefName = s + 2; - resourceNameLen = len - 2; - } else { - createIfNotFound = false; - resourceRefName = s + 1; - resourceNameLen = len - 1; - } - String16 package, type, name; - if (!expandResourceRef(resourceRefName,resourceNameLen, &package, &type, &name, - defType, defPackage, &errorMsg)) { - if (accessor != NULL) { - accessor->reportError(accessorCookie, errorMsg); - } - return false; - } - - uint32_t specFlags = 0; - uint32_t rid = identifierForName(name.string(), name.size(), type.string(), - type.size(), package.string(), package.size(), &specFlags); - if (rid != 0) { - if (enforcePrivate) { - if ((specFlags&ResTable_typeSpec::SPEC_PUBLIC) == 0) { - if (accessor != NULL) { - accessor->reportError(accessorCookie, "Resource is not public."); - } - return false; - } - } - if (!accessor) { - outValue->data = rid; - return true; - } - rid = Res_MAKEID( - accessor->getRemappedPackage(Res_GETPACKAGE(rid)), - Res_GETTYPE(rid), Res_GETENTRY(rid)); - TABLE_NOISY(printf("Incl %s:%s/%s: 0x%08x\n", - String8(package).string(), String8(type).string(), - String8(name).string(), rid)); - outValue->data = rid; - return true; - } - - if (accessor) { - uint32_t rid = accessor->getCustomResourceWithCreation(package, type, name, - createIfNotFound); - if (rid != 0) { - TABLE_NOISY(printf("Pckg %s:%s/%s: 0x%08x\n", - String8(package).string(), String8(type).string(), - String8(name).string(), rid)); - outValue->data = rid; - return true; - } - } - } - - if (accessor != NULL) { - accessor->reportError(accessorCookie, "No resource found that matches the given name"); - } - return false; - } - - // if we got to here, and localization is required and it's not a reference, - // complain and bail. - if (l10nReq == ResTable_map::L10N_SUGGESTED) { - if (localizationSetting) { - if (accessor != NULL) { - accessor->reportError(accessorCookie, "This attribute must be localized."); - } - } - } - - if (*s == '#') { - // It's a color! Convert to an integer of the form 0xaarrggbb. - uint32_t color = 0; - bool error = false; - if (len == 4) { - outValue->dataType = outValue->TYPE_INT_COLOR_RGB4; - color |= 0xFF000000; - color |= get_hex(s[1], &error) << 20; - color |= get_hex(s[1], &error) << 16; - color |= get_hex(s[2], &error) << 12; - color |= get_hex(s[2], &error) << 8; - color |= get_hex(s[3], &error) << 4; - color |= get_hex(s[3], &error); - } else if (len == 5) { - outValue->dataType = outValue->TYPE_INT_COLOR_ARGB4; - color |= get_hex(s[1], &error) << 28; - color |= get_hex(s[1], &error) << 24; - color |= get_hex(s[2], &error) << 20; - color |= get_hex(s[2], &error) << 16; - color |= get_hex(s[3], &error) << 12; - color |= get_hex(s[3], &error) << 8; - color |= get_hex(s[4], &error) << 4; - color |= get_hex(s[4], &error); - } else if (len == 7) { - outValue->dataType = outValue->TYPE_INT_COLOR_RGB8; - color |= 0xFF000000; - color |= get_hex(s[1], &error) << 20; - color |= get_hex(s[2], &error) << 16; - color |= get_hex(s[3], &error) << 12; - color |= get_hex(s[4], &error) << 8; - color |= get_hex(s[5], &error) << 4; - color |= get_hex(s[6], &error); - } else if (len == 9) { - outValue->dataType = outValue->TYPE_INT_COLOR_ARGB8; - color |= get_hex(s[1], &error) << 28; - color |= get_hex(s[2], &error) << 24; - color |= get_hex(s[3], &error) << 20; - color |= get_hex(s[4], &error) << 16; - color |= get_hex(s[5], &error) << 12; - color |= get_hex(s[6], &error) << 8; - color |= get_hex(s[7], &error) << 4; - color |= get_hex(s[8], &error); - } else { - error = true; - } - if (!error) { - if ((attrType&ResTable_map::TYPE_COLOR) == 0) { - if (!canStringCoerce) { - if (accessor != NULL) { - accessor->reportError(accessorCookie, - "Color types not allowed"); - } - return false; - } - } else { - outValue->data = color; - //printf("Color input=%s, output=0x%x\n", String8(s, len).string(), color); - return true; - } - } else { - if ((attrType&ResTable_map::TYPE_COLOR) != 0) { - if (accessor != NULL) { - accessor->reportError(accessorCookie, "Color value not valid --" - " must be #rgb, #argb, #rrggbb, or #aarrggbb"); - } - #if 0 - fprintf(stderr, "%s: Color ID %s value %s is not valid\n", - "Resource File", //(const char*)in->getPrintableSource(), - String8(*curTag).string(), - String8(s, len).string()); - #endif - return false; - } - } - } - - if (*s == '?') { - outValue->dataType = outValue->TYPE_ATTRIBUTE; - - // Note: we don't check attrType here because the reference can - // be to any other type; we just need to count on the client making - // sure the referenced type is correct. - - //printf("Looking up attr: %s\n", String8(s, len).string()); - - static const String16 attr16("attr"); - String16 package, type, name; - if (!expandResourceRef(s+1, len-1, &package, &type, &name, - &attr16, defPackage, &errorMsg)) { - if (accessor != NULL) { - accessor->reportError(accessorCookie, errorMsg); - } - return false; - } - - //printf("Pkg: %s, Type: %s, Name: %s\n", - // String8(package).string(), String8(type).string(), - // String8(name).string()); - uint32_t specFlags = 0; - uint32_t rid = - identifierForName(name.string(), name.size(), - type.string(), type.size(), - package.string(), package.size(), &specFlags); - if (rid != 0) { - if (enforcePrivate) { - if ((specFlags&ResTable_typeSpec::SPEC_PUBLIC) == 0) { - if (accessor != NULL) { - accessor->reportError(accessorCookie, "Attribute is not public."); - } - return false; - } - } - if (!accessor) { - outValue->data = rid; - return true; - } - rid = Res_MAKEID( - accessor->getRemappedPackage(Res_GETPACKAGE(rid)), - Res_GETTYPE(rid), Res_GETENTRY(rid)); - //printf("Incl %s:%s/%s: 0x%08x\n", - // String8(package).string(), String8(type).string(), - // String8(name).string(), rid); - outValue->data = rid; - return true; - } - - if (accessor) { - uint32_t rid = accessor->getCustomResource(package, type, name); - if (rid != 0) { - //printf("Mine %s:%s/%s: 0x%08x\n", - // String8(package).string(), String8(type).string(), - // String8(name).string(), rid); - outValue->data = rid; - return true; - } - } - - if (accessor != NULL) { - accessor->reportError(accessorCookie, "No resource found that matches the given name"); - } - return false; - } - - if (stringToInt(s, len, outValue)) { - if ((attrType&ResTable_map::TYPE_INTEGER) == 0) { - // If this type does not allow integers, but does allow floats, - // fall through on this error case because the float type should - // be able to accept any integer value. - if (!canStringCoerce && (attrType&ResTable_map::TYPE_FLOAT) == 0) { - if (accessor != NULL) { - accessor->reportError(accessorCookie, "Integer types not allowed"); - } - return false; - } - } else { - if (((int32_t)outValue->data) < ((int32_t)attrMin) - || ((int32_t)outValue->data) > ((int32_t)attrMax)) { - if (accessor != NULL) { - accessor->reportError(accessorCookie, "Integer value out of range"); - } - return false; - } - return true; - } - } - - if (stringToFloat(s, len, outValue)) { - if (outValue->dataType == Res_value::TYPE_DIMENSION) { - if ((attrType&ResTable_map::TYPE_DIMENSION) != 0) { - return true; - } - if (!canStringCoerce) { - if (accessor != NULL) { - accessor->reportError(accessorCookie, "Dimension types not allowed"); - } - return false; - } - } else if (outValue->dataType == Res_value::TYPE_FRACTION) { - if ((attrType&ResTable_map::TYPE_FRACTION) != 0) { - return true; - } - if (!canStringCoerce) { - if (accessor != NULL) { - accessor->reportError(accessorCookie, "Fraction types not allowed"); - } - return false; - } - } else if ((attrType&ResTable_map::TYPE_FLOAT) == 0) { - if (!canStringCoerce) { - if (accessor != NULL) { - accessor->reportError(accessorCookie, "Float types not allowed"); - } - return false; - } - } else { - return true; - } - } - - if (len == 4) { - if ((s[0] == 't' || s[0] == 'T') && - (s[1] == 'r' || s[1] == 'R') && - (s[2] == 'u' || s[2] == 'U') && - (s[3] == 'e' || s[3] == 'E')) { - if ((attrType&ResTable_map::TYPE_BOOLEAN) == 0) { - if (!canStringCoerce) { - if (accessor != NULL) { - accessor->reportError(accessorCookie, "Boolean types not allowed"); - } - return false; - } - } else { - outValue->dataType = outValue->TYPE_INT_BOOLEAN; - outValue->data = (uint32_t)-1; - return true; - } - } - } - - if (len == 5) { - if ((s[0] == 'f' || s[0] == 'F') && - (s[1] == 'a' || s[1] == 'A') && - (s[2] == 'l' || s[2] == 'L') && - (s[3] == 's' || s[3] == 'S') && - (s[4] == 'e' || s[4] == 'E')) { - if ((attrType&ResTable_map::TYPE_BOOLEAN) == 0) { - if (!canStringCoerce) { - if (accessor != NULL) { - accessor->reportError(accessorCookie, "Boolean types not allowed"); - } - return false; - } - } else { - outValue->dataType = outValue->TYPE_INT_BOOLEAN; - outValue->data = 0; - return true; - } - } - } - - if ((attrType&ResTable_map::TYPE_ENUM) != 0) { - const ssize_t p = getResourcePackageIndex(attrID); - const bag_entry* bag; - ssize_t cnt = p >= 0 ? lockBag(attrID, &bag) : -1; - //printf("Got %d for enum\n", cnt); - if (cnt >= 0) { - resource_name rname; - while (cnt > 0) { - if (!Res_INTERNALID(bag->map.name.ident)) { - //printf("Trying attr #%08x\n", bag->map.name.ident); - if (getResourceName(bag->map.name.ident, &rname)) { - #if 0 - printf("Matching %s against %s (0x%08x)\n", - String8(s, len).string(), - String8(rname.name, rname.nameLen).string(), - bag->map.name.ident); - #endif - if (strzcmp16(s, len, rname.name, rname.nameLen) == 0) { - outValue->dataType = bag->map.value.dataType; - outValue->data = bag->map.value.data; - unlockBag(bag); - return true; - } - } - - } - bag++; - cnt--; - } - unlockBag(bag); - } - - if (fromAccessor) { - if (accessor->getAttributeEnum(attrID, s, len, outValue)) { - return true; - } - } - } - - if ((attrType&ResTable_map::TYPE_FLAGS) != 0) { - const ssize_t p = getResourcePackageIndex(attrID); - const bag_entry* bag; - ssize_t cnt = p >= 0 ? lockBag(attrID, &bag) : -1; - //printf("Got %d for flags\n", cnt); - if (cnt >= 0) { - bool failed = false; - resource_name rname; - outValue->dataType = Res_value::TYPE_INT_HEX; - outValue->data = 0; - const char16_t* end = s + len; - const char16_t* pos = s; - while (pos < end && !failed) { - const char16_t* start = pos; - end++; - while (pos < end && *pos != '|') { - pos++; - } - //printf("Looking for: %s\n", String8(start, pos-start).string()); - const bag_entry* bagi = bag; - ssize_t i; - for (i=0; imap.name.ident)) { - //printf("Trying attr #%08x\n", bagi->map.name.ident); - if (getResourceName(bagi->map.name.ident, &rname)) { - #if 0 - printf("Matching %s against %s (0x%08x)\n", - String8(start,pos-start).string(), - String8(rname.name, rname.nameLen).string(), - bagi->map.name.ident); - #endif - if (strzcmp16(start, pos-start, rname.name, rname.nameLen) == 0) { - outValue->data |= bagi->map.value.data; - break; - } - } - } - } - if (i >= cnt) { - // Didn't find this flag identifier. - failed = true; - } - if (pos < end) { - pos++; - } - } - unlockBag(bag); - if (!failed) { - //printf("Final flag value: 0x%lx\n", outValue->data); - return true; - } - } - - - if (fromAccessor) { - if (accessor->getAttributeFlags(attrID, s, len, outValue)) { - //printf("Final flag value: 0x%lx\n", outValue->data); - return true; - } - } - } - - if ((attrType&ResTable_map::TYPE_STRING) == 0) { - if (accessor != NULL) { - accessor->reportError(accessorCookie, "String types not allowed"); - } - return false; - } - - // Generic string handling... - outValue->dataType = outValue->TYPE_STRING; - if (outString) { - bool failed = collectString(outString, s, len, preserveSpaces, &errorMsg); - if (accessor != NULL) { - accessor->reportError(accessorCookie, errorMsg); - } - return failed; - } - - return true; -} - -bool ResTable::collectString(String16* outString, - const char16_t* s, size_t len, - bool preserveSpaces, - const char** outErrorMsg, - bool append) -{ - String16 tmp; - - char quoted = 0; - const char16_t* p = s; - while (p < (s+len)) { - while (p < (s+len)) { - const char16_t c = *p; - if (c == '\\') { - break; - } - if (!preserveSpaces) { - if (quoted == 0 && isspace16(c) - && (c != ' ' || isspace16(*(p+1)))) { - break; - } - if (c == '"' && (quoted == 0 || quoted == '"')) { - break; - } - if (c == '\'' && (quoted == 0 || quoted == '\'')) { - break; - } - } - p++; - } - if (p < (s+len)) { - if (p > s) { - tmp.append(String16(s, p-s)); - } - if (!preserveSpaces && (*p == '"' || *p == '\'')) { - if (quoted == 0) { - quoted = *p; - } else { - quoted = 0; - } - p++; - } else if (!preserveSpaces && isspace16(*p)) { - // Space outside of a quote -- consume all spaces and - // leave a single plain space char. - tmp.append(String16(" ")); - p++; - while (p < (s+len) && isspace16(*p)) { - p++; - } - } else if (*p == '\\') { - p++; - if (p < (s+len)) { - switch (*p) { - case 't': - tmp.append(String16("\t")); - break; - case 'n': - tmp.append(String16("\n")); - break; - case '#': - tmp.append(String16("#")); - break; - case '@': - tmp.append(String16("@")); - break; - case '?': - tmp.append(String16("?")); - break; - case '"': - tmp.append(String16("\"")); - break; - case '\'': - tmp.append(String16("'")); - break; - case '\\': - tmp.append(String16("\\")); - break; - case 'u': - { - char16_t chr = 0; - int i = 0; - while (i < 4 && p[1] != 0) { - p++; - i++; - int c; - if (*p >= '0' && *p <= '9') { - c = *p - '0'; - } else if (*p >= 'a' && *p <= 'f') { - c = *p - 'a' + 10; - } else if (*p >= 'A' && *p <= 'F') { - c = *p - 'A' + 10; - } else { - if (outErrorMsg) { - *outErrorMsg = "Bad character in \\u unicode escape sequence"; - } - return false; - } - chr = (chr<<4) | c; - } - tmp.append(String16(&chr, 1)); - } break; - default: - // ignore unknown escape chars. - break; - } - p++; - } - } - len -= (p-s); - s = p; - } - } - - if (tmp.size() != 0) { - if (len > 0) { - tmp.append(String16(s, len)); - } - if (append) { - outString->append(tmp); - } else { - outString->setTo(tmp); - } - } else { - if (append) { - outString->append(String16(s, len)); - } else { - outString->setTo(s, len); - } - } - - return true; -} - -size_t ResTable::getBasePackageCount() const -{ - if (mError != NO_ERROR) { - return 0; - } - return mPackageGroups.size(); -} - -const char16_t* ResTable::getBasePackageName(size_t idx) const -{ - if (mError != NO_ERROR) { - return 0; - } - LOG_FATAL_IF(idx >= mPackageGroups.size(), - "Requested package index %d past package count %d", - (int)idx, (int)mPackageGroups.size()); - return mPackageGroups[idx]->name.string(); -} - -uint32_t ResTable::getBasePackageId(size_t idx) const -{ - if (mError != NO_ERROR) { - return 0; - } - LOG_FATAL_IF(idx >= mPackageGroups.size(), - "Requested package index %d past package count %d", - (int)idx, (int)mPackageGroups.size()); - return mPackageGroups[idx]->id; -} - -size_t ResTable::getTableCount() const -{ - return mHeaders.size(); -} - -const ResStringPool* ResTable::getTableStringBlock(size_t index) const -{ - return &mHeaders[index]->values; -} - -void* ResTable::getTableCookie(size_t index) const -{ - return mHeaders[index]->cookie; -} - -void ResTable::getConfigurations(Vector* configs) const -{ - const size_t I = mPackageGroups.size(); - for (size_t i=0; ipackages.size(); - for (size_t j=0; jpackages[j]; - const size_t K = package->types.size(); - for (size_t k=0; ktypes[k]; - if (type == NULL) continue; - const size_t L = type->configs.size(); - for (size_t l=0; lconfigs[l]; - const ResTable_config* cfg = &config->config; - // only insert unique - const size_t M = configs->size(); - size_t m; - for (m=0; madd(*cfg); - } - } - } - } - } -} - -void ResTable::getLocales(Vector* locales) const -{ - Vector configs; - LOGD("calling getConfigurations"); - getConfigurations(&configs); - LOGD("called getConfigurations size=%d", (int)configs.size()); - const size_t I = configs.size(); - for (size_t i=0; isize(); - size_t j; - for (j=0; jadd(String8(locale)); - } - } -} - -ssize_t ResTable::getEntry( - const Package* package, int typeIndex, int entryIndex, - const ResTable_config* config, - const ResTable_type** outType, const ResTable_entry** outEntry, - const Type** outTypeClass) const -{ - LOGV("Getting entry from package %p\n", package); - const ResTable_package* const pkg = package->package; - - const Type* allTypes = package->getType(typeIndex); - LOGV("allTypes=%p\n", allTypes); - if (allTypes == NULL) { - LOGV("Skipping entry type index 0x%02x because type is NULL!\n", typeIndex); - return 0; - } - - if ((size_t)entryIndex >= allTypes->entryCount) { - LOGW("getEntry failing because entryIndex %d is beyond type entryCount %d", - entryIndex, (int)allTypes->entryCount); - return BAD_TYPE; - } - - const ResTable_type* type = NULL; - uint32_t offset = ResTable_type::NO_ENTRY; - ResTable_config bestConfig; - memset(&bestConfig, 0, sizeof(bestConfig)); // make the compiler shut up - - const size_t NT = allTypes->configs.size(); - for (size_t i=0; iconfigs[i]; - if (thisType == NULL) continue; - - ResTable_config thisConfig; - thisConfig.copyFromDtoH(thisType->config); - - TABLE_GETENTRY(LOGI("Match entry 0x%x in type 0x%x (sz 0x%x): imsi:%d/%d=%d/%d lang:%c%c=%c%c cnt:%c%c=%c%c " - "orien:%d=%d touch:%d=%d density:%d=%d key:%d=%d inp:%d=%d nav:%d=%d w:%d=%d h:%d=%d\n", - entryIndex, typeIndex+1, dtohl(thisType->config.size), - thisConfig.mcc, thisConfig.mnc, - config ? config->mcc : 0, config ? config->mnc : 0, - thisConfig.language[0] ? thisConfig.language[0] : '-', - thisConfig.language[1] ? thisConfig.language[1] : '-', - config && config->language[0] ? config->language[0] : '-', - config && config->language[1] ? config->language[1] : '-', - thisConfig.country[0] ? thisConfig.country[0] : '-', - thisConfig.country[1] ? thisConfig.country[1] : '-', - config && config->country[0] ? config->country[0] : '-', - config && config->country[1] ? config->country[1] : '-', - thisConfig.orientation, - config ? config->orientation : 0, - thisConfig.touchscreen, - config ? config->touchscreen : 0, - thisConfig.density, - config ? config->density : 0, - thisConfig.keyboard, - config ? config->keyboard : 0, - thisConfig.inputFlags, - config ? config->inputFlags : 0, - thisConfig.navigation, - config ? config->navigation : 0, - thisConfig.screenWidth, - config ? config->screenWidth : 0, - thisConfig.screenHeight, - config ? config->screenHeight : 0)); - - // Check to make sure this one is valid for the current parameters. - if (config && !thisConfig.match(*config)) { - TABLE_GETENTRY(LOGI("Does not match config!\n")); - continue; - } - - // Check if there is the desired entry in this type. - - const uint8_t* const end = ((const uint8_t*)thisType) - + dtohl(thisType->header.size); - const uint32_t* const eindex = (const uint32_t*) - (((const uint8_t*)thisType) + dtohs(thisType->header.headerSize)); - - uint32_t thisOffset = dtohl(eindex[entryIndex]); - if (thisOffset == ResTable_type::NO_ENTRY) { - TABLE_GETENTRY(LOGI("Skipping because it is not defined!\n")); - continue; - } - - if (type != NULL) { - // Check if this one is less specific than the last found. If so, - // we will skip it. We check starting with things we most care - // about to those we least care about. - if (!thisConfig.isBetterThan(bestConfig, config)) { - TABLE_GETENTRY(LOGI("This config is worse than last!\n")); - continue; - } - } - - type = thisType; - offset = thisOffset; - bestConfig = thisConfig; - TABLE_GETENTRY(LOGI("Best entry so far -- using it!\n")); - if (!config) break; - } - - if (type == NULL) { - TABLE_GETENTRY(LOGI("No value found for requested entry!\n")); - return BAD_INDEX; - } - - offset += dtohl(type->entriesStart); - TABLE_NOISY(aout << "Looking in resource table " << package->header->header - << ", typeOff=" - << (void*)(((const char*)type)-((const char*)package->header->header)) - << ", offset=" << (void*)offset << endl); - - if (offset > (dtohl(type->header.size)-sizeof(ResTable_entry))) { - LOGW("ResTable_entry at 0x%x is beyond type chunk data 0x%x", - offset, dtohl(type->header.size)); - return BAD_TYPE; - } - if ((offset&0x3) != 0) { - LOGW("ResTable_entry at 0x%x is not on an integer boundary", - offset); - return BAD_TYPE; - } - - const ResTable_entry* const entry = (const ResTable_entry*) - (((const uint8_t*)type) + offset); - if (dtohs(entry->size) < sizeof(*entry)) { - LOGW("ResTable_entry size 0x%x is too small", dtohs(entry->size)); - return BAD_TYPE; - } - - *outType = type; - *outEntry = entry; - if (outTypeClass != NULL) { - *outTypeClass = allTypes; - } - return offset + dtohs(entry->size); -} - -status_t ResTable::parsePackage(const ResTable_package* const pkg, - const Header* const header) -{ - const uint8_t* base = (const uint8_t*)pkg; - status_t err = validate_chunk(&pkg->header, sizeof(*pkg), - header->dataEnd, "ResTable_package"); - if (err != NO_ERROR) { - return (mError=err); - } - - const size_t pkgSize = dtohl(pkg->header.size); - - if (dtohl(pkg->typeStrings) >= pkgSize) { - LOGW("ResTable_package type strings at %p are past chunk size %p.", - (void*)dtohl(pkg->typeStrings), (void*)pkgSize); - return (mError=BAD_TYPE); - } - if ((dtohl(pkg->typeStrings)&0x3) != 0) { - LOGW("ResTable_package type strings at %p is not on an integer boundary.", - (void*)dtohl(pkg->typeStrings)); - return (mError=BAD_TYPE); - } - if (dtohl(pkg->keyStrings) >= pkgSize) { - LOGW("ResTable_package key strings at %p are past chunk size %p.", - (void*)dtohl(pkg->keyStrings), (void*)pkgSize); - return (mError=BAD_TYPE); - } - if ((dtohl(pkg->keyStrings)&0x3) != 0) { - LOGW("ResTable_package key strings at %p is not on an integer boundary.", - (void*)dtohl(pkg->keyStrings)); - return (mError=BAD_TYPE); - } - - Package* package = NULL; - PackageGroup* group = NULL; - uint32_t id = dtohl(pkg->id); - if (id != 0 && id < 256) { - size_t idx = mPackageMap[id]; - if (idx == 0) { - idx = mPackageGroups.size()+1; - - char16_t tmpName[sizeof(pkg->name)/sizeof(char16_t)]; - strcpy16_dtoh(tmpName, pkg->name, sizeof(pkg->name)/sizeof(char16_t)); - group = new PackageGroup(String16(tmpName), id); - if (group == NULL) { - return (mError=NO_MEMORY); - } - - err = group->typeStrings.setTo(base+dtohl(pkg->typeStrings), - header->dataEnd-(base+dtohl(pkg->typeStrings))); - if (err != NO_ERROR) { - return (mError=err); - } - err = group->keyStrings.setTo(base+dtohl(pkg->keyStrings), - header->dataEnd-(base+dtohl(pkg->keyStrings))); - if (err != NO_ERROR) { - return (mError=err); - } - - //printf("Adding new package id %d at index %d\n", id, idx); - err = mPackageGroups.add(group); - if (err < NO_ERROR) { - return (mError=err); - } - mPackageMap[id] = (uint8_t)idx; - } else { - group = mPackageGroups.itemAt(idx-1); - if (group == NULL) { - return (mError=UNKNOWN_ERROR); - } - } - package = new Package(header, pkg); - if (package == NULL) { - return (mError=NO_MEMORY); - } - err = group->packages.add(package); - if (err < NO_ERROR) { - return (mError=err); - } - } else { - LOG_ALWAYS_FATAL("Skins not supported!"); - return NO_ERROR; - } - - - // Iterate through all chunks. - size_t curPackage = 0; - - const ResChunk_header* chunk = - (const ResChunk_header*)(((const uint8_t*)pkg) - + dtohs(pkg->header.headerSize)); - const uint8_t* endPos = ((const uint8_t*)pkg) + dtohs(pkg->header.size); - while (((const uint8_t*)chunk) <= (endPos-sizeof(ResChunk_header)) && - ((const uint8_t*)chunk) <= (endPos-dtohl(chunk->size))) { - TABLE_NOISY(LOGV("PackageChunk: type=0x%x, headerSize=0x%x, size=0x%x, pos=%p\n", - dtohs(chunk->type), dtohs(chunk->headerSize), dtohl(chunk->size), - (void*)(((const uint8_t*)chunk) - ((const uint8_t*)header->header)))); - const size_t csize = dtohl(chunk->size); - const uint16_t ctype = dtohs(chunk->type); - if (ctype == RES_TABLE_TYPE_SPEC_TYPE) { - const ResTable_typeSpec* typeSpec = (const ResTable_typeSpec*)(chunk); - err = validate_chunk(&typeSpec->header, sizeof(*typeSpec), - endPos, "ResTable_typeSpec"); - if (err != NO_ERROR) { - return (mError=err); - } - - const size_t typeSpecSize = dtohl(typeSpec->header.size); - - LOAD_TABLE_NOISY(printf("TypeSpec off %p: type=0x%x, headerSize=0x%x, size=%p\n", - (void*)(base-(const uint8_t*)chunk), - dtohs(typeSpec->header.type), - dtohs(typeSpec->header.headerSize), - (void*)typeSize)); - // look for block overrun or int overflow when multiplying by 4 - if ((dtohl(typeSpec->entryCount) > (INT32_MAX/sizeof(uint32_t)) - || dtohs(typeSpec->header.headerSize)+(sizeof(uint32_t)*dtohl(typeSpec->entryCount)) - > typeSpecSize)) { - LOGW("ResTable_typeSpec entry index to %p extends beyond chunk end %p.", - (void*)(dtohs(typeSpec->header.headerSize) - +(sizeof(uint32_t)*dtohl(typeSpec->entryCount))), - (void*)typeSpecSize); - return (mError=BAD_TYPE); - } - - if (typeSpec->id == 0) { - LOGW("ResTable_type has an id of 0."); - return (mError=BAD_TYPE); - } - - while (package->types.size() < typeSpec->id) { - package->types.add(NULL); - } - Type* t = package->types[typeSpec->id-1]; - if (t == NULL) { - t = new Type(header, package, dtohl(typeSpec->entryCount)); - package->types.editItemAt(typeSpec->id-1) = t; - } else if (dtohl(typeSpec->entryCount) != t->entryCount) { - LOGW("ResTable_typeSpec entry count inconsistent: given %d, previously %d", - (int)dtohl(typeSpec->entryCount), (int)t->entryCount); - return (mError=BAD_TYPE); - } - t->typeSpecFlags = (const uint32_t*)( - ((const uint8_t*)typeSpec) + dtohs(typeSpec->header.headerSize)); - t->typeSpec = typeSpec; - - } else if (ctype == RES_TABLE_TYPE_TYPE) { - const ResTable_type* type = (const ResTable_type*)(chunk); - err = validate_chunk(&type->header, sizeof(*type)-sizeof(ResTable_config)+4, - endPos, "ResTable_type"); - if (err != NO_ERROR) { - return (mError=err); - } - - const size_t typeSize = dtohl(type->header.size); - - LOAD_TABLE_NOISY(printf("Type off %p: type=0x%x, headerSize=0x%x, size=%p\n", - (void*)(base-(const uint8_t*)chunk), - dtohs(type->header.type), - dtohs(type->header.headerSize), - (void*)typeSize)); - if (dtohs(type->header.headerSize)+(sizeof(uint32_t)*dtohl(type->entryCount)) - > typeSize) { - LOGW("ResTable_type entry index to %p extends beyond chunk end %p.", - (void*)(dtohs(type->header.headerSize) - +(sizeof(uint32_t)*dtohl(type->entryCount))), - (void*)typeSize); - return (mError=BAD_TYPE); - } - if (dtohl(type->entryCount) != 0 - && dtohl(type->entriesStart) > (typeSize-sizeof(ResTable_entry))) { - LOGW("ResTable_type entriesStart at %p extends beyond chunk end %p.", - (void*)dtohl(type->entriesStart), (void*)typeSize); - return (mError=BAD_TYPE); - } - if (type->id == 0) { - LOGW("ResTable_type has an id of 0."); - return (mError=BAD_TYPE); - } - - while (package->types.size() < type->id) { - package->types.add(NULL); - } - Type* t = package->types[type->id-1]; - if (t == NULL) { - t = new Type(header, package, dtohl(type->entryCount)); - package->types.editItemAt(type->id-1) = t; - } else if (dtohl(type->entryCount) != t->entryCount) { - LOGW("ResTable_type entry count inconsistent: given %d, previously %d", - (int)dtohl(type->entryCount), (int)t->entryCount); - return (mError=BAD_TYPE); - } - - TABLE_GETENTRY( - ResTable_config thisConfig; - thisConfig.copyFromDtoH(type->config); - LOGI("Adding config to type %d: imsi:%d/%d lang:%c%c cnt:%c%c " - "orien:%d touch:%d density:%d key:%d inp:%d nav:%d w:%d h:%d\n", - type->id, - thisConfig.mcc, thisConfig.mnc, - thisConfig.language[0] ? thisConfig.language[0] : '-', - thisConfig.language[1] ? thisConfig.language[1] : '-', - thisConfig.country[0] ? thisConfig.country[0] : '-', - thisConfig.country[1] ? thisConfig.country[1] : '-', - thisConfig.orientation, - thisConfig.touchscreen, - thisConfig.density, - thisConfig.keyboard, - thisConfig.inputFlags, - thisConfig.navigation, - thisConfig.screenWidth, - thisConfig.screenHeight)); - t->configs.add(type); - } else { - status_t err = validate_chunk(chunk, sizeof(ResChunk_header), - endPos, "ResTable_package:unknown"); - if (err != NO_ERROR) { - return (mError=err); - } - } - chunk = (const ResChunk_header*) - (((const uint8_t*)chunk) + csize); - } - - if (group->typeCount == 0) { - group->typeCount = package->types.size(); - } - - return NO_ERROR; -} - -#ifndef HAVE_ANDROID_OS -#define CHAR16_TO_CSTR(c16, len) (String8(String16(c16,len)).string()) - -#define CHAR16_ARRAY_EQ(constant, var, len) \ - ((len == (sizeof(constant)/sizeof(constant[0]))) && (0 == memcmp((var), (constant), (len)))) - -void ResTable::print() const -{ - printf("mError=0x%x (%s)\n", mError, strerror(mError)); -#if 0 - printf("mParams=%c%c-%c%c,\n", - mParams.language[0], mParams.language[1], - mParams.country[0], mParams.country[1]); -#endif - size_t pgCount = mPackageGroups.size(); - printf("Package Groups (%d)\n", (int)pgCount); - for (size_t pgIndex=0; pgIndexid, (int)pg->packages.size(), - String8(pg->name).string()); - - size_t pkgCount = pg->packages.size(); - for (size_t pkgIndex=0; pkgIndexpackages[pkgIndex]; - size_t typeCount = pkg->types.size(); - printf(" Package %d id=%d name=%s typeCount=%d\n", (int)pkgIndex, - pkg->package->id, String8(String16(pkg->package->name)).string(), - (int)typeCount); - for (size_t typeIndex=0; typeIndexgetType(typeIndex); - if (typeConfigs == NULL) { - printf(" type %d NULL\n", (int)typeIndex); - continue; - } - const size_t NTC = typeConfigs->configs.size(); - printf(" type %d configCount=%d entryCount=%d\n", - (int)typeIndex, (int)NTC, (int)typeConfigs->entryCount); - if (typeConfigs->typeSpecFlags != NULL) { - for (size_t entryIndex=0; entryIndexentryCount; entryIndex++) { - uint32_t resID = (0xff000000 & ((pkg->package->id)<<24)) - | (0x00ff0000 & ((typeIndex+1)<<16)) - | (0x0000ffff & (entryIndex)); - resource_name resName; - this->getResourceName(resID, &resName); - printf(" spec resource 0x%08x %s:%s/%s: flags=0x%08x\n", - resID, - CHAR16_TO_CSTR(resName.package, resName.packageLen), - CHAR16_TO_CSTR(resName.type, resName.typeLen), - CHAR16_TO_CSTR(resName.name, resName.nameLen), - dtohl(typeConfigs->typeSpecFlags[entryIndex])); - } - } - for (size_t configIndex=0; configIndexconfigs[configIndex]; - if ((((uint64_t)type)&0x3) != 0) { - printf(" NON-INTEGER ResTable_type ADDRESS: %p\n", type); - continue; - } - printf(" config %d lang=%c%c cnt=%c%c orien=%d touch=%d density=%d key=%d infl=%d nav=%d w=%d h=%d\n", - (int)configIndex, - type->config.language[0] ? type->config.language[0] : '-', - type->config.language[1] ? type->config.language[1] : '-', - type->config.country[0] ? type->config.country[0] : '-', - type->config.country[1] ? type->config.country[1] : '-', - type->config.orientation, - type->config.touchscreen, - dtohs(type->config.density), - type->config.keyboard, - type->config.inputFlags, - type->config.navigation, - dtohs(type->config.screenWidth), - dtohs(type->config.screenHeight)); - size_t entryCount = dtohl(type->entryCount); - uint32_t entriesStart = dtohl(type->entriesStart); - if ((entriesStart&0x3) != 0) { - printf(" NON-INTEGER ResTable_type entriesStart OFFSET: %p\n", (void*)entriesStart); - continue; - } - uint32_t typeSize = dtohl(type->header.size); - if ((typeSize&0x3) != 0) { - printf(" NON-INTEGER ResTable_type header.size: %p\n", (void*)typeSize); - continue; - } - for (size_t entryIndex=0; entryIndexheader.size); - const uint32_t* const eindex = (const uint32_t*) - (((const uint8_t*)type) + dtohs(type->header.headerSize)); - - uint32_t thisOffset = dtohl(eindex[entryIndex]); - if (thisOffset == ResTable_type::NO_ENTRY) { - continue; - } - - uint32_t resID = (0xff000000 & ((pkg->package->id)<<24)) - | (0x00ff0000 & ((typeIndex+1)<<16)) - | (0x0000ffff & (entryIndex)); - resource_name resName; - this->getResourceName(resID, &resName); - printf(" resource 0x%08x %s:%s/%s: ", resID, - CHAR16_TO_CSTR(resName.package, resName.packageLen), - CHAR16_TO_CSTR(resName.type, resName.typeLen), - CHAR16_TO_CSTR(resName.name, resName.nameLen)); - if ((thisOffset&0x3) != 0) { - printf("NON-INTEGER OFFSET: %p\n", (void*)thisOffset); - continue; - } - if ((thisOffset+sizeof(ResTable_entry)) > typeSize) { - printf("OFFSET OUT OF BOUNDS: %p+%p (size is %p)\n", - (void*)entriesStart, (void*)thisOffset, - (void*)typeSize); - continue; - } - - const ResTable_entry* ent = (const ResTable_entry*) - (((const uint8_t*)type) + entriesStart + thisOffset); - if (((entriesStart + thisOffset)&0x3) != 0) { - printf("NON-INTEGER ResTable_entry OFFSET: %p\n", - (void*)(entriesStart + thisOffset)); - continue; - } - if ((dtohs(ent->flags)&ResTable_entry::FLAG_COMPLEX) != 0) { - printf(""); - } 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; - } - - const Res_value* value = (const Res_value*) - (((const uint8_t*)ent) + esize); - 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); - } - - if ((dtohs(ent->flags)&ResTable_entry::FLAG_PUBLIC) != 0) { - printf(" (PUBLIC)"); - } - printf("\n"); - } - } - } - } - } -} - -#endif // HAVE_ANDROID_OS - -} // namespace android diff --git a/libs/utils/SharedBuffer.cpp b/libs/utils/SharedBuffer.cpp deleted file mode 100644 index 3555fb712..000000000 --- a/libs/utils/SharedBuffer.cpp +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (C) 2005 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 -#include - -#include -#include - -// --------------------------------------------------------------------------- - -namespace android { - -SharedBuffer* SharedBuffer::alloc(size_t size) -{ - SharedBuffer* sb = static_cast(malloc(sizeof(SharedBuffer) + size)); - if (sb) { - sb->mRefs = 1; - sb->mSize = size; - } - return sb; -} - - -ssize_t SharedBuffer::dealloc(const SharedBuffer* released) -{ - if (released->mRefs != 0) return -1; // XXX: invalid operation - free(const_cast(released)); - return 0; -} - -SharedBuffer* SharedBuffer::edit() const -{ - if (onlyOwner()) { - return const_cast(this); - } - SharedBuffer* sb = alloc(mSize); - if (sb) { - memcpy(sb->data(), data(), size()); - release(); - } - return sb; -} - -SharedBuffer* SharedBuffer::editResize(size_t newSize) const -{ - if (onlyOwner()) { - SharedBuffer* buf = const_cast(this); - if (buf->mSize == newSize) return buf; - buf = (SharedBuffer*)realloc(buf, sizeof(SharedBuffer) + newSize); - if (buf != NULL) { - buf->mSize = newSize; - return buf; - } - } - SharedBuffer* sb = alloc(newSize); - if (sb) { - const size_t mySize = mSize; - memcpy(sb->data(), data(), newSize < mySize ? newSize : mySize); - release(); - } - return sb; -} - -SharedBuffer* SharedBuffer::attemptEdit() const -{ - if (onlyOwner()) { - return const_cast(this); - } - return 0; -} - -SharedBuffer* SharedBuffer::reset(size_t new_size) const -{ - // cheap-o-reset. - SharedBuffer* sb = alloc(new_size); - if (sb) { - release(); - } - return sb; -} - -void SharedBuffer::acquire() const { - android_atomic_inc(&mRefs); -} - -int32_t SharedBuffer::release(uint32_t flags) const -{ - int32_t prev = 1; - if (onlyOwner() || ((prev = android_atomic_dec(&mRefs)) == 1)) { - mRefs = 0; - if ((flags & eKeepStorage) == 0) { - free(const_cast(this)); - } - } - return prev; -} - - -}; // namespace android diff --git a/libs/utils/Socket.cpp b/libs/utils/Socket.cpp deleted file mode 100644 index 51509a304..000000000 --- a/libs/utils/Socket.cpp +++ /dev/null @@ -1,388 +0,0 @@ -/* - * Copyright (C) 2005 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. - */ - -// -// Internet address class. -// - -#ifdef HAVE_WINSOCK -// This needs to come first, or Cygwin gets concerned about a potential -// clash between WinSock and . -# include -#endif - -#include -#include -#include -#include - -#ifndef HAVE_WINSOCK -# include -# include -# include -# include -#endif - -#include -#include -#include -#include -#include -#include - -using namespace android; - - -/* - * =========================================================================== - * Socket - * =========================================================================== - */ - -#ifndef INVALID_SOCKET -# define INVALID_SOCKET (-1) -#endif -#define UNDEF_SOCKET ((unsigned long) INVALID_SOCKET) - -/*static*/ bool Socket::mBootInitialized = false; - -/* - * Extract system-dependent error code. - */ -static inline int getSocketError(void) { -#ifdef HAVE_WINSOCK - return WSAGetLastError(); -#else - return errno; -#endif -} - -/* - * One-time initialization for socket code. - */ -/*static*/ bool Socket::bootInit(void) -{ -#ifdef HAVE_WINSOCK - WSADATA wsaData; - int err; - - err = WSAStartup(MAKEWORD(2, 0), &wsaData); - if (err != 0) { - LOG(LOG_ERROR, "socket", "Unable to start WinSock\n"); - return false; - } - - LOG(LOG_INFO, "socket", "Using WinSock v%d.%d\n", - LOBYTE(wsaData.wVersion), HIBYTE(wsaData.wVersion)); -#endif - - mBootInitialized = true; - return true; -} - -/* - * One-time shutdown for socket code. - */ -/*static*/ void Socket::finalShutdown(void) -{ -#ifdef HAVE_WINSOCK - WSACleanup(); -#endif - mBootInitialized = false; -} - - -/* - * Simple constructor. Allow the application to create us and then make - * bind/connect calls. - */ -Socket::Socket(void) - : mSock(UNDEF_SOCKET) -{ - if (!mBootInitialized) - LOG(LOG_WARN, "socket", "WARNING: sockets not initialized\n"); -} - -/* - * Destructor. Closes the socket and resets our storage. - */ -Socket::~Socket(void) -{ - close(); -} - - -/* - * Create a socket and connect to the specified host and port. - */ -int Socket::connect(const char* host, int port) -{ - if (mSock != UNDEF_SOCKET) { - LOG(LOG_WARN, "socket", "Socket already connected\n"); - return -1; - } - - InetSocketAddress sockAddr; - if (!sockAddr.create(host, port)) - return -1; - - //return doConnect(sockAddr); - int foo; - foo = doConnect(sockAddr); - return foo; -} - -/* - * Create a socket and connect to the specified host and port. - */ -int Socket::connect(const InetAddress* addr, int port) -{ - if (mSock != UNDEF_SOCKET) { - LOG(LOG_WARN, "socket", "Socket already connected\n"); - return -1; - } - - InetSocketAddress sockAddr; - if (!sockAddr.create(addr, port)) - return -1; - - return doConnect(sockAddr); -} - -/* - * Finish creating a socket by connecting to the remote host. - * - * Returns 0 on success. - */ -int Socket::doConnect(const InetSocketAddress& sockAddr) -{ -#ifdef HAVE_WINSOCK - SOCKET sock; -#else - int sock; -#endif - const InetAddress* addr = sockAddr.getAddress(); - int port = sockAddr.getPort(); - struct sockaddr_in inaddr; - DurationTimer connectTimer; - - assert(sizeof(struct sockaddr_in) == addr->getAddressLength()); - memcpy(&inaddr, addr->getAddress(), addr->getAddressLength()); - inaddr.sin_port = htons(port); - - //fprintf(stderr, "--- connecting to %s:%d\n", - // sockAddr.getHostName(), port); - - sock = ::socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); - if (sock == INVALID_SOCKET) { - int err = getSocketError(); - LOG(LOG_ERROR, "socket", "Unable to create socket (err=%d)\n", err); - return (err != 0) ? err : -1; - } - - connectTimer.start(); - - if (::connect(sock, (struct sockaddr*) &inaddr, sizeof(inaddr)) != 0) { - int err = getSocketError(); - LOG(LOG_WARN, "socket", "Connect to %s:%d failed: %d\n", - sockAddr.getHostName(), port, err); - return (err != 0) ? err : -1; - } - - connectTimer.stop(); - if ((long) connectTimer.durationUsecs() > 100000) { - LOG(LOG_INFO, "socket", - "Connect to %s:%d took %.3fs\n", sockAddr.getHostName(), - port, ((long) connectTimer.durationUsecs()) / 1000000.0); - } - - mSock = (unsigned long) sock; - LOG(LOG_VERBOSE, "socket", - "--- connected to %s:%d\n", sockAddr.getHostName(), port); - return 0; -} - - -/* - * Close the socket if it needs closing. - */ -bool Socket::close(void) -{ - if (mSock != UNDEF_SOCKET) { - //fprintf(stderr, "--- closing socket %lu\n", mSock); -#ifdef HAVE_WINSOCK - if (::closesocket((SOCKET) mSock) != 0) - return false; -#else - if (::close((int) mSock) != 0) - return false; -#endif - } - - mSock = UNDEF_SOCKET; - - return true; -} - -/* - * Read data from socket. - * - * Standard semantics: read up to "len" bytes into "buf". Returns the - * number of bytes read, or less than zero on error. - */ -int Socket::read(void* buf, ssize_t len) const -{ - if (mSock == UNDEF_SOCKET) { - LOG(LOG_ERROR, "socket", "ERROR: read on invalid socket\n"); - return -500; - } - -#ifdef HAVE_WINSOCK - SOCKET sock = (SOCKET) mSock; -#else - int sock = (int) mSock; -#endif - int cc; - - cc = recv(sock, (char*)buf, len, 0); - if (cc < 0) { - int err = getSocketError(); - return (err > 0) ? -err : -1; - } - - return cc; -} - -/* - * Write data to a socket. - * - * Standard semantics: write up to "len" bytes into "buf". Returns the - * number of bytes written, or less than zero on error. - */ -int Socket::write(const void* buf, ssize_t len) const -{ - if (mSock == UNDEF_SOCKET) { - LOG(LOG_ERROR, "socket", "ERROR: write on invalid socket\n"); - return -500; - } - -#ifdef HAVE_WINSOCK - SOCKET sock = (SOCKET) mSock; -#else - int sock = (int) mSock; -#endif - int cc; - - cc = send(sock, (const char*)buf, len, 0); - if (cc < 0) { - int err = getSocketError(); - return (err > 0) ? -err : -1; - } - - return cc; -} - - -/* - * =========================================================================== - * Socket tests - * =========================================================================== - */ - -/* - * Read all data from the socket. The data is read into a buffer that - * expands as needed. - * - * On exit, the buffer is returned, and the length of the data is stored - * in "*sz". A null byte is added to the end, but is not included in - * the length. - */ -static char* socketReadAll(const Socket& s, int *sz) -{ - int max, r; - char *data, *ptr, *tmp; - - data = (char*) malloc(max = 32768); - if (data == NULL) - return NULL; - - ptr = data; - - for (;;) { - if ((ptr - data) == max) { - tmp = (char*) realloc(data, max *= 2); - if(tmp == 0) { - free(data); - return 0; - } - } - r = s.read(ptr, max - (ptr - data)); - if (r == 0) - break; - if (r < 0) { - LOG(LOG_WARN, "socket", "WARNING: socket read failed (res=%d)\n",r); - break; - } - ptr += r; - } - - if ((ptr - data) == max) { - tmp = (char*) realloc(data, max + 1); - if (tmp == NULL) { - free(data); - return NULL; - } - } - *ptr = '\0'; - *sz = (ptr - data); - return data; -} - -/* - * Exercise the Socket class. - */ -void android::TestSockets(void) -{ - printf("----- SOCKET TEST ------\n"); - Socket::bootInit(); - - char* buf = NULL; - int len, cc; - const char* kTestStr = - "GET / HTTP/1.0\n" - "Connection: close\n" - "\n"; - - Socket sock; - if (sock.connect("www.google.com", 80) != 0) { - fprintf(stderr, "socket connected failed\n"); - goto bail; - } - - cc = sock.write(kTestStr, strlen(kTestStr)); - if (cc != (int) strlen(kTestStr)) { - fprintf(stderr, "write failed, res=%d\n", cc); - goto bail; - } - buf = socketReadAll(sock, &len); - - printf("GOT '%s'\n", buf); - -bail: - sock.close(); - free(buf); -} - diff --git a/libs/utils/Static.cpp b/libs/utils/Static.cpp deleted file mode 100644 index 93f7e4f0c..000000000 --- a/libs/utils/Static.cpp +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// All static variables go here, to control initialization and -// destruction order in the library. - -#include - -#include -#include -#include - -namespace android { - -class LibUtilsFirstStatics -{ -public: - LibUtilsFirstStatics() - { - initialize_string8(); - initialize_string16(); - } - - ~LibUtilsFirstStatics() - { - terminate_string16(); - terminate_string8(); - } -}; - -static LibUtilsFirstStatics gFirstStatics; -int gDarwinCantLoadAllObjects = 1; - -// ------------ Text output streams - -Vector gTextBuffers; - -class LogTextOutput : public BufferedTextOutput -{ -public: - LogTextOutput() : BufferedTextOutput(MULTITHREADED) { } - virtual ~LogTextOutput() { }; - -protected: - virtual status_t writeLines(const struct iovec& vec, size_t N) - { - android_writevLog(&vec, N); - return NO_ERROR; - } -}; - -class FdTextOutput : public BufferedTextOutput -{ -public: - FdTextOutput(int fd) : BufferedTextOutput(MULTITHREADED), mFD(fd) { } - virtual ~FdTextOutput() { }; - -protected: - virtual status_t writeLines(const struct iovec& vec, size_t N) - { - writev(mFD, &vec, N); - return NO_ERROR; - } - -private: - int mFD; -}; - -static LogTextOutput gLogTextOutput; -static FdTextOutput gStdoutTextOutput(STDOUT_FILENO); -static FdTextOutput gStderrTextOutput(STDERR_FILENO); - -TextOutput& alog(gLogTextOutput); -TextOutput& aout(gStdoutTextOutput); -TextOutput& aerr(gStderrTextOutput); - -#ifndef LIBUTILS_NATIVE - -// ------------ ProcessState.cpp - -Mutex gProcessMutex; -sp gProcess; - -class LibUtilsIPCtStatics -{ -public: - LibUtilsIPCtStatics() - { - } - - ~LibUtilsIPCtStatics() - { - IPCThreadState::shutdown(); - } -}; - -static LibUtilsIPCtStatics gIPCStatics; - -// ------------ ServiceManager.cpp - -Mutex gDefaultServiceManagerLock; -sp gDefaultServiceManager; -sp gPermissionController; - -#endif - -} // namespace android diff --git a/libs/utils/StopWatch.cpp b/libs/utils/StopWatch.cpp deleted file mode 100644 index 68a1c5217..000000000 --- a/libs/utils/StopWatch.cpp +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (C) 2005 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 "StopWatch" - -#include -#include -#include - -#include -#include -#include - -/*****************************************************************************/ - -namespace android { - - -StopWatch::StopWatch(const char *name, int clock, uint32_t flags) - : mName(name), mClock(clock), mFlags(flags), - mStartTime(0), mNumLaps(0) -{ - mStartTime = systemTime(mClock); -} - -StopWatch::~StopWatch() -{ - nsecs_t elapsed = elapsedTime(); - const int n = mNumLaps; - LOGD("StopWatch %s (us): %lld ", mName, ns2us(elapsed)); - for (int i=0 ; i= 8) { - elapsed = 0; - } else { - const int n = mNumLaps; - mLaps[n].soFar = elapsed; - mLaps[n].thisLap = n ? (elapsed - mLaps[n-1].soFar) : elapsed; - mNumLaps = n+1; - } - return elapsed; -} - -nsecs_t StopWatch::elapsedTime() const -{ - return systemTime(mClock) - mStartTime; -} - - -/*****************************************************************************/ - -}; // namespace android - diff --git a/libs/utils/String16.cpp b/libs/utils/String16.cpp deleted file mode 100644 index 1f81cadb7..000000000 --- a/libs/utils/String16.cpp +++ /dev/null @@ -1,609 +0,0 @@ -/* - * Copyright (C) 2005 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 - -#include -#include -#include -#include -#include - -#include - -#ifdef HAVE_WINSOCK -# undef nhtol -# undef htonl -# undef nhtos -# undef htons - -# ifdef HAVE_LITTLE_ENDIAN -# define ntohl(x) ( ((x) << 24) | (((x) >> 24) & 255) | (((x) << 8) & 0xff0000) | (((x) >> 8) & 0xff00) ) -# define htonl(x) ntohl(x) -# define ntohs(x) ( (((x) << 8) & 0xff00) | (((x) >> 8) & 255) ) -# define htons(x) ntohs(x) -# else -# define ntohl(x) (x) -# define htonl(x) (x) -# define ntohs(x) (x) -# define htons(x) (x) -# endif -#else -# include -#endif - -#include -#include -#include - -// --------------------------------------------------------------------------- - -int strcmp16(const char16_t *s1, const char16_t *s2) -{ - char16_t ch; - int d = 0; - - while ( 1 ) { - d = (int)(ch = *s1++) - (int)*s2++; - if ( d || !ch ) - break; - } - - return d; -} - -int strncmp16(const char16_t *s1, const char16_t *s2, size_t n) -{ - char16_t ch; - int d = 0; - - while ( n-- ) { - d = (int)(ch = *s1++) - (int)*s2++; - if ( d || !ch ) - break; - } - - return d; -} - -char16_t *strcpy16(char16_t *dst, const char16_t *src) -{ - char16_t *q = dst; - const char16_t *p = src; - char16_t ch; - - do { - *q++ = ch = *p++; - } while ( ch ); - - return dst; -} - -size_t strlen16(const char16_t *s) -{ - const char16_t *ss = s; - while ( *ss ) - ss++; - return ss-s; -} - - -char16_t *strncpy16(char16_t *dst, const char16_t *src, size_t n) -{ - char16_t *q = dst; - const char16_t *p = src; - char ch; - - while (n) { - n--; - *q++ = ch = *p++; - if ( !ch ) - break; - } - - *q = 0; - - return dst; -} - -size_t strnlen16(const char16_t *s, size_t maxlen) -{ - const char16_t *ss = s; - - /* Important: the maxlen test must precede the reference through ss; - since the byte beyond the maximum may segfault */ - while ((maxlen > 0) && *ss) { - ss++; - maxlen--; - } - return ss-s; -} - -int strzcmp16(const char16_t *s1, size_t n1, const char16_t *s2, size_t n2) -{ - const char16_t* e1 = s1+n1; - const char16_t* e2 = s2+n2; - - while (s1 < e1 && s2 < e2) { - const int d = (int)*s1++ - (int)*s2++; - if (d) { - return d; - } - } - - return n1 < n2 - ? (0 - (int)*s2) - : (n1 > n2 - ? ((int)*s1 - 0) - : 0); -} - -int strzcmp16_h_n(const char16_t *s1H, size_t n1, const char16_t *s2N, size_t n2) -{ - const char16_t* e1 = s1H+n1; - const char16_t* e2 = s2N+n2; - - while (s1H < e1 && s2N < e2) { - const char16_t c2 = ntohs(*s2N); - const int d = (int)*s1H++ - (int)c2; - s2N++; - if (d) { - return d; - } - } - - return n1 < n2 - ? (0 - (int)ntohs(*s2N)) - : (n1 > n2 - ? ((int)*s1H - 0) - : 0); -} - -// --------------------------------------------------------------------------- - -namespace android { - -static inline size_t -utf8_char_len(uint8_t ch) -{ - return ((0xe5000000 >> ((ch >> 3) & 0x1e)) & 3) + 1; -} - -#define UTF8_SHIFT_AND_MASK(unicode, byte) (unicode)<<=6; (unicode) |= (0x3f & (byte)); - -static inline uint32_t -utf8_to_utf32(const uint8_t *src, size_t length) -{ - uint32_t unicode; - - switch (length) - { - case 1: - return src[0]; - case 2: - unicode = src[0] & 0x1f; - UTF8_SHIFT_AND_MASK(unicode, src[1]) - return unicode; - case 3: - unicode = src[0] & 0x0f; - UTF8_SHIFT_AND_MASK(unicode, src[1]) - UTF8_SHIFT_AND_MASK(unicode, src[2]) - return unicode; - case 4: - unicode = src[0] & 0x07; - UTF8_SHIFT_AND_MASK(unicode, src[1]) - UTF8_SHIFT_AND_MASK(unicode, src[2]) - UTF8_SHIFT_AND_MASK(unicode, src[3]) - return unicode; - default: - return 0xffff; - } - - //printf("Char at %p: len=%d, utf-16=%p\n", src, length, (void*)result); -} - -// --------------------------------------------------------------------------- - -static SharedBuffer* gEmptyStringBuf = NULL; -static char16_t* gEmptyString = NULL; - -static inline char16_t* getEmptyString() -{ - gEmptyStringBuf->acquire(); - return gEmptyString; -} - -void initialize_string16() -{ - SharedBuffer* buf = SharedBuffer::alloc(sizeof(char16_t)); - char16_t* str = (char16_t*)buf->data(); - *str = 0; - gEmptyStringBuf = buf; - gEmptyString = str; -} - -void terminate_string16() -{ - SharedBuffer::bufferFromData(gEmptyString)->release(); - gEmptyStringBuf = NULL; - gEmptyString = NULL; -} - -// --------------------------------------------------------------------------- - -// Note: not dealing with generating surrogate pairs. -static char16_t* allocFromUTF8(const char* in, size_t len) -{ - if (len == 0) return getEmptyString(); - - size_t chars = 0; - const char* end = in+len; - const char* p = in; - - while (p < end) { - chars++; - p += utf8_char_len(*p); - } - - SharedBuffer* buf = SharedBuffer::alloc((chars+1)*sizeof(char16_t)); - if (buf) { - p = in; - char16_t* str = (char16_t*)buf->data(); - char16_t* d = str; - while (p < end) { - size_t len = utf8_char_len(*p); - *d++ = (char16_t)utf8_to_utf32((const uint8_t*)p, len); - p += len; - } - *d = 0; - - //printf("Created UTF-16 string from UTF-8 \"%s\":", in); - //printHexData(1, str, buf->size(), 16, 1); - //printf("\n"); - - return str; - } - - return getEmptyString(); -} - -// --------------------------------------------------------------------------- - -String16::String16() - : mString(getEmptyString()) -{ -} - -String16::String16(const String16& o) - : mString(o.mString) -{ - SharedBuffer::bufferFromData(mString)->acquire(); -} - -String16::String16(const String16& o, size_t len, size_t begin) - : mString(getEmptyString()) -{ - setTo(o, len, begin); -} - -String16::String16(const char16_t* o) -{ - size_t len = strlen16(o); - SharedBuffer* buf = SharedBuffer::alloc((len+1)*sizeof(char16_t)); - LOG_ASSERT(buf, "Unable to allocate shared buffer"); - if (buf) { - char16_t* str = (char16_t*)buf->data(); - strcpy16(str, o); - mString = str; - return; - } - - mString = getEmptyString(); -} - -String16::String16(const char16_t* o, size_t len) -{ - SharedBuffer* buf = SharedBuffer::alloc((len+1)*sizeof(char16_t)); - LOG_ASSERT(buf, "Unable to allocate shared buffer"); - if (buf) { - char16_t* str = (char16_t*)buf->data(); - memcpy(str, o, len*sizeof(char16_t)); - str[len] = 0; - mString = str; - return; - } - - mString = getEmptyString(); -} - -String16::String16(const String8& o) - : mString(allocFromUTF8(o.string(), o.size())) -{ -} - -String16::String16(const char* o) - : mString(allocFromUTF8(o, strlen(o))) -{ -} - -String16::String16(const char* o, size_t len) - : mString(allocFromUTF8(o, len)) -{ -} - -String16::~String16() -{ - SharedBuffer::bufferFromData(mString)->release(); -} - -void String16::setTo(const String16& other) -{ - SharedBuffer::bufferFromData(other.mString)->acquire(); - SharedBuffer::bufferFromData(mString)->release(); - mString = other.mString; -} - -status_t String16::setTo(const String16& other, size_t len, size_t begin) -{ - const size_t N = other.size(); - if (begin >= N) { - SharedBuffer::bufferFromData(mString)->release(); - mString = getEmptyString(); - return NO_ERROR; - } - if ((begin+len) > N) len = N-begin; - if (begin == 0 && len == N) { - setTo(other); - return NO_ERROR; - } - - if (&other == this) { - LOG_ALWAYS_FATAL("Not implemented"); - } - - return setTo(other.string()+begin, len); -} - -status_t String16::setTo(const char16_t* other) -{ - return setTo(other, strlen16(other)); -} - -status_t String16::setTo(const char16_t* other, size_t len) -{ - SharedBuffer* buf = SharedBuffer::bufferFromData(mString) - ->editResize((len+1)*sizeof(char16_t)); - if (buf) { - char16_t* str = (char16_t*)buf->data(); - memcpy(str, other, len*sizeof(char16_t)); - str[len] = 0; - mString = str; - return NO_ERROR; - } - return NO_MEMORY; -} - -status_t String16::append(const String16& other) -{ - const size_t myLen = size(); - const size_t otherLen = other.size(); - if (myLen == 0) { - setTo(other); - return NO_ERROR; - } else if (otherLen == 0) { - return NO_ERROR; - } - - SharedBuffer* buf = SharedBuffer::bufferFromData(mString) - ->editResize((myLen+otherLen+1)*sizeof(char16_t)); - if (buf) { - char16_t* str = (char16_t*)buf->data(); - memcpy(str+myLen, other, (otherLen+1)*sizeof(char16_t)); - mString = str; - return NO_ERROR; - } - return NO_MEMORY; -} - -status_t String16::append(const char16_t* chrs, size_t otherLen) -{ - const size_t myLen = size(); - if (myLen == 0) { - setTo(chrs, otherLen); - return NO_ERROR; - } else if (otherLen == 0) { - return NO_ERROR; - } - - SharedBuffer* buf = SharedBuffer::bufferFromData(mString) - ->editResize((myLen+otherLen+1)*sizeof(char16_t)); - if (buf) { - char16_t* str = (char16_t*)buf->data(); - memcpy(str+myLen, chrs, otherLen*sizeof(char16_t)); - str[myLen+otherLen] = 0; - mString = str; - return NO_ERROR; - } - return NO_MEMORY; -} - -status_t String16::insert(size_t pos, const char16_t* chrs) -{ - return insert(pos, chrs, strlen16(chrs)); -} - -status_t String16::insert(size_t pos, const char16_t* chrs, size_t len) -{ - const size_t myLen = size(); - if (myLen == 0) { - return setTo(chrs, len); - return NO_ERROR; - } else if (len == 0) { - return NO_ERROR; - } - - if (pos > myLen) pos = myLen; - - #if 0 - printf("Insert in to %s: pos=%d, len=%d, myLen=%d, chrs=%s\n", - String8(*this).string(), pos, - len, myLen, String8(chrs, len).string()); - #endif - - SharedBuffer* buf = SharedBuffer::bufferFromData(mString) - ->editResize((myLen+len+1)*sizeof(char16_t)); - if (buf) { - char16_t* str = (char16_t*)buf->data(); - if (pos < myLen) { - memmove(str+pos+len, str+pos, (myLen-pos)*sizeof(char16_t)); - } - memcpy(str+pos, chrs, len*sizeof(char16_t)); - str[myLen+len] = 0; - mString = str; - #if 0 - printf("Result (%d chrs): %s\n", size(), String8(*this).string()); - #endif - return NO_ERROR; - } - return NO_MEMORY; -} - -ssize_t String16::findFirst(char16_t c) const -{ - const char16_t* str = string(); - const char16_t* p = str; - const char16_t* e = p + size(); - while (p < e) { - if (*p == c) { - return p-str; - } - p++; - } - return -1; -} - -ssize_t String16::findLast(char16_t c) const -{ - const char16_t* str = string(); - const char16_t* p = str; - const char16_t* e = p + size(); - while (p < e) { - e--; - if (*e == c) { - return e-str; - } - } - return -1; -} - -bool String16::startsWith(const String16& prefix) const -{ - const size_t ps = prefix.size(); - if (ps > size()) return false; - return strzcmp16(mString, ps, prefix.string(), ps) == 0; -} - -bool String16::startsWith(const char16_t* prefix) const -{ - const size_t ps = strlen16(prefix); - if (ps > size()) return false; - return strncmp16(mString, prefix, ps) == 0; -} - -status_t String16::makeLower() -{ - const size_t N = size(); - const char16_t* str = string(); - char16_t* edit = NULL; - for (size_t i=0; i= 'A' && v <= 'Z') { - if (!edit) { - SharedBuffer* buf = SharedBuffer::bufferFromData(mString)->edit(); - if (!buf) { - return NO_MEMORY; - } - edit = (char16_t*)buf->data(); - mString = str = edit; - } - edit[i] = tolower((char)v); - } - } - return NO_ERROR; -} - -status_t String16::replaceAll(char16_t replaceThis, char16_t withThis) -{ - const size_t N = size(); - const char16_t* str = string(); - char16_t* edit = NULL; - for (size_t i=0; iedit(); - if (!buf) { - return NO_MEMORY; - } - edit = (char16_t*)buf->data(); - mString = str = edit; - } - edit[i] = withThis; - } - } - return NO_ERROR; -} - -status_t String16::remove(size_t len, size_t begin) -{ - const size_t N = size(); - if (begin >= N) { - SharedBuffer::bufferFromData(mString)->release(); - mString = getEmptyString(); - return NO_ERROR; - } - if ((begin+len) > N) len = N-begin; - if (begin == 0 && len == N) { - return NO_ERROR; - } - - if (begin > 0) { - SharedBuffer* buf = SharedBuffer::bufferFromData(mString) - ->editResize((N+1)*sizeof(char16_t)); - if (!buf) { - return NO_MEMORY; - } - char16_t* str = (char16_t*)buf->data(); - memmove(str, str+begin, (N-begin+1)*sizeof(char16_t)); - mString = str; - } - SharedBuffer* buf = SharedBuffer::bufferFromData(mString) - ->editResize((len+1)*sizeof(char16_t)); - if (buf) { - char16_t* str = (char16_t*)buf->data(); - str[len] = 0; - mString = str; - return NO_ERROR; - } - return NO_MEMORY; -} - -TextOutput& operator<<(TextOutput& to, const String16& val) -{ - to << String8(val).string(); - return to; -} - -}; // namespace android diff --git a/libs/utils/String8.cpp b/libs/utils/String8.cpp deleted file mode 100644 index c50d343a7..000000000 --- a/libs/utils/String8.cpp +++ /dev/null @@ -1,604 +0,0 @@ -/* - * Copyright (C) 2005 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 - -#include -#include -#include -#include - -#include - -#include - -namespace android { - -// --------------------------------------------------------------------------- - -static const uint32_t kByteMask = 0x000000BF; -static const uint32_t kByteMark = 0x00000080; - -// Surrogates aren't valid for UTF-32 characters, so define some -// constants that will let us screen them out. -static const uint32_t kUnicodeSurrogateHighStart = 0x0000D800; -static const uint32_t kUnicodeSurrogateHighEnd = 0x0000DBFF; -static const uint32_t kUnicodeSurrogateLowStart = 0x0000DC00; -static const uint32_t kUnicodeSurrogateLowEnd = 0x0000DFFF; -static const uint32_t kUnicodeSurrogateStart = kUnicodeSurrogateHighStart; -static const uint32_t kUnicodeSurrogateEnd = kUnicodeSurrogateLowEnd; - -// Mask used to set appropriate bits in first byte of UTF-8 sequence, -// indexed by number of bytes in the sequence. -static const uint32_t kFirstByteMark[] = { - 0x00000000, 0x00000000, 0x000000C0, 0x000000E0, 0x000000F0 -}; - -// Separator used by resource paths. This is not platform dependent contrary -// to OS_PATH_SEPARATOR. -#define RES_PATH_SEPARATOR '/' - -// Return number of utf8 bytes required for the character. -static size_t utf32_to_utf8_bytes(uint32_t srcChar) -{ - size_t bytesToWrite; - - // Figure out how many bytes the result will require. - if (srcChar < 0x00000080) - { - bytesToWrite = 1; - } - else if (srcChar < 0x00000800) - { - bytesToWrite = 2; - } - else if (srcChar < 0x00010000) - { - if ((srcChar < kUnicodeSurrogateStart) - || (srcChar > kUnicodeSurrogateEnd)) - { - bytesToWrite = 3; - } - else - { - // Surrogates are invalid UTF-32 characters. - return 0; - } - } - // Max code point for Unicode is 0x0010FFFF. - else if (srcChar < 0x00110000) - { - bytesToWrite = 4; - } - else - { - // Invalid UTF-32 character. - return 0; - } - - return bytesToWrite; -} - -// Write out the source character to . - -static void utf32_to_utf8(uint8_t* dstP, uint32_t srcChar, size_t bytes) -{ - dstP += bytes; - switch (bytes) - { /* note: everything falls through. */ - case 4: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6; - case 3: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6; - case 2: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6; - case 1: *--dstP = (uint8_t)(srcChar | kFirstByteMark[bytes]); - } -} - -// --------------------------------------------------------------------------- - -static SharedBuffer* gEmptyStringBuf = NULL; -static char* gEmptyString = NULL; - -extern int gDarwinCantLoadAllObjects; -int gDarwinIsReallyAnnoying; - -static inline char* getEmptyString() -{ - gEmptyStringBuf->acquire(); - return gEmptyString; -} - -void initialize_string8() -{ -#ifdef LIBUTILS_NATIVE - // Bite me, Darwin! - gDarwinIsReallyAnnoying = gDarwinCantLoadAllObjects; -#endif - - SharedBuffer* buf = SharedBuffer::alloc(1); - char* str = (char*)buf->data(); - *str = 0; - gEmptyStringBuf = buf; - gEmptyString = str; -} - -void terminate_string8() -{ - SharedBuffer::bufferFromData(gEmptyString)->release(); - gEmptyStringBuf = NULL; - gEmptyString = NULL; -} - -// --------------------------------------------------------------------------- - -static char* allocFromUTF8(const char* in, size_t len) -{ - if (len > 0) { - SharedBuffer* buf = SharedBuffer::alloc(len+1); - LOG_ASSERT(buf, "Unable to allocate shared buffer"); - if (buf) { - char* str = (char*)buf->data(); - memcpy(str, in, len); - str[len] = 0; - return str; - } - return NULL; - } - - return getEmptyString(); -} - -// Note: not dealing with expanding surrogate pairs. -static char* allocFromUTF16(const char16_t* in, size_t len) -{ - if (len == 0) return getEmptyString(); - - size_t bytes = 0; - const char16_t* end = in+len; - const char16_t* p = in; - - while (p < end) { - bytes += utf32_to_utf8_bytes(*p); - p++; - } - - SharedBuffer* buf = SharedBuffer::alloc(bytes+1); - LOG_ASSERT(buf, "Unable to allocate shared buffer"); - if (buf) { - p = in; - char* str = (char*)buf->data(); - char* d = str; - while (p < end) { - uint32_t c = *p++; - size_t len = utf32_to_utf8_bytes(c); - utf32_to_utf8((uint8_t*)d, c, len); - d += len; - } - *d = 0; - - return str; - } - - return getEmptyString(); -} - -// --------------------------------------------------------------------------- - -String8::String8() - : mString(getEmptyString()) -{ -} - -String8::String8(const String8& o) - : mString(o.mString) -{ - SharedBuffer::bufferFromData(mString)->acquire(); -} - -String8::String8(const char* o) - : mString(allocFromUTF8(o, strlen(o))) -{ - if (mString == NULL) { - mString = getEmptyString(); - } -} - -String8::String8(const char* o, size_t len) - : mString(allocFromUTF8(o, len)) -{ - if (mString == NULL) { - mString = getEmptyString(); - } -} - -String8::String8(const String16& o) - : mString(allocFromUTF16(o.string(), o.size())) -{ -} - -String8::String8(const char16_t* o) - : mString(allocFromUTF16(o, strlen16(o))) -{ -} - -String8::String8(const char16_t* o, size_t len) - : mString(allocFromUTF16(o, len)) -{ -} - -String8::~String8() -{ - SharedBuffer::bufferFromData(mString)->release(); -} - -void String8::setTo(const String8& other) -{ - SharedBuffer::bufferFromData(other.mString)->acquire(); - SharedBuffer::bufferFromData(mString)->release(); - mString = other.mString; -} - -status_t String8::setTo(const char* other) -{ - SharedBuffer::bufferFromData(mString)->release(); - mString = allocFromUTF8(other, strlen(other)); - if (mString) return NO_ERROR; - - mString = getEmptyString(); - return NO_MEMORY; -} - -status_t String8::setTo(const char* other, size_t len) -{ - SharedBuffer::bufferFromData(mString)->release(); - mString = allocFromUTF8(other, len); - if (mString) return NO_ERROR; - - mString = getEmptyString(); - return NO_MEMORY; -} - -status_t String8::setTo(const char16_t* other, size_t len) -{ - SharedBuffer::bufferFromData(mString)->release(); - mString = allocFromUTF16(other, len); - if (mString) return NO_ERROR; - - mString = getEmptyString(); - return NO_MEMORY; -} - -status_t String8::append(const String8& other) -{ - const size_t otherLen = other.bytes(); - if (bytes() == 0) { - setTo(other); - return NO_ERROR; - } else if (otherLen == 0) { - return NO_ERROR; - } - - return real_append(other.string(), otherLen); -} - -status_t String8::append(const char* other) -{ - return append(other, strlen(other)); -} - -status_t String8::append(const char* other, size_t otherLen) -{ - if (bytes() == 0) { - return setTo(other, otherLen); - } else if (otherLen == 0) { - return NO_ERROR; - } - - return real_append(other, otherLen); -} - -status_t String8::real_append(const char* other, size_t otherLen) -{ - const size_t myLen = bytes(); - - SharedBuffer* buf = SharedBuffer::bufferFromData(mString) - ->editResize(myLen+otherLen+1); - if (buf) { - char* str = (char*)buf->data(); - mString = str; - str += myLen; - memcpy(str, other, otherLen); - str[otherLen] = '\0'; - return NO_ERROR; - } - return NO_MEMORY; -} - -char* String8::lockBuffer(size_t size) -{ - SharedBuffer* buf = SharedBuffer::bufferFromData(mString) - ->editResize(size+1); - if (buf) { - char* str = (char*)buf->data(); - mString = str; - return str; - } - return NULL; -} - -void String8::unlockBuffer() -{ - unlockBuffer(strlen(mString)); -} - -status_t String8::unlockBuffer(size_t size) -{ - if (size != this->size()) { - SharedBuffer* buf = SharedBuffer::bufferFromData(mString) - ->editResize(size+1); - if (buf) { - char* str = (char*)buf->data(); - str[size] = 0; - mString = str; - return NO_ERROR; - } - } - - return NO_MEMORY; -} - -ssize_t String8::find(const char* other, size_t start) const -{ - size_t len = size(); - if (start >= len) { - return -1; - } - const char* s = mString+start; - const char* p = strstr(s, other); - return p ? p-mString : -1; -} - -void String8::toLower() -{ - toLower(0, size()); -} - -void String8::toLower(size_t start, size_t length) -{ - const size_t len = size(); - if (start >= len) { - return; - } - if (start+length > len) { - length = len-start; - } - char* buf = lockBuffer(len); - buf += start; - while (length > 0) { - *buf = tolower(*buf); - buf++; - length--; - } - unlockBuffer(len); -} - -void String8::toUpper() -{ - toUpper(0, size()); -} - -void String8::toUpper(size_t start, size_t length) -{ - const size_t len = size(); - if (start >= len) { - return; - } - if (start+length > len) { - length = len-start; - } - char* buf = lockBuffer(len); - buf += start; - while (length > 0) { - *buf = toupper(*buf); - buf++; - length--; - } - unlockBuffer(len); -} - -TextOutput& operator<<(TextOutput& to, const String8& val) -{ - to << val.string(); - return to; -} - -// --------------------------------------------------------------------------- -// Path functions - - -void String8::setPathName(const char* name) -{ - setPathName(name, strlen(name)); -} - -void String8::setPathName(const char* name, size_t len) -{ - char* buf = lockBuffer(len); - - memcpy(buf, name, len); - - // remove trailing path separator, if present - if (len > 0 && buf[len-1] == OS_PATH_SEPARATOR) - len--; - - buf[len] = '\0'; - - unlockBuffer(len); -} - -String8 String8::getPathLeaf(void) const -{ - const char* cp; - const char*const buf = mString; - - cp = strrchr(buf, OS_PATH_SEPARATOR); - if (cp == NULL) - return String8(*this); - else - return String8(cp+1); -} - -String8 String8::getPathDir(void) const -{ - const char* cp; - const char*const str = mString; - - cp = strrchr(str, OS_PATH_SEPARATOR); - if (cp == NULL) - return String8(""); - else - return String8(str, cp - str); -} - -String8 String8::walkPath(String8* outRemains) const -{ - const char* cp; - const char*const str = mString; - const char* buf = str; - - cp = strchr(buf, OS_PATH_SEPARATOR); - if (cp == buf) { - // don't include a leading '/'. - buf = buf+1; - cp = strchr(buf, OS_PATH_SEPARATOR); - } - - if (cp == NULL) { - String8 res = buf != str ? String8(buf) : *this; - if (outRemains) *outRemains = String8(""); - return res; - } - - String8 res(buf, cp-buf); - if (outRemains) *outRemains = String8(cp+1); - return res; -} - -/* - * Helper function for finding the start of an extension in a pathname. - * - * Returns a pointer inside mString, or NULL if no extension was found. - */ -char* String8::find_extension(void) const -{ - const char* lastSlash; - const char* lastDot; - int extLen; - const char* const str = mString; - - // only look at the filename - lastSlash = strrchr(str, OS_PATH_SEPARATOR); - if (lastSlash == NULL) - lastSlash = str; - else - lastSlash++; - - // find the last dot - lastDot = strrchr(lastSlash, '.'); - if (lastDot == NULL) - return NULL; - - // looks good, ship it - return const_cast(lastDot); -} - -String8 String8::getPathExtension(void) const -{ - char* ext; - - ext = find_extension(); - if (ext != NULL) - return String8(ext); - else - return String8(""); -} - -String8 String8::getBasePath(void) const -{ - char* ext; - const char* const str = mString; - - ext = find_extension(); - if (ext == NULL) - return String8(*this); - else - return String8(str, ext - str); -} - -String8& String8::appendPath(const char* name) -{ - // TODO: The test below will fail for Win32 paths. Fix later or ignore. - if (name[0] != OS_PATH_SEPARATOR) { - if (*name == '\0') { - // nothing to do - return *this; - } - - size_t len = length(); - if (len == 0) { - // no existing filename, just use the new one - setPathName(name); - return *this; - } - - // make room for oldPath + '/' + newPath - int newlen = strlen(name); - - char* buf = lockBuffer(len+1+newlen); - - // insert a '/' if needed - if (buf[len-1] != OS_PATH_SEPARATOR) - buf[len++] = OS_PATH_SEPARATOR; - - memcpy(buf+len, name, newlen+1); - len += newlen; - - unlockBuffer(len); - - return *this; - } else { - setPathName(name); - return *this; - } -} - -String8& String8::convertToResPath() -{ -#if OS_PATH_SEPARATOR != RES_PATH_SEPARATOR - size_t len = length(); - if (len > 0) { - char * buf = lockBuffer(len); - for (char * end = buf + len; buf < end; ++buf) { - if (*buf == OS_PATH_SEPARATOR) - *buf = RES_PATH_SEPARATOR; - } - unlockBuffer(len); - } -#endif - return *this; -} - - -}; // namespace android diff --git a/libs/utils/SystemClock.cpp b/libs/utils/SystemClock.cpp deleted file mode 100644 index 2bdc0ce27..000000000 --- a/libs/utils/SystemClock.cpp +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -/* - * System clock functions. - */ - -#if HAVE_ANDROID_OS -#include -#include -#include -#include -#endif - -#include -#include -#include -#include -#include - -#include -#include - -#define LOG_TAG "SystemClock" -#include "utils/Log.h" - -namespace android { - -/* - * Set the current time. This only works when running as root. - */ -int setCurrentTimeMillis(int64_t millis) -{ -#if WIN32 - // not implemented - return -1; -#else - struct timeval tv; -#if HAVE_ANDROID_OS - struct timespec ts; - int fd; - int res; -#endif - int ret = 0; - - if (millis <= 0 || millis / 1000LL >= INT_MAX) { - return -1; - } - - tv.tv_sec = (time_t) (millis / 1000LL); - tv.tv_usec = (suseconds_t) ((millis % 1000LL) * 1000LL); - - LOGD("Setting time of day to sec=%d\n", (int) tv.tv_sec); - -#if HAVE_ANDROID_OS - fd = open("/dev/alarm", O_RDWR); - if(fd < 0) { - LOGW("Unable to open alarm driver: %s\n", strerror(errno)); - return -1; - } - ts.tv_sec = tv.tv_sec; - ts.tv_nsec = tv.tv_usec * 1000; - res = ioctl(fd, ANDROID_ALARM_SET_RTC, &ts); - if(res < 0) { - LOGW("Unable to set rtc to %ld: %s\n", tv.tv_sec, strerror(errno)); - ret = -1; - } - close(fd); -#else - if (settimeofday(&tv, NULL) != 0) { - LOGW("Unable to set clock to %d.%d: %s\n", - (int) tv.tv_sec, (int) tv.tv_usec, strerror(errno)); - ret = -1; - } -#endif - - return ret; -#endif // WIN32 -} - -/* - * native public static long uptimeMillis(); - */ -int64_t uptimeMillis() -{ - int64_t when = systemTime(SYSTEM_TIME_MONOTONIC); - return (int64_t) nanoseconds_to_milliseconds(when); -} - -/* - * native public static long elapsedRealtime(); - */ -int64_t elapsedRealtime() -{ -#if HAVE_ANDROID_OS - static int s_fd = -1; - - if (s_fd == -1) { - int fd = open("/dev/alarm", O_RDONLY); - if (android_atomic_cmpxchg(-1, fd, &s_fd)) { - close(fd); - } - } - - struct timespec ts; - int result = ioctl(s_fd, - ANDROID_ALARM_GET_TIME(ANDROID_ALARM_ELAPSED_REALTIME), &ts); - - if (result == 0) { - int64_t when = seconds_to_nanoseconds(ts.tv_sec) + ts.tv_nsec; - return (int64_t) nanoseconds_to_milliseconds(when); - } else { - // XXX: there was an error, probably because the driver didn't - // exist ... this should return - // a real error, like an exception! - int64_t when = systemTime(SYSTEM_TIME_MONOTONIC); - return (int64_t) nanoseconds_to_milliseconds(when); - } -#else - int64_t when = systemTime(SYSTEM_TIME_MONOTONIC); - return (int64_t) nanoseconds_to_milliseconds(when); -#endif -} - -}; // namespace android diff --git a/libs/utils/TextOutput.cpp b/libs/utils/TextOutput.cpp deleted file mode 100644 index cebee99e5..000000000 --- a/libs/utils/TextOutput.cpp +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright (C) 2005 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 - -#include - -#include -#include -#include - -// --------------------------------------------------------------------------- - -namespace android { - -TextOutput& operator<<(TextOutput& to, bool val) -{ - if (val) to.print("true", 4); - else to.print("false", 5); - return to; -} - -TextOutput& operator<<(TextOutput& to, int val) -{ - char buf[16]; - sprintf(buf, "%d", val); - to.print(buf, strlen(buf)); - return to; -} - -TextOutput& operator<<(TextOutput& to, long val) -{ - char buf[16]; - sprintf(buf, "%ld", val); - to.print(buf, strlen(buf)); - return to; -} - -TextOutput& operator<<(TextOutput& to, unsigned int val) -{ - char buf[16]; - sprintf(buf, "%u", val); - to.print(buf, strlen(buf)); - return to; -} - -TextOutput& operator<<(TextOutput& to, unsigned long val) -{ - char buf[16]; - sprintf(buf, "%lu", val); - to.print(buf, strlen(buf)); - return to; -} - -TextOutput& operator<<(TextOutput& to, long long val) -{ - char buf[32]; - sprintf(buf, "%Ld", val); - to.print(buf, strlen(buf)); - return to; -} - -TextOutput& operator<<(TextOutput& to, unsigned long long val) -{ - char buf[32]; - sprintf(buf, "%Lu", val); - to.print(buf, strlen(buf)); - return to; -} - -static TextOutput& print_float(TextOutput& to, double value) -{ - char buf[64]; - sprintf(buf, "%g", value); - if( !strchr(buf, '.') && !strchr(buf, 'e') && - !strchr(buf, 'E') ) { - strncat(buf, ".0", sizeof(buf)-1); - } - to.print(buf, strlen(buf)); - return to; -} - -TextOutput& operator<<(TextOutput& to, float val) -{ - return print_float(to,val); -} - -TextOutput& operator<<(TextOutput& to, double val) -{ - return print_float(to,val); -} - -TextOutput& operator<<(TextOutput& to, const void* val) -{ - char buf[16]; - sprintf(buf, "%p", val); - to.print(buf, strlen(buf)); - return to; -} - -static void textOutputPrinter(void* cookie, const char* txt) -{ - ((TextOutput*)cookie)->print(txt, strlen(txt)); -} - -TextOutput& operator<<(TextOutput& to, const TypeCode& val) -{ - printTypeCode(val.typeCode(), textOutputPrinter, (void*)&to); - return to; -} - -HexDump::HexDump(const void *buf, size_t size, size_t bytesPerLine) - : mBuffer(buf) - , mSize(size) - , mBytesPerLine(bytesPerLine) - , mSingleLineCutoff(16) - , mAlignment(4) - , mCArrayStyle(false) -{ - if (bytesPerLine >= 16) mAlignment = 4; - else if (bytesPerLine >= 8) mAlignment = 2; - else mAlignment = 1; -} - -TextOutput& operator<<(TextOutput& to, const HexDump& val) -{ - printHexData(0, val.buffer(), val.size(), val.bytesPerLine(), - val.singleLineCutoff(), val.alignment(), val.carrayStyle(), - textOutputPrinter, (void*)&to); - return to; -} - -}; // namespace android diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp deleted file mode 100644 index 74271ba3b..000000000 --- a/libs/utils/Threads.cpp +++ /dev/null @@ -1,1126 +0,0 @@ -/* - * 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. - */ - -#define LOG_TAG "libutils.threads" - -#include -#include - -#include -#include -#include -#include -#include -#include - -#if defined(HAVE_PTHREADS) -# include -# include -# include -#elif defined(HAVE_WIN32_THREADS) -# include -# include -# include -# define HAVE_CREATETHREAD // Cygwin, vs. HAVE__BEGINTHREADEX for MinGW -#endif - -#if defined(HAVE_FUTEX) -#include -#endif - -#if defined(HAVE_PRCTL) -#include -#endif - -/* - * =========================================================================== - * Thread wrappers - * =========================================================================== - */ - -using namespace android; - -// ---------------------------------------------------------------------------- -#if defined(HAVE_PTHREADS) -#if 0 -#pragma mark - -#pragma mark PTHREAD -#endif -// ---------------------------------------------------------------------------- - -/* - * Create and run a new thead. - * - * We create it "detached", so it cleans up after itself. - */ - -typedef void* (*android_pthread_entry)(void*); - -struct thread_data_t { - thread_func_t entryFunction; - void* userData; - int priority; - char * threadName; - - // we use this trampoline when we need to set the priority with - // nice/setpriority. - static int trampoline(const thread_data_t* t) { - thread_func_t f = t->entryFunction; - void* u = t->userData; - int prio = t->priority; - char * name = t->threadName; - delete t; - setpriority(PRIO_PROCESS, 0, prio); - if (name) { -#if defined(HAVE_PRCTL) - // Mac OS doesn't have this, and we build libutil for the host too - int hasAt = 0; - int hasDot = 0; - char *s = name; - while (*s) { - if (*s == '.') hasDot = 1; - else if (*s == '@') hasAt = 1; - s++; - } - int len = s - name; - if (len < 15 || hasAt || !hasDot) { - s = name; - } else { - s = name + len - 15; - } - prctl(PR_SET_NAME, (unsigned long) s, 0, 0, 0); -#endif - free(name); - } - return f(u); - } -}; - -int androidCreateRawThreadEtc(android_thread_func_t entryFunction, - void *userData, - const char* threadName, - int32_t threadPriority, - size_t threadStackSize, - android_thread_id_t *threadId) -{ - pthread_attr_t attr; - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - -#ifdef HAVE_ANDROID_OS /* valgrind is rejecting RT-priority create reqs */ - if (threadPriority != PRIORITY_DEFAULT || threadName != NULL) { - // We could avoid the trampoline if there was a way to get to the - // android_thread_id_t (pid) from pthread_t - thread_data_t* t = new thread_data_t; - t->priority = threadPriority; - t->threadName = threadName ? strdup(threadName) : NULL; - t->entryFunction = entryFunction; - t->userData = userData; - entryFunction = (android_thread_func_t)&thread_data_t::trampoline; - userData = t; - } -#endif - - if (threadStackSize) { - pthread_attr_setstacksize(&attr, threadStackSize); - } - - errno = 0; - pthread_t thread; - int result = pthread_create(&thread, &attr, - (android_pthread_entry)entryFunction, userData); - if (result != 0) { - LOGE("androidCreateRawThreadEtc failed (entry=%p, res=%d, errno=%d)\n" - "(android threadPriority=%d)", - entryFunction, result, errno, threadPriority); - return 0; - } - - if (threadId != NULL) { - *threadId = (android_thread_id_t)thread; // XXX: this is not portable - } - return 1; -} - -android_thread_id_t androidGetThreadId() -{ - return (android_thread_id_t)pthread_self(); -} - -// ---------------------------------------------------------------------------- -#elif defined(HAVE_WIN32_THREADS) -#if 0 -#pragma mark - -#pragma mark WIN32_THREADS -#endif -// ---------------------------------------------------------------------------- - -/* - * Trampoline to make us __stdcall-compliant. - * - * We're expected to delete "vDetails" when we're done. - */ -struct threadDetails { - int (*func)(void*); - void* arg; -}; -static __stdcall unsigned int threadIntermediary(void* vDetails) -{ - struct threadDetails* pDetails = (struct threadDetails*) vDetails; - int result; - - result = (*(pDetails->func))(pDetails->arg); - - delete pDetails; - - LOG(LOG_VERBOSE, "thread", "thread exiting\n"); - return (unsigned int) result; -} - -/* - * Create and run a new thread. - */ -static bool doCreateThread(android_thread_func_t fn, void* arg, android_thread_id_t *id) -{ - HANDLE hThread; - struct threadDetails* pDetails = new threadDetails; // must be on heap - unsigned int thrdaddr; - - pDetails->func = fn; - pDetails->arg = arg; - -#if defined(HAVE__BEGINTHREADEX) - hThread = (HANDLE) _beginthreadex(NULL, 0, threadIntermediary, pDetails, 0, - &thrdaddr); - if (hThread == 0) -#elif defined(HAVE_CREATETHREAD) - hThread = CreateThread(NULL, 0, - (LPTHREAD_START_ROUTINE) threadIntermediary, - (void*) pDetails, 0, (DWORD*) &thrdaddr); - if (hThread == NULL) -#endif - { - LOG(LOG_WARN, "thread", "WARNING: thread create failed\n"); - return false; - } - -#if defined(HAVE_CREATETHREAD) - /* close the management handle */ - CloseHandle(hThread); -#endif - - if (id != NULL) { - *id = (android_thread_id_t)thrdaddr; - } - - return true; -} - -int androidCreateRawThreadEtc(android_thread_func_t fn, - void *userData, - const char* threadName, - int32_t threadPriority, - size_t threadStackSize, - android_thread_id_t *threadId) -{ - return doCreateThread( fn, userData, threadId); -} - -android_thread_id_t androidGetThreadId() -{ - return (android_thread_id_t)GetCurrentThreadId(); -} - -// ---------------------------------------------------------------------------- -#else -#error "Threads not supported" -#endif - -// ---------------------------------------------------------------------------- - -#if 0 -#pragma mark - -#pragma mark Common Thread functions -#endif - -int androidCreateThread(android_thread_func_t fn, void* arg) -{ - return createThreadEtc(fn, arg); -} - -int androidCreateThreadGetID(android_thread_func_t fn, void *arg, android_thread_id_t *id) -{ - return createThreadEtc(fn, arg, "android:unnamed_thread", - PRIORITY_DEFAULT, 0, id); -} - -static android_create_thread_fn gCreateThreadFn = androidCreateRawThreadEtc; - -int androidCreateThreadEtc(android_thread_func_t entryFunction, - void *userData, - const char* threadName, - int32_t threadPriority, - size_t threadStackSize, - android_thread_id_t *threadId) -{ - return gCreateThreadFn(entryFunction, userData, threadName, - threadPriority, threadStackSize, threadId); -} - -void androidSetCreateThreadFunc(android_create_thread_fn func) -{ - gCreateThreadFn = func; -} - -namespace android { - -/* - * =========================================================================== - * Mutex class - * =========================================================================== - */ - -#if 0 -#pragma mark - -#pragma mark Mutex -#endif - -#if defined(HAVE_PTHREADS) && !defined(HAVE_FUTEX) -/* - * Simple pthread wrapper. - */ - -Mutex::Mutex() -{ - _init(); -} - -Mutex::Mutex(const char* name) -{ - // XXX: name not used for now - _init(); -} - -void Mutex::_init() -{ - pthread_mutex_t* pMutex = new pthread_mutex_t; - pthread_mutex_init(pMutex, NULL); - mState = pMutex; -} - -Mutex::~Mutex() -{ - delete (pthread_mutex_t*) mState; -} - -status_t Mutex::lock() -{ - int res; - while ((res=pthread_mutex_lock((pthread_mutex_t*) mState)) == EINTR) ; - return -res; -} - -void Mutex::unlock() -{ - pthread_mutex_unlock((pthread_mutex_t*) mState); -} - -status_t Mutex::tryLock() -{ - int res; - while ((res=pthread_mutex_trylock((pthread_mutex_t*) mState)) == EINTR) ; - return -res; -} - -#elif defined(HAVE_FUTEX) -#if 0 -#pragma mark - -#endif - -#define STATE ((futex_mutex_t*) (&mState)) - -Mutex::Mutex() -{ - _init(); -} - -Mutex::Mutex(const char* name) -{ - _init(); -} - -void -Mutex::_init() -{ - futex_mutex_init(STATE); -} - -Mutex::~Mutex() -{ -} - -status_t Mutex::lock() -{ - int res; - while ((res=futex_mutex_lock(STATE, FUTEX_WAIT_INFINITE)) == EINTR) ; - return -res; -} - -void Mutex::unlock() -{ - futex_mutex_unlock(STATE); -} - -status_t Mutex::tryLock() -{ - int res; - while ((res=futex_mutex_trylock(STATE)) == EINTR) ; - return -res; -} -#undef STATE - -#elif defined(HAVE_WIN32_THREADS) -#if 0 -#pragma mark - -#endif - -Mutex::Mutex() -{ - HANDLE hMutex; - - assert(sizeof(hMutex) == sizeof(mState)); - - hMutex = CreateMutex(NULL, FALSE, NULL); - mState = (void*) hMutex; -} - -Mutex::Mutex(const char* name) -{ - // XXX: name not used for now - HANDLE hMutex; - - hMutex = CreateMutex(NULL, FALSE, NULL); - mState = (void*) hMutex; -} - -Mutex::~Mutex() -{ - CloseHandle((HANDLE) mState); -} - -status_t Mutex::lock() -{ - DWORD dwWaitResult; - dwWaitResult = WaitForSingleObject((HANDLE) mState, INFINITE); - return dwWaitResult != WAIT_OBJECT_0 ? -1 : NO_ERROR; -} - -void Mutex::unlock() -{ - if (!ReleaseMutex((HANDLE) mState)) - LOG(LOG_WARN, "thread", "WARNING: bad result from unlocking mutex\n"); -} - -status_t Mutex::tryLock() -{ - DWORD dwWaitResult; - - dwWaitResult = WaitForSingleObject((HANDLE) mState, 0); - if (dwWaitResult != WAIT_OBJECT_0 && dwWaitResult != WAIT_TIMEOUT) - LOG(LOG_WARN, "thread", "WARNING: bad result from try-locking mutex\n"); - return (dwWaitResult == WAIT_OBJECT_0) ? 0 : -1; -} - -#else -#error "Somebody forgot to implement threads for this platform." -#endif - - -/* - * =========================================================================== - * Condition class - * =========================================================================== - */ - -#if 0 -#pragma mark - -#pragma mark Condition -#endif - -#if defined(HAVE_PTHREADS) && !defined(HAVE_FUTEX) - -/* - * Constructor. This is a simple pthread wrapper. - */ -Condition::Condition() -{ - pthread_cond_t* pCond = new pthread_cond_t; - - pthread_cond_init(pCond, NULL); - mState = pCond; -} - -/* - * Destructor. - */ -Condition::~Condition() -{ - pthread_cond_destroy((pthread_cond_t*) mState); - delete (pthread_cond_t*) mState; -} - -/* - * Wait on a condition variable. Lock the mutex before calling. - */ - -status_t Condition::wait(Mutex& mutex) -{ - assert(mutex.mState != NULL); - - int cc; - while ((cc = pthread_cond_wait((pthread_cond_t*)mState, - (pthread_mutex_t*) mutex.mState)) == EINTR) ; - return -cc; -} - -status_t Condition::wait(Mutex& mutex, nsecs_t abstime) -{ - assert(mutex.mState != NULL); - - struct timespec ts; - ts.tv_sec = abstime/1000000000; - ts.tv_nsec = abstime-(ts.tv_sec*1000000000); - - int cc; - while ((cc = pthread_cond_timedwait((pthread_cond_t*)mState, - (pthread_mutex_t*) mutex.mState, &ts)) == EINTR) ; - return -cc; -} - -status_t Condition::waitRelative(Mutex& mutex, nsecs_t reltime) -{ - return wait(mutex, systemTime()+reltime); -} - -/* - * Signal the condition variable, allowing one thread to continue. - */ -void Condition::signal() -{ - pthread_cond_signal((pthread_cond_t*) mState); -} - -/* - * Signal the condition variable, allowing all threads to continue. - */ -void Condition::broadcast() -{ - pthread_cond_broadcast((pthread_cond_t*) mState); -} - -#elif defined(HAVE_FUTEX) -#if 0 -#pragma mark - -#endif - -#define STATE ((futex_cond_t*) (&mState)) - -/* - * Constructor. This is a simple pthread wrapper. - */ -Condition::Condition() -{ - futex_cond_init(STATE); -} - -/* - * Destructor. - */ -Condition::~Condition() -{ -} - -/* - * Wait on a condition variable. Lock the mutex before calling. - */ - -status_t Condition::wait(Mutex& mutex) -{ - assert(mutex.mState != NULL); - - int res; - while ((res = futex_cond_wait(STATE, - (futex_mutex_t*)(&mutex.mState), FUTEX_WAIT_INFINITE)) == -EINTR) ; - - return -res; -} - -status_t Condition::wait(Mutex& mutex, nsecs_t abstime) -{ - nsecs_t reltime = abstime - systemTime(); - if (reltime <= 0) return true; - return waitRelative(mutex, reltime); -} - -status_t Condition::waitRelative(Mutex& mutex, nsecs_t reltime) -{ - assert(mutex.mState != NULL); - int res; - unsigned msec = ns2ms(reltime); - if(msec == 0) - return true; - // This code will not time out at the correct time if interrupted by signals - while ((res = futex_cond_wait(STATE, - (futex_mutex_t*)(&mutex.mState), msec)) == -EINTR) ; - return res; -} - -/* - * Signal the condition variable, allowing one thread to continue. - */ -void Condition::signal() -{ - futex_cond_signal(STATE); -} - -/* - * Signal the condition variable, allowing all threads to continue. - */ -void Condition::broadcast() -{ - futex_cond_broadcast(STATE); -} - -#undef STATE - -#elif defined(HAVE_WIN32_THREADS) -#if 0 -#pragma mark - -#endif - -/* - * Windows doesn't have a condition variable solution. It's possible - * to create one, but it's easy to get it wrong. For a discussion, and - * the origin of this implementation, see: - * - * http://www.cs.wustl.edu/~schmidt/win32-cv-1.html - * - * The implementation shown on the page does NOT follow POSIX semantics. - * As an optimization they require acquiring the external mutex before - * calling signal() and broadcast(), whereas POSIX only requires grabbing - * it before calling wait(). The implementation here has been un-optimized - * to have the correct behavior. - */ -typedef struct WinCondition { - // Number of waiting threads. - int waitersCount; - - // Serialize access to waitersCount. - CRITICAL_SECTION waitersCountLock; - - // Semaphore used to queue up threads waiting for the condition to - // become signaled. - HANDLE sema; - - // An auto-reset event used by the broadcast/signal thread to wait - // for all the waiting thread(s) to wake up and be released from - // the semaphore. - HANDLE waitersDone; - - // This mutex wouldn't be necessary if we required that the caller - // lock the external mutex before calling signal() and broadcast(). - // I'm trying to mimic pthread semantics though. - HANDLE internalMutex; - - // Keeps track of whether we were broadcasting or signaling. This - // allows us to optimize the code if we're just signaling. - bool wasBroadcast; - - status_t wait(WinCondition* condState, HANDLE hMutex, nsecs_t* abstime) - { - // Increment the wait count, avoiding race conditions. - EnterCriticalSection(&condState->waitersCountLock); - condState->waitersCount++; - //printf("+++ wait: incr waitersCount to %d (tid=%ld)\n", - // condState->waitersCount, getThreadId()); - LeaveCriticalSection(&condState->waitersCountLock); - - DWORD timeout = INFINITE; - if (abstime) { - nsecs_t reltime = *abstime - systemTime(); - if (reltime < 0) - reltime = 0; - timeout = reltime/1000000; - } - - // Atomically release the external mutex and wait on the semaphore. - DWORD res = - SignalObjectAndWait(hMutex, condState->sema, timeout, FALSE); - - //printf("+++ wait: awake (tid=%ld)\n", getThreadId()); - - // Reacquire lock to avoid race conditions. - EnterCriticalSection(&condState->waitersCountLock); - - // No longer waiting. - condState->waitersCount--; - - // Check to see if we're the last waiter after a broadcast. - bool lastWaiter = (condState->wasBroadcast && condState->waitersCount == 0); - - //printf("+++ wait: lastWaiter=%d (wasBc=%d wc=%d)\n", - // lastWaiter, condState->wasBroadcast, condState->waitersCount); - - LeaveCriticalSection(&condState->waitersCountLock); - - // If we're the last waiter thread during this particular broadcast - // then signal broadcast() that we're all awake. It'll drop the - // internal mutex. - if (lastWaiter) { - // Atomically signal the "waitersDone" event and wait until we - // can acquire the internal mutex. We want to do this in one step - // because it ensures that everybody is in the mutex FIFO before - // any thread has a chance to run. Without it, another thread - // could wake up, do work, and hop back in ahead of us. - SignalObjectAndWait(condState->waitersDone, condState->internalMutex, - INFINITE, FALSE); - } else { - // Grab the internal mutex. - WaitForSingleObject(condState->internalMutex, INFINITE); - } - - // Release the internal and grab the external. - ReleaseMutex(condState->internalMutex); - WaitForSingleObject(hMutex, INFINITE); - - return res == WAIT_OBJECT_0 ? NO_ERROR : -1; - } -} WinCondition; - -/* - * Constructor. Set up the WinCondition stuff. - */ -Condition::Condition() -{ - WinCondition* condState = new WinCondition; - - condState->waitersCount = 0; - condState->wasBroadcast = false; - // semaphore: no security, initial value of 0 - condState->sema = CreateSemaphore(NULL, 0, 0x7fffffff, NULL); - InitializeCriticalSection(&condState->waitersCountLock); - // auto-reset event, not signaled initially - condState->waitersDone = CreateEvent(NULL, FALSE, FALSE, NULL); - // used so we don't have to lock external mutex on signal/broadcast - condState->internalMutex = CreateMutex(NULL, FALSE, NULL); - - mState = condState; -} - -/* - * Destructor. Free Windows resources as well as our allocated storage. - */ -Condition::~Condition() -{ - WinCondition* condState = (WinCondition*) mState; - if (condState != NULL) { - CloseHandle(condState->sema); - CloseHandle(condState->waitersDone); - delete condState; - } -} - - -status_t Condition::wait(Mutex& mutex) -{ - WinCondition* condState = (WinCondition*) mState; - HANDLE hMutex = (HANDLE) mutex.mState; - - return ((WinCondition*)mState)->wait(condState, hMutex, NULL); -} - -status_t Condition::wait(Mutex& mutex, nsecs_t abstime) -{ - WinCondition* condState = (WinCondition*) mState; - HANDLE hMutex = (HANDLE) mutex.mState; - - return ((WinCondition*)mState)->wait(condState, hMutex, &abstime); -} - -status_t Condition::waitRelative(Mutex& mutex, nsecs_t reltime) -{ - return wait(mutex, systemTime()+reltime); -} - -/* - * Signal the condition variable, allowing one thread to continue. - */ -void Condition::signal() -{ - WinCondition* condState = (WinCondition*) mState; - - // Lock the internal mutex. This ensures that we don't clash with - // broadcast(). - WaitForSingleObject(condState->internalMutex, INFINITE); - - EnterCriticalSection(&condState->waitersCountLock); - bool haveWaiters = (condState->waitersCount > 0); - LeaveCriticalSection(&condState->waitersCountLock); - - // If no waiters, then this is a no-op. Otherwise, knock the semaphore - // down a notch. - if (haveWaiters) - ReleaseSemaphore(condState->sema, 1, 0); - - // Release internal mutex. - ReleaseMutex(condState->internalMutex); -} - -/* - * Signal the condition variable, allowing all threads to continue. - * - * First we have to wake up all threads waiting on the semaphore, then - * we wait until all of the threads have actually been woken before - * releasing the internal mutex. This ensures that all threads are woken. - */ -void Condition::broadcast() -{ - WinCondition* condState = (WinCondition*) mState; - - // Lock the internal mutex. This keeps the guys we're waking up - // from getting too far. - WaitForSingleObject(condState->internalMutex, INFINITE); - - EnterCriticalSection(&condState->waitersCountLock); - bool haveWaiters = false; - - if (condState->waitersCount > 0) { - haveWaiters = true; - condState->wasBroadcast = true; - } - - if (haveWaiters) { - // Wake up all the waiters. - ReleaseSemaphore(condState->sema, condState->waitersCount, 0); - - LeaveCriticalSection(&condState->waitersCountLock); - - // Wait for all awakened threads to acquire the counting semaphore. - // The last guy who was waiting sets this. - WaitForSingleObject(condState->waitersDone, INFINITE); - - // Reset wasBroadcast. (No crit section needed because nobody - // else can wake up to poke at it.) - condState->wasBroadcast = 0; - } else { - // nothing to do - LeaveCriticalSection(&condState->waitersCountLock); - } - - // Release internal mutex. - ReleaseMutex(condState->internalMutex); -} - -#else -#error "condition variables not supported on this platform" -#endif - - -/* - * =========================================================================== - * ReadWriteLock class - * =========================================================================== - */ - -#if 0 -#pragma mark - -#pragma mark ReadWriteLock -#endif - -/* - * Add a reader. Readers are nice. They share. - */ -void ReadWriteLock::lockForRead() -{ - mLock.lock(); - while (mNumWriters > 0) { - LOG(LOG_DEBUG, "thread", "+++ lockForRead: waiting\n"); - mReadWaiter.wait(mLock); - } - assert(mNumWriters == 0); - mNumReaders++; -#if defined(PRINT_RENDER_TIMES) - if (mNumReaders == 1) - mDebugTimer.start(); -#endif - mLock.unlock(); -} - -/* - * Try to add a reader. If it doesn't work right away, return "false". - */ -bool ReadWriteLock::tryLockForRead() -{ - mLock.lock(); - if (mNumWriters > 0) { - mLock.unlock(); - return false; - } - assert(mNumWriters == 0); - mNumReaders++; -#if defined(PRINT_RENDER_TIMES) - if (mNumReaders == 1) - mDebugTimer.start(); -#endif - mLock.unlock(); - return true; -} - -/* - * Remove a reader. - */ -void ReadWriteLock::unlockForRead() -{ - mLock.lock(); - if (mNumReaders == 0) { - LOG(LOG_WARN, "thread", - "WARNING: unlockForRead requested, but not locked\n"); - return; - } - assert(mNumReaders > 0); - assert(mNumWriters == 0); - mNumReaders--; - if (mNumReaders == 0) { // last reader? -#if defined(PRINT_RENDER_TIMES) - mDebugTimer.stop(); - printf(" rdlk held %.3f msec\n", - (double) mDebugTimer.durationUsecs() / 1000.0); -#endif - //printf("+++ signaling writers (if any)\n"); - mWriteWaiter.signal(); // wake one writer (if any) - } - mLock.unlock(); -} - -/* - * Add a writer. This requires exclusive access to the object. - */ -void ReadWriteLock::lockForWrite() -{ - mLock.lock(); - while (mNumReaders > 0 || mNumWriters > 0) { - LOG(LOG_DEBUG, "thread", "+++ lockForWrite: waiting\n"); - mWriteWaiter.wait(mLock); - } - assert(mNumReaders == 0); - assert(mNumWriters == 0); - mNumWriters++; -#if defined(PRINT_RENDER_TIMES) - mDebugTimer.start(); -#endif - mLock.unlock(); -} - -/* - * Try to add a writer. If it doesn't work right away, return "false". - */ -bool ReadWriteLock::tryLockForWrite() -{ - mLock.lock(); - if (mNumReaders > 0 || mNumWriters > 0) { - mLock.unlock(); - return false; - } - assert(mNumReaders == 0); - assert(mNumWriters == 0); - mNumWriters++; -#if defined(PRINT_RENDER_TIMES) - mDebugTimer.start(); -#endif - mLock.unlock(); - return true; -} - -/* - * Remove a writer. - */ -void ReadWriteLock::unlockForWrite() -{ - mLock.lock(); - if (mNumWriters == 0) { - LOG(LOG_WARN, "thread", - "WARNING: unlockForWrite requested, but not locked\n"); - return; - } - assert(mNumWriters == 1); - mNumWriters--; -#if defined(PRINT_RENDER_TIMES) - mDebugTimer.stop(); - //printf(" wrlk held %.3f msec\n", - // (double) mDebugTimer.durationUsecs() / 1000.0); -#endif - // mWriteWaiter.signal(); // should other writers get first dibs? - //printf("+++ signaling readers (if any)\n"); - mReadWaiter.broadcast(); // wake all readers (if any) - mLock.unlock(); -} - -// ---------------------------------------------------------------------------- - -#if 0 -#pragma mark - -#pragma mark Thread::Thread -#endif - -/* - * This is our thread object! - */ - -Thread::Thread(bool canCallJava) - : mCanCallJava(canCallJava), - mThread(thread_id_t(-1)), - mLock("Thread::mLock"), - mStatus(NO_ERROR), - mExitPending(false), mRunning(false) -{ -} - -Thread::~Thread() -{ -} - -status_t Thread::readyToRun() -{ - return NO_ERROR; -} - -status_t Thread::run(const char* name, int32_t priority, size_t stack) -{ - Mutex::Autolock _l(mLock); - - if (mRunning) { - // thread already started - return INVALID_OPERATION; - } - - // reset status and exitPending to their default value, so we can - // try again after an error happened (either below, or in readyToRun()) - mStatus = NO_ERROR; - mExitPending = false; - mThread = thread_id_t(-1); - - // hold a strong reference on ourself - mHoldSelf = this; - - bool res; - if (mCanCallJava) { - res = createThreadEtc(_threadLoop, - this, name, priority, stack, &mThread); - } else { - res = androidCreateRawThreadEtc(_threadLoop, - this, name, priority, stack, &mThread); - } - - if (res == false) { - mStatus = UNKNOWN_ERROR; // something happened! - mRunning = false; - mThread = thread_id_t(-1); - } - - if (mStatus < 0) { - // something happened, don't leak - mHoldSelf.clear(); - } - - return mStatus; -} - -int Thread::_threadLoop(void* user) -{ - Thread* const self = static_cast(user); - sp strong(self->mHoldSelf); - wp weak(strong); - self->mHoldSelf.clear(); - - // we're about to run... - self->mStatus = self->readyToRun(); - if (self->mStatus!=NO_ERROR || self->mExitPending) { - // pretend the thread never started... - self->mExitPending = false; - self->mRunning = false; - return 0; - } - - // thread is running now - self->mRunning = true; - - do { - bool result = self->threadLoop(); - if (result == false || self->mExitPending) { - self->mExitPending = true; - self->mLock.lock(); - self->mRunning = false; - self->mThreadExitedCondition.signal(); - self->mLock.unlock(); - break; - } - - // Release our strong reference, to let a chance to the thread - // to die a peaceful death. - strong.clear(); - // And immediately, reacquire a strong reference for the next loop - strong = weak.promote(); - } while(strong != 0); - - return 0; -} - -void Thread::requestExit() -{ - mExitPending = true; -} - -status_t Thread::requestExitAndWait() -{ - if (mStatus == OK) { - - if (mThread == getThreadId()) { - LOGW( - "Thread (this=%p): don't call waitForExit() from this " - "Thread object's thread. It's a guaranteed deadlock!", - this); - return WOULD_BLOCK; - } - - requestExit(); - - Mutex::Autolock _l(mLock); - while (mRunning == true) { - mThreadExitedCondition.wait(mLock); - } - mExitPending = false; - } - return mStatus; -} - -bool Thread::exitPending() const -{ - return mExitPending; -} - - - -}; // namespace android diff --git a/libs/utils/TimerProbe.cpp b/libs/utils/TimerProbe.cpp deleted file mode 100644 index 835480d36..000000000 --- a/libs/utils/TimerProbe.cpp +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#if ENABLE_TIMER_PROBE - -#ifdef LOG_TAG -#undef LOG_TAG -#endif -#define LOG_TAG "time" - -namespace android { - -Vector TimerProbe::gBuckets; -TimerProbe* TimerProbe::gExecuteChain; -int TimerProbe::gIndent; -timespec TimerProbe::gRealBase; - -TimerProbe::TimerProbe(const char tag[], int* slot) : mTag(tag) -{ - mNext = gExecuteChain; - gExecuteChain = this; - mIndent = gIndent; - gIndent += 1; - if (mIndent > 0) { - if (*slot == 0) { - int count = gBuckets.add(); - *slot = count; - Bucket& bucket = gBuckets.editItemAt(count); - memset(&bucket, 0, sizeof(Bucket)); - bucket.mTag = tag; - bucket.mSlotPtr = slot; - bucket.mIndent = mIndent; - } - mBucket = *slot; - } - clock_gettime(CLOCK_REALTIME, &mRealStart); - if (gRealBase.tv_sec == 0) - gRealBase = mRealStart; - clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &mPStart); - clock_gettime(CLOCK_THREAD_CPUTIME_ID, &mTStart); -} - -void TimerProbe::end() -{ - timespec realEnd, pEnd, tEnd; - clock_gettime(CLOCK_REALTIME, &realEnd); - clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &pEnd); - clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tEnd); - print(realEnd, pEnd, tEnd); - mTag = NULL; -} - -TimerProbe::~TimerProbe() -{ - if (mTag != NULL) - end(); - gExecuteChain = mNext; - gIndent--; -} - - -uint32_t TimerProbe::ElapsedTime(const timespec& start, const timespec& end) -{ - int sec = end.tv_sec - start.tv_sec; - int nsec = end.tv_nsec - start.tv_nsec; - if (nsec < 0) { - sec--; - nsec += 1000000000; - } - return sec * 1000000 + nsec / 1000; -} - -void TimerProbe::print(const timespec& r, const timespec& p, - const timespec& t) const -{ - uint32_t es = ElapsedTime(gRealBase, mRealStart); - uint32_t er = ElapsedTime(mRealStart, r); - uint32_t ep = ElapsedTime(mPStart, p); - uint32_t et = ElapsedTime(mTStart, t); - if (mIndent > 0) { - Bucket& bucket = gBuckets.editItemAt(mBucket); - if (bucket.mStart == 0) - bucket.mStart = es; - bucket.mReal += er; - bucket.mProcess += ep; - bucket.mThread += et; - bucket.mCount++; - return; - } - int index = 0; - int buckets = gBuckets.size(); - int count = 1; - const char* tag = mTag; - int indent = mIndent; - do { - LOGD("%-30.30s: (%3d) %-5.*s time=%-10.3f real=%7dus process=%7dus (%3d%%) thread=%7dus (%3d%%)\n", - tag, count, indent > 5 ? 5 : indent, "+++++", es / 1000000.0, - er, ep, ep * 100 / er, et, et * 100 / er); - if (index >= buckets) - break; - Bucket& bucket = gBuckets.editItemAt(index); - count = bucket.mCount; - es = bucket.mStart; - er = bucket.mReal; - ep = bucket.mProcess; - et = bucket.mThread; - tag = bucket.mTag; - indent = bucket.mIndent; - *bucket.mSlotPtr = 0; - } while (++index); // always true - gBuckets.clear(); -} - -}; // namespace android - -#endif diff --git a/libs/utils/Timers.cpp b/libs/utils/Timers.cpp deleted file mode 100644 index 2abc811a0..000000000 --- a/libs/utils/Timers.cpp +++ /dev/null @@ -1,240 +0,0 @@ -/* - * Copyright (C) 2005 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. - */ - -// -// Timer functions. -// -#include -#include // may need usleep -#include - -#include -#include -#include -#include -#include -#include - -#ifdef HAVE_WIN32_THREADS -#include -#endif - -nsecs_t systemTime(int clock) -{ -#if defined(HAVE_POSIX_CLOCKS) - static const clockid_t clocks[] = { - CLOCK_REALTIME, - CLOCK_MONOTONIC, - CLOCK_PROCESS_CPUTIME_ID, - CLOCK_THREAD_CPUTIME_ID - }; - struct timespec t; - t.tv_sec = t.tv_nsec = 0; - clock_gettime(clocks[clock], &t); - return nsecs_t(t.tv_sec)*1000000000LL + t.tv_nsec; -#else - // we don't support the clocks here. - struct timeval t; - t.tv_sec = t.tv_usec = 0; - gettimeofday(&t, NULL); - return nsecs_t(t.tv_sec)*1000000000LL + nsecs_t(t.tv_usec)*1000LL; -#endif -} - -//#define MONITOR_USLEEP - -/* - * Sleep long enough that we'll wake up "interval" milliseconds after - * the previous snooze. - * - * The "nextTick" argument is updated on each call, and should be passed - * in every time. Set its fields to zero on the first call. - * - * Returns the #of intervals we have overslept, which will be zero if we're - * on time. [Currently just returns 0 or 1.] - */ -int sleepForInterval(long interval, struct timeval* pNextTick) -{ - struct timeval now; - long long timeBeforeNext; - long sleepTime = 0; - bool overSlept = false; - //int usleepBias = 0; - -#ifdef USLEEP_BIAS - /* - * Linux likes to add 9000ms or so. - * [not using this for now] - */ - //usleepBias = USLEEP_BIAS; -#endif - - gettimeofday(&now, NULL); - - if (pNextTick->tv_sec == 0) { - /* special-case for first time through */ - *pNextTick = now; - sleepTime = interval; - android::DurationTimer::addToTimeval(pNextTick, interval); - } else { - /* - * Compute how much time there is before the next tick. If this - * value is negative, we've run over. If we've run over a little - * bit we can shorten the next frame to keep the pace steady, but - * if we've dramatically overshot we need to re-sync. - */ - timeBeforeNext = android::DurationTimer::subtractTimevals(pNextTick, &now); - //printf("TOP: now=%ld.%ld next=%ld.%ld diff=%ld\n", - // now.tv_sec, now.tv_usec, pNextTick->tv_sec, pNextTick->tv_usec, - // (long) timeBeforeNext); - if (timeBeforeNext < -interval) { - /* way over */ - overSlept = true; - sleepTime = 0; - *pNextTick = now; - } else if (timeBeforeNext <= 0) { - /* slightly over, keep the pace steady */ - overSlept = true; - sleepTime = 0; - } else if (timeBeforeNext <= interval) { - /* right on schedule */ - sleepTime = timeBeforeNext; - } else if (timeBeforeNext > interval && timeBeforeNext <= 2*interval) { - /* sleep call returned early; do a longer sleep this time */ - sleepTime = timeBeforeNext; - } else if (timeBeforeNext > interval) { - /* we went back in time -- somebody updated system clock? */ - /* (could also be a *seriously* broken usleep()) */ - LOG(LOG_DEBUG, "", - " Impossible: timeBeforeNext = %ld\n", (long)timeBeforeNext); - sleepTime = 0; - *pNextTick = now; - } - android::DurationTimer::addToTimeval(pNextTick, interval); - } - //printf(" Before sleep: now=%ld.%ld next=%ld.%ld sleepTime=%ld\n", - // now.tv_sec, now.tv_usec, pNextTick->tv_sec, pNextTick->tv_usec, - // sleepTime); - - /* - * Sleep for the designated period of time. - * - * Linux tends to sleep for longer than requested, often by 17-18ms. - * MinGW tends to sleep for less than requested, by as much as 14ms, - * but occasionally oversleeps for 40+ms (looks like some external - * factors plus round-off on a 64Hz clock). Cygwin is pretty steady. - * - * If you start the MinGW version, and then launch the Cygwin version, - * the MinGW clock becomes more erratic. Not entirely sure why. - * - * (There's a lot of stuff here; it's really just a usleep() call with - * a bunch of instrumentation.) - */ - if (sleepTime > 0) { -#if defined(MONITOR_USLEEP) - struct timeval before, after; - long long actual; - - gettimeofday(&before, NULL); - usleep((long) sleepTime); - gettimeofday(&after, NULL); - - /* check usleep() accuracy; default Linux threads are pretty sloppy */ - actual = android::DurationTimer::subtractTimevals(&after, &before); - if ((long) actual < sleepTime - 14000 /*(sleepTime/10)*/ || - (long) actual > sleepTime + 20000 /*(sleepTime/10)*/) - { - LOG(LOG_DEBUG, "", " Odd usleep: req=%ld, actual=%ld\n", sleepTime, - (long) actual); - } -#else -#ifdef HAVE_WIN32_THREADS - Sleep( sleepTime/1000 ); -#else - usleep((long) sleepTime); -#endif -#endif - } - - //printf("slept %d\n", sleepTime); - - if (overSlept) - return 1; // close enough - else - return 0; -} - - -/* - * =========================================================================== - * DurationTimer - * =========================================================================== - */ - -using namespace android; - -// Start the timer. -void DurationTimer::start(void) -{ - gettimeofday(&mStartWhen, NULL); -} - -// Stop the timer. -void DurationTimer::stop(void) -{ - gettimeofday(&mStopWhen, NULL); -} - -// Get the duration in microseconds. -long long DurationTimer::durationUsecs(void) const -{ - return (long) subtractTimevals(&mStopWhen, &mStartWhen); -} - -// Subtract two timevals. Returns the difference (ptv1-ptv2) in -// microseconds. -/*static*/ long long DurationTimer::subtractTimevals(const struct timeval* ptv1, - const struct timeval* ptv2) -{ - long long stop = ((long long) ptv1->tv_sec) * 1000000LL + - ((long long) ptv1->tv_usec); - long long start = ((long long) ptv2->tv_sec) * 1000000LL + - ((long long) ptv2->tv_usec); - return stop - start; -} - -// Add the specified amount of time to the timeval. -/*static*/ void DurationTimer::addToTimeval(struct timeval* ptv, long usec) -{ - if (usec < 0) { - LOG(LOG_WARN, "", "Negative values not supported in addToTimeval\n"); - return; - } - - // normalize tv_usec if necessary - if (ptv->tv_usec >= 1000000) { - ptv->tv_sec += ptv->tv_usec / 1000000; - ptv->tv_usec %= 1000000; - } - - ptv->tv_usec += usec % 1000000; - if (ptv->tv_usec >= 1000000) { - ptv->tv_usec -= 1000000; - ptv->tv_sec++; - } - ptv->tv_sec += usec / 1000000; -} - diff --git a/libs/utils/Unicode.cpp b/libs/utils/Unicode.cpp deleted file mode 100644 index 33f535fd1..000000000 --- a/libs/utils/Unicode.cpp +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "utils/AndroidUnicode.h" -#include "characterData.h" - -#define LOG_TAG "Unicode" -#include "utils/Log.h" - -// ICU headers for using macros -#include - -#define MIN_RADIX 2 -#define MAX_RADIX 36 - -#define TYPE_SHIFT 0 -#define TYPE_MASK ((1<<5)-1) - -#define DIRECTION_SHIFT (TYPE_SHIFT+5) -#define DIRECTION_MASK ((1<<5)-1) - -#define MIRRORED_SHIFT (DIRECTION_SHIFT+5) -#define MIRRORED_MASK ((1<<1)-1) - -#define TOUPPER_SHIFT (MIRRORED_SHIFT+1) -#define TOUPPER_MASK ((1<<6)-1) - -#define TOLOWER_SHIFT (TOUPPER_SHIFT+6) -#define TOLOWER_MASK ((1<<6)-1) - -#define TOTITLE_SHIFT (TOLOWER_SHIFT+6) -#define TOTITLE_MASK ((1<<2)-1) - -#define MIRROR_SHIFT (TOTITLE_SHIFT+2) -#define MIRROR_MASK ((1<<5)-1) - -#define NUMERIC_SHIFT (TOTITLE_SHIFT+2) -#define NUMERIC_MASK ((1<<7)-1) - -#define DECOMPOSITION_SHIFT (11) -#define DECOMPOSITION_MASK ((1<<5)-1) - -/* - * Returns the value stored in the CharacterData tables that contains - * an index into the packed data table and the decomposition type. - */ -static uint16_t findCharacterValue(UChar32 c) -{ - LOG_ASSERT(c >= 0 && c <= 0x10FFFF, "findCharacterValue received an invalid codepoint"); - if (c < 256) - return CharacterData::LATIN1_DATA[c]; - - // Rotate the bits because the tables are separated into even and odd codepoints - c = (c >> 1) | ((c & 1) << 20); - - CharacterData::Range search = CharacterData::FULL_DATA[c >> 16]; - const uint32_t* array = search.array; - - // This trick is so that that compare in the while loop does not - // need to shift the array entry down by 16 - c <<= 16; - c |= 0xFFFF; - - int high = (int)search.length - 1; - int low = 0; - - if (high < 0) - return 0; - - while (low < high - 1) - { - int probe = (high + low) >> 1; - - // The entries contain the codepoint in the high 16 bits and the index - // into PACKED_DATA in the low 16. - if (array[probe] > (unsigned)c) - high = probe; - else - low = probe; - } - - LOG_ASSERT((array[low] <= (unsigned)c), "A suitable range was not found"); - return array[low] & 0xFFFF; -} - -uint32_t android::Unicode::getPackedData(UChar32 c) -{ - // findCharacterValue returns a 16-bit value with the top 5 bits containing a decomposition type - // and the remaining bits containing an index. - return CharacterData::PACKED_DATA[findCharacterValue(c) & 0x7FF]; -} - -android::Unicode::CharType android::Unicode::getType(UChar32 c) -{ - if (c < 0 || c >= 0x10FFFF) - return CHARTYPE_UNASSIGNED; - return (CharType)((getPackedData(c) >> TYPE_SHIFT) & TYPE_MASK); -} - -android::Unicode::DecompositionType android::Unicode::getDecompositionType(UChar32 c) -{ - // findCharacterValue returns a 16-bit value with the top 5 bits containing a decomposition type - // and the remaining bits containing an index. - return (DecompositionType)((findCharacterValue(c) >> DECOMPOSITION_SHIFT) & DECOMPOSITION_MASK); -} - -int android::Unicode::getDigitValue(UChar32 c, int radix) -{ - if (radix < MIN_RADIX || radix > MAX_RADIX) - return -1; - - int tempValue = radix; - - if (c >= '0' && c <= '9') - tempValue = c - '0'; - else if (c >= 'a' && c <= 'z') - tempValue = c - 'a' + 10; - else if (c >= 'A' && c <= 'Z') - tempValue = c - 'A' + 10; - - return tempValue < radix ? tempValue : -1; -} - -int android::Unicode::getNumericValue(UChar32 c) -{ - if (isMirrored(c)) - return -1; - - return (int) CharacterData::NUMERICS[((getPackedData(c) >> NUMERIC_SHIFT) & NUMERIC_MASK)]; -} - -UChar32 android::Unicode::toLower(UChar32 c) -{ - return c + CharacterData::LCDIFF[(getPackedData(c) >> TOLOWER_SHIFT) & TOLOWER_MASK]; -} - -UChar32 android::Unicode::toUpper(UChar32 c) -{ - return c + CharacterData::UCDIFF[(getPackedData(c) >> TOUPPER_SHIFT) & TOUPPER_MASK]; -} - -android::Unicode::Direction android::Unicode::getDirectionality(UChar32 c) -{ - uint32_t data = getPackedData(c); - - if (0 == data) - return DIRECTIONALITY_UNDEFINED; - - Direction d = (Direction) ((data >> DIRECTION_SHIFT) & DIRECTION_MASK); - - if (DIRECTION_MASK == d) - return DIRECTIONALITY_UNDEFINED; - - return d; -} - -bool android::Unicode::isMirrored(UChar32 c) -{ - return ((getPackedData(c) >> MIRRORED_SHIFT) & MIRRORED_MASK) != 0; -} - -UChar32 android::Unicode::toMirror(UChar32 c) -{ - if (!isMirrored(c)) - return c; - - return c + CharacterData::MIRROR_DIFF[(getPackedData(c) >> MIRROR_SHIFT) & MIRROR_MASK]; -} - -UChar32 android::Unicode::toTitle(UChar32 c) -{ - int32_t diff = CharacterData::TCDIFF[(getPackedData(c) >> TOTITLE_SHIFT) & TOTITLE_MASK]; - - if (TOTITLE_MASK == diff) - return toUpper(c); - - return c + diff; -} - - diff --git a/libs/utils/VectorImpl.cpp b/libs/utils/VectorImpl.cpp deleted file mode 100644 index 2c2d6675c..000000000 --- a/libs/utils/VectorImpl.cpp +++ /dev/null @@ -1,611 +0,0 @@ -/* - * Copyright (C) 2005 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 "Vector" - -#include -#include -#include - -#include -#include -#include -#include - -/*****************************************************************************/ - - -namespace android { - -// ---------------------------------------------------------------------------- - -const size_t kMinVectorCapacity = 4; - -static inline size_t max(size_t a, size_t b) { - return a>b ? a : b; -} - -// ---------------------------------------------------------------------------- - -VectorImpl::VectorImpl(size_t itemSize, uint32_t flags) - : mStorage(0), mCount(0), mFlags(flags), mItemSize(itemSize) -{ -} - -VectorImpl::VectorImpl(const VectorImpl& rhs) - : mStorage(rhs.mStorage), mCount(rhs.mCount), - mFlags(rhs.mFlags), mItemSize(rhs.mItemSize) -{ - if (mStorage) { - SharedBuffer::sharedBuffer(mStorage)->acquire(); - } -} - -VectorImpl::~VectorImpl() -{ - LOG_ASSERT(!mCount, - "[%p] " - "subclasses of VectorImpl must call finish_vector()" - " in their destructor. Leaking %d bytes.", - this, (int)(mCount*mItemSize)); - // We can't call _do_destroy() here because the vtable is already gone. -} - -VectorImpl& VectorImpl::operator = (const VectorImpl& rhs) -{ - LOG_ASSERT(mItemSize == rhs.mItemSize, - "Vector<> have different types (this=%p, rhs=%p)", this, &rhs); - if (this != &rhs) { - release_storage(); - if (rhs.mCount) { - mStorage = rhs.mStorage; - mCount = rhs.mCount; - SharedBuffer::sharedBuffer(mStorage)->acquire(); - } else { - mStorage = 0; - mCount = 0; - } - } - return *this; -} - -void* VectorImpl::editArrayImpl() -{ - if (mStorage) { - SharedBuffer* sb = SharedBuffer::sharedBuffer(mStorage)->attemptEdit(); - if (sb == 0) { - sb = SharedBuffer::alloc(capacity() * mItemSize); - if (sb) { - _do_copy(sb->data(), mStorage, mCount); - release_storage(); - mStorage = sb->data(); - } - } - } - return mStorage; -} - -size_t VectorImpl::capacity() const -{ - if (mStorage) { - return SharedBuffer::sharedBuffer(mStorage)->size() / mItemSize; - } - return 0; -} - -ssize_t VectorImpl::insertVectorAt(const VectorImpl& vector, size_t index) -{ - if (index > size()) - return BAD_INDEX; - void* where = _grow(index, vector.size()); - if (where) { - _do_copy(where, vector.arrayImpl(), vector.size()); - } - return where ? index : (ssize_t)NO_MEMORY; -} - -ssize_t VectorImpl::appendVector(const VectorImpl& vector) -{ - return insertVectorAt(vector, size()); -} - -ssize_t VectorImpl::insertAt(size_t index, size_t numItems) -{ - return insertAt(0, index, numItems); -} - -ssize_t VectorImpl::insertAt(const void* item, size_t index, size_t numItems) -{ - if (index > size()) - return BAD_INDEX; - void* where = _grow(index, numItems); - if (where) { - if (item) { - _do_splat(where, item, numItems); - } else { - _do_construct(where, numItems); - } - } - return where ? index : (ssize_t)NO_MEMORY; -} - -static int sortProxy(const void* lhs, const void* rhs, void* func) -{ - return (*(VectorImpl::compar_t)func)(lhs, rhs); -} - -status_t VectorImpl::sort(VectorImpl::compar_t cmp) -{ - return sort(sortProxy, (void*)cmp); -} - -status_t VectorImpl::sort(VectorImpl::compar_r_t cmp, void* state) -{ - // the sort must be stable. we're using insertion sort which - // is well suited for small and already sorted arrays - // for big arrays, it could be better to use mergesort - const ssize_t count = size(); - if (count > 1) { - void* array = const_cast(arrayImpl()); - void* temp = 0; - ssize_t i = 1; - while (i < count) { - void* item = reinterpret_cast(array) + mItemSize*(i); - void* curr = reinterpret_cast(array) + mItemSize*(i-1); - if (cmp(curr, item, state) > 0) { - - if (!temp) { - // we're going to have to modify the array... - array = editArrayImpl(); - if (!array) return NO_MEMORY; - temp = malloc(mItemSize); - if (!temp) return NO_MEMORY; - _do_construct(temp, 1); - item = reinterpret_cast(array) + mItemSize*(i); - curr = reinterpret_cast(array) + mItemSize*(i-1); - } - - _do_copy(temp, item, 1); - - ssize_t j = i-1; - void* next = reinterpret_cast(array) + mItemSize*(i); - do { - _do_copy(next, curr, 1); - next = curr; - --j; - curr = reinterpret_cast(array) + mItemSize*(j); - } while (j>=0 && (cmp(curr, temp, state) > 0)); - - _do_copy(next, temp, 1); - } - i++; - } - - if (temp) { - _do_destroy(temp, 1); - free(temp); - } - } - return NO_ERROR; -} - -void VectorImpl::pop() -{ - if (size()) - removeItemsAt(size()-1, 1); -} - -void VectorImpl::push() -{ - push(0); -} - -void VectorImpl::push(const void* item) -{ - insertAt(item, size()); -} - -ssize_t VectorImpl::add() -{ - return add(0); -} - -ssize_t VectorImpl::add(const void* item) -{ - return insertAt(item, size()); -} - -ssize_t VectorImpl::replaceAt(size_t index) -{ - return replaceAt(0, index); -} - -ssize_t VectorImpl::replaceAt(const void* prototype, size_t index) -{ - LOG_ASSERT(index size()) - return BAD_VALUE; - _shrink(index, count); - return index; -} - -void VectorImpl::finish_vector() -{ - release_storage(); - mStorage = 0; - mCount = 0; -} - -void VectorImpl::clear() -{ - _shrink(0, mCount); -} - -void* VectorImpl::editItemLocation(size_t index) -{ - LOG_ASSERT(index(buffer) + index*mItemSize; - return 0; -} - -const void* VectorImpl::itemLocation(size_t index) const -{ - LOG_ASSERT(index(buffer) + index*mItemSize; - return 0; -} - -ssize_t VectorImpl::setCapacity(size_t new_capacity) -{ - size_t current_capacity = capacity(); - ssize_t amount = new_capacity - size(); - if (amount <= 0) { - // we can't reduce the capacity - return current_capacity; - } - SharedBuffer* sb = SharedBuffer::alloc(new_capacity * mItemSize); - if (sb) { - void* array = sb->data(); - _do_copy(array, mStorage, size()); - release_storage(); - mStorage = const_cast(array); - } else { - return NO_MEMORY; - } - return new_capacity; -} - -void VectorImpl::release_storage() -{ - if (mStorage) { - const SharedBuffer* sb = SharedBuffer::sharedBuffer(mStorage); - if (sb->release(SharedBuffer::eKeepStorage) == 1) { - _do_destroy(mStorage, mCount); - SharedBuffer::dealloc(sb); - } - } -} - -void* VectorImpl::_grow(size_t where, size_t amount) -{ -// LOGV("_grow(this=%p, where=%d, amount=%d) count=%d, capacity=%d", -// this, (int)where, (int)amount, (int)mCount, (int)capacity()); - - if (where > mCount) - where = mCount; - - const size_t new_size = mCount + amount; - if (capacity() < new_size) { - const size_t new_capacity = max(kMinVectorCapacity, ((new_size*3)+1)/2); -// LOGV("grow vector %p, new_capacity=%d", this, (int)new_capacity); - if ((mStorage) && - (mCount==where) && - (mFlags & HAS_TRIVIAL_COPY) && - (mFlags & HAS_TRIVIAL_DTOR)) - { - const SharedBuffer* cur_sb = SharedBuffer::sharedBuffer(mStorage); - SharedBuffer* sb = cur_sb->editResize(new_capacity * mItemSize); - mStorage = sb->data(); - } else { - SharedBuffer* sb = SharedBuffer::alloc(new_capacity * mItemSize); - if (sb) { - void* array = sb->data(); - if (where>0) { - _do_copy(array, mStorage, where); - } - if (mCount>where) { - const void* from = reinterpret_cast(mStorage) + where*mItemSize; - void* dest = reinterpret_cast(array) + (where+amount)*mItemSize; - _do_copy(dest, from, mCount-where); - } - release_storage(); - mStorage = const_cast(array); - } - } - } else { - ssize_t s = mCount-where; - if (s>0) { - void* array = editArrayImpl(); - void* to = reinterpret_cast(array) + (where+amount)*mItemSize; - const void* from = reinterpret_cast(array) + where*mItemSize; - _do_move_forward(to, from, s); - } - } - mCount += amount; - void* free_space = const_cast(itemLocation(where)); - return free_space; -} - -void VectorImpl::_shrink(size_t where, size_t amount) -{ - if (!mStorage) - return; - -// LOGV("_shrink(this=%p, where=%d, amount=%d) count=%d, capacity=%d", -// this, (int)where, (int)amount, (int)mCount, (int)capacity()); - - if (where >= mCount) - where = mCount - amount; - - const size_t new_size = mCount - amount; - if (new_size*3 < capacity()) { - const size_t new_capacity = max(kMinVectorCapacity, new_size*2); -// LOGV("shrink vector %p, new_capacity=%d", this, (int)new_capacity); - if ((where == mCount-amount) && - (mFlags & HAS_TRIVIAL_COPY) && - (mFlags & HAS_TRIVIAL_DTOR)) - { - const SharedBuffer* cur_sb = SharedBuffer::sharedBuffer(mStorage); - SharedBuffer* sb = cur_sb->editResize(new_capacity * mItemSize); - mStorage = sb->data(); - } else { - SharedBuffer* sb = SharedBuffer::alloc(new_capacity * mItemSize); - if (sb) { - void* array = sb->data(); - if (where>0) { - _do_copy(array, mStorage, where); - } - if (mCount > where+amount) { - const void* from = reinterpret_cast(mStorage) + (where+amount)*mItemSize; - void* dest = reinterpret_cast(array) + where*mItemSize; - _do_copy(dest, from, mCount-(where+amount)); - } - release_storage(); - mStorage = const_cast(array); - } - } - } else { - void* array = editArrayImpl(); - void* to = reinterpret_cast(array) + where*mItemSize; - _do_destroy(to, amount); - ssize_t s = mCount-(where+amount); - if (s>0) { - const void* from = reinterpret_cast(array) + (where+amount)*mItemSize; - _do_move_backward(to, from, s); - } - } - - // adjust the number of items... - mCount -= amount; -} - -size_t VectorImpl::itemSize() const { - return mItemSize; -} - -void VectorImpl::_do_construct(void* storage, size_t num) const -{ - if (!(mFlags & HAS_TRIVIAL_CTOR)) { - do_construct(storage, num); - } -} - -void VectorImpl::_do_destroy(void* storage, size_t num) const -{ - if (!(mFlags & HAS_TRIVIAL_DTOR)) { - do_destroy(storage, num); - } -} - -void VectorImpl::_do_copy(void* dest, const void* from, size_t num) const -{ - if (!(mFlags & HAS_TRIVIAL_COPY)) { - do_copy(dest, from, num); - } else { - memcpy(dest, from, num*itemSize()); - } -} - -void VectorImpl::_do_splat(void* dest, const void* item, size_t num) const { - do_splat(dest, item, num); -} - -void VectorImpl::_do_move_forward(void* dest, const void* from, size_t num) const { - do_move_forward(dest, from, num); -} - -void VectorImpl::_do_move_backward(void* dest, const void* from, size_t num) const { - do_move_backward(dest, from, num); -} - -void VectorImpl::reservedVectorImpl1() { } -void VectorImpl::reservedVectorImpl2() { } -void VectorImpl::reservedVectorImpl3() { } -void VectorImpl::reservedVectorImpl4() { } -void VectorImpl::reservedVectorImpl5() { } -void VectorImpl::reservedVectorImpl6() { } -void VectorImpl::reservedVectorImpl7() { } -void VectorImpl::reservedVectorImpl8() { } - -/*****************************************************************************/ - -SortedVectorImpl::SortedVectorImpl(size_t itemSize, uint32_t flags) - : VectorImpl(itemSize, flags) -{ -} - -SortedVectorImpl::SortedVectorImpl(const VectorImpl& rhs) -: VectorImpl(rhs) -{ -} - -SortedVectorImpl::~SortedVectorImpl() -{ -} - -SortedVectorImpl& SortedVectorImpl::operator = (const SortedVectorImpl& rhs) -{ - return static_cast( VectorImpl::operator = (static_cast(rhs)) ); -} - -ssize_t SortedVectorImpl::indexOf(const void* item) const -{ - return _indexOrderOf(item); -} - -size_t SortedVectorImpl::orderOf(const void* item) const -{ - size_t o; - _indexOrderOf(item, &o); - return o; -} - -ssize_t SortedVectorImpl::_indexOrderOf(const void* item, size_t* order) const -{ - // binary search - ssize_t err = NAME_NOT_FOUND; - ssize_t l = 0; - ssize_t h = size()-1; - ssize_t mid; - const void* a = arrayImpl(); - const size_t s = itemSize(); - while (l <= h) { - mid = l + (h - l)/2; - const void* const curr = reinterpret_cast(a) + (mid*s); - const int c = do_compare(curr, item); - if (c == 0) { - err = l = mid; - break; - } else if (c < 0) { - l = mid + 1; - } else { - h = mid - 1; - } - } - if (order) *order = l; - return err; -} - -ssize_t SortedVectorImpl::add(const void* item) -{ - size_t order; - ssize_t index = _indexOrderOf(item, &order); - if (index < 0) { - index = VectorImpl::insertAt(item, order, 1); - } else { - index = VectorImpl::replaceAt(item, index); - } - return index; -} - -ssize_t SortedVectorImpl::merge(const VectorImpl& vector) -{ - // naive merge... - if (!vector.isEmpty()) { - const void* buffer = vector.arrayImpl(); - const size_t is = itemSize(); - size_t s = vector.size(); - for (size_t i=0 ; i(buffer) + i*is ); - if (err<0) { - return err; - } - } - } - return NO_ERROR; -} - -ssize_t SortedVectorImpl::merge(const SortedVectorImpl& vector) -{ - // we've merging a sorted vector... nice! - ssize_t err = NO_ERROR; - if (!vector.isEmpty()) { - // first take care of the case where the vectors are sorted together - if (do_compare(vector.itemLocation(vector.size()-1), arrayImpl()) <= 0) { - err = VectorImpl::insertVectorAt(static_cast(vector), 0); - } else if (do_compare(vector.arrayImpl(), itemLocation(size()-1)) >= 0) { - err = VectorImpl::appendVector(static_cast(vector)); - } else { - // this could be made a little better - err = merge(static_cast(vector)); - } - } - return err; -} - -ssize_t SortedVectorImpl::remove(const void* item) -{ - ssize_t i = indexOf(item); - if (i>=0) { - VectorImpl::removeItemsAt(i, 1); - } - return i; -} - -void SortedVectorImpl::reservedSortedVectorImpl1() { }; -void SortedVectorImpl::reservedSortedVectorImpl2() { }; -void SortedVectorImpl::reservedSortedVectorImpl3() { }; -void SortedVectorImpl::reservedSortedVectorImpl4() { }; -void SortedVectorImpl::reservedSortedVectorImpl5() { }; -void SortedVectorImpl::reservedSortedVectorImpl6() { }; -void SortedVectorImpl::reservedSortedVectorImpl7() { }; -void SortedVectorImpl::reservedSortedVectorImpl8() { }; - - -/*****************************************************************************/ - -}; // namespace android - diff --git a/libs/utils/ZipEntry.cpp b/libs/utils/ZipEntry.cpp deleted file mode 100644 index fbc9e6784..000000000 --- a/libs/utils/ZipEntry.cpp +++ /dev/null @@ -1,696 +0,0 @@ -/* - * Copyright (C) 2006 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. - */ - -// -// Access to entries in a Zip archive. -// - -#define LOG_TAG "zip" - -#include "utils/ZipEntry.h" -#include "utils/Log.h" - -#include -#include -#include - -using namespace android; - -/* - * Initialize a new ZipEntry structure from a FILE* positioned at a - * CentralDirectoryEntry. - * - * On exit, the file pointer will be at the start of the next CDE or - * at the EOCD. - */ -status_t ZipEntry::initFromCDE(FILE* fp) -{ - status_t result; - long posn; - bool hasDD; - - //LOGV("initFromCDE ---\n"); - - /* read the CDE */ - result = mCDE.read(fp); - if (result != NO_ERROR) { - LOGD("mCDE.read failed\n"); - return result; - } - - //mCDE.dump(); - - /* using the info in the CDE, go load up the LFH */ - posn = ftell(fp); - if (fseek(fp, mCDE.mLocalHeaderRelOffset, SEEK_SET) != 0) { - LOGD("local header seek failed (%ld)\n", - mCDE.mLocalHeaderRelOffset); - return UNKNOWN_ERROR; - } - - result = mLFH.read(fp); - if (result != NO_ERROR) { - LOGD("mLFH.read failed\n"); - return result; - } - - if (fseek(fp, posn, SEEK_SET) != 0) - return UNKNOWN_ERROR; - - //mLFH.dump(); - - /* - * We *might* need to read the Data Descriptor at this point and - * integrate it into the LFH. If this bit is set, the CRC-32, - * compressed size, and uncompressed size will be zero. In practice - * these seem to be rare. - */ - hasDD = (mLFH.mGPBitFlag & kUsesDataDescr) != 0; - if (hasDD) { - // do something clever - //LOGD("+++ has data descriptor\n"); - } - - /* - * Sanity-check the LFH. Note that this will fail if the "kUsesDataDescr" - * flag is set, because the LFH is incomplete. (Not a problem, since we - * prefer the CDE values.) - */ - if (!hasDD && !compareHeaders()) { - LOGW("WARNING: header mismatch\n"); - // keep going? - } - - /* - * If the mVersionToExtract is greater than 20, we may have an - * issue unpacking the record -- could be encrypted, compressed - * with something we don't support, or use Zip64 extensions. We - * can defer worrying about that to when we're extracting data. - */ - - return NO_ERROR; -} - -/* - * Initialize a new entry. Pass in the file name and an optional comment. - * - * Initializes the CDE and the LFH. - */ -void ZipEntry::initNew(const char* fileName, const char* comment) -{ - assert(fileName != NULL && *fileName != '\0'); // name required - - /* most fields are properly initialized by constructor */ - mCDE.mVersionMadeBy = kDefaultMadeBy; - mCDE.mVersionToExtract = kDefaultVersion; - mCDE.mCompressionMethod = kCompressStored; - mCDE.mFileNameLength = strlen(fileName); - if (comment != NULL) - mCDE.mFileCommentLength = strlen(comment); - mCDE.mExternalAttrs = 0x81b60020; // matches what WinZip does - - if (mCDE.mFileNameLength > 0) { - mCDE.mFileName = new unsigned char[mCDE.mFileNameLength+1]; - strcpy((char*) mCDE.mFileName, fileName); - } - if (mCDE.mFileCommentLength > 0) { - /* TODO: stop assuming null-terminated ASCII here? */ - mCDE.mFileComment = new unsigned char[mCDE.mFileCommentLength+1]; - strcpy((char*) mCDE.mFileComment, comment); - } - - copyCDEtoLFH(); -} - -/* - * Initialize a new entry, starting with the ZipEntry from a different - * archive. - * - * Initializes the CDE and the LFH. - */ -status_t ZipEntry::initFromExternal(const ZipFile* pZipFile, - const ZipEntry* pEntry) -{ - /* - * Copy everything in the CDE over, then fix up the hairy bits. - */ - memcpy(&mCDE, &pEntry->mCDE, sizeof(mCDE)); - - if (mCDE.mFileNameLength > 0) { - mCDE.mFileName = new unsigned char[mCDE.mFileNameLength+1]; - if (mCDE.mFileName == NULL) - return NO_MEMORY; - strcpy((char*) mCDE.mFileName, (char*)pEntry->mCDE.mFileName); - } - if (mCDE.mFileCommentLength > 0) { - mCDE.mFileComment = new unsigned char[mCDE.mFileCommentLength+1]; - if (mCDE.mFileComment == NULL) - return NO_MEMORY; - strcpy((char*) mCDE.mFileComment, (char*)pEntry->mCDE.mFileComment); - } - if (mCDE.mExtraFieldLength > 0) { - /* we null-terminate this, though it may not be a string */ - mCDE.mExtraField = new unsigned char[mCDE.mExtraFieldLength+1]; - if (mCDE.mExtraField == NULL) - return NO_MEMORY; - memcpy(mCDE.mExtraField, pEntry->mCDE.mExtraField, - mCDE.mExtraFieldLength+1); - } - - /* construct the LFH from the CDE */ - copyCDEtoLFH(); - - /* - * The LFH "extra" field is independent of the CDE "extra", so we - * handle it here. - */ - assert(mLFH.mExtraField == NULL); - mLFH.mExtraFieldLength = pEntry->mLFH.mExtraFieldLength; - if (mLFH.mExtraFieldLength > 0) { - mLFH.mExtraField = new unsigned char[mLFH.mExtraFieldLength+1]; - if (mLFH.mExtraField == NULL) - return NO_MEMORY; - memcpy(mLFH.mExtraField, pEntry->mLFH.mExtraField, - mLFH.mExtraFieldLength+1); - } - - return NO_ERROR; -} - -/* - * Insert pad bytes in the LFH by tweaking the "extra" field. This will - * potentially confuse something that put "extra" data in here earlier, - * but I can't find an actual problem. - */ -status_t ZipEntry::addPadding(int padding) -{ - if (padding <= 0) - return INVALID_OPERATION; - - //LOGI("HEY: adding %d pad bytes to existing %d in %s\n", - // padding, mLFH.mExtraFieldLength, mCDE.mFileName); - - if (mLFH.mExtraFieldLength > 0) { - /* extend existing field */ - unsigned char* newExtra; - - newExtra = new unsigned char[mLFH.mExtraFieldLength + padding]; - if (newExtra == NULL) - return NO_MEMORY; - memset(newExtra + mLFH.mExtraFieldLength, 0, padding); - memcpy(newExtra, mLFH.mExtraField, mLFH.mExtraFieldLength); - - delete[] mLFH.mExtraField; - mLFH.mExtraField = newExtra; - mLFH.mExtraFieldLength += padding; - } else { - /* create new field */ - mLFH.mExtraField = new unsigned char[padding]; - memset(mLFH.mExtraField, 0, padding); - mLFH.mExtraFieldLength = padding; - } - - return NO_ERROR; -} - -/* - * Set the fields in the LFH equal to the corresponding fields in the CDE. - * - * This does not touch the LFH "extra" field. - */ -void ZipEntry::copyCDEtoLFH(void) -{ - mLFH.mVersionToExtract = mCDE.mVersionToExtract; - mLFH.mGPBitFlag = mCDE.mGPBitFlag; - mLFH.mCompressionMethod = mCDE.mCompressionMethod; - mLFH.mLastModFileTime = mCDE.mLastModFileTime; - mLFH.mLastModFileDate = mCDE.mLastModFileDate; - mLFH.mCRC32 = mCDE.mCRC32; - mLFH.mCompressedSize = mCDE.mCompressedSize; - mLFH.mUncompressedSize = mCDE.mUncompressedSize; - mLFH.mFileNameLength = mCDE.mFileNameLength; - // the "extra field" is independent - - delete[] mLFH.mFileName; - if (mLFH.mFileNameLength > 0) { - mLFH.mFileName = new unsigned char[mLFH.mFileNameLength+1]; - strcpy((char*) mLFH.mFileName, (const char*) mCDE.mFileName); - } else { - mLFH.mFileName = NULL; - } -} - -/* - * Set some information about a file after we add it. - */ -void ZipEntry::setDataInfo(long uncompLen, long compLen, unsigned long crc32, - int compressionMethod) -{ - mCDE.mCompressionMethod = compressionMethod; - mCDE.mCRC32 = crc32; - mCDE.mCompressedSize = compLen; - mCDE.mUncompressedSize = uncompLen; - mCDE.mCompressionMethod = compressionMethod; - if (compressionMethod == kCompressDeflated) { - mCDE.mGPBitFlag |= 0x0002; // indicates maximum compression used - } - copyCDEtoLFH(); -} - -/* - * See if the data in mCDE and mLFH match up. This is mostly useful for - * debugging these classes, but it can be used to identify damaged - * archives. - * - * Returns "false" if they differ. - */ -bool ZipEntry::compareHeaders(void) const -{ - if (mCDE.mVersionToExtract != mLFH.mVersionToExtract) { - LOGV("cmp: VersionToExtract\n"); - return false; - } - if (mCDE.mGPBitFlag != mLFH.mGPBitFlag) { - LOGV("cmp: GPBitFlag\n"); - return false; - } - if (mCDE.mCompressionMethod != mLFH.mCompressionMethod) { - LOGV("cmp: CompressionMethod\n"); - return false; - } - if (mCDE.mLastModFileTime != mLFH.mLastModFileTime) { - LOGV("cmp: LastModFileTime\n"); - return false; - } - if (mCDE.mLastModFileDate != mLFH.mLastModFileDate) { - LOGV("cmp: LastModFileDate\n"); - return false; - } - if (mCDE.mCRC32 != mLFH.mCRC32) { - LOGV("cmp: CRC32\n"); - return false; - } - if (mCDE.mCompressedSize != mLFH.mCompressedSize) { - LOGV("cmp: CompressedSize\n"); - return false; - } - if (mCDE.mUncompressedSize != mLFH.mUncompressedSize) { - LOGV("cmp: UncompressedSize\n"); - return false; - } - if (mCDE.mFileNameLength != mLFH.mFileNameLength) { - LOGV("cmp: FileNameLength\n"); - return false; - } -#if 0 // this seems to be used for padding, not real data - if (mCDE.mExtraFieldLength != mLFH.mExtraFieldLength) { - LOGV("cmp: ExtraFieldLength\n"); - return false; - } -#endif - if (mCDE.mFileName != NULL) { - if (strcmp((char*) mCDE.mFileName, (char*) mLFH.mFileName) != 0) { - LOGV("cmp: FileName\n"); - return false; - } - } - - return true; -} - - -/* - * Convert the DOS date/time stamp into a UNIX time stamp. - */ -time_t ZipEntry::getModWhen(void) const -{ - struct tm parts; - - parts.tm_sec = (mCDE.mLastModFileTime & 0x001f) << 1; - parts.tm_min = (mCDE.mLastModFileTime & 0x07e0) >> 5; - parts.tm_hour = (mCDE.mLastModFileTime & 0xf800) >> 11; - parts.tm_mday = (mCDE.mLastModFileDate & 0x001f); - parts.tm_mon = ((mCDE.mLastModFileDate & 0x01e0) >> 5) -1; - parts.tm_year = ((mCDE.mLastModFileDate & 0xfe00) >> 9) + 80; - parts.tm_wday = parts.tm_yday = 0; - parts.tm_isdst = -1; // DST info "not available" - - return mktime(&parts); -} - -/* - * Set the CDE/LFH timestamp from UNIX time. - */ -void ZipEntry::setModWhen(time_t when) -{ -#ifdef HAVE_LOCALTIME_R - struct tm tmResult; -#endif - time_t even; - unsigned short zdate, ztime; - - struct tm* ptm; - - /* round up to an even number of seconds */ - even = (time_t)(((unsigned long)(when) + 1) & (~1)); - - /* expand */ -#ifdef HAVE_LOCALTIME_R - ptm = localtime_r(&even, &tmResult); -#else - ptm = localtime(&even); -#endif - - int year; - year = ptm->tm_year; - if (year < 80) - year = 80; - - zdate = (year - 80) << 9 | (ptm->tm_mon+1) << 5 | ptm->tm_mday; - ztime = ptm->tm_hour << 11 | ptm->tm_min << 5 | ptm->tm_sec >> 1; - - mCDE.mLastModFileTime = mLFH.mLastModFileTime = ztime; - mCDE.mLastModFileDate = mLFH.mLastModFileDate = zdate; -} - - -/* - * =========================================================================== - * ZipEntry::LocalFileHeader - * =========================================================================== - */ - -/* - * Read a local file header. - * - * On entry, "fp" points to the signature at the start of the header. - * On exit, "fp" points to the start of data. - */ -status_t ZipEntry::LocalFileHeader::read(FILE* fp) -{ - status_t result = NO_ERROR; - unsigned char buf[kLFHLen]; - - assert(mFileName == NULL); - assert(mExtraField == NULL); - - if (fread(buf, 1, kLFHLen, fp) != kLFHLen) { - result = UNKNOWN_ERROR; - goto bail; - } - - if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) { - LOGD("whoops: didn't find expected signature\n"); - result = UNKNOWN_ERROR; - goto bail; - } - - mVersionToExtract = ZipEntry::getShortLE(&buf[0x04]); - mGPBitFlag = ZipEntry::getShortLE(&buf[0x06]); - mCompressionMethod = ZipEntry::getShortLE(&buf[0x08]); - mLastModFileTime = ZipEntry::getShortLE(&buf[0x0a]); - mLastModFileDate = ZipEntry::getShortLE(&buf[0x0c]); - mCRC32 = ZipEntry::getLongLE(&buf[0x0e]); - mCompressedSize = ZipEntry::getLongLE(&buf[0x12]); - mUncompressedSize = ZipEntry::getLongLE(&buf[0x16]); - mFileNameLength = ZipEntry::getShortLE(&buf[0x1a]); - mExtraFieldLength = ZipEntry::getShortLE(&buf[0x1c]); - - // TODO: validate sizes - - /* grab filename */ - if (mFileNameLength != 0) { - mFileName = new unsigned char[mFileNameLength+1]; - if (mFileName == NULL) { - result = NO_MEMORY; - goto bail; - } - if (fread(mFileName, 1, mFileNameLength, fp) != mFileNameLength) { - result = UNKNOWN_ERROR; - goto bail; - } - mFileName[mFileNameLength] = '\0'; - } - - /* grab extra field */ - if (mExtraFieldLength != 0) { - mExtraField = new unsigned char[mExtraFieldLength+1]; - if (mExtraField == NULL) { - result = NO_MEMORY; - goto bail; - } - if (fread(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) { - result = UNKNOWN_ERROR; - goto bail; - } - mExtraField[mExtraFieldLength] = '\0'; - } - -bail: - return result; -} - -/* - * Write a local file header. - */ -status_t ZipEntry::LocalFileHeader::write(FILE* fp) -{ - unsigned char buf[kLFHLen]; - - ZipEntry::putLongLE(&buf[0x00], kSignature); - ZipEntry::putShortLE(&buf[0x04], mVersionToExtract); - ZipEntry::putShortLE(&buf[0x06], mGPBitFlag); - ZipEntry::putShortLE(&buf[0x08], mCompressionMethod); - ZipEntry::putShortLE(&buf[0x0a], mLastModFileTime); - ZipEntry::putShortLE(&buf[0x0c], mLastModFileDate); - ZipEntry::putLongLE(&buf[0x0e], mCRC32); - ZipEntry::putLongLE(&buf[0x12], mCompressedSize); - ZipEntry::putLongLE(&buf[0x16], mUncompressedSize); - ZipEntry::putShortLE(&buf[0x1a], mFileNameLength); - ZipEntry::putShortLE(&buf[0x1c], mExtraFieldLength); - - if (fwrite(buf, 1, kLFHLen, fp) != kLFHLen) - return UNKNOWN_ERROR; - - /* write filename */ - if (mFileNameLength != 0) { - if (fwrite(mFileName, 1, mFileNameLength, fp) != mFileNameLength) - return UNKNOWN_ERROR; - } - - /* write "extra field" */ - if (mExtraFieldLength != 0) { - if (fwrite(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) - return UNKNOWN_ERROR; - } - - return NO_ERROR; -} - - -/* - * Dump the contents of a LocalFileHeader object. - */ -void ZipEntry::LocalFileHeader::dump(void) const -{ - LOGD(" LocalFileHeader contents:\n"); - LOGD(" versToExt=%u gpBits=0x%04x compression=%u\n", - mVersionToExtract, mGPBitFlag, mCompressionMethod); - LOGD(" modTime=0x%04x modDate=0x%04x crc32=0x%08lx\n", - mLastModFileTime, mLastModFileDate, mCRC32); - LOGD(" compressedSize=%lu uncompressedSize=%lu\n", - mCompressedSize, mUncompressedSize); - LOGD(" filenameLen=%u extraLen=%u\n", - mFileNameLength, mExtraFieldLength); - if (mFileName != NULL) - LOGD(" filename: '%s'\n", mFileName); -} - - -/* - * =========================================================================== - * ZipEntry::CentralDirEntry - * =========================================================================== - */ - -/* - * Read the central dir entry that appears next in the file. - * - * On entry, "fp" should be positioned on the signature bytes for the - * entry. On exit, "fp" will point at the signature word for the next - * entry or for the EOCD. - */ -status_t ZipEntry::CentralDirEntry::read(FILE* fp) -{ - status_t result = NO_ERROR; - unsigned char buf[kCDELen]; - - /* no re-use */ - assert(mFileName == NULL); - assert(mExtraField == NULL); - assert(mFileComment == NULL); - - if (fread(buf, 1, kCDELen, fp) != kCDELen) { - result = UNKNOWN_ERROR; - goto bail; - } - - if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) { - LOGD("Whoops: didn't find expected signature\n"); - result = UNKNOWN_ERROR; - goto bail; - } - - mVersionMadeBy = ZipEntry::getShortLE(&buf[0x04]); - mVersionToExtract = ZipEntry::getShortLE(&buf[0x06]); - mGPBitFlag = ZipEntry::getShortLE(&buf[0x08]); - mCompressionMethod = ZipEntry::getShortLE(&buf[0x0a]); - mLastModFileTime = ZipEntry::getShortLE(&buf[0x0c]); - mLastModFileDate = ZipEntry::getShortLE(&buf[0x0e]); - mCRC32 = ZipEntry::getLongLE(&buf[0x10]); - mCompressedSize = ZipEntry::getLongLE(&buf[0x14]); - mUncompressedSize = ZipEntry::getLongLE(&buf[0x18]); - mFileNameLength = ZipEntry::getShortLE(&buf[0x1c]); - mExtraFieldLength = ZipEntry::getShortLE(&buf[0x1e]); - mFileCommentLength = ZipEntry::getShortLE(&buf[0x20]); - mDiskNumberStart = ZipEntry::getShortLE(&buf[0x22]); - mInternalAttrs = ZipEntry::getShortLE(&buf[0x24]); - mExternalAttrs = ZipEntry::getLongLE(&buf[0x26]); - mLocalHeaderRelOffset = ZipEntry::getLongLE(&buf[0x2a]); - - // TODO: validate sizes and offsets - - /* grab filename */ - if (mFileNameLength != 0) { - mFileName = new unsigned char[mFileNameLength+1]; - if (mFileName == NULL) { - result = NO_MEMORY; - goto bail; - } - if (fread(mFileName, 1, mFileNameLength, fp) != mFileNameLength) { - result = UNKNOWN_ERROR; - goto bail; - } - mFileName[mFileNameLength] = '\0'; - } - - /* read "extra field" */ - if (mExtraFieldLength != 0) { - mExtraField = new unsigned char[mExtraFieldLength+1]; - if (mExtraField == NULL) { - result = NO_MEMORY; - goto bail; - } - if (fread(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) { - result = UNKNOWN_ERROR; - goto bail; - } - mExtraField[mExtraFieldLength] = '\0'; - } - - - /* grab comment, if any */ - if (mFileCommentLength != 0) { - mFileComment = new unsigned char[mFileCommentLength+1]; - if (mFileComment == NULL) { - result = NO_MEMORY; - goto bail; - } - if (fread(mFileComment, 1, mFileCommentLength, fp) != mFileCommentLength) - { - result = UNKNOWN_ERROR; - goto bail; - } - mFileComment[mFileCommentLength] = '\0'; - } - -bail: - return result; -} - -/* - * Write a central dir entry. - */ -status_t ZipEntry::CentralDirEntry::write(FILE* fp) -{ - unsigned char buf[kCDELen]; - - ZipEntry::putLongLE(&buf[0x00], kSignature); - ZipEntry::putShortLE(&buf[0x04], mVersionMadeBy); - ZipEntry::putShortLE(&buf[0x06], mVersionToExtract); - ZipEntry::putShortLE(&buf[0x08], mGPBitFlag); - ZipEntry::putShortLE(&buf[0x0a], mCompressionMethod); - ZipEntry::putShortLE(&buf[0x0c], mLastModFileTime); - ZipEntry::putShortLE(&buf[0x0e], mLastModFileDate); - ZipEntry::putLongLE(&buf[0x10], mCRC32); - ZipEntry::putLongLE(&buf[0x14], mCompressedSize); - ZipEntry::putLongLE(&buf[0x18], mUncompressedSize); - ZipEntry::putShortLE(&buf[0x1c], mFileNameLength); - ZipEntry::putShortLE(&buf[0x1e], mExtraFieldLength); - ZipEntry::putShortLE(&buf[0x20], mFileCommentLength); - ZipEntry::putShortLE(&buf[0x22], mDiskNumberStart); - ZipEntry::putShortLE(&buf[0x24], mInternalAttrs); - ZipEntry::putLongLE(&buf[0x26], mExternalAttrs); - ZipEntry::putLongLE(&buf[0x2a], mLocalHeaderRelOffset); - - if (fwrite(buf, 1, kCDELen, fp) != kCDELen) - return UNKNOWN_ERROR; - - /* write filename */ - if (mFileNameLength != 0) { - if (fwrite(mFileName, 1, mFileNameLength, fp) != mFileNameLength) - return UNKNOWN_ERROR; - } - - /* write "extra field" */ - if (mExtraFieldLength != 0) { - if (fwrite(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) - return UNKNOWN_ERROR; - } - - /* write comment */ - if (mFileCommentLength != 0) { - if (fwrite(mFileComment, 1, mFileCommentLength, fp) != mFileCommentLength) - return UNKNOWN_ERROR; - } - - return NO_ERROR; -} - -/* - * Dump the contents of a CentralDirEntry object. - */ -void ZipEntry::CentralDirEntry::dump(void) const -{ - LOGD(" CentralDirEntry contents:\n"); - LOGD(" versMadeBy=%u versToExt=%u gpBits=0x%04x compression=%u\n", - mVersionMadeBy, mVersionToExtract, mGPBitFlag, mCompressionMethod); - LOGD(" modTime=0x%04x modDate=0x%04x crc32=0x%08lx\n", - mLastModFileTime, mLastModFileDate, mCRC32); - LOGD(" compressedSize=%lu uncompressedSize=%lu\n", - mCompressedSize, mUncompressedSize); - LOGD(" filenameLen=%u extraLen=%u commentLen=%u\n", - mFileNameLength, mExtraFieldLength, mFileCommentLength); - LOGD(" diskNumStart=%u intAttr=0x%04x extAttr=0x%08lx relOffset=%lu\n", - mDiskNumberStart, mInternalAttrs, mExternalAttrs, - mLocalHeaderRelOffset); - - if (mFileName != NULL) - LOGD(" filename: '%s'\n", mFileName); - if (mFileComment != NULL) - LOGD(" comment: '%s'\n", mFileComment); -} - diff --git a/libs/utils/ZipFile.cpp b/libs/utils/ZipFile.cpp deleted file mode 100644 index 89aa874b4..000000000 --- a/libs/utils/ZipFile.cpp +++ /dev/null @@ -1,1296 +0,0 @@ -/* - * Copyright (C) 2006 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. - */ - -// -// Access to Zip archives. -// - -#define LOG_TAG "zip" - -#include "utils/ZipFile.h" -#include "utils/ZipUtils.h" -#include "utils/Log.h" - -#include -#define DEF_MEM_LEVEL 8 // normally in zutil.h? - -#include -#include -#include -#include - -using namespace android; - -/* - * Some environments require the "b", some choke on it. - */ -#define FILE_OPEN_RO "rb" -#define FILE_OPEN_RW "r+b" -#define FILE_OPEN_RW_CREATE "w+b" - -/* should live somewhere else? */ -static status_t errnoToStatus(int err) -{ - if (err == ENOENT) - return NAME_NOT_FOUND; - else if (err == EACCES) - return PERMISSION_DENIED; - else - return UNKNOWN_ERROR; -} - -/* - * Open a file and parse its guts. - */ -status_t ZipFile::open(const char* zipFileName, int flags) -{ - bool newArchive = false; - - assert(mZipFp == NULL); // no reopen - - if ((flags & kOpenTruncate)) - flags |= kOpenCreate; // trunc implies create - - if ((flags & kOpenReadOnly) && (flags & kOpenReadWrite)) - return INVALID_OPERATION; // not both - if (!((flags & kOpenReadOnly) || (flags & kOpenReadWrite))) - return INVALID_OPERATION; // not neither - if ((flags & kOpenCreate) && !(flags & kOpenReadWrite)) - return INVALID_OPERATION; // create requires write - - if (flags & kOpenTruncate) { - newArchive = true; - } else { - newArchive = (access(zipFileName, F_OK) != 0); - if (!(flags & kOpenCreate) && newArchive) { - /* not creating, must already exist */ - LOGD("File %s does not exist", zipFileName); - return NAME_NOT_FOUND; - } - } - - /* open the file */ - const char* openflags; - if (flags & kOpenReadWrite) { - if (newArchive) - openflags = FILE_OPEN_RW_CREATE; - else - openflags = FILE_OPEN_RW; - } else { - openflags = FILE_OPEN_RO; - } - mZipFp = fopen(zipFileName, openflags); - if (mZipFp == NULL) { - int err = errno; - LOGD("fopen failed: %d\n", err); - return errnoToStatus(err); - } - - status_t result; - if (!newArchive) { - /* - * Load the central directory. If that fails, then this probably - * isn't a Zip archive. - */ - result = readCentralDir(); - } else { - /* - * Newly-created. The EndOfCentralDir constructor actually - * sets everything to be the way we want it (all zeroes). We - * set mNeedCDRewrite so that we create *something* if the - * caller doesn't add any files. (We could also just unlink - * the file if it's brand new and nothing was added, but that's - * probably doing more than we really should -- the user might - * have a need for empty zip files.) - */ - mNeedCDRewrite = true; - result = NO_ERROR; - } - - if (flags & kOpenReadOnly) - mReadOnly = true; - else - assert(!mReadOnly); - - return result; -} - -/* - * Return the Nth entry in the archive. - */ -ZipEntry* ZipFile::getEntryByIndex(int idx) const -{ - if (idx < 0 || idx >= (int) mEntries.size()) - return NULL; - - return mEntries[idx]; -} - -/* - * Find an entry by name. - */ -ZipEntry* ZipFile::getEntryByName(const char* fileName) const -{ - /* - * Do a stupid linear string-compare search. - * - * There are various ways to speed this up, especially since it's rare - * to intermingle changes to the archive with "get by name" calls. We - * don't want to sort the mEntries vector itself, however, because - * it's used to recreate the Central Directory. - * - * (Hash table works, parallel list of pointers in sorted order is good.) - */ - int idx; - - for (idx = mEntries.size()-1; idx >= 0; idx--) { - ZipEntry* pEntry = mEntries[idx]; - if (!pEntry->getDeleted() && - strcmp(fileName, pEntry->getFileName()) == 0) - { - return pEntry; - } - } - - return NULL; -} - -/* - * Empty the mEntries vector. - */ -void ZipFile::discardEntries(void) -{ - int count = mEntries.size(); - - while (--count >= 0) - delete mEntries[count]; - - mEntries.clear(); -} - - -/* - * Find the central directory and read the contents. - * - * The fun thing about ZIP archives is that they may or may not be - * readable from start to end. In some cases, notably for archives - * that were written to stdout, the only length information is in the - * central directory at the end of the file. - * - * Of course, the central directory can be followed by a variable-length - * comment field, so we have to scan through it backwards. The comment - * is at most 64K, plus we have 18 bytes for the end-of-central-dir stuff - * itself, plus apparently sometimes people throw random junk on the end - * just for the fun of it. - * - * This is all a little wobbly. If the wrong value ends up in the EOCD - * area, we're hosed. This appears to be the way that everbody handles - * it though, so we're in pretty good company if this fails. - */ -status_t ZipFile::readCentralDir(void) -{ - status_t result = NO_ERROR; - unsigned char* buf = NULL; - off_t fileLength, seekStart; - long readAmount; - int i; - - fseek(mZipFp, 0, SEEK_END); - fileLength = ftell(mZipFp); - rewind(mZipFp); - - /* too small to be a ZIP archive? */ - if (fileLength < EndOfCentralDir::kEOCDLen) { - LOGD("Length is %ld -- too small\n", (long)fileLength); - result = INVALID_OPERATION; - goto bail; - } - - buf = new unsigned char[EndOfCentralDir::kMaxEOCDSearch]; - if (buf == NULL) { - LOGD("Failure allocating %d bytes for EOCD search", - EndOfCentralDir::kMaxEOCDSearch); - result = NO_MEMORY; - goto bail; - } - - if (fileLength > EndOfCentralDir::kMaxEOCDSearch) { - seekStart = fileLength - EndOfCentralDir::kMaxEOCDSearch; - readAmount = EndOfCentralDir::kMaxEOCDSearch; - } else { - seekStart = 0; - readAmount = (long) fileLength; - } - if (fseek(mZipFp, seekStart, SEEK_SET) != 0) { - LOGD("Failure seeking to end of zip at %ld", (long) seekStart); - result = UNKNOWN_ERROR; - goto bail; - } - - /* read the last part of the file into the buffer */ - if (fread(buf, 1, readAmount, mZipFp) != (size_t) readAmount) { - LOGD("short file? wanted %ld\n", readAmount); - result = UNKNOWN_ERROR; - goto bail; - } - - /* find the end-of-central-dir magic */ - for (i = readAmount - 4; i >= 0; i--) { - if (buf[i] == 0x50 && - ZipEntry::getLongLE(&buf[i]) == EndOfCentralDir::kSignature) - { - LOGV("+++ Found EOCD at buf+%d\n", i); - break; - } - } - if (i < 0) { - LOGD("EOCD not found, not Zip\n"); - result = INVALID_OPERATION; - goto bail; - } - - /* extract eocd values */ - result = mEOCD.readBuf(buf + i, readAmount - i); - if (result != NO_ERROR) { - LOGD("Failure reading %ld bytes of EOCD values", readAmount - i); - goto bail; - } - //mEOCD.dump(); - - if (mEOCD.mDiskNumber != 0 || mEOCD.mDiskWithCentralDir != 0 || - mEOCD.mNumEntries != mEOCD.mTotalNumEntries) - { - LOGD("Archive spanning not supported\n"); - result = INVALID_OPERATION; - goto bail; - } - - /* - * So far so good. "mCentralDirSize" is the size in bytes of the - * central directory, so we can just seek back that far to find it. - * We can also seek forward mCentralDirOffset bytes from the - * start of the file. - * - * We're not guaranteed to have the rest of the central dir in the - * buffer, nor are we guaranteed that the central dir will have any - * sort of convenient size. We need to skip to the start of it and - * read the header, then the other goodies. - * - * The only thing we really need right now is the file comment, which - * we're hoping to preserve. - */ - if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) { - LOGD("Failure seeking to central dir offset %ld\n", - mEOCD.mCentralDirOffset); - result = UNKNOWN_ERROR; - goto bail; - } - - /* - * Loop through and read the central dir entries. - */ - LOGV("Scanning %d entries...\n", mEOCD.mTotalNumEntries); - int entry; - for (entry = 0; entry < mEOCD.mTotalNumEntries; entry++) { - ZipEntry* pEntry = new ZipEntry; - - result = pEntry->initFromCDE(mZipFp); - if (result != NO_ERROR) { - LOGD("initFromCDE failed\n"); - delete pEntry; - goto bail; - } - - mEntries.add(pEntry); - } - - - /* - * If all went well, we should now be back at the EOCD. - */ - { - unsigned char checkBuf[4]; - if (fread(checkBuf, 1, 4, mZipFp) != 4) { - LOGD("EOCD check read failed\n"); - result = INVALID_OPERATION; - goto bail; - } - if (ZipEntry::getLongLE(checkBuf) != EndOfCentralDir::kSignature) { - LOGD("EOCD read check failed\n"); - result = UNKNOWN_ERROR; - goto bail; - } - LOGV("+++ EOCD read check passed\n"); - } - -bail: - delete[] buf; - return result; -} - - -/* - * Add a new file to the archive. - * - * This requires creating and populating a ZipEntry structure, and copying - * the data into the file at the appropriate position. The "appropriate - * position" is the current location of the central directory, which we - * casually overwrite (we can put it back later). - * - * If we were concerned about safety, we would want to make all changes - * in a temp file and then overwrite the original after everything was - * safely written. Not really a concern for us. - */ -status_t ZipFile::addCommon(const char* fileName, const void* data, size_t size, - const char* storageName, int sourceType, int compressionMethod, - ZipEntry** ppEntry) -{ - ZipEntry* pEntry = NULL; - status_t result = NO_ERROR; - long lfhPosn, startPosn, endPosn, uncompressedLen; - FILE* inputFp = NULL; - unsigned long crc; - time_t modWhen; - - if (mReadOnly) - return INVALID_OPERATION; - - assert(compressionMethod == ZipEntry::kCompressDeflated || - compressionMethod == ZipEntry::kCompressStored); - - /* make sure we're in a reasonable state */ - assert(mZipFp != NULL); - assert(mEntries.size() == mEOCD.mTotalNumEntries); - - /* make sure it doesn't already exist */ - if (getEntryByName(storageName) != NULL) - return ALREADY_EXISTS; - - if (!data) { - inputFp = fopen(fileName, FILE_OPEN_RO); - if (inputFp == NULL) - return errnoToStatus(errno); - } - - if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) { - result = UNKNOWN_ERROR; - goto bail; - } - - pEntry = new ZipEntry; - pEntry->initNew(storageName, NULL); - - /* - * From here on out, failures are more interesting. - */ - mNeedCDRewrite = true; - - /* - * Write the LFH, even though it's still mostly blank. We need it - * as a place-holder. In theory the LFH isn't necessary, but in - * practice some utilities demand it. - */ - lfhPosn = ftell(mZipFp); - pEntry->mLFH.write(mZipFp); - startPosn = ftell(mZipFp); - - /* - * Copy the data in, possibly compressing it as we go. - */ - if (sourceType == ZipEntry::kCompressStored) { - if (compressionMethod == ZipEntry::kCompressDeflated) { - bool failed = false; - result = compressFpToFp(mZipFp, inputFp, data, size, &crc); - if (result != NO_ERROR) { - LOGD("compression failed, storing\n"); - failed = true; - } else { - /* - * Make sure it has compressed "enough". This probably ought - * to be set through an API call, but I don't expect our - * criteria to change over time. - */ - long src = inputFp ? ftell(inputFp) : size; - long dst = ftell(mZipFp) - startPosn; - if (dst + (dst / 10) > src) { - LOGD("insufficient compression (src=%ld dst=%ld), storing\n", - src, dst); - failed = true; - } - } - - if (failed) { - compressionMethod = ZipEntry::kCompressStored; - if (inputFp) rewind(inputFp); - fseek(mZipFp, startPosn, SEEK_SET); - /* fall through to kCompressStored case */ - } - } - /* handle "no compression" request, or failed compression from above */ - if (compressionMethod == ZipEntry::kCompressStored) { - if (inputFp) { - result = copyFpToFp(mZipFp, inputFp, &crc); - } else { - result = copyDataToFp(mZipFp, data, size, &crc); - } - if (result != NO_ERROR) { - // don't need to truncate; happens in CDE rewrite - LOGD("failed copying data in\n"); - goto bail; - } - } - - // currently seeked to end of file - uncompressedLen = inputFp ? ftell(inputFp) : size; - } else if (sourceType == ZipEntry::kCompressDeflated) { - /* we should support uncompressed-from-compressed, but it's not - * important right now */ - assert(compressionMethod == ZipEntry::kCompressDeflated); - - bool scanResult; - int method; - long compressedLen; - - scanResult = ZipUtils::examineGzip(inputFp, &method, &uncompressedLen, - &compressedLen, &crc); - if (!scanResult || method != ZipEntry::kCompressDeflated) { - LOGD("this isn't a deflated gzip file?"); - result = UNKNOWN_ERROR; - goto bail; - } - - result = copyPartialFpToFp(mZipFp, inputFp, compressedLen, NULL); - if (result != NO_ERROR) { - LOGD("failed copying gzip data in\n"); - goto bail; - } - } else { - assert(false); - result = UNKNOWN_ERROR; - goto bail; - } - - /* - * We could write the "Data Descriptor", but there doesn't seem to - * be any point since we're going to go back and write the LFH. - * - * Update file offsets. - */ - endPosn = ftell(mZipFp); // seeked to end of compressed data - - /* - * Success! Fill out new values. - */ - pEntry->setDataInfo(uncompressedLen, endPosn - startPosn, crc, - compressionMethod); - modWhen = getModTime(inputFp ? fileno(inputFp) : fileno(mZipFp)); - pEntry->setModWhen(modWhen); - pEntry->setLFHOffset(lfhPosn); - mEOCD.mNumEntries++; - mEOCD.mTotalNumEntries++; - mEOCD.mCentralDirSize = 0; // mark invalid; set by flush() - mEOCD.mCentralDirOffset = endPosn; - - /* - * Go back and write the LFH. - */ - if (fseek(mZipFp, lfhPosn, SEEK_SET) != 0) { - result = UNKNOWN_ERROR; - goto bail; - } - pEntry->mLFH.write(mZipFp); - - /* - * Add pEntry to the list. - */ - mEntries.add(pEntry); - if (ppEntry != NULL) - *ppEntry = pEntry; - pEntry = NULL; - -bail: - if (inputFp != NULL) - fclose(inputFp); - delete pEntry; - return result; -} - -/* - * Add an entry by copying it from another zip file. If "padding" is - * nonzero, the specified number of bytes will be added to the "extra" - * field in the header. - * - * If "ppEntry" is non-NULL, a pointer to the new entry will be returned. - */ -status_t ZipFile::add(const ZipFile* pSourceZip, const ZipEntry* pSourceEntry, - int padding, ZipEntry** ppEntry) -{ - ZipEntry* pEntry = NULL; - status_t result; - long lfhPosn, endPosn; - - if (mReadOnly) - return INVALID_OPERATION; - - /* make sure we're in a reasonable state */ - assert(mZipFp != NULL); - assert(mEntries.size() == mEOCD.mTotalNumEntries); - - if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) { - result = UNKNOWN_ERROR; - goto bail; - } - - pEntry = new ZipEntry; - if (pEntry == NULL) { - result = NO_MEMORY; - goto bail; - } - - result = pEntry->initFromExternal(pSourceZip, pSourceEntry); - if (result != NO_ERROR) - goto bail; - if (padding != 0) { - result = pEntry->addPadding(padding); - if (result != NO_ERROR) - goto bail; - } - - /* - * From here on out, failures are more interesting. - */ - mNeedCDRewrite = true; - - /* - * Write the LFH. Since we're not recompressing the data, we already - * have all of the fields filled out. - */ - lfhPosn = ftell(mZipFp); - pEntry->mLFH.write(mZipFp); - - /* - * Copy the data over. - * - * If the "has data descriptor" flag is set, we want to copy the DD - * fields as well. This is a fixed-size area immediately following - * the data. - */ - if (fseek(pSourceZip->mZipFp, pSourceEntry->getFileOffset(), SEEK_SET) != 0) - { - result = UNKNOWN_ERROR; - goto bail; - } - - off_t copyLen; - copyLen = pSourceEntry->getCompressedLen(); - if ((pSourceEntry->mLFH.mGPBitFlag & ZipEntry::kUsesDataDescr) != 0) - copyLen += ZipEntry::kDataDescriptorLen; - - if (copyPartialFpToFp(mZipFp, pSourceZip->mZipFp, copyLen, NULL) - != NO_ERROR) - { - LOGW("copy of '%s' failed\n", pEntry->mCDE.mFileName); - result = UNKNOWN_ERROR; - goto bail; - } - - /* - * Update file offsets. - */ - endPosn = ftell(mZipFp); - - /* - * Success! Fill out new values. - */ - pEntry->setLFHOffset(lfhPosn); // sets mCDE.mLocalHeaderRelOffset - mEOCD.mNumEntries++; - mEOCD.mTotalNumEntries++; - mEOCD.mCentralDirSize = 0; // mark invalid; set by flush() - mEOCD.mCentralDirOffset = endPosn; - - /* - * Add pEntry to the list. - */ - mEntries.add(pEntry); - if (ppEntry != NULL) - *ppEntry = pEntry; - pEntry = NULL; - - result = NO_ERROR; - -bail: - delete pEntry; - return result; -} - -/* - * Copy all of the bytes in "src" to "dst". - * - * On exit, "srcFp" will be seeked to the end of the file, and "dstFp" - * will be seeked immediately past the data. - */ -status_t ZipFile::copyFpToFp(FILE* dstFp, FILE* srcFp, unsigned long* pCRC32) -{ - unsigned char tmpBuf[32768]; - size_t count; - - *pCRC32 = crc32(0L, Z_NULL, 0); - - while (1) { - count = fread(tmpBuf, 1, sizeof(tmpBuf), srcFp); - if (ferror(srcFp) || ferror(dstFp)) - return errnoToStatus(errno); - if (count == 0) - break; - - *pCRC32 = crc32(*pCRC32, tmpBuf, count); - - if (fwrite(tmpBuf, 1, count, dstFp) != count) { - LOGD("fwrite %d bytes failed\n", (int) count); - return UNKNOWN_ERROR; - } - } - - return NO_ERROR; -} - -/* - * Copy all of the bytes in "src" to "dst". - * - * On exit, "dstFp" will be seeked immediately past the data. - */ -status_t ZipFile::copyDataToFp(FILE* dstFp, - const void* data, size_t size, unsigned long* pCRC32) -{ - size_t count; - - *pCRC32 = crc32(0L, Z_NULL, 0); - if (size > 0) { - *pCRC32 = crc32(*pCRC32, (const unsigned char*)data, size); - if (fwrite(data, 1, size, dstFp) != size) { - LOGD("fwrite %d bytes failed\n", (int) size); - return UNKNOWN_ERROR; - } - } - - return NO_ERROR; -} - -/* - * Copy some of the bytes in "src" to "dst". - * - * If "pCRC32" is NULL, the CRC will not be computed. - * - * On exit, "srcFp" will be seeked to the end of the file, and "dstFp" - * will be seeked immediately past the data just written. - */ -status_t ZipFile::copyPartialFpToFp(FILE* dstFp, FILE* srcFp, long length, - unsigned long* pCRC32) -{ - unsigned char tmpBuf[32768]; - size_t count; - - if (pCRC32 != NULL) - *pCRC32 = crc32(0L, Z_NULL, 0); - - while (length) { - long readSize; - - readSize = sizeof(tmpBuf); - if (readSize > length) - readSize = length; - - count = fread(tmpBuf, 1, readSize, srcFp); - if ((long) count != readSize) { // error or unexpected EOF - LOGD("fread %d bytes failed\n", (int) readSize); - return UNKNOWN_ERROR; - } - - if (pCRC32 != NULL) - *pCRC32 = crc32(*pCRC32, tmpBuf, count); - - if (fwrite(tmpBuf, 1, count, dstFp) != count) { - LOGD("fwrite %d bytes failed\n", (int) count); - return UNKNOWN_ERROR; - } - - length -= readSize; - } - - return NO_ERROR; -} - -/* - * Compress all of the data in "srcFp" and write it to "dstFp". - * - * On exit, "srcFp" will be seeked to the end of the file, and "dstFp" - * will be seeked immediately past the compressed data. - */ -status_t ZipFile::compressFpToFp(FILE* dstFp, FILE* srcFp, - const void* data, size_t size, unsigned long* pCRC32) -{ - status_t result = NO_ERROR; - const size_t kBufSize = 32768; - unsigned char* inBuf = NULL; - unsigned char* outBuf = NULL; - z_stream zstream; - bool atEof = false; // no feof() aviailable yet - unsigned long crc; - int zerr; - - /* - * Create an input buffer and an output buffer. - */ - inBuf = new unsigned char[kBufSize]; - outBuf = new unsigned char[kBufSize]; - if (inBuf == NULL || outBuf == NULL) { - result = NO_MEMORY; - goto bail; - } - - /* - * Initialize the zlib stream. - */ - memset(&zstream, 0, sizeof(zstream)); - zstream.zalloc = Z_NULL; - zstream.zfree = Z_NULL; - zstream.opaque = Z_NULL; - zstream.next_in = NULL; - zstream.avail_in = 0; - zstream.next_out = outBuf; - zstream.avail_out = kBufSize; - zstream.data_type = Z_UNKNOWN; - - zerr = deflateInit2(&zstream, Z_BEST_COMPRESSION, - Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY); - if (zerr != Z_OK) { - result = UNKNOWN_ERROR; - if (zerr == Z_VERSION_ERROR) { - LOGE("Installed zlib is not compatible with linked version (%s)\n", - ZLIB_VERSION); - } else { - LOGD("Call to deflateInit2 failed (zerr=%d)\n", zerr); - } - goto bail; - } - - crc = crc32(0L, Z_NULL, 0); - - /* - * Loop while we have data. - */ - do { - size_t getSize; - int flush; - - /* only read if the input buffer is empty */ - if (zstream.avail_in == 0 && !atEof) { - LOGV("+++ reading %d bytes\n", (int)kBufSize); - if (data) { - getSize = size > kBufSize ? kBufSize : size; - memcpy(inBuf, data, getSize); - data = ((const char*)data) + getSize; - size -= getSize; - } else { - getSize = fread(inBuf, 1, kBufSize, srcFp); - if (ferror(srcFp)) { - LOGD("deflate read failed (errno=%d)\n", errno); - goto z_bail; - } - } - if (getSize < kBufSize) { - LOGV("+++ got %d bytes, EOF reached\n", - (int)getSize); - atEof = true; - } - - crc = crc32(crc, inBuf, getSize); - - zstream.next_in = inBuf; - zstream.avail_in = getSize; - } - - if (atEof) - flush = Z_FINISH; /* tell zlib that we're done */ - else - flush = Z_NO_FLUSH; /* more to come! */ - - zerr = deflate(&zstream, flush); - if (zerr != Z_OK && zerr != Z_STREAM_END) { - LOGD("zlib deflate call failed (zerr=%d)\n", zerr); - result = UNKNOWN_ERROR; - goto z_bail; - } - - /* write when we're full or when we're done */ - if (zstream.avail_out == 0 || - (zerr == Z_STREAM_END && zstream.avail_out != (uInt) kBufSize)) - { - LOGV("+++ writing %d bytes\n", (int) (zstream.next_out - outBuf)); - if (fwrite(outBuf, 1, zstream.next_out - outBuf, dstFp) != - (size_t)(zstream.next_out - outBuf)) - { - LOGD("write %d failed in deflate\n", - (int) (zstream.next_out - outBuf)); - goto z_bail; - } - - zstream.next_out = outBuf; - zstream.avail_out = kBufSize; - } - } while (zerr == Z_OK); - - assert(zerr == Z_STREAM_END); /* other errors should've been caught */ - - *pCRC32 = crc; - -z_bail: - deflateEnd(&zstream); /* free up any allocated structures */ - -bail: - delete[] inBuf; - delete[] outBuf; - - return result; -} - -/* - * Mark an entry as deleted. - * - * We will eventually need to crunch the file down, but if several files - * are being removed (perhaps as part of an "update" process) we can make - * things considerably faster by deferring the removal to "flush" time. - */ -status_t ZipFile::remove(ZipEntry* pEntry) -{ - /* - * Should verify that pEntry is actually part of this archive, and - * not some stray ZipEntry from a different file. - */ - - /* mark entry as deleted, and mark archive as dirty */ - pEntry->setDeleted(); - mNeedCDRewrite = true; - return NO_ERROR; -} - -/* - * Flush any pending writes. - * - * In particular, this will crunch out deleted entries, and write the - * Central Directory and EOCD if we have stomped on them. - */ -status_t ZipFile::flush(void) -{ - status_t result = NO_ERROR; - long eocdPosn; - int i, count; - - if (mReadOnly) - return INVALID_OPERATION; - if (!mNeedCDRewrite) - return NO_ERROR; - - assert(mZipFp != NULL); - - result = crunchArchive(); - if (result != NO_ERROR) - return result; - - if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) - return UNKNOWN_ERROR; - - count = mEntries.size(); - for (i = 0; i < count; i++) { - ZipEntry* pEntry = mEntries[i]; - pEntry->mCDE.write(mZipFp); - } - - eocdPosn = ftell(mZipFp); - mEOCD.mCentralDirSize = eocdPosn - mEOCD.mCentralDirOffset; - - mEOCD.write(mZipFp); - - /* - * If we had some stuff bloat up during compression and get replaced - * with plain files, or if we deleted some entries, there's a lot - * of wasted space at the end of the file. Remove it now. - */ - if (ftruncate(fileno(mZipFp), ftell(mZipFp)) != 0) { - LOGW("ftruncate failed %ld: %s\n", ftell(mZipFp), strerror(errno)); - // not fatal - } - - /* should we clear the "newly added" flag in all entries now? */ - - mNeedCDRewrite = false; - return NO_ERROR; -} - -/* - * Crunch deleted files out of an archive by shifting the later files down. - * - * Because we're not using a temp file, we do the operation inside the - * current file. - */ -status_t ZipFile::crunchArchive(void) -{ - status_t result = NO_ERROR; - int i, count; - long delCount, adjust; - -#if 0 - printf("CONTENTS:\n"); - for (i = 0; i < (int) mEntries.size(); i++) { - printf(" %d: lfhOff=%ld del=%d\n", - i, mEntries[i]->getLFHOffset(), mEntries[i]->getDeleted()); - } - printf(" END is %ld\n", (long) mEOCD.mCentralDirOffset); -#endif - - /* - * Roll through the set of files, shifting them as appropriate. We - * could probably get a slight performance improvement by sliding - * multiple files down at once (because we could use larger reads - * when operating on batches of small files), but it's not that useful. - */ - count = mEntries.size(); - delCount = adjust = 0; - for (i = 0; i < count; i++) { - ZipEntry* pEntry = mEntries[i]; - long span; - - if (pEntry->getLFHOffset() != 0) { - long nextOffset; - - /* Get the length of this entry by finding the offset - * of the next entry. Directory entries don't have - * file offsets, so we need to find the next non-directory - * entry. - */ - nextOffset = 0; - for (int ii = i+1; nextOffset == 0 && ii < count; ii++) - nextOffset = mEntries[ii]->getLFHOffset(); - if (nextOffset == 0) - nextOffset = mEOCD.mCentralDirOffset; - span = nextOffset - pEntry->getLFHOffset(); - - assert(span >= ZipEntry::LocalFileHeader::kLFHLen); - } else { - /* This is a directory entry. It doesn't have - * any actual file contents, so there's no need to - * move anything. - */ - span = 0; - } - - //printf("+++ %d: off=%ld span=%ld del=%d [count=%d]\n", - // i, pEntry->getLFHOffset(), span, pEntry->getDeleted(), count); - - if (pEntry->getDeleted()) { - adjust += span; - delCount++; - - delete pEntry; - mEntries.removeAt(i); - - /* adjust loop control */ - count--; - i--; - } else if (span != 0 && adjust > 0) { - /* shuffle this entry back */ - //printf("+++ Shuffling '%s' back %ld\n", - // pEntry->getFileName(), adjust); - result = filemove(mZipFp, pEntry->getLFHOffset() - adjust, - pEntry->getLFHOffset(), span); - if (result != NO_ERROR) { - /* this is why you use a temp file */ - LOGE("error during crunch - archive is toast\n"); - return result; - } - - pEntry->setLFHOffset(pEntry->getLFHOffset() - adjust); - } - } - - /* - * Fix EOCD info. We have to wait until the end to do some of this - * because we use mCentralDirOffset to determine "span" for the - * last entry. - */ - mEOCD.mCentralDirOffset -= adjust; - mEOCD.mNumEntries -= delCount; - mEOCD.mTotalNumEntries -= delCount; - mEOCD.mCentralDirSize = 0; // mark invalid; set by flush() - - assert(mEOCD.mNumEntries == mEOCD.mTotalNumEntries); - assert(mEOCD.mNumEntries == count); - - return result; -} - -/* - * Works like memmove(), but on pieces of a file. - */ -status_t ZipFile::filemove(FILE* fp, off_t dst, off_t src, size_t n) -{ - if (dst == src || n <= 0) - return NO_ERROR; - - unsigned char readBuf[32768]; - - if (dst < src) { - /* shift stuff toward start of file; must read from start */ - while (n != 0) { - size_t getSize = sizeof(readBuf); - if (getSize > n) - getSize = n; - - if (fseek(fp, (long) src, SEEK_SET) != 0) { - LOGD("filemove src seek %ld failed\n", (long) src); - return UNKNOWN_ERROR; - } - - if (fread(readBuf, 1, getSize, fp) != getSize) { - LOGD("filemove read %ld off=%ld failed\n", - (long) getSize, (long) src); - return UNKNOWN_ERROR; - } - - if (fseek(fp, (long) dst, SEEK_SET) != 0) { - LOGD("filemove dst seek %ld failed\n", (long) dst); - return UNKNOWN_ERROR; - } - - if (fwrite(readBuf, 1, getSize, fp) != getSize) { - LOGD("filemove write %ld off=%ld failed\n", - (long) getSize, (long) dst); - return UNKNOWN_ERROR; - } - - src += getSize; - dst += getSize; - n -= getSize; - } - } else { - /* shift stuff toward end of file; must read from end */ - assert(false); // write this someday, maybe - return UNKNOWN_ERROR; - } - - return NO_ERROR; -} - - -/* - * Get the modification time from a file descriptor. - */ -time_t ZipFile::getModTime(int fd) -{ - struct stat sb; - - if (fstat(fd, &sb) < 0) { - LOGD("HEY: fstat on fd %d failed\n", fd); - return (time_t) -1; - } - - return sb.st_mtime; -} - - -#if 0 /* this is a bad idea */ -/* - * Get a copy of the Zip file descriptor. - * - * We don't allow this if the file was opened read-write because we tend - * to leave the file contents in an uncertain state between calls to - * flush(). The duplicated file descriptor should only be valid for reads. - */ -int ZipFile::getZipFd(void) const -{ - if (!mReadOnly) - return INVALID_OPERATION; - assert(mZipFp != NULL); - - int fd; - fd = dup(fileno(mZipFp)); - if (fd < 0) { - LOGD("didn't work, errno=%d\n", errno); - } - - return fd; -} -#endif - - -#if 0 -/* - * Expand data. - */ -bool ZipFile::uncompress(const ZipEntry* pEntry, void* buf) const -{ - return false; -} -#endif - -// free the memory when you're done -void* ZipFile::uncompress(const ZipEntry* entry) -{ - size_t unlen = entry->getUncompressedLen(); - size_t clen = entry->getCompressedLen(); - - void* buf = malloc(unlen); - if (buf == NULL) { - return NULL; - } - - fseek(mZipFp, 0, SEEK_SET); - - off_t offset = entry->getFileOffset(); - if (fseek(mZipFp, offset, SEEK_SET) != 0) { - goto bail; - } - - switch (entry->getCompressionMethod()) - { - case ZipEntry::kCompressStored: { - ssize_t amt = fread(buf, 1, unlen, mZipFp); - if (amt != (ssize_t)unlen) { - goto bail; - } -#if 0 - printf("data...\n"); - const unsigned char* p = (unsigned char*)buf; - const unsigned char* end = p+unlen; - for (int i=0; i<32 && p < end; i++) { - printf("0x%08x ", (int)(offset+(i*0x10))); - for (int j=0; j<0x10 && p < end; j++) { - printf(" %02x", *p); - p++; - } - printf("\n"); - } -#endif - - } - break; - case ZipEntry::kCompressDeflated: { - if (!ZipUtils::inflateToBuffer(mZipFp, buf, unlen, clen)) { - goto bail; - } - } - break; - default: - goto bail; - } - return buf; - -bail: - free(buf); - return NULL; -} - - -/* - * =========================================================================== - * ZipFile::EndOfCentralDir - * =========================================================================== - */ - -/* - * Read the end-of-central-dir fields. - * - * "buf" should be positioned at the EOCD signature, and should contain - * the entire EOCD area including the comment. - */ -status_t ZipFile::EndOfCentralDir::readBuf(const unsigned char* buf, int len) -{ - /* don't allow re-use */ - assert(mComment == NULL); - - if (len < kEOCDLen) { - /* looks like ZIP file got truncated */ - LOGD(" Zip EOCD: expected >= %d bytes, found %d\n", - kEOCDLen, len); - return INVALID_OPERATION; - } - - /* this should probably be an assert() */ - if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) - return UNKNOWN_ERROR; - - mDiskNumber = ZipEntry::getShortLE(&buf[0x04]); - mDiskWithCentralDir = ZipEntry::getShortLE(&buf[0x06]); - mNumEntries = ZipEntry::getShortLE(&buf[0x08]); - mTotalNumEntries = ZipEntry::getShortLE(&buf[0x0a]); - mCentralDirSize = ZipEntry::getLongLE(&buf[0x0c]); - mCentralDirOffset = ZipEntry::getLongLE(&buf[0x10]); - mCommentLen = ZipEntry::getShortLE(&buf[0x14]); - - // TODO: validate mCentralDirOffset - - if (mCommentLen > 0) { - if (kEOCDLen + mCommentLen > len) { - LOGD("EOCD(%d) + comment(%d) exceeds len (%d)\n", - kEOCDLen, mCommentLen, len); - return UNKNOWN_ERROR; - } - mComment = new unsigned char[mCommentLen]; - memcpy(mComment, buf + kEOCDLen, mCommentLen); - } - - return NO_ERROR; -} - -/* - * Write an end-of-central-directory section. - */ -status_t ZipFile::EndOfCentralDir::write(FILE* fp) -{ - unsigned char buf[kEOCDLen]; - - ZipEntry::putLongLE(&buf[0x00], kSignature); - ZipEntry::putShortLE(&buf[0x04], mDiskNumber); - ZipEntry::putShortLE(&buf[0x06], mDiskWithCentralDir); - ZipEntry::putShortLE(&buf[0x08], mNumEntries); - ZipEntry::putShortLE(&buf[0x0a], mTotalNumEntries); - ZipEntry::putLongLE(&buf[0x0c], mCentralDirSize); - ZipEntry::putLongLE(&buf[0x10], mCentralDirOffset); - ZipEntry::putShortLE(&buf[0x14], mCommentLen); - - if (fwrite(buf, 1, kEOCDLen, fp) != kEOCDLen) - return UNKNOWN_ERROR; - if (mCommentLen > 0) { - assert(mComment != NULL); - if (fwrite(mComment, mCommentLen, 1, fp) != mCommentLen) - return UNKNOWN_ERROR; - } - - return NO_ERROR; -} - -/* - * Dump the contents of an EndOfCentralDir object. - */ -void ZipFile::EndOfCentralDir::dump(void) const -{ - LOGD(" EndOfCentralDir contents:\n"); - LOGD(" diskNum=%u diskWCD=%u numEnt=%u totalNumEnt=%u\n", - mDiskNumber, mDiskWithCentralDir, mNumEntries, mTotalNumEntries); - LOGD(" centDirSize=%lu centDirOff=%lu commentLen=%u\n", - mCentralDirSize, mCentralDirOffset, mCommentLen); -} - diff --git a/libs/utils/ZipFileCRO.cpp b/libs/utils/ZipFileCRO.cpp deleted file mode 100644 index d312dafad..000000000 --- a/libs/utils/ZipFileCRO.cpp +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "utils/ZipFileCRO.h" -#include "utils/ZipFileRO.h" - -using namespace android; - -ZipFileCRO ZipFileXRO_open(const char* path) { - ZipFileRO* zip = new ZipFileRO(); - if (zip->open(path) == NO_ERROR) { - return (ZipFileCRO)zip; - } - return NULL; -} - -void ZipFileCRO_destroy(ZipFileCRO zipToken) { - ZipFileRO* zip = (ZipFileRO*)zipToken; - delete zip; -} - -ZipEntryCRO ZipFileCRO_findEntryByName(ZipFileCRO zipToken, - const char* fileName) { - ZipFileRO* zip = (ZipFileRO*)zipToken; - return (ZipEntryCRO)zip->findEntryByName(fileName); -} - -bool ZipFileCRO_getEntryInfo(ZipFileCRO zipToken, ZipEntryRO entryToken, - int* pMethod, long* pUncompLen, - long* pCompLen, off_t* pOffset, long* pModWhen, long* pCrc32) { - ZipFileRO* zip = (ZipFileRO*)zipToken; - ZipEntryRO entry = (ZipEntryRO)entryToken; - return zip->getEntryInfo(entry, pMethod, pUncompLen, pCompLen, pOffset, - pModWhen, pCrc32); -} - -bool ZipFileCRO_uncompressEntry(ZipFileCRO zipToken, ZipEntryRO entryToken, int fd) { - ZipFileRO* zip = (ZipFileRO*)zipToken; - ZipEntryRO entry = (ZipEntryRO)entryToken; - return zip->uncompressEntry(entry, fd); -} diff --git a/libs/utils/ZipFileRO.cpp b/libs/utils/ZipFileRO.cpp deleted file mode 100644 index ae8c71972..000000000 --- a/libs/utils/ZipFileRO.cpp +++ /dev/null @@ -1,724 +0,0 @@ -/* - * 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. - */ - -// -// Read-only access to Zip archives, with minimal heap allocation. -// -#define LOG_TAG "zipro" -//#define LOG_NDEBUG 0 -#include "utils/ZipFileRO.h" -#include "utils/Log.h" -#include "utils/misc.h" - -#include - -#include -#include -#include -#include - -using namespace android; - -/* - * Zip file constants. - */ -#define kEOCDSignature 0x06054b50 -#define kEOCDLen 22 -#define kEOCDNumEntries 8 // offset to #of entries in file -#define kEOCDFileOffset 16 // offset to central directory - -#define kMaxCommentLen 65535 // longest possible in ushort -#define kMaxEOCDSearch (kMaxCommentLen + kEOCDLen) - -#define kLFHSignature 0x04034b50 -#define kLFHLen 30 // excluding variable-len fields -#define kLFHNameLen 26 // offset to filename length -#define kLFHExtraLen 28 // offset to extra length - -#define kCDESignature 0x02014b50 -#define kCDELen 46 // excluding variable-len fields -#define kCDEMethod 10 // offset to compression method -#define kCDEModWhen 12 // offset to modification timestamp -#define kCDECRC 16 // offset to entry CRC -#define kCDECompLen 20 // offset to compressed length -#define kCDEUncompLen 24 // offset to uncompressed length -#define kCDENameLen 28 // offset to filename length -#define kCDEExtraLen 30 // offset to extra length -#define kCDECommentLen 32 // offset to comment length -#define kCDELocalOffset 42 // offset to local hdr - -/* - * The values we return for ZipEntryRO use 0 as an invalid value, so we - * want to adjust the hash table index by a fixed amount. Using a large - * value helps insure that people don't mix & match arguments, e.g. to - * findEntryByIndex(). - */ -#define kZipEntryAdj 10000 - -/* - * Convert a ZipEntryRO to a hash table index, verifying that it's in a - * valid range. - */ -int ZipFileRO::entryToIndex(const ZipEntryRO entry) const -{ - long ent = ((long) entry) - kZipEntryAdj; - if (ent < 0 || ent >= mHashTableSize || mHashTable[ent].name == NULL) { - LOGW("Invalid ZipEntryRO %p (%ld)\n", entry, ent); - return -1; - } - return ent; -} - - -/* - * Open the specified file read-only. We memory-map the entire thing and - * close the file before returning. - */ -status_t ZipFileRO::open(const char* zipFileName) -{ - int fd = -1; - off_t length; - - assert(mFileMap == NULL); - - /* - * Open and map the specified file. - */ - fd = ::open(zipFileName, O_RDONLY); - if (fd < 0) { - LOGW("Unable to open zip '%s': %s\n", zipFileName, strerror(errno)); - return NAME_NOT_FOUND; - } - - length = lseek(fd, 0, SEEK_END); - if (length < 0) { - close(fd); - return UNKNOWN_ERROR; - } - - mFileMap = new FileMap(); - if (mFileMap == NULL) { - close(fd); - return NO_MEMORY; - } - if (!mFileMap->create(zipFileName, fd, 0, length, true)) { - LOGW("Unable to map '%s': %s\n", zipFileName, strerror(errno)); - close(fd); - return UNKNOWN_ERROR; - } - - mFd = fd; - - /* - * Got it mapped, verify it and create data structures for fast access. - */ - if (!parseZipArchive()) { - mFileMap->release(); - mFileMap = NULL; - return UNKNOWN_ERROR; - } - - return OK; -} - -/* - * Parse the Zip archive, verifying its contents and initializing internal - * data structures. - */ -bool ZipFileRO::parseZipArchive(void) -{ -#define CHECK_OFFSET(_off) { \ - if ((unsigned int) (_off) >= maxOffset) { \ - LOGE("ERROR: bad offset %u (max %d): %s\n", \ - (unsigned int) (_off), maxOffset, #_off); \ - goto bail; \ - } \ - } - const unsigned char* basePtr = (const unsigned char*)mFileMap->getDataPtr(); - const unsigned char* ptr; - size_t length = mFileMap->getDataLength(); - bool result = false; - unsigned int i, numEntries, cdOffset; - unsigned int val; - - /* - * The first 4 bytes of the file will either be the local header - * signature for the first file (kLFHSignature) or, if the archive doesn't - * have any files in it, the end-of-central-directory signature - * (kEOCDSignature). - */ - val = get4LE(basePtr); - if (val == kEOCDSignature) { - LOGI("Found Zip archive, but it looks empty\n"); - goto bail; - } else if (val != kLFHSignature) { - LOGV("Not a Zip archive (found 0x%08x)\n", val); - goto bail; - } - - /* - * Find the EOCD. We'll find it immediately unless they have a file - * comment. - */ - ptr = basePtr + length - kEOCDLen; - - while (ptr >= basePtr) { - if (*ptr == (kEOCDSignature & 0xff) && get4LE(ptr) == kEOCDSignature) - break; - ptr--; - } - if (ptr < basePtr) { - LOGI("Could not find end-of-central-directory in Zip\n"); - goto bail; - } - - /* - * There are two interesting items in the EOCD block: the number of - * entries in the file, and the file offset of the start of the - * central directory. - * - * (There's actually a count of the #of entries in this file, and for - * all files which comprise a spanned archive, but for our purposes - * we're only interested in the current file. Besides, we expect the - * two to be equivalent for our stuff.) - */ - numEntries = get2LE(ptr + kEOCDNumEntries); - cdOffset = get4LE(ptr + kEOCDFileOffset); - - /* valid offsets are [0,EOCD] */ - unsigned int maxOffset; - maxOffset = (ptr - basePtr) +1; - - LOGV("+++ numEntries=%d cdOffset=%d\n", numEntries, cdOffset); - if (numEntries == 0 || cdOffset >= length) { - LOGW("Invalid entries=%d offset=%d (len=%zd)\n", - numEntries, cdOffset, length); - goto bail; - } - - /* - * Create hash table. We have a minimum 75% load factor, possibly as - * low as 50% after we round off to a power of 2. - */ - mNumEntries = numEntries; - mHashTableSize = roundUpPower2(1 + ((numEntries * 4) / 3)); - mHashTable = (HashEntry*) calloc(1, sizeof(HashEntry) * mHashTableSize); - - /* - * Walk through the central directory, adding entries to the hash - * table. - */ - ptr = basePtr + cdOffset; - for (i = 0; i < numEntries; i++) { - unsigned int fileNameLen, extraLen, commentLen, localHdrOffset; - const unsigned char* localHdr; - unsigned int hash; - - if (get4LE(ptr) != kCDESignature) { - LOGW("Missed a central dir sig (at %d)\n", i); - goto bail; - } - if (ptr + kCDELen > basePtr + length) { - LOGW("Ran off the end (at %d)\n", i); - goto bail; - } - - localHdrOffset = get4LE(ptr + kCDELocalOffset); - CHECK_OFFSET(localHdrOffset); - fileNameLen = get2LE(ptr + kCDENameLen); - extraLen = get2LE(ptr + kCDEExtraLen); - commentLen = get2LE(ptr + kCDECommentLen); - - //LOGV("+++ %d: localHdr=%d fnl=%d el=%d cl=%d\n", - // i, localHdrOffset, fileNameLen, extraLen, commentLen); - //LOGV(" '%.*s'\n", fileNameLen, ptr + kCDELen); - - /* add the CDE filename to the hash table */ - hash = computeHash((const char*)ptr + kCDELen, fileNameLen); - addToHash((const char*)ptr + kCDELen, fileNameLen, hash); - - localHdr = basePtr + localHdrOffset; - if (get4LE(localHdr) != kLFHSignature) { - LOGW("Bad offset to local header: %d (at %d)\n", - localHdrOffset, i); - goto bail; - } - - ptr += kCDELen + fileNameLen + extraLen + commentLen; - CHECK_OFFSET(ptr - basePtr); - } - - result = true; - -bail: - return result; -#undef CHECK_OFFSET -} - - -/* - * Simple string hash function for non-null-terminated strings. - */ -/*static*/ unsigned int ZipFileRO::computeHash(const char* str, int len) -{ - unsigned int hash = 0; - - while (len--) - hash = hash * 31 + *str++; - - return hash; -} - -/* - * Add a new entry to the hash table. - */ -void ZipFileRO::addToHash(const char* str, int strLen, unsigned int hash) -{ - int ent = hash & (mHashTableSize-1); - - /* - * We over-allocate the table, so we're guaranteed to find an empty slot. - */ - while (mHashTable[ent].name != NULL) - ent = (ent + 1) & (mHashTableSize-1); - - mHashTable[ent].name = str; - mHashTable[ent].nameLen = strLen; -} - -/* - * Find a matching entry. - * - * Returns 0 if not found. - */ -ZipEntryRO ZipFileRO::findEntryByName(const char* fileName) const -{ - int nameLen = strlen(fileName); - unsigned int hash = computeHash(fileName, nameLen); - int ent = hash & (mHashTableSize-1); - - while (mHashTable[ent].name != NULL) { - if (mHashTable[ent].nameLen == nameLen && - memcmp(mHashTable[ent].name, fileName, nameLen) == 0) - { - /* match */ - return (ZipEntryRO) (ent + kZipEntryAdj); - } - - ent = (ent + 1) & (mHashTableSize-1); - } - - return NULL; -} - -/* - * Find the Nth entry. - * - * This currently involves walking through the sparse hash table, counting - * non-empty entries. If we need to speed this up we can either allocate - * a parallel lookup table or (perhaps better) provide an iterator interface. - */ -ZipEntryRO ZipFileRO::findEntryByIndex(int idx) const -{ - if (idx < 0 || idx >= mNumEntries) { - LOGW("Invalid index %d\n", idx); - return NULL; - } - - for (int ent = 0; ent < mHashTableSize; ent++) { - if (mHashTable[ent].name != NULL) { - if (idx-- == 0) - return (ZipEntryRO) (ent + kZipEntryAdj); - } - } - - return NULL; -} - -/* - * Get the useful fields from the zip entry. - * - * Returns "false" if the offsets to the fields or the contents of the fields - * appear to be bogus. - */ -bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, long* pUncompLen, - long* pCompLen, off_t* pOffset, long* pModWhen, long* pCrc32) const -{ - int ent = entryToIndex(entry); - if (ent < 0) - return false; - - /* - * Recover the start of the central directory entry from the filename - * pointer. - */ - const unsigned char* basePtr = (const unsigned char*)mFileMap->getDataPtr(); - const unsigned char* ptr = (const unsigned char*) mHashTable[ent].name; - size_t zipLength = mFileMap->getDataLength(); - - ptr -= kCDELen; - - int method = get2LE(ptr + kCDEMethod); - if (pMethod != NULL) - *pMethod = method; - - if (pModWhen != NULL) - *pModWhen = get4LE(ptr + kCDEModWhen); - if (pCrc32 != NULL) - *pCrc32 = get4LE(ptr + kCDECRC); - - /* - * We need to make sure that the lengths are not so large that somebody - * trying to map the compressed or uncompressed data runs off the end - * of the mapped region. - */ - unsigned long localHdrOffset = get4LE(ptr + kCDELocalOffset); - if (localHdrOffset + kLFHLen >= zipLength) { - LOGE("ERROR: bad local hdr offset in zip\n"); - return false; - } - const unsigned char* localHdr = basePtr + localHdrOffset; - off_t dataOffset = localHdrOffset + kLFHLen - + get2LE(localHdr + kLFHNameLen) + get2LE(localHdr + kLFHExtraLen); - if ((unsigned long) dataOffset >= zipLength) { - LOGE("ERROR: bad data offset in zip\n"); - return false; - } - - if (pCompLen != NULL) { - *pCompLen = get4LE(ptr + kCDECompLen); - if (*pCompLen < 0 || (size_t)(dataOffset + *pCompLen) >= zipLength) { - LOGE("ERROR: bad compressed length in zip\n"); - return false; - } - } - if (pUncompLen != NULL) { - *pUncompLen = get4LE(ptr + kCDEUncompLen); - if (*pUncompLen < 0) { - LOGE("ERROR: negative uncompressed length in zip\n"); - return false; - } - if (method == kCompressStored && - (size_t)(dataOffset + *pUncompLen) >= zipLength) - { - LOGE("ERROR: bad uncompressed length in zip\n"); - return false; - } - } - - if (pOffset != NULL) { - *pOffset = dataOffset; - } - return true; -} - -/* - * Copy the entry's filename to the buffer. - */ -int ZipFileRO::getEntryFileName(ZipEntryRO entry, char* buffer, int bufLen) - const -{ - int ent = entryToIndex(entry); - if (ent < 0) - return -1; - - int nameLen = mHashTable[ent].nameLen; - if (bufLen < nameLen+1) - return nameLen+1; - - memcpy(buffer, mHashTable[ent].name, nameLen); - buffer[nameLen] = '\0'; - return 0; -} - -/* - * Create a new FileMap object that spans the data in "entry". - */ -FileMap* ZipFileRO::createEntryFileMap(ZipEntryRO entry) const -{ - /* - * TODO: the efficient way to do this is to modify FileMap to allow - * sub-regions of a file to be mapped. A reference-counting scheme - * can manage the base memory mapping. For now, we just create a brand - * new mapping off of the Zip archive file descriptor. - */ - - FileMap* newMap; - long compLen; - off_t offset; - - if (!getEntryInfo(entry, NULL, NULL, &compLen, &offset, NULL, NULL)) - return NULL; - - newMap = new FileMap(); - if (!newMap->create(mFileMap->getFileName(), mFd, offset, compLen, true)) { - newMap->release(); - return NULL; - } - - return newMap; -} - -/* - * Uncompress an entry, in its entirety, into the provided output buffer. - * - * This doesn't verify the data's CRC, which might be useful for - * uncompressed data. The caller should be able to manage it. - */ -bool ZipFileRO::uncompressEntry(ZipEntryRO entry, void* buffer) const -{ - const int kSequentialMin = 32768; - bool result = false; - int ent = entryToIndex(entry); - if (ent < 0) - return -1; - - const unsigned char* basePtr = (const unsigned char*)mFileMap->getDataPtr(); - int method; - long uncompLen, compLen; - off_t offset; - - getEntryInfo(entry, &method, &uncompLen, &compLen, &offset, NULL, NULL); - - /* - * Experiment with madvise hint. When we want to uncompress a file, - * we pull some stuff out of the central dir entry and then hit a - * bunch of compressed or uncompressed data sequentially. The CDE - * visit will cause a limited amount of read-ahead because it's at - * the end of the file. We could end up doing lots of extra disk - * access if the file we're prying open is small. Bottom line is we - * probably don't want to turn MADV_SEQUENTIAL on and leave it on. - * - * So, if the compressed size of the file is above a certain minimum - * size, temporarily boost the read-ahead in the hope that the extra - * pair of system calls are negated by a reduction in page faults. - */ - if (compLen > kSequentialMin) - mFileMap->advise(FileMap::SEQUENTIAL); - - if (method == kCompressStored) { - memcpy(buffer, basePtr + offset, uncompLen); - } else { - if (!inflateBuffer(buffer, basePtr + offset, uncompLen, compLen)) - goto bail; - } - - if (compLen > kSequentialMin) - mFileMap->advise(FileMap::NORMAL); - - result = true; - -bail: - return result; -} - -/* - * Uncompress an entry, in its entirety, to an open file descriptor. - * - * This doesn't verify the data's CRC, but probably should. - */ -bool ZipFileRO::uncompressEntry(ZipEntryRO entry, int fd) const -{ - bool result = false; - int ent = entryToIndex(entry); - if (ent < 0) - return -1; - - const unsigned char* basePtr = (const unsigned char*)mFileMap->getDataPtr(); - int method; - long uncompLen, compLen; - off_t offset; - - getEntryInfo(entry, &method, &uncompLen, &compLen, &offset, NULL, NULL); - - if (method == kCompressStored) { - ssize_t actual; - - actual = write(fd, basePtr + offset, uncompLen); - if (actual < 0) { - LOGE("Write failed: %s\n", strerror(errno)); - goto bail; - } else if (actual != uncompLen) { - LOGE("Partial write during uncompress (%d of %ld)\n", - (int)actual, uncompLen); - goto bail; - } else { - LOGI("+++ successful write\n"); - } - } else { - if (!inflateBuffer(fd, basePtr+offset, uncompLen, compLen)) - goto bail; - } - - result = true; - -bail: - return result; -} - -/* - * Uncompress "deflate" data from one buffer to another. - */ -/*static*/ bool ZipFileRO::inflateBuffer(void* outBuf, const void* inBuf, - long uncompLen, long compLen) -{ - bool result = false; - z_stream zstream; - int zerr; - - /* - * Initialize the zlib stream struct. - */ - memset(&zstream, 0, sizeof(zstream)); - zstream.zalloc = Z_NULL; - zstream.zfree = Z_NULL; - zstream.opaque = Z_NULL; - zstream.next_in = (Bytef*)inBuf; - zstream.avail_in = compLen; - zstream.next_out = (Bytef*) outBuf; - zstream.avail_out = uncompLen; - zstream.data_type = Z_UNKNOWN; - - /* - * Use the undocumented "negative window bits" feature to tell zlib - * that there's no zlib header waiting for it. - */ - zerr = inflateInit2(&zstream, -MAX_WBITS); - if (zerr != Z_OK) { - if (zerr == Z_VERSION_ERROR) { - LOGE("Installed zlib is not compatible with linked version (%s)\n", - ZLIB_VERSION); - } else { - LOGE("Call to inflateInit2 failed (zerr=%d)\n", zerr); - } - goto bail; - } - - /* - * Expand data. - */ - zerr = inflate(&zstream, Z_FINISH); - if (zerr != Z_STREAM_END) { - LOGW("Zip inflate failed, zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)\n", - zerr, zstream.next_in, zstream.avail_in, - zstream.next_out, zstream.avail_out); - goto z_bail; - } - - /* paranoia */ - if ((long) zstream.total_out != uncompLen) { - LOGW("Size mismatch on inflated file (%ld vs %ld)\n", - zstream.total_out, uncompLen); - goto z_bail; - } - - result = true; - -z_bail: - inflateEnd(&zstream); /* free up any allocated structures */ - -bail: - return result; -} - -/* - * Uncompress "deflate" data from one buffer to an open file descriptor. - */ -/*static*/ bool ZipFileRO::inflateBuffer(int fd, const void* inBuf, - long uncompLen, long compLen) -{ - bool result = false; - const int kWriteBufSize = 32768; - unsigned char writeBuf[kWriteBufSize]; - z_stream zstream; - int zerr; - - /* - * Initialize the zlib stream struct. - */ - memset(&zstream, 0, sizeof(zstream)); - zstream.zalloc = Z_NULL; - zstream.zfree = Z_NULL; - zstream.opaque = Z_NULL; - zstream.next_in = (Bytef*)inBuf; - zstream.avail_in = compLen; - zstream.next_out = (Bytef*) writeBuf; - zstream.avail_out = sizeof(writeBuf); - zstream.data_type = Z_UNKNOWN; - - /* - * Use the undocumented "negative window bits" feature to tell zlib - * that there's no zlib header waiting for it. - */ - zerr = inflateInit2(&zstream, -MAX_WBITS); - if (zerr != Z_OK) { - if (zerr == Z_VERSION_ERROR) { - LOGE("Installed zlib is not compatible with linked version (%s)\n", - ZLIB_VERSION); - } else { - LOGE("Call to inflateInit2 failed (zerr=%d)\n", zerr); - } - goto bail; - } - - /* - * Loop while we have more to do. - */ - do { - /* - * Expand data. - */ - zerr = inflate(&zstream, Z_NO_FLUSH); - if (zerr != Z_OK && zerr != Z_STREAM_END) { - LOGW("zlib inflate: zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)\n", - zerr, zstream.next_in, zstream.avail_in, - zstream.next_out, zstream.avail_out); - goto z_bail; - } - - /* write when we're full or when we're done */ - if (zstream.avail_out == 0 || - (zerr == Z_STREAM_END && zstream.avail_out != sizeof(writeBuf))) - { - long writeSize = zstream.next_out - writeBuf; - int cc = write(fd, writeBuf, writeSize); - if (cc != (int) writeSize) { - LOGW("write failed in inflate (%d vs %ld)\n", cc, writeSize); - goto z_bail; - } - - zstream.next_out = writeBuf; - zstream.avail_out = sizeof(writeBuf); - } - } while (zerr == Z_OK); - - assert(zerr == Z_STREAM_END); /* other errors should've been caught */ - - /* paranoia */ - if ((long) zstream.total_out != uncompLen) { - LOGW("Size mismatch on inflated file (%ld vs %ld)\n", - zstream.total_out, uncompLen); - goto z_bail; - } - - result = true; - -z_bail: - inflateEnd(&zstream); /* free up any allocated structures */ - -bail: - return result; -} diff --git a/libs/utils/ZipUtils.cpp b/libs/utils/ZipUtils.cpp deleted file mode 100644 index bfbacfecd..000000000 --- a/libs/utils/ZipUtils.cpp +++ /dev/null @@ -1,344 +0,0 @@ -/* - * 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. - */ - -// -// Misc zip/gzip utility functions. -// - -#define LOG_TAG "ziputil" - -#include "utils/ZipUtils.h" -#include "utils/ZipFileRO.h" -#include "utils/Log.h" - -#include -#include -#include - -#include - -using namespace android; - -/* - * Utility function that expands zip/gzip "deflate" compressed data - * into a buffer. - * - * "fd" is an open file positioned at the start of the "deflate" data - * "buf" must hold at least "uncompressedLen" bytes. - */ -/*static*/ bool ZipUtils::inflateToBuffer(int fd, void* buf, - long uncompressedLen, long compressedLen) -{ - bool result = false; - const unsigned long kReadBufSize = 32768; - unsigned char* readBuf = NULL; - z_stream zstream; - int zerr; - unsigned long compRemaining; - - assert(uncompressedLen >= 0); - assert(compressedLen >= 0); - - readBuf = new unsigned char[kReadBufSize]; - if (readBuf == NULL) - goto bail; - compRemaining = compressedLen; - - /* - * Initialize the zlib stream. - */ - memset(&zstream, 0, sizeof(zstream)); - zstream.zalloc = Z_NULL; - zstream.zfree = Z_NULL; - zstream.opaque = Z_NULL; - zstream.next_in = NULL; - zstream.avail_in = 0; - zstream.next_out = (Bytef*) buf; - zstream.avail_out = uncompressedLen; - zstream.data_type = Z_UNKNOWN; - - /* - * Use the undocumented "negative window bits" feature to tell zlib - * that there's no zlib header waiting for it. - */ - zerr = inflateInit2(&zstream, -MAX_WBITS); - if (zerr != Z_OK) { - if (zerr == Z_VERSION_ERROR) { - LOGE("Installed zlib is not compatible with linked version (%s)\n", - ZLIB_VERSION); - } else { - LOGE("Call to inflateInit2 failed (zerr=%d)\n", zerr); - } - goto bail; - } - - /* - * Loop while we have data. - */ - do { - unsigned long getSize; - - /* read as much as we can */ - if (zstream.avail_in == 0) { - getSize = (compRemaining > kReadBufSize) ? - kReadBufSize : compRemaining; - LOGV("+++ reading %ld bytes (%ld left)\n", - getSize, compRemaining); - - int cc = read(fd, readBuf, getSize); - if (cc != (int) getSize) { - LOGD("inflate read failed (%d vs %ld)\n", - cc, getSize); - goto z_bail; - } - - compRemaining -= getSize; - - zstream.next_in = readBuf; - zstream.avail_in = getSize; - } - - /* uncompress the data */ - zerr = inflate(&zstream, Z_NO_FLUSH); - if (zerr != Z_OK && zerr != Z_STREAM_END) { - LOGD("zlib inflate call failed (zerr=%d)\n", zerr); - goto z_bail; - } - - /* output buffer holds all, so no need to write the output */ - } while (zerr == Z_OK); - - assert(zerr == Z_STREAM_END); /* other errors should've been caught */ - - if ((long) zstream.total_out != uncompressedLen) { - LOGW("Size mismatch on inflated file (%ld vs %ld)\n", - zstream.total_out, uncompressedLen); - goto z_bail; - } - - // success! - result = true; - -z_bail: - inflateEnd(&zstream); /* free up any allocated structures */ - -bail: - delete[] readBuf; - return result; -} - -/* - * Utility function that expands zip/gzip "deflate" compressed data - * into a buffer. - * - * (This is a clone of the previous function, but it takes a FILE* instead - * of an fd. We could pass fileno(fd) to the above, but we can run into - * trouble when "fp" has a different notion of what fd's file position is.) - * - * "fp" is an open file positioned at the start of the "deflate" data - * "buf" must hold at least "uncompressedLen" bytes. - */ -/*static*/ bool ZipUtils::inflateToBuffer(FILE* fp, void* buf, - long uncompressedLen, long compressedLen) -{ - bool result = false; - const unsigned long kReadBufSize = 32768; - unsigned char* readBuf = NULL; - z_stream zstream; - int zerr; - unsigned long compRemaining; - - assert(uncompressedLen >= 0); - assert(compressedLen >= 0); - - readBuf = new unsigned char[kReadBufSize]; - if (readBuf == NULL) - goto bail; - compRemaining = compressedLen; - - /* - * Initialize the zlib stream. - */ - memset(&zstream, 0, sizeof(zstream)); - zstream.zalloc = Z_NULL; - zstream.zfree = Z_NULL; - zstream.opaque = Z_NULL; - zstream.next_in = NULL; - zstream.avail_in = 0; - zstream.next_out = (Bytef*) buf; - zstream.avail_out = uncompressedLen; - zstream.data_type = Z_UNKNOWN; - - /* - * Use the undocumented "negative window bits" feature to tell zlib - * that there's no zlib header waiting for it. - */ - zerr = inflateInit2(&zstream, -MAX_WBITS); - if (zerr != Z_OK) { - if (zerr == Z_VERSION_ERROR) { - LOGE("Installed zlib is not compatible with linked version (%s)\n", - ZLIB_VERSION); - } else { - LOGE("Call to inflateInit2 failed (zerr=%d)\n", zerr); - } - goto bail; - } - - /* - * Loop while we have data. - */ - do { - unsigned long getSize; - - /* read as much as we can */ - if (zstream.avail_in == 0) { - getSize = (compRemaining > kReadBufSize) ? - kReadBufSize : compRemaining; - LOGV("+++ reading %ld bytes (%ld left)\n", - getSize, compRemaining); - - int cc = fread(readBuf, getSize, 1, fp); - if (cc != (int) getSize) { - LOGD("inflate read failed (%d vs %ld)\n", - cc, getSize); - goto z_bail; - } - - compRemaining -= getSize; - - zstream.next_in = readBuf; - zstream.avail_in = getSize; - } - - /* uncompress the data */ - zerr = inflate(&zstream, Z_NO_FLUSH); - if (zerr != Z_OK && zerr != Z_STREAM_END) { - LOGD("zlib inflate call failed (zerr=%d)\n", zerr); - goto z_bail; - } - - /* output buffer holds all, so no need to write the output */ - } while (zerr == Z_OK); - - assert(zerr == Z_STREAM_END); /* other errors should've been caught */ - - if ((long) zstream.total_out != uncompressedLen) { - LOGW("Size mismatch on inflated file (%ld vs %ld)\n", - zstream.total_out, uncompressedLen); - goto z_bail; - } - - // success! - result = true; - -z_bail: - inflateEnd(&zstream); /* free up any allocated structures */ - -bail: - delete[] readBuf; - return result; -} - -/* - * Look at the contents of a gzip archive. We want to know where the - * data starts, and how long it will be after it is uncompressed. - * - * We expect to find the CRC and length as the last 8 bytes on the file. - * This is a pretty reasonable thing to expect for locally-compressed - * files, but there's a small chance that some extra padding got thrown - * on (the man page talks about compressed data written to tape). We - * don't currently deal with that here. If "gzip -l" whines, we're going - * to fail too. - * - * On exit, "fp" is pointing at the start of the compressed data. - */ -/*static*/ bool ZipUtils::examineGzip(FILE* fp, int* pCompressionMethod, - long* pUncompressedLen, long* pCompressedLen, unsigned long* pCRC32) -{ - enum { // flags - FTEXT = 0x01, - FHCRC = 0x02, - FEXTRA = 0x04, - FNAME = 0x08, - FCOMMENT = 0x10, - }; - int ic; - int method, flags; - int i; - - ic = getc(fp); - if (ic != 0x1f || getc(fp) != 0x8b) - return false; // not gzip - method = getc(fp); - flags = getc(fp); - - /* quick sanity checks */ - if (method == EOF || flags == EOF) - return false; - if (method != ZipFileRO::kCompressDeflated) - return false; - - /* skip over 4 bytes of mod time, 1 byte XFL, 1 byte OS */ - for (i = 0; i < 6; i++) - (void) getc(fp); - /* consume "extra" field, if present */ - if ((flags & FEXTRA) != 0) { - int len; - - len = getc(fp); - len |= getc(fp) << 8; - while (len-- && getc(fp) != EOF) - ; - } - /* consume filename, if present */ - if ((flags & FNAME) != 0) { - do { - ic = getc(fp); - } while (ic != 0 && ic != EOF); - } - /* consume comment, if present */ - if ((flags & FCOMMENT) != 0) { - do { - ic = getc(fp); - } while (ic != 0 && ic != EOF); - } - /* consume 16-bit header CRC, if present */ - if ((flags & FHCRC) != 0) { - (void) getc(fp); - (void) getc(fp); - } - - if (feof(fp) || ferror(fp)) - return false; - - /* seek to the end; CRC and length are in the last 8 bytes */ - long curPosn = ftell(fp); - unsigned char buf[8]; - fseek(fp, -8, SEEK_END); - *pCompressedLen = ftell(fp) - curPosn; - - if (fread(buf, 1, 8, fp) != 8) - return false; - /* seek back to start of compressed data */ - fseek(fp, curPosn, SEEK_SET); - - *pCompressionMethod = method; - *pCRC32 = ZipFileRO::get4LE(&buf[0]); - *pUncompressedLen = ZipFileRO::get4LE(&buf[4]); - - return true; -} - diff --git a/libs/utils/characterData.h b/libs/utils/characterData.h deleted file mode 100644 index e931d995e..000000000 --- a/libs/utils/characterData.h +++ /dev/null @@ -1,730 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// Automatically generated on 07-11-2006 by make-CharacterDataC -// DO NOT EDIT DIRECTLY -namespace CharacterData { - - // Structure containing an array of ranges - struct Range { - int length; - const uint32_t* array; - }; - - // For Latin1 characters just index into this array to get the index and decomposition - static const uint16_t LATIN1_DATA[] = { - 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, - 0x0001, 0x0002, 0x0003, 0x0002, 0x0004, 0x0003, 0x0001, 0x0001, - 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, - 0x0001, 0x0001, 0x0001, 0x0001, 0x0003, 0x0003, 0x0003, 0x0002, - 0x0005, 0x0006, 0x0006, 0x0007, 0x0008, 0x0007, 0x0006, 0x0006, - 0x0009, 0x000A, 0x0006, 0x000B, 0x000C, 0x000D, 0x000C, 0x000C, - 0x000E, 0x000F, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, - 0x0016, 0x0017, 0x000C, 0x0006, 0x0018, 0x0019, 0x001A, 0x0006, - 0x0006, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, 0x0020, 0x0021, - 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, - 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, 0x0030, 0x0031, - 0x0032, 0x0033, 0x0034, 0x0035, 0x0006, 0x0036, 0x0037, 0x0038, - 0x0037, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, - 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, - 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, - 0x0050, 0x0051, 0x0052, 0x0035, 0x0019, 0x0036, 0x0019, 0x0001, - 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0003, 0x0001, 0x0001, - 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, - 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, - 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, - 0x5853, 0x0006, 0x0008, 0x0008, 0x0008, 0x0008, 0x0054, 0x0054, - 0x1037, 0x0054, 0x7855, 0x0056, 0x0019, 0x0057, 0x0054, 0x1037, - 0x0058, 0x0059, 0x785A, 0x785B, 0x1037, 0x105C, 0x0054, 0x0006, - 0x1037, 0x785D, 0x7855, 0x005E, 0x305F, 0x305F, 0x305F, 0x0006, - 0x0860, 0x0860, 0x0860, 0x0860, 0x0860, 0x0860, 0x0060, 0x0860, - 0x0860, 0x0860, 0x0860, 0x0860, 0x0860, 0x0860, 0x0860, 0x0860, - 0x0060, 0x0860, 0x0860, 0x0860, 0x0860, 0x0860, 0x0860, 0x0019, - 0x0060, 0x0860, 0x0860, 0x0860, 0x0860, 0x0860, 0x0060, 0x0055, - 0x0861, 0x0861, 0x0861, 0x0861, 0x0861, 0x0861, 0x0061, 0x0861, - 0x0861, 0x0861, 0x0861, 0x0861, 0x0861, 0x0861, 0x0861, 0x0861, - 0x0061, 0x0861, 0x0861, 0x0861, 0x0861, 0x0861, 0x0861, 0x0019, - 0x0061, 0x0861, 0x0861, 0x0861, 0x0861, 0x0861, 0x0061, 0x0862 - }; - - // Each of these arrays is stripped into ranges. In order to build the arrays, each - // codepoint was bit-shifted so that even and odd characters were separated into different - // arrays. The identifier of each array is the top byte after bit-shifting. - // The numbers stored in the array are the bit-shifted codepoint, the decomposition, and an - // index into another array of all possible packed data values. The top 16 bits are the - // codepoint and the bottom 16 are the decomposition and index. The top 5 bits for the decomposition - // and the rest for the index. - static const uint32_t a0[] = { - 0x00800863, 0x00880063, 0x00890863, 0x00930063, 0x00940863, 0x00980864, 0x00991063, 0x009A0863, - 0x009C0055, 0x009D0865, 0x00A01065, 0x00A10065, 0x00A20865, 0x00A50063, 0x00A60863, 0x00A90063, - 0x00AA0863, 0x00B30063, 0x00B40863, 0x00BC0866, 0x00BD0865, 0x00C00055, 0x00C10063, 0x00C30067, - 0x00C40065, 0x00C50068, 0x00C60065, 0x00C70069, 0x00C8006A, 0x00C90065, 0x00CA006B, 0x00CB006C, - 0x00CC0063, 0x00CD006D, 0x00CE006C, 0x00CF006E, 0x00D00863, 0x00D10063, 0x00D3006F, 0x00D40065, - 0x00D50055, 0x00D60063, 0x00D7006F, 0x00D80865, 0x00D90070, 0x00DA0065, 0x00DC0063, 0x00DD0055, - 0x00DE0063, 0x00DF0055, 0x00E00071, 0x00E21072, 0x00E31073, 0x00E41074, 0x00E51072, 0x00E61073, - 0x00E70865, 0x00EF0863, 0x00F20063, 0x00F30863, 0x00F80855, 0x00F91074, 0x00FA0863, 0x00FB0075, - 0x00FC0863, 0x010E0063, 0x010F0863, 0x01100076, 0x01110063, 0x01130863, 0x011A0055, 0x011D0077, - 0x011E0065, 0x011F0077, 0x01200055, 0x01210078, 0x01280055, 0x012A0079, 0x012B007A, 0x012C0055, - 0x0130007A, 0x01310055, 0x0134007B, 0x01350055, 0x0139007C, 0x013A0055, 0x0140007D, 0x01410055, - 0x0144007D, 0x0145007E, 0x01460055, 0x0149007F, 0x014A0080, 0x014B0055, 0x01587881, 0x015D0082, - 0x015E0081, 0x01610037, 0x01630082, 0x01680081, 0x01690037, 0x016C1037, 0x016F0037, 0x01707881, - 0x01730037, 0x01770081, 0x01780037, 0x01800083, 0x01A00883, 0x01A10083, 0x01A20883, 0x01A30083, - 0x01B80078, 0x01BA0837, 0x01BB0078, 0x01BD1081, 0x01BE0078, 0x01BF0806, 0x01C00078, 0x01C21037, - 0x01C30884, 0x01C40885, 0x01C60886, 0x01C70887, 0x01C80855, 0x01C90060, 0x01D10078, 0x01D20060, - 0x01D50860, 0x01D60888, 0x01D70889, 0x01D80855, 0x01D90061, 0x01E1008A, 0x01E20061, 0x01E50861, - 0x01E6088B, 0x01E7088C, 0x01E8108D, 0x01E91077, 0x01EA0877, 0x01EB108E, 0x01EC0063, 0x01F8108F, - 0x01F91090, 0x01FA1091, 0x01FB0019, 0x01FC0065, 0x01FD0063, 0x01FE0055, 0x01FF0077, 0x02000892, - 0x02010092, 0x02060892, 0x02080060, 0x02180061, 0x02280893, 0x02290093, 0x022E0893, 0x02300063, - 0x023B0863, 0x023C0063, 0x02410094, 0x02420083, 0x02440095, 0x02450063, 0x02600077, 0x02610865, - 0x02620065, 0x02680863, 0x026A0063, 0x026B0863, 0x026C0063, 0x026D0863, 0x02700063, 0x02710863, - 0x02740063, 0x02750863, 0x027B0063, 0x027C0863, 0x027D0078, 0x02800063, 0x02880078, 0x02990096, - 0x02AC0078, 0x02AD0097, 0x02B00078, 0x02B10098, 0x02C40078, 0x02C50099, 0x02C60078, 0x02C90083, - 0x02DD0078, 0x02DE0083, 0x02DF009A, 0x02E10083, 0x02E3009A, 0x02E40078, 0x02E8009B, 0x02F60078, - 0x02F8009B, 0x02FA009A, 0x02FB0078, 0x0300009C, 0x03020078, 0x0306000C, 0x03070054, 0x03080083, - 0x030B0078, 0x030F009D, 0x03100078, 0x0311089E, 0x0314009E, 0x031E0078, 0x0320009F, 0x0321009E, - 0x03260083, 0x033000A0, 0x033100A1, 0x033200A2, 0x033300A3, 0x033400A4, 0x03350007, 0x033600A5, - 0x0337009E, 0x03380083, 0x0339009E, 0x033B109E, 0x033D009E, 0x0360089E, 0x0362009E, 0x036A009D, - 0x036B0083, 0x036F0095, 0x03700083, 0x0373009F, 0x03740083, 0x0377009E, 0x0378000E, 0x03790010, - 0x037A0012, 0x037B0014, 0x037C0016, 0x037D009E, 0x037F00A6, 0x0380009D, 0x03870078, 0x0388009E, - 0x03980083, 0x03A60078, 0x03A7009E, 0x03B70078, 0x03C0009E, 0x03D30083, 0x03D90078, 0x04810083, - 0x04820071, 0x049A0871, 0x049B0071, 0x049D0078, 0x049E0083, 0x049F00A7, 0x04A10083, 0x04A500A7, - 0x04A70078, 0x04A80071, 0x04A90083, 0x04AB0078, 0x04AC0871, 0x04B00071, 0x04B10083, 0x04B20097, - 0x04B300A8, 0x04B400A9, 0x04B500AA, 0x04B600AB, 0x04B700AC, 0x04B80097, 0x04B90078, 0x04C100A7, - 0x04C20078, 0x04C30071, 0x04C70078, 0x04C80071, 0x04C90078, 0x04CA0071, 0x04DA0078, 0x04DB0071, - 0x04DD0078, 0x04DE0083, 0x04DF00A7, 0x04E10083, 0x04E30078, 0x04E400A7, 0x04E50078, 0x04E608A7, - 0x04E70071, 0x04E80078, 0x04EE0871, 0x04EF0078, 0x04F00071, 0x04F10083, 0x04F20078, 0x04F300A8, - 0x04F400A9, 0x04F500AA, 0x04F600AB, 0x04F700AC, 0x04F80071, 0x04F90008, 0x04FA00AD, 0x04FB00AE, - 0x04FC00AF, 0x04FD0094, 0x04FE0078, 0x05010083, 0x05020078, 0x05030071, 0x05060078, 0x05080071, - 0x05090078, 0x050A0071, 0x051A0078, 0x051B0871, 0x051C0071, 0x051D0078, 0x051E0083, 0x051F00A7, - 0x05210083, 0x05220078, 0x05240083, 0x05250078, 0x05260083, 0x05270078, 0x052D0871, 0x052E0071, - 0x052F0871, 0x05300078, 0x053300A8, 0x053400A9, 0x053500AA, 0x053600AB, 0x053700AC, 0x05380083, - 0x05390071, 0x053B0078, 0x05410083, 0x05420078, 0x05430071, 0x05470078, 0x05480071, 0x05490078, - 0x054A0071, 0x055A0078, 0x055B0071, 0x055D0078, 0x055E0083, 0x055F00A7, 0x05610083, 0x05630078, - 0x05640083, 0x05650078, 0x056600A7, 0x05670078, 0x05680071, 0x05690078, 0x05700071, 0x05710083, - 0x05720078, 0x057300A8, 0x057400A9, 0x057500AA, 0x057600AB, 0x057700AC, 0x05780078, 0x058100A7, - 0x05820078, 0x05830071, 0x05870078, 0x05880071, 0x05890078, 0x058A0071, 0x059A0078, 0x059B0071, - 0x059D0078, 0x059E0083, 0x059F00A7, 0x05A10083, 0x05A20078, 0x05A408A7, 0x05A50078, 0x05A608A7, - 0x05A70078, 0x05AB0083, 0x05AC0078, 0x05AE0871, 0x05AF0078, 0x05B00071, 0x05B10078, 0x05B300A8, - 0x05B400A9, 0x05B500AA, 0x05B600AB, 0x05B700AC, 0x05B80094, 0x05B90078, 0x05C10083, 0x05C20078, - 0x05C30071, 0x05C60078, 0x05C70071, 0x05CA0871, 0x05CB0078, 0x05CD0071, 0x05D00078, 0x05D20071, - 0x05D30078, 0x05D40071, 0x05D60078, 0x05D70071, 0x05DD0078, 0x05DF00A7, 0x05E00083, 0x05E100A7, - 0x05E20078, 0x05E300A7, 0x05E508A7, 0x05E70078, 0x05F300A8, 0x05F400A9, 0x05F500AA, 0x05F600AB, - 0x05F700AC, 0x05F800B0, 0x05F900B1, 0x05FA0054, 0x05FE0078, 0x060100A7, 0x06020078, 0x06030071, - 0x061A0078, 0x061B0071, 0x061D0078, 0x061F0083, 0x062100A7, 0x06230083, 0x06240883, 0x06250083, - 0x06270078, 0x062B0083, 0x062C0078, 0x06300071, 0x06310078, 0x063300A8, 0x063400A9, 0x063500AA, - 0x063600AB, 0x063700AC, 0x06380078, 0x064100A7, 0x06420078, 0x06430071, 0x065A0078, 0x065B0071, - 0x065D0078, 0x065E0083, 0x065F00A7, 0x066008A7, 0x066100A7, 0x066300B2, 0x066408A7, 0x06660083, - 0x06670078, 0x066B00A7, 0x066C0078, 0x066F0071, 0x06710078, 0x067300A8, 0x067400A9, 0x067500AA, - 0x067600AB, 0x067700AC, 0x06780078, 0x068100A7, 0x06820078, 0x06830071, 0x069D0078, 0x069F00A7, - 0x06A10083, 0x06A20078, 0x06A300A7, 0x06A508A7, 0x06A70078, 0x06B00071, 0x06B10078, 0x06B300A8, - 0x06B400A9, 0x06B500AA, 0x06B600AB, 0x06B700AC, 0x06B80078, 0x06C100A7, 0x06C20078, 0x06C30071, - 0x06CC0078, 0x06CD0071, 0x06D90078, 0x06DA0071, 0x06DE0078, 0x06E00071, 0x06E40078, 0x06E50083, - 0x06E60078, 0x06E800A7, 0x06E90083, 0x06EC00A7, 0x06ED08A7, 0x06F00078, 0x06F900A7, 0x06FA0097, - 0x06FB0078, 0x07010071, 0x071A0083, 0x071E0078, 0x07200071, 0x07230081, 0x07240083, 0x072800A8, - 0x072900A9, 0x072A00AA, 0x072B00AB, 0x072C00AC, 0x072D0097, 0x072E0078, 0x07410071, 0x07430078, - 0x07440071, 0x07460078, 0x074A0071, 0x074C0078, 0x074D0071, 0x07500078, 0x07510071, 0x07520078, - 0x07550071, 0x07560078, 0x07570071, 0x075A0083, 0x075D0078, 0x075E0083, 0x075F0078, 0x07600071, - 0x07630081, 0x07640083, 0x07670078, 0x076800A8, 0x076900A9, 0x076A00AA, 0x076B00AB, 0x076C00AC, - 0x076D0078, 0x076E1071, 0x076F0078, 0x07800071, 0x07810094, 0x07820097, 0x07865897, 0x07870097, - 0x078A0094, 0x078C0083, 0x078D0094, 0x079000A8, 0x079100A9, 0x079200AA, 0x079300AB, 0x079400AC, - 0x079500B3, 0x079A0094, 0x079D00B4, 0x079F00A7, 0x07A00071, 0x07A40078, 0x07A50071, 0x07A90871, - 0x07AA0071, 0x07AE0871, 0x07AF0071, 0x07B60078, 0x07B90083, 0x07BB0883, 0x07BD0083, 0x07C40071, - 0x07C60078, 0x07C80083, 0x07CC0078, 0x07CD0083, 0x07D10883, 0x07D20083, 0x07D60883, 0x07D70083, - 0x07DF0094, 0x07E30083, 0x07E40094, 0x07E70078, 0x07E80097, 0x07E90078, 0x08000071, 0x08110078, - 0x08120071, 0x08130871, 0x08140078, 0x08150071, 0x081600A7, 0x08170083, 0x081A0078, 0x081B0083, - 0x081C00A7, 0x081D0078, 0x082000A8, 0x082100A9, 0x082200AA, 0x082300AB, 0x082400AC, 0x08250097, - 0x08280071, 0x082B00A7, 0x082C0083, 0x082D0078, 0x085000B5, 0x08630078, 0x08680071, 0x087E7881, - 0x087F0078, 0x08800071, 0x08AD0078, 0x08B00071, 0x08D20078, 0x08D40071, 0x08FD0078, 0x09000071, - 0x09270078, 0x09280071, 0x092F0078, 0x09300071, 0x09470078, 0x09480071, 0x095B0078, 0x095C0071, - 0x09630078, 0x09640071, 0x098B0078, 0x098C0071, 0x09AE0078, 0x09B00094, 0x09B10097, 0x09B500B6, - 0x09B600B7, 0x09B700B8, 0x09B800B9, 0x09B900B0, 0x09BA00BA, 0x09BB00BB, 0x09BC00BC, 0x09BD00BD, - 0x09BE00BE, 0x09BF0078, 0x09C00071, 0x09C80054, 0x09CD0078, 0x09D00071, 0x09FB0078, 0x0A010071, - 0x0B370097, 0x0B380071, 0x0B3C0078, 0x0B400005, 0x0B410071, 0x0B4E00BF, 0x0B4F0078, 0x0B500071, - 0x0B760097, 0x0B7700C0, 0x0B7800C1, 0x0B790078, 0x0B800071, 0x0B890083, 0x0B8B0078, 0x0B900071, - 0x0B990083, 0x0B9B0097, 0x0B9C0078, 0x0BA00071, 0x0BA90083, 0x0BAA0078, 0x0BB00071, 0x0BB90083, - 0x0BBA0078, 0x0BC00071, 0x0BDA00C2, 0x0BDB00A7, 0x0BDC0083, 0x0BDF00A7, 0x0BE30083, 0x0BE400A7, - 0x0BE50083, 0x0BEA0097, 0x0BEE0071, 0x0BEF0078, 0x0BF000A8, 0x0BF100A9, 0x0BF200AA, 0x0BF300AB, - 0x0BF400AC, 0x0BF50078, 0x0BF800C3, 0x0BF900C4, 0x0BFA00C5, 0x0BFB00C6, 0x0BFC00C7, 0x0BFD0078, - 0x0C000006, 0x0C030099, 0x0C040006, 0x0C060083, 0x0C070005, 0x0C0800A8, 0x0C0900A9, 0x0C0A00AA, - 0x0C0B00AB, 0x0C0C00AC, 0x0C0D0078, 0x0C100071, 0x0C3C0078, 0x0C400071, 0x0C550078, 0x0C800071, - 0x0C8F0078, 0x0C900083, 0x0C9200A7, 0x0C940083, 0x0C9500C8, 0x0C960078, 0x0C9800A7, 0x0C990083, - 0x0C9A00A7, 0x0C9D0083, 0x0C9E0078, 0x0CA00054, 0x0CA10078, 0x0CA20006, 0x0CA300A8, 0x0CA400A9, - 0x0CA500AA, 0x0CA600AB, 0x0CA700AC, 0x0CA80071, 0x0CB70078, 0x0CB80071, 0x0CBB0078, 0x0CC00071, - 0x0CD50078, 0x0CD800A7, 0x0CE10071, 0x0CE400A7, 0x0CE50078, 0x0CE800A8, 0x0CE900A9, 0x0CEA00AA, - 0x0CEB00AB, 0x0CEC00AC, 0x0CED0078, 0x0CEF0006, 0x0CF00054, 0x0D000071, 0x0D0C0083, 0x0D0D00A7, - 0x0D0E0078, 0x0D0F0097, 0x0D100078, 0x0E800055, 0x0E967881, 0x0EA70081, 0x0EA87881, 0x0EB17055, - 0x0EB60055, 0x0EBC7881, 0x0EBD0055, 0x0ECE7881, 0x0EE00083, 0x0EE20078, 0x0F000863, 0x0F4B0855, - 0x0F4D1055, 0x0F4E0078, 0x0F500863, 0x0F7D0078, 0x0F8008C9, 0x0F8408CA, 0x0F8808C9, 0x0F8B0078, - 0x0F8C08CA, 0x0F8F0078, 0x0F9008C9, 0x0F9408CA, 0x0F9808C9, 0x0F9C08CA, 0x0FA008C9, 0x0FA30078, - 0x0FA408CA, 0x0FA70078, 0x0FA80855, 0x0FAC0078, 0x0FB008C9, 0x0FB408CA, 0x0FB808CB, 0x0FB908CC, - 0x0FBB08CD, 0x0FBC08CE, 0x0FBD08CF, 0x0FBE08D0, 0x0FBF0078, 0x0FC008C9, 0x0FC408D1, 0x0FC808C9, - 0x0FCC08D1, 0x0FD008C9, 0x0FD408D1, 0x0FD808C9, 0x0FD90855, 0x0FDC08CA, 0x0FDD08D2, 0x0FDE08D3, - 0x0FDF08D4, 0x0FE01037, 0x0FE10855, 0x0FE408D5, 0x0FE608D3, 0x0FE70837, 0x0FE808C9, 0x0FE90855, - 0x0FEA0078, 0x0FEB0855, 0x0FEC08CA, 0x0FED08D6, 0x0FEE0078, 0x0FEF0837, 0x0FF008C9, 0x0FF10855, - 0x0FF408CA, 0x0FF508D7, 0x0FF608D8, 0x0FF70837, 0x0FF80078, 0x0FF90855, 0x0FFC08D9, 0x0FFD08DA, - 0x0FFE08D3, 0x0FFF1037, 0x10000805, 0x10011005, 0x10060057, 0x100700C2, 0x10080099, 0x100B0006, - 0x100C00DB, 0x100D00B4, 0x100E00DB, 0x100F00B4, 0x10100006, 0x10121006, 0x101400DC, 0x101500DD, - 0x101600DE, 0x101700DF, 0x10180007, 0x101A1007, 0x101B1006, 0x101C0006, 0x101D00E0, 0x101E1006, - 0x10200038, 0x10210006, 0x102200E1, 0x1023000A, 0x10241006, 0x10250006, 0x10290019, 0x102A0038, - 0x102B0006, 0x10300057, 0x10320078, 0x10350057, 0x103878E2, 0x10390078, 0x103A78E3, 0x103B78E4, - 0x103C78E5, 0x103D780B, 0x103E7819, 0x103F780A, 0x104070E2, 0x1041705A, 0x104270E3, 0x104370E4, - 0x104470E5, 0x1045700B, 0x10467019, 0x1047700A, 0x10487081, 0x104B0078, 0x10500008, 0x10541008, - 0x10550008, 0x105B0078, 0x10680083, 0x106F0095, 0x10730083, 0x10760078, 0x10801054, 0x10812877, - 0x10820054, 0x10831054, 0x10840054, 0x10852855, 0x10862877, 0x10872855, 0x10882877, 0x108A0054, - 0x108B1054, 0x108C0054, 0x108D2877, 0x108F0054, 0x10907854, 0x10922877, 0x109308E6, 0x10942877, - 0x109508E7, 0x10962877, 0x10970058, 0x10982877, 0x10990054, 0x109A2855, 0x109B1071, 0x109D0054, - 0x109E2855, 0x109F2877, 0x10A028E8, 0x10A10019, 0x10A32855, 0x10A50054, 0x10A70078, 0x10AA305F, - 0x10B010E9, 0x10B110EA, 0x10B210EB, 0x10B310EC, 0x10B410ED, 0x10B510EE, 0x10B610EF, 0x10B710F0, - 0x10B810F1, 0x10B910F2, 0x10BA10F3, 0x10BB10F4, 0x10BC10F5, 0x10BD10F6, 0x10BE10F7, 0x10BF10F8, - 0x10C000F9, 0x10C100FA, 0x10C20078, 0x10C80019, 0x10CB0054, 0x10CD0819, 0x10CE0054, 0x10D00019, - 0x10D10054, 0x10D30019, 0x10D40054, 0x10D70819, 0x10D80054, 0x10E70819, 0x10E80054, 0x10E90019, - 0x10EB0054, 0x10FA0019, 0x110100E8, 0x110208E8, 0x11030019, 0x110400FB, 0x110608FC, 0x11070019, - 0x1109000B, 0x110A0019, 0x110B00E8, 0x110C0019, 0x110D00E8, 0x110F0019, 0x111000E8, 0x111208E8, - 0x11140019, 0x111610E8, 0x111700E8, 0x111810E8, 0x111900E8, 0x111A0019, 0x111E00FD, 0x111F00E8, - 0x112208E8, 0x112300E8, 0x11270019, 0x112900FD, 0x112B0019, 0x113008E8, 0x113200FD, 0x11360019, - 0x113708FD, 0x113900FD, 0x113A08FD, 0x113B00FD, 0x113C08FD, 0x113D00FD, 0x114008FD, 0x114100FD, - 0x114208FD, 0x114300FD, 0x114408FD, 0x114500FD, 0x114600E8, 0x11470019, 0x114800FE, 0x114A0019, - 0x114C00FF, 0x114D0019, 0x115100FD, 0x11520019, 0x11530100, 0x11540101, 0x115500E8, 0x115608E8, - 0x115800FD, 0x115C00E8, 0x115D0019, 0x115F00E8, 0x11600019, 0x116500FE, 0x11670019, 0x116800FD, - 0x11690019, 0x116B00FD, 0x117008FD, 0x117200FD, 0x117508FD, 0x11770019, 0x117800FD, 0x11790102, - 0x117B0103, 0x117C00E8, 0x117D0104, 0x117F0105, 0x11800054, 0x118400FD, 0x11860054, 0x119000E8, - 0x11910054, 0x1195080A, 0x11960054, 0x119B0094, 0x11BE0019, 0x11BF0054, 0x11CE0019, 0x11DA00B4, - 0x11DB0006, 0x11DC0054, 0x11EE0078, 0x12000054, 0x12140078, 0x12200054, 0x12260078, 0x12301906, - 0x12311907, 0x12321908, 0x12331909, 0x1234190A, 0x1235190B, 0x1236190C, 0x1237190D, 0x1238190E, - 0x1239190F, 0x123A1106, 0x123B1107, 0x123C1108, 0x123D1109, 0x123E110A, 0x123F110B, 0x1240110C, - 0x1241110D, 0x1242110E, 0x1243110F, 0x1244105D, 0x1245105B, 0x12461110, 0x12471111, 0x12481112, - 0x12491113, 0x124A1114, 0x124B1115, 0x124C1116, 0x124D1117, 0x124E1094, 0x125B1918, 0x12681919, - 0x127518C3, 0x1276011A, 0x1277011B, 0x1278011C, 0x1279011D, 0x127A011E, 0x127B00C4, 0x127C00C5, - 0x127D00C6, 0x127E00C7, 0x127F011F, 0x12800054, 0x12FC0019, 0x13000054, 0x134F0078, 0x13500054, - 0x13560094, 0x13570054, 0x13590078, 0x13810054, 0x13850078, 0x13860054, 0x13940078, 0x13950054, - 0x13A60078, 0x13A80054, 0x13AA0078, 0x13AB0054, 0x13B00078, 0x13B10054, 0x13B40009, 0x13BB0106, - 0x13BC0107, 0x13BD0108, 0x13BE0109, 0x13BF010A, 0x13C00106, 0x13C10107, 0x13C20108, 0x13C30109, - 0x13C4010A, 0x13C50106, 0x13C60107, 0x13C70108, 0x13C80109, 0x13C9010A, 0x13CA0054, 0x13CB0078, - 0x13CC0054, 0x13D80078, 0x13D90054, 0x13E000E8, 0x13E10019, 0x13E200FE, 0x13E3000A, 0x13E40078, - 0x13E80019, 0x13EA00E8, 0x13EB00FE, 0x13EC0019, 0x13EE00E8, 0x13EF00FE, 0x13F00019, 0x13F100FD, - 0x13F30009, 0x13F60078, 0x13F80019, 0x14000094, 0x14800019, 0x14C2000A, 0x14C70120, 0x14C80121, - 0x14C9000A, 0x14CD0019, 0x14CE00E8, 0x14D80019, 0x14DC0122, 0x14DD0019, 0x14E000FD, 0x14E100E8, - 0x14E200FD, 0x14E30019, 0x14E700E8, 0x14E800FE, 0x14EA00FD, 0x14EB0019, 0x14EC0009, 0x14EE00E8, - 0x14EF0019, 0x14F200E8, 0x14F30019, 0x14F400E8, 0x14F50019, 0x14FA00E8, 0x14FC00FD, 0x14FD0019, - 0x14FE0009, 0x14FF0019, 0x150500E8, 0x150610E8, 0x150700E8, 0x15110019, 0x151200E8, 0x15140019, - 0x151600FE, 0x15180019, 0x151A00FD, 0x151B0019, 0x151E00FD, 0x151F00E8, 0x15200019, 0x152C00E8, - 0x152D0019, 0x153200FD, 0x15330019, 0x153500E8, 0x15370019, 0x153800E8, 0x15390019, 0x153A10E8, - 0x153B1019, 0x153C0019, 0x153D00FE, 0x153E00E8, 0x153F00FE, 0x154300E8, 0x154600FE, 0x154700E8, - 0x154900FE, 0x154F00E8, 0x155100FE, 0x15520019, 0x155300FD, 0x15570019, 0x155800FE, 0x155900E8, - 0x155A00FE, 0x155B00E8, 0x155E00FE, 0x156400E8, 0x156700FE, 0x156C0019, 0x156E08E8, 0x156F0123, - 0x15700019, 0x157100E8, 0x15720124, 0x157300E8, 0x15740019, 0x157600FD, 0x157700E8, 0x15780019, - 0x157C00FE, 0x157E0019, 0x15800054, 0x158A0078, 0x16000096, 0x16180098, 0x16300078, 0x16400063, - 0x16720055, 0x16730054, 0x16760078, 0x167D0006, 0x16800125, 0x16930078, 0x16980071, 0x16B30078, - 0x16C00071, 0x16CC0078, 0x16D00071, 0x16F00078, 0x17000006, 0x17010126, 0x17030006, 0x170500E0, - 0x17060126, 0x17070006, 0x170C0078, 0x170E0126, 0x170F0078, 0x17400054, 0x174D0078, 0x174E0054, - 0x177A0078, 0x17801054, 0x17EB0078, 0x17F80054, 0x17FE0078, 0x18008805, 0x18010006, 0x18020054, - 0x18030071, 0x18040009, 0x18090054, 0x180A0009, 0x180E0099, 0x180F00BF, 0x18100054, 0x18110127, - 0x18120128, 0x18130129, 0x1814012A, 0x18150083, 0x18180099, 0x18190081, 0x181B1054, 0x181C112B, - 0x181D112C, 0x181E0071, 0x181F0054, 0x18200078, 0x18210071, 0x18260871, 0x18320071, 0x18380871, - 0x18390071, 0x183A0871, 0x183C0071, 0x183D0871, 0x183F0071, 0x184A0871, 0x184B0071, 0x184C0078, - 0x184D0083, 0x184E1037, 0x184F0881, 0x18500099, 0x18510071, 0x18560871, 0x18620071, 0x18680871, - 0x18690071, 0x186A0871, 0x186C0071, 0x186D0871, 0x186F0071, 0x187A0871, 0x187B0071, 0x187C0871, - 0x187E0081, 0x187F0881, 0x18800078, 0x18830071, 0x18970078, 0x18991071, 0x18C80094, 0x18C978AD, - 0x18CA78AE, 0x18CB7894, 0x18D00071, 0x18DC0078, 0x18E00054, 0x18E80078, 0x18F80071, 0x19001094, - 0x190F1054, 0x191010AD, 0x191110AE, 0x1912112D, 0x1913112E, 0x1914112F, 0x19151094, 0x19220078, - 0x19286854, 0x19291930, 0x192A1931, 0x192B1932, 0x192C1933, 0x192D1934, 0x192E1935, 0x192F1936, - 0x19301894, 0x193E1854, 0x194018AD, 0x194118AE, 0x1942192D, 0x1943192E, 0x1944192F, 0x19451894, - 0x19591937, 0x195A1938, 0x195B1939, 0x195C193A, 0x195D193B, 0x195E193C, 0x195F193D, 0x19601094, - 0x19666854, 0x19681894, 0x19806894, 0x19AC1094, 0x19B96894, 0x19BC6854, 0x19BE6894, 0x19EF6854, - 0x19F01094, 0x1A000071, 0x26DB0078, 0x26E00054, 0x27000071, 0x4FDE0078, 0x50000071, 0x52470078, - 0x52480054, 0x52640078, 0x53800037, 0x538C0078, 0x54000071, 0x540100C8, 0x54020071, 0x54030083, - 0x54040071, 0x541200A7, 0x54130083, 0x54140054, 0x54160078, 0x56000071, 0x6BD20078, 0x6C00013E, - 0x7000013F, 0x7C800871, 0x7D070071, 0x7D080871, 0x7D0A0071, 0x7D0B0871, 0x7D120071, 0x7D130871, - 0x7D140071, 0x7D150871, 0x7D170078, 0x7D180871, 0x7D360078, 0x7D380871, 0x7D6D0078, 0x7D801055, - 0x7D840078, 0x7D8A1055, 0x7D8C0078, 0x7D8F0083, 0x7D90289B, 0x7D95089B, 0x7DA10078, 0x7DA2089B, - 0x7DA8409E, 0x7DAA389E, 0x7DAB409E, 0x7DAC389E, 0x7DAD409E, 0x7DAE389E, 0x7DAF409E, 0x7DB0389E, - 0x7DB1409E, 0x7DB2389E, 0x7DB3409E, 0x7DB4389E, 0x7DB5409E, 0x7DB6389E, 0x7DB7409E, 0x7DB8389E, - 0x7DB9409E, 0x7DBA389E, 0x7DBB409E, 0x7DBC389E, 0x7DBD409E, 0x7DBE389E, 0x7DBF409E, 0x7DC0389E, - 0x7DC1409E, 0x7DC8389E, 0x7DC9409E, 0x7DCA389E, 0x7DCB409E, 0x7DCC389E, 0x7DCD409E, 0x7DCE389E, - 0x7DCF409E, 0x7DD1389E, 0x7DD2409E, 0x7DD4389E, 0x7DD5409E, 0x7DD6389E, 0x7DD7409E, 0x7DD90078, - 0x7DEA209E, 0x7DEB489E, 0x7DEC209E, 0x7DEF409E, 0x7DF3389E, 0x7DF5409E, 0x7DFC389E, 0x7DFD209E, - 0x7DFE409E, 0x7DFF389E, 0x7E00409E, 0x7E32209E, 0x7E4C389E, 0x7E70489E, 0x7E7B409E, 0x7E89209E, - 0x7E97389E, 0x7E9A489E, 0x7E9E209E, 0x7E9F00B4, 0x7EA00078, 0x7EA8389E, 0x7EAC209E, 0x7EAE389E, - 0x7EAF209E, 0x7EB0389E, 0x7EB1209E, 0x7EB4389E, 0x7EB5209E, 0x7EB8389E, 0x7EBA209E, 0x7EC3389E, - 0x7EC80078, 0x7EC9389E, 0x7ECB209E, 0x7ECC389E, 0x7ECD209E, 0x7EDA389E, 0x7EDB209E, 0x7EDC389E, - 0x7EDE209E, 0x7EE2389E, 0x7EE3209E, 0x7EE40078, 0x7EF8409E, 0x7EFE4140, 0x7EFF0078, 0x7F000083, - 0x7F088006, 0x7F0C80BF, 0x7F0D0078, 0x7F100083, 0x7F120078, 0x7F188006, 0x7F198099, 0x7F1A8038, - 0x7F1B80BF, 0x7F230006, 0x7F2480BF, 0x7F251006, 0x7F271038, 0x7F28600C, 0x7F2A6006, 0x7F2C6099, - 0x7F2D60BF, 0x7F306006, 0x7F31600B, 0x7F326019, 0x7F346006, 0x7F356007, 0x7F360078, 0x7F38409E, - 0x7F41209E, 0x7F46489E, 0x7F47209E, 0x7F49489E, 0x7F4A209E, 0x7F4C489E, 0x7F4D209E, 0x7F4E489E, - 0x7F4F209E, 0x7F50489E, 0x7F51209E, 0x7F52489E, 0x7F53209E, 0x7F54489E, 0x7F55209E, 0x7F5A489E, - 0x7F5B209E, 0x7F5C489E, 0x7F5D209E, 0x7F5E489E, 0x7F5F209E, 0x7F60489E, 0x7F61209E, 0x7F62489E, - 0x7F63209E, 0x7F64489E, 0x7F65209E, 0x7F66489E, 0x7F67209E, 0x7F68489E, 0x7F69209E, 0x7F6A489E, - 0x7F6B209E, 0x7F6C489E, 0x7F6D209E, 0x7F6E489E, 0x7F6F209E, 0x7F70489E, 0x7F71209E, 0x7F72489E, - 0x7F73209E, 0x7F74489E, 0x7F75209E, 0x7F76489E, 0x7F77209E, 0x7F7A489E, 0x7F7B209E, 0x7F7F0078, - 0x7F818806, 0x7F828808, 0x7F838806, 0x7F848809, 0x7F858806, 0x7F86880C, 0x7F88880E, 0x7F898810, - 0x7F8A8812, 0x7F8B8814, 0x7F8C8816, 0x7F8D880C, 0x7F8E8818, 0x7F8F881A, 0x7F908806, 0x7F918860, - 0x7F9E8806, 0x7F9F8837, 0x7FA18861, 0x7FAE8819, 0x7FB0880A, 0x7FB15009, 0x7FB25006, 0x7FB35071, - 0x7FB85081, 0x7FB95071, 0x7FCF5081, 0x7FD05071, 0x7FE00078, 0x7FE15071, 0x7FE40078, 0x7FE55071, - 0x7FE80078, 0x7FE95071, 0x7FEC0078, 0x7FED5071, 0x7FEF0078, 0x7FF08808, 0x7FF18819, 0x7FF28854, - 0x7FF38808, 0x7FF45054, 0x7FF55019, 0x7FF75054, 0x7FF80078, 0x7FFD0141, 0x7FFE0054, 0x7FFF0078, - 0x80000071, 0x80060078, 0x80070071, 0x801F0078, 0x80200071, 0x80270078, 0x80280071, 0x802F0078, - 0x80400071, 0x807E0078, 0x80800097, 0x80810094, 0x80820078, 0x808400B6, 0x808500B7, 0x808600B8, - 0x808700B9, 0x808800B0, 0x808900BA, 0x808A00BB, 0x808B00BC, 0x808C00BD, 0x808D0142, 0x808E0143, - 0x808F0144, 0x80900145, 0x809100B1, 0x80920146, 0x80930147, 0x80940148, 0x80950149, 0x8096014A, - 0x8097014B, 0x8098014C, 0x8099014D, 0x809A0078, 0x809C0094, 0x80A0014E, 0x80A1014F, 0x80A20150, - 0x80A30151, 0x80A40152, 0x80A50150, 0x80A60153, 0x80A70151, 0x80A80154, 0x80A90155, 0x80AA0156, - 0x80AB0157, 0x80AC014F, 0x80AE0158, 0x80B00154, 0x80B30150, 0x80B50155, 0x80B60153, 0x80B90151, - 0x80BA0150, 0x80BB005F, 0x80BD0054, 0x80C500C3, 0x80C60078, 0x81800071, 0x819000AD, 0x819100B0, - 0x81920078, 0x81980071, 0x81A50159, 0x81A60078, 0x81C00071, 0x81CF0078, 0x81D00071, 0x81E20078, - 0x81E40071, 0x81E80094, 0x81E90158, 0x81EA015A, 0x81EB0078, 0x8200015B, 0x8214015C, 0x82280071, - 0x824F0078, 0x825000A8, 0x825100A9, 0x825200AA, 0x825300AB, 0x825400AC, 0x82550078, 0x8400009B, - 0x84030078, 0x8404009B, 0x841B0078, 0x841C009B, 0x841D0078, 0x841E009B, 0x841F0078, 0x8500009B, - 0x85010083, 0x85020078, 0x85030083, 0x85040078, 0x85060083, 0x8508009B, 0x850A0078, 0x850B009B, - 0x850C0078, 0x850D009B, 0x851A0078, 0x851C0083, 0x851E0078, 0x8520015D, 0x8521015E, 0x8522015F, - 0x85230160, 0x85240078, 0x8528009A, 0x852D0078, 0xE8000094, 0xE87B0078, 0xE8800094, 0xE8940078, - 0xE8950094, 0xE8AF0894, 0xE8B300A7, 0xE8B40083, 0xE8B50094, 0xE8B700A7, 0xE8BA0057, 0xE8BE0083, - 0xE8C20094, 0xE8C30083, 0xE8C60094, 0xE8D50083, 0xE8D70094, 0xE8DE0894, 0xE8E10094, 0xE8EF0078, - 0xE9000054, 0xE9210083, 0xE9230078, 0xE9800054, 0xE9AC0078, 0xEA002877, 0xEA0D2855, 0xEA1A2877, - 0xEA272855, 0xEA342877, 0xEA412855, 0xEA4E2877, 0xEA500078, 0xEA512877, 0xEA520078, 0xEA532877, - 0xEA540078, 0xEA552877, 0xEA5B2855, 0xEA5D0078, 0xEA5F2855, 0xEA620078, 0xEA632855, 0xEA682877, - 0xEA752855, 0xEA822877, 0xEA830078, 0xEA842877, 0xEA860078, 0xEA872877, 0xEA8F2855, 0xEA9C2877, - 0xEA9D0078, 0xEA9E2877, 0xEAA40078, 0xEAA52877, 0xEAA92855, 0xEAB62877, 0xEAC32855, 0xEAD02877, - 0xEADD2855, 0xEAEA2877, 0xEAF72855, 0xEB042877, 0xEB112855, 0xEB1E2877, 0xEB2B2855, 0xEB382877, - 0xEB452855, 0xEB530078, 0xEB542877, 0xEB612855, 0xEB712877, 0xEB7E2855, 0xEB8E2877, 0xEB9B2855, - 0xEBAB2877, 0xEBB82855, 0xEBC82877, 0xEBD52855, 0xEBE50078, 0xEBE7280E, 0xEBE82810, 0xEBE92812, - 0xEBEA2814, 0xEBEB2816, 0xEBEC280E, 0xEBED2810, 0xEBEE2812, 0xEBEF2814, 0xEBF02816, 0xEBF1280E, - 0xEBF22810, 0xEBF32812, 0xEBF42814, 0xEBF52816, 0xEBF6280E, 0xEBF72810, 0xEBF82812, 0xEBF92814, - 0xEBFA2816, 0xEBFB280E, 0xEBFC2810, 0xEBFD2812, 0xEBFE2814, 0xEBFF2816, 0xEC000078 - }; - - static const uint32_t a1[] = { - 0x00000071, 0x536C0078, 0x7C000871, 0x7D0F0078 - }; - - static const uint32_t a7[] = { - 0x00100057, 0x00400078, 0x00800083, 0x00F80078, 0x8000013F, 0xFFFF0078 - }; - - static const uint32_t a8[] = { - 0x0000013F, 0x7FFF0078 - }; - - static const uint32_t a16[] = { - 0x00800865, 0x00880065, 0x00890865, 0x00930065, 0x00940865, 0x00980161, 0x00991065, 0x009A0865, - 0x009C0863, 0x009F1063, 0x00A00063, 0x00A10863, 0x00A41055, 0x00A50065, 0x00A60865, 0x00A90065, - 0x00AA0865, 0x00B30065, 0x00B40865, 0x00BC0863, 0x00BF1162, 0x00C00163, 0x00C10065, 0x00C30063, - 0x00C40068, 0x00C50063, 0x00C60055, 0x00C70164, 0x00C80063, 0x00C90068, 0x00CA0165, 0x00CB0166, - 0x00CC0065, 0x00CD0055, 0x00CE0167, 0x00CF0168, 0x00D00865, 0x00D10065, 0x00D30063, 0x00D4006F, - 0x00D50055, 0x00D60065, 0x00D70863, 0x00D80070, 0x00D90063, 0x00DB0169, 0x00DC0065, 0x00DD0071, - 0x00DE0065, 0x00DF016A, 0x00E00071, 0x00E21074, 0x00E31072, 0x00E41073, 0x00E51074, 0x00E60863, - 0x00EE016B, 0x00EF0865, 0x00F20065, 0x00F30865, 0x00F81072, 0x00F91073, 0x00FA0865, 0x00FB016C, - 0x00FC0865, 0x010E0065, 0x010F0865, 0x01100055, 0x01110065, 0x01130865, 0x011A0055, 0x011D0063, - 0x011E016D, 0x011F0055, 0x0120016E, 0x01210078, 0x01280055, 0x0129016F, 0x012A0055, 0x012B007A, - 0x012C0170, 0x012D0171, 0x012E0055, 0x01310172, 0x01320055, 0x01340173, 0x01350055, 0x01370173, - 0x01380055, 0x013A0174, 0x013B0055, 0x0141007D, 0x01420055, 0x0145007E, 0x01460055, 0x01587881, - 0x015C0082, 0x015D0081, 0x01610037, 0x01630082, 0x01680081, 0x01690037, 0x016C1037, 0x016F0037, - 0x01707881, 0x01720037, 0x01800083, 0x01A00883, 0x01A20175, 0x01A30083, 0x01B80078, 0x01BA0037, - 0x01BB0078, 0x01C20837, 0x01C30806, 0x01C40885, 0x01C50078, 0x01C70887, 0x01C80060, 0x01D50860, - 0x01D60889, 0x01D80061, 0x01E50861, 0x01E6088C, 0x01E70078, 0x01E81176, 0x01E90877, 0x01EA1177, - 0x01EB0055, 0x01EC0065, 0x01F81093, 0x01F90055, 0x01FA1178, 0x01FB0063, 0x01FC10D8, 0x01FD0065, - 0x01FE0077, 0x02000892, 0x02020092, 0x02030892, 0x02040092, 0x02060892, 0x02070092, 0x02080060, - 0x020C0860, 0x020D0060, 0x02180061, 0x021C0861, 0x021D0061, 0x02280893, 0x022A0093, 0x022B0893, - 0x022C0093, 0x022E0893, 0x022F0093, 0x02300065, 0x023B0865, 0x023C0065, 0x02410083, 0x02430078, - 0x02440095, 0x02450065, 0x02600863, 0x02610063, 0x02670078, 0x02680865, 0x026A0065, 0x026B0865, - 0x026C0065, 0x026D0865, 0x02700065, 0x02710865, 0x02740065, 0x02750865, 0x027B0065, 0x027C0865, - 0x027D0078, 0x02800065, 0x02880078, 0x02980096, 0x02AB0078, 0x02AC0081, 0x02AD0097, 0x02B00098, - 0x02C31055, 0x02C40097, 0x02C50078, 0x02C80083, 0x02E1009A, 0x02E20083, 0x02E40078, 0x02E8009B, - 0x02F50078, 0x02F8009B, 0x02F9009A, 0x02FA0078, 0x0300009C, 0x03020078, 0x03050140, 0x0306009D, - 0x03070054, 0x03080083, 0x030B0078, 0x030D009D, 0x030E0078, 0x030F009D, 0x0310009E, 0x0311089E, - 0x0313009E, 0x031D0078, 0x0320009E, 0x03250083, 0x032F0078, 0x03300179, 0x0331017A, 0x0332017B, - 0x0333017C, 0x0334017D, 0x033500A5, 0x0336009D, 0x0337009E, 0x033A109E, 0x033C009E, 0x0369089E, - 0x036A009E, 0x036B0083, 0x036E009C, 0x036F0083, 0x0372009F, 0x03730083, 0x03740054, 0x03750083, - 0x0377009E, 0x0378000F, 0x03790011, 0x037A0013, 0x037B0015, 0x037C0017, 0x037D009E, 0x037E00A6, - 0x037F009E, 0x0380009D, 0x03870057, 0x03880083, 0x0389009E, 0x03980083, 0x03A50078, 0x03A6009E, - 0x03B70078, 0x03C0009E, 0x03D30083, 0x03D8009E, 0x03D90078, 0x04800083, 0x048100A7, 0x04820071, - 0x04940871, 0x04950071, 0x04980871, 0x04990071, 0x049D0078, 0x049E0071, 0x049F00A7, 0x04A00083, - 0x04A400A7, 0x04A60083, 0x04A70078, 0x04A80083, 0x04AA0078, 0x04AC0871, 0x04B00071, 0x04B10083, - 0x04B20097, 0x04B3017E, 0x04B4017F, 0x04B50180, 0x04B60181, 0x04B70182, 0x04B80078, 0x04BE0071, - 0x04BF0078, 0x04C00083, 0x04C100A7, 0x04C20071, 0x04C60078, 0x04C70071, 0x04C80078, 0x04C90071, - 0x04D40078, 0x04D50071, 0x04D80078, 0x04DB0071, 0x04DD0078, 0x04DE0071, 0x04DF00A7, 0x04E00083, - 0x04E20078, 0x04E300A7, 0x04E40078, 0x04E508A7, 0x04E60083, 0x04E70078, 0x04EB00A7, 0x04EC0078, - 0x04EE0871, 0x04F00071, 0x04F10083, 0x04F20078, 0x04F3017E, 0x04F4017F, 0x04F50180, 0x04F60181, - 0x04F70182, 0x04F80071, 0x04F90008, 0x04FA00B6, 0x04FB00B7, 0x04FC0183, 0x04FD0078, 0x05000083, - 0x050100A7, 0x05020071, 0x05050078, 0x05070071, 0x05080078, 0x05090071, 0x05140078, 0x05150071, - 0x05180078, 0x05190871, 0x051A0071, 0x051B0078, 0x051C0071, 0x051D0078, 0x051F00A7, 0x05200083, - 0x05210078, 0x05230083, 0x05240078, 0x05250083, 0x05270078, 0x052C0871, 0x052E0078, 0x0533017E, - 0x0534017F, 0x05350180, 0x05360181, 0x05370182, 0x05380083, 0x05390071, 0x053A0078, 0x05400083, - 0x054100A7, 0x05420071, 0x05540078, 0x05550071, 0x05580078, 0x05590071, 0x055D0078, 0x055E0071, - 0x055F00A7, 0x05600083, 0x056400A7, 0x05660083, 0x05670078, 0x05700071, 0x05710083, 0x05720078, - 0x0573017E, 0x0574017F, 0x05750180, 0x05760181, 0x05770182, 0x05780008, 0x05790078, 0x05800083, - 0x058100A7, 0x05820071, 0x05860078, 0x05870071, 0x05880078, 0x05890071, 0x05940078, 0x05950071, - 0x05980078, 0x05990071, 0x059D0078, 0x059E0071, 0x059F0083, 0x05A20078, 0x05A300A7, 0x05A40078, - 0x05A508A7, 0x05A60083, 0x05A70078, 0x05AB00A7, 0x05AC0078, 0x05AE0871, 0x05AF0071, 0x05B10078, - 0x05B3017E, 0x05B4017F, 0x05B50180, 0x05B60181, 0x05B70182, 0x05B80071, 0x05B90078, 0x05C10071, - 0x05C50078, 0x05C70071, 0x05C80078, 0x05C90071, 0x05CB0078, 0x05CC0071, 0x05CD0078, 0x05CF0071, - 0x05D00078, 0x05D10071, 0x05D20078, 0x05D40071, 0x05D50078, 0x05D70071, 0x05DD0078, 0x05DF00A7, - 0x05E10078, 0x05E300A7, 0x05E40078, 0x05E508A7, 0x05E60083, 0x05E70078, 0x05EB00A7, 0x05EC0078, - 0x05F3017E, 0x05F4017F, 0x05F50180, 0x05F60181, 0x05F70182, 0x05F80184, 0x05F90054, 0x05FC0008, - 0x05FD0078, 0x060000A7, 0x06020071, 0x06060078, 0x06070071, 0x06080078, 0x06090071, 0x06140078, - 0x06150071, 0x061D0078, 0x061F0083, 0x062000A7, 0x06220078, 0x06230083, 0x06240078, 0x06250083, - 0x06270078, 0x062A0083, 0x062B0078, 0x06300071, 0x06310078, 0x0633017E, 0x0634017F, 0x06350180, - 0x06360181, 0x06370182, 0x06380078, 0x064100A7, 0x06420071, 0x06460078, 0x06470071, 0x06480078, - 0x06490071, 0x06540078, 0x06550071, 0x065D0078, 0x065E0071, 0x065F00B2, 0x066000A7, 0x06620078, - 0x066308A7, 0x06640078, 0x066508A7, 0x06660083, 0x06670078, 0x066A00A7, 0x066B0078, 0x06700071, - 0x06710078, 0x0673017E, 0x0674017F, 0x06750180, 0x06760181, 0x06770182, 0x06780078, 0x068100A7, - 0x06820071, 0x06860078, 0x06870071, 0x06880078, 0x06890071, 0x06940078, 0x06950071, 0x069D0078, - 0x069F00A7, 0x06A00083, 0x06A20078, 0x06A300A7, 0x06A40078, 0x06A508A7, 0x06A60083, 0x06A70078, - 0x06AB00A7, 0x06AC0078, 0x06B00071, 0x06B10078, 0x06B3017E, 0x06B4017F, 0x06B50180, 0x06B60181, - 0x06B70182, 0x06B80078, 0x06C100A7, 0x06C20071, 0x06CB0078, 0x06CD0071, 0x06DF0078, 0x06E00071, - 0x06E30078, 0x06E700A7, 0x06E90083, 0x06EA0078, 0x06EC00A7, 0x06EE08A7, 0x06EF00A7, 0x06F00078, - 0x06F900A7, 0x06FA0078, 0x07000071, 0x07180083, 0x07191071, 0x071A0083, 0x071D0078, 0x071F0008, - 0x07200071, 0x07230083, 0x07270097, 0x0728017E, 0x0729017F, 0x072A0180, 0x072B0181, 0x072C0182, - 0x072D0097, 0x072E0078, 0x07400071, 0x07410078, 0x07430071, 0x07440078, 0x07460071, 0x07470078, - 0x074A0071, 0x07540078, 0x07550071, 0x07580083, 0x07591071, 0x075A0083, 0x075E0071, 0x075F0078, - 0x07600071, 0x07620078, 0x07640083, 0x07670078, 0x0768017E, 0x0769017F, 0x076A0180, 0x076B0181, - 0x076C0182, 0x076D0078, 0x076E1071, 0x076F0078, 0x07800094, 0x07820097, 0x07890094, 0x078C0083, - 0x078D0094, 0x0790017E, 0x0791017F, 0x07920180, 0x07930181, 0x07940182, 0x079500B3, 0x079A0083, - 0x079D00BF, 0x079F00A7, 0x07A00071, 0x07A10871, 0x07A20071, 0x07A60871, 0x07A70071, 0x07AB0871, - 0x07AC0071, 0x07B40871, 0x07B50078, 0x07B80083, 0x07B90883, 0x07BB1083, 0x07BD0083, 0x07BF00A7, - 0x07C00883, 0x07C10083, 0x07C20097, 0x07C30083, 0x07C40071, 0x07C60078, 0x07C80083, 0x07C90883, - 0x07CA0083, 0x07CE0883, 0x07CF0083, 0x07D30883, 0x07D40083, 0x07DC0883, 0x07DD0083, 0x07DE0078, - 0x07DF0094, 0x07E60078, 0x07E70094, 0x07E80097, 0x07E90078, 0x08000071, 0x08150078, 0x08160083, - 0x081800A7, 0x08190078, 0x081B0083, 0x081D0078, 0x0820017E, 0x0821017F, 0x08220180, 0x08230181, - 0x08240182, 0x08250097, 0x08280071, 0x082B00A7, 0x082C0083, 0x082D0078, 0x085000B5, 0x08630078, - 0x08680071, 0x087D0097, 0x087E0078, 0x08800071, 0x08AD0078, 0x08AF0071, 0x08D10078, 0x08D40071, - 0x08FD0078, 0x09000071, 0x09240078, 0x09250071, 0x09270078, 0x09280071, 0x092B0078, 0x092D0071, - 0x092F0078, 0x09300071, 0x09440078, 0x09450071, 0x09470078, 0x09480071, 0x09580078, 0x09590071, - 0x095B0078, 0x095C0071, 0x095F0078, 0x09610071, 0x09630078, 0x09640071, 0x096B0078, 0x096C0071, - 0x09880078, 0x09890071, 0x098B0078, 0x098C0071, 0x09AD0078, 0x09AF0083, 0x09B00097, 0x09B400AD, - 0x09B500AE, 0x09B6012D, 0x09B7012E, 0x09B8012F, 0x09B90185, 0x09BA0186, 0x09BB0187, 0x09BC0188, - 0x09BD0184, 0x09BE0078, 0x09C00071, 0x09C80054, 0x09CD0078, 0x09D00071, 0x09FA0078, 0x0A000071, - 0x0B360097, 0x0B370071, 0x0B3B0078, 0x0B400071, 0x0B4D00B4, 0x0B4E0078, 0x0B500071, 0x0B750097, - 0x0B770189, 0x0B780078, 0x0B800071, 0x0B860078, 0x0B870071, 0x0B890083, 0x0B8A0078, 0x0B900071, - 0x0B990083, 0x0B9A0097, 0x0B9B0078, 0x0BA00071, 0x0BA90083, 0x0BAA0078, 0x0BB00071, 0x0BB60078, - 0x0BB70071, 0x0BB80078, 0x0BB90083, 0x0BBA0078, 0x0BC00071, 0x0BDA00C2, 0x0BDB0083, 0x0BDF00A7, - 0x0BE40083, 0x0BEA0097, 0x0BEB0081, 0x0BEC0097, 0x0BED0008, 0x0BEE0083, 0x0BEF0078, 0x0BF0017E, - 0x0BF1017F, 0x0BF20180, 0x0BF30181, 0x0BF40182, 0x0BF50078, 0x0BF80106, 0x0BF90107, 0x0BFA0108, - 0x0BFB0109, 0x0BFC010A, 0x0BFD0078, 0x0C000006, 0x0C050083, 0x0C070078, 0x0C08017E, 0x0C09017F, - 0x0C0A0180, 0x0C0B0181, 0x0C0C0182, 0x0C0D0078, 0x0C100071, 0x0C210081, 0x0C220071, 0x0C3C0078, - 0x0C400071, 0x0C540083, 0x0C550078, 0x0C800071, 0x0C8E0078, 0x0C900083, 0x0C9100A7, 0x0C930083, - 0x0C9400C8, 0x0C960078, 0x0C9800A7, 0x0C9C0083, 0x0C9E0078, 0x0CA20006, 0x0CA3017E, 0x0CA4017F, - 0x0CA50180, 0x0CA60181, 0x0CA70182, 0x0CA80071, 0x0CB70078, 0x0CB80071, 0x0CBA0078, 0x0CC00071, - 0x0CD50078, 0x0CD800A7, 0x0CE00071, 0x0CE400A7, 0x0CE50078, 0x0CE8017E, 0x0CE9017F, 0x0CEA0180, - 0x0CEB0181, 0x0CEC0182, 0x0CED0078, 0x0CEF0006, 0x0CF00054, 0x0D000071, 0x0D0B0083, 0x0D0C00A7, - 0x0D0E0078, 0x0D0F0097, 0x0D100078, 0x0E800055, 0x0E967881, 0x0E970081, 0x0E987881, 0x0E9D0081, - 0x0E9E7881, 0x0EB17055, 0x0EB50055, 0x0ECD7881, 0x0EE00083, 0x0EE20078, 0x0F000865, 0x0F4B0855, - 0x0F4D098A, 0x0F4E0078, 0x0F500865, 0x0F7D0078, 0x0F8008C9, 0x0F8408CA, 0x0F8808C9, 0x0F8B0078, - 0x0F8C08CA, 0x0F8F0078, 0x0F9008C9, 0x0F9408CA, 0x0F9808C9, 0x0F9C08CA, 0x0FA008C9, 0x0FA30078, - 0x0FA408CA, 0x0FA70078, 0x0FA808C9, 0x0FAC08CA, 0x0FB008C9, 0x0FB408CA, 0x0FB808CB, 0x0FB908CC, - 0x0FBB08CD, 0x0FBC08CE, 0x0FBD08CF, 0x0FBE08D0, 0x0FBF0078, 0x0FC008C9, 0x0FC408D1, 0x0FC808C9, - 0x0FCC08D1, 0x0FD008C9, 0x0FD408D1, 0x0FD808C9, 0x0FD9098B, 0x0FDA0078, 0x0FDB0855, 0x0FDC08CA, - 0x0FDD08D2, 0x0FDE1037, 0x0FE00837, 0x0FE1098B, 0x0FE20078, 0x0FE30855, 0x0FE408D5, 0x0FE60837, - 0x0FE808C9, 0x0FE90855, 0x0FEA0078, 0x0FEB0855, 0x0FEC08CA, 0x0FED08D6, 0x0FEE0837, 0x0FF008C9, - 0x0FF10855, 0x0FF20890, 0x0FF30855, 0x0FF408CA, 0x0FF508D7, 0x0FF60837, 0x0FF80078, 0x0FF9098B, - 0x0FFA0078, 0x0FFB0855, 0x0FFC08D9, 0x0FFD08DA, 0x0FFE0837, 0x0FFF0078, 0x10000805, 0x10011005, - 0x10035805, 0x10041005, 0x10050057, 0x1007018C, 0x10085899, 0x10090099, 0x100B1006, 0x100C018D, - 0x100D00DB, 0x100E018D, 0x100F00DB, 0x10100006, 0x10121006, 0x10130006, 0x1014018E, 0x1015018F, - 0x10160190, 0x10175853, 0x10180007, 0x10191007, 0x101A0006, 0x101B1006, 0x101C0126, 0x101D0006, - 0x101F0038, 0x10200006, 0x10220009, 0x10231006, 0x10250006, 0x102B1006, 0x102C0006, 0x102F1005, - 0x10300057, 0x10320078, 0x10350057, 0x10387855, 0x10390078, 0x103A7910, 0x103B7911, 0x103C7912, - 0x103D780B, 0x103E7809, 0x103F7855, 0x1040705D, 0x1041705B, 0x10427110, 0x10437111, 0x10447112, - 0x1045700B, 0x10467009, 0x10470078, 0x10487081, 0x104A0078, 0x10500008, 0x105B0078, 0x10680083, - 0x106E0095, 0x10700083, 0x10710095, 0x10720083, 0x10760078, 0x10801054, 0x10831077, 0x10841054, - 0x10852877, 0x10872855, 0x10882877, 0x10892855, 0x108A2877, 0x108B0054, 0x108C2877, 0x108F0054, - 0x10901054, 0x10910054, 0x10950991, 0x10962877, 0x10972855, 0x10982877, 0x109A1071, 0x109C2855, - 0x109D1054, 0x109E2855, 0x109F2877, 0x10A00019, 0x10A22877, 0x10A32855, 0x10A50019, 0x10A60078, - 0x10A9305F, 0x10AF3106, 0x10B01192, 0x10B11193, 0x10B21194, 0x10B31195, 0x10B41196, 0x10B51197, - 0x10B61198, 0x10B71199, 0x10B8119A, 0x10B9119B, 0x10BA119C, 0x10BB119D, 0x10BC119E, 0x10BD119F, - 0x10BE11A0, 0x10BF11A1, 0x10C001A2, 0x10C101A3, 0x10C20078, 0x10C80019, 0x10CA0054, 0x10CD0819, - 0x10CE0054, 0x10D10019, 0x10D20054, 0x10E60854, 0x10E70819, 0x10E80054, 0x10FA0019, 0x110000E8, - 0x11020019, 0x110408FB, 0x110500FC, 0x11070019, 0x110800E8, 0x11090059, 0x110A01A4, 0x110B0019, - 0x110D00E8, 0x11110019, 0x111500E8, 0x111610E8, 0x111800E8, 0x111A0019, 0x111C00E8, 0x111E00FE, - 0x111F00E8, 0x112008E8, 0x112101A5, 0x112200E8, 0x112308E8, 0x112500E8, 0x11260019, 0x112900FE, - 0x112B0019, 0x112F00E8, 0x11300019, 0x113200FE, 0x11360819, 0x113708FE, 0x113900FE, 0x113A08FE, - 0x113B00FE, 0x113C08FE, 0x113D00FE, 0x114008FE, 0x114100FE, 0x114208FE, 0x114300FE, 0x114408FE, - 0x114500FE, 0x11460019, 0x114700FD, 0x11490019, 0x115100FE, 0x11520019, 0x115300E8, 0x115401A6, - 0x115608E8, 0x115800FE, 0x115C0019, 0x115F00E8, 0x11600019, 0x116400FD, 0x116601A7, 0x11670019, - 0x116800FE, 0x11690019, 0x116B00FE, 0x117008FE, 0x117200FE, 0x117508FE, 0x11770019, 0x117800FE, - 0x11790102, 0x117A00E8, 0x117B0103, 0x117C00E8, 0x117D0104, 0x117E0105, 0x117F00E8, 0x11800054, - 0x118400FE, 0x11860054, 0x119000E8, 0x11910054, 0x11940809, 0x11950054, 0x119B0094, 0x11BD0054, - 0x11CA0094, 0x11CB0054, 0x11CD0019, 0x11DA00BF, 0x11DB0054, 0x11EE0078, 0x12000054, 0x12130078, - 0x12200054, 0x12250078, 0x123018C4, 0x123118C5, 0x123218C6, 0x123318C7, 0x1234191F, 0x1235191A, - 0x1236191B, 0x1237191C, 0x1238191D, 0x1239191E, 0x123A10C4, 0x123B10C5, 0x123C10C6, 0x123D10C7, - 0x123E111F, 0x123F111A, 0x1240111B, 0x1241111C, 0x1242111D, 0x1243111E, 0x1244105A, 0x124510E3, - 0x124610E4, 0x124710E5, 0x124811A8, 0x124911A9, 0x124A11AA, 0x124B11AB, 0x124C11AC, 0x124D11AD, - 0x124E1094, 0x125B1918, 0x12681919, 0x1275010B, 0x1276010C, 0x1277010D, 0x1278010E, 0x1279010F, - 0x127A0106, 0x127B0107, 0x127C0108, 0x127D0109, 0x127E010A, 0x127F00C3, 0x12800054, 0x12DB0019, - 0x12DC0054, 0x12E00019, 0x12E10054, 0x12FC0019, 0x13000054, 0x13370019, 0x13380054, 0x134E0078, - 0x13500054, 0x13590078, 0x13800054, 0x13820078, 0x13830054, 0x13850078, 0x13860054, 0x13A90078, - 0x13AC0054, 0x13AF0078, 0x13B00054, 0x13B4000A, 0x13BB00C4, 0x13BC00C5, 0x13BD00C6, 0x13BE00C7, - 0x13BF011F, 0x13C000C4, 0x13C100C5, 0x13C200C6, 0x13C300C7, 0x13C4011F, 0x13C500C4, 0x13C600C5, - 0x13C700C6, 0x13C800C7, 0x13C9011F, 0x13CA0078, 0x13CC0054, 0x13DF0078, 0x13E00019, 0x13E100FD, - 0x13E20009, 0x13E30078, 0x13E80019, 0x13E900E8, 0x13EA00FD, 0x13EB0019, 0x13EE00FD, 0x13EF0019, - 0x13F100FE, 0x13F3000A, 0x13F60078, 0x13F80019, 0x14000094, 0x14800019, 0x14C10009, 0x14C601AE, - 0x14C701AF, 0x14C80009, 0x14CC0019, 0x14CD00E8, 0x14D80019, 0x14E000FE, 0x14E100E8, 0x14E200FE, - 0x14E30019, 0x14E400E8, 0x14E50019, 0x14E700FD, 0x14E90019, 0x14EA00FE, 0x14EB0019, 0x14EC000A, - 0x14EE0019, 0x14F000E8, 0x14F30019, 0x14F400E8, 0x14F50019, 0x14FA01B0, 0x14FB00E8, 0x14FC00FE, - 0x14FD0019, 0x14FE000A, 0x14FF0019, 0x150500E8, 0x150E0019, 0x150F00E8, 0x15110019, 0x151400E8, - 0x151500FD, 0x15170019, 0x151A00FE, 0x151B0019, 0x151E00FE, 0x151F0019, 0x152B00E8, 0x152C0019, - 0x153200FE, 0x15330019, 0x153500E8, 0x15380019, 0x153900E8, 0x153A1019, 0x153B0019, 0x153C00FD, - 0x153D00E8, 0x153E00FD, 0x154200E8, 0x154500FD, 0x154600E8, 0x154800FD, 0x154E00E8, 0x155000FD, - 0x155100E8, 0x15520019, 0x155300FE, 0x155700FD, 0x155800E8, 0x155900FD, 0x155A00E8, 0x155D00FD, - 0x156300E8, 0x156600FD, 0x156B0019, 0x157101B1, 0x15730019, 0x157600FE, 0x15770019, 0x157900E8, - 0x157A0019, 0x157B00FD, 0x157D00E8, 0x157F0019, 0x15800054, 0x158A0078, 0x16000096, 0x16170078, - 0x16180098, 0x162F0078, 0x16400065, 0x16720054, 0x16750078, 0x167C0006, 0x167E005F, 0x167F0006, - 0x16800125, 0x16930078, 0x16980071, 0x16B30078, 0x16B77881, 0x16B80078, 0x16C00071, 0x16CB0078, - 0x16D00071, 0x16D30078, 0x16D40071, 0x16D70078, 0x16D80071, 0x16DB0078, 0x16DC0071, 0x16DF0078, - 0x16E00071, 0x16E30078, 0x16E40071, 0x16E70078, 0x16E80071, 0x16EB0078, 0x16EC0071, 0x16EF0078, - 0x17000006, 0x170100E0, 0x17030006, 0x17040126, 0x17050006, 0x170600E0, 0x17070006, 0x170B0099, - 0x170C0078, 0x170E00E0, 0x170F0078, 0x17400054, 0x174F1054, 0x17500054, 0x17791054, 0x177A0078, - 0x17801054, 0x17EB0078, 0x17F80054, 0x17FE0078, 0x18000006, 0x18020081, 0x180301B2, 0x1804000A, - 0x18090054, 0x180A000A, 0x180E00B4, 0x180F00BF, 0x181001B3, 0x181101B4, 0x181201B5, 0x181301B6, - 0x181401B7, 0x18150083, 0x18180081, 0x181B0054, 0x181C11B8, 0x181D0081, 0x181E0006, 0x181F0054, - 0x18200071, 0x18320871, 0x18350071, 0x18380871, 0x183A0071, 0x183B0871, 0x183D0071, 0x183E0871, - 0x183F0071, 0x184B0078, 0x184C0083, 0x184D1037, 0x184E0081, 0x184F8071, 0x18500071, 0x18620871, - 0x18650071, 0x18680871, 0x186A0071, 0x186B0871, 0x186D0071, 0x186E0871, 0x186F0071, 0x187B0871, - 0x187D0006, 0x187E0081, 0x187F8071, 0x18800078, 0x18820071, 0x18960078, 0x18981071, 0x18C70078, - 0x18C80094, 0x18C978B6, 0x18CA78B7, 0x18CB7894, 0x18D00071, 0x18DC0078, 0x18E00054, 0x18E80078, - 0x18F80071, 0x19001094, 0x190E1054, 0x190F0078, 0x191010B6, 0x191110B7, 0x191210B8, 0x191310B9, - 0x191410B0, 0x19151094, 0x19220078, 0x192819B9, 0x192919BA, 0x192A19BB, 0x192B19BC, 0x192C19BD, - 0x192D19BE, 0x192E19BF, 0x192F19C0, 0x19301894, 0x193E1854, 0x193F0094, 0x194018B6, 0x194118B7, - 0x194218B8, 0x194318B9, 0x194418B0, 0x19451894, 0x195819C1, 0x195919C2, 0x195A19C3, 0x195B19C4, - 0x195C19C5, 0x195D19C6, 0x195E19C7, 0x195F19C8, 0x19601094, 0x19666854, 0x19681894, 0x197F0078, - 0x19806894, 0x19AC1094, 0x19B86894, 0x19BB6854, 0x19BD6894, 0x19EF6854, 0x19F01094, 0x19FF6854, - 0x1A000071, 0x26DB0078, 0x26E00054, 0x27000071, 0x4FDE0078, 0x50000071, 0x500A0081, 0x500B0071, - 0x52460078, 0x52480054, 0x52630078, 0x53800037, 0x538B0078, 0x54000071, 0x54050083, 0x54060071, - 0x541100A7, 0x54120083, 0x541300A7, 0x54140054, 0x54160078, 0x56000071, 0x6BD20078, 0x6C00013E, - 0x7000013F, 0x7C800871, 0x7D070071, 0x7D0A0871, 0x7D0F0071, 0x7D120871, 0x7D130071, 0x7D150871, - 0x7D170078, 0x7D180871, 0x7D350078, 0x7D380871, 0x7D6D0078, 0x7D801055, 0x7D830078, 0x7D891055, - 0x7D8C0078, 0x7D8E089B, 0x7D90289B, 0x7D94280B, 0x7D95089B, 0x7D9B0078, 0x7D9C089B, 0x7D9E0078, - 0x7DA0089B, 0x7DA20078, 0x7DA3089B, 0x7DA7109B, 0x7DA8209E, 0x7DAA489E, 0x7DAB209E, 0x7DAC489E, - 0x7DAD209E, 0x7DAE489E, 0x7DAF209E, 0x7DB0489E, 0x7DB1209E, 0x7DB2489E, 0x7DB3209E, 0x7DB4489E, - 0x7DB5209E, 0x7DB6489E, 0x7DB7209E, 0x7DB8489E, 0x7DB9209E, 0x7DBA489E, 0x7DBB209E, 0x7DBC489E, - 0x7DBD209E, 0x7DBE489E, 0x7DBF209E, 0x7DC0489E, 0x7DC1209E, 0x7DC8489E, 0x7DC9209E, 0x7DCA489E, - 0x7DCB209E, 0x7DCC489E, 0x7DCD209E, 0x7DCE489E, 0x7DCF209E, 0x7DD1489E, 0x7DD2209E, 0x7DD4489E, - 0x7DD5209E, 0x7DD6489E, 0x7DD7209E, 0x7DD90078, 0x7DE9409E, 0x7DEA389E, 0x7DEB409E, 0x7DEF209E, - 0x7DF3489E, 0x7DF5209E, 0x7DFC409E, 0x7DFD389E, 0x7DFE209E, 0x7DFF489E, 0x7E00409E, 0x7E32209E, - 0x7E4B389E, 0x7E6F489E, 0x7E7A409E, 0x7E88209E, 0x7E96389E, 0x7E9A489E, 0x7E9E409E, 0x7E9F00BF, - 0x7EA00078, 0x7EA8209E, 0x7EA9389E, 0x7EAD209E, 0x7EAE389E, 0x7EAF209E, 0x7EB0389E, 0x7EB3209E, - 0x7EB5389E, 0x7EB7209E, 0x7EB9389E, 0x7EBA209E, 0x7EBB389E, 0x7EBC209E, 0x7EBE389E, 0x7EBF209E, - 0x7EC1389E, 0x7EC2209E, 0x7EC4389E, 0x7EC5209E, 0x7EC6389E, 0x7EC80078, 0x7EC9389E, 0x7ECB209E, - 0x7ECE389E, 0x7ECF209E, 0x7EDA389E, 0x7EDB209E, 0x7EE1389E, 0x7EE3209E, 0x7EE40078, 0x7EF8409E, - 0x7EFE0054, 0x7EFF0078, 0x7F000083, 0x7F088006, 0x7F0B80B4, 0x7F0C8006, 0x7F0D0078, 0x7F100083, - 0x7F120078, 0x7F188099, 0x7F198038, 0x7F1A80B4, 0x7F220006, 0x7F2380B4, 0x7F241006, 0x7F261038, - 0x7F286006, 0x7F290078, 0x7F2A600C, 0x7F2B6006, 0x7F2C60B4, 0x7F2F6007, 0x7F306006, 0x7F31600D, - 0x7F326019, 0x7F330078, 0x7F346008, 0x7F356006, 0x7F360078, 0x7F38489E, 0x7F39009E, 0x7F3A0078, - 0x7F3B489E, 0x7F40409E, 0x7F45389E, 0x7F46409E, 0x7F48389E, 0x7F49409E, 0x7F4B389E, 0x7F4C409E, - 0x7F4D389E, 0x7F4E409E, 0x7F4F389E, 0x7F50409E, 0x7F51389E, 0x7F52409E, 0x7F53389E, 0x7F54409E, - 0x7F59389E, 0x7F5A409E, 0x7F5B389E, 0x7F5C409E, 0x7F5D389E, 0x7F5E409E, 0x7F5F389E, 0x7F60409E, - 0x7F61389E, 0x7F62409E, 0x7F63389E, 0x7F64409E, 0x7F65389E, 0x7F66409E, 0x7F67389E, 0x7F68409E, - 0x7F69389E, 0x7F6A409E, 0x7F6B389E, 0x7F6C409E, 0x7F6D389E, 0x7F6E409E, 0x7F6F389E, 0x7F70409E, - 0x7F71389E, 0x7F72409E, 0x7F73389E, 0x7F74409E, 0x7F75389E, 0x7F76409E, 0x7F79389E, 0x7F7A409E, - 0x7F7E0078, 0x7F7F0057, 0x7F808806, 0x7F818807, 0x7F838806, 0x7F84880A, 0x7F85880B, 0x7F86880D, - 0x7F87880C, 0x7F88880F, 0x7F898811, 0x7F8A8813, 0x7F8B8815, 0x7F8C8817, 0x7F8D8806, 0x7F8E8819, - 0x7F8F8806, 0x7F908860, 0x7F9D8835, 0x7F9E8836, 0x7F9F8838, 0x7FA08861, 0x7FAD8835, 0x7FAE8836, - 0x7FAF8809, 0x7FB05006, 0x7FB1500A, 0x7FB25006, 0x7FB35071, 0x7FCF5081, 0x7FD05071, 0x7FDF0078, - 0x7FE15071, 0x7FE40078, 0x7FE55071, 0x7FE80078, 0x7FE95071, 0x7FEC0078, 0x7FED5071, 0x7FEE0078, - 0x7FF08808, 0x7FF18837, 0x7FF28808, 0x7FF30078, 0x7FF45019, 0x7FF65054, 0x7FF70078, 0x7FFC0141, - 0x7FFE0054, 0x7FFF0078, 0x80000071, 0x80130078, 0x80140071, 0x801D0078, 0x801E0071, 0x80270078, - 0x80280071, 0x802F0078, 0x80400071, 0x807D0078, 0x80800006, 0x80810078, 0x808300AD, 0x808400AE, - 0x8085012D, 0x8086012E, 0x8087012F, 0x80880185, 0x80890186, 0x808A0187, 0x808B0188, 0x808C0184, - 0x808D01C9, 0x808E01CA, 0x808F01CB, 0x809001CC, 0x809101CD, 0x809201CE, 0x809301CF, 0x809401D0, - 0x809500BE, 0x809601D1, 0x809701D2, 0x809801D3, 0x809901D4, 0x809A0078, 0x809B0094, 0x80A0014E, - 0x80A10152, 0x80A20153, 0x80A30157, 0x80A40154, 0x80A50155, 0x80A60156, 0x80A70152, 0x80A80150, - 0x80A90153, 0x80AA01D5, 0x80AB0154, 0x80AC014F, 0x80AD0158, 0x80AF0152, 0x80B00154, 0x80B201D6, - 0x80B30150, 0x80B501D7, 0x80B60153, 0x80B80156, 0x80B90152, 0x80BA005F, 0x80BC0054, 0x80C50078, - 0x81800071, 0x818F0078, 0x8190012D, 0x819100BB, 0x81920078, 0x81980071, 0x81A50078, 0x81C00071, - 0x81CF0097, 0x81D00071, 0x81E20078, 0x81E40071, 0x81E8014F, 0x81E90154, 0x81EA0155, 0x81EB0078, - 0x8200015B, 0x8214015C, 0x82280071, 0x824F0078, 0x8250017E, 0x8251017F, 0x82520180, 0x82530181, - 0x82540182, 0x82550078, 0x8400009B, 0x84030078, 0x8405009B, 0x841C0078, 0x841F009B, 0x84200078, - 0x85000083, 0x85030078, 0x85060083, 0x8508009B, 0x851A0078, 0x851C0083, 0x851D0078, 0x851F0083, - 0x852001D8, 0x852101D9, 0x852201DA, 0x852301DB, 0x85240078, 0x8528009A, 0x852C0078, 0xE8000094, - 0xE87B0078, 0xE8800094, 0xE8930078, 0xE8950094, 0xE8AF0894, 0xE8B200A7, 0xE8B30083, 0xE8B50094, - 0xE8B600A7, 0xE8B90057, 0xE8BD0083, 0xE8C10094, 0xE8C20083, 0xE8C60094, 0xE8D50083, 0xE8D70094, - 0xE8DD0894, 0xE8E00094, 0xE8EF0078, 0xE9000054, 0xE9210083, 0xE9220054, 0xE9230078, 0xE9800054, - 0xE9AB0078, 0xEA002877, 0xEA0D2855, 0xEA1A2877, 0xEA272855, 0xEA2A0078, 0xEA2B2855, 0xEA342877, - 0xEA412855, 0xEA4E0078, 0xEA4F2877, 0xEA500078, 0xEA522877, 0xEA530078, 0xEA542877, 0xEA560078, - 0xEA572877, 0xEA5B2855, 0xEA682877, 0xEA752855, 0xEA822877, 0xEA850078, 0xEA862877, 0xEA8A0078, - 0xEA8B2877, 0xEA8E0078, 0xEA8F2855, 0xEA9C2877, 0xEA9F0078, 0xEAA02877, 0xEAA20078, 0xEAA52877, - 0xEAA80078, 0xEAA92855, 0xEAB62877, 0xEAC32855, 0xEAD02877, 0xEADD2855, 0xEAEA2877, 0xEAF72855, - 0xEB042877, 0xEB112855, 0xEB1E2877, 0xEB2B2855, 0xEB382877, 0xEB452855, 0xEB530078, 0xEB542877, - 0xEB6029DC, 0xEB612855, 0xEB6D29DC, 0xEB6E2855, 0xEB712877, 0xEB7D29DC, 0xEB7E2855, 0xEB8A29DC, - 0xEB8B2855, 0xEB8E2877, 0xEB9A29DC, 0xEB9B2855, 0xEBA729DC, 0xEBA82855, 0xEBAB2877, 0xEBB729DC, - 0xEBB82855, 0xEBC429DC, 0xEBC52855, 0xEBC82877, 0xEBD429DC, 0xEBD52855, 0xEBE129DC, 0xEBE22855, - 0xEBE50078, 0xEBE7280F, 0xEBE82811, 0xEBE92813, 0xEBEA2815, 0xEBEB2817, 0xEBEC280F, 0xEBED2811, - 0xEBEE2813, 0xEBEF2815, 0xEBF02817, 0xEBF1280F, 0xEBF22811, 0xEBF32813, 0xEBF42815, 0xEBF52817, - 0xEBF6280F, 0xEBF72811, 0xEBF82813, 0xEBF92815, 0xEBFA2817, 0xEBFB280F, 0xEBFC2811, 0xEBFD2813, - 0xEBFE2815, 0xEBFF2817, 0xEC000078 - }; - - static const uint32_t a17[] = { - 0x00000071, 0x536B0078, 0x7C000871, 0x7D0F0078 - }; - - static const uint32_t a23[] = { - 0x00000057, 0x00010078, 0x00100057, 0x00400078, 0x00800083, 0x00F80078, 0x8000013F, 0xFFFF0078 - }; - - static const uint32_t a24[] = { - 0x0000013F, 0x7FFF0078 - }; - - - // The full set of all arrays to be searched. - static const Range FULL_DATA[] = { - {sizeof(a0)/sizeof(uint32_t), a0}, - {sizeof(a1)/sizeof(uint32_t), a1}, - {0, 0}, - {0, 0}, - {0, 0}, - {0, 0}, - {0, 0}, - {sizeof(a7)/sizeof(uint32_t), a7}, - {sizeof(a8)/sizeof(uint32_t), a8}, - {0, 0}, - {0, 0}, - {0, 0}, - {0, 0}, - {0, 0}, - {0, 0}, - {0, 0}, - {sizeof(a16)/sizeof(uint32_t), a16}, - {sizeof(a17)/sizeof(uint32_t), a17}, - {0, 0}, - {0, 0}, - {0, 0}, - {0, 0}, - {0, 0}, - {sizeof(a23)/sizeof(uint32_t), a23}, - {sizeof(a24)/sizeof(uint32_t), a24}, - {0, 0}, - {0, 0}, - {0, 0}, - {0, 0}, - {0, 0}, - {0, 0}, - {0, 0} - }; - - // Array of uppercase differences - static const short UCDIFF[] = { - 0, -32, 743, 121, -1, -232, -300, 97, - 163, 130, 56, -2, -79, -210, -206, -205, - -202, -203, -207, -209, -211, -213, -214, -218, - -217, -219, -83, 84, -38, -37, -31, -64, - -63, -62, -57, -47, -54, -86, -80, 7, - -96, -48, -59, 8, 74, 86, 100, 128, - 112, 126, 9, -7205, -16, -26, -7264, -40 - }; - - // Array of lowercase differences - static const short LCDIFF[] = { - 0, 32, 1, -199, -121, 210, 206, 205, - 79, 202, 203, 207, 211, 209, 213, 214, - 218, 217, 219, 2, -97, -56, -130, -163, - 83, 38, 37, 64, 63, -60, -7, 80, - 48, 7264, -8, -74, -9, -86, -100, -112, - -128, -126, -7517, -8383, -8262, 16, 26, 40 - }; - - // Array of titlecase differences - static const short TCDIFF[] = { - 3, 1, 0, -1 - }; - - // Array of mirrored character differences - static const short MIRROR_DIFF[] = { - 0, 1, -1, 2, -2, 16, -16, 3, - -3, 2016, 138, 1824, 2104, 2108, 2106, -138, - 8, 7, -8, -7, -1824, -2016, -2104, -2106, - -2108 - }; - - // Array of all possible numeric values - static const int NUMERICS[] = { - -1, 0, 1, 2, 3, 4, 5, 6, - 7, 8, 9, 10, 11, 12, 13, 14, - 15, 16, 17, 18, 19, 20, 21, 22, - 23, 24, 25, 26, 27, 28, 29, 30, - 31, 32, 33, 34, 35, -2, 100, 1000, - 40, 50, 60, 70, 80, 90, 10000, 500, - 5000, 36, 37, 38, 39, 41, 42, 43, - 44, 45, 46, 47, 48, 49, 200, 300, - 400, 600, 700, 800, 900, 2000, 3000, 4000, - 6000, 7000, 8000, 9000, 20000, 30000, 40000, 50000, - 60000, 70000, 80000, 90000 - }; - - // All possible packed data values, no duplicates - static const uint32_t PACKED_DATA[] = { - 0x00000000, 0x0000012F, 0x0000016F, 0x0000014F, 0x0000018F, 0x0000018C, 0x000001B8, 0x000000B8, - 0x000000BA, 0x020005B5, 0x040005B6, 0x00000099, 0x000000F8, 0x00000094, 0x02000069, 0x04000069, - 0x06000069, 0x08000069, 0x0A000069, 0x0C000069, 0x0E000069, 0x10000069, 0x12000069, 0x14000069, - 0x060005B9, 0x000001B9, 0x080005B9, 0x16020001, 0x18020001, 0x1A020001, 0x1C020001, 0x1E020001, - 0x20020001, 0x22020001, 0x24020001, 0x26020001, 0x28020001, 0x2A020001, 0x2C020001, 0x2E020001, - 0x30020001, 0x32020001, 0x34020001, 0x36020001, 0x38020001, 0x3A020001, 0x3C020001, 0x3E020001, - 0x40020001, 0x42020001, 0x44020001, 0x46020001, 0x48020001, 0x060005B5, 0x080005B6, 0x000001BB, - 0x000001B7, 0x16000802, 0x18000802, 0x1A000802, 0x1C000802, 0x1E000802, 0x20000802, 0x22000802, - 0x24000802, 0x26000802, 0x28000802, 0x2A000802, 0x2C000802, 0x2E000802, 0x30000802, 0x32000802, - 0x34000802, 0x36000802, 0x38000802, 0x3A000802, 0x3C000802, 0x3E000802, 0x40000802, 0x42000802, - 0x44000802, 0x46000802, 0x48000802, 0x000000EC, 0x000001BC, 0x00000002, 0x0A0005BD, 0x00000130, - 0x000000BC, 0x000000B9, 0x0600006B, 0x0800006B, 0x00001002, 0x0400006B, 0x0C0005BE, 0x4A0001AB, - 0x00020001, 0x00000802, 0x00001802, 0x00040001, 0x00060001, 0x00002002, 0x00080001, 0x000C0001, - 0x000E0001, 0x00100001, 0x00140001, 0x00160001, 0x00180001, 0x00004002, 0x00004802, 0x00200001, - 0x00220001, 0x00000005, 0x00A60001, 0x01805802, 0x01042003, 0x00280001, 0x002C0001, 0x00000001, - 0x00000000, 0x00007002, 0x00007802, 0x00009802, 0x0000A802, 0x0000B802, 0x0000C002, 0x0000C802, - 0x0000D002, 0x00000004, 0x000001A4, 0x00000106, 0x00320001, 0x00340001, 0x00360001, 0x00380001, - 0x0000E002, 0x0000E802, 0x0000F002, 0x0000F802, 0x00010002, 0x00010802, 0x00012002, 0x00012802, - 0x00013802, 0x003A0001, 0x003E0001, 0x00013002, 0x0000001C, 0x00000107, 0x00400001, 0x00000018, - 0x00014802, 0x000001B4, 0x00000038, 0x00000025, 0x00000050, 0x00000058, 0x00000045, 0x00000044, - 0x020000C9, 0x060000C9, 0x0A0000C9, 0x0E0000C9, 0x120000C9, 0x000000D8, 0x0000005C, 0x00000008, - 0x02000009, 0x06000009, 0x0A000009, 0x0E000009, 0x12000009, 0x0400000B, 0x0800000B, 0x0000000B, - 0x1600000B, 0x4E00000B, 0x00000006, 0x4A00000B, 0x000001B5, 0x00420001, 0x0600000B, 0x0A00000B, - 0x0E00000B, 0x1200000B, 0x3E00000B, 0x5200000B, 0x5600000B, 0x5A00000B, 0x5C00000B, 0x000001B6, - 0x2400000A, 0x2800000A, 0x00000010, 0x020001AB, 0x060001AB, 0x0A0001AB, 0x0E0001AB, 0x120001AB, - 0x00000108, 0x00015802, 0x00440001, 0x00016002, 0x00016802, 0x00017002, 0x00017802, 0x00018002, - 0x00018802, 0x00440003, 0x00460001, 0x00480003, 0x00019802, 0x004A0001, 0x004C0001, 0x004E0001, - 0x003C0001, 0x00500001, 0x00520001, 0x000001BD, 0x0000018D, 0x000001D0, 0x00000250, 0x00000230, - 0x040005BE, 0x000000F9, 0x0200006B, 0x0A00006B, 0x0E00006B, 0x1200006B, 0x00540001, 0x00560001, - 0x000005B9, 0x045A000A, 0x085A000A, 0x0C5A000A, 0x105A000A, 0x145A000A, 0x185A000A, 0x525A000A, - 0x5E5A000A, 0x0401A00A, 0x0801A00A, 0x0C01A00A, 0x1001A00A, 0x1401A00A, 0x1801A00A, 0x5201A00A, - 0x5E01A00A, 0x4E00000A, 0x5C00000A, 0x0E0005B9, 0x100005B9, 0x020005B9, 0x040005B9, 0x160005B9, - 0x180005B9, 0x1A0005B9, 0x200005B9, 0x220005B9, 0x240005B9, 0x260005B9, 0x040001AB, 0x080001AB, - 0x0C0001AB, 0x100001AB, 0x140001AB, 0x180001AB, 0x1C0001AB, 0x200001AB, 0x240001AB, 0x280001AB, - 0x0C00006B, 0x1000006B, 0x1400006B, 0x1800006B, 0x1C00006B, 0x2000006B, 0x2400006B, 0x2800006B, - 0x005C001C, 0x0001A81C, 0x1A0001AB, 0x1E0001AB, 0x220001AB, 0x260001AB, 0x2A0001AB, 0x160001AB, - 0x020005B6, 0x100005B6, 0x280005B9, 0x2C0005B9, 0x300005B9, 0x0001B002, 0x020005BD, 0x0600000A, - 0x0A00000A, 0x0E00000A, 0x1200000A, 0x1600000A, 0x3E00000A, 0x0C00000B, 0x1000000B, 0x1400000B, - 0x2E0001AB, 0x320001AB, 0x360001AB, 0x3A0001AB, 0x3E0001AB, 0x420001AB, 0x460001AB, 0x640001AB, - 0x680001AB, 0x6A0001AB, 0x6E0001AB, 0x720001AB, 0x760001AB, 0x7A0001AB, 0x00000013, 0x00000012, - 0x0000005A, 0x000001B0, 0x7C00000B, 0x8000000B, 0x8200000B, 0x8600000B, 0x8C00000B, 0x6000000B, - 0x9200000B, 0x9600000B, 0x9800000B, 0x9C00000B, 0xA000000B, 0xA400000B, 0x4A0001AA, 0x040001AA, - 0x520001AA, 0x600001AA, 0x0C0001AA, 0x5E0001AA, 0x160001AA, 0x4C0001AA, 0x4E0001AA, 0x9E0001AA, - 0x060001AA, 0x8800000A, 0x2A0001AA, 0x005E0001, 0x0001B802, 0x0400002B, 0x0800002B, 0x1600002B, - 0x4C00002B, 0x00002802, 0x00003002, 0x000A0001, 0x00120001, 0x00003802, 0x001A0001, 0x001C0001, - 0x001E0001, 0x00240001, 0x00005002, 0x00006002, 0x002A0001, 0x002E0001, 0x00300001, 0x00006802, - 0x00008002, 0x00008802, 0x00009002, 0x0000A002, 0x0000B002, 0x0000D906, 0x00011002, 0x00011802, - 0x00014002, 0x040000C9, 0x080000C9, 0x0C0000C9, 0x100000C9, 0x140000C9, 0x04000009, 0x08000009, - 0x0C000009, 0x10000009, 0x14000009, 0x2200000B, 0x4C00000B, 0x2A00000B, 0x5000000B, 0x5400000B, - 0x5800000B, 0x2600000A, 0x00015002, 0x00019002, 0x00000030, 0x000001BE, 0x0000014E, 0x00000210, - 0x000001F0, 0x00580001, 0x065A000A, 0x0A5A000A, 0x0E5A000A, 0x125A000A, 0x165A000A, 0x1A5A000A, - 0x4C5A000A, 0x4E5A000A, 0x0601A00A, 0x0A01A00A, 0x0E01A00A, 0x1201A00A, 0x1601A00A, 0x1A01A00A, - 0x4C01A00A, 0x4E01A00A, 0x6000000A, 0x0000000A, 0x120005B9, 0x140005B9, 0x1C0005B9, 0x1E0005B9, - 0x1600006B, 0x1A00006B, 0x1E00006B, 0x2200006B, 0x2600006B, 0x2A00006B, 0x0E0005B5, 0x040005B5, - 0x2A0005B9, 0x2E0005B9, 0x0200000A, 0x0400000A, 0x0800000A, 0x0C00000A, 0x1000000A, 0x1400000A, - 0x2A00000A, 0x2C0001AB, 0x300001AB, 0x340001AB, 0x380001AB, 0x3C0001AB, 0x400001AB, 0x440001AB, - 0x480001AB, 0x620001AB, 0x660001AB, 0x500001AB, 0x6C0001AB, 0x700001AB, 0x740001AB, 0x780001AB, - 0x520001AB, 0x7E00000B, 0x5E00000B, 0x8400000B, 0x8800000B, 0x8A00000B, 0x8E00000B, 0x9000000B, - 0x9400000B, 0x9A00000B, 0x9E00000B, 0xA200000B, 0xA600000B, 0x5C0001AA, 0x3E0001AA, 0x7E0001AA, - 0x0600002B, 0x0A00002B, 0x2A00002B, 0x4E00002B, 0x00000019 - }; -} diff --git a/libs/utils/executablepath_darwin.cpp b/libs/utils/executablepath_darwin.cpp deleted file mode 100644 index 2e3c3a01f..000000000 --- a/libs/utils/executablepath_darwin.cpp +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#import -#include - -void executablepath(char s[PATH_MAX]) -{ - ProcessSerialNumber psn; - GetCurrentProcess(&psn); - CFDictionaryRef dict; - dict = ProcessInformationCopyDictionary(&psn, 0xffffffff); - CFStringRef value = (CFStringRef)CFDictionaryGetValue(dict, - CFSTR("CFBundleExecutable")); - CFStringGetCString(value, s, PATH_MAX+1, kCFStringEncodingUTF8); -} - diff --git a/libs/utils/executablepath_linux.cpp b/libs/utils/executablepath_linux.cpp deleted file mode 100644 index b8d2a3d6f..000000000 --- a/libs/utils/executablepath_linux.cpp +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include -#include -#include - -void executablepath(char exe[PATH_MAX]) -{ - char proc[100]; - sprintf(proc, "/proc/%d/exe", getpid()); - - int err = readlink(proc, exe, PATH_MAX); -} - diff --git a/libs/utils/futex_synchro.c b/libs/utils/futex_synchro.c deleted file mode 100644 index ba1952033..000000000 --- a/libs/utils/futex_synchro.c +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include - -#include -#include - -#include - -#include - - -// This futex glue code is need on desktop linux, but is already part of bionic. -#if !defined(HAVE_FUTEX_WRAPPERS) - -#include -typedef unsigned int u32; -#define asmlinkage -#define __user -#include -#include - - -int futex (int *uaddr, int op, int val, const struct timespec *timeout, int *uaddr2, int val3) -{ - int err = syscall(SYS_futex, uaddr, op, val, timeout, uaddr2, val3); - return err == 0 ? 0 : -errno; -} - -int __futex_wait(volatile void *ftx, int val, const struct timespec *timeout) -{ - return futex((int*)ftx, FUTEX_WAIT, val, timeout, NULL, 0); -} - -int __futex_wake(volatile void *ftx, int count) -{ - return futex((int*)ftx, FUTEX_WAKE, count, NULL, NULL, 0); -} - -int __atomic_cmpxchg(int old, int _new, volatile int *ptr) -{ - return android_atomic_cmpxchg(old, _new, ptr); -} - -int __atomic_swap(int _new, volatile int *ptr) -{ - return android_atomic_swap(_new, ptr); -} - -int __atomic_dec(volatile int *ptr) -{ - return android_atomic_dec(ptr); -} - -#else // !defined(__arm__) - -int __futex_wait(volatile void *ftx, int val, const struct timespec *timeout); -int __futex_wake(volatile void *ftx, int count); - -int __atomic_cmpxchg(int old, int _new, volatile int *ptr); -int __atomic_swap(int _new, volatile int *ptr); -int __atomic_dec(volatile int *ptr); - -#endif // !defined(HAVE_FUTEX_WRAPPERS) - - -// lock states -// -// 0: unlocked -// 1: locked, no waiters -// 2: locked, maybe waiters - -void futex_mutex_init(futex_mutex_t *m) -{ - m->value = 0; -} - -int futex_mutex_lock(futex_mutex_t *m, unsigned msec) -{ - if(__atomic_cmpxchg(0, 1, &m->value) == 0) { - return 0; - } - if(msec == FUTEX_WAIT_INFINITE) { - while(__atomic_swap(2, &m->value) != 0) { - __futex_wait(&m->value, 2, 0); - } - } else { - struct timespec ts; - ts.tv_sec = msec / 1000; - ts.tv_nsec = (msec % 1000) * 1000000; - while(__atomic_swap(2, &m->value) != 0) { - if(__futex_wait(&m->value, 2, &ts) == -ETIMEDOUT) { - return -1; - } - } - } - return 0; -} - -int futex_mutex_trylock(futex_mutex_t *m) -{ - if(__atomic_cmpxchg(0, 1, &m->value) == 0) { - return 0; - } - return -1; -} - -void futex_mutex_unlock(futex_mutex_t *m) -{ - if(__atomic_dec(&m->value) != 1) { - m->value = 0; - __futex_wake(&m->value, 1); - } -} - -/* XXX *technically* there is a race condition that could allow - * XXX a signal to be missed. If thread A is preempted in _wait() - * XXX after unlocking the mutex and before waiting, and if other - * XXX threads call signal or broadcast UINT_MAX times (exactly), - * XXX before thread A is scheduled again and calls futex_wait(), - * XXX then the signal will be lost. - */ - -void futex_cond_init(futex_cond_t *c) -{ - c->value = 0; -} - -int futex_cond_wait(futex_cond_t *c, futex_mutex_t *m, unsigned msec) -{ - if(msec == FUTEX_WAIT_INFINITE){ - int oldvalue = c->value; - futex_mutex_unlock(m); - __futex_wait(&c->value, oldvalue, 0); - futex_mutex_lock(m, FUTEX_WAIT_INFINITE); - return 0; - } else { - int oldvalue = c->value; - struct timespec ts; - ts.tv_sec = msec / 1000; - ts.tv_nsec = (msec % 1000) * 1000000; - futex_mutex_unlock(m); - const int err = __futex_wait(&c->value, oldvalue, &ts); - futex_mutex_lock(m, FUTEX_WAIT_INFINITE); - return err; - } -} - -void futex_cond_signal(futex_cond_t *c) -{ - __atomic_dec(&c->value); - __futex_wake(&c->value, 1); -} - -void futex_cond_broadcast(futex_cond_t *c) -{ - __atomic_dec(&c->value); - __futex_wake(&c->value, INT_MAX); -} - diff --git a/libs/utils/misc.cpp b/libs/utils/misc.cpp deleted file mode 100644 index dc89d15b6..000000000 --- a/libs/utils/misc.cpp +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Copyright (C) 2005 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. - */ - -// -// Miscellaneous utility functions. -// -#include - -#include -#include -#include -#include -#include - -using namespace android; - -namespace android { - -/* - * Like strdup(), but uses C++ "new" operator instead of malloc. - */ -char* strdupNew(const char* str) -{ - char* newStr; - int len; - - if (str == NULL) - return NULL; - - len = strlen(str); - newStr = new char[len+1]; - memcpy(newStr, str, len+1); - - return newStr; -} - -/* - * Concatenate an argument vector. - */ -char* concatArgv(int argc, const char* const argv[]) -{ - char* newStr = NULL; - int len, totalLen, posn, idx; - - /* - * First, figure out the total length. - */ - totalLen = idx = 0; - while (1) { - if (idx == argc || argv[idx] == NULL) - break; - if (idx) - totalLen++; // leave a space between args - totalLen += strlen(argv[idx]); - idx++; - } - - /* - * Alloc the string. - */ - newStr = new char[totalLen +1]; - if (newStr == NULL) - return NULL; - - /* - * Finally, allocate the string and copy data over. - */ - idx = posn = 0; - while (1) { - if (idx == argc || argv[idx] == NULL) - break; - if (idx) - newStr[posn++] = ' '; - - len = strlen(argv[idx]); - memcpy(&newStr[posn], argv[idx], len); - posn += len; - - idx++; - } - - assert(posn == totalLen); - newStr[posn] = '\0'; - - return newStr; -} - -/* - * Count the #of args in an argument vector. Don't count the final NULL. - */ -int countArgv(const char* const argv[]) -{ - int count = 0; - - while (argv[count] != NULL) - count++; - - return count; -} - - -#include -/* - * Get a file's type. - */ -FileType getFileType(const char* fileName) -{ - struct stat sb; - - if (stat(fileName, &sb) < 0) { - if (errno == ENOENT || errno == ENOTDIR) - return kFileTypeNonexistent; - else { - fprintf(stderr, "getFileType got errno=%d on '%s'\n", - errno, fileName); - return kFileTypeUnknown; - } - } else { - if (S_ISREG(sb.st_mode)) - return kFileTypeRegular; - else if (S_ISDIR(sb.st_mode)) - return kFileTypeDirectory; - else if (S_ISCHR(sb.st_mode)) - return kFileTypeCharDev; - else if (S_ISBLK(sb.st_mode)) - return kFileTypeBlockDev; - else if (S_ISFIFO(sb.st_mode)) - return kFileTypeFifo; -#ifdef HAVE_SYMLINKS - else if (S_ISLNK(sb.st_mode)) - return kFileTypeSymlink; - else if (S_ISSOCK(sb.st_mode)) - return kFileTypeSocket; -#endif - else - return kFileTypeUnknown; - } -} - -/* - * Get a file's modification date. - */ -time_t getFileModDate(const char* fileName) -{ - struct stat sb; - - if (stat(fileName, &sb) < 0) - return (time_t) -1; - - return sb.st_mtime; -} - -/* - * Round up to the next highest power of 2. - * - * Found on http://graphics.stanford.edu/~seander/bithacks.html. - */ -unsigned int roundUpPower2(unsigned int val) -{ - val--; - val |= val >> 1; - val |= val >> 2; - val |= val >> 4; - val |= val >> 8; - val |= val >> 16; - val++; - - return val; -} - -}; // namespace android - diff --git a/libs/utils/ported.cpp b/libs/utils/ported.cpp deleted file mode 100644 index 656e46f09..000000000 --- a/libs/utils/ported.cpp +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (C) 2005 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. - */ - -// -// Ports of standard functions that don't exist on a specific platform. -// -// Note these are NOT in the "android" namespace. -// -#include - -#if defined(NEED_GETTIMEOFDAY) || defined(NEED_USLEEP) -# include -# include -#endif - - -#if defined(NEED_GETTIMEOFDAY) -/* - * Replacement gettimeofday() for Windows environments (primarily MinGW). - * - * Ignores "tz". - */ -int gettimeofday(struct timeval* ptv, struct timezone* tz) -{ - long long nsTime; // time in 100ns units since Jan 1 1601 - FILETIME ft; - - if (tz != NULL) { - // oh well - } - - ::GetSystemTimeAsFileTime(&ft); - nsTime = (long long) ft.dwHighDateTime << 32 | - (long long) ft.dwLowDateTime; - // convert to time in usec since Jan 1 1970 - ptv->tv_usec = (long) ((nsTime / 10LL) % 1000000LL); - ptv->tv_sec = (long) ((nsTime - 116444736000000000LL) / 10000000LL); - - return 0; -} -#endif - -#if defined(NEED_USLEEP) -// -// Replacement usleep for Windows environments (primarily MinGW). -// -void usleep(unsigned long usec) -{ - // Win32 API function Sleep() takes milliseconds - ::Sleep((usec + 500) / 1000); -} -#endif - -#if 0 //defined(NEED_PIPE) -// -// Replacement pipe() command for MinGW -// -// The _O_NOINHERIT flag sets bInheritHandle to FALSE in the -// SecurityAttributes argument to CreatePipe(). This means the handles -// aren't inherited when a new process is created. The examples I've seen -// use it, possibly because there's a lot of junk going on behind the -// scenes. (I'm assuming "process" and "thread" are different here, so -// we should be okay spinning up a thread.) The recommended practice is -// to dup() the descriptor you want the child to have. -// -// It appears that unnamed pipes can't do non-blocking ("overlapped") I/O. -// You can't use select() either, since that only works on sockets. The -// Windows API calls that are useful here all operate on a HANDLE, not -// an integer file descriptor, and I don't think you can get there from -// here. The "named pipe" stuff is insane. -// -int pipe(int filedes[2]) -{ - return _pipe(filedes, 0, _O_BINARY | _O_NOINHERIT); -} -#endif - -#if defined(NEED_SETENV) -/* - * MinGW lacks these. For now, just stub them out so the code compiles. - */ -int setenv(const char* name, const char* value, int overwrite) -{ - return 0; -} -void unsetenv(const char* name) -{ -} -char* getenv(const char* name) -{ - return NULL; -} -#endif From cbb1011c95e0c25c29e40e203a6a31bccd029da3 Mon Sep 17 00:00:00 2001 From: The Android Open Source Project Date: Tue, 3 Mar 2009 19:31:44 -0800 Subject: [PATCH 079/541] auto import from //depot/cupcake/@135843 --- MODULE_LICENSE_APACHE2 | 0 NOTICE | 222 ++ include/utils.h | 33 + include/utils/AndroidUnicode.h | 255 ++ include/utils/Asset.h | 315 ++ include/utils/AssetDir.h | 145 + include/utils/AssetManager.h | 323 ++ include/utils/Atomic.h | 22 + include/utils/Binder.h | 103 + include/utils/BpBinder.h | 122 + include/utils/Buffer.h | 107 + include/utils/BufferedTextOutput.h | 67 + include/utils/ByteOrder.h | 69 + include/utils/CallStack.h | 76 + include/utils/Debug.h | 45 + include/utils/Endian.h | 40 + include/utils/Errors.h | 87 + include/utils/FileMap.h | 134 + include/utils/IBinder.h | 159 + include/utils/IInterface.h | 135 + include/utils/IMemory.h | 94 + include/utils/IPCThreadState.h | 110 + include/utils/IPermissionController.h | 56 + include/utils/IServiceManager.h | 98 + include/utils/KeyedVector.h | 201 ++ include/utils/List.h | 280 ++ include/utils/Log.h | 33 + include/utils/LogSocket.h | 20 + include/utils/MemoryBase.h | 51 + include/utils/MemoryDealer.h | 238 ++ include/utils/MemoryHeapBase.h | 98 + include/utils/MemoryHeapPmem.h | 80 + include/utils/Parcel.h | 209 ++ include/utils/Pipe.h | 108 + include/utils/ProcessState.h | 117 + include/utils/RefBase.h | 550 ++++ include/utils/ResourceTypes.h | 1720 +++++++++++ include/utils/SharedBuffer.h | 146 + include/utils/Socket.h | 80 + include/utils/SortedVector.h | 282 ++ include/utils/StopWatch.h | 62 + include/utils/String16.h | 260 ++ include/utils/String8.h | 353 +++ include/utils/SystemClock.h | 32 + include/utils/TextOutput.h | 190 ++ include/utils/TimeUtils.h | 89 + include/utils/TimerProbe.h | 72 + include/utils/Timers.h | 137 + include/utils/TypeHelpers.h | 254 ++ include/utils/Vector.h | 359 +++ include/utils/VectorImpl.h | 199 ++ include/utils/ZipEntry.h | 345 +++ include/utils/ZipFile.h | 269 ++ include/utils/ZipFileCRO.h | 59 + include/utils/ZipFileRO.h | 222 ++ include/utils/ZipUtils.h | 67 + include/utils/ashmem.h | 41 + include/utils/executablepath.h | 28 + include/utils/inet_address.h | 103 + include/utils/misc.h | 93 + include/utils/ported.h | 50 + include/utils/string_array.h | 135 + include/utils/threads.h | 347 +++ libs/utils/Android.mk | 156 + libs/utils/Asset.cpp | 813 +++++ libs/utils/AssetDir.cpp | 66 + libs/utils/AssetManager.cpp | 1637 ++++++++++ libs/utils/Binder.cpp | 242 ++ libs/utils/BpBinder.cpp | 348 +++ libs/utils/BufferedTextOutput.cpp | 279 ++ libs/utils/CallStack.cpp | 335 +++ libs/utils/Debug.cpp | 318 ++ libs/utils/FileMap.cpp | 222 ++ libs/utils/IDataConnection.cpp | 89 + libs/utils/IInterface.cpp | 35 + libs/utils/IMemory.cpp | 486 +++ libs/utils/IPCThreadState.cpp | 1030 +++++++ libs/utils/IPermissionController.cpp | 86 + libs/utils/IServiceManager.cpp | 230 ++ libs/utils/InetAddress.cpp | 236 ++ libs/utils/LogSocket.cpp | 129 + libs/utils/MODULE_LICENSE_APACHE2 | 0 libs/utils/MemoryBase.cpp | 46 + libs/utils/MemoryDealer.cpp | 409 +++ libs/utils/MemoryHeapBase.cpp | 183 ++ libs/utils/MemoryHeapPmem.cpp | 248 ++ libs/utils/NOTICE | 190 ++ libs/utils/Parcel.cpp | 1377 +++++++++ libs/utils/Pipe.cpp | 465 +++ libs/utils/ProcessState.cpp | 398 +++ libs/utils/README | 14 + libs/utils/RefBase.cpp | 534 ++++ libs/utils/ResourceTypes.cpp | 3983 +++++++++++++++++++++++++ libs/utils/SharedBuffer.cpp | 113 + libs/utils/Socket.cpp | 388 +++ libs/utils/Static.cpp | 120 + libs/utils/StopWatch.cpp | 79 + libs/utils/String16.cpp | 609 ++++ libs/utils/String8.cpp | 604 ++++ libs/utils/SystemClock.cpp | 139 + libs/utils/TextOutput.cpp | 146 + libs/utils/Threads.cpp | 1128 +++++++ libs/utils/TimerProbe.cpp | 131 + libs/utils/Timers.cpp | 240 ++ libs/utils/Unicode.cpp | 193 ++ libs/utils/VectorImpl.cpp | 611 ++++ libs/utils/ZipEntry.cpp | 696 +++++ libs/utils/ZipFile.cpp | 1296 ++++++++ libs/utils/ZipFileCRO.cpp | 54 + libs/utils/ZipFileRO.cpp | 724 +++++ libs/utils/ZipUtils.cpp | 344 +++ libs/utils/characterData.h | 730 +++++ libs/utils/executablepath_darwin.cpp | 31 + libs/utils/executablepath_linux.cpp | 30 + libs/utils/futex_synchro.c | 175 ++ libs/utils/misc.cpp | 185 ++ libs/utils/ported.cpp | 106 + 117 files changed, 34252 insertions(+) create mode 100644 MODULE_LICENSE_APACHE2 create mode 100644 NOTICE create mode 100644 include/utils.h create mode 100644 include/utils/AndroidUnicode.h create mode 100644 include/utils/Asset.h create mode 100644 include/utils/AssetDir.h create mode 100644 include/utils/AssetManager.h create mode 100644 include/utils/Atomic.h create mode 100644 include/utils/Binder.h create mode 100644 include/utils/BpBinder.h create mode 100644 include/utils/Buffer.h create mode 100644 include/utils/BufferedTextOutput.h create mode 100644 include/utils/ByteOrder.h create mode 100644 include/utils/CallStack.h create mode 100644 include/utils/Debug.h create mode 100644 include/utils/Endian.h create mode 100644 include/utils/Errors.h create mode 100644 include/utils/FileMap.h create mode 100644 include/utils/IBinder.h create mode 100644 include/utils/IInterface.h create mode 100644 include/utils/IMemory.h create mode 100644 include/utils/IPCThreadState.h create mode 100644 include/utils/IPermissionController.h create mode 100644 include/utils/IServiceManager.h create mode 100644 include/utils/KeyedVector.h create mode 100644 include/utils/List.h create mode 100644 include/utils/Log.h create mode 100644 include/utils/LogSocket.h create mode 100644 include/utils/MemoryBase.h create mode 100644 include/utils/MemoryDealer.h create mode 100644 include/utils/MemoryHeapBase.h create mode 100644 include/utils/MemoryHeapPmem.h create mode 100644 include/utils/Parcel.h create mode 100644 include/utils/Pipe.h create mode 100644 include/utils/ProcessState.h create mode 100644 include/utils/RefBase.h create mode 100644 include/utils/ResourceTypes.h create mode 100644 include/utils/SharedBuffer.h create mode 100644 include/utils/Socket.h create mode 100644 include/utils/SortedVector.h create mode 100644 include/utils/StopWatch.h create mode 100644 include/utils/String16.h create mode 100644 include/utils/String8.h create mode 100644 include/utils/SystemClock.h create mode 100644 include/utils/TextOutput.h create mode 100644 include/utils/TimeUtils.h create mode 100644 include/utils/TimerProbe.h create mode 100644 include/utils/Timers.h create mode 100644 include/utils/TypeHelpers.h create mode 100644 include/utils/Vector.h create mode 100644 include/utils/VectorImpl.h create mode 100644 include/utils/ZipEntry.h create mode 100644 include/utils/ZipFile.h create mode 100644 include/utils/ZipFileCRO.h create mode 100644 include/utils/ZipFileRO.h create mode 100644 include/utils/ZipUtils.h create mode 100644 include/utils/ashmem.h create mode 100644 include/utils/executablepath.h create mode 100644 include/utils/inet_address.h create mode 100644 include/utils/misc.h create mode 100644 include/utils/ported.h create mode 100644 include/utils/string_array.h create mode 100644 include/utils/threads.h create mode 100644 libs/utils/Android.mk create mode 100644 libs/utils/Asset.cpp create mode 100644 libs/utils/AssetDir.cpp create mode 100644 libs/utils/AssetManager.cpp create mode 100644 libs/utils/Binder.cpp create mode 100644 libs/utils/BpBinder.cpp create mode 100644 libs/utils/BufferedTextOutput.cpp create mode 100644 libs/utils/CallStack.cpp create mode 100644 libs/utils/Debug.cpp create mode 100644 libs/utils/FileMap.cpp create mode 100644 libs/utils/IDataConnection.cpp create mode 100644 libs/utils/IInterface.cpp create mode 100644 libs/utils/IMemory.cpp create mode 100644 libs/utils/IPCThreadState.cpp create mode 100644 libs/utils/IPermissionController.cpp create mode 100644 libs/utils/IServiceManager.cpp create mode 100644 libs/utils/InetAddress.cpp create mode 100644 libs/utils/LogSocket.cpp create mode 100644 libs/utils/MODULE_LICENSE_APACHE2 create mode 100644 libs/utils/MemoryBase.cpp create mode 100644 libs/utils/MemoryDealer.cpp create mode 100644 libs/utils/MemoryHeapBase.cpp create mode 100644 libs/utils/MemoryHeapPmem.cpp create mode 100644 libs/utils/NOTICE create mode 100644 libs/utils/Parcel.cpp create mode 100644 libs/utils/Pipe.cpp create mode 100644 libs/utils/ProcessState.cpp create mode 100644 libs/utils/README create mode 100644 libs/utils/RefBase.cpp create mode 100644 libs/utils/ResourceTypes.cpp create mode 100644 libs/utils/SharedBuffer.cpp create mode 100644 libs/utils/Socket.cpp create mode 100644 libs/utils/Static.cpp create mode 100644 libs/utils/StopWatch.cpp create mode 100644 libs/utils/String16.cpp create mode 100644 libs/utils/String8.cpp create mode 100644 libs/utils/SystemClock.cpp create mode 100644 libs/utils/TextOutput.cpp create mode 100644 libs/utils/Threads.cpp create mode 100644 libs/utils/TimerProbe.cpp create mode 100644 libs/utils/Timers.cpp create mode 100644 libs/utils/Unicode.cpp create mode 100644 libs/utils/VectorImpl.cpp create mode 100644 libs/utils/ZipEntry.cpp create mode 100644 libs/utils/ZipFile.cpp create mode 100644 libs/utils/ZipFileCRO.cpp create mode 100644 libs/utils/ZipFileRO.cpp create mode 100644 libs/utils/ZipUtils.cpp create mode 100644 libs/utils/characterData.h create mode 100644 libs/utils/executablepath_darwin.cpp create mode 100644 libs/utils/executablepath_linux.cpp create mode 100644 libs/utils/futex_synchro.c create mode 100644 libs/utils/misc.cpp create mode 100644 libs/utils/ported.cpp diff --git a/MODULE_LICENSE_APACHE2 b/MODULE_LICENSE_APACHE2 new file mode 100644 index 000000000..e69de29bb diff --git a/NOTICE b/NOTICE new file mode 100644 index 000000000..267a6aafd --- /dev/null +++ b/NOTICE @@ -0,0 +1,222 @@ + ========================================================================= + == NOTICE file corresponding to the section 4 d of == + == the Apache License, Version 2.0, == + == in this case for the Android-specific code. == + ========================================================================= + +Android Code +Copyright 2005-2008 The Android Open Source Project + +This product includes software developed as part of +The Android Open Source Project (http://source.android.com). + + ========================================================================= + == NOTICE file corresponding to the section 4 d of == + == the Apache License, Version 2.0, == + == in this case for Apache Commons code. == + ========================================================================= + +Apache Commons +Copyright 1999-2004 The Apache Software Foundation + +This product includes software developed at +The Apache Software Foundation (http://www.apache.org/). + + ========================================================================= + == NOTICE file corresponding to the section 4 d of == + == the Apache License, Version 2.0, == + == in this case for Jakarta Commons Logging. == + ========================================================================= + +Jakarta Commons Logging (JCL) +Copyright 2005,2006 The Apache Software Foundation. + +This product includes software developed at +The Apache Software Foundation (http://www.apache.org/). + + ========================================================================= + == NOTICE file corresponding to the section 4 d of == + == the Apache License, Version 2.0, == + == in this case for the Nuance code. == + ========================================================================= + +These files are Copyright 2007 Nuance Communications, but released under +the Apache2 License. + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + diff --git a/include/utils.h b/include/utils.h new file mode 100644 index 000000000..30648b185 --- /dev/null +++ b/include/utils.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2005 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. + */ + +// +// Handy utility functions and portability code. This file includes all +// of the generally-useful headers in the "utils" directory. +// +#ifndef _LIBS_UTILS_H +#define _LIBS_UTILS_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#endif // _LIBS_UTILS_H diff --git a/include/utils/AndroidUnicode.h b/include/utils/AndroidUnicode.h new file mode 100644 index 000000000..563fcd019 --- /dev/null +++ b/include/utils/AndroidUnicode.h @@ -0,0 +1,255 @@ +/* + * Copyright (C) 2006 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_UNICODE_H +#define ANDROID_UNICODE_H + +#include +#include + +#define REPLACEMENT_CHAR (0xFFFD) + +// this part of code is copied from umachine.h under ICU +/** + * Define UChar32 as a type for single Unicode code points. + * UChar32 is a signed 32-bit integer (same as int32_t). + * + * The Unicode code point range is 0..0x10ffff. + * All other values (negative or >=0x110000) are illegal as Unicode code points. + * They may be used as sentinel values to indicate "done", "error" + * or similar non-code point conditions. + * + * @stable ICU 2.4 + */ +typedef int32_t UChar32; + +namespace android { + + class Encoding; + /** + * \class Unicode + * + * Helper class for getting properties of Unicode characters. Characters + * can have one of the types listed in CharType and each character can have the + * directionality of Direction. + */ + class Unicode + { + public: + /** + * Directions specified in the Unicode standard. These directions map directly + * to java.lang.Character. + */ + enum Direction { + DIRECTIONALITY_UNDEFINED = -1, + DIRECTIONALITY_LEFT_TO_RIGHT, + DIRECTIONALITY_RIGHT_TO_LEFT, + DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC, + DIRECTIONALITY_EUROPEAN_NUMBER, + DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR, + DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR, + DIRECTIONALITY_ARABIC_NUMBER, + DIRECTIONALITY_COMMON_NUMBER_SEPARATOR, + DIRECTIONALITY_NONSPACING_MARK, + DIRECTIONALITY_BOUNDARY_NEUTRAL, + DIRECTIONALITY_PARAGRAPH_SEPARATOR, + DIRECTIONALITY_SEGMENT_SEPARATOR, + DIRECTIONALITY_WHITESPACE, + DIRECTIONALITY_OTHER_NEUTRALS, + DIRECTIONALITY_LEFT_TO_RIGHT_EMBEDDING, + DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE, + DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING, + DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE, + DIRECTIONALITY_POP_DIRECTIONAL_FORMAT + }; + + /** + * Character types as specified in the Unicode standard. These map directly to + * java.lang.Character. + */ + enum CharType { + CHARTYPE_UNASSIGNED = 0, + CHARTYPE_UPPERCASE_LETTER, + CHARTYPE_LOWERCASE_LETTER, + CHARTYPE_TITLECASE_LETTER, + CHARTYPE_MODIFIER_LETTER, + CHARTYPE_OTHER_LETTER, + CHARTYPE_NON_SPACING_MARK, + CHARTYPE_ENCLOSING_MARK, + CHARTYPE_COMBINING_SPACING_MARK, + CHARTYPE_DECIMAL_DIGIT_NUMBER, + CHARTYPE_LETTER_NUMBER, + CHARTYPE_OTHER_NUMBER, + CHARTYPE_SPACE_SEPARATOR, + CHARTYPE_LINE_SEPARATOR, + CHARTYPE_PARAGRAPH_SEPARATOR, + CHARTYPE_CONTROL, + CHARTYPE_FORMAT, + CHARTYPE_MISSING_VALUE_FOR_JAVA, /* This is the mysterious missing 17 value from the java constants */ + CHARTYPE_PRIVATE_USE, + CHARTYPE_SURROGATE, + CHARTYPE_DASH_PUNCTUATION, + CHARTYPE_START_PUNCTUATION, + CHARTYPE_END_PUNCTUATION, + CHARTYPE_CONNECTOR_PUNCTUATION, + CHARTYPE_OTHER_PUNCTUATION, + CHARTYPE_MATH_SYMBOL, + CHARTYPE_CURRENCY_SYMBOL, + CHARTYPE_MODIFIER_SYMBOL, + CHARTYPE_OTHER_SYMBOL, + CHARTYPE_INITIAL_QUOTE_PUNCTUATION, + CHARTYPE_FINAL_QUOTE_PUNCTUATION + }; + + /** + * Decomposition types as described by the unicode standard. These values map to + * the same values in uchar.h in ICU. + */ + enum DecompositionType { + DECOMPOSITION_NONE = 0, + DECOMPOSITION_CANONICAL, + DECOMPOSITION_COMPAT, + DECOMPOSITION_CIRCLE, + DECOMPOSITION_FINAL, + DECOMPOSITION_FONT, + DECOMPOSITION_FRACTION, + DECOMPOSITION_INITIAL, + DECOMPOSITION_ISOLATED, + DECOMPOSITION_MEDIAL, + DECOMPOSITION_NARROW, + DECOMPOSITION_NOBREAK, + DECOMPOSITION_SMALL, + DECOMPOSITION_SQUARE, + DECOMPOSITION_SUB, + DECOMPOSITION_SUPER, + DECOMPOSITION_VERTICAL, + DECOMPOSITION_WIDE + }; + + /** + * Returns the packed data for java calls + * @param c The unicode character. + * @return The packed data for the character. + * + * Copied from java.lang.Character implementation: + * 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 + * F E D C B A 9 8 7 6 5 4 3 2 1 0 F E D C B A 9 8 7 6 5 4 3 2 1 0 + * + * 31 types --------- + * 18 directionalities --------- + * 2 mirroreds - + * ----------- 56 toupper diffs + * ----------- 48 tolower diffs + * --- 4 totitlecase diffs + * ------------- 84 numeric values + * --------- 24 mirror char diffs + */ + static uint32_t getPackedData(UChar32 c); + + /** + * Get the Character type. + * @param c The unicode character. + * @return The character's type or CHARTYPE_UNASSIGNED if the character is invalid + * or has an unassigned class. + */ + static CharType getType(UChar32 c); + + /** + * Get the Character's decomposition type. + * @param c The unicode character. + * @return The character's decomposition type or DECOMPOSITION_NONE is there + * is no decomposition. + */ + static DecompositionType getDecompositionType(UChar32 c); + + /** + * Returns the digit value of a character or -1 if the character + * is not within the specified radix. + * + * The digit value is computed for integer characters and letters + * within the given radix. This function does not handle Roman Numerals, + * fractions, or any other characters that may represent numbers. + * + * @param c The unicode character + * @param radix The intended radix. + * @return The digit value or -1 if there is no digit value or if the value is outside the radix. + */ + static int getDigitValue(UChar32 c, int radix = 10); + + /** + * Return the numeric value of a character + * + * @param c The unicode character. + * @return The numeric value of the character. -1 if the character has no numeric value, + * -2 if the character has a numeric value that is not representable by an integer. + */ + static int getNumericValue(UChar32 c); + + /** + * Convert the character to lowercase + * @param c The unicode character. + * @return The lowercase character equivalent of c. If c does not have a lowercase equivalent, + * the original character is returned. + */ + static UChar32 toLower(UChar32 c); + + /** + * Convert the character to uppercase + * @param c The unicode character. + * @return The uppercase character equivalent of c. If c does not have an uppercase equivalent, + * the original character is returned. + */ + static UChar32 toUpper(UChar32 c); + + /** + * Get the directionality of the character. + * @param c The unicode character. + * @return The direction of the character or DIRECTIONALITY_UNDEFINED. + */ + static Direction getDirectionality(UChar32 c); + + /** + * Check if the character is a mirrored character. This means that the character + * has an equivalent character that is the mirror image of itself. + * @param c The unicode character. + * @return True iff c has a mirror equivalent. + */ + static bool isMirrored(UChar32 c); + + /** + * Return the mirror of the given character. + * @param c The unicode character. + * @return The mirror equivalent of c. If c does not have a mirror equivalent, + * the original character is returned. + * @see isMirrored + */ + static UChar32 toMirror(UChar32 c); + + /** + * Convert the character to title case. + * @param c The unicode character. + * @return The titlecase equivalent of c. If c does not have a titlecase equivalent, + * the original character is returned. + */ + static UChar32 toTitle(UChar32 c); + + }; + +} + +#endif diff --git a/include/utils/Asset.h b/include/utils/Asset.h new file mode 100644 index 000000000..453a2049a --- /dev/null +++ b/include/utils/Asset.h @@ -0,0 +1,315 @@ +/* + * Copyright (C) 2006 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. + */ + +// +// Class providing access to a read-only asset. Asset objects are NOT +// thread-safe, and should not be shared across threads. +// +#ifndef __LIBS_ASSET_H +#define __LIBS_ASSET_H + +#include +#include +#include "FileMap.h" +#include "String8.h" +#include "Errors.h" + +namespace android { + +/* + * Instances of this class provide read-only operations on a byte stream. + * + * Access may be optimized for streaming, random, or whole buffer modes. All + * operations are supported regardless of how the file was opened, but some + * things will be less efficient. [pass that in??] + * + * "Asset" is the base class for all types of assets. The classes below + * provide most of the implementation. The AssetManager uses one of the + * static "create" functions defined here to create a new instance. + */ +class Asset { +public: + virtual ~Asset(void); + + static int32_t getGlobalCount(); + + /* used when opening an asset */ + typedef enum AccessMode { + ACCESS_UNKNOWN = 0, + + /* read chunks, and seek forward and backward */ + ACCESS_RANDOM, + + /* read sequentially, with an occasional forward seek */ + ACCESS_STREAMING, + + /* caller plans to ask for a read-only buffer with all data */ + ACCESS_BUFFER, + } AccessMode; + + enum { + /* data larger than this does not get uncompressed into a buffer */ +#ifdef HAVE_ANDROID_OS + UNCOMPRESS_DATA_MAX = 1 * 1024 * 1024 +#else + UNCOMPRESS_DATA_MAX = 2 * 1024 * 1024 +#endif + }; + + /* + * Read data from the current offset. Returns the actual number of + * bytes read, 0 on EOF, or -1 on error. + */ + virtual ssize_t read(void* buf, size_t count) = 0; + + /* + * Seek to the specified offset. "whence" uses the same values as + * lseek/fseek. Returns the new position on success, or (off_t) -1 + * on failure. + */ + virtual off_t seek(off_t offset, int whence) = 0; + + /* + * Close the asset, freeing all associated resources. + */ + virtual void close(void) = 0; + + /* + * Get a pointer to a buffer with the entire contents of the file. + */ + virtual const void* getBuffer(bool wordAligned) = 0; + + /* + * Get the total amount of data that can be read. + */ + virtual off_t getLength(void) const = 0; + + /* + * Get the total amount of data that can be read from the current position. + */ + virtual off_t getRemainingLength(void) const = 0; + + /* + * Open a new file descriptor that can be used to read this asset. + * Returns -1 if you can not use the file descriptor (for example if the + * asset is compressed). + */ + virtual int openFileDescriptor(off_t* outStart, off_t* outLength) const = 0; + + /* + * Get a string identifying the asset's source. This might be a full + * path, it might be a colon-separated list of identifiers. + * + * This is NOT intended to be used for anything except debug output. + * DO NOT try to parse this or use it to open a file. + */ + const char* getAssetSource(void) const { return mAssetSource.string(); } + +protected: + Asset(void); // constructor; only invoked indirectly + + /* handle common seek() housekeeping */ + off_t handleSeek(off_t offset, int whence, off_t curPosn, off_t maxPosn); + + /* set the asset source string */ + void setAssetSource(const String8& path) { mAssetSource = path; } + + AccessMode getAccessMode(void) const { return mAccessMode; } + +private: + /* these operations are not implemented */ + Asset(const Asset& src); + Asset& operator=(const Asset& src); + + /* AssetManager needs access to our "create" functions */ + friend class AssetManager; + + /* + * Create the asset from a named file on disk. + */ + static Asset* createFromFile(const char* fileName, AccessMode mode); + + /* + * Create the asset from a named, compressed file on disk (e.g. ".gz"). + */ + static Asset* createFromCompressedFile(const char* fileName, + AccessMode mode); + +#if 0 + /* + * Create the asset from a segment of an open file. This will fail + * if "offset" and "length" don't fit within the bounds of the file. + * + * The asset takes ownership of the file descriptor. + */ + static Asset* createFromFileSegment(int fd, off_t offset, size_t length, + AccessMode mode); + + /* + * Create from compressed data. "fd" should be seeked to the start of + * the compressed data. This could be inside a gzip file or part of a + * Zip archive. + * + * The asset takes ownership of the file descriptor. + * + * This may not verify the validity of the compressed data until first + * use. + */ + static Asset* createFromCompressedData(int fd, off_t offset, + int compressionMethod, size_t compressedLength, + size_t uncompressedLength, AccessMode mode); +#endif + + /* + * Create the asset from a memory-mapped file segment. + * + * The asset takes ownership of the FileMap. + */ + static Asset* createFromUncompressedMap(FileMap* dataMap, AccessMode mode); + + /* + * Create the asset from a memory-mapped file segment with compressed + * data. "method" is a Zip archive compression method constant. + * + * The asset takes ownership of the FileMap. + */ + static Asset* createFromCompressedMap(FileMap* dataMap, int method, + size_t uncompressedLen, AccessMode mode); + + + /* + * Create from a reference-counted chunk of shared memory. + */ + // TODO + + AccessMode mAccessMode; // how the asset was opened + String8 mAssetSource; // debug string +}; + + +/* + * =========================================================================== + * + * Innards follow. Do not use these classes directly. + */ + +/* + * An asset based on an uncompressed file on disk. It may encompass the + * entire file or just a piece of it. Access is through fread/fseek. + */ +class _FileAsset : public Asset { +public: + _FileAsset(void); + virtual ~_FileAsset(void); + + /* + * Use a piece of an already-open file. + * + * On success, the object takes ownership of "fd". + */ + status_t openChunk(const char* fileName, int fd, off_t offset, size_t length); + + /* + * Use a memory-mapped region. + * + * On success, the object takes ownership of "dataMap". + */ + status_t openChunk(FileMap* dataMap); + + /* + * Standard Asset interfaces. + */ + virtual ssize_t read(void* buf, size_t count); + virtual off_t seek(off_t offset, int whence); + virtual void close(void); + virtual const void* getBuffer(bool wordAligned); + virtual off_t getLength(void) const { return mLength; } + virtual off_t getRemainingLength(void) const { return mLength-mOffset; } + virtual int openFileDescriptor(off_t* outStart, off_t* outLength) const; + +private: + off_t mStart; // absolute file offset of start of chunk + off_t mLength; // length of the chunk + off_t mOffset; // current local offset, 0 == mStart + FILE* mFp; // for read/seek + char* mFileName; // for opening + + /* + * To support getBuffer() we either need to read the entire thing into + * a buffer or memory-map it. For small files it's probably best to + * just read them in. + */ + enum { kReadVsMapThreshold = 4096 }; + + FileMap* mMap; // for memory map + unsigned char* mBuf; // for read + + const void* ensureAlignment(FileMap* map); +}; + + +/* + * An asset based on compressed data in a file. + */ +class _CompressedAsset : public Asset { +public: + _CompressedAsset(void); + virtual ~_CompressedAsset(void); + + /* + * Use a piece of an already-open file. + * + * On success, the object takes ownership of "fd". + */ + status_t openChunk(int fd, off_t offset, int compressionMethod, + size_t uncompressedLen, size_t compressedLen); + + /* + * Use a memory-mapped region. + * + * On success, the object takes ownership of "fd". + */ + status_t openChunk(FileMap* dataMap, int compressionMethod, + size_t uncompressedLen); + + /* + * Standard Asset interfaces. + */ + virtual ssize_t read(void* buf, size_t count); + virtual off_t seek(off_t offset, int whence); + virtual void close(void); + virtual const void* getBuffer(bool wordAligned); + virtual off_t getLength(void) const { return mUncompressedLen; } + virtual off_t getRemainingLength(void) const { return mUncompressedLen-mOffset; } + virtual int openFileDescriptor(off_t* outStart, off_t* outLength) const { return -1; } + +private: + off_t mStart; // offset to start of compressed data + off_t mCompressedLen; // length of the compressed data + off_t mUncompressedLen; // length of the uncompressed data + off_t mOffset; // current offset, 0 == start of uncomp data + + FileMap* mMap; // for memory-mapped input + int mFd; // for file input + + unsigned char* mBuf; // for getBuffer() +}; + +// need: shared mmap version? + +}; // namespace android + +#endif // __LIBS_ASSET_H diff --git a/include/utils/AssetDir.h b/include/utils/AssetDir.h new file mode 100644 index 000000000..abf8a3595 --- /dev/null +++ b/include/utils/AssetDir.h @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2006 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. + */ + +// +// Access a chunk of the asset hierarchy as if it were a single directory. +// +#ifndef __LIBS_ASSETDIR_H +#define __LIBS_ASSETDIR_H + +#include +#include +#include +#include +#include + +namespace android { + +/* + * This provides vector-style access to a directory. We do this rather + * than modeling opendir/readdir access because it's simpler and the + * nature of the operation requires us to have all data on hand anyway. + * + * The list of files will be sorted in ascending order by ASCII value. + * + * The contents are populated by our friend, the AssetManager. + */ +class AssetDir { +public: + AssetDir(void) + : mFileInfo(NULL) + {} + virtual ~AssetDir(void) { + delete mFileInfo; + } + + /* + * Vector-style access. + */ + size_t getFileCount(void) { return mFileInfo->size(); } + const String8& getFileName(int idx) { + return mFileInfo->itemAt(idx).getFileName(); + } + const String8& getSourceName(int idx) { + return mFileInfo->itemAt(idx).getSourceName(); + } + + /* + * Get the type of a file (usually regular or directory). + */ + FileType getFileType(int idx) { + return mFileInfo->itemAt(idx).getFileType(); + } + +private: + /* these operations are not implemented */ + AssetDir(const AssetDir& src); + const AssetDir& operator=(const AssetDir& src); + + friend class AssetManager; + + /* + * This holds information about files in the asset hierarchy. + */ + class FileInfo { + public: + FileInfo(void) {} + FileInfo(const String8& path) // useful for e.g. svect.indexOf + : mFileName(path), mFileType(kFileTypeUnknown) + {} + ~FileInfo(void) {} + FileInfo(const FileInfo& src) { + copyMembers(src); + } + const FileInfo& operator= (const FileInfo& src) { + if (this != &src) + copyMembers(src); + return *this; + } + + void copyMembers(const FileInfo& src) { + mFileName = src.mFileName; + mFileType = src.mFileType; + mSourceName = src.mSourceName; + } + + /* need this for SortedVector; must compare only on file name */ + bool operator< (const FileInfo& rhs) const { + return mFileName < rhs.mFileName; + } + + /* used by AssetManager */ + bool operator== (const FileInfo& rhs) const { + return mFileName == rhs.mFileName; + } + + void set(const String8& path, FileType type) { + mFileName = path; + mFileType = type; + } + + const String8& getFileName(void) const { return mFileName; } + void setFileName(const String8& path) { mFileName = path; } + + FileType getFileType(void) const { return mFileType; } + void setFileType(FileType type) { mFileType = type; } + + const String8& getSourceName(void) const { return mSourceName; } + void setSourceName(const String8& path) { mSourceName = path; } + + /* + * Handy utility for finding an entry in a sorted vector of FileInfo. + * Returns the index of the matching entry, or -1 if none found. + */ + static int findEntry(const SortedVector* pVector, + const String8& fileName); + + private: + String8 mFileName; // filename only + FileType mFileType; // regular, directory, etc + + String8 mSourceName; // currently debug-only + }; + + /* AssetManager uses this to initialize us */ + void setFileList(SortedVector* list) { mFileInfo = list; } + + SortedVector* mFileInfo; +}; + +}; // namespace android + +#endif // __LIBS_ASSETDIR_H diff --git a/include/utils/AssetManager.h b/include/utils/AssetManager.h new file mode 100644 index 000000000..e94c0e8fe --- /dev/null +++ b/include/utils/AssetManager.h @@ -0,0 +1,323 @@ +/* + * Copyright (C) 2006 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. + */ + +// +// Asset management class. AssetManager objects are thread-safe. +// +#ifndef __LIBS_ASSETMANAGER_H +#define __LIBS_ASSETMANAGER_H + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace android { + +class Asset; // fwd decl for things that include Asset.h first +class ResTable; +struct ResTable_config; + +/* + * Every application that uses assets needs one instance of this. A + * single instance may be shared across multiple threads, and a single + * thread may have more than one instance (the latter is discouraged). + * + * The purpose of the AssetManager is to create Asset objects. To do + * this efficiently it may cache information about the locations of + * files it has seen. This can be controlled with the "cacheMode" + * argument. + * + * The asset hierarchy may be examined like a filesystem, using + * AssetDir objects to peruse a single directory. + */ +class AssetManager { +public: + typedef enum CacheMode { + CACHE_UNKNOWN = 0, + CACHE_OFF, // don't try to cache file locations + CACHE_DEFER, // construct cache as pieces are needed + //CACHE_SCAN, // scan full(!) asset hierarchy at init() time + } CacheMode; + + AssetManager(CacheMode cacheMode = CACHE_OFF); + virtual ~AssetManager(void); + + static int32_t getGlobalCount(); + + /* + * Add a new source for assets. This can be called multiple times to + * look in multiple places for assets. It can be either a directory (for + * finding assets as raw files on the disk) or a ZIP file. This newly + * added asset path will be examined first when searching for assets, + * before any that were previously added. + * + * Returns "true" on success, "false" on failure. If 'cookie' is non-NULL, + * then on success, *cookie is set to the value corresponding to the + * newly-added asset source. + */ + bool addAssetPath(const String8& path, void** cookie); + + /* + * Convenience for adding the standard system assets. Uses the + * ANDROID_ROOT environment variable to find them. + */ + bool addDefaultAssets(); + + /* + * Iterate over the asset paths in this manager. (Previously + * added via addAssetPath() and addDefaultAssets().) On first call, + * 'cookie' must be NULL, resulting in the first cookie being returned. + * Each next cookie will be returned there-after, until NULL indicating + * the end has been reached. + */ + void* nextAssetPath(void* cookie) const; + + /* + * Return an asset path in the manager. 'which' must be between 0 and + * countAssetPaths(). + */ + String8 getAssetPath(void* cookie) const; + + /* + * Set the current locale and vendor. The locale can change during + * the lifetime of an AssetManager if the user updates the device's + * language setting. The vendor is less likely to change. + * + * Pass in NULL to indicate no preference. + */ + void setLocale(const char* locale); + void setVendor(const char* vendor); + + /* + * Choose screen orientation for resources values returned. + */ + void setConfiguration(const ResTable_config& config, const char* locale = NULL); + + typedef Asset::AccessMode AccessMode; // typing shortcut + + /* + * Open an asset. + * + * This will search through locale-specific and vendor-specific + * directories and packages to find the file. + * + * The object returned does not depend on the AssetManager. It should + * be freed by calling Asset::close(). + */ + Asset* open(const char* fileName, AccessMode mode); + + /* + * Open a non-asset file as an asset. + * + * This is for opening files that are included in an asset package + * but aren't assets. These sit outside the usual "locale/vendor" + * path hierarchy, and will not be seen by "AssetDir" or included + * in our filename cache. + */ + Asset* openNonAsset(const char* fileName, AccessMode mode); + + /* + * Explicit non-asset file. The file explicitly named by the cookie (the + * resource set to look in) and fileName will be opened and returned. + */ + Asset* openNonAsset(void* cookie, const char* fileName, AccessMode mode); + + /* + * Open a directory within the asset hierarchy. + * + * The contents of the directory are an amalgam of vendor-specific, + * locale-specific, and generic assets stored loosely or in asset + * packages. Depending on the cache setting and previous accesses, + * this call may incur significant disk overhead. + * + * To open the top-level directory, pass in "". + */ + AssetDir* openDir(const char* dirName); + + /* + * Get the type of a file in the asset hierarchy. They will either + * be "regular" or "directory". [Currently only works for "regular".] + * + * Can also be used as a quick test for existence of a file. + */ + FileType getFileType(const char* fileName); + + /* + * Return the complete resource table to find things in the package. + */ + const ResTable& getResources(bool required = true) const; + + /* + * Discard cached filename information. This only needs to be called + * if somebody has updated the set of "loose" files, and we want to + * discard our cached notion of what's where. + */ + void purge(void) { purgeFileNameCacheLocked(); } + + /* + * Return true if the files this AssetManager references are all + * up-to-date (have not been changed since it was created). If false + * is returned, you will need to create a new AssetManager to get + * the current data. + */ + bool isUpToDate(); + + /** + * Get the known locales for this asset manager object. + */ + void getLocales(Vector* locales) const; + +private: + struct asset_path + { + String8 path; + FileType type; + }; + + Asset* openInPathLocked(const char* fileName, AccessMode mode, + const asset_path& path); + Asset* openNonAssetInPathLocked(const char* fileName, AccessMode mode, + const asset_path& path); + Asset* openInLocaleVendorLocked(const char* fileName, AccessMode mode, + const asset_path& path, const char* locale, const char* vendor); + String8 createPathNameLocked(const asset_path& path, const char* locale, + const char* vendor); + String8 createPathNameLocked(const asset_path& path, const char* rootDir); + String8 createZipSourceNameLocked(const String8& zipFileName, + const String8& dirName, const String8& fileName); + + ZipFileRO* getZipFileLocked(const asset_path& path); + Asset* openAssetFromFileLocked(const String8& fileName, AccessMode mode); + Asset* openAssetFromZipLocked(const ZipFileRO* pZipFile, + const ZipEntryRO entry, AccessMode mode, const String8& entryName); + + bool scanAndMergeDirLocked(SortedVector* pMergedInfo, + const asset_path& path, const char* rootDir, const char* dirName); + SortedVector* scanDirLocked(const String8& path); + bool scanAndMergeZipLocked(SortedVector* pMergedInfo, + const asset_path& path, const char* rootDir, const char* dirName); + void mergeInfoLocked(SortedVector* pMergedInfo, + const SortedVector* pContents); + + void loadFileNameCacheLocked(void); + void fncScanLocked(SortedVector* pMergedInfo, + const char* dirName); + bool fncScanAndMergeDirLocked( + SortedVector* pMergedInfo, + const asset_path& path, const char* locale, const char* vendor, + const char* dirName); + void purgeFileNameCacheLocked(void); + + const ResTable* getResTable(bool required = true) const; + void setLocaleLocked(const char* locale); + void updateResourceParamsLocked() const; + + class SharedZip : public RefBase { + public: + static sp get(const String8& path); + + ZipFileRO* getZip(); + + Asset* getResourceTableAsset(); + Asset* setResourceTableAsset(Asset* asset); + + bool isUpToDate(); + + protected: + ~SharedZip(); + + private: + SharedZip(const String8& path, time_t modWhen); + SharedZip(); // <-- not implemented + + String8 mPath; + ZipFileRO* mZipFile; + time_t mModWhen; + + Asset* mResourceTableAsset; + + static Mutex gLock; + static DefaultKeyedVector > gOpen; + }; + + /* + * Manage a set of Zip files. For each file we need a pointer to the + * ZipFile and a time_t with the file's modification date. + * + * We currently only have two zip files (current app, "common" app). + * (This was originally written for 8, based on app/locale/vendor.) + */ + class ZipSet { + public: + ZipSet(void); + ~ZipSet(void); + + /* + * Return a ZipFileRO structure for a ZipFileRO with the specified + * parameters. + */ + ZipFileRO* getZip(const String8& path); + + Asset* getZipResourceTable(const String8& path); + Asset* setZipResourceTable(const String8& path, Asset* asset); + + // generate path, e.g. "common/en-US-noogle.zip" + static String8 getPathName(const char* path); + + bool isUpToDate(); + + private: + void closeZip(int idx); + + int getIndex(const String8& zip) const; + mutable Vector mZipPath; + mutable Vector > mZipFile; + }; + + // Protect all internal state. + mutable Mutex mLock; + + ZipSet mZipSet; + + Vector mAssetPaths; + char* mLocale; + char* mVendor; + + mutable ResTable* mResources; + ResTable_config* mConfig; + + /* + * Cached data for "loose" files. This lets us avoid poking at the + * filesystem when searching for loose assets. Each entry is the + * "extended partial" path, e.g. "default/default/foo/bar.txt". The + * full set of files is present, including ".EXCLUDE" entries. + * + * We do not cache directory names. We don't retain the ".gz", + * because to our clients "foo" and "foo.gz" both look like "foo". + */ + CacheMode mCacheMode; // is the cache enabled? + bool mCacheValid; // clear when locale or vendor changes + SortedVector mCache; +}; + +}; // namespace android + +#endif // __LIBS_ASSETMANAGER_H diff --git a/include/utils/Atomic.h b/include/utils/Atomic.h new file mode 100644 index 000000000..7eb476c94 --- /dev/null +++ b/include/utils/Atomic.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2005 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_UTILS_ATOMIC_H +#define ANDROID_UTILS_ATOMIC_H + +#include + +#endif // ANDROID_UTILS_ATOMIC_H diff --git a/include/utils/Binder.h b/include/utils/Binder.h new file mode 100644 index 000000000..b5b8d9851 --- /dev/null +++ b/include/utils/Binder.h @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_BINDER_H +#define ANDROID_BINDER_H + +#include + +// --------------------------------------------------------------------------- +namespace android { + +class BBinder : public IBinder +{ +public: + BBinder(); + + virtual String16 getInterfaceDescriptor() const; + virtual bool isBinderAlive() const; + virtual status_t pingBinder(); + virtual status_t dump(int fd, const Vector& args); + + virtual status_t transact( uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags = 0); + + virtual status_t linkToDeath(const sp& recipient, + void* cookie = NULL, + uint32_t flags = 0); + + virtual status_t unlinkToDeath( const wp& recipient, + void* cookie = NULL, + uint32_t flags = 0, + wp* outRecipient = NULL); + + virtual void attachObject( const void* objectID, + void* object, + void* cleanupCookie, + object_cleanup_func func); + virtual void* findObject(const void* objectID) const; + virtual void detachObject(const void* objectID); + + virtual BBinder* localBinder(); + +protected: + virtual ~BBinder(); + + virtual status_t onTransact( uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags = 0); + +private: + BBinder(const BBinder& o); + BBinder& operator=(const BBinder& o); + + class Extras; + + Extras* mExtras; + void* mReserved0; +}; + +// --------------------------------------------------------------------------- + +class BpRefBase : public virtual RefBase +{ +protected: + BpRefBase(const sp& o); + virtual ~BpRefBase(); + virtual void onFirstRef(); + virtual void onLastStrongRef(const void* id); + virtual bool onIncStrongAttempted(uint32_t flags, const void* id); + + inline IBinder* remote() { return mRemote; } + inline IBinder* remote() const { return mRemote; } + +private: + BpRefBase(const BpRefBase& o); + BpRefBase& operator=(const BpRefBase& o); + + IBinder* const mRemote; + RefBase::weakref_type* mRefs; + volatile int32_t mState; +}; + +}; // namespace android + +// --------------------------------------------------------------------------- + +#endif // ANDROID_BINDER_H diff --git a/include/utils/BpBinder.h b/include/utils/BpBinder.h new file mode 100644 index 000000000..7b96e296f --- /dev/null +++ b/include/utils/BpBinder.h @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2005 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_BPBINDER_H +#define ANDROID_BPBINDER_H + +#include +#include +#include + +// --------------------------------------------------------------------------- +namespace android { + +class BpBinder : public IBinder +{ +public: + BpBinder(int32_t handle); + + inline int32_t handle() const { return mHandle; } + + virtual String16 getInterfaceDescriptor() const; + virtual bool isBinderAlive() const; + virtual status_t pingBinder(); + virtual status_t dump(int fd, const Vector& args); + + virtual status_t transact( uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags = 0); + + virtual status_t linkToDeath(const sp& recipient, + void* cookie = NULL, + uint32_t flags = 0); + virtual status_t unlinkToDeath( const wp& recipient, + void* cookie = NULL, + uint32_t flags = 0, + wp* outRecipient = NULL); + + virtual void attachObject( const void* objectID, + void* object, + void* cleanupCookie, + object_cleanup_func func); + virtual void* findObject(const void* objectID) const; + virtual void detachObject(const void* objectID); + + virtual BpBinder* remoteBinder(); + + status_t setConstantData(const void* data, size_t size); + void sendObituary(); + + class ObjectManager + { + public: + ObjectManager(); + ~ObjectManager(); + + void attach( const void* objectID, + void* object, + void* cleanupCookie, + IBinder::object_cleanup_func func); + void* find(const void* objectID) const; + void detach(const void* objectID); + + void kill(); + + private: + ObjectManager(const ObjectManager&); + ObjectManager& operator=(const ObjectManager&); + + struct entry_t + { + void* object; + void* cleanupCookie; + IBinder::object_cleanup_func func; + }; + + KeyedVector mObjects; + }; + +protected: + virtual ~BpBinder(); + virtual void onFirstRef(); + virtual void onLastStrongRef(const void* id); + virtual bool onIncStrongAttempted(uint32_t flags, const void* id); + +private: + const int32_t mHandle; + + struct Obituary { + wp recipient; + void* cookie; + uint32_t flags; + }; + + void reportOneDeath(const Obituary& obit); + + mutable Mutex mLock; + volatile int32_t mAlive; + volatile int32_t mObitsSent; + Vector* mObituaries; + ObjectManager mObjects; + Parcel* mConstantData; +}; + +}; // namespace android + +// --------------------------------------------------------------------------- + +#endif // ANDROID_BPBINDER_H diff --git a/include/utils/Buffer.h b/include/utils/Buffer.h new file mode 100644 index 000000000..8e22b0f21 --- /dev/null +++ b/include/utils/Buffer.h @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __UTILS_BUFFER_H__ +#define __UTILS_BUFFER_H__ 1 + +#include +#include +#include + +namespace android { + +class Buffer +{ +private: + char *buf; + int bufsiz; + int used; + void ensureCapacity(int len); + + void + makeRoomFor(int len) + { + if (len + used >= bufsiz) { + bufsiz = (len + used) * 3/2 + 2; + char *blah = new char[bufsiz]; + + memcpy(blah, buf, used); + delete[] buf; + buf = blah; + } + } + +public: + Buffer() + { + bufsiz = 16; + buf = new char[bufsiz]; + clear(); + } + + ~Buffer() + { + delete[] buf; + } + + void + clear() + { + buf[0] = '\0'; + used = 0; + } + + int + length() + { + return used; + } + + void + append(const char c) + { + makeRoomFor(1); + buf[used] = c; + used++; + buf[used] = '\0'; + } + + void + append(const char *s, int len) + { + makeRoomFor(len); + + memcpy(buf + used, s, len); + used += len; + buf[used] = '\0'; + } + + void + append(const char *s) + { + append(s, strlen(s)); + } + + char * + getBytes() + { + return buf; + } +}; + +}; // namespace android + +#endif diff --git a/include/utils/BufferedTextOutput.h b/include/utils/BufferedTextOutput.h new file mode 100644 index 000000000..69c624037 --- /dev/null +++ b/include/utils/BufferedTextOutput.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2006 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_BUFFEREDTEXTOUTPUT_H +#define ANDROID_BUFFEREDTEXTOUTPUT_H + +#include +#include +#include + +// --------------------------------------------------------------------------- +namespace android { + +class BufferedTextOutput : public TextOutput +{ +public: + //** Flags for constructor */ + enum { + MULTITHREADED = 0x0001 + }; + + BufferedTextOutput(uint32_t flags = 0); + virtual ~BufferedTextOutput(); + + virtual status_t print(const char* txt, size_t len); + virtual void moveIndent(int delta); + + virtual void pushBundle(); + virtual void popBundle(); + +protected: + virtual status_t writeLines(const struct iovec& vec, size_t N) = 0; + +private: + struct BufferState; + struct ThreadState; + + static ThreadState*getThreadState(); + static void threadDestructor(void *st); + + BufferState*getBuffer() const; + + uint32_t mFlags; + const int32_t mSeq; + const int32_t mIndex; + + Mutex mLock; + BufferState* mGlobalState; +}; + +// --------------------------------------------------------------------------- +}; // namespace android + +#endif // ANDROID_BUFFEREDTEXTOUTPUT_H diff --git a/include/utils/ByteOrder.h b/include/utils/ByteOrder.h new file mode 100644 index 000000000..4c0606763 --- /dev/null +++ b/include/utils/ByteOrder.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2006 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 _LIBS_UTILS_BYTE_ORDER_H +#define _LIBS_UTILS_BYTE_ORDER_H + +#include +#include +#ifdef HAVE_WINSOCK +#include +#else +#include +#endif + +/* + * These macros are like the hton/ntoh byte swapping macros, + * except they allow you to swap to and from the "device" byte + * order. The device byte order is the endianness of the target + * device -- for the ARM CPUs we use today, this is little endian. + * + * Note that the byte swapping functions have not been optimized + * much; performance is currently not an issue for them since the + * intent is to allow us to avoid byte swapping on the device. + */ + +#define DEVICE_BYTE_ORDER LITTLE_ENDIAN + +#if BYTE_ORDER == DEVICE_BYTE_ORDER + +#define dtohl(x) (x) +#define dtohs(x) (x) +#define htodl(x) (x) +#define htods(x) (x) + +#else + +static inline uint32_t android_swap_long(uint32_t v) +{ + return (v<<24) | ((v<<8)&0x00FF0000) | ((v>>8)&0x0000FF00) | (v>>24); +} + +static inline uint16_t android_swap_short(uint16_t v) +{ + return (v<<8) | (v>>8); +} + +#define dtohl(x) (android_swap_long(x)) +#define dtohs(x) (android_swap_short(x)) +#define htodl(x) (android_swap_long(x)) +#define htods(x) (android_swap_short(x)) + +#endif + +#endif // _LIBS_UTILS_BYTE_ORDER_H diff --git a/include/utils/CallStack.h b/include/utils/CallStack.h new file mode 100644 index 000000000..c2c8ce514 --- /dev/null +++ b/include/utils/CallStack.h @@ -0,0 +1,76 @@ +/* + * 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. + */ + +#ifndef ANDROID_CALLSTACK_H +#define ANDROID_CALLSTACK_H + +#include +#include + +#include + +// --------------------------------------------------------------------------- + +namespace android { + +class CallStack +{ +public: + enum { + MAX_DEPTH = 31 + }; + + CallStack(); + CallStack(const CallStack& rhs); + ~CallStack(); + + CallStack& operator = (const CallStack& rhs); + + bool operator == (const CallStack& rhs) const; + bool operator != (const CallStack& rhs) const; + bool operator < (const CallStack& rhs) const; + bool operator >= (const CallStack& rhs) const; + bool operator > (const CallStack& rhs) const; + bool operator <= (const CallStack& rhs) const; + + const void* operator [] (int index) const; + + void clear(); + + void update(int32_t ignoreDepth=0, int32_t maxDepth=MAX_DEPTH); + + // Dump a stack trace to the log + void dump(const char* prefix = 0) const; + + // Return a string (possibly very long) containing the complete stack trace + String8 toString(const char* prefix = 0) const; + + size_t size() const { return mCount; } + +private: + // Internal helper function + String8 toStringSingleLevel(const char* prefix, int32_t level) const; + + size_t mCount; + const void* mStack[MAX_DEPTH]; +}; + +}; // namespace android + + +// --------------------------------------------------------------------------- + +#endif // ANDROID_CALLSTACK_H diff --git a/include/utils/Debug.h b/include/utils/Debug.h new file mode 100644 index 000000000..a662b9cc2 --- /dev/null +++ b/include/utils/Debug.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2005 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. + */ + +// +// Debugging tools. These should be able to be stripped +// in release builds. +// +#ifndef ANDROID_DEBUG_H +#define ANDROID_DEBUG_H + +#include +#include + +namespace android { + +template struct CompileTimeAssert; +template<> struct CompileTimeAssert {}; + +const char* stringForIndent(int32_t indentLevel); + +typedef void (*debugPrintFunc)(void* cookie, const char* txt); + +void printTypeCode(uint32_t typeCode, + debugPrintFunc func = 0, void* cookie = 0); +void printHexData(int32_t indent, const void *buf, size_t length, + size_t bytesPerLine=16, int32_t singleLineBytesCutoff=16, + size_t alignment=0, bool cArrayStyle=false, + debugPrintFunc func = 0, void* cookie = 0); + +}; // namespace android + +#endif // ANDROID_DEBUG_H diff --git a/include/utils/Endian.h b/include/utils/Endian.h new file mode 100644 index 000000000..19f250494 --- /dev/null +++ b/include/utils/Endian.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2005 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. + */ + +// +// Android endian-ness defines. +// +#ifndef _LIBS_UTILS_ENDIAN_H +#define _LIBS_UTILS_ENDIAN_H + +#if defined(HAVE_ENDIAN_H) + +#include + +#else /*not HAVE_ENDIAN_H*/ + +#define __BIG_ENDIAN 0x1000 +#define __LITTLE_ENDIAN 0x0001 + +#if defined(HAVE_LITTLE_ENDIAN) +# define __BYTE_ORDER __LITTLE_ENDIAN +#else +# define __BYTE_ORDER __BIG_ENDIAN +#endif + +#endif /*not HAVE_ENDIAN_H*/ + +#endif /*_LIBS_UTILS_ENDIAN_H*/ diff --git a/include/utils/Errors.h b/include/utils/Errors.h new file mode 100644 index 000000000..1bf9e6f2b --- /dev/null +++ b/include/utils/Errors.h @@ -0,0 +1,87 @@ +/* + * 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. + */ + +#ifndef ANDROID_ERRORS_H +#define ANDROID_ERRORS_H + +#include +#include + +namespace android { + +// use this type to return error codes +#ifdef HAVE_MS_C_RUNTIME +typedef int status_t; +#else +typedef int32_t status_t; +#endif + +/* the MS C runtime lacks a few error codes */ + +/* + * Error codes. + * All error codes are negative values. + */ + +// Win32 #defines NO_ERROR as well. It has the same value, so there's no +// real conflict, though it's a bit awkward. +#ifdef _WIN32 +# undef NO_ERROR +#endif + +enum { + OK = 0, // Everything's swell. + NO_ERROR = 0, // No errors. + + UNKNOWN_ERROR = 0x80000000, + + NO_MEMORY = -ENOMEM, + INVALID_OPERATION = -ENOSYS, + BAD_VALUE = -EINVAL, + BAD_TYPE = 0x80000001, + NAME_NOT_FOUND = -ENOENT, + PERMISSION_DENIED = -EPERM, + NO_INIT = -ENODEV, + ALREADY_EXISTS = -EEXIST, + DEAD_OBJECT = -EPIPE, + FAILED_TRANSACTION = 0x80000002, + JPARKS_BROKE_IT = -EPIPE, +#if !defined(HAVE_MS_C_RUNTIME) + BAD_INDEX = -EOVERFLOW, + NOT_ENOUGH_DATA = -ENODATA, + WOULD_BLOCK = -EWOULDBLOCK, + TIMED_OUT = -ETIME, + UNKNOWN_TRANSACTION = -EBADMSG, +#else + BAD_INDEX = -E2BIG, + NOT_ENOUGH_DATA = 0x80000003, + WOULD_BLOCK = 0x80000004, + TIMED_OUT = 0x80000005, + UNKNOWN_TRANSACTION = 0x80000006, +#endif +}; + +// Restore define; enumeration is in "android" namespace, so the value defined +// there won't work for Win32 code in a different namespace. +#ifdef _WIN32 +# define NO_ERROR 0L +#endif + +}; // namespace android + +// --------------------------------------------------------------------------- + +#endif // ANDROID_ERRORS_H diff --git a/include/utils/FileMap.h b/include/utils/FileMap.h new file mode 100644 index 000000000..8dfd3bea6 --- /dev/null +++ b/include/utils/FileMap.h @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2006 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. + */ + +// +// Encapsulate a shared file mapping. +// +#ifndef __LIBS_FILE_MAP_H +#define __LIBS_FILE_MAP_H + +#include + +#ifdef HAVE_WIN32_FILEMAP +#include +#endif + +namespace android { + +/* + * This represents a memory-mapped file. It might be the entire file or + * only part of it. This requires a little bookkeeping because the mapping + * needs to be aligned on page boundaries, and in some cases we'd like to + * have multiple references to the mapped area without creating additional + * maps. + * + * This always uses MAP_SHARED. + * + * TODO: we should be able to create a new FileMap that is a subset of + * an existing FileMap and shares the underlying mapped pages. Requires + * completing the refcounting stuff and possibly introducing the notion + * of a FileMap hierarchy. + */ +class FileMap { +public: + FileMap(void); + + /* + * Create a new mapping on an open file. + * + * Closing the file descriptor does not unmap the pages, so we don't + * claim ownership of the fd. + * + * Returns "false" on failure. + */ + bool create(const char* origFileName, int fd, + off_t offset, size_t length, bool readOnly); + + /* + * Return the name of the file this map came from, if known. + */ + const char* getFileName(void) const { return mFileName; } + + /* + * Get a pointer to the piece of the file we requested. + */ + void* getDataPtr(void) const { return mDataPtr; } + + /* + * Get the length we requested. + */ + size_t getDataLength(void) const { return mDataLength; } + + /* + * Get the data offset used to create this map. + */ + off_t getDataOffset(void) const { return mDataOffset; } + + /* + * Get a "copy" of the object. + */ + FileMap* acquire(void) { mRefCount++; return this; } + + /* + * Call this when mapping is no longer needed. + */ + void release(void) { + if (--mRefCount <= 0) + delete this; + } + + /* + * This maps directly to madvise() values, but allows us to avoid + * including everywhere. + */ + enum MapAdvice { + NORMAL, RANDOM, SEQUENTIAL, WILLNEED, DONTNEED + }; + + /* + * Apply an madvise() call to the entire file. + * + * Returns 0 on success, -1 on failure. + */ + int advise(MapAdvice advice); + +protected: + // don't delete objects; call release() + ~FileMap(void); + +private: + // these are not implemented + FileMap(const FileMap& src); + const FileMap& operator=(const FileMap& src); + + int mRefCount; // reference count + char* mFileName; // original file name, if known + void* mBasePtr; // base of mmap area; page aligned + size_t mBaseLength; // length, measured from "mBasePtr" + off_t mDataOffset; // offset used when map was created + void* mDataPtr; // start of requested data, offset from base + size_t mDataLength; // length, measured from "mDataPtr" +#ifdef HAVE_WIN32_FILEMAP + HANDLE mFileHandle; // Win32 file handle + HANDLE mFileMapping; // Win32 file mapping handle +#endif + + static long mPageSize; +}; + +}; // namespace android + +#endif // __LIBS_FILE_MAP_H diff --git a/include/utils/IBinder.h b/include/utils/IBinder.h new file mode 100644 index 000000000..737033090 --- /dev/null +++ b/include/utils/IBinder.h @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_IBINDER_H +#define ANDROID_IBINDER_H + +#include +#include +#include +#include + + +#define B_PACK_CHARS(c1, c2, c3, c4) \ + ((((c1)<<24)) | (((c2)<<16)) | (((c3)<<8)) | (c4)) + +// --------------------------------------------------------------------------- +namespace android { + +class BBinder; +class BpBinder; +class IInterface; +class Parcel; + +/** + * Base class and low-level protocol for a remotable object. + * You can derive from this class to create an object for which other + * processes can hold references to it. Communication between processes + * (method calls, property get and set) is down through a low-level + * protocol implemented on top of the transact() API. + */ +class IBinder : public virtual RefBase +{ +public: + enum { + FIRST_CALL_TRANSACTION = 0x00000001, + LAST_CALL_TRANSACTION = 0x00ffffff, + + PING_TRANSACTION = B_PACK_CHARS('_','P','N','G'), + DUMP_TRANSACTION = B_PACK_CHARS('_','D','M','P'), + INTERFACE_TRANSACTION = B_PACK_CHARS('_', 'N', 'T', 'F'), + + // Corresponds to tfOneWay -- an asynchronous call. + FLAG_ONEWAY = 0x00000001 + }; + + inline IBinder() { } + + /** + * Check if this IBinder implements the interface named by + * @a descriptor. If it does, the base pointer to it is returned, + * which you can safely static_cast<> to the concrete C++ interface. + */ + virtual sp queryLocalInterface(const String16& descriptor); + + /** + * Return the canonical name of the interface provided by this IBinder + * object. + */ + virtual String16 getInterfaceDescriptor() const = 0; + + virtual bool isBinderAlive() const = 0; + virtual status_t pingBinder() = 0; + virtual status_t dump(int fd, const Vector& args) = 0; + + virtual status_t transact( uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags = 0) = 0; + + /** + * This method allows you to add data that is transported through + * IPC along with your IBinder pointer. When implementing a Binder + * object, override it to write your desired data in to @a outData. + * You can then call getConstantData() on your IBinder to retrieve + * that data, from any process. You MUST return the number of bytes + * written in to the parcel (including padding). + */ + class DeathRecipient : public virtual RefBase + { + public: + virtual void binderDied(const wp& who) = 0; + }; + + /** + * Register the @a recipient for a notification if this binder + * goes away. If this binder object unexpectedly goes away + * (typically because its hosting process has been killed), + * then DeathRecipient::binderDied() will be called with a referene + * to this. + * + * The @a cookie is optional -- if non-NULL, it should be a + * memory address that you own (that is, you know it is unique). + * + * @note You will only receive death notifications for remote binders, + * as local binders by definition can't die without you dying as well. + * Trying to use this function on a local binder will result in an + * INVALID_OPERATION code being returned and nothing happening. + * + * @note This link always holds a weak reference to its recipient. + * + * @note You will only receive a weak reference to the dead + * binder. You should not try to promote this to a strong reference. + * (Nor should you need to, as there is nothing useful you can + * directly do with it now that it has passed on.) + */ + virtual status_t linkToDeath(const sp& recipient, + void* cookie = NULL, + uint32_t flags = 0) = 0; + + /** + * Remove a previously registered death notification. + * The @a recipient will no longer be called if this object + * dies. The @a cookie is optional. If non-NULL, you can + * supply a NULL @a recipient, and the recipient previously + * added with that cookie will be unlinked. + */ + virtual status_t unlinkToDeath( const wp& recipient, + void* cookie = NULL, + uint32_t flags = 0, + wp* outRecipient = NULL) = 0; + + virtual bool checkSubclass(const void* subclassID) const; + + typedef void (*object_cleanup_func)(const void* id, void* obj, void* cleanupCookie); + + virtual void attachObject( const void* objectID, + void* object, + void* cleanupCookie, + object_cleanup_func func) = 0; + virtual void* findObject(const void* objectID) const = 0; + virtual void detachObject(const void* objectID) = 0; + + virtual BBinder* localBinder(); + virtual BpBinder* remoteBinder(); + +protected: + inline virtual ~IBinder() { } + +private: +}; + +}; // namespace android + +// --------------------------------------------------------------------------- + +#endif // ANDROID_IBINDER_H diff --git a/include/utils/IInterface.h b/include/utils/IInterface.h new file mode 100644 index 000000000..959722a4d --- /dev/null +++ b/include/utils/IInterface.h @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2005 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_IINTERFACE_H +#define ANDROID_IINTERFACE_H + +#include + +namespace android { + +// ---------------------------------------------------------------------- + +class IInterface : public virtual RefBase +{ +public: + sp asBinder(); + sp asBinder() const; + +protected: + virtual IBinder* onAsBinder() = 0; +}; + +// ---------------------------------------------------------------------- + +template +inline sp interface_cast(const sp& obj) +{ + return INTERFACE::asInterface(obj); +} + +// ---------------------------------------------------------------------- + +template +class BnInterface : public INTERFACE, public BBinder +{ +public: + virtual sp queryLocalInterface(const String16& _descriptor); + virtual String16 getInterfaceDescriptor() const; + +protected: + virtual IBinder* onAsBinder(); +}; + +// ---------------------------------------------------------------------- + +template +class BpInterface : public INTERFACE, public BpRefBase +{ +public: + BpInterface(const sp& remote); + +protected: + virtual IBinder* onAsBinder(); +}; + +// ---------------------------------------------------------------------- + +#define DECLARE_META_INTERFACE(INTERFACE) \ + static const String16 descriptor; \ + static sp asInterface(const sp& obj); \ + virtual String16 getInterfaceDescriptor() const; \ + +#define IMPLEMENT_META_INTERFACE(INTERFACE, NAME) \ + const String16 I##INTERFACE::descriptor(NAME); \ + String16 I##INTERFACE::getInterfaceDescriptor() const { \ + return I##INTERFACE::descriptor; \ + } \ + sp I##INTERFACE::asInterface(const sp& obj) \ + { \ + sp intr; \ + if (obj != NULL) { \ + intr = static_cast( \ + obj->queryLocalInterface( \ + I##INTERFACE::descriptor).get()); \ + if (intr == NULL) { \ + intr = new Bp##INTERFACE(obj); \ + } \ + } \ + return intr; \ + } \ + +// ---------------------------------------------------------------------- +// No user-servicable parts after this... + +template +inline sp BnInterface::queryLocalInterface( + const String16& _descriptor) +{ + if (_descriptor == INTERFACE::descriptor) return this; + return NULL; +} + +template +inline String16 BnInterface::getInterfaceDescriptor() const +{ + return INTERFACE::getInterfaceDescriptor(); +} + +template +IBinder* BnInterface::onAsBinder() +{ + return this; +} + +template +inline BpInterface::BpInterface(const sp& remote) + : BpRefBase(remote) +{ +} + +template +inline IBinder* BpInterface::onAsBinder() +{ + return remote(); +} + +// ---------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_IINTERFACE_H diff --git a/include/utils/IMemory.h b/include/utils/IMemory.h new file mode 100644 index 000000000..35a3fd7da --- /dev/null +++ b/include/utils/IMemory.h @@ -0,0 +1,94 @@ +/* + * 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. + */ + +#ifndef ANDROID_IMEMORY_H +#define ANDROID_IMEMORY_H + +#include +#include +#include + +#include +#include +#include + +namespace android { + +// ---------------------------------------------------------------------------- + +class IMemoryHeap : public IInterface +{ +public: + DECLARE_META_INTERFACE(MemoryHeap); + + // flags returned by getFlags() + enum { + READ_ONLY = 0x00000001, + MAP_ONCE = 0x00000002 + }; + + virtual int getHeapID() const = 0; + virtual void* getBase() const = 0; + virtual size_t getSize() const = 0; + virtual uint32_t getFlags() const = 0; + + // these are there just for backward source compatibility + int32_t heapID() const { return getHeapID(); } + void* base() const { return getBase(); } + size_t virtualSize() const { return getSize(); } +}; + +class BnMemoryHeap : public BnInterface +{ +public: + virtual status_t onTransact( + uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags = 0); +}; + +// ---------------------------------------------------------------------------- + +class IMemory : public IInterface +{ +public: + DECLARE_META_INTERFACE(Memory); + + virtual sp getMemory(ssize_t* offset=0, size_t* size=0) const = 0; + + // helpers + void* fastPointer(const sp& heap, ssize_t offset) const; + void* pointer() const; + size_t size() const; + ssize_t offset() const; +}; + +class BnMemory : public BnInterface +{ +public: + virtual status_t onTransact( + uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags = 0); +}; + +// ---------------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_IMEMORY_H diff --git a/include/utils/IPCThreadState.h b/include/utils/IPCThreadState.h new file mode 100644 index 000000000..0490fd3ec --- /dev/null +++ b/include/utils/IPCThreadState.h @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2005 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_IPC_THREAD_STATE_H +#define ANDROID_IPC_THREAD_STATE_H + +#include +#include +#include +#include + +#ifdef HAVE_WIN32_PROC +typedef int uid_t; +#endif + +// --------------------------------------------------------------------------- +namespace android { + +class IPCThreadState +{ +public: + static IPCThreadState* self(); + + sp process(); + + status_t clearLastError(); + + int getCallingPid(); + int getCallingUid(); + + int64_t clearCallingIdentity(); + void restoreCallingIdentity(int64_t token); + + void flushCommands(); + + void joinThreadPool(bool isMain = true); + + // Stop the local process. + void stopProcess(bool immediate = true); + + status_t transact(int32_t handle, + uint32_t code, const Parcel& data, + Parcel* reply, uint32_t flags); + + void incStrongHandle(int32_t handle); + void decStrongHandle(int32_t handle); + void incWeakHandle(int32_t handle); + void decWeakHandle(int32_t handle); + status_t attemptIncStrongHandle(int32_t handle); + static void expungeHandle(int32_t handle, IBinder* binder); + status_t requestDeathNotification( int32_t handle, + BpBinder* proxy); + status_t clearDeathNotification( int32_t handle, + BpBinder* proxy); + + static void shutdown(); + +private: + IPCThreadState(); + ~IPCThreadState(); + + status_t sendReply(const Parcel& reply, uint32_t flags); + status_t waitForResponse(Parcel *reply, + status_t *acquireResult=NULL); + status_t talkWithDriver(bool doReceive=true); + status_t writeTransactionData(int32_t cmd, + uint32_t binderFlags, + int32_t handle, + uint32_t code, + const Parcel& data, + status_t* statusBuffer); + status_t executeCommand(int32_t command); + + void clearCaller(); + + static void threadDestructor(void *st); + static void freeBuffer(Parcel* parcel, + const uint8_t* data, size_t dataSize, + const size_t* objects, size_t objectsSize, + void* cookie); + + const sp mProcess; + Vector mPendingStrongDerefs; + Vector mPendingWeakDerefs; + + Parcel mIn; + Parcel mOut; + status_t mLastError; + pid_t mCallingPid; + uid_t mCallingUid; +}; + +}; // namespace android + +// --------------------------------------------------------------------------- + +#endif // ANDROID_IPC_THREAD_STATE_H diff --git a/include/utils/IPermissionController.h b/include/utils/IPermissionController.h new file mode 100644 index 000000000..cb1dd345d --- /dev/null +++ b/include/utils/IPermissionController.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2005 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_IPERMISSION_CONTROLLER_H +#define ANDROID_IPERMISSION_CONTROLLER_H + +#include + +namespace android { + +// ---------------------------------------------------------------------- + +class IPermissionController : public IInterface +{ +public: + DECLARE_META_INTERFACE(PermissionController); + + virtual bool checkPermission(const String16& permission, + int32_t pid, int32_t uid) = 0; + + enum { + CHECK_PERMISSION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION + }; +}; + +// ---------------------------------------------------------------------- + +class BnPermissionController : public BnInterface +{ +public: + virtual status_t onTransact( uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags = 0); +}; + +// ---------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_IPERMISSION_CONTROLLER_H + diff --git a/include/utils/IServiceManager.h b/include/utils/IServiceManager.h new file mode 100644 index 000000000..e3d99fe7e --- /dev/null +++ b/include/utils/IServiceManager.h @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2005 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_ISERVICE_MANAGER_H +#define ANDROID_ISERVICE_MANAGER_H + +#include +#include +#include +#include + +namespace android { + +// ---------------------------------------------------------------------- + +class IServiceManager : public IInterface +{ +public: + DECLARE_META_INTERFACE(ServiceManager); + + /** + * Retrieve an existing service, blocking for a few seconds + * if it doesn't yet exist. + */ + virtual sp getService( const String16& name) const = 0; + + /** + * Retrieve an existing service, non-blocking. + */ + virtual sp checkService( const String16& name) const = 0; + + /** + * Register a service. + */ + virtual status_t addService( const String16& name, + const sp& service) = 0; + + /** + * Return list of all existing services. + */ + virtual Vector listServices() = 0; + + enum { + GET_SERVICE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION, + CHECK_SERVICE_TRANSACTION, + ADD_SERVICE_TRANSACTION, + LIST_SERVICES_TRANSACTION, + }; +}; + +sp defaultServiceManager(); + +template +status_t getService(const String16& name, sp* outService) +{ + const sp sm = defaultServiceManager(); + if (sm != NULL) { + *outService = interface_cast(sm->getService(name)); + if ((*outService) != NULL) return NO_ERROR; + } + return NAME_NOT_FOUND; +} + +bool checkCallingPermission(const String16& permission); +bool checkCallingPermission(const String16& permission, + int32_t* outPid, int32_t* outUid); + +// ---------------------------------------------------------------------- + +class BnServiceManager : public BnInterface +{ +public: + virtual status_t onTransact( uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags = 0); +}; + +// ---------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_ISERVICE_MANAGER_H + diff --git a/include/utils/KeyedVector.h b/include/utils/KeyedVector.h new file mode 100644 index 000000000..f4513ee20 --- /dev/null +++ b/include/utils/KeyedVector.h @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2005 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_KEYED_VECTOR_H +#define ANDROID_KEYED_VECTOR_H + +#include +#include +#include + +#include +#include +#include + +// --------------------------------------------------------------------------- + +namespace android { + +template +class KeyedVector +{ +public: + typedef KEY key_type; + typedef VALUE value_type; + + inline KeyedVector(); + + /* + * empty the vector + */ + + inline void clear() { mVector.clear(); } + + /*! + * vector stats + */ + + //! returns number of items in the vector + inline size_t size() const { return mVector.size(); } + //! returns wether or not the vector is empty + inline bool isEmpty() const { return mVector.isEmpty(); } + //! returns how many items can be stored without reallocating the backing store + inline size_t capacity() const { return mVector.capacity(); } + //! setst the capacity. capacity can never be reduced less than size() + inline ssize_t setCapacity(size_t size) { return mVector.setCapacity(size); } + + /*! + * accessors + */ + const VALUE& valueFor(const KEY& key) const; + const VALUE& valueAt(size_t index) const; + const KEY& keyAt(size_t index) const; + ssize_t indexOfKey(const KEY& key) const; + + /*! + * modifing the array + */ + + VALUE& editValueFor(const KEY& key); + VALUE& editValueAt(size_t index); + + /*! + * add/insert/replace items + */ + + ssize_t add(const KEY& key, const VALUE& item); + ssize_t replaceValueFor(const KEY& key, const VALUE& item); + ssize_t replaceValueAt(size_t index, const VALUE& item); + + /*! + * remove items + */ + + ssize_t removeItem(const KEY& key); + ssize_t removeItemsAt(size_t index, size_t count = 1); + +private: + SortedVector< key_value_pair_t > mVector; +}; + +// --------------------------------------------------------------------------- + +/** + * Variation of KeyedVector that holds a default value to return when + * valueFor() is called with a key that doesn't exist. + */ +template +class DefaultKeyedVector : public KeyedVector +{ +public: + inline DefaultKeyedVector(const VALUE& defValue = VALUE()); + const VALUE& valueFor(const KEY& key) const; + +private: + VALUE mDefault; +}; + +// --------------------------------------------------------------------------- + +template inline +KeyedVector::KeyedVector() +{ +} + +template inline +ssize_t KeyedVector::indexOfKey(const KEY& key) const { + return mVector.indexOf( key_value_pair_t(key) ); +} + +template inline +const VALUE& KeyedVector::valueFor(const KEY& key) const { + ssize_t i = indexOfKey(key); + assert(i>=0); + return mVector.itemAt(i).value; +} + +template inline +const VALUE& KeyedVector::valueAt(size_t index) const { + return mVector.itemAt(index).value; +} + +template inline +const KEY& KeyedVector::keyAt(size_t index) const { + return mVector.itemAt(index).key; +} + +template inline +VALUE& KeyedVector::editValueFor(const KEY& key) { + ssize_t i = indexOfKey(key); + assert(i>=0); + return mVector.editItemAt(i).value; +} + +template inline +VALUE& KeyedVector::editValueAt(size_t index) { + return mVector.editItemAt(index).value; +} + +template inline +ssize_t KeyedVector::add(const KEY& key, const VALUE& value) { + return mVector.add( key_value_pair_t(key, value) ); +} + +template inline +ssize_t KeyedVector::replaceValueFor(const KEY& key, const VALUE& value) { + key_value_pair_t pair(key, value); + mVector.remove(pair); + return mVector.add(pair); +} + +template inline +ssize_t KeyedVector::replaceValueAt(size_t index, const VALUE& item) { + if (index inline +ssize_t KeyedVector::removeItem(const KEY& key) { + return mVector.remove(key_value_pair_t(key)); +} + +template inline +ssize_t KeyedVector::removeItemsAt(size_t index, size_t count) { + return mVector.removeItemsAt(index, count); +} + +// --------------------------------------------------------------------------- + +template inline +DefaultKeyedVector::DefaultKeyedVector(const VALUE& defValue) + : mDefault(defValue) +{ +} + +template inline +const VALUE& DefaultKeyedVector::valueFor(const KEY& key) const { + ssize_t i = indexOfKey(key); + return i >= 0 ? KeyedVector::valueAt(i) : mDefault; +} + +}; // namespace android + +// --------------------------------------------------------------------------- + +#endif // ANDROID_KEYED_VECTOR_H diff --git a/include/utils/List.h b/include/utils/List.h new file mode 100644 index 000000000..1a6be9ac9 --- /dev/null +++ b/include/utils/List.h @@ -0,0 +1,280 @@ +/* + * Copyright (C) 2005 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. + */ + +// +// Templated list class. Normally we'd use STL, but we don't have that. +// This class mimics STL's interfaces. +// +// Objects are copied into the list with the '=' operator or with copy- +// construction, so if the compiler's auto-generated versions won't work for +// you, define your own. +// +// The only class you want to use from here is "List". Do not use classes +// starting with "_" directly. +// +#ifndef _LIBS_UTILS_LIST_H +#define _LIBS_UTILS_LIST_H + +namespace android { + +/* + * One element in the list. + */ +template class _ListNode { +public: + typedef _ListNode _Node; + + _ListNode(const T& val) : mVal(val) {} + ~_ListNode(void) {} + + T& getRef(void) { return mVal; } + void setVal(const T& val) { mVal = val; } + + _Node* getPrev(void) const { return mpPrev; } + void setPrev(_Node* ptr) { mpPrev = ptr; } + _Node* getNext(void) const { return mpNext; } + void setNext(_Node* ptr) { mpNext = ptr; } + +private: + T mVal; + _Node* mpPrev; + _Node* mpNext; +}; + +/* + * Iterator for walking through the list. + */ +template class _ListIterator { +public: + typedef _ListIterator _Iter; + typedef _ListNode _Node; + + _ListIterator(void) {} + _ListIterator(_Node* ptr) : mpNode(ptr) {} + ~_ListIterator(void) {} + + /* + * Dereference operator. Used to get at the juicy insides. + */ + Tref operator*() const { return mpNode->getRef(); } + + /* + * Iterator comparison. + */ + bool operator==(const _Iter& right) const { return mpNode == right.mpNode; } + bool operator!=(const _Iter& right) const { return mpNode != right.mpNode; } + + /* + * Incr/decr, used to move through the list. + */ + _Iter& operator++(void) { // pre-increment + mpNode = mpNode->getNext(); + return *this; + } + _Iter operator++(int) { // post-increment + _Iter tmp = *this; + ++*this; + return tmp; + } + _Iter& operator--(void) { // pre-increment + mpNode = mpNode->getPrev(); + return *this; + } + _Iter operator--(int) { // post-increment + _Iter tmp = *this; + --*this; + return tmp; + } + + _Node* getNode(void) const { return mpNode; } + +private: + _Node* mpNode; +}; + + +/* + * Doubly-linked list. Instantiate with "List myList". + * + * Objects added to the list are copied using the assignment operator, + * so this must be defined. + */ +template class List { +public: + typedef _ListNode _Node; + + List(void) { + prep(); + } + List(const List& src) { // copy-constructor + prep(); + insert(begin(), src.begin(), src.end()); + } + virtual ~List(void) { + clear(); + delete[] (unsigned char*) mpMiddle; + } + + typedef _ListIterator iterator; + typedef _ListIterator const_iterator; + + List& operator=(const List& right); + + /* returns true if the list is empty */ + bool empty(void) const { return mpMiddle->getNext() == mpMiddle; } + + /* return #of elements in list */ + unsigned int size(void) const { + return distance(begin(), end()); + } + + /* + * Return the first element or one past the last element. The + * _ListNode* we're returning is converted to an "iterator" by a + * constructor in _ListIterator. + */ + iterator begin() { return mpMiddle->getNext(); } + const_iterator begin() const { return mpMiddle->getNext(); } + iterator end() { return mpMiddle; } + const_iterator end() const { return mpMiddle; } + + /* add the object to the head or tail of the list */ + void push_front(const T& val) { insert(begin(), val); } + void push_back(const T& val) { insert(end(), val); } + + /* insert before the current node; returns iterator at new node */ + iterator insert(iterator posn, const T& val) { + _Node* newNode = new _Node(val); // alloc & copy-construct + newNode->setNext(posn.getNode()); + newNode->setPrev(posn.getNode()->getPrev()); + posn.getNode()->getPrev()->setNext(newNode); + posn.getNode()->setPrev(newNode); + return newNode; + } + + /* insert a range of elements before the current node */ + void insert(iterator posn, const_iterator first, const_iterator last) { + for ( ; first != last; ++first) + insert(posn, *first); + } + + /* remove one entry; returns iterator at next node */ + iterator erase(iterator posn) { + _Node* pNext = posn.getNode()->getNext(); + _Node* pPrev = posn.getNode()->getPrev(); + pPrev->setNext(pNext); + pNext->setPrev(pPrev); + delete posn.getNode(); + return pNext; + } + + /* remove a range of elements */ + iterator erase(iterator first, iterator last) { + while (first != last) + erase(first++); // don't erase than incr later! + return last; + } + + /* remove all contents of the list */ + void clear(void) { + _Node* pCurrent = mpMiddle->getNext(); + _Node* pNext; + + while (pCurrent != mpMiddle) { + pNext = pCurrent->getNext(); + delete pCurrent; + pCurrent = pNext; + } + mpMiddle->setPrev(mpMiddle); + mpMiddle->setNext(mpMiddle); + } + + /* + * Measure the distance between two iterators. On exist, "first" + * will be equal to "last". The iterators must refer to the same + * list. + * + * (This is actually a generic iterator function. It should be part + * of some other class, possibly an iterator base class. It needs to + * know the difference between a list, which has to march through, + * and a vector, which can just do pointer math.) + */ + unsigned int distance(iterator first, iterator last) { + unsigned int count = 0; + while (first != last) { + ++first; + ++count; + } + return count; + } + unsigned int distance(const_iterator first, const_iterator last) const { + unsigned int count = 0; + while (first != last) { + ++first; + ++count; + } + return count; + } + +private: + /* + * I want a _ListNode but don't need it to hold valid data. More + * to the point, I don't want T's constructor to fire, since it + * might have side-effects or require arguments. So, we do this + * slightly uncouth storage alloc. + */ + void prep(void) { + mpMiddle = (_Node*) new unsigned char[sizeof(_Node)]; + mpMiddle->setPrev(mpMiddle); + mpMiddle->setNext(mpMiddle); + } + + /* + * This node plays the role of "pointer to head" and "pointer to tail". + * It sits in the middle of a circular list of nodes. The iterator + * runs around the circle until it encounters this one. + */ + _Node* mpMiddle; +}; + +/* + * Assignment operator. + * + * The simplest way to do this would be to clear out the target list and + * fill it with the source. However, we can speed things along by + * re-using existing elements. + */ +template +List& List::operator=(const List& right) +{ + if (this == &right) + return *this; // self-assignment + iterator firstDst = begin(); + iterator lastDst = end(); + const_iterator firstSrc = right.begin(); + const_iterator lastSrc = right.end(); + while (firstSrc != lastSrc && firstDst != lastDst) + *firstDst++ = *firstSrc++; + if (firstSrc == lastSrc) // ran out of elements in source? + erase(firstDst, lastDst); // yes, erase any extras + else + insert(lastDst, firstSrc, lastSrc); // copy remaining over + return *this; +} + +}; // namespace android + +#endif // _LIBS_UTILS_LIST_H diff --git a/include/utils/Log.h b/include/utils/Log.h new file mode 100644 index 000000000..3c6cc8bdc --- /dev/null +++ b/include/utils/Log.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2005 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. + */ + +// +// C/C++ logging functions. See the logging documentation for API details. +// +// We'd like these to be available from C code (in case we import some from +// somewhere), so this has a C interface. +// +// The output will be correct when the log file is shared between multiple +// threads and/or multiple processes so long as the operating system +// supports O_APPEND. These calls have mutex-protected data structures +// and so are NOT reentrant. Do not use LOG in a signal handler. +// +#ifndef _LIBS_UTILS_LOG_H +#define _LIBS_UTILS_LOG_H + +#include + +#endif // _LIBS_UTILS_LOG_H diff --git a/include/utils/LogSocket.h b/include/utils/LogSocket.h new file mode 100644 index 000000000..01fbfb50e --- /dev/null +++ b/include/utils/LogSocket.h @@ -0,0 +1,20 @@ +/* utils/LogSocket.h +** +** Copyright 2008, The Android Open Source Project +** +** This file is dual licensed. It may be redistributed and/or modified +** under the terms of the Apache 2.0 License OR version 2 of the GNU +** General Public License. +*/ + +#ifndef _UTILS_LOGSOCKET_H +#define _UTILS_LOGSOCKET_H + +#define SOCKET_CLOSE_LOCAL 0 + +void add_send_stats(int fd, int send); +void add_recv_stats(int fd, int recv); +void log_socket_close(int fd, short reason); +void log_socket_connect(int fd, unsigned int ip, unsigned short port); + +#endif /* _UTILS_LOGSOCKET_H */ diff --git a/include/utils/MemoryBase.h b/include/utils/MemoryBase.h new file mode 100644 index 000000000..eb5a9d275 --- /dev/null +++ b/include/utils/MemoryBase.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_MEMORY_BASE_H +#define ANDROID_MEMORY_BASE_H + +#include +#include + +#include + + +namespace android { + +// --------------------------------------------------------------------------- + +class MemoryBase : public BnMemory +{ +public: + MemoryBase(const sp& heap, ssize_t offset, size_t size); + virtual ~MemoryBase(); + virtual sp getMemory(ssize_t* offset, size_t* size) const; + +protected: + size_t getSize() const { return mSize; } + ssize_t getOffset() const { return mOffset; } + const sp& getHeap() const { return mHeap; } + +private: + size_t mSize; + ssize_t mOffset; + sp mHeap; +}; + +// --------------------------------------------------------------------------- +}; // namespace android + +#endif // ANDROID_MEMORY_BASE_H diff --git a/include/utils/MemoryDealer.h b/include/utils/MemoryDealer.h new file mode 100644 index 000000000..454b6270e --- /dev/null +++ b/include/utils/MemoryDealer.h @@ -0,0 +1,238 @@ +/* + * 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. + */ + +#ifndef ANDROID_MEMORY_DEALER_H +#define ANDROID_MEMORY_DEALER_H + + +#include +#include + +#include +#include +#include + +namespace android { +// ---------------------------------------------------------------------------- +class String8; + +/* + * interface for implementing a "heap". A heap basically provides + * the IMemoryHeap interface for cross-process sharing and the + * ability to map/unmap pages within the heap. + */ +class HeapInterface : public virtual BnMemoryHeap +{ +public: + // all values must be page-aligned + virtual sp mapMemory(size_t offset, size_t size) = 0; +}; + +// ---------------------------------------------------------------------------- + +/* + * interface for implementing an allocator. An allocator provides + * methods for allocating and freeing memory blocks and dumping + * its state. + */ +class AllocatorInterface : public RefBase +{ +public: + enum { + PAGE_ALIGNED = 0x00000001 + }; + + virtual size_t allocate(size_t size, uint32_t flags = 0) = 0; + virtual status_t deallocate(size_t offset) = 0; + virtual size_t size() const = 0; + virtual void dump(const char* what, uint32_t flags = 0) const = 0; + virtual void dump(String8& res, + const char* what, uint32_t flags = 0) const = 0; +}; + +// ---------------------------------------------------------------------------- + +/* + * concrete implementation of HeapInterface on top of mmap() + */ +class SharedHeap : public HeapInterface, public MemoryHeapBase +{ +public: + SharedHeap(size_t size, uint32_t flags = 0, char const * name = NULL); + virtual ~SharedHeap(); + virtual sp mapMemory(size_t offset, size_t size); +}; + +// ---------------------------------------------------------------------------- + +/* + * A simple templatized doubly linked-list implementation + */ + +template +class LinkedList +{ + NODE* mFirst; + NODE* mLast; + +public: + LinkedList() : mFirst(0), mLast(0) { } + bool isEmpty() const { return mFirst == 0; } + NODE const* head() const { return mFirst; } + NODE* head() { return mFirst; } + NODE const* tail() const { return mLast; } + NODE* tail() { return mLast; } + + void insertAfter(NODE* node, NODE* newNode) { + newNode->prev = node; + newNode->next = node->next; + if (node->next == 0) mLast = newNode; + else node->next->prev = newNode; + node->next = newNode; + } + + void insertBefore(NODE* node, NODE* newNode) { + newNode->prev = node->prev; + newNode->next = node; + if (node->prev == 0) mFirst = newNode; + else node->prev->next = newNode; + node->prev = newNode; + } + + void insertHead(NODE* newNode) { + if (mFirst == 0) { + mFirst = mLast = newNode; + newNode->prev = newNode->next = 0; + } else { + insertBefore(mFirst, newNode); + } + } + + void insertTail(NODE* newNode) { + if (mLast == 0) insertBeginning(newNode); + else insertAfter(mLast, newNode); + } + + NODE* remove(NODE* node) { + if (node->prev == 0) mFirst = node->next; + else node->prev->next = node->next; + if (node->next == 0) mLast = node->prev; + else node->next->prev = node->prev; + return node; + } +}; + + +/* + * concrete implementation of AllocatorInterface using a simple + * best-fit allocation scheme + */ +class SimpleBestFitAllocator : public AllocatorInterface +{ +public: + + SimpleBestFitAllocator(size_t size); + virtual ~SimpleBestFitAllocator(); + + virtual size_t allocate(size_t size, uint32_t flags = 0); + virtual status_t deallocate(size_t offset); + virtual size_t size() const; + virtual void dump(const char* what, uint32_t flags = 0) const; + virtual void dump(String8& res, + const char* what, uint32_t flags = 0) const; + +private: + + struct chunk_t { + chunk_t(size_t start, size_t size) + : start(start), size(size), free(1), prev(0), next(0) { + } + size_t start; + size_t size : 28; + int free : 4; + mutable chunk_t* prev; + mutable chunk_t* next; + }; + + ssize_t alloc(size_t size, uint32_t flags); + chunk_t* dealloc(size_t start); + void dump_l(const char* what, uint32_t flags = 0) const; + void dump_l(String8& res, const char* what, uint32_t flags = 0) const; + + static const int kMemoryAlign; + mutable Mutex mLock; + LinkedList mList; + size_t mHeapSize; +}; + +// ---------------------------------------------------------------------------- + +class MemoryDealer : public RefBase +{ +public: + + enum { + READ_ONLY = MemoryHeapBase::READ_ONLY, + PAGE_ALIGNED = AllocatorInterface::PAGE_ALIGNED + }; + + // creates a memory dealer with the SharedHeap and SimpleBestFitAllocator + MemoryDealer(size_t size, uint32_t flags = 0, const char* name = 0); + + // provide a custom heap but use the SimpleBestFitAllocator + MemoryDealer(const sp& heap); + + // provide both custom heap and allocotar + MemoryDealer( + const sp& heap, + const sp& allocator); + + virtual ~MemoryDealer(); + + virtual sp allocate(size_t size, uint32_t flags = 0); + virtual void deallocate(size_t offset); + virtual void dump(const char* what, uint32_t flags = 0) const; + + + sp getMemoryHeap() const { return heap(); } + sp getAllocator() const { return allocator(); } + +private: + const sp& heap() const; + const sp& allocator() const; + + class Allocation : public BnMemory { + public: + Allocation(const sp& dealer, + ssize_t offset, size_t size, const sp& memory); + virtual ~Allocation(); + virtual sp getMemory(ssize_t* offset, size_t* size) const; + private: + sp mDealer; + ssize_t mOffset; + size_t mSize; + sp mMemory; + }; + + sp mHeap; + sp mAllocator; +}; + + +// ---------------------------------------------------------------------------- +}; // namespace android + +#endif // ANDROID_MEMORY_DEALER_H diff --git a/include/utils/MemoryHeapBase.h b/include/utils/MemoryHeapBase.h new file mode 100644 index 000000000..574acf4f9 --- /dev/null +++ b/include/utils/MemoryHeapBase.h @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_MEMORY_HEAP_BASE_H +#define ANDROID_MEMORY_HEAP_BASE_H + +#include +#include + +#include + + +namespace android { + +// --------------------------------------------------------------------------- + +class MemoryHeapBase : public virtual BnMemoryHeap +{ +public: + enum { + READ_ONLY = IMemoryHeap::READ_ONLY, + MAP_ONCE = IMemoryHeap::MAP_ONCE, + // memory won't be mapped locally, but will be mapped in the remote + // process. + DONT_MAP_LOCALLY = 0x00000100 + }; + + /* + * maps the memory referenced by fd. but DOESN'T take ownership + * of the filedescriptor (it makes a copy with dup() + */ + MemoryHeapBase(int fd, size_t size, uint32_t flags = 0); + + /* + * maps memory from the given device + */ + MemoryHeapBase(const char* device, size_t size = 0, uint32_t flags = 0); + + /* + * maps memory from ashmem, with the given name for debugging + */ + MemoryHeapBase(size_t size, uint32_t flags = 0, char const* name = NULL); + + virtual ~MemoryHeapBase(); + + /* implement IMemoryHeap interface */ + virtual int getHeapID() const; + virtual void* getBase() const; + virtual size_t getSize() const; + virtual uint32_t getFlags() const; + + const char* getDevice() const; + + /* this closes this heap -- use carefully */ + void dispose(); + + /* this is only needed as a workaround, use only if you know + * what you are doing */ + status_t setDevice(const char* device) { + if (mDevice == 0) + mDevice = device; + return mDevice ? NO_ERROR : ALREADY_EXISTS; + } + +protected: + MemoryHeapBase(); + // init() takes ownership of fd + status_t init(int fd, void *base, int size, + int flags = 0, const char* device = NULL); + +private: + status_t mapfd(int fd, size_t size); + + int mFD; + size_t mSize; + void* mBase; + uint32_t mFlags; + const char* mDevice; + bool mNeedUnmap; +}; + +// --------------------------------------------------------------------------- +}; // namespace android + +#endif // ANDROID_MEMORY_HEAP_BASE_H diff --git a/include/utils/MemoryHeapPmem.h b/include/utils/MemoryHeapPmem.h new file mode 100644 index 000000000..60335adae --- /dev/null +++ b/include/utils/MemoryHeapPmem.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_MEMORY_HEAP_PMEM_H +#define ANDROID_MEMORY_HEAP_PMEM_H + +#include +#include + +#include +#include +#include +#include + +namespace android { + +class MemoryHeapBase; + +// --------------------------------------------------------------------------- + +class MemoryHeapPmem : public HeapInterface, public MemoryHeapBase +{ +public: + class MemoryPmem : public BnMemory { + public: + MemoryPmem(const sp& heap); + ~MemoryPmem(); + protected: + const sp& getHeap() const { return mClientHeap; } + private: + friend class MemoryHeapPmem; + virtual void revoke() = 0; + sp mClientHeap; + }; + + MemoryHeapPmem(const sp& pmemHeap, + uint32_t flags = IMemoryHeap::MAP_ONCE); + ~MemoryHeapPmem(); + + /* HeapInterface additions */ + virtual sp mapMemory(size_t offset, size_t size); + + /* make the whole heap visible (you know who you are) */ + virtual status_t slap(); + + /* hide (revoke) the whole heap (the client will see the garbage page) */ + virtual status_t unslap(); + + /* revoke all allocations made by this heap */ + virtual void revoke(); + +private: + /* use this to create your own IMemory for mapMemory */ + virtual sp createMemory(size_t offset, size_t size); + void remove(const wp& memory); + +private: + sp mParentHeap; + mutable Mutex mLock; + SortedVector< wp > mAllocations; +}; + + +// --------------------------------------------------------------------------- +}; // namespace android + +#endif // ANDROID_MEMORY_HEAP_PMEM_H diff --git a/include/utils/Parcel.h b/include/utils/Parcel.h new file mode 100644 index 000000000..9087c4465 --- /dev/null +++ b/include/utils/Parcel.h @@ -0,0 +1,209 @@ +/* + * Copyright (C) 2005 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_PARCEL_H +#define ANDROID_PARCEL_H + +#include +#include +#include +#include +#include + +// --------------------------------------------------------------------------- +namespace android { + +class IBinder; +class ProcessState; +class String8; +class TextOutput; + +struct flat_binder_object; // defined in support_p/binder_module.h + +class Parcel +{ +public: + Parcel(); + ~Parcel(); + + const uint8_t* data() const; + size_t dataSize() const; + size_t dataAvail() const; + size_t dataPosition() const; + size_t dataCapacity() const; + + status_t setDataSize(size_t size); + void setDataPosition(size_t pos) const; + status_t setDataCapacity(size_t size); + + status_t setData(const uint8_t* buffer, size_t len); + + status_t appendFrom(Parcel *parcel, size_t start, size_t len); + + bool hasFileDescriptors() const; + + status_t writeInterfaceToken(const String16& interface); + bool enforceInterface(const String16& interface) const; + + void freeData(); + + const size_t* objects() const; + size_t objectsCount() const; + + status_t errorCheck() const; + void setError(status_t err); + + status_t write(const void* data, size_t len); + void* writeInplace(size_t len); + status_t writeUnpadded(const void* data, size_t len); + status_t writeInt32(int32_t val); + status_t writeInt64(int64_t val); + status_t writeFloat(float val); + status_t writeDouble(double val); + status_t writeCString(const char* str); + status_t writeString8(const String8& str); + status_t writeString16(const String16& str); + status_t writeString16(const char16_t* str, size_t len); + status_t writeStrongBinder(const sp& val); + status_t writeWeakBinder(const wp& val); + + // doesn't take ownership of the native_handle + status_t writeNativeHandle(const native_handle& handle); + + // Place a file descriptor into the parcel. The given fd must remain + // valid for the lifetime of the parcel. + status_t writeFileDescriptor(int fd); + + // Place a file descriptor into the parcel. A dup of the fd is made, which + // will be closed once the parcel is destroyed. + status_t writeDupFileDescriptor(int fd); + + status_t writeObject(const flat_binder_object& val, bool nullMetaData); + + void remove(size_t start, size_t amt); + + status_t read(void* outData, size_t len) const; + const void* readInplace(size_t len) const; + int32_t readInt32() const; + status_t readInt32(int32_t *pArg) const; + int64_t readInt64() const; + status_t readInt64(int64_t *pArg) const; + float readFloat() const; + status_t readFloat(float *pArg) const; + double readDouble() const; + status_t readDouble(double *pArg) const; + + const char* readCString() const; + String8 readString8() const; + String16 readString16() const; + const char16_t* readString16Inplace(size_t* outLen) const; + sp readStrongBinder() const; + wp readWeakBinder() const; + + + // if alloc is NULL, native_handle is allocated with malloc(), otherwise + // alloc is used. If the function fails, the effects of alloc() must be + // reverted by the caller. + native_handle* readNativeHandle( + native_handle* (*alloc)(void* cookie, int numFds, int ints), + void* cookie) const; + + + // Retrieve a file descriptor from the parcel. This returns the raw fd + // in the parcel, which you do not own -- use dup() to get your own copy. + int readFileDescriptor() const; + + const flat_binder_object* readObject(bool nullMetaData) const; + + // Explicitly close all file descriptors in the parcel. + void closeFileDescriptors(); + + typedef void (*release_func)(Parcel* parcel, + const uint8_t* data, size_t dataSize, + const size_t* objects, size_t objectsSize, + void* cookie); + + const uint8_t* ipcData() const; + size_t ipcDataSize() const; + const size_t* ipcObjects() const; + size_t ipcObjectsCount() const; + void ipcSetDataReference(const uint8_t* data, size_t dataSize, + const size_t* objects, size_t objectsCount, + release_func relFunc, void* relCookie); + + void print(TextOutput& to, uint32_t flags = 0) const; + +private: + Parcel(const Parcel& o); + Parcel& operator=(const Parcel& o); + + status_t finishWrite(size_t len); + void releaseObjects(); + void acquireObjects(); + status_t growData(size_t len); + status_t restartWrite(size_t desired); + status_t continueWrite(size_t desired); + void freeDataNoInit(); + void initState(); + void scanForFds() const; + + status_t mError; + uint8_t* mData; + size_t mDataSize; + size_t mDataCapacity; + mutable size_t mDataPos; + size_t* mObjects; + size_t mObjectsSize; + size_t mObjectsCapacity; + mutable size_t mNextObjectHint; + + mutable bool mFdsKnown; + mutable bool mHasFds; + + release_func mOwner; + void* mOwnerCookie; +}; + +// --------------------------------------------------------------------------- + +inline TextOutput& operator<<(TextOutput& to, const Parcel& parcel) +{ + parcel.print(to); + return to; +} + +// --------------------------------------------------------------------------- + +// Generic acquire and release of objects. +void acquire_object(const sp& proc, + const flat_binder_object& obj, const void* who); +void release_object(const sp& proc, + const flat_binder_object& obj, const void* who); + +void flatten_binder(const sp& proc, + const sp& binder, flat_binder_object* out); +void flatten_binder(const sp& proc, + const wp& binder, flat_binder_object* out); +status_t unflatten_binder(const sp& proc, + const flat_binder_object& flat, sp* out); +status_t unflatten_binder(const sp& proc, + const flat_binder_object& flat, wp* out); + +}; // namespace android + +// --------------------------------------------------------------------------- + +#endif // ANDROID_PARCEL_H diff --git a/include/utils/Pipe.h b/include/utils/Pipe.h new file mode 100644 index 000000000..6404168a2 --- /dev/null +++ b/include/utils/Pipe.h @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2005 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. + */ + +// +// FIFO I/O. +// +#ifndef _LIBS_UTILS_PIPE_H +#define _LIBS_UTILS_PIPE_H + +#ifdef HAVE_ANDROID_OS +#error DO NOT USE THIS FILE IN THE DEVICE BUILD +#endif + +namespace android { + +/* + * Simple anonymous unidirectional pipe. + * + * The primary goal is to create an implementation with minimal overhead + * under Linux. Making Windows, Mac OS X, and Linux all work the same way + * is a secondary goal. Part of this goal is to have something that can + * be fed to a select() call, so that the application can sleep in the + * kernel until something interesting happens. + */ +class Pipe { +public: + Pipe(void); + virtual ~Pipe(void); + + /* Create the pipe */ + bool create(void); + + /* Create a read-only pipe, using the supplied handle as read handle */ + bool createReader(unsigned long handle); + /* Create a write-only pipe, using the supplied handle as write handle */ + bool createWriter(unsigned long handle); + + /* Is this object ready to go? */ + bool isCreated(void); + + /* + * Read "count" bytes from the pipe. Returns the amount of data read, + * or 0 if no data available and we're non-blocking. + * Returns -1 on error. + */ + int read(void* buf, int count); + + /* + * Write "count" bytes into the pipe. Returns number of bytes written, + * or 0 if there's no room for more data and we're non-blocking. + * Returns -1 on error. + */ + int write(const void* buf, int count); + + /* Returns "true" if data is available to read */ + bool readReady(void); + + /* Enable or disable non-blocking I/O for reads */ + bool setReadNonBlocking(bool val); + /* Enable or disable non-blocking I/O for writes. Only works on Linux. */ + bool setWriteNonBlocking(bool val); + + /* + * Get the handle. Only useful in some platform-specific situations. + */ + unsigned long getReadHandle(void); + unsigned long getWriteHandle(void); + + /* + * Modify inheritance, i.e. whether or not a child process will get + * copies of the descriptors. Systems with fork+exec allow us to close + * the descriptors before launching the child process, but Win32 + * doesn't allow it. + */ + bool disallowReadInherit(void); + bool disallowWriteInherit(void); + + /* + * Close one side or the other. Useful in the parent after launching + * a child process. + */ + bool closeRead(void); + bool closeWrite(void); + +private: + bool mReadNonBlocking; + bool mWriteNonBlocking; + + unsigned long mReadHandle; + unsigned long mWriteHandle; +}; + +}; // android + +#endif // _LIBS_UTILS_PIPE_H diff --git a/include/utils/ProcessState.h b/include/utils/ProcessState.h new file mode 100644 index 000000000..39584f42c --- /dev/null +++ b/include/utils/ProcessState.h @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2005 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_PROCESS_STATE_H +#define ANDROID_PROCESS_STATE_H + +#include +#include +#include +#include + +#include + +// --------------------------------------------------------------------------- +namespace android { + +// Global variables +extern int mArgC; +extern const char* const* mArgV; +extern int mArgLen; + +class IPCThreadState; + +class ProcessState : public virtual RefBase +{ +public: + static sp self(); + + static void setSingleProcess(bool singleProcess); + + void setContextObject(const sp& object); + sp getContextObject(const sp& caller); + + void setContextObject(const sp& object, + const String16& name); + sp getContextObject(const String16& name, + const sp& caller); + + bool supportsProcesses() const; + + void startThreadPool(); + + typedef bool (*context_check_func)(const String16& name, + const sp& caller, + void* userData); + + bool isContextManager(void) const; + bool becomeContextManager( + context_check_func checkFunc, + void* userData); + + sp getStrongProxyForHandle(int32_t handle); + wp getWeakProxyForHandle(int32_t handle); + void expungeHandle(int32_t handle, IBinder* binder); + + void setArgs(int argc, const char* const argv[]); + int getArgC() const; + const char* const* getArgV() const; + + void setArgV0(const char* txt); + + void spawnPooledThread(bool isMain); + +private: + friend class IPCThreadState; + + ProcessState(); + ~ProcessState(); + + ProcessState(const ProcessState& o); + ProcessState& operator=(const ProcessState& o); + + struct handle_entry { + IBinder* binder; + RefBase::weakref_type* refs; + }; + + handle_entry* lookupHandleLocked(int32_t handle); + + int mDriverFD; + void* mVMStart; + + mutable Mutex mLock; // protects everything below. + + VectormHandleToObject; + + bool mManagesContexts; + context_check_func mBinderContextCheckFunc; + void* mBinderContextUserData; + + KeyedVector > + mContexts; + + + String8 mRootDir; + bool mThreadPoolStarted; + volatile int32_t mThreadPoolSeq; +}; + +}; // namespace android + +// --------------------------------------------------------------------------- + +#endif // ANDROID_PROCESS_STATE_H diff --git a/include/utils/RefBase.h b/include/utils/RefBase.h new file mode 100644 index 000000000..cbda0fd80 --- /dev/null +++ b/include/utils/RefBase.h @@ -0,0 +1,550 @@ +/* + * Copyright (C) 2005 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_REF_BASE_H +#define ANDROID_REF_BASE_H + +#include +#include + +#include +#include +#include + +// --------------------------------------------------------------------------- +namespace android { + +template class wp; + +// --------------------------------------------------------------------------- + +#define COMPARE(_op_) \ +inline bool operator _op_ (const sp& o) const { \ + return m_ptr _op_ o.m_ptr; \ +} \ +inline bool operator _op_ (const wp& o) const { \ + return m_ptr _op_ o.m_ptr; \ +} \ +inline bool operator _op_ (const T* o) const { \ + return m_ptr _op_ o; \ +} \ +template \ +inline bool operator _op_ (const sp& o) const { \ + return m_ptr _op_ o.m_ptr; \ +} \ +template \ +inline bool operator _op_ (const wp& o) const { \ + return m_ptr _op_ o.m_ptr; \ +} \ +template \ +inline bool operator _op_ (const U* o) const { \ + return m_ptr _op_ o; \ +} + +// --------------------------------------------------------------------------- + +class RefBase +{ +public: + void incStrong(const void* id) const; + void decStrong(const void* id) const; + + void forceIncStrong(const void* id) const; + + //! DEBUGGING ONLY: Get current strong ref count. + int32_t getStrongCount() const; + + class weakref_type + { + public: + RefBase* refBase() const; + + void incWeak(const void* id); + void decWeak(const void* id); + + bool attemptIncStrong(const void* id); + + //! This is only safe if you have set OBJECT_LIFETIME_FOREVER. + bool attemptIncWeak(const void* id); + + //! DEBUGGING ONLY: Get current weak ref count. + int32_t getWeakCount() const; + + //! DEBUGGING ONLY: Print references held on object. + void printRefs() const; + + //! DEBUGGING ONLY: Enable tracking for this object. + // enable -- enable/disable tracking + // retain -- when tracking is enable, if true, then we save a stack trace + // for each reference and dereference; when retain == false, we + // match up references and dereferences and keep only the + // outstanding ones. + + void trackMe(bool enable, bool retain); + }; + + weakref_type* createWeak(const void* id) const; + + weakref_type* getWeakRefs() const; + + //! DEBUGGING ONLY: Print references held on object. + inline void printRefs() const { getWeakRefs()->printRefs(); } + + //! DEBUGGING ONLY: Enable tracking of object. + inline void trackMe(bool enable, bool retain) + { + getWeakRefs()->trackMe(enable, retain); + } + +protected: + RefBase(); + virtual ~RefBase(); + + //! Flags for extendObjectLifetime() + enum { + OBJECT_LIFETIME_WEAK = 0x0001, + OBJECT_LIFETIME_FOREVER = 0x0003 + }; + + void extendObjectLifetime(int32_t mode); + + //! Flags for onIncStrongAttempted() + enum { + FIRST_INC_STRONG = 0x0001 + }; + + virtual void onFirstRef(); + virtual void onLastStrongRef(const void* id); + virtual bool onIncStrongAttempted(uint32_t flags, const void* id); + virtual void onLastWeakRef(const void* id); + +private: + friend class weakref_type; + class weakref_impl; + + RefBase(const RefBase& o); + RefBase& operator=(const RefBase& o); + + weakref_impl* const mRefs; +}; + +// --------------------------------------------------------------------------- + +template +class LightRefBase +{ +public: + inline LightRefBase() : mCount(0) { } + inline void incStrong(const void* id) const { + android_atomic_inc(&mCount); + } + inline void decStrong(const void* id) const { + if (android_atomic_dec(&mCount) == 1) { + delete static_cast(this); + } + } + +protected: + inline ~LightRefBase() { } + +private: + mutable volatile int32_t mCount; +}; + +// --------------------------------------------------------------------------- + +template +class sp +{ +public: + typedef typename RefBase::weakref_type weakref_type; + + inline sp() : m_ptr(0) { } + + sp(T* other); + sp(const sp& other); + template sp(U* other); + template sp(const sp& other); + + ~sp(); + + // Assignment + + sp& operator = (T* other); + sp& operator = (const sp& other); + + template sp& operator = (const sp& other); + template sp& operator = (U* other); + + //! Special optimization for use by ProcessState (and nobody else). + void force_set(T* other); + + // Reset + + void clear(); + + // Accessors + + inline T& operator* () const { return *m_ptr; } + inline T* operator-> () const { return m_ptr; } + inline T* get() const { return m_ptr; } + + // Operators + + COMPARE(==) + COMPARE(!=) + COMPARE(>) + COMPARE(<) + COMPARE(<=) + COMPARE(>=) + +private: + template friend class sp; + template friend class wp; + + // Optimization for wp::promote(). + sp(T* p, weakref_type* refs); + + T* m_ptr; +}; + +template +TextOutput& operator<<(TextOutput& to, const sp& val); + +// --------------------------------------------------------------------------- + +template +class wp +{ +public: + typedef typename RefBase::weakref_type weakref_type; + + inline wp() : m_ptr(0) { } + + wp(T* other); + wp(const wp& other); + wp(const sp& other); + template wp(U* other); + template wp(const sp& other); + template wp(const wp& other); + + ~wp(); + + // Assignment + + wp& operator = (T* other); + wp& operator = (const wp& other); + wp& operator = (const sp& other); + + template wp& operator = (U* other); + template wp& operator = (const wp& other); + template wp& operator = (const sp& other); + + void set_object_and_refs(T* other, weakref_type* refs); + + // promotion to sp + + sp promote() const; + + // Reset + + void clear(); + + // Accessors + + inline weakref_type* get_refs() const { return m_refs; } + + inline T* unsafe_get() const { return m_ptr; } + + // Operators + + COMPARE(==) + COMPARE(!=) + COMPARE(>) + COMPARE(<) + COMPARE(<=) + COMPARE(>=) + +private: + template friend class sp; + template friend class wp; + + T* m_ptr; + weakref_type* m_refs; +}; + +template +TextOutput& operator<<(TextOutput& to, const wp& val); + +#undef COMPARE + +// --------------------------------------------------------------------------- +// No user serviceable parts below here. + +template +sp::sp(T* other) + : m_ptr(other) +{ + if (other) other->incStrong(this); +} + +template +sp::sp(const sp& other) + : m_ptr(other.m_ptr) +{ + if (m_ptr) m_ptr->incStrong(this); +} + +template template +sp::sp(U* other) : m_ptr(other) +{ + if (other) other->incStrong(this); +} + +template template +sp::sp(const sp& other) + : m_ptr(other.m_ptr) +{ + if (m_ptr) m_ptr->incStrong(this); +} + +template +sp::~sp() +{ + if (m_ptr) m_ptr->decStrong(this); +} + +template +sp& sp::operator = (const sp& other) { + if (other.m_ptr) other.m_ptr->incStrong(this); + if (m_ptr) m_ptr->decStrong(this); + m_ptr = other.m_ptr; + return *this; +} + +template +sp& sp::operator = (T* other) +{ + if (other) other->incStrong(this); + if (m_ptr) m_ptr->decStrong(this); + m_ptr = other; + return *this; +} + +template template +sp& sp::operator = (const sp& other) +{ + if (other.m_ptr) other.m_ptr->incStrong(this); + if (m_ptr) m_ptr->decStrong(this); + m_ptr = other.m_ptr; + return *this; +} + +template template +sp& sp::operator = (U* other) +{ + if (other) other->incStrong(this); + if (m_ptr) m_ptr->decStrong(this); + m_ptr = other; + return *this; +} + +template +void sp::force_set(T* other) +{ + other->forceIncStrong(this); + m_ptr = other; +} + +template +void sp::clear() +{ + if (m_ptr) { + m_ptr->decStrong(this); + m_ptr = 0; + } +} + +template +sp::sp(T* p, weakref_type* refs) + : m_ptr((p && refs->attemptIncStrong(this)) ? p : 0) +{ +} + +template +inline TextOutput& operator<<(TextOutput& to, const sp& val) +{ + to << "sp<>(" << val.get() << ")"; + return to; +} + +// --------------------------------------------------------------------------- + +template +wp::wp(T* other) + : m_ptr(other) +{ + if (other) m_refs = other->createWeak(this); +} + +template +wp::wp(const wp& other) + : m_ptr(other.m_ptr), m_refs(other.m_refs) +{ + if (m_ptr) m_refs->incWeak(this); +} + +template +wp::wp(const sp& other) + : m_ptr(other.m_ptr) +{ + if (m_ptr) { + m_refs = m_ptr->createWeak(this); + } +} + +template template +wp::wp(U* other) + : m_ptr(other) +{ + if (other) m_refs = other->createWeak(this); +} + +template template +wp::wp(const wp& other) + : m_ptr(other.m_ptr) +{ + if (m_ptr) { + m_refs = other.m_refs; + m_refs->incWeak(this); + } +} + +template template +wp::wp(const sp& other) + : m_ptr(other.m_ptr) +{ + if (m_ptr) { + m_refs = m_ptr->createWeak(this); + } +} + +template +wp::~wp() +{ + if (m_ptr) m_refs->decWeak(this); +} + +template +wp& wp::operator = (T* other) +{ + weakref_type* newRefs = + other ? other->createWeak(this) : 0; + if (m_ptr) m_refs->decWeak(this); + m_ptr = other; + m_refs = newRefs; + return *this; +} + +template +wp& wp::operator = (const wp& other) +{ + if (other.m_ptr) other.m_refs->incWeak(this); + if (m_ptr) m_refs->decWeak(this); + m_ptr = other.m_ptr; + m_refs = other.m_refs; + return *this; +} + +template +wp& wp::operator = (const sp& other) +{ + weakref_type* newRefs = + other != NULL ? other->createWeak(this) : 0; + if (m_ptr) m_refs->decWeak(this); + m_ptr = other.get(); + m_refs = newRefs; + return *this; +} + +template template +wp& wp::operator = (U* other) +{ + weakref_type* newRefs = + other ? other->createWeak(this) : 0; + if (m_ptr) m_refs->decWeak(this); + m_ptr = other; + m_refs = newRefs; + return *this; +} + +template template +wp& wp::operator = (const wp& other) +{ + if (other.m_ptr) other.m_refs->incWeak(this); + if (m_ptr) m_refs->decWeak(this); + m_ptr = other.m_ptr; + m_refs = other.m_refs; + return *this; +} + +template template +wp& wp::operator = (const sp& other) +{ + weakref_type* newRefs = + other != NULL ? other->createWeak(this) : 0; + if (m_ptr) m_refs->decWeak(this); + m_ptr = other.get(); + m_refs = newRefs; + return *this; +} + +template +void wp::set_object_and_refs(T* other, weakref_type* refs) +{ + if (other) refs->incWeak(this); + if (m_ptr) m_refs->decWeak(this); + m_ptr = other; + m_refs = refs; +} + +template +sp wp::promote() const +{ + return sp(m_ptr, m_refs); +} + +template +void wp::clear() +{ + if (m_ptr) { + m_refs->decWeak(this); + m_ptr = 0; + } +} + +template +inline TextOutput& operator<<(TextOutput& to, const wp& val) +{ + to << "wp<>(" << val.unsafe_get() << ")"; + return to; +} + +}; // namespace android + +// --------------------------------------------------------------------------- + +#endif // ANDROID_REF_BASE_H diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h new file mode 100644 index 000000000..7d3fcf2a8 --- /dev/null +++ b/include/utils/ResourceTypes.h @@ -0,0 +1,1720 @@ +/* + * Copyright (C) 2005 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. + */ + +// +// Definitions of resource data structures. +// +#ifndef _LIBS_UTILS_RESOURCE_TYPES_H +#define _LIBS_UTILS_RESOURCE_TYPES_H + +#include +#include +#include +#include +#include + +#include + +#include +#include + +namespace android { + +/** ******************************************************************** + * PNG Extensions + * + * New private chunks that may be placed in PNG images. + * + *********************************************************************** */ + +/** + * This chunk specifies how to split an image into segments for + * scaling. + * + * There are J horizontal and K vertical segments. These segments divide + * the image into J*K regions as follows (where J=4 and K=3): + * + * F0 S0 F1 S1 + * +-----+----+------+-------+ + * S2| 0 | 1 | 2 | 3 | + * +-----+----+------+-------+ + * | | | | | + * | | | | | + * F2| 4 | 5 | 6 | 7 | + * | | | | | + * | | | | | + * +-----+----+------+-------+ + * S3| 8 | 9 | 10 | 11 | + * +-----+----+------+-------+ + * + * Each horizontal and vertical segment is considered to by either + * stretchable (marked by the Sx labels) or fixed (marked by the Fy + * labels), in the horizontal or vertical axis, respectively. In the + * above example, the first is horizontal segment (F0) is fixed, the + * next is stretchable and then they continue to alternate. Note that + * the segment list for each axis can begin or end with a stretchable + * or fixed segment. + * + * The relative sizes of the stretchy segments indicates the relative + * amount of stretchiness of the regions bordered by the segments. For + * example, regions 3, 7 and 11 above will take up more horizontal space + * than regions 1, 5 and 9 since the horizonal segment associated with + * the first set of regions is larger than the other set of regions. The + * ratios of the amount of horizontal (or vertical) space taken by any + * two stretchable slices is exactly the ratio of their corresponding + * segment lengths. + * + * xDivs and yDivs point to arrays of horizontal and vertical pixel + * indices. The first pair of Divs (in either array) indicate the + * starting and ending points of the first stretchable segment in that + * axis. The next pair specifies the next stretchable segment, etc. So + * in the above example xDiv[0] and xDiv[1] specify the horizontal + * coordinates for the regions labeled 1, 5 and 9. xDiv[2] and + * xDiv[3] specify the coordinates for regions 3, 7 and 11. Note that + * the leftmost slices always start at x=0 and the rightmost slices + * always end at the end of the image. So, for example, the regions 0, + * 4 and 8 (which are fixed along the X axis) start at x value 0 and + * go to xDiv[0] amd slices 2, 6 and 10 start at xDiv[1] and end at + * xDiv[2]. + * + * The array pointed to by the colors field lists contains hints for + * each of the regions. They are ordered according left-to-right and + * top-to-bottom as indicated above. For each segment that is a solid + * color the array entry will contain that color value; otherwise it + * will contain NO_COLOR. Segments that are completely transparent + * will always have the value TRANSPARENT_COLOR. + * + * The PNG chunk type is "npTc". + */ +struct Res_png_9patch +{ + Res_png_9patch() : wasDeserialized(false), xDivs(NULL), + yDivs(NULL), colors(NULL) { } + + int8_t wasDeserialized; + int8_t numXDivs; + int8_t numYDivs; + int8_t numColors; + + // These tell where the next section of a patch starts. + // For example, the first patch includes the pixels from + // 0 to xDivs[0]-1 and the second patch includes the pixels + // from xDivs[0] to xDivs[1]-1. + // Note: allocation/free of these pointers is left to the caller. + int32_t* xDivs; + int32_t* yDivs; + + int32_t paddingLeft, paddingRight; + int32_t paddingTop, paddingBottom; + + enum { + // The 9 patch segment is not a solid color. + NO_COLOR = 0x00000001, + + // The 9 patch segment is completely transparent. + TRANSPARENT_COLOR = 0x00000000 + }; + // Note: allocation/free of this pointer is left to the caller. + uint32_t* colors; + + // Convert data from device representation to PNG file representation. + void deviceToFile(); + // Convert data from PNG file representation to device representation. + void fileToDevice(); + // Serialize/Marshall the patch data into a newly malloc-ed block + void* serialize(); + // Serialize/Marshall the patch data + void serialize(void* outData); + // Deserialize/Unmarshall the patch data + static Res_png_9patch* deserialize(const void* data); + // Compute the size of the serialized data structure + size_t serializedSize(); +}; + +/** ******************************************************************** + * Base Types + * + * These are standard types that are shared between multiple specific + * resource types. + * + *********************************************************************** */ + +/** + * Header that appears at the front of every data chunk in a resource. + */ +struct ResChunk_header +{ + // Type identifier for this chunk. The meaning of this value depends + // on the containing chunk. + uint16_t type; + + // Size of the chunk header (in bytes). Adding this value to + // the address of the chunk allows you to find its associated data + // (if any). + uint16_t headerSize; + + // Total size of this chunk (in bytes). This is the chunkSize plus + // the size of any data associated with the chunk. Adding this value + // to the chunk allows you to completely skip its contents (including + // any child chunks). If this value is the same as chunkSize, there is + // no data associated with the chunk. + uint32_t size; +}; + +enum { + RES_NULL_TYPE = 0x0000, + RES_STRING_POOL_TYPE = 0x0001, + RES_TABLE_TYPE = 0x0002, + RES_XML_TYPE = 0x0003, + + // Chunk types in RES_XML_TYPE + RES_XML_FIRST_CHUNK_TYPE = 0x0100, + RES_XML_START_NAMESPACE_TYPE= 0x0100, + RES_XML_END_NAMESPACE_TYPE = 0x0101, + RES_XML_START_ELEMENT_TYPE = 0x0102, + RES_XML_END_ELEMENT_TYPE = 0x0103, + RES_XML_CDATA_TYPE = 0x0104, + RES_XML_LAST_CHUNK_TYPE = 0x017f, + // This contains a uint32_t array mapping strings in the string + // pool back to resource identifiers. It is optional. + RES_XML_RESOURCE_MAP_TYPE = 0x0180, + + // Chunk types in RES_TABLE_TYPE + RES_TABLE_PACKAGE_TYPE = 0x0200, + RES_TABLE_TYPE_TYPE = 0x0201, + RES_TABLE_TYPE_SPEC_TYPE = 0x0202 +}; + +/** + * Macros for building/splitting resource identifiers. + */ +#define Res_VALIDID(resid) (resid != 0) +#define Res_CHECKID(resid) ((resid&0xFFFF0000) != 0) +#define Res_MAKEID(package, type, entry) \ + (((package+1)<<24) | (((type+1)&0xFF)<<16) | (entry&0xFFFF)) +#define Res_GETPACKAGE(id) ((id>>24)-1) +#define Res_GETTYPE(id) (((id>>16)&0xFF)-1) +#define Res_GETENTRY(id) (id&0xFFFF) + +#define Res_INTERNALID(resid) ((resid&0xFFFF0000) != 0 && (resid&0xFF0000) == 0) +#define Res_MAKEINTERNAL(entry) (0x01000000 | (entry&0xFFFF)) +#define Res_MAKEARRAY(entry) (0x02000000 | (entry&0xFFFF)) + +#define Res_MAXPACKAGE 255 + +/** + * Representation of a value in a resource, supplying type + * information. + */ +struct Res_value +{ + // Number of bytes in this structure. + uint16_t size; + + // Always set to 0. + uint8_t res0; + + // Type of the data value. + enum { + // Contains no data. + TYPE_NULL = 0x00, + // The 'data' holds a ResTable_ref, a reference to another resource + // table entry. + TYPE_REFERENCE = 0x01, + // The 'data' holds an attribute resource identifier. + TYPE_ATTRIBUTE = 0x02, + // The 'data' holds an index into the containing resource table's + // global value string pool. + TYPE_STRING = 0x03, + // The 'data' holds a single-precision floating point number. + TYPE_FLOAT = 0x04, + // The 'data' holds a complex number encoding a dimension value, + // such as "100in". + TYPE_DIMENSION = 0x05, + // The 'data' holds a complex number encoding a fraction of a + // container. + TYPE_FRACTION = 0x06, + + // Beginning of integer flavors... + TYPE_FIRST_INT = 0x10, + + // The 'data' is a raw integer value of the form n..n. + TYPE_INT_DEC = 0x10, + // The 'data' is a raw integer value of the form 0xn..n. + TYPE_INT_HEX = 0x11, + // The 'data' is either 0 or 1, for input "false" or "true" respectively. + TYPE_INT_BOOLEAN = 0x12, + + // Beginning of color integer flavors... + TYPE_FIRST_COLOR_INT = 0x1c, + + // The 'data' is a raw integer value of the form #aarrggbb. + TYPE_INT_COLOR_ARGB8 = 0x1c, + // The 'data' is a raw integer value of the form #rrggbb. + TYPE_INT_COLOR_RGB8 = 0x1d, + // The 'data' is a raw integer value of the form #argb. + TYPE_INT_COLOR_ARGB4 = 0x1e, + // The 'data' is a raw integer value of the form #rgb. + TYPE_INT_COLOR_RGB4 = 0x1f, + + // ...end of integer flavors. + TYPE_LAST_COLOR_INT = 0x1f, + + // ...end of integer flavors. + TYPE_LAST_INT = 0x1f + }; + uint8_t dataType; + + // Structure of complex data values (TYPE_UNIT and TYPE_FRACTION) + enum { + // Where the unit type information is. This gives us 16 possible + // types, as defined below. + COMPLEX_UNIT_SHIFT = 0, + COMPLEX_UNIT_MASK = 0xf, + + // TYPE_DIMENSION: Value is raw pixels. + COMPLEX_UNIT_PX = 0, + // TYPE_DIMENSION: Value is Device Independent Pixels. + COMPLEX_UNIT_DIP = 1, + // TYPE_DIMENSION: Value is a Scaled device independent Pixels. + COMPLEX_UNIT_SP = 2, + // TYPE_DIMENSION: Value is in points. + COMPLEX_UNIT_PT = 3, + // TYPE_DIMENSION: Value is in inches. + COMPLEX_UNIT_IN = 4, + // TYPE_DIMENSION: Value is in millimeters. + COMPLEX_UNIT_MM = 5, + + // TYPE_FRACTION: A basic fraction of the overall size. + COMPLEX_UNIT_FRACTION = 0, + // TYPE_FRACTION: A fraction of the parent size. + COMPLEX_UNIT_FRACTION_PARENT = 1, + + // Where the radix information is, telling where the decimal place + // appears in the mantissa. This give us 4 possible fixed point + // representations as defined below. + COMPLEX_RADIX_SHIFT = 4, + COMPLEX_RADIX_MASK = 0x3, + + // The mantissa is an integral number -- i.e., 0xnnnnnn.0 + COMPLEX_RADIX_23p0 = 0, + // The mantissa magnitude is 16 bits -- i.e, 0xnnnn.nn + COMPLEX_RADIX_16p7 = 1, + // The mantissa magnitude is 8 bits -- i.e, 0xnn.nnnn + COMPLEX_RADIX_8p15 = 2, + // The mantissa magnitude is 0 bits -- i.e, 0x0.nnnnnn + COMPLEX_RADIX_0p23 = 3, + + // Where the actual value is. This gives us 23 bits of + // precision. The top bit is the sign. + COMPLEX_MANTISSA_SHIFT = 8, + COMPLEX_MANTISSA_MASK = 0xffffff + }; + + // The data for this item, as interpreted according to dataType. + uint32_t data; + + void copyFrom_dtoh(const Res_value& src); +}; + +/** + * This is a reference to a unique entry (a ResTable_entry structure) + * in a resource table. The value is structured as: 0xpptteeee, + * where pp is the package index, tt is the type index in that + * package, and eeee is the entry index in that type. The package + * and type values start at 1 for the first item, to help catch cases + * where they have not been supplied. + */ +struct ResTable_ref +{ + uint32_t ident; +}; + +/** + * Reference to a string in a string pool. + */ +struct ResStringPool_ref +{ + // Index into the string pool table (uint32_t-offset from the indices + // immediately after ResStringPool_header) at which to find the location + // of the string data in the pool. + uint32_t index; +}; + +/** ******************************************************************** + * String Pool + * + * A set of strings that can be references by others through a + * ResStringPool_ref. + * + *********************************************************************** */ + +/** + * Definition for a pool of strings. The data of this chunk is an + * array of uint32_t providing indices into the pool, relative to + * stringsStart. At stringsStart are all of the UTF-16 strings + * concatenated together; each starts with a uint16_t of the string's + * length and each ends with a 0x0000 terminator. If a string is > + * 32767 characters, the high bit of the length is set meaning to take + * those 15 bits as a high word and it will be followed by another + * uint16_t containing the low word. + * + * If styleCount is not zero, then immediately following the array of + * uint32_t indices into the string table is another array of indices + * into a style table starting at stylesStart. Each entry in the + * style table is an array of ResStringPool_span structures. + */ +struct ResStringPool_header +{ + struct ResChunk_header header; + + // Number of strings in this pool (number of uint32_t indices that follow + // in the data). + uint32_t stringCount; + + // Number of style span arrays in the pool (number of uint32_t indices + // follow the string indices). + uint32_t styleCount; + + // Flags. + enum { + // If set, the string index is sorted by the string values (based + // on strcmp16()). + SORTED_FLAG = 1<<0 + }; + uint32_t flags; + + // Index from header of the string data. + uint32_t stringsStart; + + // Index from header of the style data. + uint32_t stylesStart; +}; + +/** + * This structure defines a span of style information associated with + * a string in the pool. + */ +struct ResStringPool_span +{ + enum { + END = 0xFFFFFFFF + }; + + // This is the name of the span -- that is, the name of the XML + // tag that defined it. The special value END (0xFFFFFFFF) indicates + // the end of an array of spans. + ResStringPool_ref name; + + // The range of characters in the string that this span applies to. + uint32_t firstChar, lastChar; +}; + +/** + * Convenience class for accessing data in a ResStringPool resource. + */ +class ResStringPool +{ +public: + ResStringPool(); + ResStringPool(const void* data, size_t size, bool copyData=false); + ~ResStringPool(); + + status_t setTo(const void* data, size_t size, bool copyData=false); + + status_t getError() const; + + void uninit(); + + inline const char16_t* stringAt(const ResStringPool_ref& ref, size_t* outLen) const { + return stringAt(ref.index, outLen); + } + const char16_t* stringAt(size_t idx, size_t* outLen) const; + + const ResStringPool_span* styleAt(const ResStringPool_ref& ref) const; + const ResStringPool_span* styleAt(size_t idx) const; + + ssize_t indexOfString(const char16_t* str, size_t strLen) const; + + size_t size() const; + +private: + status_t mError; + void* mOwnedData; + const ResStringPool_header* mHeader; + size_t mSize; + const uint32_t* mEntries; + const uint32_t* mEntryStyles; + const char16_t* mStrings; + uint32_t mStringPoolSize; // number of uint16_t + const uint32_t* mStyles; + uint32_t mStylePoolSize; // number of uint32_t +}; + +/** ******************************************************************** + * XML Tree + * + * Binary representation of an XML document. This is designed to + * express everything in an XML document, in a form that is much + * easier to parse on the device. + * + *********************************************************************** */ + +/** + * XML tree header. This appears at the front of an XML tree, + * describing its content. It is followed by a flat array of + * ResXMLTree_node structures; the hierarchy of the XML document + * is described by the occurrance of RES_XML_START_ELEMENT_TYPE + * and corresponding RES_XML_END_ELEMENT_TYPE nodes in the array. + */ +struct ResXMLTree_header +{ + struct ResChunk_header header; +}; + +/** + * Basic XML tree node. A single item in the XML document. Extended info + * about the node can be found after header.headerSize. + */ +struct ResXMLTree_node +{ + struct ResChunk_header header; + + // Line number in original source file at which this element appeared. + uint32_t lineNumber; + + // Optional XML comment that was associated with this element; -1 if none. + struct ResStringPool_ref comment; +}; + +/** + * Extended XML tree node for CDATA tags -- includes the CDATA string. + * Appears header.headerSize bytes after a ResXMLTree_node. + */ +struct ResXMLTree_cdataExt +{ + // The raw CDATA character data. + struct ResStringPool_ref data; + + // The typed value of the character data if this is a CDATA node. + struct Res_value typedData; +}; + +/** + * Extended XML tree node for namespace start/end nodes. + * Appears header.headerSize bytes after a ResXMLTree_node. + */ +struct ResXMLTree_namespaceExt +{ + // The prefix of the namespace. + struct ResStringPool_ref prefix; + + // The URI of the namespace. + struct ResStringPool_ref uri; +}; + +/** + * Extended XML tree node for element start/end nodes. + * Appears header.headerSize bytes after a ResXMLTree_node. + */ +struct ResXMLTree_endElementExt +{ + // String of the full namespace of this element. + struct ResStringPool_ref ns; + + // String name of this node if it is an ELEMENT; the raw + // character data if this is a CDATA node. + struct ResStringPool_ref name; +}; + +/** + * Extended XML tree node for start tags -- includes attribute + * information. + * Appears header.headerSize bytes after a ResXMLTree_node. + */ +struct ResXMLTree_attrExt +{ + // String of the full namespace of this element. + struct ResStringPool_ref ns; + + // String name of this node if it is an ELEMENT; the raw + // character data if this is a CDATA node. + struct ResStringPool_ref name; + + // Byte offset from the start of this structure where the attributes start. + uint16_t attributeStart; + + // Size of the ResXMLTree_attribute structures that follow. + uint16_t attributeSize; + + // Number of attributes associated with an ELEMENT. These are + // available as an array of ResXMLTree_attribute structures + // immediately following this node. + uint16_t attributeCount; + + // Index (1-based) of the "id" attribute. 0 if none. + uint16_t idIndex; + + // Index (1-based) of the "class" attribute. 0 if none. + uint16_t classIndex; + + // Index (1-based) of the "style" attribute. 0 if none. + uint16_t styleIndex; +}; + +struct ResXMLTree_attribute +{ + // Namespace of this attribute. + struct ResStringPool_ref ns; + + // Name of this attribute. + struct ResStringPool_ref name; + + // The original raw string value of this attribute. + struct ResStringPool_ref rawValue; + + // Processesd typed value of this attribute. + struct Res_value typedValue; +}; + +class ResXMLTree; + +class ResXMLParser +{ +public: + ResXMLParser(const ResXMLTree& tree); + + enum event_code_t { + BAD_DOCUMENT = -1, + START_DOCUMENT = 0, + END_DOCUMENT = 1, + + FIRST_CHUNK_CODE = RES_XML_FIRST_CHUNK_TYPE, + + START_NAMESPACE = RES_XML_START_NAMESPACE_TYPE, + END_NAMESPACE = RES_XML_END_NAMESPACE_TYPE, + START_TAG = RES_XML_START_ELEMENT_TYPE, + END_TAG = RES_XML_END_ELEMENT_TYPE, + TEXT = RES_XML_CDATA_TYPE + }; + + struct ResXMLPosition + { + event_code_t eventCode; + const ResXMLTree_node* curNode; + const void* curExt; + }; + + void restart(); + + event_code_t getEventType() const; + // Note, unlike XmlPullParser, the first call to next() will return + // START_TAG of the first element. + event_code_t next(); + + // These are available for all nodes: + const int32_t getCommentID() const; + const uint16_t* getComment(size_t* outLen) const; + const uint32_t getLineNumber() const; + + // This is available for TEXT: + const int32_t getTextID() const; + const uint16_t* getText(size_t* outLen) const; + ssize_t getTextValue(Res_value* outValue) const; + + // These are available for START_NAMESPACE and END_NAMESPACE: + const int32_t getNamespacePrefixID() const; + const uint16_t* getNamespacePrefix(size_t* outLen) const; + const int32_t getNamespaceUriID() const; + const uint16_t* getNamespaceUri(size_t* outLen) const; + + // These are available for START_TAG and END_TAG: + const int32_t getElementNamespaceID() const; + const uint16_t* getElementNamespace(size_t* outLen) const; + const int32_t getElementNameID() const; + const uint16_t* getElementName(size_t* outLen) const; + + // Remaining methods are for retrieving information about attributes + // associated with a START_TAG: + + size_t getAttributeCount() const; + + // Returns -1 if no namespace, -2 if idx out of range. + const int32_t getAttributeNamespaceID(size_t idx) const; + const uint16_t* getAttributeNamespace(size_t idx, size_t* outLen) const; + + const int32_t getAttributeNameID(size_t idx) const; + const uint16_t* getAttributeName(size_t idx, size_t* outLen) const; + const uint32_t getAttributeNameResID(size_t idx) const; + + const int32_t getAttributeValueStringID(size_t idx) const; + const uint16_t* getAttributeStringValue(size_t idx, size_t* outLen) const; + + int32_t getAttributeDataType(size_t idx) const; + int32_t getAttributeData(size_t idx) const; + ssize_t getAttributeValue(size_t idx, Res_value* outValue) const; + + ssize_t indexOfAttribute(const char* ns, const char* attr) const; + ssize_t indexOfAttribute(const char16_t* ns, size_t nsLen, + const char16_t* attr, size_t attrLen) const; + + ssize_t indexOfID() const; + ssize_t indexOfClass() const; + ssize_t indexOfStyle() const; + + void getPosition(ResXMLPosition* pos) const; + void setPosition(const ResXMLPosition& pos); + +private: + friend class ResXMLTree; + + event_code_t nextNode(); + + const ResXMLTree& mTree; + event_code_t mEventCode; + const ResXMLTree_node* mCurNode; + const void* mCurExt; +}; + +/** + * Convenience class for accessing data in a ResXMLTree resource. + */ +class ResXMLTree : public ResXMLParser +{ +public: + ResXMLTree(); + ResXMLTree(const void* data, size_t size, bool copyData=false); + ~ResXMLTree(); + + status_t setTo(const void* data, size_t size, bool copyData=false); + + status_t getError() const; + + void uninit(); + + const ResStringPool& getStrings() const; + +private: + friend class ResXMLParser; + + status_t validateNode(const ResXMLTree_node* node) const; + + status_t mError; + void* mOwnedData; + const ResXMLTree_header* mHeader; + size_t mSize; + const uint8_t* mDataEnd; + ResStringPool mStrings; + const uint32_t* mResIds; + size_t mNumResIds; + const ResXMLTree_node* mRootNode; + const void* mRootExt; + event_code_t mRootCode; +}; + +/** ******************************************************************** + * RESOURCE TABLE + * + *********************************************************************** */ + +/** + * Header for a resource table. Its data contains a series of + * additional chunks: + * * A ResStringPool_header containing all table values. + * * One or more ResTable_package chunks. + * + * Specific entries within a resource table can be uniquely identified + * with a single integer as defined by the ResTable_ref structure. + */ +struct ResTable_header +{ + struct ResChunk_header header; + + // The number of ResTable_package structures. + uint32_t packageCount; +}; + +/** + * A collection of resource data types within a package. Followed by + * one or more ResTable_type and ResTable_typeSpec structures containing the + * entry values for each resource type. + */ +struct ResTable_package +{ + struct ResChunk_header header; + + // If this is a base package, its ID. Package IDs start + // at 1 (corresponding to the value of the package bits in a + // resource identifier). 0 means this is not a base package. + uint32_t id; + + // Actual name of this package, \0-terminated. + char16_t name[128]; + + // Offset to a ResStringPool_header defining the resource + // type symbol table. If zero, this package is inheriting from + // another base package (overriding specific values in it). + uint32_t typeStrings; + + // Last index into typeStrings that is for public use by others. + uint32_t lastPublicType; + + // Offset to a ResStringPool_header defining the resource + // key symbol table. If zero, this package is inheriting from + // another base package (overriding specific values in it). + uint32_t keyStrings; + + // Last index into keyStrings that is for public use by others. + uint32_t lastPublicKey; +}; + +/** + * Describes a particular resource configuration. + */ +struct ResTable_config +{ + // Number of bytes in this structure. + uint32_t size; + + union { + struct { + // Mobile country code (from SIM). 0 means "any". + uint16_t mcc; + // Mobile network code (from SIM). 0 means "any". + uint16_t mnc; + }; + uint32_t imsi; + }; + + union { + struct { + // \0\0 means "any". Otherwise, en, fr, etc. + char language[2]; + + // \0\0 means "any". Otherwise, US, CA, etc. + char country[2]; + }; + uint32_t locale; + }; + + enum { + ORIENTATION_ANY = 0x0000, + ORIENTATION_PORT = 0x0001, + ORIENTATION_LAND = 0x0002, + ORIENTATION_SQUARE = 0x0002, + }; + + enum { + TOUCHSCREEN_ANY = 0x0000, + TOUCHSCREEN_NOTOUCH = 0x0001, + TOUCHSCREEN_STYLUS = 0x0002, + TOUCHSCREEN_FINGER = 0x0003, + }; + + enum { + DENSITY_ANY = 0 + }; + + union { + struct { + uint8_t orientation; + uint8_t touchscreen; + uint16_t density; + }; + uint32_t screenType; + }; + + enum { + KEYBOARD_ANY = 0x0000, + KEYBOARD_NOKEYS = 0x0001, + KEYBOARD_QWERTY = 0x0002, + KEYBOARD_12KEY = 0x0003, + }; + + enum { + NAVIGATION_ANY = 0x0000, + NAVIGATION_NONAV = 0x0001, + NAVIGATION_DPAD = 0x0002, + NAVIGATION_TRACKBALL = 0x0003, + NAVIGATION_WHEEL = 0x0004, + }; + + enum { + MASK_KEYSHIDDEN = 0x0003, + SHIFT_KEYSHIDDEN = 0, + KEYSHIDDEN_ANY = 0x0000, + KEYSHIDDEN_NO = 0x0001, + KEYSHIDDEN_YES = 0x0002, + KEYSHIDDEN_SOFT = 0x0003, + }; + + union { + struct { + uint8_t keyboard; + uint8_t navigation; + uint8_t inputFlags; + uint8_t pad0; + }; + uint32_t input; + }; + + enum { + SCREENWIDTH_ANY = 0 + }; + + enum { + SCREENHEIGHT_ANY = 0 + }; + + union { + struct { + uint16_t screenWidth; + uint16_t screenHeight; + }; + uint32_t screenSize; + }; + + enum { + SDKVERSION_ANY = 0 + }; + + enum { + MINORVERSION_ANY = 0 + }; + + union { + struct { + uint16_t sdkVersion; + // For now minorVersion must always be 0!!! Its meaning + // is currently undefined. + uint16_t minorVersion; + }; + uint32_t version; + }; + + inline void copyFromDeviceNoSwap(const ResTable_config& o) { + const size_t size = dtohl(o.size); + if (size >= sizeof(ResTable_config)) { + *this = o; + } else { + memcpy(this, &o, size); + memset(((uint8_t*)this)+size, 0, sizeof(ResTable_config)-size); + } + } + + inline void copyFromDtoH(const ResTable_config& o) { + copyFromDeviceNoSwap(o); + size = sizeof(ResTable_config); + mcc = dtohs(mcc); + mnc = dtohs(mnc); + density = dtohs(density); + screenWidth = dtohs(screenWidth); + screenHeight = dtohs(screenHeight); + sdkVersion = dtohs(sdkVersion); + minorVersion = dtohs(minorVersion); + } + + inline void swapHtoD() { + size = htodl(size); + mcc = htods(mcc); + mnc = htods(mnc); + density = htods(density); + screenWidth = htods(screenWidth); + screenHeight = htods(screenHeight); + sdkVersion = htods(sdkVersion); + minorVersion = htods(minorVersion); + } + + inline int compare(const ResTable_config& o) const { + int32_t diff = (int32_t)(imsi - o.imsi); + if (diff != 0) return diff; + diff = (int32_t)(locale - o.locale); + if (diff != 0) return diff; + diff = (int32_t)(screenType - o.screenType); + if (diff != 0) return diff; + diff = (int32_t)(input - o.input); + if (diff != 0) return diff; + diff = (int32_t)(screenSize - o.screenSize); + if (diff != 0) return diff; + diff = (int32_t)(version - o.version); + return (int)diff; + } + + // Flags indicating a set of config values. These flag constants must + // match the corresponding ones in android.content.pm.ActivityInfo and + // attrs_manifest.xml. + enum { + CONFIG_MCC = 0x0001, + CONFIG_MNC = 0x0002, + CONFIG_LOCALE = 0x0004, + CONFIG_TOUCHSCREEN = 0x0008, + CONFIG_KEYBOARD = 0x0010, + CONFIG_KEYBOARD_HIDDEN = 0x0020, + CONFIG_NAVIGATION = 0x0040, + CONFIG_ORIENTATION = 0x0080, + CONFIG_DENSITY = 0x0100, + CONFIG_SCREEN_SIZE = 0x0200, + CONFIG_VERSION = 0x0400 + }; + + // Compare two configuration, returning CONFIG_* flags set for each value + // that is different. + inline int diff(const ResTable_config& o) const { + int diffs = 0; + if (mcc != o.mcc) diffs |= CONFIG_MCC; + if (mnc != o.mnc) diffs |= CONFIG_MNC; + if (locale != o.locale) diffs |= CONFIG_LOCALE; + if (orientation != o.orientation) diffs |= CONFIG_ORIENTATION; + if (density != o.density) diffs |= CONFIG_DENSITY; + if (touchscreen != o.touchscreen) diffs |= CONFIG_TOUCHSCREEN; + if (((inputFlags^o.inputFlags)&MASK_KEYSHIDDEN) != 0) diffs |= CONFIG_KEYBOARD_HIDDEN; + if (keyboard != o.keyboard) diffs |= CONFIG_KEYBOARD; + if (navigation != o.navigation) diffs |= CONFIG_NAVIGATION; + if (screenSize != o.screenSize) diffs |= CONFIG_SCREEN_SIZE; + if (version != o.version) diffs |= CONFIG_VERSION; + return diffs; + } + + // Return true if 'this' is more specific than 'o'. Optionally, if + // 'requested' is null, then they will also be compared against the + // requested configuration and true will only be returned if 'this' + // is a better candidate than 'o' for the configuration. This assumes that + // match() has already been used to remove any configurations that don't + // match the requested configuration at all; if they are not first filtered, + // non-matching results can be considered better than matching ones. + inline bool + isBetterThan(const ResTable_config& o, const ResTable_config* requested = NULL) const { + // The order of the following tests defines the importance of one + // configuration parameter over another. Those tests first are more + // important, trumping any values in those following them. + if (imsi != 0 && (!requested || requested->imsi != 0)) { + if (mcc != 0 && (!requested || requested->mcc != 0)) { + if (o.mcc == 0) { + return true; + } + } + if (mnc != 0 && (!requested || requested->mnc != 0)) { + if (o.mnc == 0) { + return true; + } + } + } + if (locale != 0 && (!requested || requested->locale != 0)) { + if (language[0] != 0 && (!requested || requested->language[0] != 0)) { + if (o.language[0] == 0) { + return true; + } + } + if (country[0] != 0 && (!requested || requested->country[0] != 0)) { + if (o.country[0] == 0) { + return true; + } + } + } + if (screenType != 0 && (!requested || requested->screenType != 0)) { + if (orientation != 0 && (!requested || requested->orientation != 0)) { + if (o.orientation == 0) { + return true; + } + } + if (density != 0 && (!requested || requested->density != 0)) { + if (o.density == 0) { + return true; + } + } + if (touchscreen != 0 && (!requested || requested->touchscreen != 0)) { + if (o.touchscreen == 0) { + return true; + } + } + } + if (input != 0 && (!requested || requested->input != 0)) { + const int keysHidden = inputFlags&MASK_KEYSHIDDEN; + const int reqKeysHidden = requested + ? requested->inputFlags&MASK_KEYSHIDDEN : 0; + if (keysHidden != 0 && reqKeysHidden != 0) { + const int oKeysHidden = o.inputFlags&MASK_KEYSHIDDEN; + //LOGI("isBetterThan keysHidden: cur=%d, given=%d, config=%d\n", + // keysHidden, oKeysHidden, reqKeysHidden); + if (oKeysHidden == 0) { + //LOGI("Better because 0!"); + return true; + } + // For compatibility, we count KEYSHIDDEN_NO as being + // the same as KEYSHIDDEN_SOFT. Here we disambiguate these + // may making an exact match more specific. + if (keysHidden == reqKeysHidden && oKeysHidden != reqKeysHidden) { + // The current configuration is an exact match, and + // the given one is not, so the current one is better. + //LOGI("Better because other not same!"); + return true; + } + } + if (keyboard != 0 && (!requested || requested->keyboard != 0)) { + if (o.keyboard == 0) { + return true; + } + } + if (navigation != 0 && (!requested || requested->navigation != 0)) { + if (o.navigation == 0) { + return true; + } + } + } + if (screenSize != 0 && (!requested || requested->screenSize != 0)) { + if (screenWidth != 0 && (!requested || requested->screenWidth != 0)) { + if (o.screenWidth == 0) { + return true; + } + } + if (screenHeight != 0 && (!requested || requested->screenHeight != 0)) { + if (o.screenHeight == 0) { + return true; + } + } + } + if (version != 0 && (!requested || requested->version != 0)) { + if (sdkVersion != 0 && (!requested || requested->sdkVersion != 0)) { + if (o.sdkVersion == 0) { + return true; + } + } + if (minorVersion != 0 && (!requested || requested->minorVersion != 0)) { + if (o.minorVersion == 0) { + return true; + } + } + } + return false; + } + + // Return true if 'this' can be considered a match for the parameters in + // 'settings'. + // Note this is asymetric. A default piece of data will match every request + // but a request for the default should not match odd specifics + // (ie, request with no mcc should not match a particular mcc's data) + // settings is the requested settings + inline bool match(const ResTable_config& settings) const { + if (imsi != 0) { + if ((settings.mcc != 0 && mcc != 0 + && mcc != settings.mcc) || + (settings.mcc == 0 && mcc != 0)) { + return false; + } + if ((settings.mnc != 0 && mnc != 0 + && mnc != settings.mnc) || + (settings.mnc == 0 && mnc != 0)) { + return false; + } + } + if (locale != 0) { + if (settings.language[0] != 0 && language[0] != 0 + && (language[0] != settings.language[0] + || language[1] != settings.language[1])) { + return false; + } + if (settings.country[0] != 0 && country[0] != 0 + && (country[0] != settings.country[0] + || country[1] != settings.country[1])) { + return false; + } + } + if (screenType != 0) { + if (settings.orientation != 0 && orientation != 0 + && orientation != settings.orientation) { + return false; + } + // Density not taken into account, always match, no matter what + // density is specified for the resource + if (settings.touchscreen != 0 && touchscreen != 0 + && touchscreen != settings.touchscreen) { + return false; + } + } + if (input != 0) { + const int keysHidden = inputFlags&MASK_KEYSHIDDEN; + const int setKeysHidden = settings.inputFlags&MASK_KEYSHIDDEN; + if (setKeysHidden != 0 && keysHidden != 0 + && keysHidden != setKeysHidden) { + // For compatibility, we count a request for KEYSHIDDEN_NO as also + // matching the more recent KEYSHIDDEN_SOFT. Basically + // KEYSHIDDEN_NO means there is some kind of keyboard available. + //LOGI("Matching keysHidden: have=%d, config=%d\n", keysHidden, setKeysHidden); + if (keysHidden != KEYSHIDDEN_NO || setKeysHidden != KEYSHIDDEN_SOFT) { + //LOGI("No match!"); + return false; + } + } + if (settings.keyboard != 0 && keyboard != 0 + && keyboard != settings.keyboard) { + return false; + } + if (settings.navigation != 0 && navigation != 0 + && navigation != settings.navigation) { + return false; + } + } + if (screenSize != 0) { + if (settings.screenWidth != 0 && screenWidth != 0 + && screenWidth != settings.screenWidth) { + return false; + } + if (settings.screenHeight != 0 && screenHeight != 0 + && screenHeight != settings.screenHeight) { + return false; + } + } + if (version != 0) { + if (settings.sdkVersion != 0 && sdkVersion != 0 + && sdkVersion != settings.sdkVersion) { + return false; + } + if (settings.minorVersion != 0 && minorVersion != 0 + && minorVersion != settings.minorVersion) { + return false; + } + } + return true; + } + + void getLocale(char str[6]) const { + memset(str, 0, 6); + if (language[0]) { + str[0] = language[0]; + str[1] = language[1]; + if (country[0]) { + str[2] = '_'; + str[3] = country[0]; + str[4] = country[1]; + } + } + } + + String8 toString() const { + char buf[200]; + sprintf(buf, "imsi=%d/%d lang=%c%c reg=%c%c orient=0x%02x touch=0x%02x dens=0x%02x " + "kbd=0x%02x nav=0x%02x input=0x%02x screenW=0x%04x screenH=0x%04x vers=%d.%d", + mcc, mnc, + language[0] ? language[0] : '-', language[1] ? language[1] : '-', + country[0] ? country[0] : '-', country[1] ? country[1] : '-', + orientation, touchscreen, density, keyboard, navigation, inputFlags, + screenWidth, screenHeight, sdkVersion, minorVersion); + return String8(buf); + } +}; + +/** + * A specification of the resources defined by a particular type. + * + * There should be one of these chunks for each resource type. + * + * This structure is followed by an array of integers providing the set of + * configuation change flags (ResTable_config::CONFIG_*) that have multiple + * resources for that configuration. In addition, the high bit is set if that + * resource has been made public. + */ +struct ResTable_typeSpec +{ + struct ResChunk_header header; + + // The type identifier this chunk is holding. Type IDs start + // at 1 (corresponding to the value of the type bits in a + // resource identifier). 0 is invalid. + uint8_t id; + + // Must be 0. + uint8_t res0; + // Must be 0. + uint16_t res1; + + // Number of uint32_t entry configuration masks that follow. + uint32_t entryCount; + + enum { + // Additional flag indicating an entry is public. + SPEC_PUBLIC = 0x40000000 + }; +}; + +/** + * A collection of resource entries for a particular resource data + * type. Followed by an array of uint32_t defining the resource + * values, corresponding to the array of type strings in the + * ResTable_package::typeStrings string block. Each of these hold an + * index from entriesStart; a value of NO_ENTRY means that entry is + * not defined. + * + * There may be multiple of these chunks for a particular resource type, + * supply different configuration variations for the resource values of + * that type. + * + * It would be nice to have an additional ordered index of entries, so + * we can do a binary search if trying to find a resource by string name. + */ +struct ResTable_type +{ + struct ResChunk_header header; + + enum { + NO_ENTRY = 0xFFFFFFFF + }; + + // The type identifier this chunk is holding. Type IDs start + // at 1 (corresponding to the value of the type bits in a + // resource identifier). 0 is invalid. + uint8_t id; + + // Must be 0. + uint8_t res0; + // Must be 0. + uint16_t res1; + + // Number of uint32_t entry indices that follow. + uint32_t entryCount; + + // Offset from header where ResTable_entry data starts. + uint32_t entriesStart; + + // Configuration this collection of entries is designed for. + ResTable_config config; +}; + +/** + * 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. + * * An array of ResTable_map structures, if FLAG_COMPLEX is set. + * These supply a set of name/value mappings of data. + */ +struct ResTable_entry +{ + // Number of bytes in this structure. + uint16_t size; + + enum { + // If set, this is a complex entry, holding a set of name/value + // mappings. It is followed by an array of ResTable_map structures. + FLAG_COMPLEX = 0x0001, + // If set, this resource has been declared public, so libraries + // are allowed to reference it. + FLAG_PUBLIC = 0x0002 + }; + uint16_t flags; + + // Reference into ResTable_package::keyStrings identifying this entry. + struct ResStringPool_ref key; +}; + +/** + * Extended form of a ResTable_entry for map entries, defining a parent map + * resource from which to inherit values. + */ +struct ResTable_map_entry : public ResTable_entry +{ + // Resource identifier of the parent mapping, or 0 if there is none. + ResTable_ref parent; + // Number of name/value pairs that follow for FLAG_COMPLEX. + uint32_t count; +}; + +/** + * A single name/value mapping that is part of a complex resource + * entry. + */ +struct ResTable_map +{ + // The resource identifier defining this mapping's name. For attribute + // resources, 'name' can be one of the following special resource types + // to supply meta-data about the attribute; for all other resource types + // it must be an attribute resource. + ResTable_ref name; + + // Special values for 'name' when defining attribute resources. + enum { + // This entry holds the attribute's type code. + ATTR_TYPE = Res_MAKEINTERNAL(0), + + // For integral attributes, this is the minimum value it can hold. + ATTR_MIN = Res_MAKEINTERNAL(1), + + // For integral attributes, this is the maximum value it can hold. + ATTR_MAX = Res_MAKEINTERNAL(2), + + // Localization of this resource is can be encouraged or required with + // an aapt flag if this is set + ATTR_L10N = Res_MAKEINTERNAL(3), + + // for plural support, see android.content.res.PluralRules#attrForQuantity(int) + ATTR_OTHER = Res_MAKEINTERNAL(4), + ATTR_ZERO = Res_MAKEINTERNAL(5), + ATTR_ONE = Res_MAKEINTERNAL(6), + ATTR_TWO = Res_MAKEINTERNAL(7), + ATTR_FEW = Res_MAKEINTERNAL(8), + ATTR_MANY = Res_MAKEINTERNAL(9) + + }; + + // Bit mask of allowed types, for use with ATTR_TYPE. + enum { + // No type has been defined for this attribute, use generic + // type handling. The low 16 bits are for types that can be + // handled generically; the upper 16 require additional information + // in the bag so can not be handled generically for TYPE_ANY. + TYPE_ANY = 0x0000FFFF, + + // Attribute holds a references to another resource. + TYPE_REFERENCE = 1<<0, + + // Attribute holds a generic string. + TYPE_STRING = 1<<1, + + // Attribute holds an integer value. ATTR_MIN and ATTR_MIN can + // optionally specify a constrained range of possible integer values. + TYPE_INTEGER = 1<<2, + + // Attribute holds a boolean integer. + TYPE_BOOLEAN = 1<<3, + + // Attribute holds a color value. + TYPE_COLOR = 1<<4, + + // Attribute holds a floating point value. + TYPE_FLOAT = 1<<5, + + // Attribute holds a dimension value, such as "20px". + TYPE_DIMENSION = 1<<6, + + // Attribute holds a fraction value, such as "20%". + TYPE_FRACTION = 1<<7, + + // Attribute holds an enumeration. The enumeration values are + // supplied as additional entries in the map. + TYPE_ENUM = 1<<16, + + // Attribute holds a bitmaks of flags. The flag bit values are + // supplied as additional entries in the map. + TYPE_FLAGS = 1<<17 + }; + + // Enum of localization modes, for use with ATTR_L10N. + enum { + L10N_NOT_REQUIRED = 0, + L10N_SUGGESTED = 1 + }; + + // This mapping's value. + Res_value value; +}; + +/** + * Convenience class for accessing data in a ResTable resource. + */ +class ResTable +{ +public: + ResTable(); + ResTable(const void* data, size_t size, void* cookie, + bool copyData=false); + ~ResTable(); + + status_t add(const void* data, size_t size, void* cookie, + bool copyData=false); + status_t add(Asset* asset, void* cookie, + bool copyData=false); + + status_t getError() const; + + void uninit(); + + struct resource_name + { + const char16_t* package; + size_t packageLen; + const char16_t* type; + size_t typeLen; + const char16_t* name; + size_t nameLen; + }; + + bool getResourceName(uint32_t resID, resource_name* outName) const; + + /** + * Retrieve the value of a resource. If the resource is found, returns a + * value >= 0 indicating the table it is in (for use with + * getTableStringBlock() and getTableCookie()) and fills in 'outValue'. If + * not found, returns a negative error code. + * + * Note that this function does not do reference traversal. If you want + * to follow references to other resources to get the "real" value to + * use, you need to call resolveReference() after this function. + * + * @param resID The desired resoruce identifier. + * @param outValue Filled in with the resource data that was found. + * + * @return ssize_t Either a >= 0 table index or a negative error code. + */ + ssize_t getResource(uint32_t resID, Res_value* outValue, bool mayBeBag=false, + uint32_t* outSpecFlags=NULL, ResTable_config* outConfig=NULL) const; + + inline ssize_t getResource(const ResTable_ref& res, Res_value* outValue, + uint32_t* outSpecFlags=NULL) const { + return getResource(res.ident, outValue, false, outSpecFlags, NULL); + } + + ssize_t resolveReference(Res_value* inOutValue, + ssize_t blockIndex, + uint32_t* outLastRef = NULL, + uint32_t* inoutTypeSpecFlags = NULL) const; + + enum { + TMP_BUFFER_SIZE = 16 + }; + const char16_t* valueToString(const Res_value* value, size_t stringBlock, + char16_t tmpBuffer[TMP_BUFFER_SIZE], + size_t* outLen); + + struct bag_entry { + ssize_t stringBlock; + ResTable_map map; + }; + + /** + * Retrieve the bag of a resource. If the resoruce is found, returns the + * number of bags it contains and 'outBag' points to an array of their + * values. If not found, a negative error code is returned. + * + * Note that this function -does- do reference traversal of the bag data. + * + * @param resID The desired resource identifier. + * @param outBag Filled inm with a pointer to the bag mappings. + * + * @return ssize_t Either a >= 0 bag count of negative error code. + */ + ssize_t lockBag(uint32_t resID, const bag_entry** outBag) const; + + void unlockBag(const bag_entry* bag) const; + + void lock() const; + + ssize_t getBagLocked(uint32_t resID, const bag_entry** outBag, + uint32_t* outTypeSpecFlags=NULL) const; + + void unlock() const; + + class Theme { + public: + Theme(const ResTable& table); + ~Theme(); + + inline const ResTable& getResTable() const { return mTable; } + + status_t applyStyle(uint32_t resID, bool force=false); + status_t setTo(const Theme& other); + + /** + * Retrieve a value in the theme. If the theme defines this + * value, returns a value >= 0 indicating the table it is in + * (for use with getTableStringBlock() and getTableCookie) and + * fills in 'outValue'. If not found, returns a negative error + * code. + * + * Note that this function does not do reference traversal. If you want + * to follow references to other resources to get the "real" value to + * use, you need to call resolveReference() after this function. + * + * @param resID A resource identifier naming the desired theme + * attribute. + * @param outValue Filled in with the theme value that was + * found. + * + * @return ssize_t Either a >= 0 table index or a negative error code. + */ + ssize_t getAttribute(uint32_t resID, Res_value* outValue, + uint32_t* outTypeSpecFlags = NULL) const; + + /** + * This is like ResTable::resolveReference(), but also takes + * care of resolving attribute references to the theme. + */ + ssize_t resolveAttributeReference(Res_value* inOutValue, + ssize_t blockIndex, uint32_t* outLastRef = NULL, + uint32_t* inoutTypeSpecFlags = NULL) const; + + void dumpToLog() const; + + private: + Theme(const Theme&); + Theme& operator=(const Theme&); + + struct theme_entry { + ssize_t stringBlock; + uint32_t typeSpecFlags; + Res_value value; + }; + struct type_info { + size_t numEntries; + theme_entry* entries; + }; + struct package_info { + size_t numTypes; + type_info types[]; + }; + + void free_package(package_info* pi); + package_info* copy_package(package_info* pi); + + const ResTable& mTable; + package_info* mPackages[Res_MAXPACKAGE]; + }; + + void setParameters(const ResTable_config* params); + void getParameters(ResTable_config* params) const; + + // Retrieve an identifier (which can be passed to getResource) + // for a given resource name. The 'name' can be fully qualified + // (:.) or the package or type components + // can be dropped if default values are supplied here. + // + // Returns 0 if no such resource was found, else a valid resource ID. + uint32_t identifierForName(const char16_t* name, size_t nameLen, + const char16_t* type = 0, size_t typeLen = 0, + const char16_t* defPackage = 0, + size_t defPackageLen = 0, + uint32_t* outTypeSpecFlags = NULL) const; + + static bool expandResourceRef(const uint16_t* refStr, size_t refLen, + String16* outPackage, + String16* outType, + String16* outName, + const String16* defType = NULL, + const String16* defPackage = NULL, + const char** outErrorMsg = NULL); + + static bool stringToInt(const char16_t* s, size_t len, Res_value* outValue); + static bool stringToFloat(const char16_t* s, size_t len, Res_value* outValue); + + // Used with stringToValue. + class Accessor + { + public: + inline virtual ~Accessor() { } + + virtual uint32_t getCustomResource(const String16& package, + const String16& type, + const String16& name) const = 0; + virtual uint32_t getCustomResourceWithCreation(const String16& package, + const String16& type, + const String16& name, + const bool createIfNeeded = false) = 0; + virtual uint32_t getRemappedPackage(uint32_t origPackage) const = 0; + virtual bool getAttributeType(uint32_t attrID, uint32_t* outType) = 0; + virtual bool getAttributeMin(uint32_t attrID, uint32_t* outMin) = 0; + virtual bool getAttributeMax(uint32_t attrID, uint32_t* outMax) = 0; + virtual bool getAttributeEnum(uint32_t attrID, + const char16_t* name, size_t nameLen, + Res_value* outValue) = 0; + virtual bool getAttributeFlags(uint32_t attrID, + const char16_t* name, size_t nameLen, + Res_value* outValue) = 0; + virtual uint32_t getAttributeL10N(uint32_t attrID) = 0; + virtual bool getLocalizationSetting() = 0; + virtual void reportError(void* accessorCookie, const char* fmt, ...) = 0; + }; + + // Convert a string to a resource value. Handles standard "@res", + // "#color", "123", and "0x1bd" types; performs escaping of strings. + // The resulting value is placed in 'outValue'; if it is a string type, + // 'outString' receives the string. If 'attrID' is supplied, the value is + // type checked against this attribute and it is used to perform enum + // evaluation. If 'acccessor' is supplied, it will be used to attempt to + // resolve resources that do not exist in this ResTable. If 'attrType' is + // supplied, the value will be type checked for this format if 'attrID' + // is not supplied or found. + bool stringToValue(Res_value* outValue, String16* outString, + const char16_t* s, size_t len, + bool preserveSpaces, bool coerceType, + uint32_t attrID = 0, + const String16* defType = NULL, + const String16* defPackage = NULL, + Accessor* accessor = NULL, + void* accessorCookie = NULL, + uint32_t attrType = ResTable_map::TYPE_ANY, + bool enforcePrivate = true) const; + + // Perform processing of escapes and quotes in a string. + static bool collectString(String16* outString, + const char16_t* s, size_t len, + bool preserveSpaces, + const char** outErrorMsg = NULL, + bool append = false); + + size_t getBasePackageCount() const; + const char16_t* getBasePackageName(size_t idx) const; + uint32_t getBasePackageId(size_t idx) const; + + size_t getTableCount() const; + const ResStringPool* getTableStringBlock(size_t index) const; + void* getTableCookie(size_t index) const; + + // Return the configurations (ResTable_config) that we know about + void getConfigurations(Vector* configs) const; + + void getLocales(Vector* locales) const; + +#ifndef HAVE_ANDROID_OS + void print() const; +#endif + +private: + struct Header; + struct Type; + struct Package; + struct PackageGroup; + struct bag_set; + + status_t add(const void* data, size_t size, void* cookie, + Asset* asset, bool copyData); + + ssize_t getResourcePackageIndex(uint32_t resID) const; + ssize_t getEntry( + const Package* package, int typeIndex, int entryIndex, + const ResTable_config* config, + const ResTable_type** outType, const ResTable_entry** outEntry, + const Type** outTypeClass) const; + status_t parsePackage( + const ResTable_package* const pkg, const Header* const header); + + mutable Mutex mLock; + + status_t mError; + + ResTable_config mParams; + + // Array of all resource tables. + Vector mHeaders; + + // Array of packages in all resource tables. + Vector mPackageGroups; + + // Mapping from resource package IDs to indices into the internal + // package array. + uint8_t mPackageMap[256]; +}; + +} // namespace android + +#endif // _LIBS_UTILS_RESOURCE_TYPES_H diff --git a/include/utils/SharedBuffer.h b/include/utils/SharedBuffer.h new file mode 100644 index 000000000..24508b0f7 --- /dev/null +++ b/include/utils/SharedBuffer.h @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2005 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_SHARED_BUFFER_H +#define ANDROID_SHARED_BUFFER_H + +#include +#include + +// --------------------------------------------------------------------------- + +namespace android { + +class SharedBuffer +{ +public: + + /* flags to use with release() */ + enum { + eKeepStorage = 0x00000001 + }; + + /*! allocate a buffer of size 'size' and acquire() it. + * call release() to free it. + */ + static SharedBuffer* alloc(size_t size); + + /*! free the memory associated with the SharedBuffer. + * Fails if there are any users associated with this SharedBuffer. + * In other words, the buffer must have been release by all its + * users. + */ + static ssize_t dealloc(const SharedBuffer* released); + + //! get the SharedBuffer from the data pointer + static inline const SharedBuffer* sharedBuffer(const void* data); + + //! access the data for read + inline const void* data() const; + + //! access the data for read/write + inline void* data(); + + //! get size of the buffer + inline size_t size() const; + + //! get back a SharedBuffer object from its data + static inline SharedBuffer* bufferFromData(void* data); + + //! get back a SharedBuffer object from its data + static inline const SharedBuffer* bufferFromData(const void* data); + + //! get the size of a SharedBuffer object from its data + static inline size_t sizeFromData(const void* data); + + //! edit the buffer (get a writtable, or non-const, version of it) + SharedBuffer* edit() const; + + //! edit the buffer, resizing if needed + SharedBuffer* editResize(size_t size) const; + + //! like edit() but fails if a copy is required + SharedBuffer* attemptEdit() const; + + //! resize and edit the buffer, loose it's content. + SharedBuffer* reset(size_t size) const; + + //! acquire/release a reference on this buffer + void acquire() const; + + /*! release a reference on this buffer, with the option of not + * freeing the memory associated with it if it was the last reference + * returns the previous reference count + */ + int32_t release(uint32_t flags = 0) const; + + //! returns wether or not we're the only owner + inline bool onlyOwner() const; + + +private: + inline SharedBuffer() { } + inline ~SharedBuffer() { } + inline SharedBuffer(const SharedBuffer&); + + // 16 bytes. must be sized to preserve correct alingment. + mutable int32_t mRefs; + size_t mSize; + uint32_t mReserved[2]; +}; + +// --------------------------------------------------------------------------- + +const SharedBuffer* SharedBuffer::sharedBuffer(const void* data) { + return data ? reinterpret_cast(data)-1 : 0; +} + +const void* SharedBuffer::data() const { + return this + 1; +} + +void* SharedBuffer::data() { + return this + 1; +} + +size_t SharedBuffer::size() const { + return mSize; +} + +SharedBuffer* SharedBuffer::bufferFromData(void* data) +{ + return ((SharedBuffer*)data)-1; +} + +const SharedBuffer* SharedBuffer::bufferFromData(const void* data) +{ + return ((const SharedBuffer*)data)-1; +} + +size_t SharedBuffer::sizeFromData(const void* data) +{ + return (((const SharedBuffer*)data)-1)->mSize; +} + +bool SharedBuffer::onlyOwner() const { + return (mRefs == 1); +} + +}; // namespace android + +// --------------------------------------------------------------------------- + +#endif // ANDROID_VECTOR_H diff --git a/include/utils/Socket.h b/include/utils/Socket.h new file mode 100644 index 000000000..8b7f40617 --- /dev/null +++ b/include/utils/Socket.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2005 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. + */ + +// +// Socket class. Modeled after Java classes. +// +#ifndef _RUNTIME_SOCKET_H +#define _RUNTIME_SOCKET_H + +#include +#include + +namespace android { + +/* + * Basic socket class, needed to abstract away the differences between + * BSD sockets and WinSock. This establishes a streaming network + * connection (TCP/IP) to somebody. + */ +class Socket { +public: + Socket(void); + ~Socket(void); + + // Create a connection to somewhere. + // Return 0 on success. + int connect(const char* host, int port); + int connect(const InetAddress* addr, int port); + + + // Close the socket. Don't try to use this object again after + // calling this. Returns false on failure. + bool close(void); + + // If we created the socket without an address, we can use these + // to finish the connection. Returns 0 on success. + int bind(const SocketAddress& bindPoint); + int connect(const SocketAddress& endPoint); + + // Here we deviate from the traditional object-oriented fanciness + // and just provide read/write operators instead of getters for + // objects that abstract a stream. + // + // Standard read/write semantics. + int read(void* buf, ssize_t len) const; + int write(const void* buf, ssize_t len) const; + + // This must be called once, at program startup. + static bool bootInit(void); + static void finalShutdown(void); + +private: + // Internal function that establishes a connection. + int doConnect(const InetSocketAddress& addr); + + unsigned long mSock; // holds SOCKET or int + + static bool mBootInitialized; +}; + + +// debug -- unit tests +void TestSockets(void); + +}; // namespace android + +#endif // _RUNTIME_SOCKET_H diff --git a/include/utils/SortedVector.h b/include/utils/SortedVector.h new file mode 100644 index 000000000..c8a61531f --- /dev/null +++ b/include/utils/SortedVector.h @@ -0,0 +1,282 @@ +/* + * Copyright (C) 2005 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_SORTED_VECTOR_H +#define ANDROID_SORTED_VECTOR_H + +#include +#include +#include + +#include +#include +#include + +// --------------------------------------------------------------------------- + +namespace android { + +template +class SortedVector : private SortedVectorImpl +{ +public: + typedef TYPE value_type; + + /*! + * Constructors and destructors + */ + + SortedVector(); + SortedVector(const SortedVector& rhs); + virtual ~SortedVector(); + + /*! copy operator */ + const SortedVector& operator = (const SortedVector& rhs) const; + SortedVector& operator = (const SortedVector& rhs); + + /* + * empty the vector + */ + + inline void clear() { VectorImpl::clear(); } + + /*! + * vector stats + */ + + //! returns number of items in the vector + inline size_t size() const { return VectorImpl::size(); } + //! returns wether or not the vector is empty + inline bool isEmpty() const { return VectorImpl::isEmpty(); } + //! returns how many items can be stored without reallocating the backing store + inline size_t capacity() const { return VectorImpl::capacity(); } + //! setst the capacity. capacity can never be reduced less than size() + inline ssize_t setCapacity(size_t size) { return VectorImpl::setCapacity(size); } + + /*! + * C-style array access + */ + + //! read-only C-style access + inline const TYPE* array() const; + + //! read-write C-style access. BE VERY CAREFUL when modifying the array + //! you ust keep it sorted! You usually don't use this function. + TYPE* editArray(); + + //! finds the index of an item + ssize_t indexOf(const TYPE& item) const; + + //! finds where this item should be inserted + size_t orderOf(const TYPE& item) const; + + + /*! + * accessors + */ + + //! read-only access to an item at a given index + inline const TYPE& operator [] (size_t index) const; + //! alternate name for operator [] + inline const TYPE& itemAt(size_t index) const; + //! stack-usage of the vector. returns the top of the stack (last element) + const TYPE& top() const; + //! same as operator [], but allows to access the vector backward (from the end) with a negative index + const TYPE& mirrorItemAt(ssize_t index) const; + + /*! + * modifing the array + */ + + //! add an item in the right place (and replace the one that is there) + ssize_t add(const TYPE& item); + + //! editItemAt() MUST NOT change the order of this item + TYPE& editItemAt(size_t index) { + return *( static_cast(VectorImpl::editItemLocation(index)) ); + } + + //! merges a vector into this one + ssize_t merge(const Vector& vector); + ssize_t merge(const SortedVector& vector); + + //! removes an item + ssize_t remove(const TYPE&); + + //! remove several items + inline ssize_t removeItemsAt(size_t index, size_t count = 1); + //! remove one item + inline ssize_t removeAt(size_t index) { return removeItemsAt(index); } + +protected: + virtual void do_construct(void* storage, size_t num) const; + virtual void do_destroy(void* storage, size_t num) const; + virtual void do_copy(void* dest, const void* from, size_t num) const; + virtual void do_splat(void* dest, const void* item, size_t num) const; + virtual void do_move_forward(void* dest, const void* from, size_t num) const; + virtual void do_move_backward(void* dest, const void* from, size_t num) const; + virtual int do_compare(const void* lhs, const void* rhs) const; +}; + + +// --------------------------------------------------------------------------- +// No user serviceable parts from here... +// --------------------------------------------------------------------------- + +template inline +SortedVector::SortedVector() + : SortedVectorImpl(sizeof(TYPE), + ((traits::has_trivial_ctor ? HAS_TRIVIAL_CTOR : 0) + |(traits::has_trivial_dtor ? HAS_TRIVIAL_DTOR : 0) + |(traits::has_trivial_copy ? HAS_TRIVIAL_COPY : 0) + |(traits::has_trivial_assign ? HAS_TRIVIAL_ASSIGN : 0)) + ) +{ +} + +template inline +SortedVector::SortedVector(const SortedVector& rhs) + : SortedVectorImpl(rhs) { +} + +template inline +SortedVector::~SortedVector() { + finish_vector(); +} + +template inline +SortedVector& SortedVector::operator = (const SortedVector& rhs) { + SortedVectorImpl::operator = (rhs); + return *this; +} + +template inline +const SortedVector& SortedVector::operator = (const SortedVector& rhs) const { + SortedVectorImpl::operator = (rhs); + return *this; +} + +template inline +const TYPE* SortedVector::array() const { + return static_cast(arrayImpl()); +} + +template inline +TYPE* SortedVector::editArray() { + return static_cast(editArrayImpl()); +} + + +template inline +const TYPE& SortedVector::operator[](size_t index) const { + assert( index inline +const TYPE& SortedVector::itemAt(size_t index) const { + return operator[](index); +} + +template inline +const TYPE& SortedVector::mirrorItemAt(ssize_t index) const { + assert( (index>0 ? index : -index) inline +const TYPE& SortedVector::top() const { + return *(array() + size() - 1); +} + +template inline +ssize_t SortedVector::add(const TYPE& item) { + return SortedVectorImpl::add(&item); +} + +template inline +ssize_t SortedVector::indexOf(const TYPE& item) const { + return SortedVectorImpl::indexOf(&item); +} + +template inline +size_t SortedVector::orderOf(const TYPE& item) const { + return SortedVectorImpl::orderOf(&item); +} + +template inline +ssize_t SortedVector::merge(const Vector& vector) { + return SortedVectorImpl::merge(reinterpret_cast(vector)); +} + +template inline +ssize_t SortedVector::merge(const SortedVector& vector) { + return SortedVectorImpl::merge(reinterpret_cast(vector)); +} + +template inline +ssize_t SortedVector::remove(const TYPE& item) { + return SortedVectorImpl::remove(&item); +} + +template inline +ssize_t SortedVector::removeItemsAt(size_t index, size_t count) { + return VectorImpl::removeItemsAt(index, count); +} + +// --------------------------------------------------------------------------- + +template +void SortedVector::do_construct(void* storage, size_t num) const { + construct_type( reinterpret_cast(storage), num ); +} + +template +void SortedVector::do_destroy(void* storage, size_t num) const { + destroy_type( reinterpret_cast(storage), num ); +} + +template +void SortedVector::do_copy(void* dest, const void* from, size_t num) const { + copy_type( reinterpret_cast(dest), reinterpret_cast(from), num ); +} + +template +void SortedVector::do_splat(void* dest, const void* item, size_t num) const { + splat_type( reinterpret_cast(dest), reinterpret_cast(item), num ); +} + +template +void SortedVector::do_move_forward(void* dest, const void* from, size_t num) const { + move_forward_type( reinterpret_cast(dest), reinterpret_cast(from), num ); +} + +template +void SortedVector::do_move_backward(void* dest, const void* from, size_t num) const { + move_backward_type( reinterpret_cast(dest), reinterpret_cast(from), num ); +} + +template +int SortedVector::do_compare(const void* lhs, const void* rhs) const { + return compare_type( *reinterpret_cast(lhs), *reinterpret_cast(rhs) ); +} + +}; // namespace android + + +// --------------------------------------------------------------------------- + +#endif // ANDROID_SORTED_VECTOR_H diff --git a/include/utils/StopWatch.h b/include/utils/StopWatch.h new file mode 100644 index 000000000..cc0bebc40 --- /dev/null +++ b/include/utils/StopWatch.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2005 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_STOPWATCH_H +#define ANDROID_STOPWATCH_H + +#include +#include + +#include + +// --------------------------------------------------------------------------- + +namespace android { + +class StopWatch +{ +public: + StopWatch( const char *name, + int clock = SYSTEM_TIME_MONOTONIC, + uint32_t flags = 0); + ~StopWatch(); + + const char* name() const; + nsecs_t lap(); + nsecs_t elapsedTime() const; + +private: + const char* mName; + int mClock; + uint32_t mFlags; + + struct lap_t { + nsecs_t soFar; + nsecs_t thisLap; + }; + + nsecs_t mStartTime; + lap_t mLaps[8]; + int mNumLaps; +}; + + +}; // namespace android + + +// --------------------------------------------------------------------------- + +#endif // ANDROID_STOPWATCH_H diff --git a/include/utils/String16.h b/include/utils/String16.h new file mode 100644 index 000000000..a2d22eea9 --- /dev/null +++ b/include/utils/String16.h @@ -0,0 +1,260 @@ +/* + * Copyright (C) 2005 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_STRING16_H +#define ANDROID_STRING16_H + +#include +#include + +#include +#include + +// --------------------------------------------------------------------------- + +extern "C" { + +typedef uint16_t char16_t; + +// Standard string functions on char16 strings. +int strcmp16(const char16_t *, const char16_t *); +int strncmp16(const char16_t *s1, const char16_t *s2, size_t n); +size_t strlen16(const char16_t *); +size_t strnlen16(const char16_t *, size_t); +char16_t *strcpy16(char16_t *, const char16_t *); +char16_t *strncpy16(char16_t *, const char16_t *, size_t); + +// Version of comparison that supports embedded nulls. +// This is different than strncmp() because we don't stop +// at a nul character and consider the strings to be different +// if the lengths are different (thus we need to supply the +// lengths of both strings). This can also be used when +// your string is not nul-terminated as it will have the +// equivalent result as strcmp16 (unlike strncmp16). +int strzcmp16(const char16_t *s1, size_t n1, const char16_t *s2, size_t n2); + +// Version of strzcmp16 for comparing strings in different endianness. +int strzcmp16_h_n(const char16_t *s1H, size_t n1, const char16_t *s2N, size_t n2); + +} + +// --------------------------------------------------------------------------- + +namespace android { + +class String8; +class TextOutput; + +//! This is a string holding UTF-16 characters. +class String16 +{ +public: + String16(); + String16(const String16& o); + String16(const String16& o, + size_t len, + size_t begin=0); + explicit String16(const char16_t* o); + explicit String16(const char16_t* o, size_t len); + explicit String16(const String8& o); + explicit String16(const char* o); + explicit String16(const char* o, size_t len); + + ~String16(); + + inline const char16_t* string() const; + inline size_t size() const; + + inline const SharedBuffer* sharedBuffer() const; + + void setTo(const String16& other); + status_t setTo(const char16_t* other); + status_t setTo(const char16_t* other, size_t len); + status_t setTo(const String16& other, + size_t len, + size_t begin=0); + + status_t append(const String16& other); + status_t append(const char16_t* other, size_t len); + + inline String16& operator=(const String16& other); + + inline String16& operator+=(const String16& other); + inline String16 operator+(const String16& other) const; + + status_t insert(size_t pos, const char16_t* chrs); + status_t insert(size_t pos, + const char16_t* chrs, size_t len); + + ssize_t findFirst(char16_t c) const; + ssize_t findLast(char16_t c) const; + + bool startsWith(const String16& prefix) const; + bool startsWith(const char16_t* prefix) const; + + status_t makeLower(); + + status_t replaceAll(char16_t replaceThis, + char16_t withThis); + + status_t remove(size_t len, size_t begin=0); + + inline int compare(const String16& other) const; + + inline bool operator<(const String16& other) const; + inline bool operator<=(const String16& other) const; + inline bool operator==(const String16& other) const; + inline bool operator!=(const String16& other) const; + inline bool operator>=(const String16& other) const; + inline bool operator>(const String16& other) const; + + inline bool operator<(const char16_t* other) const; + inline bool operator<=(const char16_t* other) const; + inline bool operator==(const char16_t* other) const; + inline bool operator!=(const char16_t* other) const; + inline bool operator>=(const char16_t* other) const; + inline bool operator>(const char16_t* other) const; + + inline operator const char16_t*() const; + +private: + const char16_t* mString; +}; + +TextOutput& operator<<(TextOutput& to, const String16& val); + +// --------------------------------------------------------------------------- +// No user servicable parts below. + +inline int compare_type(const String16& lhs, const String16& rhs) +{ + return lhs.compare(rhs); +} + +inline int strictly_order_type(const String16& lhs, const String16& rhs) +{ + return compare_type(lhs, rhs) < 0; +} + +inline const char16_t* String16::string() const +{ + return mString; +} + +inline size_t String16::size() const +{ + return SharedBuffer::sizeFromData(mString)/sizeof(char16_t)-1; +} + +inline const SharedBuffer* String16::sharedBuffer() const +{ + return SharedBuffer::bufferFromData(mString); +} + +inline String16& String16::operator=(const String16& other) +{ + setTo(other); + return *this; +} + +inline String16& String16::operator+=(const String16& other) +{ + append(other); + return *this; +} + +inline String16 String16::operator+(const String16& other) const +{ + String16 tmp; + tmp += other; + return tmp; +} + +inline int String16::compare(const String16& other) const +{ + return strzcmp16(mString, size(), other.mString, other.size()); +} + +inline bool String16::operator<(const String16& other) const +{ + return strzcmp16(mString, size(), other.mString, other.size()) < 0; +} + +inline bool String16::operator<=(const String16& other) const +{ + return strzcmp16(mString, size(), other.mString, other.size()) <= 0; +} + +inline bool String16::operator==(const String16& other) const +{ + return strzcmp16(mString, size(), other.mString, other.size()) == 0; +} + +inline bool String16::operator!=(const String16& other) const +{ + return strzcmp16(mString, size(), other.mString, other.size()) != 0; +} + +inline bool String16::operator>=(const String16& other) const +{ + return strzcmp16(mString, size(), other.mString, other.size()) >= 0; +} + +inline bool String16::operator>(const String16& other) const +{ + return strzcmp16(mString, size(), other.mString, other.size()) > 0; +} + +inline bool String16::operator<(const char16_t* other) const +{ + return strcmp16(mString, other) < 0; +} + +inline bool String16::operator<=(const char16_t* other) const +{ + return strcmp16(mString, other) <= 0; +} + +inline bool String16::operator==(const char16_t* other) const +{ + return strcmp16(mString, other) == 0; +} + +inline bool String16::operator!=(const char16_t* other) const +{ + return strcmp16(mString, other) != 0; +} + +inline bool String16::operator>=(const char16_t* other) const +{ + return strcmp16(mString, other) >= 0; +} + +inline bool String16::operator>(const char16_t* other) const +{ + return strcmp16(mString, other) > 0; +} + +inline String16::operator const char16_t*() const +{ + return mString; +} + +}; // namespace android + +// --------------------------------------------------------------------------- + +#endif // ANDROID_STRING16_H diff --git a/include/utils/String8.h b/include/utils/String8.h new file mode 100644 index 000000000..c49faf6fe --- /dev/null +++ b/include/utils/String8.h @@ -0,0 +1,353 @@ +/* + * Copyright (C) 2005 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_STRING8_H +#define ANDROID_STRING8_H + +#include + +// Need this for the char16_t type; String8.h should not +// be depedent on the String16 class. +#include + +#include +#include +#include + +// --------------------------------------------------------------------------- + +namespace android { + +class TextOutput; + +//! This is a string holding UTF-8 characters. +class String8 +{ +public: + String8(); + String8(const String8& o); + explicit String8(const char* o); + explicit String8(const char* o, size_t numChars); + + explicit String8(const String16& o); + explicit String8(const char16_t* o); + explicit String8(const char16_t* o, size_t numChars); + + ~String8(); + + inline const char* string() const; + inline size_t size() const; + inline size_t length() const; + inline size_t bytes() const; + + inline const SharedBuffer* sharedBuffer() const; + + void setTo(const String8& other); + status_t setTo(const char* other); + status_t setTo(const char* other, size_t numChars); + status_t setTo(const char16_t* other, size_t numChars); + + status_t append(const String8& other); + status_t append(const char* other); + status_t append(const char* other, size_t numChars); + + inline String8& operator=(const String8& other); + inline String8& operator=(const char* other); + + inline String8& operator+=(const String8& other); + inline String8 operator+(const String8& other) const; + + inline String8& operator+=(const char* other); + inline String8 operator+(const char* other) const; + + inline int compare(const String8& other) const; + + inline bool operator<(const String8& other) const; + inline bool operator<=(const String8& other) const; + inline bool operator==(const String8& other) const; + inline bool operator!=(const String8& other) const; + inline bool operator>=(const String8& other) const; + inline bool operator>(const String8& other) const; + + inline bool operator<(const char* other) const; + inline bool operator<=(const char* other) const; + inline bool operator==(const char* other) const; + inline bool operator!=(const char* other) const; + inline bool operator>=(const char* other) const; + inline bool operator>(const char* other) const; + + inline operator const char*() const; + + char* lockBuffer(size_t size); + void unlockBuffer(); + status_t unlockBuffer(size_t size); + + // return the index of the first byte of other in this at or after + // start, or -1 if not found + ssize_t find(const char* other, size_t start = 0) const; + + void toLower(); + void toLower(size_t start, size_t numChars); + void toUpper(); + void toUpper(size_t start, size_t numChars); + + /* + * These methods operate on the string as if it were a path name. + */ + + /* + * Set the filename field to a specific value. + * + * Normalizes the filename, removing a trailing '/' if present. + */ + void setPathName(const char* name); + void setPathName(const char* name, size_t numChars); + + /* + * Get just the filename component. + * + * "/tmp/foo/bar.c" --> "bar.c" + */ + String8 getPathLeaf(void) const; + + /* + * Remove the last (file name) component, leaving just the directory + * name. + * + * "/tmp/foo/bar.c" --> "/tmp/foo" + * "/tmp" --> "" // ????? shouldn't this be "/" ???? XXX + * "bar.c" --> "" + */ + String8 getPathDir(void) const; + + /* + * Retrieve the front (root dir) component. Optionally also return the + * remaining components. + * + * "/tmp/foo/bar.c" --> "tmp" (remain = "foo/bar.c") + * "/tmp" --> "tmp" (remain = "") + * "bar.c" --> "bar.c" (remain = "") + */ + String8 walkPath(String8* outRemains = NULL) const; + + /* + * Return the filename extension. This is the last '.' and up to + * four characters that follow it. The '.' is included in case we + * decide to expand our definition of what constitutes an extension. + * + * "/tmp/foo/bar.c" --> ".c" + * "/tmp" --> "" + * "/tmp/foo.bar/baz" --> "" + * "foo.jpeg" --> ".jpeg" + * "foo." --> "" + */ + String8 getPathExtension(void) const; + + /* + * Return the path without the extension. Rules for what constitutes + * an extension are described in the comment for getPathExtension(). + * + * "/tmp/foo/bar.c" --> "/tmp/foo/bar" + */ + String8 getBasePath(void) const; + + /* + * Add a component to the pathname. We guarantee that there is + * exactly one path separator between the old path and the new. + * If there is no existing name, we just copy the new name in. + * + * If leaf is a fully qualified path (i.e. starts with '/', it + * replaces whatever was there before. + */ + String8& appendPath(const char* leaf); + String8& appendPath(const String8& leaf) { return appendPath(leaf.string()); } + + /* + * Like appendPath(), but does not affect this string. Returns a new one instead. + */ + String8 appendPathCopy(const char* leaf) const + { String8 p(*this); p.appendPath(leaf); return p; } + String8 appendPathCopy(const String8& leaf) const { return appendPathCopy(leaf.string()); } + + /* + * Converts all separators in this string to /, the default path separator. + * + * If the default OS separator is backslash, this converts all + * backslashes to slashes, in-place. Otherwise it does nothing. + * Returns self. + */ + String8& convertToResPath(); + +private: + status_t real_append(const char* other, size_t numChars); + char* find_extension(void) const; + + const char* mString; +}; + +TextOutput& operator<<(TextOutput& to, const String16& val); + +// --------------------------------------------------------------------------- +// No user servicable parts below. + +inline int compare_type(const String8& lhs, const String8& rhs) +{ + return lhs.compare(rhs); +} + +inline int strictly_order_type(const String8& lhs, const String8& rhs) +{ + return compare_type(lhs, rhs) < 0; +} + +inline const char* String8::string() const +{ + return mString; +} + +inline size_t String8::length() const +{ + return SharedBuffer::sizeFromData(mString)-1; +} + +inline size_t String8::size() const +{ + return length(); +} + +inline size_t String8::bytes() const +{ + return SharedBuffer::sizeFromData(mString)-1; +} + +inline const SharedBuffer* String8::sharedBuffer() const +{ + return SharedBuffer::bufferFromData(mString); +} + +inline String8& String8::operator=(const String8& other) +{ + setTo(other); + return *this; +} + +inline String8& String8::operator=(const char* other) +{ + setTo(other); + return *this; +} + +inline String8& String8::operator+=(const String8& other) +{ + append(other); + return *this; +} + +inline String8 String8::operator+(const String8& other) const +{ + String8 tmp; + tmp += other; + return tmp; +} + +inline String8& String8::operator+=(const char* other) +{ + append(other); + return *this; +} + +inline String8 String8::operator+(const char* other) const +{ + String8 tmp; + tmp += other; + return tmp; +} + +inline int String8::compare(const String8& other) const +{ + return strcmp(mString, other.mString); +} + +inline bool String8::operator<(const String8& other) const +{ + return strcmp(mString, other.mString) < 0; +} + +inline bool String8::operator<=(const String8& other) const +{ + return strcmp(mString, other.mString) <= 0; +} + +inline bool String8::operator==(const String8& other) const +{ + return strcmp(mString, other.mString) == 0; +} + +inline bool String8::operator!=(const String8& other) const +{ + return strcmp(mString, other.mString) != 0; +} + +inline bool String8::operator>=(const String8& other) const +{ + return strcmp(mString, other.mString) >= 0; +} + +inline bool String8::operator>(const String8& other) const +{ + return strcmp(mString, other.mString) > 0; +} + +inline bool String8::operator<(const char* other) const +{ + return strcmp(mString, other) < 0; +} + +inline bool String8::operator<=(const char* other) const +{ + return strcmp(mString, other) <= 0; +} + +inline bool String8::operator==(const char* other) const +{ + return strcmp(mString, other) == 0; +} + +inline bool String8::operator!=(const char* other) const +{ + return strcmp(mString, other) != 0; +} + +inline bool String8::operator>=(const char* other) const +{ + return strcmp(mString, other) >= 0; +} + +inline bool String8::operator>(const char* other) const +{ + return strcmp(mString, other) > 0; +} + +inline String8::operator const char*() const +{ + return mString; +} + +}; // namespace android + +// --------------------------------------------------------------------------- + +#endif // ANDROID_STRING8_H diff --git a/include/utils/SystemClock.h b/include/utils/SystemClock.h new file mode 100644 index 000000000..7c319be13 --- /dev/null +++ b/include/utils/SystemClock.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_UTILS_SYSTEMCLOCK_H +#define ANDROID_UTILS_SYSTEMCLOCK_H + +#include +#include + +namespace android { + +int setCurrentTimeMillis(int64_t millis); +int64_t uptimeMillis(); +int64_t elapsedRealtime(); + +}; // namespace android + +#endif // ANDROID_UTILS_SYSTEMCLOCK_H + diff --git a/include/utils/TextOutput.h b/include/utils/TextOutput.h new file mode 100644 index 000000000..d8d86ba82 --- /dev/null +++ b/include/utils/TextOutput.h @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2006 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_TEXTOUTPUT_H +#define ANDROID_TEXTOUTPUT_H + +#include + +#include +#include + +// --------------------------------------------------------------------------- +namespace android { + +class TextOutput +{ +public: + TextOutput() { } + virtual ~TextOutput() { } + + virtual status_t print(const char* txt, size_t len) = 0; + virtual void moveIndent(int delta) = 0; + + class Bundle { + public: + inline Bundle(TextOutput& to) : mTO(to) { to.pushBundle(); } + inline ~Bundle() { mTO.popBundle(); } + private: + TextOutput& mTO; + }; + + virtual void pushBundle() = 0; + virtual void popBundle() = 0; +}; + +// --------------------------------------------------------------------------- + +// Text output stream for printing to the log (via utils/Log.h). +extern TextOutput& alog; + +// Text output stream for printing to stdout. +extern TextOutput& aout; + +// Text output stream for printing to stderr. +extern TextOutput& aerr; + +typedef TextOutput& (*TextOutputManipFunc)(TextOutput&); + +TextOutput& endl(TextOutput& to); +TextOutput& indent(TextOutput& to); +TextOutput& dedent(TextOutput& to); + +TextOutput& operator<<(TextOutput& to, const char* str); +TextOutput& operator<<(TextOutput& to, char); // writes raw character +TextOutput& operator<<(TextOutput& to, bool); +TextOutput& operator<<(TextOutput& to, int); +TextOutput& operator<<(TextOutput& to, long); +TextOutput& operator<<(TextOutput& to, unsigned int); +TextOutput& operator<<(TextOutput& to, unsigned long); +TextOutput& operator<<(TextOutput& to, long long); +TextOutput& operator<<(TextOutput& to, unsigned long long); +TextOutput& operator<<(TextOutput& to, float); +TextOutput& operator<<(TextOutput& to, double); +TextOutput& operator<<(TextOutput& to, TextOutputManipFunc func); +TextOutput& operator<<(TextOutput& to, const void*); + +class TypeCode +{ +public: + inline TypeCode(uint32_t code); + inline ~TypeCode(); + + inline uint32_t typeCode() const; + +private: + uint32_t mCode; +}; + +TextOutput& operator<<(TextOutput& to, const TypeCode& val); + +class HexDump +{ +public: + HexDump(const void *buf, size_t size, size_t bytesPerLine=16); + inline ~HexDump(); + + inline HexDump& setBytesPerLine(size_t bytesPerLine); + inline HexDump& setSingleLineCutoff(int32_t bytes); + inline HexDump& setAlignment(size_t alignment); + inline HexDump& setCArrayStyle(bool enabled); + + inline const void* buffer() const; + inline size_t size() const; + inline size_t bytesPerLine() const; + inline int32_t singleLineCutoff() const; + inline size_t alignment() const; + inline bool carrayStyle() const; + +private: + const void* mBuffer; + size_t mSize; + size_t mBytesPerLine; + int32_t mSingleLineCutoff; + size_t mAlignment; + bool mCArrayStyle; +}; + +TextOutput& operator<<(TextOutput& to, const HexDump& val); + +// --------------------------------------------------------------------------- +// No user servicable parts below. + +inline TextOutput& endl(TextOutput& to) +{ + to.print("\n", 1); + return to; +} + +inline TextOutput& indent(TextOutput& to) +{ + to.moveIndent(1); + return to; +} + +inline TextOutput& dedent(TextOutput& to) +{ + to.moveIndent(-1); + return to; +} + +inline TextOutput& operator<<(TextOutput& to, const char* str) +{ + to.print(str, strlen(str)); + return to; +} + +inline TextOutput& operator<<(TextOutput& to, char c) +{ + to.print(&c, 1); + return to; +} + +inline TextOutput& operator<<(TextOutput& to, TextOutputManipFunc func) +{ + return (*func)(to); +} + +inline TypeCode::TypeCode(uint32_t code) : mCode(code) { } +inline TypeCode::~TypeCode() { } +inline uint32_t TypeCode::typeCode() const { return mCode; } + +inline HexDump::~HexDump() { } + +inline HexDump& HexDump::setBytesPerLine(size_t bytesPerLine) { + mBytesPerLine = bytesPerLine; return *this; +} +inline HexDump& HexDump::setSingleLineCutoff(int32_t bytes) { + mSingleLineCutoff = bytes; return *this; +} +inline HexDump& HexDump::setAlignment(size_t alignment) { + mAlignment = alignment; return *this; +} +inline HexDump& HexDump::setCArrayStyle(bool enabled) { + mCArrayStyle = enabled; return *this; +} + +inline const void* HexDump::buffer() const { return mBuffer; } +inline size_t HexDump::size() const { return mSize; } +inline size_t HexDump::bytesPerLine() const { return mBytesPerLine; } +inline int32_t HexDump::singleLineCutoff() const { return mSingleLineCutoff; } +inline size_t HexDump::alignment() const { return mAlignment; } +inline bool HexDump::carrayStyle() const { return mCArrayStyle; } + +// --------------------------------------------------------------------------- +}; // namespace android + +#endif // ANDROID_TEXTOUTPUT_H diff --git a/include/utils/TimeUtils.h b/include/utils/TimeUtils.h new file mode 100644 index 000000000..b19e02126 --- /dev/null +++ b/include/utils/TimeUtils.h @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2005 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_TIME_H +#define ANDROID_TIME_H + +#include +#include +#include +#include +#include +#include +#include + +namespace android { + +/* + * This class is the core implementation of the android.util.Time java + * class. It doesn't implement some of the methods that are implemented + * in Java. They could be done here, but it's not expected that this class + * will be used. If that assumption is incorrect, feel free to update this + * file. The reason to do it here is to not mix the implementation of this + * class and the jni glue code. + */ +class Time +{ +public: + struct tm t; + + // this object doesn't own this string + const char *timezone; + + enum { + SEC = 1, + MIN = 2, + HOUR = 3, + MDAY = 4, + MON = 5, + YEAR = 6, + WDAY = 7, + YDAY = 8 + }; + + static int compare(Time& a, Time& b); + + Time(); + + void switchTimezone(const char *timezone); + String8 format(const char *format, const struct strftime_locale *locale) const; + void format2445(short* buf, bool hasTime) const; + String8 toString() const; + void setToNow(); + int64_t toMillis(bool ignoreDst); + void set(int64_t millis); + + inline void set(int sec, int min, int hour, int mday, int mon, int year, + int isdst) + { + this->t.tm_sec = sec; + this->t.tm_min = min; + this->t.tm_hour = hour; + this->t.tm_mday = mday; + this->t.tm_mon = mon; + this->t.tm_year = year; + this->t.tm_isdst = isdst; +#ifdef HAVE_TM_GMTOFF + this->t.tm_gmtoff = 0; +#endif + this->t.tm_wday = 0; + this->t.tm_yday = 0; + } +}; + +}; // namespace android + +#endif // ANDROID_TIME_H diff --git a/include/utils/TimerProbe.h b/include/utils/TimerProbe.h new file mode 100644 index 000000000..f2e32b21a --- /dev/null +++ b/include/utils/TimerProbe.h @@ -0,0 +1,72 @@ +/* + * 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. + */ + +#ifndef ANDROID_TIMER_PROBE_H +#define ANDROID_TIMER_PROBE_H + +#if 0 && defined(HAVE_POSIX_CLOCKS) +#define ENABLE_TIMER_PROBE 1 +#else +#define ENABLE_TIMER_PROBE 0 +#endif + +#if ENABLE_TIMER_PROBE + +#include +#include +#include + +#define TIMER_PROBE(tag) \ + static int _timer_slot_; \ + android::TimerProbe probe(tag, &_timer_slot_) +#define TIMER_PROBE_END() probe.end() +#else +#define TIMER_PROBE(tag) +#define TIMER_PROBE_END() +#endif + +#if ENABLE_TIMER_PROBE +namespace android { + +class TimerProbe { +public: + TimerProbe(const char tag[], int* slot); + void end(); + ~TimerProbe(); +private: + struct Bucket { + int mStart, mReal, mProcess, mThread, mCount; + const char* mTag; + int* mSlotPtr; + int mIndent; + }; + static Vector gBuckets; + static TimerProbe* gExecuteChain; + static int gIndent; + static timespec gRealBase; + TimerProbe* mNext; + static uint32_t ElapsedTime(const timespec& start, const timespec& end); + void print(const timespec& r, const timespec& p, const timespec& t) const; + timespec mRealStart, mPStart, mTStart; + const char* mTag; + int mIndent; + int mBucket; +}; + +}; // namespace android + +#endif +#endif diff --git a/include/utils/Timers.h b/include/utils/Timers.h new file mode 100644 index 000000000..96103995b --- /dev/null +++ b/include/utils/Timers.h @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2005 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. + */ + +// +// Timer functions. +// +#ifndef _LIBS_UTILS_TIMERS_H +#define _LIBS_UTILS_TIMERS_H + +#include +#include +#include + +// ------------------------------------------------------------------ +// C API + +#ifdef __cplusplus +extern "C" { +#endif + +typedef int64_t nsecs_t; // nano-seconds + +static inline nsecs_t seconds_to_nanoseconds(nsecs_t secs) +{ + return secs*1000000000; +} + +static inline nsecs_t milliseconds_to_nanoseconds(nsecs_t secs) +{ + return secs*1000000; +} + +static inline nsecs_t microseconds_to_nanoseconds(nsecs_t secs) +{ + return secs*1000; +} + +static inline nsecs_t nanoseconds_to_seconds(nsecs_t secs) +{ + return secs/1000000000; +} + +static inline nsecs_t nanoseconds_to_milliseconds(nsecs_t secs) +{ + return secs/1000000; +} + +static inline nsecs_t nanoseconds_to_microseconds(nsecs_t secs) +{ + return secs/1000; +} + +static inline nsecs_t s2ns(nsecs_t v) {return seconds_to_nanoseconds(v);} +static inline nsecs_t ms2ns(nsecs_t v) {return milliseconds_to_nanoseconds(v);} +static inline nsecs_t us2ns(nsecs_t v) {return microseconds_to_nanoseconds(v);} +static inline nsecs_t ns2s(nsecs_t v) {return nanoseconds_to_seconds(v);} +static inline nsecs_t ns2ms(nsecs_t v) {return nanoseconds_to_milliseconds(v);} +static inline nsecs_t ns2us(nsecs_t v) {return nanoseconds_to_microseconds(v);} + +static inline nsecs_t seconds(nsecs_t v) { return s2ns(v); } +static inline nsecs_t milliseconds(nsecs_t v) { return ms2ns(v); } +static inline nsecs_t microseconds(nsecs_t v) { return us2ns(v); } + +enum { + SYSTEM_TIME_REALTIME = 0, // system-wide realtime clock + SYSTEM_TIME_MONOTONIC = 1, // monotonic time since unspecified starting point + SYSTEM_TIME_PROCESS = 2, // high-resolution per-process clock + SYSTEM_TIME_THREAD = 3 // high-resolution per-thread clock +}; + +// return the system-time according to the specified clock +#ifdef __cplusplus +nsecs_t systemTime(int clock = SYSTEM_TIME_MONOTONIC); +#else +nsecs_t systemTime(int clock); +#endif // def __cplusplus + +// return the system-time according to the specified clock +int sleepForInterval(long interval, struct timeval* pNextTick); + +#ifdef __cplusplus +} // extern "C" +#endif + +// ------------------------------------------------------------------ +// C++ API + +#ifdef __cplusplus + +namespace android { +/* + * Time the duration of something. + * + * Includes some timeval manipulation functions. + */ +class DurationTimer { +public: + DurationTimer(void) {} + ~DurationTimer(void) {} + + // Start the timer. + void start(void); + // Stop the timer. + void stop(void); + // Get the duration in microseconds. + long long durationUsecs(void) const; + + // Subtract two timevals. Returns the difference (ptv1-ptv2) in + // microseconds. + static long long subtractTimevals(const struct timeval* ptv1, + const struct timeval* ptv2); + + // Add the specified amount of time to the timeval. + static void addToTimeval(struct timeval* ptv, long usec); + +private: + struct timeval mStartWhen; + struct timeval mStopWhen; +}; + +}; // android +#endif // def __cplusplus + +#endif // _LIBS_UTILS_TIMERS_H diff --git a/include/utils/TypeHelpers.h b/include/utils/TypeHelpers.h new file mode 100644 index 000000000..c04c37fa9 --- /dev/null +++ b/include/utils/TypeHelpers.h @@ -0,0 +1,254 @@ +/* + * Copyright (C) 2005 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_TYPE_HELPERS_H +#define ANDROID_TYPE_HELPERS_H + +#include +#include +#include +#include + +// --------------------------------------------------------------------------- + +namespace android { + +/* + * Types traits + */ + +template struct trait_trivial_ctor { enum { value = false }; }; +template struct trait_trivial_dtor { enum { value = false }; }; +template struct trait_trivial_copy { enum { value = false }; }; +template struct trait_trivial_assign{ enum { value = false }; }; + +template struct trait_pointer { enum { value = false }; }; +template struct trait_pointer { enum { value = true }; }; + +#define ANDROID_BASIC_TYPES_TRAITS( T ) \ + template<> struct trait_trivial_ctor< T > { enum { value = true }; }; \ + template<> struct trait_trivial_dtor< T > { enum { value = true }; }; \ + template<> struct trait_trivial_copy< T > { enum { value = true }; }; \ + template<> struct trait_trivial_assign< T >{ enum { value = true }; }; + +#define ANDROID_TYPE_TRAITS( T, ctor, dtor, copy, assign ) \ + template<> struct trait_trivial_ctor< T > { enum { value = ctor }; }; \ + template<> struct trait_trivial_dtor< T > { enum { value = dtor }; }; \ + template<> struct trait_trivial_copy< T > { enum { value = copy }; }; \ + template<> struct trait_trivial_assign< T >{ enum { value = assign }; }; + +template +struct traits { + enum { + is_pointer = trait_pointer::value, + has_trivial_ctor = is_pointer || trait_trivial_ctor::value, + has_trivial_dtor = is_pointer || trait_trivial_dtor::value, + has_trivial_copy = is_pointer || trait_trivial_copy::value, + has_trivial_assign = is_pointer || trait_trivial_assign::value + }; +}; + +template +struct aggregate_traits { + enum { + is_pointer = false, + has_trivial_ctor = traits::has_trivial_ctor && traits::has_trivial_ctor, + has_trivial_dtor = traits::has_trivial_dtor && traits::has_trivial_dtor, + has_trivial_copy = traits::has_trivial_copy && traits::has_trivial_copy, + has_trivial_assign = traits::has_trivial_assign && traits::has_trivial_assign + }; +}; + +// --------------------------------------------------------------------------- + +/* + * basic types traits + */ + +ANDROID_BASIC_TYPES_TRAITS( void ); +ANDROID_BASIC_TYPES_TRAITS( bool ); +ANDROID_BASIC_TYPES_TRAITS( char ); +ANDROID_BASIC_TYPES_TRAITS( unsigned char ); +ANDROID_BASIC_TYPES_TRAITS( short ); +ANDROID_BASIC_TYPES_TRAITS( unsigned short ); +ANDROID_BASIC_TYPES_TRAITS( int ); +ANDROID_BASIC_TYPES_TRAITS( unsigned int ); +ANDROID_BASIC_TYPES_TRAITS( long ); +ANDROID_BASIC_TYPES_TRAITS( unsigned long ); +ANDROID_BASIC_TYPES_TRAITS( long long ); +ANDROID_BASIC_TYPES_TRAITS( unsigned long long ); +ANDROID_BASIC_TYPES_TRAITS( float ); +ANDROID_BASIC_TYPES_TRAITS( double ); + +// --------------------------------------------------------------------------- + + +/* + * compare and order types + */ + +template inline +int strictly_order_type(const TYPE& lhs, const TYPE& rhs) { + return (lhs < rhs) ? 1 : 0; +} + +template inline +int compare_type(const TYPE& lhs, const TYPE& rhs) { + return strictly_order_type(rhs, lhs) - strictly_order_type(lhs, rhs); +} + +/* + * create, destroy, copy and assign types... + */ + +template inline +void construct_type(TYPE* p, size_t n) { + if (!traits::has_trivial_ctor) { + while (n--) { + new(p++) TYPE; + } + } +} + +template inline +void destroy_type(TYPE* p, size_t n) { + if (!traits::has_trivial_dtor) { + while (n--) { + p->~TYPE(); + p++; + } + } +} + +template inline +void copy_type(TYPE* d, const TYPE* s, size_t n) { + if (!traits::has_trivial_copy) { + while (n--) { + new(d) TYPE(*s); + d++, s++; + } + } else { + memcpy(d,s,n*sizeof(TYPE)); + } +} + +template inline +void assign_type(TYPE* d, const TYPE* s, size_t n) { + if (!traits::has_trivial_assign) { + while (n--) { + *d++ = *s++; + } + } else { + memcpy(d,s,n*sizeof(TYPE)); + } +} + +template inline +void splat_type(TYPE* where, const TYPE* what, size_t n) { + if (!traits::has_trivial_copy) { + while (n--) { + new(where) TYPE(*what); + where++; + } + } else { + while (n--) { + *where++ = *what; + } + } +} + +template inline +void move_forward_type(TYPE* d, const TYPE* s, size_t n = 1) { + if (!traits::has_trivial_copy || !traits::has_trivial_dtor) { + d += n; + s += n; + while (n--) { + --d, --s; + if (!traits::has_trivial_copy) { + new(d) TYPE(*s); + } else { + *d = *s; + } + if (!traits::has_trivial_dtor) { + s->~TYPE(); + } + } + } else { + memmove(d,s,n*sizeof(TYPE)); + } +} + +template inline +void move_backward_type(TYPE* d, const TYPE* s, size_t n = 1) { + if (!traits::has_trivial_copy || !traits::has_trivial_dtor) { + while (n--) { + if (!traits::has_trivial_copy) { + new(d) TYPE(*s); + } else { + *d = *s; + } + if (!traits::has_trivial_dtor) { + s->~TYPE(); + } + d++, s++; + } + } else { + memmove(d,s,n*sizeof(TYPE)); + } +} +// --------------------------------------------------------------------------- + +/* + * a key/value pair + */ + +template +struct key_value_pair_t { + KEY key; + VALUE value; + key_value_pair_t() { } + key_value_pair_t(const key_value_pair_t& o) : key(o.key), value(o.value) { } + key_value_pair_t(const KEY& k, const VALUE& v) : key(k), value(v) { } + key_value_pair_t(const KEY& k) : key(k) { } + inline bool operator < (const key_value_pair_t& o) const { + return strictly_order_type(key, o.key); + } +}; + +template<> +template +struct trait_trivial_ctor< key_value_pair_t > +{ enum { value = aggregate_traits::has_trivial_ctor }; }; +template<> +template +struct trait_trivial_dtor< key_value_pair_t > +{ enum { value = aggregate_traits::has_trivial_dtor }; }; +template<> +template +struct trait_trivial_copy< key_value_pair_t > +{ enum { value = aggregate_traits::has_trivial_copy }; }; +template<> +template +struct trait_trivial_assign< key_value_pair_t > +{ enum { value = aggregate_traits::has_trivial_assign};}; + +// --------------------------------------------------------------------------- + +}; // namespace android + +// --------------------------------------------------------------------------- + +#endif // ANDROID_TYPE_HELPERS_H diff --git a/include/utils/Vector.h b/include/utils/Vector.h new file mode 100644 index 000000000..be365d83e --- /dev/null +++ b/include/utils/Vector.h @@ -0,0 +1,359 @@ +/* + * Copyright (C) 2005 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_VECTOR_H +#define ANDROID_VECTOR_H + +#include +#include +#include + +#include +#include +#include + +// --------------------------------------------------------------------------- + +namespace android { + +/*! + * The main templated vector class ensuring type safety + * while making use of VectorImpl. + * This is the class users want to use. + */ + +template +class Vector : private VectorImpl +{ +public: + typedef TYPE value_type; + + /*! + * Constructors and destructors + */ + + Vector(); + Vector(const Vector& rhs); + virtual ~Vector(); + + /*! copy operator */ + const Vector& operator = (const Vector& rhs) const; + Vector& operator = (const Vector& rhs); + + /* + * empty the vector + */ + + inline void clear() { VectorImpl::clear(); } + + /*! + * vector stats + */ + + //! returns number of items in the vector + inline size_t size() const { return VectorImpl::size(); } + //! returns wether or not the vector is empty + inline bool isEmpty() const { return VectorImpl::isEmpty(); } + //! returns how many items can be stored without reallocating the backing store + inline size_t capacity() const { return VectorImpl::capacity(); } + //! setst the capacity. capacity can never be reduced less than size() + inline ssize_t setCapacity(size_t size) { return VectorImpl::setCapacity(size); } + + /*! + * C-style array access + */ + + //! read-only C-style access + inline const TYPE* array() const; + //! read-write C-style access + TYPE* editArray(); + + /*! + * accessors + */ + + //! read-only access to an item at a given index + inline const TYPE& operator [] (size_t index) const; + //! alternate name for operator [] + inline const TYPE& itemAt(size_t index) const; + //! stack-usage of the vector. returns the top of the stack (last element) + const TYPE& top() const; + //! same as operator [], but allows to access the vector backward (from the end) with a negative index + const TYPE& mirrorItemAt(ssize_t index) const; + + /*! + * modifing the array + */ + + //! copy-on write support, grants write access to an item + TYPE& editItemAt(size_t index); + //! grants right acces to the top of the stack (last element) + TYPE& editTop(); + + /*! + * append/insert another vector + */ + + //! insert another vector at a given index + ssize_t insertVectorAt(const Vector& vector, size_t index); + + //! append another vector at the end of this one + ssize_t appendVector(const Vector& vector); + + + /*! + * add/insert/replace items + */ + + //! insert one or several items initialized with their default constructor + inline ssize_t insertAt(size_t index, size_t numItems = 1); + //! insert on onr several items initialized from a prototype item + ssize_t insertAt(const TYPE& prototype_item, size_t index, size_t numItems = 1); + //! pop the top of the stack (removes the last element). No-op if the stack's empty + inline void pop(); + //! pushes an item initialized with its default constructor + inline void push(); + //! pushes an item on the top of the stack + void push(const TYPE& item); + //! same as push() but returns the index the item was added at (or an error) + inline ssize_t add(); + //! same as push() but returns the index the item was added at (or an error) + ssize_t add(const TYPE& item); + //! replace an item with a new one initialized with its default constructor + inline ssize_t replaceAt(size_t index); + //! replace an item with a new one + ssize_t replaceAt(const TYPE& item, size_t index); + + /*! + * remove items + */ + + //! remove several items + inline ssize_t removeItemsAt(size_t index, size_t count = 1); + //! remove one item + inline ssize_t removeAt(size_t index) { return removeItemsAt(index); } + + /*! + * sort (stable) the array + */ + + typedef int (*compar_t)(const TYPE* lhs, const TYPE* rhs); + typedef int (*compar_r_t)(const TYPE* lhs, const TYPE* rhs, void* state); + + inline status_t sort(compar_t cmp); + inline status_t sort(compar_r_t cmp, void* state); + +protected: + virtual void do_construct(void* storage, size_t num) const; + virtual void do_destroy(void* storage, size_t num) const; + virtual void do_copy(void* dest, const void* from, size_t num) const; + virtual void do_splat(void* dest, const void* item, size_t num) const; + virtual void do_move_forward(void* dest, const void* from, size_t num) const; + virtual void do_move_backward(void* dest, const void* from, size_t num) const; +}; + + +// --------------------------------------------------------------------------- +// No user serviceable parts from here... +// --------------------------------------------------------------------------- + +template inline +Vector::Vector() + : VectorImpl(sizeof(TYPE), + ((traits::has_trivial_ctor ? HAS_TRIVIAL_CTOR : 0) + |(traits::has_trivial_dtor ? HAS_TRIVIAL_DTOR : 0) + |(traits::has_trivial_copy ? HAS_TRIVIAL_COPY : 0) + |(traits::has_trivial_assign ? HAS_TRIVIAL_ASSIGN : 0)) + ) +{ +} + +template inline +Vector::Vector(const Vector& rhs) + : VectorImpl(rhs) { +} + +template inline +Vector::~Vector() { + finish_vector(); +} + +template inline +Vector& Vector::operator = (const Vector& rhs) { + VectorImpl::operator = (rhs); + return *this; +} + +template inline +const Vector& Vector::operator = (const Vector& rhs) const { + VectorImpl::operator = (rhs); + return *this; +} + +template inline +const TYPE* Vector::array() const { + return static_cast(arrayImpl()); +} + +template inline +TYPE* Vector::editArray() { + return static_cast(editArrayImpl()); +} + + +template inline +const TYPE& Vector::operator[](size_t index) const { + LOG_FATAL_IF( index>=size(), + "itemAt: index %d is past size %d", (int)index, (int)size() ); + return *(array() + index); +} + +template inline +const TYPE& Vector::itemAt(size_t index) const { + return operator[](index); +} + +template inline +const TYPE& Vector::mirrorItemAt(ssize_t index) const { + LOG_FATAL_IF( (index>0 ? index : -index)>=size(), + "mirrorItemAt: index %d is past size %d", + (int)index, (int)size() ); + return *(array() + ((index<0) ? (size()-index) : index)); +} + +template inline +const TYPE& Vector::top() const { + return *(array() + size() - 1); +} + +template inline +TYPE& Vector::editItemAt(size_t index) { + return *( static_cast(editItemLocation(index)) ); +} + +template inline +TYPE& Vector::editTop() { + return *( static_cast(editItemLocation(size()-1)) ); +} + +template inline +ssize_t Vector::insertVectorAt(const Vector& vector, size_t index) { + return VectorImpl::insertVectorAt(reinterpret_cast(vector), index); +} + +template inline +ssize_t Vector::appendVector(const Vector& vector) { + return VectorImpl::appendVector(reinterpret_cast(vector)); +} + +template inline +ssize_t Vector::insertAt(const TYPE& item, size_t index, size_t numItems) { + return VectorImpl::insertAt(&item, index, numItems); +} + +template inline +void Vector::push(const TYPE& item) { + return VectorImpl::push(&item); +} + +template inline +ssize_t Vector::add(const TYPE& item) { + return VectorImpl::add(&item); +} + +template inline +ssize_t Vector::replaceAt(const TYPE& item, size_t index) { + return VectorImpl::replaceAt(&item, index); +} + +template inline +ssize_t Vector::insertAt(size_t index, size_t numItems) { + return VectorImpl::insertAt(index, numItems); +} + +template inline +void Vector::pop() { + VectorImpl::pop(); +} + +template inline +void Vector::push() { + VectorImpl::push(); +} + +template inline +ssize_t Vector::add() { + return VectorImpl::add(); +} + +template inline +ssize_t Vector::replaceAt(size_t index) { + return VectorImpl::replaceAt(index); +} + +template inline +ssize_t Vector::removeItemsAt(size_t index, size_t count) { + return VectorImpl::removeItemsAt(index, count); +} + +template inline +status_t Vector::sort(Vector::compar_t cmp) { + return VectorImpl::sort((VectorImpl::compar_t)cmp); +} + +template inline +status_t Vector::sort(Vector::compar_r_t cmp, void* state) { + return VectorImpl::sort((VectorImpl::compar_r_t)cmp, state); +} + +// --------------------------------------------------------------------------- + +template +void Vector::do_construct(void* storage, size_t num) const { + construct_type( reinterpret_cast(storage), num ); +} + +template +void Vector::do_destroy(void* storage, size_t num) const { + destroy_type( reinterpret_cast(storage), num ); +} + +template +void Vector::do_copy(void* dest, const void* from, size_t num) const { + copy_type( reinterpret_cast(dest), reinterpret_cast(from), num ); +} + +template +void Vector::do_splat(void* dest, const void* item, size_t num) const { + splat_type( reinterpret_cast(dest), reinterpret_cast(item), num ); +} + +template +void Vector::do_move_forward(void* dest, const void* from, size_t num) const { + move_forward_type( reinterpret_cast(dest), reinterpret_cast(from), num ); +} + +template +void Vector::do_move_backward(void* dest, const void* from, size_t num) const { + move_backward_type( reinterpret_cast(dest), reinterpret_cast(from), num ); +} + +}; // namespace android + + +// --------------------------------------------------------------------------- + +#endif // ANDROID_VECTOR_H diff --git a/include/utils/VectorImpl.h b/include/utils/VectorImpl.h new file mode 100644 index 000000000..2525229be --- /dev/null +++ b/include/utils/VectorImpl.h @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2005 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_VECTOR_IMPL_H +#define ANDROID_VECTOR_IMPL_H + +#include +#include +#include +#include + +// --------------------------------------------------------------------------- +// No user serviceable parts in here... +// --------------------------------------------------------------------------- + +namespace android { + +/*! + * Implementation of the guts of the vector<> class + * this ensures backward binary compatibility and + * reduces code size. + * For performance reasons, we expose mStorage and mCount + * so these fields are set in stone. + * + */ + +class VectorImpl +{ +public: + enum { // flags passed to the ctor + HAS_TRIVIAL_CTOR = 0x00000001, + HAS_TRIVIAL_DTOR = 0x00000002, + HAS_TRIVIAL_COPY = 0x00000004, + HAS_TRIVIAL_ASSIGN = 0x00000008 + }; + + VectorImpl(size_t itemSize, uint32_t flags); + VectorImpl(const VectorImpl& rhs); + virtual ~VectorImpl(); + + /*! must be called from subclasses destructor */ + void finish_vector(); + + VectorImpl& operator = (const VectorImpl& rhs); + + /*! C-style array access */ + inline const void* arrayImpl() const { return mStorage; } + void* editArrayImpl(); + + /*! vector stats */ + inline size_t size() const { return mCount; } + inline bool isEmpty() const { return mCount == 0; } + size_t capacity() const; + ssize_t setCapacity(size_t size); + + /*! append/insert another vector */ + ssize_t insertVectorAt(const VectorImpl& vector, size_t index); + ssize_t appendVector(const VectorImpl& vector); + + /*! add/insert/replace items */ + ssize_t insertAt(size_t where, size_t numItems = 1); + ssize_t insertAt(const void* item, size_t where, size_t numItems = 1); + void pop(); + void push(); + void push(const void* item); + ssize_t add(); + ssize_t add(const void* item); + ssize_t replaceAt(size_t index); + ssize_t replaceAt(const void* item, size_t index); + + /*! remove items */ + ssize_t removeItemsAt(size_t index, size_t count = 1); + void clear(); + + const void* itemLocation(size_t index) const; + void* editItemLocation(size_t index); + + typedef int (*compar_t)(const void* lhs, const void* rhs); + typedef int (*compar_r_t)(const void* lhs, const void* rhs, void* state); + status_t sort(compar_t cmp); + status_t sort(compar_r_t cmp, void* state); + +protected: + size_t itemSize() const; + void release_storage(); + + virtual void do_construct(void* storage, size_t num) const = 0; + virtual void do_destroy(void* storage, size_t num) const = 0; + virtual void do_copy(void* dest, const void* from, size_t num) const = 0; + virtual void do_splat(void* dest, const void* item, size_t num) const = 0; + virtual void do_move_forward(void* dest, const void* from, size_t num) const = 0; + virtual void do_move_backward(void* dest, const void* from, size_t num) const = 0; + + // take care of FBC... + virtual void reservedVectorImpl1(); + virtual void reservedVectorImpl2(); + virtual void reservedVectorImpl3(); + virtual void reservedVectorImpl4(); + virtual void reservedVectorImpl5(); + virtual void reservedVectorImpl6(); + virtual void reservedVectorImpl7(); + virtual void reservedVectorImpl8(); + +private: + void* _grow(size_t where, size_t amount); + void _shrink(size_t where, size_t amount); + + inline void _do_construct(void* storage, size_t num) const; + inline void _do_destroy(void* storage, size_t num) const; + inline void _do_copy(void* dest, const void* from, size_t num) const; + inline void _do_splat(void* dest, const void* item, size_t num) const; + inline void _do_move_forward(void* dest, const void* from, size_t num) const; + inline void _do_move_backward(void* dest, const void* from, size_t num) const; + + // These 2 fields are exposed in the inlines below, + // so they're set in stone. + void * mStorage; // base address of the vector + size_t mCount; // number of items + + const uint32_t mFlags; + const size_t mItemSize; +}; + + + +class SortedVectorImpl : public VectorImpl +{ +public: + SortedVectorImpl(size_t itemSize, uint32_t flags); + SortedVectorImpl(const VectorImpl& rhs); + virtual ~SortedVectorImpl(); + + SortedVectorImpl& operator = (const SortedVectorImpl& rhs); + + //! finds the index of an item + ssize_t indexOf(const void* item) const; + + //! finds where this item should be inserted + size_t orderOf(const void* item) const; + + //! add an item in the right place (or replaces it if there is one) + ssize_t add(const void* item); + + //! merges a vector into this one + ssize_t merge(const VectorImpl& vector); + ssize_t merge(const SortedVectorImpl& vector); + + //! removes an item + ssize_t remove(const void* item); + +protected: + virtual int do_compare(const void* lhs, const void* rhs) const = 0; + + // take care of FBC... + virtual void reservedSortedVectorImpl1(); + virtual void reservedSortedVectorImpl2(); + virtual void reservedSortedVectorImpl3(); + virtual void reservedSortedVectorImpl4(); + virtual void reservedSortedVectorImpl5(); + virtual void reservedSortedVectorImpl6(); + virtual void reservedSortedVectorImpl7(); + virtual void reservedSortedVectorImpl8(); + +private: + ssize_t _indexOrderOf(const void* item, size_t* order = 0) const; + + // these are made private, because they can't be used on a SortedVector + // (they don't have an implementation either) + ssize_t add(); + void pop(); + void push(); + void push(const void* item); + ssize_t insertVectorAt(const VectorImpl& vector, size_t index); + ssize_t appendVector(const VectorImpl& vector); + ssize_t insertAt(size_t where, size_t numItems = 1); + ssize_t insertAt(const void* item, size_t where, size_t numItems = 1); + ssize_t replaceAt(size_t index); + ssize_t replaceAt(const void* item, size_t index); +}; + +}; // namespace android + + +// --------------------------------------------------------------------------- + +#endif // ANDROID_VECTOR_IMPL_H diff --git a/include/utils/ZipEntry.h b/include/utils/ZipEntry.h new file mode 100644 index 000000000..e4698dfbb --- /dev/null +++ b/include/utils/ZipEntry.h @@ -0,0 +1,345 @@ +/* + * Copyright (C) 2006 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. + */ + +// +// Zip archive entries. +// +// The ZipEntry class is tightly meshed with the ZipFile class. +// +#ifndef __LIBS_ZIPENTRY_H +#define __LIBS_ZIPENTRY_H + +#include "Errors.h" + +#include +#include + +namespace android { + +class ZipFile; + +/* + * ZipEntry objects represent a single entry in a Zip archive. + * + * You can use one of these to get or set information about an entry, but + * there are no functions here for accessing the data itself. (We could + * tuck a pointer to the ZipFile in here for convenience, but that raises + * the likelihood of using ZipEntry objects after discarding the ZipFile.) + * + * File information is stored in two places: next to the file data (the Local + * File Header, and possibly a Data Descriptor), and at the end of the file + * (the Central Directory Entry). The two must be kept in sync. + */ +class ZipEntry { +public: + friend class ZipFile; + + ZipEntry(void) + : mDeleted(false), mMarked(false) + {} + ~ZipEntry(void) {} + + /* + * Returns "true" if the data is compressed. + */ + bool isCompressed(void) const { + return mCDE.mCompressionMethod != kCompressStored; + } + int getCompressionMethod(void) const { return mCDE.mCompressionMethod; } + + /* + * Return the uncompressed length. + */ + off_t getUncompressedLen(void) const { return mCDE.mUncompressedSize; } + + /* + * Return the compressed length. For uncompressed data, this returns + * the same thing as getUncompresesdLen(). + */ + off_t getCompressedLen(void) const { return mCDE.mCompressedSize; } + + /* + * Return the absolute file offset of the start of the compressed or + * uncompressed data. + */ + off_t getFileOffset(void) const { + return mCDE.mLocalHeaderRelOffset + + LocalFileHeader::kLFHLen + + mLFH.mFileNameLength + + mLFH.mExtraFieldLength; + } + + /* + * Return the data CRC. + */ + unsigned long getCRC32(void) const { return mCDE.mCRC32; } + + /* + * Return file modification time in UNIX seconds-since-epoch. + */ + time_t getModWhen(void) const; + + /* + * Return the archived file name. + */ + const char* getFileName(void) const { return (const char*) mCDE.mFileName; } + + /* + * Application-defined "mark". Can be useful when synchronizing the + * contents of an archive with contents on disk. + */ + bool getMarked(void) const { return mMarked; } + void setMarked(bool val) { mMarked = val; } + + /* + * Some basic functions for raw data manipulation. "LE" means + * Little Endian. + */ + static inline unsigned short getShortLE(const unsigned char* buf) { + return buf[0] | (buf[1] << 8); + } + static inline unsigned long getLongLE(const unsigned char* buf) { + return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); + } + static inline void putShortLE(unsigned char* buf, short val) { + buf[0] = (unsigned char) val; + buf[1] = (unsigned char) (val >> 8); + } + static inline void putLongLE(unsigned char* buf, long val) { + buf[0] = (unsigned char) val; + buf[1] = (unsigned char) (val >> 8); + buf[2] = (unsigned char) (val >> 16); + buf[3] = (unsigned char) (val >> 24); + } + + /* defined for Zip archives */ + enum { + kCompressStored = 0, // no compression + // shrunk = 1, + // reduced 1 = 2, + // reduced 2 = 3, + // reduced 3 = 4, + // reduced 4 = 5, + // imploded = 6, + // tokenized = 7, + kCompressDeflated = 8, // standard deflate + // Deflate64 = 9, + // lib imploded = 10, + // reserved = 11, + // bzip2 = 12, + }; + + /* + * Deletion flag. If set, the entry will be removed on the next + * call to "flush". + */ + bool getDeleted(void) const { return mDeleted; } + +protected: + /* + * Initialize the structure from the file, which is pointing at + * our Central Directory entry. + */ + status_t initFromCDE(FILE* fp); + + /* + * Initialize the structure for a new file. We need the filename + * and comment so that we can properly size the LFH area. The + * filename is mandatory, the comment is optional. + */ + void initNew(const char* fileName, const char* comment); + + /* + * Initialize the structure with the contents of a ZipEntry from + * another file. + */ + status_t initFromExternal(const ZipFile* pZipFile, const ZipEntry* pEntry); + + /* + * Add some pad bytes to the LFH. We do this by adding or resizing + * the "extra" field. + */ + status_t addPadding(int padding); + + /* + * Set information about the data for this entry. + */ + void setDataInfo(long uncompLen, long compLen, unsigned long crc32, + int compressionMethod); + + /* + * Set the modification date. + */ + void setModWhen(time_t when); + + /* + * Return the offset of the local file header. + */ + off_t getLFHOffset(void) const { return mCDE.mLocalHeaderRelOffset; } + + /* + * Set the offset of the local file header, relative to the start of + * the current file. + */ + void setLFHOffset(off_t offset) { + mCDE.mLocalHeaderRelOffset = (long) offset; + } + + /* mark for deletion; used by ZipFile::remove() */ + void setDeleted(void) { mDeleted = true; } + +private: + /* these are private and not defined */ + ZipEntry(const ZipEntry& src); + ZipEntry& operator=(const ZipEntry& src); + + /* returns "true" if the CDE and the LFH agree */ + bool compareHeaders(void) const; + void copyCDEtoLFH(void); + + bool mDeleted; // set if entry is pending deletion + bool mMarked; // app-defined marker + + /* + * Every entry in the Zip archive starts off with one of these. + */ + class LocalFileHeader { + public: + LocalFileHeader(void) : + mVersionToExtract(0), + mGPBitFlag(0), + mCompressionMethod(0), + mLastModFileTime(0), + mLastModFileDate(0), + mCRC32(0), + mCompressedSize(0), + mUncompressedSize(0), + mFileNameLength(0), + mExtraFieldLength(0), + mFileName(NULL), + mExtraField(NULL) + {} + virtual ~LocalFileHeader(void) { + delete[] mFileName; + delete[] mExtraField; + } + + status_t read(FILE* fp); + status_t write(FILE* fp); + + // unsigned long mSignature; + unsigned short mVersionToExtract; + unsigned short mGPBitFlag; + unsigned short mCompressionMethod; + unsigned short mLastModFileTime; + unsigned short mLastModFileDate; + unsigned long mCRC32; + unsigned long mCompressedSize; + unsigned long mUncompressedSize; + unsigned short mFileNameLength; + unsigned short mExtraFieldLength; + unsigned char* mFileName; + unsigned char* mExtraField; + + enum { + kSignature = 0x04034b50, + kLFHLen = 30, // LocalFileHdr len, excl. var fields + }; + + void dump(void) const; + }; + + /* + * Every entry in the Zip archive has one of these in the "central + * directory" at the end of the file. + */ + class CentralDirEntry { + public: + CentralDirEntry(void) : + mVersionMadeBy(0), + mVersionToExtract(0), + mGPBitFlag(0), + mCompressionMethod(0), + mLastModFileTime(0), + mLastModFileDate(0), + mCRC32(0), + mCompressedSize(0), + mUncompressedSize(0), + mFileNameLength(0), + mExtraFieldLength(0), + mFileCommentLength(0), + mDiskNumberStart(0), + mInternalAttrs(0), + mExternalAttrs(0), + mLocalHeaderRelOffset(0), + mFileName(NULL), + mExtraField(NULL), + mFileComment(NULL) + {} + virtual ~CentralDirEntry(void) { + delete[] mFileName; + delete[] mExtraField; + delete[] mFileComment; + } + + status_t read(FILE* fp); + status_t write(FILE* fp); + + // unsigned long mSignature; + unsigned short mVersionMadeBy; + unsigned short mVersionToExtract; + unsigned short mGPBitFlag; + unsigned short mCompressionMethod; + unsigned short mLastModFileTime; + unsigned short mLastModFileDate; + unsigned long mCRC32; + unsigned long mCompressedSize; + unsigned long mUncompressedSize; + unsigned short mFileNameLength; + unsigned short mExtraFieldLength; + unsigned short mFileCommentLength; + unsigned short mDiskNumberStart; + unsigned short mInternalAttrs; + unsigned long mExternalAttrs; + unsigned long mLocalHeaderRelOffset; + unsigned char* mFileName; + unsigned char* mExtraField; + unsigned char* mFileComment; + + void dump(void) const; + + enum { + kSignature = 0x02014b50, + kCDELen = 46, // CentralDirEnt len, excl. var fields + }; + }; + + enum { + //kDataDescriptorSignature = 0x08074b50, // currently unused + kDataDescriptorLen = 16, // four 32-bit fields + + kDefaultVersion = 20, // need deflate, nothing much else + kDefaultMadeBy = 0x0317, // 03=UNIX, 17=spec v2.3 + kUsesDataDescr = 0x0008, // GPBitFlag bit 3 + }; + + LocalFileHeader mLFH; + CentralDirEntry mCDE; +}; + +}; // namespace android + +#endif // __LIBS_ZIPENTRY_H diff --git a/include/utils/ZipFile.h b/include/utils/ZipFile.h new file mode 100644 index 000000000..44df5bbaa --- /dev/null +++ b/include/utils/ZipFile.h @@ -0,0 +1,269 @@ +/* + * Copyright (C) 2006 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. + */ + +// +// General-purpose Zip archive access. This class allows both reading and +// writing to Zip archives, including deletion of existing entries. +// +#ifndef __LIBS_ZIPFILE_H +#define __LIBS_ZIPFILE_H + +#include "ZipEntry.h" +#include "Vector.h" +#include "Errors.h" +#include + +namespace android { + +/* + * Manipulate a Zip archive. + * + * Some changes will not be visible in the until until "flush" is called. + * + * The correct way to update a file archive is to make all changes to a + * copy of the archive in a temporary file, and then unlink/rename over + * the original after everything completes. Because we're only interested + * in using this for packaging, we don't worry about such things. Crashing + * after making changes and before flush() completes could leave us with + * an unusable Zip archive. + */ +class ZipFile { +public: + ZipFile(void) + : mZipFp(NULL), mReadOnly(false), mNeedCDRewrite(false) + {} + ~ZipFile(void) { + if (!mReadOnly) + flush(); + if (mZipFp != NULL) + fclose(mZipFp); + discardEntries(); + } + + /* + * Open a new or existing archive. + */ + typedef enum { + kOpenReadOnly = 0x01, + kOpenReadWrite = 0x02, + kOpenCreate = 0x04, // create if it doesn't exist + kOpenTruncate = 0x08, // if it exists, empty it + }; + status_t open(const char* zipFileName, int flags); + + /* + * Add a file to the end of the archive. Specify whether you want the + * library to try to store it compressed. + * + * If "storageName" is specified, the archive will use that instead + * of "fileName". + * + * If there is already an entry with the same name, the call fails. + * Existing entries with the same name must be removed first. + * + * If "ppEntry" is non-NULL, a pointer to the new entry will be returned. + */ + status_t add(const char* fileName, int compressionMethod, + ZipEntry** ppEntry) + { + return add(fileName, fileName, compressionMethod, ppEntry); + } + status_t add(const char* fileName, const char* storageName, + int compressionMethod, ZipEntry** ppEntry) + { + return addCommon(fileName, NULL, 0, storageName, + ZipEntry::kCompressStored, + compressionMethod, ppEntry); + } + + /* + * Add a file that is already compressed with gzip. + * + * If "ppEntry" is non-NULL, a pointer to the new entry will be returned. + */ + status_t addGzip(const char* fileName, const char* storageName, + ZipEntry** ppEntry) + { + return addCommon(fileName, NULL, 0, storageName, + ZipEntry::kCompressDeflated, + ZipEntry::kCompressDeflated, ppEntry); + } + + /* + * Add a file from an in-memory data buffer. + * + * If "ppEntry" is non-NULL, a pointer to the new entry will be returned. + */ + status_t add(const void* data, size_t size, const char* storageName, + int compressionMethod, ZipEntry** ppEntry) + { + return addCommon(NULL, data, size, storageName, + ZipEntry::kCompressStored, + compressionMethod, ppEntry); + } + + /* + * Add an entry by copying it from another zip file. If "padding" is + * nonzero, the specified number of bytes will be added to the "extra" + * field in the header. + * + * If "ppEntry" is non-NULL, a pointer to the new entry will be returned. + */ + status_t add(const ZipFile* pSourceZip, const ZipEntry* pSourceEntry, + int padding, ZipEntry** ppEntry); + + /* + * Mark an entry as having been removed. It is not actually deleted + * from the archive or our internal data structures until flush() is + * called. + */ + status_t remove(ZipEntry* pEntry); + + /* + * Flush changes. If mNeedCDRewrite is set, this writes the central dir. + */ + status_t flush(void); + + /* + * Expand the data into the buffer provided. The buffer must hold + * at least bytes. Variation expands directly + * to a file. + * + * Returns "false" if an error was encountered in the compressed data. + */ + //bool uncompress(const ZipEntry* pEntry, void* buf) const; + //bool uncompress(const ZipEntry* pEntry, FILE* fp) const; + void* uncompress(const ZipEntry* pEntry); + + /* + * Get an entry, by name. Returns NULL if not found. + * + * Does not return entries pending deletion. + */ + ZipEntry* getEntryByName(const char* fileName) const; + + /* + * Get the Nth entry in the archive. + * + * This will return an entry that is pending deletion. + */ + int getNumEntries(void) const { return mEntries.size(); } + ZipEntry* getEntryByIndex(int idx) const; + +private: + /* these are private and not defined */ + ZipFile(const ZipFile& src); + ZipFile& operator=(const ZipFile& src); + + class EndOfCentralDir { + public: + EndOfCentralDir(void) : + mDiskNumber(0), + mDiskWithCentralDir(0), + mNumEntries(0), + mTotalNumEntries(0), + mCentralDirSize(0), + mCentralDirOffset(0), + mCommentLen(0), + mComment(NULL) + {} + virtual ~EndOfCentralDir(void) { + delete[] mComment; + } + + status_t readBuf(const unsigned char* buf, int len); + status_t write(FILE* fp); + + //unsigned long mSignature; + unsigned short mDiskNumber; + unsigned short mDiskWithCentralDir; + unsigned short mNumEntries; + unsigned short mTotalNumEntries; + unsigned long mCentralDirSize; + unsigned long mCentralDirOffset; // offset from first disk + unsigned short mCommentLen; + unsigned char* mComment; + + enum { + kSignature = 0x06054b50, + kEOCDLen = 22, // EndOfCentralDir len, excl. comment + + kMaxCommentLen = 65535, // longest possible in ushort + kMaxEOCDSearch = kMaxCommentLen + EndOfCentralDir::kEOCDLen, + + }; + + void dump(void) const; + }; + + + /* read all entries in the central dir */ + status_t readCentralDir(void); + + /* crunch deleted entries out */ + status_t crunchArchive(void); + + /* clean up mEntries */ + void discardEntries(void); + + /* common handler for all "add" functions */ + status_t addCommon(const char* fileName, const void* data, size_t size, + const char* storageName, int sourceType, int compressionMethod, + ZipEntry** ppEntry); + + /* copy all of "srcFp" into "dstFp" */ + status_t copyFpToFp(FILE* dstFp, FILE* srcFp, unsigned long* pCRC32); + /* copy all of "data" into "dstFp" */ + status_t copyDataToFp(FILE* dstFp, + const void* data, size_t size, unsigned long* pCRC32); + /* copy some of "srcFp" into "dstFp" */ + status_t copyPartialFpToFp(FILE* dstFp, FILE* srcFp, long length, + unsigned long* pCRC32); + /* like memmove(), but on parts of a single file */ + status_t filemove(FILE* fp, off_t dest, off_t src, size_t n); + /* compress all of "srcFp" into "dstFp", using Deflate */ + status_t compressFpToFp(FILE* dstFp, FILE* srcFp, + const void* data, size_t size, unsigned long* pCRC32); + + /* get modification date from a file descriptor */ + time_t getModTime(int fd); + + /* + * We use stdio FILE*, which gives us buffering but makes dealing + * with files >2GB awkward. Until we support Zip64, we're fine. + */ + FILE* mZipFp; // Zip file pointer + + /* one of these per file */ + EndOfCentralDir mEOCD; + + /* did we open this read-only? */ + bool mReadOnly; + + /* set this when we trash the central dir */ + bool mNeedCDRewrite; + + /* + * One ZipEntry per entry in the zip file. I'm using pointers instead + * of objects because it's easier than making operator= work for the + * classes and sub-classes. + */ + Vector mEntries; +}; + +}; // namespace android + +#endif // __LIBS_ZIPFILE_H diff --git a/include/utils/ZipFileCRO.h b/include/utils/ZipFileCRO.h new file mode 100644 index 000000000..30e00368e --- /dev/null +++ b/include/utils/ZipFileCRO.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// C API for ead-only access to Zip archives, with minimal heap allocation. +// +#ifndef __LIBS_ZIPFILECRO_H +#define __LIBS_ZIPFILECRO_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Trivial typedef to ensure that ZipFileCRO is not treated as a simple integer. + */ +typedef void* ZipFileCRO; + +/* + * Trivial typedef to ensure that ZipEntryCRO is not treated as a simple + * integer. We use NULL to indicate an invalid value. + */ +typedef void* ZipEntryCRO; + +extern ZipFileCRO ZipFileXRO_open(const char* path); + +extern void ZipFileCRO_destroy(ZipFileCRO zip); + +extern ZipEntryCRO ZipFileCRO_findEntryByName(ZipFileCRO zip, + const char* fileName); + +extern bool ZipFileCRO_getEntryInfo(ZipFileCRO zip, ZipEntryCRO entry, + int* pMethod, long* pUncompLen, + long* pCompLen, off_t* pOffset, long* pModWhen, long* pCrc32); + +extern bool ZipFileCRO_uncompressEntry(ZipFileCRO zip, ZipEntryCRO entry, int fd); + +#ifdef __cplusplus +} +#endif + +#endif /*__LIBS_ZIPFILECRO_H*/ diff --git a/include/utils/ZipFileRO.h b/include/utils/ZipFileRO.h new file mode 100644 index 000000000..51c4f2fb6 --- /dev/null +++ b/include/utils/ZipFileRO.h @@ -0,0 +1,222 @@ +/* + * 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. + */ + +// +// Read-only access to Zip archives, with minimal heap allocation. +// +// This is similar to the more-complete ZipFile class, but no attempt +// has been made to make them interchangeable. This class operates under +// a very different set of assumptions and constraints. +// +#ifndef __LIBS_ZIPFILERO_H +#define __LIBS_ZIPFILERO_H + +#include "Errors.h" +#include "FileMap.h" + +#include +#include +#include + +namespace android { + +/* + * Trivial typedef to ensure that ZipEntryRO is not treated as a simple + * integer. We use NULL to indicate an invalid value. + */ +typedef void* ZipEntryRO; + +/* + * Open a Zip archive for reading. + * + * We want "open" and "find entry by name" to be fast operations, and we + * want to use as little memory as possible. We memory-map the file, + * and load a hash table with pointers to the filenames (which aren't + * null-terminated). The other fields are at a fixed offset from the + * filename, so we don't need to extract those (but we do need to byte-read + * and endian-swap them every time we want them). + * + * To speed comparisons when doing a lookup by name, we could make the mapping + * "private" (copy-on-write) and null-terminate the filenames after verifying + * the record structure. However, this requires a private mapping of + * every page that the Central Directory touches. Easier to tuck a copy + * of the string length into the hash table entry. + */ +class ZipFileRO { +public: + ZipFileRO() + : mFd(-1), mFileMap(NULL), mHashTableSize(-1), mHashTable(NULL) + {} + ~ZipFileRO() { + free(mHashTable); + if (mFileMap) + mFileMap->release(); + if (mFd >= 0) + close(mFd); + } + + /* + * Open an archive. + */ + status_t open(const char* zipFileName); + + /* + * Find an entry, by name. Returns the entry identifier, or NULL if + * not found. + * + * If two entries have the same name, one will be chosen at semi-random. + */ + ZipEntryRO findEntryByName(const char* fileName) const; + + /* + * Return the #of entries in the Zip archive. + */ + int getNumEntries(void) const { + return mNumEntries; + } + + /* + * Return the Nth entry. Zip file entries are not stored in sorted + * order, and updated entries may appear at the end, so anyone walking + * the archive needs to avoid making ordering assumptions. We take + * that further by returning the Nth non-empty entry in the hash table + * rather than the Nth entry in the archive. + * + * Valid values are [0..numEntries). + * + * [This is currently O(n). If it needs to be fast we can allocate an + * additional data structure or provide an iterator interface.] + */ + ZipEntryRO findEntryByIndex(int idx) const; + + /* + * Copy the filename into the supplied buffer. Returns 0 on success, + * -1 if "entry" is invalid, or the filename length if it didn't fit. The + * length, and the returned string, include the null-termination. + */ + int getEntryFileName(ZipEntryRO entry, char* buffer, int bufLen) const; + + /* + * Get the vital stats for an entry. Pass in NULL pointers for anything + * you don't need. + * + * "*pOffset" holds the Zip file offset of the entry's data. + * + * Returns "false" if "entry" is bogus or if the data in the Zip file + * appears to be bad. + */ + bool getEntryInfo(ZipEntryRO entry, int* pMethod, long* pUncompLen, + long* pCompLen, off_t* pOffset, long* pModWhen, long* pCrc32) const; + + /* + * Create a new FileMap object that maps a subset of the archive. For + * an uncompressed entry this effectively provides a pointer to the + * actual data, for a compressed entry this provides the input buffer + * for inflate(). + */ + FileMap* createEntryFileMap(ZipEntryRO entry) const; + + /* + * Uncompress the data into a buffer. Depending on the compression + * format, this is either an "inflate" operation or a memcpy. + * + * Use "uncompLen" from getEntryInfo() to determine the required + * buffer size. + * + * Returns "true" on success. + */ + bool uncompressEntry(ZipEntryRO entry, void* buffer) const; + + /* + * Uncompress the data to an open file descriptor. + */ + bool uncompressEntry(ZipEntryRO entry, int fd) const; + + /* Zip compression methods we support */ + enum { + kCompressStored = 0, // no compression + kCompressDeflated = 8, // standard deflate + }; + + /* + * Utility function: uncompress deflated data, buffer to buffer. + */ + static bool inflateBuffer(void* outBuf, const void* inBuf, + long uncompLen, long compLen); + + /* + * Utility function: uncompress deflated data, buffer to fd. + */ + static bool inflateBuffer(int fd, const void* inBuf, + long uncompLen, long compLen); + + /* + * Some basic functions for raw data manipulation. "LE" means + * Little Endian. + */ + static inline unsigned short get2LE(const unsigned char* buf) { + return buf[0] | (buf[1] << 8); + } + static inline unsigned long get4LE(const unsigned char* buf) { + return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); + } + +private: + /* these are private and not defined */ + ZipFileRO(const ZipFileRO& src); + ZipFileRO& operator=(const ZipFileRO& src); + + /* parse the archive, prepping internal structures */ + bool parseZipArchive(void); + + /* add a new entry to the hash table */ + void addToHash(const char* str, int strLen, unsigned int hash); + + /* compute string hash code */ + static unsigned int computeHash(const char* str, int len); + + /* convert a ZipEntryRO back to a hash table index */ + int entryToIndex(const ZipEntryRO entry) const; + + /* + * One entry in the hash table. + */ + typedef struct HashEntry { + const char* name; + unsigned short nameLen; + //unsigned int hash; + } HashEntry; + + /* open Zip archive */ + int mFd; + + /* mapped file */ + FileMap* mFileMap; + + /* number of entries in the Zip archive */ + int mNumEntries; + + /* + * We know how many entries are in the Zip archive, so we have a + * fixed-size hash table. We probe for an empty slot. + */ + int mHashTableSize; + HashEntry* mHashTable; +}; + +}; // namespace android + +#endif /*__LIBS_ZIPFILERO_H*/ diff --git a/include/utils/ZipUtils.h b/include/utils/ZipUtils.h new file mode 100644 index 000000000..42c42b6c0 --- /dev/null +++ b/include/utils/ZipUtils.h @@ -0,0 +1,67 @@ +/* + * 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. + */ + +// +// Miscellaneous zip/gzip utility functions. +// +#ifndef __LIBS_ZIPUTILS_H +#define __LIBS_ZIPUTILS_H + +#include + +namespace android { + +/* + * Container class for utility functions, primarily for namespace reasons. + */ +class ZipUtils { +public: + /* + * General utility function for uncompressing "deflate" data from a file + * to a buffer. + */ + static bool inflateToBuffer(int fd, void* buf, long uncompressedLen, + long compressedLen); + static bool inflateToBuffer(FILE* fp, void* buf, long uncompressedLen, + long compressedLen); + + /* + * Someday we might want to make this generic and handle bzip2 ".bz2" + * files too. + * + * We could declare gzip to be a sub-class of zip that has exactly + * one always-compressed entry, but we currently want to treat Zip + * and gzip as distinct, so there's no value. + * + * The zlib library has some gzip utilities, but it has no interface + * for extracting the uncompressed length of the file (you do *not* + * want to gzseek to the end). + * + * Pass in a seeked file pointer for the gzip file. If this is a gzip + * file, we set our return values appropriately and return "true" with + * the file seeked to the start of the compressed data. + */ + static bool examineGzip(FILE* fp, int* pCompressionMethod, + long* pUncompressedLen, long* pCompressedLen, unsigned long* pCRC32); + +private: + ZipUtils() {} + ~ZipUtils() {} +}; + +}; // namespace android + +#endif /*__LIBS_ZIPUTILS_H*/ diff --git a/include/utils/ashmem.h b/include/utils/ashmem.h new file mode 100644 index 000000000..085477578 --- /dev/null +++ b/include/utils/ashmem.h @@ -0,0 +1,41 @@ +/* utils/ashmem.h + ** + ** Copyright 2008 The Android Open Source Project + ** + ** This file is dual licensed. It may be redistributed and/or modified + ** under the terms of the Apache 2.0 License OR version 2 of the GNU + ** General Public License. + */ + +#ifndef _UTILS_ASHMEM_H +#define _UTILS_ASHMEM_H + +#include +#include + +#define ASHMEM_NAME_LEN 256 + +#define ASHMEM_NAME_DEF "dev/ashmem" + +/* Return values from ASHMEM_PIN: Was the mapping purged while unpinned? */ +#define ASHMEM_NOT_REAPED 0 +#define ASHMEM_WAS_REAPED 1 + +/* Return values from ASHMEM_UNPIN: Is the mapping now pinned or unpinned? */ +#define ASHMEM_NOW_UNPINNED 0 +#define ASHMEM_NOW_PINNED 1 + +#define __ASHMEMIOC 0x77 + +#define ASHMEM_SET_NAME _IOW(__ASHMEMIOC, 1, char[ASHMEM_NAME_LEN]) +#define ASHMEM_GET_NAME _IOR(__ASHMEMIOC, 2, char[ASHMEM_NAME_LEN]) +#define ASHMEM_SET_SIZE _IOW(__ASHMEMIOC, 3, size_t) +#define ASHMEM_GET_SIZE _IO(__ASHMEMIOC, 4) +#define ASHMEM_SET_PROT_MASK _IOW(__ASHMEMIOC, 5, unsigned long) +#define ASHMEM_GET_PROT_MASK _IO(__ASHMEMIOC, 6) +#define ASHMEM_PIN _IO(__ASHMEMIOC, 7) +#define ASHMEM_UNPIN _IO(__ASHMEMIOC, 8) +#define ASHMEM_ISPINNED _IO(__ASHMEMIOC, 9) +#define ASHMEM_PURGE_ALL_CACHES _IO(__ASHMEMIOC, 10) + +#endif /* _UTILS_ASHMEM_H */ diff --git a/include/utils/executablepath.h b/include/utils/executablepath.h new file mode 100644 index 000000000..c979432ba --- /dev/null +++ b/include/utils/executablepath.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UTILS_EXECUTABLEPATH_H +#define _UTILS_EXECUTABLEPATH_H + +#include + +// returns the path to this executable +#if __cplusplus +extern "C" +#endif +void executablepath(char s[PATH_MAX]); + +#endif // _UTILS_EXECUTABLEPATH_H diff --git a/include/utils/inet_address.h b/include/utils/inet_address.h new file mode 100644 index 000000000..dbd8672e0 --- /dev/null +++ b/include/utils/inet_address.h @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2005 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. + */ + +// +// Internet address classes. Modeled after Java classes. +// +#ifndef _RUNTIME_INET_ADDRESS_H +#define _RUNTIME_INET_ADDRESS_H + +#ifdef HAVE_ANDROID_OS +#error DO NOT USE THIS FILE IN THE DEVICE BUILD +#endif + + +namespace android { + +/* + * This class holds Internet addresses. Perhaps more useful is its + * ability to look up addresses by name. + * + * Invoke one of the static factory methods to create a new object. + */ +class InetAddress { +public: + virtual ~InetAddress(void); + + // create from w.x.y.z or foo.bar.com notation + static InetAddress* getByName(const char* host); + + // copy-construction + InetAddress(const InetAddress& orig); + + const void* getAddress(void) const { return mAddress; } + int getAddressLength(void) const { return mLength; } + const char* getHostName(void) const { return mName; } + +private: + InetAddress(void); + // assignment (private) + InetAddress& operator=(const InetAddress& addr); + + // use a void* here so we don't have to expose actual socket headers + void* mAddress; // this is really a ptr to sockaddr_in + int mLength; + char* mName; +}; + + +/* + * Base class for socket addresses. + */ +class SocketAddress { +public: + SocketAddress() {} + virtual ~SocketAddress() {} +}; + + +/* + * Internet address class. This combines an InetAddress with a port. + */ +class InetSocketAddress : public SocketAddress { +public: + InetSocketAddress() : + mAddress(0), mPort(-1) + {} + ~InetSocketAddress(void) { + delete mAddress; + } + + // Create an address with a host wildcard (useful for servers). + bool create(int port); + // Create an address with the specified host and port. + bool create(const InetAddress* addr, int port); + // Create an address with the specified host and port. Does the + // hostname lookup. + bool create(const char* host, int port); + + const InetAddress* getAddress(void) const { return mAddress; } + const int getPort(void) const { return mPort; } + const char* getHostName(void) const { return mAddress->getHostName(); } + +private: + InetAddress* mAddress; + int mPort; +}; + +}; // namespace android + +#endif // _RUNTIME_INET_ADDRESS_H diff --git a/include/utils/misc.h b/include/utils/misc.h new file mode 100644 index 000000000..62e84b489 --- /dev/null +++ b/include/utils/misc.h @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2005 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. + */ + +// +// Handy utility functions and portability code. +// +#ifndef _LIBS_UTILS_MISC_H +#define _LIBS_UTILS_MISC_H + +#include +#include "utils/Endian.h" + +namespace android { + +/* get #of elements in a static array */ +#ifndef NELEM +# define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0]))) +#endif + +/* + * Make a copy of the string, using "new[]" instead of "malloc". Free the + * string with delete[]. + * + * Returns NULL if "str" is NULL. + */ +char* strdupNew(const char* str); + +/* + * Concatenate an argument vector into a single string. If argc is >= 0 + * it will be used; if it's < 0 then the last element in the arg vector + * must be NULL. + * + * This inserts a space between each argument. + * + * This does not automatically add double quotes around arguments with + * spaces in them. This practice is necessary for Win32, because Win32's + * CreateProcess call is stupid. + * + * The caller should delete[] the returned string. + */ +char* concatArgv(int argc, const char* const argv[]); + +/* + * Count up the number of arguments in "argv". The count does not include + * the final NULL entry. + */ +int countArgv(const char* const argv[]); + +/* + * Some utility functions for working with files. These could be made + * part of a "File" class. + */ +typedef enum FileType { + kFileTypeUnknown = 0, + kFileTypeNonexistent, // i.e. ENOENT + kFileTypeRegular, + kFileTypeDirectory, + kFileTypeCharDev, + kFileTypeBlockDev, + kFileTypeFifo, + kFileTypeSymlink, + kFileTypeSocket, +} FileType; +/* get the file's type; follows symlinks */ +FileType getFileType(const char* fileName); +/* get the file's modification date; returns -1 w/errno set on failure */ +time_t getFileModDate(const char* fileName); + +/* + * Round up to the nearest power of 2. Handy for hash tables. + */ +unsigned int roundUpPower2(unsigned int val); + +void strreverse(char* begin, char* end); +void k_itoa(int value, char* str, int base); +char* itoa(int val, int base); + +}; // namespace android + +#endif // _LIBS_UTILS_MISC_H diff --git a/include/utils/ported.h b/include/utils/ported.h new file mode 100644 index 000000000..eb3be01e9 --- /dev/null +++ b/include/utils/ported.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2005 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. + */ + +// +// Standard functions ported to the current platform. Note these are NOT +// in the "android" namespace. +// +#ifndef _LIBS_UTILS_PORTED_H +#define _LIBS_UTILS_PORTED_H + +#include // for timeval + +#ifdef __cplusplus +extern "C" { +#endif + +/* library replacement functions */ +#if defined(NEED_GETTIMEOFDAY) +int gettimeofday(struct timeval* tv, struct timezone* tz); +#endif +#if defined(NEED_USLEEP) +void usleep(unsigned long usec); +#endif +#if defined(NEED_PIPE) +int pipe(int filedes[2]); +#endif +#if defined(NEED_SETENV) +int setenv(const char* name, const char* value, int overwrite); +void unsetenv(const char* name); +char* getenv(const char* name); +#endif + +#ifdef __cplusplus +} +#endif + +#endif // _LIBS_UTILS_PORTED_H diff --git a/include/utils/string_array.h b/include/utils/string_array.h new file mode 100644 index 000000000..064dda224 --- /dev/null +++ b/include/utils/string_array.h @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2005 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. + */ + +// +// Sortable array of strings. STL-ish, but STL-free. +// +#ifndef _LIBS_UTILS_STRING_ARRAY_H +#define _LIBS_UTILS_STRING_ARRAY_H + +#include +#include + +namespace android { + +// +// An expanding array of strings. Add, get, sort, delete. +// +class StringArray { +public: + StringArray() + : mMax(0), mCurrent(0), mArray(NULL) + {} + virtual ~StringArray() { + for (int i = 0; i < mCurrent; i++) + delete[] mArray[i]; + delete[] mArray; + } + + // + // Add a string. A copy of the string is made. + // + bool push_back(const char* str) { + if (mCurrent >= mMax) { + char** tmp; + + if (mMax == 0) + mMax = 16; // initial storage + else + mMax *= 2; + + tmp = new char*[mMax]; + if (tmp == NULL) + return false; + + memcpy(tmp, mArray, mCurrent * sizeof(char*)); + delete[] mArray; + mArray = tmp; + } + + int len = strlen(str); + mArray[mCurrent] = new char[len+1]; + memcpy(mArray[mCurrent], str, len+1); + mCurrent++; + + return true; + } + + // + // Delete an entry. + // + void erase(int idx) { + if (idx < 0 || idx >= mCurrent) + return; + delete[] mArray[idx]; + if (idx < mCurrent-1) { + memmove(&mArray[idx], &mArray[idx+1], + (mCurrent-1 - idx) * sizeof(char*)); + } + mCurrent--; + } + + // + // Sort the array. + // + void sort(int (*compare)(const void*, const void*)) { + qsort(mArray, mCurrent, sizeof(char*), compare); + } + + // + // Pass this to the sort routine to do an ascending alphabetical sort. + // + static int cmpAscendingAlpha(const void* pstr1, const void* pstr2) { + return strcmp(*(const char**)pstr1, *(const char**)pstr2); + } + + // + // Get the #of items in the array. + // + inline int size(void) const { return mCurrent; } + + // + // Return entry N. + // [should use operator[] here] + // + const char* getEntry(int idx) const { + if (idx < 0 || idx >= mCurrent) + return NULL; + return mArray[idx]; + } + + // + // Set entry N to specified string. + // [should use operator[] here] + // + void setEntry(int idx, const char* str) { + if (idx < 0 || idx >= mCurrent) + return; + delete[] mArray[idx]; + int len = strlen(str); + mArray[idx] = new char[len+1]; + memcpy(mArray[idx], str, len+1); + } + +private: + int mMax; + int mCurrent; + char** mArray; +}; + +}; // namespace android + +#endif // _LIBS_UTILS_STRING_ARRAY_H diff --git a/include/utils/threads.h b/include/utils/threads.h new file mode 100644 index 000000000..7dca81004 --- /dev/null +++ b/include/utils/threads.h @@ -0,0 +1,347 @@ +/* + * 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. + */ + +#ifndef _LIBS_UTILS_THREADS_H +#define _LIBS_UTILS_THREADS_H + +#include +#include +#include + +// ------------------------------------------------------------------ +// C API + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void* android_thread_id_t; + +typedef int (*android_thread_func_t)(void*); + +enum { + /* + * *********************************************** + * ** Keep in sync with android.os.Process.java ** + * *********************************************** + * + * This maps directly to the "nice" priorites we use in Android. + * A thread priority should be chosen inverse-proportinally to + * the amount of work the thread is expected to do. The more work + * a thread will do, the less favorable priority it should get so that + * it doesn't starve the system. Threads not behaving properly might + * be "punished" by the kernel. + * Use the levels below when appropriate. Intermediate values are + * acceptable, preferably use the {MORE|LESS}_FAVORABLE constants below. + */ + ANDROID_PRIORITY_LOWEST = 19, + + /* use for background tasks */ + ANDROID_PRIORITY_BACKGROUND = 10, + + /* most threads run at normal priority */ + ANDROID_PRIORITY_NORMAL = 0, + + /* threads currently running a UI that the user is interacting with */ + ANDROID_PRIORITY_FOREGROUND = -2, + + /* the main UI thread has a slightly more favorable priority */ + ANDROID_PRIORITY_DISPLAY = -4, + + /* ui service treads might want to run at a urgent display (uncommon) */ + ANDROID_PRIORITY_URGENT_DISPLAY = -8, + + /* all normal audio threads */ + ANDROID_PRIORITY_AUDIO = -16, + + /* service audio threads (uncommon) */ + ANDROID_PRIORITY_URGENT_AUDIO = -19, + + /* should never be used in practice. regular process might not + * be allowed to use this level */ + ANDROID_PRIORITY_HIGHEST = -20, + + ANDROID_PRIORITY_DEFAULT = ANDROID_PRIORITY_NORMAL, + ANDROID_PRIORITY_MORE_FAVORABLE = -1, + ANDROID_PRIORITY_LESS_FAVORABLE = +1, +}; + +// Create and run a new thread. +extern int androidCreateThread(android_thread_func_t, void *); + +// Create thread with lots of parameters +extern int androidCreateThreadEtc(android_thread_func_t entryFunction, + void *userData, + const char* threadName, + int32_t threadPriority, + size_t threadStackSize, + android_thread_id_t *threadId); + +// Get some sort of unique identifier for the current thread. +extern android_thread_id_t androidGetThreadId(); + +// Low-level thread creation -- never creates threads that can +// interact with the Java VM. +extern int androidCreateRawThreadEtc(android_thread_func_t entryFunction, + void *userData, + const char* threadName, + int32_t threadPriority, + size_t threadStackSize, + android_thread_id_t *threadId); + +// Used by the Java Runtime to control how threads are created, so that +// they can be proper and lovely Java threads. +typedef int (*android_create_thread_fn)(android_thread_func_t entryFunction, + void *userData, + const char* threadName, + int32_t threadPriority, + size_t threadStackSize, + android_thread_id_t *threadId); + +extern void androidSetCreateThreadFunc(android_create_thread_fn func); + +#ifdef __cplusplus +} +#endif + +// ------------------------------------------------------------------ +// C++ API + +#ifdef __cplusplus + +#include +#include +#include + +namespace android { + +typedef android_thread_id_t thread_id_t; + +typedef android_thread_func_t thread_func_t; + +enum { + PRIORITY_LOWEST = ANDROID_PRIORITY_LOWEST, + PRIORITY_BACKGROUND = ANDROID_PRIORITY_BACKGROUND, + PRIORITY_NORMAL = ANDROID_PRIORITY_NORMAL, + PRIORITY_FOREGROUND = ANDROID_PRIORITY_FOREGROUND, + PRIORITY_DISPLAY = ANDROID_PRIORITY_DISPLAY, + PRIORITY_URGENT_DISPLAY = ANDROID_PRIORITY_URGENT_DISPLAY, + PRIORITY_AUDIO = ANDROID_PRIORITY_AUDIO, + PRIORITY_URGENT_AUDIO = ANDROID_PRIORITY_URGENT_AUDIO, + PRIORITY_HIGHEST = ANDROID_PRIORITY_HIGHEST, + PRIORITY_DEFAULT = ANDROID_PRIORITY_DEFAULT, + PRIORITY_MORE_FAVORABLE = ANDROID_PRIORITY_MORE_FAVORABLE, + PRIORITY_LESS_FAVORABLE = ANDROID_PRIORITY_LESS_FAVORABLE, +}; + +// Create and run a new thread. +inline bool createThread(thread_func_t f, void *a) { + return androidCreateThread(f, a) ? true : false; +} + +// Create thread with lots of parameters +inline bool createThreadEtc(thread_func_t entryFunction, + void *userData, + const char* threadName = "android:unnamed_thread", + int32_t threadPriority = PRIORITY_DEFAULT, + size_t threadStackSize = 0, + thread_id_t *threadId = 0) +{ + return androidCreateThreadEtc(entryFunction, userData, threadName, + threadPriority, threadStackSize, threadId) ? true : false; +} + +// Get some sort of unique identifier for the current thread. +inline thread_id_t getThreadId() { + return androidGetThreadId(); +} + +/* + * Simple mutex class. The implementation is system-dependent. + * + * The mutex must be unlocked by the thread that locked it. They are not + * recursive, i.e. the same thread can't lock it multiple times. + */ +class Mutex { +public: + Mutex(); + Mutex(const char* name); + ~Mutex(); + + // lock or unlock the mutex + status_t lock(); + void unlock(); + + // lock if possible; returns 0 on success, error otherwise + status_t tryLock(); + + // Manages the mutex automatically. It'll be locked when Autolock is + // constructed and released when Autolock goes out of scope. + class Autolock { + public: + inline Autolock(Mutex& mutex) : mpMutex(&mutex) { mutex.lock(); } + inline Autolock(Mutex* mutex) : mpMutex(mutex) { mutex->lock(); } + inline ~Autolock() { mpMutex->unlock(); } + private: + Mutex* mpMutex; + }; + +private: + friend class Condition; + + // A mutex cannot be copied + Mutex(const Mutex&); + Mutex& operator = (const Mutex&); + void _init(); + + void* mState; +}; + +/* + * Automatic mutex. Declare one of these at the top of a function. + * When the function returns, it will go out of scope, and release the + * mutex. + */ + +typedef Mutex::Autolock AutoMutex; + + +/* + * Condition variable class. The implementation is system-dependent. + * + * Condition variables are paired up with mutexes. Lock the mutex, + * call wait(), then either re-wait() if things aren't quite what you want, + * or unlock the mutex and continue. All threads calling wait() must + * use the same mutex for a given Condition. + */ +class Condition { +public: + Condition(); + ~Condition(); + // Wait on the condition variable. Lock the mutex before calling. + status_t wait(Mutex& mutex); + // Wait on the condition variable until the given time. Lock the mutex + // before calling. + status_t wait(Mutex& mutex, nsecs_t abstime); + // same with relative timeout + status_t waitRelative(Mutex& mutex, nsecs_t reltime); + // Signal the condition variable, allowing one thread to continue. + void signal(); + // Signal the condition variable, allowing all threads to continue. + void broadcast(); + +private: + void* mState; +}; + + +/* + * Read/write lock. The resource can have multiple readers or one writer, + * but can't be read and written at the same time. + * + * The same thread should not call a lock function while it already has + * a lock. (Should be okay for multiple readers.) + */ +class ReadWriteLock { +public: + ReadWriteLock() + : mNumReaders(0), mNumWriters(0) + {} + ~ReadWriteLock() {} + + void lockForRead(); + bool tryLockForRead(); + void unlockForRead(); + + void lockForWrite(); + bool tryLockForWrite(); + void unlockForWrite(); + +private: + int mNumReaders; + int mNumWriters; + + Mutex mLock; + Condition mReadWaiter; + Condition mWriteWaiter; +#if defined(PRINT_RENDER_TIMES) + DurationTimer mDebugTimer; +#endif +}; + + +/* + * This is our spiffy thread object! + */ + +class Thread : virtual public RefBase +{ +public: + // Create a Thread object, but doesn't create or start the associated + // thread. See the run() method. + Thread(bool canCallJava = true); + virtual ~Thread(); + + // Start the thread in threadLoop() which needs to be implemented. + virtual status_t run( const char* name = 0, + int32_t priority = PRIORITY_DEFAULT, + size_t stack = 0); + + // Ask this object's thread to exit. This function is asynchronous, when the + // function returns the thread might still be running. Of course, this + // function can be called from a different thread. + virtual void requestExit(); + + // Good place to do one-time initializations + virtual status_t readyToRun(); + + // Call requestExit() and wait until this object's thread exits. + // BE VERY CAREFUL of deadlocks. In particular, it would be silly to call + // this function from this object's thread. Will return WOULD_BLOCK in + // that case. + status_t requestExitAndWait(); + +protected: + // exitPending() returns true if requestExit() has been called. + bool exitPending() const; + +private: + // Derived class must implemtent threadLoop(). The thread starts its life + // here. There are two ways of using the Thread object: + // 1) loop: if threadLoop() returns true, it will be called again if + // requestExit() wasn't called. + // 2) once: if threadLoop() returns false, the thread will exit upon return. + virtual bool threadLoop() = 0; + +private: + Thread& operator=(const Thread&); + static int _threadLoop(void* user); + const bool mCanCallJava; + thread_id_t mThread; + Mutex mLock; + Condition mThreadExitedCondition; + status_t mStatus; + volatile bool mExitPending; + volatile bool mRunning; + sp mHoldSelf; +}; + + +}; // namespace android + +#endif // __cplusplus + +#endif // _LIBS_UTILS_THREADS_H diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk new file mode 100644 index 000000000..cdb8ca2d7 --- /dev/null +++ b/libs/utils/Android.mk @@ -0,0 +1,156 @@ +# Copyright (C) 2008 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +LOCAL_PATH:= $(call my-dir) + +# libutils is a little unique: It's built twice, once for the host +# and once for the device. + +commonSources:= \ + Asset.cpp \ + AssetDir.cpp \ + AssetManager.cpp \ + BufferedTextOutput.cpp \ + CallStack.cpp \ + Debug.cpp \ + FileMap.cpp \ + RefBase.cpp \ + ResourceTypes.cpp \ + SharedBuffer.cpp \ + Static.cpp \ + StopWatch.cpp \ + String8.cpp \ + String16.cpp \ + SystemClock.cpp \ + TextOutput.cpp \ + Threads.cpp \ + TimerProbe.cpp \ + Timers.cpp \ + VectorImpl.cpp \ + ZipFileCRO.cpp \ + ZipFileRO.cpp \ + ZipUtils.cpp \ + misc.cpp \ + ported.cpp \ + LogSocket.cpp + +# +# The cpp files listed here do not belong in the device +# build. Consult with the swetland before even thinking about +# putting them in commonSources. +# +# They're used by the simulator runtime and by host-side tools like +# aapt and the simulator front-end. +# +hostSources:= \ + InetAddress.cpp \ + Pipe.cpp \ + Socket.cpp \ + ZipEntry.cpp \ + ZipFile.cpp + +# For the host +# ===================================================== + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= $(commonSources) $(hostSources) + +ifeq ($(HOST_OS),linux) +# Use the futex based mutex and condition variable +# implementation from android-arm because it's shared mem safe + LOCAL_SRC_FILES += \ + futex_synchro.c \ + executablepath_linux.cpp +endif +ifeq ($(HOST_OS),darwin) + LOCAL_SRC_FILES += \ + executablepath_darwin.cpp +endif + +LOCAL_MODULE:= libutils + +LOCAL_CFLAGS += -DLIBUTILS_NATIVE=1 $(TOOL_CFLAGS) +LOCAL_C_INCLUDES += external/zlib + +ifeq ($(HOST_OS),windows) +ifeq ($(strip $(USE_CYGWIN),),) +# Under MinGW, ctype.h doesn't need multi-byte support +LOCAL_CFLAGS += -DMB_CUR_MAX=1 +endif +endif + +include $(BUILD_HOST_STATIC_LIBRARY) + + + +# For the device +# ===================================================== +include $(CLEAR_VARS) + + +# we have the common sources, plus some device-specific stuff +LOCAL_SRC_FILES:= \ + $(commonSources) \ + Binder.cpp \ + BpBinder.cpp \ + IInterface.cpp \ + IMemory.cpp \ + IPCThreadState.cpp \ + MemoryDealer.cpp \ + MemoryBase.cpp \ + MemoryHeapBase.cpp \ + MemoryHeapPmem.cpp \ + Parcel.cpp \ + ProcessState.cpp \ + IPermissionController.cpp \ + IServiceManager.cpp \ + Unicode.cpp + +ifeq ($(TARGET_SIMULATOR),true) +LOCAL_SRC_FILES += $(hostSources) +endif + +ifeq ($(TARGET_OS),linux) +# Use the futex based mutex and condition variable +# implementation from android-arm because it's shared mem safe +LOCAL_SRC_FILES += futex_synchro.c +LOCAL_LDLIBS += -lrt -ldl +endif + +LOCAL_C_INCLUDES += \ + external/zlib \ + external/icu4c/common +LOCAL_LDLIBS += -lpthread + +LOCAL_SHARED_LIBRARIES := \ + libz \ + liblog \ + libcutils + +ifneq ($(TARGET_SIMULATOR),true) +ifeq ($(TARGET_OS)-$(TARGET_ARCH),linux-x86) +# This is needed on x86 to bring in dl_iterate_phdr for CallStack.cpp +LOCAL_SHARED_LIBRARIES += \ + libdl +endif # linux-x86 +endif # sim + +LOCAL_MODULE:= libutils + +#LOCAL_CFLAGS+= +#LOCAL_LDFLAGS:= + +include $(BUILD_SHARED_LIBRARY) + diff --git a/libs/utils/Asset.cpp b/libs/utils/Asset.cpp new file mode 100644 index 000000000..91203ddb4 --- /dev/null +++ b/libs/utils/Asset.cpp @@ -0,0 +1,813 @@ +/* + * Copyright (C) 2006 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. + */ + +// +// Provide access to a read-only asset. +// + +#define LOG_TAG "asset" +//#define NDEBUG 0 + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +using namespace android; + +#ifndef O_BINARY +# define O_BINARY 0 +#endif + +static volatile int32_t gCount = 0; + +int32_t Asset::getGlobalCount() +{ + return gCount; +} + +Asset::Asset(void) + : mAccessMode(ACCESS_UNKNOWN) +{ + int count = android_atomic_inc(&gCount)+1; + //LOGI("Creating Asset %p #%d\n", this, count); +} + +Asset::~Asset(void) +{ + int count = android_atomic_dec(&gCount); + //LOGI("Destroying Asset in %p #%d\n", this, count); +} + +/* + * Create a new Asset from a file on disk. There is a fair chance that + * the file doesn't actually exist. + * + * We can use "mode" to decide how we want to go about it. + */ +/*static*/ Asset* Asset::createFromFile(const char* fileName, AccessMode mode) +{ + _FileAsset* pAsset; + status_t result; + off_t length; + int fd; + + fd = open(fileName, O_RDONLY | O_BINARY); + if (fd < 0) + return NULL; + + /* + * Under Linux, the lseek fails if we actually opened a directory. To + * be correct we should test the file type explicitly, but since we + * always open things read-only it doesn't really matter, so there's + * no value in incurring the extra overhead of an fstat() call. + */ + length = lseek(fd, 0, SEEK_END); + if (length < 0) { + ::close(fd); + return NULL; + } + (void) lseek(fd, 0, SEEK_SET); + + pAsset = new _FileAsset; + result = pAsset->openChunk(fileName, fd, 0, length); + if (result != NO_ERROR) { + delete pAsset; + return NULL; + } + + pAsset->mAccessMode = mode; + return pAsset; +} + + +/* + * Create a new Asset from a compressed file on disk. There is a fair chance + * that the file doesn't actually exist. + * + * We currently support gzip files. We might want to handle .bz2 someday. + */ +/*static*/ Asset* Asset::createFromCompressedFile(const char* fileName, + AccessMode mode) +{ + _CompressedAsset* pAsset; + status_t result; + off_t fileLen; + bool scanResult; + long offset; + int method; + long uncompressedLen, compressedLen; + int fd; + + fd = open(fileName, O_RDONLY | O_BINARY); + if (fd < 0) + return NULL; + + fileLen = lseek(fd, 0, SEEK_END); + if (fileLen < 0) { + ::close(fd); + return NULL; + } + (void) lseek(fd, 0, SEEK_SET); + + /* want buffered I/O for the file scan; must dup so fclose() is safe */ + FILE* fp = fdopen(dup(fd), "rb"); + if (fp == NULL) { + ::close(fd); + return NULL; + } + + unsigned long crc32; + scanResult = ZipUtils::examineGzip(fp, &method, &uncompressedLen, + &compressedLen, &crc32); + offset = ftell(fp); + fclose(fp); + if (!scanResult) { + LOGD("File '%s' is not in gzip format\n", fileName); + ::close(fd); + return NULL; + } + + pAsset = new _CompressedAsset; + result = pAsset->openChunk(fd, offset, method, uncompressedLen, + compressedLen); + if (result != NO_ERROR) { + delete pAsset; + return NULL; + } + + pAsset->mAccessMode = mode; + return pAsset; +} + + +#if 0 +/* + * Create a new Asset from part of an open file. + */ +/*static*/ Asset* Asset::createFromFileSegment(int fd, off_t offset, + size_t length, AccessMode mode) +{ + _FileAsset* pAsset; + status_t result; + + pAsset = new _FileAsset; + result = pAsset->openChunk(NULL, fd, offset, length); + if (result != NO_ERROR) + return NULL; + + pAsset->mAccessMode = mode; + return pAsset; +} + +/* + * Create a new Asset from compressed data in an open file. + */ +/*static*/ Asset* Asset::createFromCompressedData(int fd, off_t offset, + int compressionMethod, size_t uncompressedLen, size_t compressedLen, + AccessMode mode) +{ + _CompressedAsset* pAsset; + status_t result; + + pAsset = new _CompressedAsset; + result = pAsset->openChunk(fd, offset, compressionMethod, + uncompressedLen, compressedLen); + if (result != NO_ERROR) + return NULL; + + pAsset->mAccessMode = mode; + return pAsset; +} +#endif + +/* + * Create a new Asset from a memory mapping. + */ +/*static*/ Asset* Asset::createFromUncompressedMap(FileMap* dataMap, + AccessMode mode) +{ + _FileAsset* pAsset; + status_t result; + + pAsset = new _FileAsset; + result = pAsset->openChunk(dataMap); + if (result != NO_ERROR) + return NULL; + + pAsset->mAccessMode = mode; + return pAsset; +} + +/* + * Create a new Asset from compressed data in a memory mapping. + */ +/*static*/ Asset* Asset::createFromCompressedMap(FileMap* dataMap, + int method, size_t uncompressedLen, AccessMode mode) +{ + _CompressedAsset* pAsset; + status_t result; + + pAsset = new _CompressedAsset; + result = pAsset->openChunk(dataMap, method, uncompressedLen); + if (result != NO_ERROR) + return NULL; + + pAsset->mAccessMode = mode; + return pAsset; +} + + +/* + * Do generic seek() housekeeping. Pass in the offset/whence values from + * the seek request, along with the current chunk offset and the chunk + * length. + * + * Returns the new chunk offset, or -1 if the seek is illegal. + */ +off_t Asset::handleSeek(off_t offset, int whence, off_t curPosn, off_t maxPosn) +{ + off_t newOffset; + + switch (whence) { + case SEEK_SET: + newOffset = offset; + break; + case SEEK_CUR: + newOffset = curPosn + offset; + break; + case SEEK_END: + newOffset = maxPosn + offset; + break; + default: + LOGW("unexpected whence %d\n", whence); + // this was happening due to an off_t size mismatch + assert(false); + return (off_t) -1; + } + + if (newOffset < 0 || newOffset > maxPosn) { + LOGW("seek out of range: want %ld, end=%ld\n", + (long) newOffset, (long) maxPosn); + return (off_t) -1; + } + + return newOffset; +} + + +/* + * =========================================================================== + * _FileAsset + * =========================================================================== + */ + +/* + * Constructor. + */ +_FileAsset::_FileAsset(void) + : mStart(0), mLength(0), mOffset(0), mFp(NULL), mFileName(NULL), mMap(NULL), mBuf(NULL) +{ +} + +/* + * Destructor. Release resources. + */ +_FileAsset::~_FileAsset(void) +{ + close(); +} + +/* + * Operate on a chunk of an uncompressed file. + * + * Zero-length chunks are allowed. + */ +status_t _FileAsset::openChunk(const char* fileName, int fd, off_t offset, size_t length) +{ + assert(mFp == NULL); // no reopen + assert(mMap == NULL); + assert(fd >= 0); + assert(offset >= 0); + + /* + * Seek to end to get file length. + */ + off_t fileLength; + fileLength = lseek(fd, 0, SEEK_END); + if (fileLength == (off_t) -1) { + // probably a bad file descriptor + LOGD("failed lseek (errno=%d)\n", errno); + return UNKNOWN_ERROR; + } + + if ((off_t) (offset + length) > fileLength) { + LOGD("start (%ld) + len (%ld) > end (%ld)\n", + (long) offset, (long) length, (long) fileLength); + return BAD_INDEX; + } + + /* after fdopen, the fd will be closed on fclose() */ + mFp = fdopen(fd, "rb"); + if (mFp == NULL) + return UNKNOWN_ERROR; + + mStart = offset; + mLength = length; + assert(mOffset == 0); + + /* seek the FILE* to the start of chunk */ + if (fseek(mFp, mStart, SEEK_SET) != 0) { + assert(false); + } + + mFileName = fileName != NULL ? strdup(fileName) : NULL; + + return NO_ERROR; +} + +/* + * Create the chunk from the map. + */ +status_t _FileAsset::openChunk(FileMap* dataMap) +{ + assert(mFp == NULL); // no reopen + assert(mMap == NULL); + assert(dataMap != NULL); + + mMap = dataMap; + mStart = -1; // not used + mLength = dataMap->getDataLength(); + assert(mOffset == 0); + + return NO_ERROR; +} + +/* + * Read a chunk of data. + */ +ssize_t _FileAsset::read(void* buf, size_t count) +{ + size_t maxLen; + size_t actual; + + assert(mOffset >= 0 && mOffset <= mLength); + + if (getAccessMode() == ACCESS_BUFFER) { + /* + * On first access, read or map the entire file. The caller has + * requested buffer access, either because they're going to be + * using the buffer or because what they're doing has appropriate + * performance needs and access patterns. + */ + if (mBuf == NULL) + getBuffer(false); + } + + /* adjust count if we're near EOF */ + maxLen = mLength - mOffset; + if (count > maxLen) + count = maxLen; + + if (!count) + return 0; + + if (mMap != NULL) { + /* copy from mapped area */ + //printf("map read\n"); + memcpy(buf, (char*)mMap->getDataPtr() + mOffset, count); + actual = count; + } else if (mBuf != NULL) { + /* copy from buffer */ + //printf("buf read\n"); + memcpy(buf, (char*)mBuf + mOffset, count); + actual = count; + } else { + /* read from the file */ + //printf("file read\n"); + if (ftell(mFp) != mStart + mOffset) { + LOGE("Hosed: %ld != %ld+%ld\n", + ftell(mFp), (long) mStart, (long) mOffset); + assert(false); + } + + /* + * This returns 0 on error or eof. We need to use ferror() or feof() + * to tell the difference, but we don't currently have those on the + * device. However, we know how much data is *supposed* to be in the + * file, so if we don't read the full amount we know something is + * hosed. + */ + actual = fread(buf, 1, count, mFp); + if (actual == 0) // something failed -- I/O error? + return -1; + + assert(actual == count); + } + + mOffset += actual; + return actual; +} + +/* + * Seek to a new position. + */ +off_t _FileAsset::seek(off_t offset, int whence) +{ + off_t newPosn; + long actualOffset; + + // compute new position within chunk + newPosn = handleSeek(offset, whence, mOffset, mLength); + if (newPosn == (off_t) -1) + return newPosn; + + actualOffset = (long) (mStart + newPosn); + + if (mFp != NULL) { + if (fseek(mFp, (long) actualOffset, SEEK_SET) != 0) + return (off_t) -1; + } + + mOffset = actualOffset - mStart; + return mOffset; +} + +/* + * Close the asset. + */ +void _FileAsset::close(void) +{ + if (mMap != NULL) { + mMap->release(); + mMap = NULL; + } + if (mBuf != NULL) { + delete[] mBuf; + mBuf = NULL; + } + + if (mFileName != NULL) { + free(mFileName); + mFileName = NULL; + } + + if (mFp != NULL) { + // can only be NULL when called from destructor + // (otherwise we would never return this object) + fclose(mFp); + mFp = NULL; + } +} + +/* + * Return a read-only pointer to a buffer. + * + * We can either read the whole thing in or map the relevant piece of + * the source file. Ideally a map would be established at a higher + * level and we'd be using a different object, but we didn't, so we + * deal with it here. + */ +const void* _FileAsset::getBuffer(bool wordAligned) +{ + /* subsequent requests just use what we did previously */ + if (mBuf != NULL) + return mBuf; + if (mMap != NULL) { + if (!wordAligned) { + return mMap->getDataPtr(); + } + return ensureAlignment(mMap); + } + + assert(mFp != NULL); + + if (mLength < kReadVsMapThreshold) { + unsigned char* buf; + long allocLen; + + /* zero-length files are allowed; not sure about zero-len allocs */ + /* (works fine with gcc + x86linux) */ + allocLen = mLength; + if (mLength == 0) + allocLen = 1; + + buf = new unsigned char[allocLen]; + if (buf == NULL) { + LOGE("alloc of %ld bytes failed\n", (long) allocLen); + return NULL; + } + + LOGV("Asset %p allocating buffer size %d (smaller than threshold)", this, (int)allocLen); + if (mLength > 0) { + long oldPosn = ftell(mFp); + fseek(mFp, mStart, SEEK_SET); + if (fread(buf, 1, mLength, mFp) != (size_t) mLength) { + LOGE("failed reading %ld bytes\n", (long) mLength); + delete[] buf; + return NULL; + } + fseek(mFp, oldPosn, SEEK_SET); + } + + LOGV(" getBuffer: loaded into buffer\n"); + + mBuf = buf; + return mBuf; + } else { + FileMap* map; + + map = new FileMap; + if (!map->create(NULL, fileno(mFp), mStart, mLength, true)) { + map->release(); + return NULL; + } + + LOGV(" getBuffer: mapped\n"); + + mMap = map; + if (!wordAligned) { + return mMap->getDataPtr(); + } + return ensureAlignment(mMap); + } +} + +int _FileAsset::openFileDescriptor(off_t* outStart, off_t* outLength) const +{ + if (mMap != NULL) { + const char* fname = mMap->getFileName(); + if (fname == NULL) { + fname = mFileName; + } + if (fname == NULL) { + return -1; + } + *outStart = mMap->getDataOffset(); + *outLength = mMap->getDataLength(); + return open(fname, O_RDONLY | O_BINARY); + } + if (mFileName == NULL) { + return -1; + } + *outStart = mStart; + *outLength = mLength; + return open(mFileName, O_RDONLY | O_BINARY); +} + +const void* _FileAsset::ensureAlignment(FileMap* map) +{ + void* data = map->getDataPtr(); + if ((((size_t)data)&0x3) == 0) { + // We can return this directly if it is aligned on a word + // boundary. + return data; + } + // If not aligned on a word boundary, then we need to copy it into + // our own buffer. + LOGV("Copying FileAsset %p to buffer size %d to make it aligned.", this, (int)mLength); + unsigned char* buf = new unsigned char[mLength]; + if (buf == NULL) { + LOGE("alloc of %ld bytes failed\n", (long) mLength); + return NULL; + } + memcpy(buf, data, mLength); + mBuf = buf; + return buf; +} + +/* + * =========================================================================== + * _CompressedAsset + * =========================================================================== + */ + +/* + * Constructor. + */ +_CompressedAsset::_CompressedAsset(void) + : mStart(0), mCompressedLen(0), mUncompressedLen(0), mOffset(0), + mMap(NULL), mFd(-1), mBuf(NULL) +{ +} + +/* + * Destructor. Release resources. + */ +_CompressedAsset::~_CompressedAsset(void) +{ + close(); +} + +/* + * Open a chunk of compressed data inside a file. + * + * This currently just sets up some values and returns. On the first + * read, we expand the entire file into a buffer and return data from it. + */ +status_t _CompressedAsset::openChunk(int fd, off_t offset, + int compressionMethod, size_t uncompressedLen, size_t compressedLen) +{ + assert(mFd < 0); // no re-open + assert(mMap == NULL); + assert(fd >= 0); + assert(offset >= 0); + assert(compressedLen > 0); + + if (compressionMethod != ZipFileRO::kCompressDeflated) { + assert(false); + return UNKNOWN_ERROR; + } + + mStart = offset; + mCompressedLen = compressedLen; + mUncompressedLen = uncompressedLen; + assert(mOffset == 0); + mFd = fd; + assert(mBuf == NULL); + + return NO_ERROR; +} + +/* + * Open a chunk of compressed data in a mapped region. + * + * Nothing is expanded until the first read call. + */ +status_t _CompressedAsset::openChunk(FileMap* dataMap, int compressionMethod, + size_t uncompressedLen) +{ + assert(mFd < 0); // no re-open + assert(mMap == NULL); + assert(dataMap != NULL); + + if (compressionMethod != ZipFileRO::kCompressDeflated) { + assert(false); + return UNKNOWN_ERROR; + } + + mMap = dataMap; + mStart = -1; // not used + mCompressedLen = dataMap->getDataLength(); + mUncompressedLen = uncompressedLen; + assert(mOffset == 0); + + return NO_ERROR; +} + +/* + * Read data from a chunk of compressed data. + * + * [For now, that's just copying data out of a buffer.] + */ +ssize_t _CompressedAsset::read(void* buf, size_t count) +{ + size_t maxLen; + size_t actual; + + assert(mOffset >= 0 && mOffset <= mUncompressedLen); + + // TODO: if mAccessMode == ACCESS_STREAMING, use zlib more cleverly + + if (mBuf == NULL) { + if (getBuffer(false) == NULL) + return -1; + } + assert(mBuf != NULL); + + /* adjust count if we're near EOF */ + maxLen = mUncompressedLen - mOffset; + if (count > maxLen) + count = maxLen; + + if (!count) + return 0; + + /* copy from buffer */ + //printf("comp buf read\n"); + memcpy(buf, (char*)mBuf + mOffset, count); + actual = count; + + mOffset += actual; + return actual; +} + +/* + * Handle a seek request. + * + * If we're working in a streaming mode, this is going to be fairly + * expensive, because it requires plowing through a bunch of compressed + * data. + */ +off_t _CompressedAsset::seek(off_t offset, int whence) +{ + off_t newPosn; + + // compute new position within chunk + newPosn = handleSeek(offset, whence, mOffset, mUncompressedLen); + if (newPosn == (off_t) -1) + return newPosn; + + mOffset = newPosn; + return mOffset; +} + +/* + * Close the asset. + */ +void _CompressedAsset::close(void) +{ + if (mMap != NULL) { + mMap->release(); + mMap = NULL; + } + if (mBuf != NULL) { + delete[] mBuf; + mBuf = NULL; + } + + if (mFd > 0) { + ::close(mFd); + mFd = -1; + } +} + +/* + * Get a pointer to a read-only buffer of data. + * + * The first time this is called, we expand the compressed data into a + * buffer. + */ +const void* _CompressedAsset::getBuffer(bool wordAligned) +{ + unsigned char* buf = NULL; + + if (mBuf != NULL) + return mBuf; + + if (mUncompressedLen > UNCOMPRESS_DATA_MAX) { + LOGD("Data exceeds UNCOMPRESS_DATA_MAX (%ld vs %d)\n", + (long) mUncompressedLen, UNCOMPRESS_DATA_MAX); + goto bail; + } + + /* + * Allocate a buffer and read the file into it. + */ + buf = new unsigned char[mUncompressedLen]; + if (buf == NULL) { + LOGW("alloc %ld bytes failed\n", (long) mUncompressedLen); + goto bail; + } + + if (mMap != NULL) { + if (!ZipFileRO::inflateBuffer(buf, mMap->getDataPtr(), + mUncompressedLen, mCompressedLen)) + goto bail; + } else { + assert(mFd >= 0); + + /* + * Seek to the start of the compressed data. + */ + if (lseek(mFd, mStart, SEEK_SET) != mStart) + goto bail; + + /* + * Expand the data into it. + */ + if (!ZipUtils::inflateToBuffer(mFd, buf, mUncompressedLen, + mCompressedLen)) + goto bail; + } + + /* success! */ + mBuf = buf; + buf = NULL; + +bail: + delete[] buf; + return mBuf; +} + diff --git a/libs/utils/AssetDir.cpp b/libs/utils/AssetDir.cpp new file mode 100644 index 000000000..c5f664ecc --- /dev/null +++ b/libs/utils/AssetDir.cpp @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2006 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. + */ + +// +// Provide access to a virtual directory in "asset space". Most of the +// implementation is in the header file or in friend functions in +// AssetManager. +// +#include + +using namespace android; + + +/* + * Find a matching entry in a vector of FileInfo. Because it's sorted, we + * can use a binary search. + * + * Assumes the vector is sorted in ascending order. + */ +/*static*/ int AssetDir::FileInfo::findEntry(const SortedVector* pVector, + const String8& fileName) +{ + FileInfo tmpInfo; + + tmpInfo.setFileName(fileName); + return pVector->indexOf(tmpInfo); + +#if 0 // don't need this after all (uses 1/2 compares of SortedVector though) + int lo, hi, cur; + + lo = 0; + hi = pVector->size() -1; + while (lo <= hi) { + int cmp; + + cur = (hi + lo) / 2; + cmp = strcmp(pVector->itemAt(cur).getFileName(), fileName); + if (cmp == 0) { + /* match, bail */ + return cur; + } else if (cmp < 0) { + /* too low */ + lo = cur + 1; + } else { + /* too high */ + hi = cur -1; + } + } + + return -1; +#endif +} + diff --git a/libs/utils/AssetManager.cpp b/libs/utils/AssetManager.cpp new file mode 100644 index 000000000..447b80193 --- /dev/null +++ b/libs/utils/AssetManager.cpp @@ -0,0 +1,1637 @@ +/* + * Copyright (C) 2006 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. + */ + +// +// Provide access to read-only assets. +// + +#define LOG_TAG "asset" +//#define LOG_NDEBUG 0 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace android; + +/* + * Names for default app, locale, and vendor. We might want to change + * these to be an actual locale, e.g. always use en-US as the default. + */ +static const char* kDefaultLocale = "default"; +static const char* kDefaultVendor = "default"; +static const char* kAssetsRoot = "assets"; +static const char* kAppZipName = NULL; //"classes.jar"; +static const char* kSystemAssets = "framework/framework-res.apk"; + +static const char* kExcludeExtension = ".EXCLUDE"; + +static Asset* const kExcludedAsset = (Asset*) 0xd000000d; + +static volatile int32_t gCount = 0; + + +/* + * =========================================================================== + * AssetManager + * =========================================================================== + */ + +int32_t AssetManager::getGlobalCount() +{ + return gCount; +} + +AssetManager::AssetManager(CacheMode cacheMode) + : mLocale(NULL), mVendor(NULL), + mResources(NULL), mConfig(new ResTable_config), + mCacheMode(cacheMode), mCacheValid(false) +{ + int count = android_atomic_inc(&gCount)+1; + //LOGI("Creating AssetManager %p #%d\n", this, count); + memset(mConfig, 0, sizeof(ResTable_config)); +} + +AssetManager::~AssetManager(void) +{ + int count = android_atomic_dec(&gCount); + //LOGI("Destroying AssetManager in %p #%d\n", this, count); + + delete mConfig; + delete mResources; + + // don't have a String class yet, so make sure we clean up + delete[] mLocale; + delete[] mVendor; +} + +bool AssetManager::addAssetPath(const String8& path, void** cookie) +{ + AutoMutex _l(mLock); + + asset_path ap; + + String8 realPath(path); + if (kAppZipName) { + realPath.appendPath(kAppZipName); + } + ap.type = ::getFileType(realPath.string()); + if (ap.type == kFileTypeRegular) { + ap.path = realPath; + } else { + ap.path = path; + ap.type = ::getFileType(path.string()); + if (ap.type != kFileTypeDirectory && ap.type != kFileTypeRegular) { + LOGW("Asset path %s is neither a directory nor file (type=%d).", + path.string(), (int)ap.type); + return false; + } + } + + // Skip if we have it already. + for (size_t i=0; i mAssetPaths.size() ? NULL : (void*)next; +} + +String8 AssetManager::getAssetPath(void* cookie) const +{ + AutoMutex _l(mLock); + const size_t which = ((size_t)cookie)-1; + if (which < mAssetPaths.size()) { + return mAssetPaths[which].path; + } + return String8(); +} + +/* + * Set the current locale. Use NULL to indicate no locale. + * + * Close and reopen Zip archives as appropriate, and reset cached + * information in the locale-specific sections of the tree. + */ +void AssetManager::setLocale(const char* locale) +{ + AutoMutex _l(mLock); + setLocaleLocked(locale); +} + +void AssetManager::setLocaleLocked(const char* locale) +{ + if (mLocale != NULL) { + /* previously set, purge cached data */ + purgeFileNameCacheLocked(); + //mZipSet.purgeLocale(); + delete[] mLocale; + } + mLocale = strdupNew(locale); + + updateResourceParamsLocked(); +} + +/* + * Set the current vendor. Use NULL to indicate no vendor. + * + * Close and reopen Zip archives as appropriate, and reset cached + * information in the vendor-specific sections of the tree. + */ +void AssetManager::setVendor(const char* vendor) +{ + AutoMutex _l(mLock); + + if (mVendor != NULL) { + /* previously set, purge cached data */ + purgeFileNameCacheLocked(); + //mZipSet.purgeVendor(); + delete[] mVendor; + } + mVendor = strdupNew(vendor); +} + +void AssetManager::setConfiguration(const ResTable_config& config, const char* locale) +{ + AutoMutex _l(mLock); + *mConfig = config; + if (locale) { + setLocaleLocked(locale); + } else if (config.language[0] != 0) { + char spec[9]; + spec[0] = config.language[0]; + spec[1] = config.language[1]; + if (config.country[0] != 0) { + spec[2] = '_'; + spec[3] = config.country[0]; + spec[4] = config.country[1]; + spec[5] = 0; + } else { + spec[3] = 0; + } + setLocaleLocked(spec); + } else { + updateResourceParamsLocked(); + } +} + +/* + * Open an asset. + * + * The data could be; + * - In a file on disk (assetBase + fileName). + * - In a compressed file on disk (assetBase + fileName.gz). + * - In a Zip archive, uncompressed or compressed. + * + * It can be in a number of different directories and Zip archives. + * The search order is: + * - [appname] + * - locale + vendor + * - "default" + vendor + * - locale + "default" + * - "default + "default" + * - "common" + * - (same as above) + * + * To find a particular file, we have to try up to eight paths with + * all three forms of data. + * + * We should probably reject requests for "illegal" filenames, e.g. those + * with illegal characters or "../" backward relative paths. + */ +Asset* AssetManager::open(const char* fileName, AccessMode mode) +{ + AutoMutex _l(mLock); + + LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager"); + + + if (mCacheMode != CACHE_OFF && !mCacheValid) + loadFileNameCacheLocked(); + + String8 assetName(kAssetsRoot); + assetName.appendPath(fileName); + + /* + * For each top-level asset path, search for the asset. + */ + + size_t i = mAssetPaths.size(); + while (i > 0) { + i--; + LOGV("Looking for asset '%s' in '%s'\n", + assetName.string(), mAssetPaths.itemAt(i).path.string()); + Asset* pAsset = openNonAssetInPathLocked(assetName.string(), mode, mAssetPaths.itemAt(i)); + if (pAsset != NULL) { + return pAsset != kExcludedAsset ? pAsset : NULL; + } + } + + return NULL; +} + +/* + * Open a non-asset file as if it were an asset. + * + * The "fileName" is the partial path starting from the application + * name. + */ +Asset* AssetManager::openNonAsset(const char* fileName, AccessMode mode) +{ + AutoMutex _l(mLock); + + LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager"); + + + if (mCacheMode != CACHE_OFF && !mCacheValid) + loadFileNameCacheLocked(); + + /* + * For each top-level asset path, search for the asset. + */ + + size_t i = mAssetPaths.size(); + while (i > 0) { + i--; + LOGV("Looking for non-asset '%s' in '%s'\n", fileName, mAssetPaths.itemAt(i).path.string()); + Asset* pAsset = openNonAssetInPathLocked( + fileName, mode, mAssetPaths.itemAt(i)); + if (pAsset != NULL) { + return pAsset != kExcludedAsset ? pAsset : NULL; + } + } + + return NULL; +} + +Asset* AssetManager::openNonAsset(void* cookie, const char* fileName, AccessMode mode) +{ + const size_t which = ((size_t)cookie)-1; + + AutoMutex _l(mLock); + + LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager"); + + + if (mCacheMode != CACHE_OFF && !mCacheValid) + loadFileNameCacheLocked(); + + if (which < mAssetPaths.size()) { + LOGV("Looking for non-asset '%s' in '%s'\n", fileName, + mAssetPaths.itemAt(which).path.string()); + Asset* pAsset = openNonAssetInPathLocked( + fileName, mode, mAssetPaths.itemAt(which)); + if (pAsset != NULL) { + return pAsset != kExcludedAsset ? pAsset : NULL; + } + } + + return NULL; +} + +/* + * Get the type of a file in the asset namespace. + * + * This currently only works for regular files. All others (including + * directories) will return kFileTypeNonexistent. + */ +FileType AssetManager::getFileType(const char* fileName) +{ + Asset* pAsset = NULL; + + /* + * Open the asset. This is less efficient than simply finding the + * file, but it's not too bad (we don't uncompress or mmap data until + * the first read() call). + */ + pAsset = open(fileName, Asset::ACCESS_STREAMING); + delete pAsset; + + if (pAsset == NULL) + return kFileTypeNonexistent; + else + return kFileTypeRegular; +} + +const ResTable* AssetManager::getResTable(bool required) const +{ + ResTable* rt = mResources; + if (rt) { + return rt; + } + + // Iterate through all asset packages, collecting resources from each. + + AutoMutex _l(mLock); + + if (mResources != NULL) { + return mResources; + } + + if (required) { + LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager"); + } + + if (mCacheMode != CACHE_OFF && !mCacheValid) + const_cast(this)->loadFileNameCacheLocked(); + + const size_t N = mAssetPaths.size(); + for (size_t i=0; i(this)-> + mZipSet.getZipResourceTable(ap.path); + if (ass == NULL) { + LOGV("loading resource table %s\n", ap.path.string()); + ass = const_cast(this)-> + openNonAssetInPathLocked("resources.arsc", + Asset::ACCESS_BUFFER, + ap); + if (ass != NULL && ass != kExcludedAsset) { + ass = const_cast(this)-> + mZipSet.setZipResourceTable(ap.path, ass); + } + } + } else { + LOGV("loading resource table %s\n", ap.path.string()); + Asset* ass = const_cast(this)-> + openNonAssetInPathLocked("resources.arsc", + Asset::ACCESS_BUFFER, + ap); + shared = false; + } + if (ass != NULL && ass != kExcludedAsset) { + if (rt == NULL) { + mResources = rt = new ResTable(); + updateResourceParamsLocked(); + } + LOGV("Installing resource asset %p in to table %p\n", ass, mResources); + rt->add(ass, (void*)(i+1), !shared); + + if (!shared) { + delete ass; + } + } + } + + if (required && !rt) LOGW("Unable to find resources file resources.arsc"); + if (!rt) { + mResources = rt = new ResTable(); + } + return rt; +} + +void AssetManager::updateResourceParamsLocked() const +{ + ResTable* res = mResources; + if (!res) { + return; + } + + size_t llen = mLocale ? strlen(mLocale) : 0; + mConfig->language[0] = 0; + mConfig->language[1] = 0; + mConfig->country[0] = 0; + mConfig->country[1] = 0; + if (llen >= 2) { + mConfig->language[0] = mLocale[0]; + mConfig->language[1] = mLocale[1]; + } + if (llen >= 5) { + mConfig->country[0] = mLocale[3]; + mConfig->country[1] = mLocale[4]; + } + mConfig->size = sizeof(*mConfig); + + res->setParameters(mConfig); +} + +const ResTable& AssetManager::getResources(bool required) const +{ + const ResTable* rt = getResTable(required); + return *rt; +} + +bool AssetManager::isUpToDate() +{ + AutoMutex _l(mLock); + return mZipSet.isUpToDate(); +} + +void AssetManager::getLocales(Vector* locales) const +{ + ResTable* res = mResources; + if (res != NULL) { + res->getLocales(locales); + } +} + +/* + * Open a non-asset file as if it were an asset, searching for it in the + * specified app. + * + * Pass in a NULL values for "appName" if the common app directory should + * be used. + */ +Asset* AssetManager::openNonAssetInPathLocked(const char* fileName, AccessMode mode, + const asset_path& ap) +{ + Asset* pAsset = NULL; + + /* look at the filesystem on disk */ + if (ap.type == kFileTypeDirectory) { + String8 path(ap.path); + path.appendPath(fileName); + + pAsset = openAssetFromFileLocked(path, mode); + + if (pAsset == NULL) { + /* try again, this time with ".gz" */ + path.append(".gz"); + pAsset = openAssetFromFileLocked(path, mode); + } + + if (pAsset != NULL) { + //printf("FOUND NA '%s' on disk\n", fileName); + pAsset->setAssetSource(path); + } + + /* look inside the zip file */ + } else { + String8 path(fileName); + + /* check the appropriate Zip file */ + ZipFileRO* pZip; + ZipEntryRO entry; + + pZip = getZipFileLocked(ap); + if (pZip != NULL) { + //printf("GOT zip, checking NA '%s'\n", (const char*) path); + entry = pZip->findEntryByName(path.string()); + if (entry != NULL) { + //printf("FOUND NA in Zip file for %s\n", appName ? appName : kAppCommon); + pAsset = openAssetFromZipLocked(pZip, entry, mode, path); + } + } + + if (pAsset != NULL) { + /* create a "source" name, for debug/display */ + pAsset->setAssetSource( + createZipSourceNameLocked(ZipSet::getPathName(ap.path.string()), String8(""), + String8(fileName))); + } + } + + return pAsset; +} + +/* + * Open an asset, searching for it in the directory hierarchy for the + * specified app. + * + * Pass in a NULL values for "appName" if the common app directory should + * be used. + */ +Asset* AssetManager::openInPathLocked(const char* fileName, AccessMode mode, + const asset_path& ap) +{ + Asset* pAsset = NULL; + + /* + * Try various combinations of locale and vendor. + */ + if (mLocale != NULL && mVendor != NULL) + pAsset = openInLocaleVendorLocked(fileName, mode, ap, mLocale, mVendor); + if (pAsset == NULL && mVendor != NULL) + pAsset = openInLocaleVendorLocked(fileName, mode, ap, NULL, mVendor); + if (pAsset == NULL && mLocale != NULL) + pAsset = openInLocaleVendorLocked(fileName, mode, ap, mLocale, NULL); + if (pAsset == NULL) + pAsset = openInLocaleVendorLocked(fileName, mode, ap, NULL, NULL); + + return pAsset; +} + +/* + * Open an asset, searching for it in the directory hierarchy for the + * specified locale and vendor. + * + * We also search in "app.jar". + * + * Pass in NULL values for "appName", "locale", and "vendor" if the + * defaults should be used. + */ +Asset* AssetManager::openInLocaleVendorLocked(const char* fileName, AccessMode mode, + const asset_path& ap, const char* locale, const char* vendor) +{ + Asset* pAsset = NULL; + + if (ap.type == kFileTypeDirectory) { + if (mCacheMode == CACHE_OFF) { + /* look at the filesystem on disk */ + String8 path(createPathNameLocked(ap, locale, vendor)); + path.appendPath(fileName); + + String8 excludeName(path); + excludeName.append(kExcludeExtension); + if (::getFileType(excludeName.string()) != kFileTypeNonexistent) { + /* say no more */ + //printf("+++ excluding '%s'\n", (const char*) excludeName); + return kExcludedAsset; + } + + pAsset = openAssetFromFileLocked(path, mode); + + if (pAsset == NULL) { + /* try again, this time with ".gz" */ + path.append(".gz"); + pAsset = openAssetFromFileLocked(path, mode); + } + + if (pAsset != NULL) + pAsset->setAssetSource(path); + } else { + /* find in cache */ + String8 path(createPathNameLocked(ap, locale, vendor)); + path.appendPath(fileName); + + AssetDir::FileInfo tmpInfo; + bool found = false; + + String8 excludeName(path); + excludeName.append(kExcludeExtension); + + if (mCache.indexOf(excludeName) != NAME_NOT_FOUND) { + /* go no farther */ + //printf("+++ Excluding '%s'\n", (const char*) excludeName); + return kExcludedAsset; + } + + /* + * File compression extensions (".gz") don't get stored in the + * name cache, so we have to try both here. + */ + if (mCache.indexOf(path) != NAME_NOT_FOUND) { + found = true; + pAsset = openAssetFromFileLocked(path, mode); + if (pAsset == NULL) { + /* try again, this time with ".gz" */ + path.append(".gz"); + pAsset = openAssetFromFileLocked(path, mode); + } + } + + if (pAsset != NULL) + pAsset->setAssetSource(path); + + /* + * Don't continue the search into the Zip files. Our cached info + * said it was a file on disk; to be consistent with openDir() + * we want to return the loose asset. If the cached file gets + * removed, we fail. + * + * The alternative is to update our cache when files get deleted, + * or make some sort of "best effort" promise, but for now I'm + * taking the hard line. + */ + if (found) { + if (pAsset == NULL) + LOGD("Expected file not found: '%s'\n", path.string()); + return pAsset; + } + } + } + + /* + * Either it wasn't found on disk or on the cached view of the disk. + * Dig through the currently-opened set of Zip files. If caching + * is disabled, the Zip file may get reopened. + */ + if (pAsset == NULL && ap.type == kFileTypeRegular) { + String8 path; + + path.appendPath((locale != NULL) ? locale : kDefaultLocale); + path.appendPath((vendor != NULL) ? vendor : kDefaultVendor); + path.appendPath(fileName); + + /* check the appropriate Zip file */ + ZipFileRO* pZip; + ZipEntryRO entry; + + pZip = getZipFileLocked(ap); + if (pZip != NULL) { + //printf("GOT zip, checking '%s'\n", (const char*) path); + entry = pZip->findEntryByName(path.string()); + if (entry != NULL) { + //printf("FOUND in Zip file for %s/%s-%s\n", + // appName, locale, vendor); + pAsset = openAssetFromZipLocked(pZip, entry, mode, path); + } + } + + if (pAsset != NULL) { + /* create a "source" name, for debug/display */ + pAsset->setAssetSource(createZipSourceNameLocked(ZipSet::getPathName(ap.path.string()), + String8(""), String8(fileName))); + } + } + + return pAsset; +} + +/* + * Create a "source name" for a file from a Zip archive. + */ +String8 AssetManager::createZipSourceNameLocked(const String8& zipFileName, + const String8& dirName, const String8& fileName) +{ + String8 sourceName("zip:"); + sourceName.append(zipFileName); + sourceName.append(":"); + if (dirName.length() > 0) { + sourceName.appendPath(dirName); + } + sourceName.appendPath(fileName); + return sourceName; +} + +/* + * Create a path to a loose asset (asset-base/app/locale/vendor). + */ +String8 AssetManager::createPathNameLocked(const asset_path& ap, const char* locale, + const char* vendor) +{ + String8 path(ap.path); + path.appendPath((locale != NULL) ? locale : kDefaultLocale); + path.appendPath((vendor != NULL) ? vendor : kDefaultVendor); + return path; +} + +/* + * Create a path to a loose asset (asset-base/app/rootDir). + */ +String8 AssetManager::createPathNameLocked(const asset_path& ap, const char* rootDir) +{ + String8 path(ap.path); + if (rootDir != NULL) path.appendPath(rootDir); + return path; +} + +/* + * Return a pointer to one of our open Zip archives. Returns NULL if no + * matching Zip file exists. + * + * Right now we have 2 possible Zip files (1 each in app/"common"). + * + * If caching is set to CACHE_OFF, to get the expected behavior we + * need to reopen the Zip file on every request. That would be silly + * and expensive, so instead we just check the file modification date. + * + * Pass in NULL values for "appName", "locale", and "vendor" if the + * generics should be used. + */ +ZipFileRO* AssetManager::getZipFileLocked(const asset_path& ap) +{ + LOGV("getZipFileLocked() in %p\n", this); + + return mZipSet.getZip(ap.path); +} + +/* + * Try to open an asset from a file on disk. + * + * If the file is compressed with gzip, we seek to the start of the + * deflated data and pass that in (just like we would for a Zip archive). + * + * For uncompressed data, we may already have an mmap()ed version sitting + * around. If so, we want to hand that to the Asset instead. + * + * This returns NULL if the file doesn't exist, couldn't be opened, or + * claims to be a ".gz" but isn't. + */ +Asset* AssetManager::openAssetFromFileLocked(const String8& pathName, + AccessMode mode) +{ + Asset* pAsset = NULL; + + if (strcasecmp(pathName.getPathExtension().string(), ".gz") == 0) { + //printf("TRYING '%s'\n", (const char*) pathName); + pAsset = Asset::createFromCompressedFile(pathName.string(), mode); + } else { + //printf("TRYING '%s'\n", (const char*) pathName); + pAsset = Asset::createFromFile(pathName.string(), mode); + } + + return pAsset; +} + +/* + * Given an entry in a Zip archive, create a new Asset object. + * + * If the entry is uncompressed, we may want to create or share a + * slice of shared memory. + */ +Asset* AssetManager::openAssetFromZipLocked(const ZipFileRO* pZipFile, + const ZipEntryRO entry, AccessMode mode, const String8& entryName) +{ + Asset* pAsset = NULL; + + // TODO: look for previously-created shared memory slice? + int method; + long uncompressedLen; + + //printf("USING Zip '%s'\n", pEntry->getFileName()); + + //pZipFile->getEntryInfo(entry, &method, &uncompressedLen, &compressedLen, + // &offset); + if (!pZipFile->getEntryInfo(entry, &method, &uncompressedLen, NULL, NULL, + NULL, NULL)) + { + LOGW("getEntryInfo failed\n"); + return NULL; + } + + FileMap* dataMap = pZipFile->createEntryFileMap(entry); + if (dataMap == NULL) { + LOGW("create map from entry failed\n"); + return NULL; + } + + if (method == ZipFileRO::kCompressStored) { + pAsset = Asset::createFromUncompressedMap(dataMap, mode); + LOGV("Opened uncompressed entry %s in zip %s mode %d: %p", entryName.string(), + dataMap->getFileName(), mode, pAsset); + } else { + pAsset = Asset::createFromCompressedMap(dataMap, method, + uncompressedLen, mode); + LOGV("Opened compressed entry %s in zip %s mode %d: %p", entryName.string(), + dataMap->getFileName(), mode, pAsset); + } + if (pAsset == NULL) { + /* unexpected */ + LOGW("create from segment failed\n"); + } + + return pAsset; +} + + + +/* + * Open a directory in the asset namespace. + * + * An "asset directory" is simply the combination of all files in all + * locations, with ".gz" stripped for loose files. With app, locale, and + * vendor defined, we have 8 directories and 2 Zip archives to scan. + * + * Pass in "" for the root dir. + */ +AssetDir* AssetManager::openDir(const char* dirName) +{ + AutoMutex _l(mLock); + + AssetDir* pDir = NULL; + SortedVector* pMergedInfo = NULL; + + LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager"); + assert(dirName != NULL); + + //printf("+++ openDir(%s) in '%s'\n", dirName, (const char*) mAssetBase); + + if (mCacheMode != CACHE_OFF && !mCacheValid) + loadFileNameCacheLocked(); + + pDir = new AssetDir; + + /* + * Scan the various directories, merging what we find into a single + * vector. We want to scan them in reverse priority order so that + * the ".EXCLUDE" processing works correctly. Also, if we decide we + * want to remember where the file is coming from, we'll get the right + * version. + * + * We start with Zip archives, then do loose files. + */ + pMergedInfo = new SortedVector; + + size_t i = mAssetPaths.size(); + while (i > 0) { + i--; + const asset_path& ap = mAssetPaths.itemAt(i); + if (ap.type == kFileTypeRegular) { + LOGV("Adding directory %s from zip %s", dirName, ap.path.string()); + scanAndMergeZipLocked(pMergedInfo, ap, kAssetsRoot, dirName); + } else { + LOGV("Adding directory %s from dir %s", dirName, ap.path.string()); + scanAndMergeDirLocked(pMergedInfo, ap, kAssetsRoot, dirName); + } + } + +#if 0 + printf("FILE LIST:\n"); + for (i = 0; i < (size_t) pMergedInfo->size(); i++) { + printf(" %d: (%d) '%s'\n", i, + pMergedInfo->itemAt(i).getFileType(), + (const char*) pMergedInfo->itemAt(i).getFileName()); + } +#endif + + pDir->setFileList(pMergedInfo); + return pDir; +} + +/* + * Scan the contents of the specified directory and merge them into the + * "pMergedInfo" vector, removing previous entries if we find "exclude" + * directives. + * + * Returns "false" if we found nothing to contribute. + */ +bool AssetManager::scanAndMergeDirLocked(SortedVector* pMergedInfo, + const asset_path& ap, const char* rootDir, const char* dirName) +{ + SortedVector* pContents; + String8 path; + + assert(pMergedInfo != NULL); + + //printf("scanAndMergeDir: %s %s %s %s\n", appName, locale, vendor,dirName); + + if (mCacheValid) { + int i, start, count; + + pContents = new SortedVector; + + /* + * Get the basic partial path and find it in the cache. That's + * the start point for the search. + */ + path = createPathNameLocked(ap, rootDir); + if (dirName[0] != '\0') + path.appendPath(dirName); + + start = mCache.indexOf(path); + if (start == NAME_NOT_FOUND) { + //printf("+++ not found in cache: dir '%s'\n", (const char*) path); + delete pContents; + return false; + } + + /* + * The match string looks like "common/default/default/foo/bar/". + * The '/' on the end ensures that we don't match on the directory + * itself or on ".../foo/barfy/". + */ + path.append("/"); + + count = mCache.size(); + + /* + * Pick out the stuff in the current dir by examining the pathname. + * It needs to match the partial pathname prefix, and not have a '/' + * (fssep) anywhere after the prefix. + */ + for (i = start+1; i < count; i++) { + if (mCache[i].getFileName().length() > path.length() && + strncmp(mCache[i].getFileName().string(), path.string(), path.length()) == 0) + { + const char* name = mCache[i].getFileName().string(); + // XXX THIS IS BROKEN! Looks like we need to store the full + // path prefix separately from the file path. + if (strchr(name + path.length(), '/') == NULL) { + /* grab it, reducing path to just the filename component */ + AssetDir::FileInfo tmp = mCache[i]; + tmp.setFileName(tmp.getFileName().getPathLeaf()); + pContents->add(tmp); + } + } else { + /* no longer in the dir or its subdirs */ + break; + } + + } + } else { + path = createPathNameLocked(ap, rootDir); + if (dirName[0] != '\0') + path.appendPath(dirName); + pContents = scanDirLocked(path); + if (pContents == NULL) + return false; + } + + // if we wanted to do an incremental cache fill, we would do it here + + /* + * Process "exclude" directives. If we find a filename that ends with + * ".EXCLUDE", we look for a matching entry in the "merged" set, and + * remove it if we find it. We also delete the "exclude" entry. + */ + int i, count, exclExtLen; + + count = pContents->size(); + exclExtLen = strlen(kExcludeExtension); + for (i = 0; i < count; i++) { + const char* name; + int nameLen; + + name = pContents->itemAt(i).getFileName().string(); + nameLen = strlen(name); + if (nameLen > exclExtLen && + strcmp(name + (nameLen - exclExtLen), kExcludeExtension) == 0) + { + String8 match(name, nameLen - exclExtLen); + int matchIdx; + + matchIdx = AssetDir::FileInfo::findEntry(pMergedInfo, match); + if (matchIdx > 0) { + LOGV("Excluding '%s' [%s]\n", + pMergedInfo->itemAt(matchIdx).getFileName().string(), + pMergedInfo->itemAt(matchIdx).getSourceName().string()); + pMergedInfo->removeAt(matchIdx); + } else { + //printf("+++ no match on '%s'\n", (const char*) match); + } + + LOGD("HEY: size=%d removing %d\n", (int)pContents->size(), i); + pContents->removeAt(i); + i--; // adjust "for" loop + count--; // and loop limit + } + } + + mergeInfoLocked(pMergedInfo, pContents); + + delete pContents; + + return true; +} + +/* + * Scan the contents of the specified directory, and stuff what we find + * into a newly-allocated vector. + * + * Files ending in ".gz" will have their extensions removed. + * + * We should probably think about skipping files with "illegal" names, + * e.g. illegal characters (/\:) or excessive length. + * + * Returns NULL if the specified directory doesn't exist. + */ +SortedVector* AssetManager::scanDirLocked(const String8& path) +{ + SortedVector* pContents = NULL; + DIR* dir; + struct dirent* entry; + FileType fileType; + + LOGV("Scanning dir '%s'\n", path.string()); + + dir = opendir(path.string()); + if (dir == NULL) + return NULL; + + pContents = new SortedVector; + + while (1) { + entry = readdir(dir); + if (entry == NULL) + break; + + if (strcmp(entry->d_name, ".") == 0 || + strcmp(entry->d_name, "..") == 0) + continue; + +#ifdef _DIRENT_HAVE_D_TYPE + if (entry->d_type == DT_REG) + fileType = kFileTypeRegular; + else if (entry->d_type == DT_DIR) + fileType = kFileTypeDirectory; + else + fileType = kFileTypeUnknown; +#else + // stat the file + fileType = ::getFileType(path.appendPathCopy(entry->d_name).string()); +#endif + + if (fileType != kFileTypeRegular && fileType != kFileTypeDirectory) + continue; + + AssetDir::FileInfo info; + info.set(String8(entry->d_name), fileType); + if (strcasecmp(info.getFileName().getPathExtension().string(), ".gz") == 0) + info.setFileName(info.getFileName().getBasePath()); + info.setSourceName(path.appendPathCopy(info.getFileName())); + pContents->add(info); + } + + closedir(dir); + return pContents; +} + +/* + * Scan the contents out of the specified Zip archive, and merge what we + * find into "pMergedInfo". If the Zip archive in question doesn't exist, + * we return immediately. + * + * Returns "false" if we found nothing to contribute. + */ +bool AssetManager::scanAndMergeZipLocked(SortedVector* pMergedInfo, + const asset_path& ap, const char* rootDir, const char* baseDirName) +{ + ZipFileRO* pZip; + Vector dirs; + AssetDir::FileInfo info; + SortedVector contents; + String8 sourceName, zipName, dirName; + + pZip = mZipSet.getZip(ap.path); + if (pZip == NULL) { + LOGW("Failure opening zip %s\n", ap.path.string()); + return false; + } + + zipName = ZipSet::getPathName(ap.path.string()); + + /* convert "sounds" to "rootDir/sounds" */ + if (rootDir != NULL) dirName = rootDir; + dirName.appendPath(baseDirName); + + /* + * Scan through the list of files, looking for a match. The files in + * the Zip table of contents are not in sorted order, so we have to + * process the entire list. We're looking for a string that begins + * with the characters in "dirName", is followed by a '/', and has no + * subsequent '/' in the stuff that follows. + * + * What makes this especially fun is that directories are not stored + * explicitly in Zip archives, so we have to infer them from context. + * When we see "sounds/foo.wav" we have to leave a note to ourselves + * to insert a directory called "sounds" into the list. We store + * these in temporary vector so that we only return each one once. + * + * Name comparisons are case-sensitive to match UNIX filesystem + * semantics. + */ + int dirNameLen = dirName.length(); + for (int i = 0; i < pZip->getNumEntries(); i++) { + ZipEntryRO entry; + char nameBuf[256]; + + entry = pZip->findEntryByIndex(i); + if (pZip->getEntryFileName(entry, nameBuf, sizeof(nameBuf)) != 0) { + // TODO: fix this if we expect to have long names + LOGE("ARGH: name too long?\n"); + continue; + } + if (dirNameLen == 0 || + (strncmp(nameBuf, dirName.string(), dirNameLen) == 0 && + nameBuf[dirNameLen] == '/')) + { + const char* cp; + const char* nextSlash; + + cp = nameBuf + dirNameLen; + if (dirNameLen != 0) + cp++; // advance past the '/' + + nextSlash = strchr(cp, '/'); +//xxx this may break if there are bare directory entries + if (nextSlash == NULL) { + /* this is a file in the requested directory */ + + info.set(String8(nameBuf).getPathLeaf(), kFileTypeRegular); + + info.setSourceName( + createZipSourceNameLocked(zipName, dirName, info.getFileName())); + + contents.add(info); + //printf("FOUND: file '%s'\n", (const char*) info.mFileName); + } else { + /* this is a subdir; add it if we don't already have it*/ + String8 subdirName(cp, nextSlash - cp); + size_t j; + size_t N = dirs.size(); + + for (j = 0; j < N; j++) { + if (subdirName == dirs[j]) { + break; + } + } + if (j == N) { + dirs.add(subdirName); + } + + //printf("FOUND: dir '%s'\n", (const char*) subdirName); + } + } + } + + /* + * Add the set of unique directories. + */ + for (int i = 0; i < (int) dirs.size(); i++) { + info.set(dirs[i], kFileTypeDirectory); + info.setSourceName( + createZipSourceNameLocked(zipName, dirName, info.getFileName())); + contents.add(info); + } + + mergeInfoLocked(pMergedInfo, &contents); + + return true; +} + + +/* + * Merge two vectors of FileInfo. + * + * The merged contents will be stuffed into *pMergedInfo. + * + * If an entry for a file exists in both "pMergedInfo" and "pContents", + * we use the newer "pContents" entry. + */ +void AssetManager::mergeInfoLocked(SortedVector* pMergedInfo, + const SortedVector* pContents) +{ + /* + * Merge what we found in this directory with what we found in + * other places. + * + * Two basic approaches: + * (1) Create a new array that holds the unique values of the two + * arrays. + * (2) Take the elements from pContents and shove them into pMergedInfo. + * + * Because these are vectors of complex objects, moving elements around + * inside the vector requires constructing new objects and allocating + * storage for members. With approach #1, we're always adding to the + * end, whereas with #2 we could be inserting multiple elements at the + * front of the vector. Approach #1 requires a full copy of the + * contents of pMergedInfo, but approach #2 requires the same copy for + * every insertion at the front of pMergedInfo. + * + * (We should probably use a SortedVector interface that allows us to + * just stuff items in, trusting us to maintain the sort order.) + */ + SortedVector* pNewSorted; + int mergeMax, contMax; + int mergeIdx, contIdx; + + pNewSorted = new SortedVector; + mergeMax = pMergedInfo->size(); + contMax = pContents->size(); + mergeIdx = contIdx = 0; + + while (mergeIdx < mergeMax || contIdx < contMax) { + if (mergeIdx == mergeMax) { + /* hit end of "merge" list, copy rest of "contents" */ + pNewSorted->add(pContents->itemAt(contIdx)); + contIdx++; + } else if (contIdx == contMax) { + /* hit end of "cont" list, copy rest of "merge" */ + pNewSorted->add(pMergedInfo->itemAt(mergeIdx)); + mergeIdx++; + } else if (pMergedInfo->itemAt(mergeIdx) == pContents->itemAt(contIdx)) + { + /* items are identical, add newer and advance both indices */ + pNewSorted->add(pContents->itemAt(contIdx)); + mergeIdx++; + contIdx++; + } else if (pMergedInfo->itemAt(mergeIdx) < pContents->itemAt(contIdx)) + { + /* "merge" is lower, add that one */ + pNewSorted->add(pMergedInfo->itemAt(mergeIdx)); + mergeIdx++; + } else { + /* "cont" is lower, add that one */ + assert(pContents->itemAt(contIdx) < pMergedInfo->itemAt(mergeIdx)); + pNewSorted->add(pContents->itemAt(contIdx)); + contIdx++; + } + } + + /* + * Overwrite the "merged" list with the new stuff. + */ + *pMergedInfo = *pNewSorted; + delete pNewSorted; + +#if 0 // for Vector, rather than SortedVector + int i, j; + for (i = pContents->size() -1; i >= 0; i--) { + bool add = true; + + for (j = pMergedInfo->size() -1; j >= 0; j--) { + /* case-sensitive comparisons, to behave like UNIX fs */ + if (strcmp(pContents->itemAt(i).mFileName, + pMergedInfo->itemAt(j).mFileName) == 0) + { + /* match, don't add this entry */ + add = false; + break; + } + } + + if (add) + pMergedInfo->add(pContents->itemAt(i)); + } +#endif +} + + +/* + * Load all files into the file name cache. We want to do this across + * all combinations of { appname, locale, vendor }, performing a recursive + * directory traversal. + * + * This is not the most efficient data structure. Also, gathering the + * information as we needed it (file-by-file or directory-by-directory) + * would be faster. However, on the actual device, 99% of the files will + * live in Zip archives, so this list will be very small. The trouble + * is that we have to check the "loose" files first, so it's important + * that we don't beat the filesystem silly looking for files that aren't + * there. + * + * Note on thread safety: this is the only function that causes updates + * to mCache, and anybody who tries to use it will call here if !mCacheValid, + * so we need to employ a mutex here. + */ +void AssetManager::loadFileNameCacheLocked(void) +{ + assert(!mCacheValid); + assert(mCache.size() == 0); + +#ifdef DO_TIMINGS // need to link against -lrt for this now + DurationTimer timer; + timer.start(); +#endif + + fncScanLocked(&mCache, ""); + +#ifdef DO_TIMINGS + timer.stop(); + LOGD("Cache scan took %.3fms\n", + timer.durationUsecs() / 1000.0); +#endif + +#if 0 + int i; + printf("CACHED FILE LIST (%d entries):\n", mCache.size()); + for (i = 0; i < (int) mCache.size(); i++) { + printf(" %d: (%d) '%s'\n", i, + mCache.itemAt(i).getFileType(), + (const char*) mCache.itemAt(i).getFileName()); + } +#endif + + mCacheValid = true; +} + +/* + * Scan up to 8 versions of the specified directory. + */ +void AssetManager::fncScanLocked(SortedVector* pMergedInfo, + const char* dirName) +{ + size_t i = mAssetPaths.size(); + while (i > 0) { + i--; + const asset_path& ap = mAssetPaths.itemAt(i); + fncScanAndMergeDirLocked(pMergedInfo, ap, NULL, NULL, dirName); + if (mLocale != NULL) + fncScanAndMergeDirLocked(pMergedInfo, ap, mLocale, NULL, dirName); + if (mVendor != NULL) + fncScanAndMergeDirLocked(pMergedInfo, ap, NULL, mVendor, dirName); + if (mLocale != NULL && mVendor != NULL) + fncScanAndMergeDirLocked(pMergedInfo, ap, mLocale, mVendor, dirName); + } +} + +/* + * Recursively scan this directory and all subdirs. + * + * This is similar to scanAndMergeDir, but we don't remove the .EXCLUDE + * files, and we prepend the extended partial path to the filenames. + */ +bool AssetManager::fncScanAndMergeDirLocked( + SortedVector* pMergedInfo, + const asset_path& ap, const char* locale, const char* vendor, + const char* dirName) +{ + SortedVector* pContents; + String8 partialPath; + String8 fullPath; + + // XXX This is broken -- the filename cache needs to hold the base + // asset path separately from its filename. + + partialPath = createPathNameLocked(ap, locale, vendor); + if (dirName[0] != '\0') { + partialPath.appendPath(dirName); + } + + fullPath = partialPath; + pContents = scanDirLocked(fullPath); + if (pContents == NULL) { + return false; // directory did not exist + } + + /* + * Scan all subdirectories of the current dir, merging what we find + * into "pMergedInfo". + */ + for (int i = 0; i < (int) pContents->size(); i++) { + if (pContents->itemAt(i).getFileType() == kFileTypeDirectory) { + String8 subdir(dirName); + subdir.appendPath(pContents->itemAt(i).getFileName()); + + fncScanAndMergeDirLocked(pMergedInfo, ap, locale, vendor, subdir.string()); + } + } + + /* + * To be consistent, we want entries for the root directory. If + * we're the root, add one now. + */ + if (dirName[0] == '\0') { + AssetDir::FileInfo tmpInfo; + + tmpInfo.set(String8(""), kFileTypeDirectory); + tmpInfo.setSourceName(createPathNameLocked(ap, locale, vendor)); + pContents->add(tmpInfo); + } + + /* + * We want to prepend the extended partial path to every entry in + * "pContents". It's the same value for each entry, so this will + * not change the sorting order of the vector contents. + */ + for (int i = 0; i < (int) pContents->size(); i++) { + const AssetDir::FileInfo& info = pContents->itemAt(i); + pContents->editItemAt(i).setFileName(partialPath.appendPathCopy(info.getFileName())); + } + + mergeInfoLocked(pMergedInfo, pContents); + return true; +} + +/* + * Trash the cache. + */ +void AssetManager::purgeFileNameCacheLocked(void) +{ + mCacheValid = false; + mCache.clear(); +} + +/* + * =========================================================================== + * AssetManager::SharedZip + * =========================================================================== + */ + + +Mutex AssetManager::SharedZip::gLock; +DefaultKeyedVector > AssetManager::SharedZip::gOpen; + +AssetManager::SharedZip::SharedZip(const String8& path, time_t modWhen) + : mPath(path), mZipFile(NULL), mModWhen(modWhen), mResourceTableAsset(NULL) +{ + //LOGI("Creating SharedZip %p %s\n", this, (const char*)mPath); + mZipFile = new ZipFileRO; + LOGV("+++ opening zip '%s'\n", mPath.string()); + if (mZipFile->open(mPath.string()) != NO_ERROR) { + LOGD("failed to open Zip archive '%s'\n", mPath.string()); + delete mZipFile; + mZipFile = NULL; + } +} + +sp AssetManager::SharedZip::get(const String8& path) +{ + AutoMutex _l(gLock); + time_t modWhen = getFileModDate(path); + sp zip = gOpen.valueFor(path).promote(); + if (zip != NULL && zip->mModWhen == modWhen) { + return zip; + } + zip = new SharedZip(path, modWhen); + gOpen.add(path, zip); + return zip; + +} + +ZipFileRO* AssetManager::SharedZip::getZip() +{ + return mZipFile; +} + +Asset* AssetManager::SharedZip::getResourceTableAsset() +{ + LOGV("Getting from SharedZip %p resource asset %p\n", this, mResourceTableAsset); + return mResourceTableAsset; +} + +Asset* AssetManager::SharedZip::setResourceTableAsset(Asset* asset) +{ + { + AutoMutex _l(gLock); + if (mResourceTableAsset == NULL) { + mResourceTableAsset = asset; + // This is not thread safe the first time it is called, so + // do it here with the global lock held. + asset->getBuffer(true); + return asset; + } + } + delete asset; + return mResourceTableAsset; +} + +bool AssetManager::SharedZip::isUpToDate() +{ + time_t modWhen = getFileModDate(mPath.string()); + return mModWhen == modWhen; +} + +AssetManager::SharedZip::~SharedZip() +{ + //LOGI("Destroying SharedZip %p %s\n", this, (const char*)mPath); + if (mResourceTableAsset != NULL) { + delete mResourceTableAsset; + } + if (mZipFile != NULL) { + delete mZipFile; + LOGV("Closed '%s'\n", mPath.string()); + } +} + +/* + * =========================================================================== + * AssetManager::ZipSet + * =========================================================================== + */ + +/* + * Constructor. + */ +AssetManager::ZipSet::ZipSet(void) +{ +} + +/* + * Destructor. Close any open archives. + */ +AssetManager::ZipSet::~ZipSet(void) +{ + size_t N = mZipFile.size(); + for (size_t i = 0; i < N; i++) + closeZip(i); +} + +/* + * Close a Zip file and reset the entry. + */ +void AssetManager::ZipSet::closeZip(int idx) +{ + mZipFile.editItemAt(idx) = NULL; +} + + +/* + * Retrieve the appropriate Zip file from the set. + */ +ZipFileRO* AssetManager::ZipSet::getZip(const String8& path) +{ + int idx = getIndex(path); + sp zip = mZipFile[idx]; + if (zip == NULL) { + zip = SharedZip::get(path); + mZipFile.editItemAt(idx) = zip; + } + return zip->getZip(); +} + +Asset* AssetManager::ZipSet::getZipResourceTable(const String8& path) +{ + int idx = getIndex(path); + sp zip = mZipFile[idx]; + if (zip == NULL) { + zip = SharedZip::get(path); + mZipFile.editItemAt(idx) = zip; + } + return zip->getResourceTableAsset(); +} + +Asset* AssetManager::ZipSet::setZipResourceTable(const String8& path, + Asset* asset) +{ + int idx = getIndex(path); + sp zip = mZipFile[idx]; + // doesn't make sense to call before previously accessing. + return zip->setResourceTableAsset(asset); +} + +/* + * Generate the partial pathname for the specified archive. The caller + * gets to prepend the asset root directory. + * + * Returns something like "common/en-US-noogle.jar". + */ +/*static*/ String8 AssetManager::ZipSet::getPathName(const char* zipPath) +{ + return String8(zipPath); +} + +bool AssetManager::ZipSet::isUpToDate() +{ + const size_t N = mZipFile.size(); + for (size_t i=0; iisUpToDate()) { + return false; + } + } + return true; +} + +/* + * Compute the zip file's index. + * + * "appName", "locale", and "vendor" should be set to NULL to indicate the + * default directory. + */ +int AssetManager::ZipSet::getIndex(const String8& zip) const +{ + const size_t N = mZipPath.size(); + for (size_t i=0; i + +#include +#include +#include +#include + +#include + +namespace android { + +// --------------------------------------------------------------------------- + +sp IBinder::queryLocalInterface(const String16& descriptor) +{ + return NULL; +} + +BBinder* IBinder::localBinder() +{ + return NULL; +} + +BpBinder* IBinder::remoteBinder() +{ + return NULL; +} + +bool IBinder::checkSubclass(const void* /*subclassID*/) const +{ + return false; +} + +// --------------------------------------------------------------------------- + +class BBinder::Extras +{ +public: + Mutex mLock; + BpBinder::ObjectManager mObjects; +}; + +// --------------------------------------------------------------------------- + +BBinder::BBinder() + : mExtras(NULL) +{ +} + +bool BBinder::isBinderAlive() const +{ + return true; +} + +status_t BBinder::pingBinder() +{ + return NO_ERROR; +} + +String16 BBinder::getInterfaceDescriptor() const +{ + LOGW("reached BBinder::getInterfaceDescriptor (this=%p)", this); + return String16(); +} + +status_t BBinder::transact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + data.setDataPosition(0); + + status_t err = NO_ERROR; + switch (code) { + case PING_TRANSACTION: + reply->writeInt32(pingBinder()); + break; + default: + err = onTransact(code, data, reply, flags); + break; + } + + if (reply != NULL) { + reply->setDataPosition(0); + } + + return err; +} + +status_t BBinder::linkToDeath( + const sp& recipient, void* cookie, uint32_t flags) +{ + return INVALID_OPERATION; +} + +status_t BBinder::unlinkToDeath( + const wp& recipient, void* cookie, uint32_t flags, + wp* outRecipient) +{ + return INVALID_OPERATION; +} + +status_t BBinder::dump(int fd, const Vector& args) +{ + return NO_ERROR; +} + +void BBinder::attachObject( + const void* objectID, void* object, void* cleanupCookie, + object_cleanup_func func) +{ + Extras* e = mExtras; + + if (!e) { + e = new Extras; + if (android_atomic_cmpxchg(0, reinterpret_cast(e), + reinterpret_cast(&mExtras)) != 0) { + delete e; + e = mExtras; + } + if (e == 0) return; // out of memory + } + + AutoMutex _l(e->mLock); + e->mObjects.attach(objectID, object, cleanupCookie, func); +} + +void* BBinder::findObject(const void* objectID) const +{ + Extras* e = mExtras; + if (!e) return NULL; + + AutoMutex _l(e->mLock); + return e->mObjects.find(objectID); +} + +void BBinder::detachObject(const void* objectID) +{ + Extras* e = mExtras; + if (!e) return; + + AutoMutex _l(e->mLock); + e->mObjects.detach(objectID); +} + +BBinder* BBinder::localBinder() +{ + return this; +} + +BBinder::~BBinder() +{ + if (mExtras) delete mExtras; +} + + +status_t BBinder::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch (code) { + case INTERFACE_TRANSACTION: + reply->writeString16(getInterfaceDescriptor()); + return NO_ERROR; + + case DUMP_TRANSACTION: { + int fd = data.readFileDescriptor(); + int argc = data.readInt32(); + Vector args; + for (int i = 0; i < argc && data.dataAvail() > 0; i++) { + args.add(data.readString16()); + } + return dump(fd, args); + } + default: + return UNKNOWN_TRANSACTION; + } +} + +// --------------------------------------------------------------------------- + +enum { + // This is used to transfer ownership of the remote binder from + // the BpRefBase object holding it (when it is constructed), to the + // owner of the BpRefBase object when it first acquires that BpRefBase. + kRemoteAcquired = 0x00000001 +}; + +BpRefBase::BpRefBase(const sp& o) + : mRemote(o.get()), mRefs(NULL), mState(0) +{ + extendObjectLifetime(OBJECT_LIFETIME_WEAK); + + if (mRemote) { + mRemote->incStrong(this); // Removed on first IncStrong(). + mRefs = mRemote->createWeak(this); // Held for our entire lifetime. + } +} + +BpRefBase::~BpRefBase() +{ + if (mRemote) { + if (!(mState&kRemoteAcquired)) { + mRemote->decStrong(this); + } + mRefs->decWeak(this); + } +} + +void BpRefBase::onFirstRef() +{ + android_atomic_or(kRemoteAcquired, &mState); +} + +void BpRefBase::onLastStrongRef(const void* id) +{ + if (mRemote) { + mRemote->decStrong(this); + } +} + +bool BpRefBase::onIncStrongAttempted(uint32_t flags, const void* id) +{ + return mRemote ? mRefs->attemptIncStrong(this) : false; +} + +// --------------------------------------------------------------------------- + +}; // namespace android diff --git a/libs/utils/BpBinder.cpp b/libs/utils/BpBinder.cpp new file mode 100644 index 000000000..69ab19574 --- /dev/null +++ b/libs/utils/BpBinder.cpp @@ -0,0 +1,348 @@ +/* + * Copyright (C) 2005 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 "BpBinder" +//#define LOG_NDEBUG 0 + +#include + +#include +#include + +#include + +//#undef LOGV +//#define LOGV(...) fprintf(stderr, __VA_ARGS__) + +namespace android { + +// --------------------------------------------------------------------------- + +BpBinder::ObjectManager::ObjectManager() +{ +} + +BpBinder::ObjectManager::~ObjectManager() +{ + kill(); +} + +void BpBinder::ObjectManager::attach( + const void* objectID, void* object, void* cleanupCookie, + IBinder::object_cleanup_func func) +{ + entry_t e; + e.object = object; + e.cleanupCookie = cleanupCookie; + e.func = func; + + if (mObjects.indexOfKey(objectID) >= 0) { + LOGE("Trying to attach object ID %p to binder ObjectManager %p with object %p, but object ID already in use", + objectID, this, object); + return; + } + + mObjects.add(objectID, e); +} + +void* BpBinder::ObjectManager::find(const void* objectID) const +{ + const ssize_t i = mObjects.indexOfKey(objectID); + if (i < 0) return NULL; + return mObjects.valueAt(i).object; +} + +void BpBinder::ObjectManager::detach(const void* objectID) +{ + mObjects.removeItem(objectID); +} + +void BpBinder::ObjectManager::kill() +{ + const size_t N = mObjects.size(); + LOGV("Killing %d objects in manager %p", N, this); + for (size_t i=0; iincWeakHandle(handle); +} + +String16 BpBinder::getInterfaceDescriptor() const +{ + String16 res; + Parcel send, reply; + status_t err = const_cast(this)->transact( + INTERFACE_TRANSACTION, send, &reply); + if (err == NO_ERROR) { + res = reply.readString16(); + } + return res; +} + +bool BpBinder::isBinderAlive() const +{ + return mAlive != 0; +} + +status_t BpBinder::pingBinder() +{ + Parcel send; + Parcel reply; + status_t err = transact(PING_TRANSACTION, send, &reply); + if (err != NO_ERROR) return err; + if (reply.dataSize() < sizeof(status_t)) return NOT_ENOUGH_DATA; + return (status_t)reply.readInt32(); +} + +status_t BpBinder::dump(int fd, const Vector& args) +{ + Parcel send; + Parcel reply; + send.writeFileDescriptor(fd); + const size_t numArgs = args.size(); + send.writeInt32(numArgs); + for (size_t i = 0; i < numArgs; i++) { + send.writeString16(args[i]); + } + status_t err = transact(DUMP_TRANSACTION, send, &reply); + return err; +} + +status_t BpBinder::transact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + // Once a binder has died, it will never come back to life. + if (mAlive) { + status_t status = IPCThreadState::self()->transact( + mHandle, code, data, reply, flags); + if (status == DEAD_OBJECT) mAlive = 0; + return status; + } + + return DEAD_OBJECT; +} + +status_t BpBinder::linkToDeath( + const sp& recipient, void* cookie, uint32_t flags) +{ + Obituary ob; + ob.recipient = recipient; + ob.cookie = cookie; + ob.flags = flags; + + LOG_ALWAYS_FATAL_IF(recipient == NULL, + "linkToDeath(): recipient must be non-NULL"); + + { + AutoMutex _l(mLock); + + if (!mObitsSent) { + if (!mObituaries) { + mObituaries = new Vector; + if (!mObituaries) { + return NO_MEMORY; + } + LOGV("Requesting death notification: %p handle %d\n", this, mHandle); + getWeakRefs()->incWeak(this); + IPCThreadState* self = IPCThreadState::self(); + self->requestDeathNotification(mHandle, this); + self->flushCommands(); + } + ssize_t res = mObituaries->add(ob); + return res >= (ssize_t)NO_ERROR ? (status_t)NO_ERROR : res; + } + } + + return DEAD_OBJECT; +} + +status_t BpBinder::unlinkToDeath( + const wp& recipient, void* cookie, uint32_t flags, + wp* outRecipient) +{ + AutoMutex _l(mLock); + + if (mObitsSent) { + return DEAD_OBJECT; + } + + const size_t N = mObituaries ? mObituaries->size() : 0; + for (size_t i=0; iitemAt(i); + if ((obit.recipient == recipient + || (recipient == NULL && obit.cookie == cookie)) + && obit.flags == flags) { + const uint32_t allFlags = obit.flags|flags; + if (outRecipient != NULL) { + *outRecipient = mObituaries->itemAt(i).recipient; + } + mObituaries->removeAt(i); + if (mObituaries->size() == 0) { + LOGV("Clearing death notification: %p handle %d\n", this, mHandle); + IPCThreadState* self = IPCThreadState::self(); + self->clearDeathNotification(mHandle, this); + self->flushCommands(); + delete mObituaries; + mObituaries = NULL; + } + return NO_ERROR; + } + } + + return NAME_NOT_FOUND; +} + +void BpBinder::sendObituary() +{ + LOGV("Sending obituary for proxy %p handle %d, mObitsSent=%s\n", + this, mHandle, mObitsSent ? "true" : "false"); + + mAlive = 0; + if (mObitsSent) return; + + mLock.lock(); + Vector* obits = mObituaries; + if(obits != NULL) { + LOGV("Clearing sent death notification: %p handle %d\n", this, mHandle); + IPCThreadState* self = IPCThreadState::self(); + self->clearDeathNotification(mHandle, this); + self->flushCommands(); + mObituaries = NULL; + } + mObitsSent = 1; + mLock.unlock(); + + LOGV("Reporting death of proxy %p for %d recipients\n", + this, obits ? obits->size() : 0); + + if (obits != NULL) { + const size_t N = obits->size(); + for (size_t i=0; iitemAt(i)); + } + + delete obits; + } +} + +void BpBinder::reportOneDeath(const Obituary& obit) +{ + sp recipient = obit.recipient.promote(); + LOGV("Reporting death to recipient: %p\n", recipient.get()); + if (recipient == NULL) return; + + recipient->binderDied(this); +} + + +void BpBinder::attachObject( + const void* objectID, void* object, void* cleanupCookie, + object_cleanup_func func) +{ + AutoMutex _l(mLock); + LOGV("Attaching object %p to binder %p (manager=%p)", object, this, &mObjects); + mObjects.attach(objectID, object, cleanupCookie, func); +} + +void* BpBinder::findObject(const void* objectID) const +{ + AutoMutex _l(mLock); + return mObjects.find(objectID); +} + +void BpBinder::detachObject(const void* objectID) +{ + AutoMutex _l(mLock); + mObjects.detach(objectID); +} + +BpBinder* BpBinder::remoteBinder() +{ + return this; +} + +BpBinder::~BpBinder() +{ + LOGV("Destroying BpBinder %p handle %d\n", this, mHandle); + + IPCThreadState* ipc = IPCThreadState::self(); + + mLock.lock(); + Vector* obits = mObituaries; + if(obits != NULL) { + if (ipc) ipc->clearDeathNotification(mHandle, this); + mObituaries = NULL; + } + mLock.unlock(); + + if (obits != NULL) { + // XXX Should we tell any remaining DeathRecipient + // objects that the last strong ref has gone away, so they + // are no longer linked? + delete obits; + } + + if (ipc) { + ipc->expungeHandle(mHandle, this); + ipc->decWeakHandle(mHandle); + } +} + +void BpBinder::onFirstRef() +{ + LOGV("onFirstRef BpBinder %p handle %d\n", this, mHandle); + IPCThreadState* ipc = IPCThreadState::self(); + if (ipc) ipc->incStrongHandle(mHandle); +} + +void BpBinder::onLastStrongRef(const void* id) +{ + LOGV("onLastStrongRef BpBinder %p handle %d\n", this, mHandle); + IF_LOGV() { + printRefs(); + } + IPCThreadState* ipc = IPCThreadState::self(); + if (ipc) ipc->decStrongHandle(mHandle); +} + +bool BpBinder::onIncStrongAttempted(uint32_t flags, const void* id) +{ + LOGV("onIncStrongAttempted BpBinder %p handle %d\n", this, mHandle); + IPCThreadState* ipc = IPCThreadState::self(); + return ipc ? ipc->attemptIncStrongHandle(mHandle) == NO_ERROR : false; +} + +// --------------------------------------------------------------------------- + +}; // namespace android diff --git a/libs/utils/BufferedTextOutput.cpp b/libs/utils/BufferedTextOutput.cpp new file mode 100644 index 000000000..989662e84 --- /dev/null +++ b/libs/utils/BufferedTextOutput.cpp @@ -0,0 +1,279 @@ +/* + * Copyright (C) 2006 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 + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +// --------------------------------------------------------------------------- + +namespace android { + +struct BufferedTextOutput::BufferState : public RefBase +{ + BufferState(int32_t _seq) + : seq(_seq) + , buffer(NULL) + , bufferPos(0) + , bufferSize(0) + , atFront(true) + , indent(0) + , bundle(0) { + } + ~BufferState() { + free(buffer); + } + + status_t append(const char* txt, size_t len) { + if ((len+bufferPos) > bufferSize) { + void* b = realloc(buffer, ((len+bufferPos)*3)/2); + if (!b) return NO_MEMORY; + buffer = (char*)b; + } + memcpy(buffer+bufferPos, txt, len); + bufferPos += len; + return NO_ERROR; + } + + void restart() { + bufferPos = 0; + atFront = true; + if (bufferSize > 256) { + void* b = realloc(buffer, 256); + if (b) { + buffer = (char*)b; + bufferSize = 256; + } + } + } + + const int32_t seq; + char* buffer; + size_t bufferPos; + size_t bufferSize; + bool atFront; + int32_t indent; + int32_t bundle; +}; + +struct BufferedTextOutput::ThreadState +{ + Vector > states; +}; + +static mutex_t gMutex; + +static thread_store_t tls; + +BufferedTextOutput::ThreadState* BufferedTextOutput::getThreadState() +{ + ThreadState* ts = (ThreadState*) thread_store_get( &tls ); + if (ts) return ts; + ts = new ThreadState; + thread_store_set( &tls, ts, threadDestructor ); + return ts; +} + +void BufferedTextOutput::threadDestructor(void *st) +{ + delete ((ThreadState*)st); +} + +static volatile int32_t gSequence = 0; + +static volatile int32_t gFreeBufferIndex = -1; + +static int32_t allocBufferIndex() +{ + int32_t res = -1; + + mutex_lock(&gMutex); + + if (gFreeBufferIndex >= 0) { + res = gFreeBufferIndex; + gFreeBufferIndex = gTextBuffers[res]; + gTextBuffers.editItemAt(res) = -1; + + } else { + res = gTextBuffers.size(); + gTextBuffers.add(-1); + } + + mutex_unlock(&gMutex); + + return res; +} + +static void freeBufferIndex(int32_t idx) +{ + mutex_lock(&gMutex); + gTextBuffers.editItemAt(idx) = gFreeBufferIndex; + gFreeBufferIndex = idx; + mutex_unlock(&gMutex); +} + +// --------------------------------------------------------------------------- + +BufferedTextOutput::BufferedTextOutput(uint32_t flags) + : mFlags(flags) + , mSeq(android_atomic_inc(&gSequence)) + , mIndex(allocBufferIndex()) +{ + mGlobalState = new BufferState(mSeq); + if (mGlobalState) mGlobalState->incStrong(this); +} + +BufferedTextOutput::~BufferedTextOutput() +{ + if (mGlobalState) mGlobalState->decStrong(this); + freeBufferIndex(mIndex); +} + +status_t BufferedTextOutput::print(const char* txt, size_t len) +{ + //printf("BufferedTextOutput: printing %d\n", len); + + AutoMutex _l(mLock); + BufferState* b = getBuffer(); + + const char* const end = txt+len; + + status_t err; + + while (txt < end) { + // Find the next line. + const char* first = txt; + while (txt < end && *txt != '\n') txt++; + + // Include this and all following empty lines. + while (txt < end && *txt == '\n') txt++; + + // Special cases for first data on a line. + if (b->atFront) { + if (b->indent > 0) { + // If this is the start of a line, add the indent. + const char* prefix = stringForIndent(b->indent); + err = b->append(prefix, strlen(prefix)); + if (err != NO_ERROR) return err; + + } else if (*(txt-1) == '\n' && !b->bundle) { + // Fast path: if we are not indenting or bundling, and + // have been given one or more complete lines, just write + // them out without going through the buffer. + + // Slurp up all of the lines. + const char* lastLine = txt+1; + while (txt < end) { + if (*txt++ == '\n') lastLine = txt; + } + struct iovec vec; + vec.iov_base = (void*)first; + vec.iov_len = lastLine-first; + //printf("Writing %d bytes of data!\n", vec.iov_len); + writeLines(vec, 1); + txt = lastLine; + continue; + } + } + + // Append the new text to the buffer. + err = b->append(first, txt-first); + if (err != NO_ERROR) return err; + b->atFront = *(txt-1) == '\n'; + + // If we have finished a line and are not bundling, write + // it out. + //printf("Buffer is now %d bytes\n", b->bufferPos); + if (b->atFront && !b->bundle) { + struct iovec vec; + vec.iov_base = b->buffer; + vec.iov_len = b->bufferPos; + //printf("Writing %d bytes of data!\n", vec.iov_len); + writeLines(vec, 1); + b->restart(); + } + } + + return NO_ERROR; +} + +void BufferedTextOutput::moveIndent(int delta) +{ + AutoMutex _l(mLock); + BufferState* b = getBuffer(); + b->indent += delta; + if (b->indent < 0) b->indent = 0; +} + +void BufferedTextOutput::pushBundle() +{ + AutoMutex _l(mLock); + BufferState* b = getBuffer(); + b->bundle++; +} + +void BufferedTextOutput::popBundle() +{ + AutoMutex _l(mLock); + BufferState* b = getBuffer(); + b->bundle--; + LOG_FATAL_IF(b->bundle < 0, + "TextOutput::popBundle() called more times than pushBundle()"); + if (b->bundle < 0) b->bundle = 0; + + if (b->bundle == 0) { + // Last bundle, write out data if it is complete. If it is not + // complete, don't write until the last line is done... this may + // or may not be the write thing to do, but it's the easiest. + if (b->bufferPos > 0 && b->atFront) { + struct iovec vec; + vec.iov_base = b->buffer; + vec.iov_len = b->bufferPos; + writeLines(vec, 1); + b->restart(); + } + } +} + +BufferedTextOutput::BufferState* BufferedTextOutput::getBuffer() const +{ + if ((mFlags&MULTITHREADED) != 0) { + ThreadState* ts = getThreadState(); + if (ts) { + while (ts->states.size() <= (size_t)mIndex) ts->states.add(NULL); + BufferState* bs = ts->states[mIndex].get(); + if (bs != NULL && bs->seq == mSeq) return bs; + + ts->states.editItemAt(mIndex) = new BufferState(mIndex); + bs = ts->states[mIndex].get(); + if (bs != NULL) return bs; + } + } + + return mGlobalState; +} + +}; // namespace android diff --git a/libs/utils/CallStack.cpp b/libs/utils/CallStack.cpp new file mode 100644 index 000000000..26fb22abc --- /dev/null +++ b/libs/utils/CallStack.cpp @@ -0,0 +1,335 @@ +/* + * 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. + */ + +#define LOG_TAG "CallStack" + +#include +#include +#include + +#if HAVE_DLADDR +#include +#endif + +#if HAVE_CXXABI +#include +#endif + +#include + +#include +#include +#include +#include + + +/*****************************************************************************/ +namespace android { + + +typedef struct { + size_t count; + size_t ignore; + const void** addrs; +} stack_crawl_state_t; + +static +_Unwind_Reason_Code trace_function(_Unwind_Context *context, void *arg) +{ + stack_crawl_state_t* state = (stack_crawl_state_t*)arg; + if (state->count) { + void* ip = (void*)_Unwind_GetIP(context); + if (ip) { + if (state->ignore) { + state->ignore--; + } else { + state->addrs[0] = ip; + state->addrs++; + state->count--; + } + } + } + return _URC_NO_REASON; +} + +static +int backtrace(const void** addrs, size_t ignore, size_t size) +{ + stack_crawl_state_t state; + state.count = size; + state.ignore = ignore; + state.addrs = addrs; + _Unwind_Backtrace(trace_function, (void*)&state); + return size - state.count; +} + +/*****************************************************************************/ + +static +const char *lookup_symbol(const void* addr, void **offset, char* name, size_t bufSize) +{ +#if HAVE_DLADDR + Dl_info info; + if (dladdr(addr, &info)) { + *offset = info.dli_saddr; + return info.dli_sname; + } +#endif + return NULL; +} + +static +int32_t linux_gcc_demangler(const char *mangled_name, char *unmangled_name, size_t buffersize) +{ + size_t out_len = 0; +#if HAVE_CXXABI + int status = 0; + char *demangled = abi::__cxa_demangle(mangled_name, 0, &out_len, &status); + if (status == 0) { + // OK + if (out_len < buffersize) memcpy(unmangled_name, demangled, out_len); + else out_len = 0; + free(demangled); + } else { + out_len = 0; + } +#endif + return out_len; +} + +/*****************************************************************************/ + +class MapInfo { + struct mapinfo { + struct mapinfo *next; + uint64_t start; + uint64_t end; + char name[]; + }; + + const char *map_to_name(uint64_t pc, const char* def) { + mapinfo* mi = getMapInfoList(); + while(mi) { + if ((pc >= mi->start) && (pc < mi->end)) + return mi->name; + mi = mi->next; + } + return def; + } + + mapinfo *parse_maps_line(char *line) { + mapinfo *mi; + int len = strlen(line); + if (len < 1) return 0; + line[--len] = 0; + if (len < 50) return 0; + if (line[20] != 'x') return 0; + mi = (mapinfo*)malloc(sizeof(mapinfo) + (len - 47)); + if (mi == 0) return 0; + mi->start = strtoull(line, 0, 16); + mi->end = strtoull(line + 9, 0, 16); + mi->next = 0; + strcpy(mi->name, line + 49); + return mi; + } + + mapinfo* getMapInfoList() { + Mutex::Autolock _l(mLock); + if (milist == 0) { + char data[1024]; + FILE *fp; + sprintf(data, "/proc/%d/maps", getpid()); + fp = fopen(data, "r"); + if (fp) { + while(fgets(data, 1024, fp)) { + mapinfo *mi = parse_maps_line(data); + if(mi) { + mi->next = milist; + milist = mi; + } + } + fclose(fp); + } + } + return milist; + } + mapinfo* milist; + Mutex mLock; + static MapInfo sMapInfo; + +public: + MapInfo() + : milist(0) { + } + + ~MapInfo() { + while (milist) { + mapinfo *next = milist->next; + free(milist); + milist = next; + } + } + + static const char *mapAddressToName(const void* pc, const char* def) { + return sMapInfo.map_to_name((uint64_t)pc, def); + } + +}; + +/*****************************************************************************/ + +MapInfo MapInfo::sMapInfo; + +/*****************************************************************************/ + +CallStack::CallStack() + : mCount(0) +{ +} + +CallStack::CallStack(const CallStack& rhs) + : mCount(rhs.mCount) +{ + if (mCount) { + memcpy(mStack, rhs.mStack, mCount*sizeof(void*)); + } +} + +CallStack::~CallStack() +{ +} + +CallStack& CallStack::operator = (const CallStack& rhs) +{ + mCount = rhs.mCount; + if (mCount) { + memcpy(mStack, rhs.mStack, mCount*sizeof(void*)); + } + return *this; +} + +bool CallStack::operator == (const CallStack& rhs) const { + if (mCount != rhs.mCount) + return false; + return !mCount || (memcmp(mStack, rhs.mStack, mCount*sizeof(void*)) == 0); +} + +bool CallStack::operator != (const CallStack& rhs) const { + return !operator == (rhs); +} + +bool CallStack::operator < (const CallStack& rhs) const { + if (mCount != rhs.mCount) + return mCount < rhs.mCount; + return memcmp(mStack, rhs.mStack, mCount*sizeof(void*)) < 0; +} + +bool CallStack::operator >= (const CallStack& rhs) const { + return !operator < (rhs); +} + +bool CallStack::operator > (const CallStack& rhs) const { + if (mCount != rhs.mCount) + return mCount > rhs.mCount; + return memcmp(mStack, rhs.mStack, mCount*sizeof(void*)) > 0; +} + +bool CallStack::operator <= (const CallStack& rhs) const { + return !operator > (rhs); +} + +const void* CallStack::operator [] (int index) const { + if (index >= int(mCount)) + return 0; + return mStack[index]; +} + + +void CallStack::clear() +{ + mCount = 0; +} + +void CallStack::update(int32_t ignoreDepth, int32_t maxDepth) +{ + if (maxDepth > MAX_DEPTH) + maxDepth = MAX_DEPTH; + mCount = backtrace(mStack, ignoreDepth, maxDepth); +} + +// Return the stack frame name on the designated level +String8 CallStack::toStringSingleLevel(const char* prefix, int32_t level) const +{ + String8 res; + char namebuf[1024]; + char tmp[256]; + char tmp1[32]; + char tmp2[32]; + void *offs; + + const void* ip = mStack[level]; + if (!ip) return res; + + if (prefix) res.append(prefix); + snprintf(tmp1, 32, "#%02d ", level); + res.append(tmp1); + + const char* name = lookup_symbol(ip, &offs, namebuf, sizeof(namebuf)); + if (name) { + if (linux_gcc_demangler(name, tmp, 256) != 0) + name = tmp; + snprintf(tmp1, 32, "0x%p: <", ip); + snprintf(tmp2, 32, ">+0x%p", offs); + res.append(tmp1); + res.append(name); + res.append(tmp2); + } else { + name = MapInfo::mapAddressToName(ip, ""); + snprintf(tmp, 256, "pc %p %s", ip, name); + res.append(tmp); + } + res.append("\n"); + + return res; +} + +// Dump a stack trace to the log +void CallStack::dump(const char* prefix) const +{ + /* + * Sending a single long log may be truncated since the stack levels can + * get very deep. So we request function names of each frame individually. + */ + for (int i=0; i + +#include + +#include +#include +#include + +namespace android { + +// --------------------------------------------------------------------- + +static const char indentStr[] = +" " +" "; + +const char* stringForIndent(int32_t indentLevel) +{ + ssize_t off = sizeof(indentStr)-1-(indentLevel*2); + return indentStr + (off < 0 ? 0 : off); +} + +// --------------------------------------------------------------------- + +static void defaultPrintFunc(void* cookie, const char* txt) +{ + printf("%s", txt); +} + +// --------------------------------------------------------------------- + +static inline int isident(int c) +{ + return isalnum(c) || c == '_'; +} + +static inline bool isasciitype(char c) +{ + if( c >= ' ' && c < 127 && c != '\'' && c != '\\' ) return true; + return false; +} + +static inline char makehexdigit(uint32_t val) +{ + return "0123456789abcdef"[val&0xF]; +} + +static char* appendhexnum(uint32_t val, char* out) +{ + for( int32_t i=28; i>=0; i-=4 ) { + *out++ = makehexdigit( val>>i ); + } + *out = 0; + return out; +} + +static inline char makeupperhexdigit(uint32_t val) +{ + return "0123456789ABCDEF"[val&0xF]; +} + +static char* appendupperhexnum(uint32_t val, char* out) +{ + for( int32_t i=28; i>=0; i-=4 ) { + *out++ = makeupperhexdigit( val>>i ); + } + *out = 0; + return out; +} + +static char* appendcharornum(char c, char* out, bool skipzero = true) +{ + if (skipzero && c == 0) return out; + + if (isasciitype(c)) { + *out++ = c; + return out; + } + + *out++ = '\\'; + *out++ = 'x'; + *out++ = makehexdigit(c>>4); + *out++ = makehexdigit(c); + return out; +} + +static char* typetostring(uint32_t type, char* out, + bool fullContext = true, + bool strict = false) +{ + char* pos = out; + char c[4]; + c[0] = (char)((type>>24)&0xFF); + c[1] = (char)((type>>16)&0xFF); + c[2] = (char)((type>>8)&0xFF); + c[3] = (char)(type&0xFF); + bool valid; + if( !strict ) { + // now even less strict! + // valid = isasciitype(c[3]); + valid = true; + int32_t i = 0; + bool zero = true; + while (valid && i<3) { + if (c[i] == 0) { + if (!zero) valid = false; + } else { + zero = false; + //if (!isasciitype(c[i])) valid = false; + } + i++; + } + // if all zeros, not a valid type code. + if (zero) valid = false; + } else { + valid = isident(c[3]) ? true : false; + int32_t i = 0; + bool zero = true; + while (valid && i<3) { + if (c[i] == 0) { + if (!zero) valid = false; + } else { + zero = false; + if (!isident(c[i])) valid = false; + } + i++; + } + } + if( valid && (!fullContext || c[0] != '0' || c[1] != 'x') ) { + if( fullContext ) *pos++ = '\''; + pos = appendcharornum(c[0], pos); + pos = appendcharornum(c[1], pos); + pos = appendcharornum(c[2], pos); + pos = appendcharornum(c[3], pos); + if( fullContext ) *pos++ = '\''; + *pos = 0; + return pos; + } + + if( fullContext ) { + *pos++ = '0'; + *pos++ = 'x'; + } + return appendhexnum(type, pos); +} + +void printTypeCode(uint32_t typeCode, debugPrintFunc func, void* cookie) +{ + char buffer[32]; + char* end = typetostring(typeCode, buffer); + *end = 0; + func ? (*func)(cookie, buffer) : defaultPrintFunc(cookie, buffer); +} + +void printHexData(int32_t indent, const void *buf, size_t length, + size_t bytesPerLine, int32_t singleLineBytesCutoff, + size_t alignment, bool cStyle, + debugPrintFunc func, void* cookie) +{ + if (alignment == 0) { + if (bytesPerLine >= 16) alignment = 4; + else if (bytesPerLine >= 8) alignment = 2; + else alignment = 1; + } + if (func == NULL) func = defaultPrintFunc; + + size_t offset; + + unsigned char *pos = (unsigned char *)buf; + + if (pos == NULL) { + if (singleLineBytesCutoff < 0) func(cookie, "\n"); + func(cookie, "(NULL)"); + return; + } + + if (length == 0) { + if (singleLineBytesCutoff < 0) func(cookie, "\n"); + func(cookie, "(empty)"); + return; + } + + if ((int32_t)length < 0) { + if (singleLineBytesCutoff < 0) func(cookie, "\n"); + char buf[64]; + sprintf(buf, "(bad length: %d)", length); + func(cookie, buf); + return; + } + + char buffer[256]; + static const size_t maxBytesPerLine = (sizeof(buffer)-1-11-4)/(3+1); + + if (bytesPerLine > maxBytesPerLine) bytesPerLine = maxBytesPerLine; + + const bool oneLine = (int32_t)length <= singleLineBytesCutoff; + bool newLine = false; + if (cStyle) { + indent++; + func(cookie, "{\n"); + newLine = true; + } else if (!oneLine) { + func(cookie, "\n"); + newLine = true; + } + + for (offset = 0; ; offset += bytesPerLine, pos += bytesPerLine) { + long remain = length; + + char* c = buffer; + if (!oneLine && !cStyle) { + sprintf(c, "0x%08x: ", (int)offset); + c += 12; + } + + size_t index; + size_t word; + + for (word = 0; word < bytesPerLine; ) { + +#ifdef HAVE_LITTLE_ENDIAN + const size_t startIndex = word+(alignment-(alignment?1:0)); + const ssize_t dir = -1; +#else + const size_t startIndex = word; + const ssize_t dir = 1; +#endif + + for (index = 0; index < alignment || (alignment == 0 && index < bytesPerLine); index++) { + + if (!cStyle) { + if (index == 0 && word > 0 && alignment > 0) { + *c++ = ' '; + } + + if (remain-- > 0) { + const unsigned char val = *(pos+startIndex+(index*dir)); + *c++ = makehexdigit(val>>4); + *c++ = makehexdigit(val); + } else if (!oneLine) { + *c++ = ' '; + *c++ = ' '; + } + } else { + if (remain > 0) { + if (index == 0 && word > 0) { + *c++ = ','; + *c++ = ' '; + } + if (index == 0) { + *c++ = '0'; + *c++ = 'x'; + } + const unsigned char val = *(pos+startIndex+(index*dir)); + *c++ = makehexdigit(val>>4); + *c++ = makehexdigit(val); + remain--; + } + } + } + + word += index; + } + + if (!cStyle) { + remain = length; + *c++ = ' '; + *c++ = '\''; + for (index = 0; index < bytesPerLine; index++) { + + if (remain-- > 0) { + const unsigned char val = pos[index]; + *c++ = (val >= ' ' && val < 127) ? val : '.'; + } else if (!oneLine) { + *c++ = ' '; + } + } + + *c++ = '\''; + if (length > bytesPerLine) *c++ = '\n'; + } else { + if (remain > 0) *c++ = ','; + *c++ = '\n'; + } + + if (newLine && indent) func(cookie, stringForIndent(indent)); + *c = 0; + func(cookie, buffer); + newLine = true; + + if (length <= bytesPerLine) break; + length -= bytesPerLine; + } + + if (cStyle) { + if (indent > 0) func(cookie, stringForIndent(indent-1)); + func(cookie, "};"); + } +} + +}; // namespace android + diff --git a/libs/utils/FileMap.cpp b/libs/utils/FileMap.cpp new file mode 100644 index 000000000..e1ba9b238 --- /dev/null +++ b/libs/utils/FileMap.cpp @@ -0,0 +1,222 @@ +/* + * Copyright (C) 2006 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. + */ + +// +// Shared file mapping class. +// + +#define LOG_TAG "filemap" + +#include +#include + +#include +#include + +#ifdef HAVE_POSIX_FILEMAP +#include +#endif + +#include +#include +#include +#include + +using namespace android; + +/*static*/ long FileMap::mPageSize = -1; + + +/* + * Constructor. Create an empty object. + */ +FileMap::FileMap(void) + : mRefCount(1), mFileName(NULL), mBasePtr(NULL), mBaseLength(0), + mDataPtr(NULL), mDataLength(0) +{ +} + +/* + * Destructor. + */ +FileMap::~FileMap(void) +{ + assert(mRefCount == 0); + + //printf("+++ removing FileMap %p %u\n", mDataPtr, mDataLength); + + mRefCount = -100; // help catch double-free + if (mFileName != NULL) { + free(mFileName); + } +#ifdef HAVE_POSIX_FILEMAP + if (munmap(mBasePtr, mBaseLength) != 0) { + LOGD("munmap(%p, %d) failed\n", mBasePtr, (int) mBaseLength); + } +#endif +#ifdef HAVE_WIN32_FILEMAP + if ( UnmapViewOfFile(mBasePtr) == 0) { + LOGD("UnmapViewOfFile(%p) failed, error = %ld\n", mBasePtr, + GetLastError() ); + } + CloseHandle(mFileMapping); + CloseHandle(mFileHandle); +#endif +} + + +/* + * Create a new mapping on an open file. + * + * Closing the file descriptor does not unmap the pages, so we don't + * claim ownership of the fd. + * + * Returns "false" on failure. + */ +bool FileMap::create(const char* origFileName, int fd, off_t offset, size_t length, bool readOnly) +{ +#ifdef HAVE_WIN32_FILEMAP + int adjust; + off_t adjOffset; + size_t adjLength; + + if (mPageSize == -1) { + SYSTEM_INFO si; + + GetSystemInfo( &si ); + mPageSize = si.dwAllocationGranularity; + } + + DWORD protect = readOnly ? PAGE_READONLY : PAGE_READWRITE; + + mFileHandle = (HANDLE) _get_osfhandle(fd); + mFileMapping = CreateFileMapping( mFileHandle, NULL, protect, 0, 0, NULL); + if (mFileMapping == NULL) { + LOGE("CreateFileMapping(%p, %lx) failed with error %ld\n", + mFileHandle, protect, GetLastError() ); + return false; + } + + adjust = offset % mPageSize; + adjOffset = offset - adjust; + adjLength = length + adjust; + + mBasePtr = MapViewOfFile( mFileMapping, + readOnly ? FILE_MAP_READ : FILE_MAP_ALL_ACCESS, + 0, + (DWORD)(adjOffset), + adjLength ); + if (mBasePtr == NULL) { + LOGE("MapViewOfFile(%ld, %ld) failed with error %ld\n", + adjOffset, adjLength, GetLastError() ); + CloseHandle(mFileMapping); + mFileMapping = INVALID_HANDLE_VALUE; + return false; + } +#endif +#ifdef HAVE_POSIX_FILEMAP + int prot, flags, adjust; + off_t adjOffset; + size_t adjLength; + + void* ptr; + + assert(mRefCount == 1); + assert(fd >= 0); + assert(offset >= 0); + assert(length > 0); + + /* init on first use */ + if (mPageSize == -1) { +#if NOT_USING_KLIBC + mPageSize = sysconf(_SC_PAGESIZE); + if (mPageSize == -1) { + LOGE("could not get _SC_PAGESIZE\n"); + return false; + } +#else + /* this holds for Linux, Darwin, Cygwin, and doesn't pain the ARM */ + mPageSize = 4096; +#endif + } + + adjust = offset % mPageSize; +try_again: + adjOffset = offset - adjust; + adjLength = length + adjust; + + flags = MAP_SHARED; + prot = PROT_READ; + if (!readOnly) + prot |= PROT_WRITE; + + ptr = mmap(NULL, adjLength, prot, flags, fd, adjOffset); + if (ptr == MAP_FAILED) { + // Cygwin does not seem to like file mapping files from an offset. + // So if we fail, try again with offset zero + if (adjOffset > 0) { + adjust = offset; + goto try_again; + } + + LOGE("mmap(%ld,%ld) failed: %s\n", + (long) adjOffset, (long) adjLength, strerror(errno)); + return false; + } + mBasePtr = ptr; +#endif /* HAVE_POSIX_FILEMAP */ + + mFileName = origFileName != NULL ? strdup(origFileName) : NULL; + mBaseLength = adjLength; + mDataOffset = offset; + mDataPtr = (char*) mBasePtr + adjust; + mDataLength = length; + + assert(mBasePtr != NULL); + + LOGV("MAP: base %p/%d data %p/%d\n", + mBasePtr, (int) mBaseLength, mDataPtr, (int) mDataLength); + + return true; +} + +/* + * Provide guidance to the system. + */ +int FileMap::advise(MapAdvice advice) +{ +#if HAVE_MADVISE + int cc, sysAdvice; + + switch (advice) { + case NORMAL: sysAdvice = MADV_NORMAL; break; + case RANDOM: sysAdvice = MADV_RANDOM; break; + case SEQUENTIAL: sysAdvice = MADV_SEQUENTIAL; break; + case WILLNEED: sysAdvice = MADV_WILLNEED; break; + case DONTNEED: sysAdvice = MADV_DONTNEED; break; + default: + assert(false); + return -1; + } + + cc = madvise(mBasePtr, mBaseLength, sysAdvice); + if (cc != 0) + LOGW("madvise(%d) failed: %s\n", sysAdvice, strerror(errno)); + return cc; +#else + return -1; +#endif // HAVE_MADVISE +} diff --git a/libs/utils/IDataConnection.cpp b/libs/utils/IDataConnection.cpp new file mode 100644 index 000000000..c6d49aa48 --- /dev/null +++ b/libs/utils/IDataConnection.cpp @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2006 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 +#include + +#include + +#include + +namespace android { + +// --------------------------------------------------------------------------- + +enum +{ + CONNECT_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION, + DISCONNECT_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION + 1 +}; + +class BpDataConnection : public BpInterface +{ +public: + BpDataConnection::BpDataConnection(const sp& impl) + : BpInterface(impl) + { + } + + virtual void connect() + { + Parcel data, reply; + data.writeInterfaceToken(IDataConnection::descriptor()); + remote()->transact(CONNECT_TRANSACTION, data, &reply); + } + + virtual void disconnect() + { + Parcel data, reply; + remote()->transact(DISCONNECT_TRANSACTION, data, &reply); + } +}; + +IMPLEMENT_META_INTERFACE(DataConnection, "android.utils.IDataConnection"); + +#define CHECK_INTERFACE(interface, data, reply) \ + do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ + LOGW("Call incorrectly routed to " #interface); \ + return PERMISSION_DENIED; \ + } } while (0) + +status_t BnDataConnection::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch(code) + { + case CONNECT_TRANSACTION: + { + CHECK_INTERFACE(IDataConnection, data, reply); + connect(); + return NO_ERROR; + } + + case DISCONNECT_TRANSACTION: + { + CHECK_INTERFACE(IDataConnection, data, reply); + disconnect(); + return NO_ERROR; + } + + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +// ---------------------------------------------------------------------------- + +}; // namespace android diff --git a/libs/utils/IInterface.cpp b/libs/utils/IInterface.cpp new file mode 100644 index 000000000..6ea817887 --- /dev/null +++ b/libs/utils/IInterface.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2005 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 + +namespace android { + +// --------------------------------------------------------------------------- + +sp IInterface::asBinder() +{ + return this ? onAsBinder() : NULL; +} + +sp IInterface::asBinder() const +{ + return this ? const_cast(this)->onAsBinder() : NULL; +} + +// --------------------------------------------------------------------------- + +}; // namespace android diff --git a/libs/utils/IMemory.cpp b/libs/utils/IMemory.cpp new file mode 100644 index 000000000..429bc2b94 --- /dev/null +++ b/libs/utils/IMemory.cpp @@ -0,0 +1,486 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "IMemory" + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#define VERBOSE 0 + +namespace android { +// --------------------------------------------------------------------------- + +class HeapCache : public IBinder::DeathRecipient +{ +public: + HeapCache(); + virtual ~HeapCache(); + + virtual void binderDied(const wp& who); + + sp find_heap(const sp& binder); + void pin_heap(const sp& binder); + void free_heap(const sp& binder); + sp get_heap(const sp& binder); + void dump_heaps(); + +private: + // For IMemory.cpp + struct heap_info_t { + sp heap; + int32_t count; + }; + + void free_heap(const wp& binder); + + Mutex mHeapCacheLock; + KeyedVector< wp, heap_info_t > mHeapCache; +}; + +static sp gHeapCache = new HeapCache(); + +/******************************************************************************/ + +enum { + HEAP_ID = IBinder::FIRST_CALL_TRANSACTION +}; + +class BpMemoryHeap : public BpInterface +{ +public: + BpMemoryHeap(const sp& impl); + virtual ~BpMemoryHeap(); + + virtual int getHeapID() const; + virtual void* getBase() const; + virtual size_t getSize() const; + virtual uint32_t getFlags() const; + +private: + friend class IMemory; + friend class HeapCache; + + // for debugging in this module + static inline sp find_heap(const sp& binder) { + return gHeapCache->find_heap(binder); + } + static inline void free_heap(const sp& binder) { + gHeapCache->free_heap(binder); + } + static inline sp get_heap(const sp& binder) { + return gHeapCache->get_heap(binder); + } + static inline void dump_heaps() { + gHeapCache->dump_heaps(); + } + void inline pin_heap() const { + gHeapCache->pin_heap(const_cast(this)->asBinder()); + } + + void assertMapped() const; + void assertReallyMapped() const; + void pinHeap() const; + + mutable volatile int32_t mHeapId; + mutable void* mBase; + mutable size_t mSize; + mutable uint32_t mFlags; + mutable bool mRealHeap; + mutable Mutex mLock; +}; + +// ---------------------------------------------------------------------------- + +enum { + GET_MEMORY = IBinder::FIRST_CALL_TRANSACTION +}; + +class BpMemory : public BpInterface +{ +public: + BpMemory(const sp& impl); + virtual ~BpMemory(); + virtual sp getMemory(ssize_t* offset=0, size_t* size=0) const; + +private: + mutable sp mHeap; + mutable ssize_t mOffset; + mutable size_t mSize; +}; + +/******************************************************************************/ + +void* IMemory::fastPointer(const sp& binder, ssize_t offset) const +{ + sp realHeap = BpMemoryHeap::get_heap(binder); + void* const base = realHeap->base(); + if (base == MAP_FAILED) + return 0; + return static_cast(base) + offset; +} + +void* IMemory::pointer() const { + ssize_t offset; + sp heap = getMemory(&offset); + void* const base = heap!=0 ? heap->base() : MAP_FAILED; + if (base == MAP_FAILED) + return 0; + return static_cast(base) + offset; +} + +size_t IMemory::size() const { + size_t size; + getMemory(NULL, &size); + return size; +} + +ssize_t IMemory::offset() const { + ssize_t offset; + getMemory(&offset); + return offset; +} + +/******************************************************************************/ + +BpMemory::BpMemory(const sp& impl) + : BpInterface(impl), mOffset(0), mSize(0) +{ +} + +BpMemory::~BpMemory() +{ +} + +sp BpMemory::getMemory(ssize_t* offset, size_t* size) const +{ + if (mHeap == 0) { + Parcel data, reply; + data.writeInterfaceToken(IMemory::getInterfaceDescriptor()); + if (remote()->transact(GET_MEMORY, data, &reply) == NO_ERROR) { + sp heap = reply.readStrongBinder(); + ssize_t o = reply.readInt32(); + size_t s = reply.readInt32(); + if (heap != 0) { + mHeap = interface_cast(heap); + if (mHeap != 0) { + mOffset = o; + mSize = s; + } + } + } + } + if (offset) *offset = mOffset; + if (size) *size = mSize; + return mHeap; +} + +// --------------------------------------------------------------------------- + +IMPLEMENT_META_INTERFACE(Memory, "android.utils.IMemory"); + +#define CHECK_INTERFACE(interface, data, reply) \ + do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ + LOGW("Call incorrectly routed to " #interface); \ + return PERMISSION_DENIED; \ + } } while (0) + +status_t BnMemory::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch(code) { + case GET_MEMORY: { + CHECK_INTERFACE(IMemory, data, reply); + ssize_t offset; + size_t size; + reply->writeStrongBinder( getMemory(&offset, &size)->asBinder() ); + reply->writeInt32(offset); + reply->writeInt32(size); + return NO_ERROR; + } break; + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + + +/******************************************************************************/ + +BpMemoryHeap::BpMemoryHeap(const sp& impl) + : BpInterface(impl), + mHeapId(-1), mBase(MAP_FAILED), mSize(0), mFlags(0), mRealHeap(false) +{ +} + +BpMemoryHeap::~BpMemoryHeap() { + if (mHeapId != -1) { + close(mHeapId); + if (mRealHeap) { + // by construction we're the last one + if (mBase != MAP_FAILED) { + sp binder = const_cast(this)->asBinder(); + + if (VERBOSE) { + LOGD("UNMAPPING binder=%p, heap=%p, size=%d, fd=%d", + binder.get(), this, mSize, mHeapId); + CallStack stack; + stack.update(); + stack.dump("callstack"); + } + + munmap(mBase, mSize); + } + } else { + // remove from list only if it was mapped before + sp binder = const_cast(this)->asBinder(); + free_heap(binder); + } + } +} + +void BpMemoryHeap::assertMapped() const +{ + if (mHeapId == -1) { + sp binder(const_cast(this)->asBinder()); + sp heap(static_cast(find_heap(binder).get())); + heap->assertReallyMapped(); + if (heap->mBase != MAP_FAILED) { + Mutex::Autolock _l(mLock); + if (mHeapId == -1) { + mBase = heap->mBase; + mSize = heap->mSize; + android_atomic_write( dup( heap->mHeapId ), &mHeapId ); + } + } else { + // something went wrong + free_heap(binder); + } + } +} + +void BpMemoryHeap::assertReallyMapped() const +{ + if (mHeapId == -1) { + + // remote call without mLock held, worse case scenario, we end up + // calling transact() from multiple threads, but that's not a problem, + // only mmap below must be in the critical section. + + Parcel data, reply; + data.writeInterfaceToken(IMemoryHeap::getInterfaceDescriptor()); + status_t err = remote()->transact(HEAP_ID, data, &reply); + int parcel_fd = reply.readFileDescriptor(); + ssize_t size = reply.readInt32(); + uint32_t flags = reply.readInt32(); + + LOGE_IF(err, "binder=%p transaction failed fd=%d, size=%d, err=%d (%s)", + asBinder().get(), parcel_fd, size, err, strerror(-err)); + + int fd = dup( parcel_fd ); + LOGE_IF(fd==-1, "cannot dup fd=%d, size=%d, err=%d (%s)", + parcel_fd, size, err, strerror(errno)); + + int access = PROT_READ; + if (!(flags & READ_ONLY)) { + access |= PROT_WRITE; + } + + Mutex::Autolock _l(mLock); + if (mHeapId == -1) { + mRealHeap = true; + mBase = mmap(0, size, access, MAP_SHARED, fd, 0); + if (mBase == MAP_FAILED) { + LOGE("cannot map BpMemoryHeap (binder=%p), size=%d, fd=%d (%s)", + asBinder().get(), size, fd, strerror(errno)); + close(fd); + } else { + if (flags & MAP_ONCE) { + //LOGD("pinning heap (binder=%p, size=%d, fd=%d", + // asBinder().get(), size, fd); + pin_heap(); + } + mSize = size; + mFlags = flags; + android_atomic_write(fd, &mHeapId); + } + } + } +} + +int BpMemoryHeap::getHeapID() const { + assertMapped(); + return mHeapId; +} + +void* BpMemoryHeap::getBase() const { + assertMapped(); + return mBase; +} + +size_t BpMemoryHeap::getSize() const { + assertMapped(); + return mSize; +} + +uint32_t BpMemoryHeap::getFlags() const { + assertMapped(); + return mFlags; +} + +// --------------------------------------------------------------------------- + +IMPLEMENT_META_INTERFACE(MemoryHeap, "android.utils.IMemoryHeap"); + +status_t BnMemoryHeap::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch(code) { + case HEAP_ID: { + CHECK_INTERFACE(IMemoryHeap, data, reply); + reply->writeFileDescriptor(getHeapID()); + reply->writeInt32(getSize()); + reply->writeInt32(getFlags()); + return NO_ERROR; + } break; + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +/*****************************************************************************/ + +HeapCache::HeapCache() + : DeathRecipient() +{ +} + +HeapCache::~HeapCache() +{ +} + +void HeapCache::binderDied(const wp& binder) +{ + //LOGD("binderDied binder=%p", binder.unsafe_get()); + free_heap(binder); +} + +sp HeapCache::find_heap(const sp& binder) +{ + Mutex::Autolock _l(mHeapCacheLock); + ssize_t i = mHeapCache.indexOfKey(binder); + if (i>=0) { + heap_info_t& info = mHeapCache.editValueAt(i); + LOGD_IF(VERBOSE, + "found binder=%p, heap=%p, size=%d, fd=%d, count=%d", + binder.get(), info.heap.get(), + static_cast(info.heap.get())->mSize, + static_cast(info.heap.get())->mHeapId, + info.count); + android_atomic_inc(&info.count); + return info.heap; + } else { + heap_info_t info; + info.heap = interface_cast(binder); + info.count = 1; + //LOGD("adding binder=%p, heap=%p, count=%d", + // binder.get(), info.heap.get(), info.count); + mHeapCache.add(binder, info); + return info.heap; + } +} + +void HeapCache::pin_heap(const sp& binder) +{ + Mutex::Autolock _l(mHeapCacheLock); + ssize_t i = mHeapCache.indexOfKey(binder); + if (i>=0) { + heap_info_t& info(mHeapCache.editValueAt(i)); + android_atomic_inc(&info.count); + binder->linkToDeath(this); + } else { + LOGE("pin_heap binder=%p not found!!!", binder.get()); + } +} + +void HeapCache::free_heap(const sp& binder) { + free_heap( wp(binder) ); +} + +void HeapCache::free_heap(const wp& binder) +{ + sp rel; + { + Mutex::Autolock _l(mHeapCacheLock); + ssize_t i = mHeapCache.indexOfKey(binder); + if (i>=0) { + heap_info_t& info(mHeapCache.editValueAt(i)); + int32_t c = android_atomic_dec(&info.count); + if (c == 1) { + LOGD_IF(VERBOSE, + "removing binder=%p, heap=%p, size=%d, fd=%d, count=%d", + binder.unsafe_get(), info.heap.get(), + static_cast(info.heap.get())->mSize, + static_cast(info.heap.get())->mHeapId, + info.count); + rel = mHeapCache.valueAt(i).heap; + mHeapCache.removeItemsAt(i); + } + } else { + LOGE("free_heap binder=%p not found!!!", binder.unsafe_get()); + } + } +} + +sp HeapCache::get_heap(const sp& binder) +{ + sp realHeap; + Mutex::Autolock _l(mHeapCacheLock); + ssize_t i = mHeapCache.indexOfKey(binder); + if (i>=0) realHeap = mHeapCache.valueAt(i).heap; + else realHeap = interface_cast(binder); + return realHeap; +} + +void HeapCache::dump_heaps() +{ + Mutex::Autolock _l(mHeapCacheLock); + int c = mHeapCache.size(); + for (int i=0 ; i(info.heap.get())); + LOGD("hey=%p, heap=%p, count=%d, (fd=%d, base=%p, size=%d)", + mHeapCache.keyAt(i).unsafe_get(), + info.heap.get(), info.count, + h->mHeapId, h->mBase, h->mSize); + } +} + + +// --------------------------------------------------------------------------- +}; // namespace android diff --git a/libs/utils/IPCThreadState.cpp b/libs/utils/IPCThreadState.cpp new file mode 100644 index 000000000..04ae1424e --- /dev/null +++ b/libs/utils/IPCThreadState.cpp @@ -0,0 +1,1030 @@ +/* + * Copyright (C) 2005 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 + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#ifdef HAVE_PTHREADS +#include +#include +#include +#endif +#ifdef HAVE_WIN32_THREADS +#include +#endif + + +#if LOG_NDEBUG + +#define IF_LOG_TRANSACTIONS() if (false) +#define IF_LOG_COMMANDS() if (false) +#define LOG_REMOTEREFS(...) +#define IF_LOG_REMOTEREFS() if (false) +#define LOG_THREADPOOL(...) +#define LOG_ONEWAY(...) + +#else + +#define IF_LOG_TRANSACTIONS() IF_LOG(LOG_VERBOSE, "transact") +#define IF_LOG_COMMANDS() IF_LOG(LOG_VERBOSE, "ipc") +#define LOG_REMOTEREFS(...) LOG(LOG_DEBUG, "remoterefs", __VA_ARGS__) +#define IF_LOG_REMOTEREFS() IF_LOG(LOG_DEBUG, "remoterefs") +#define LOG_THREADPOOL(...) LOG(LOG_DEBUG, "threadpool", __VA_ARGS__) +#define LOG_ONEWAY(...) LOG(LOG_DEBUG, "ipc", __VA_ARGS__) + +#endif + +// --------------------------------------------------------------------------- + +namespace android { + +static const char* getReturnString(size_t idx); +static const char* getCommandString(size_t idx); +static const void* printReturnCommand(TextOutput& out, const void* _cmd); +static const void* printCommand(TextOutput& out, const void* _cmd); + +// This will result in a missing symbol failure if the IF_LOG_COMMANDS() +// conditionals don't get stripped... but that is probably what we want. +#if !LOG_NDEBUG +static const char *kReturnStrings[] = { +#if 1 /* TODO: error update strings */ + "unknown", +#else + "BR_OK", + "BR_TIMEOUT", + "BR_WAKEUP", + "BR_TRANSACTION", + "BR_REPLY", + "BR_ACQUIRE_RESULT", + "BR_DEAD_REPLY", + "BR_TRANSACTION_COMPLETE", + "BR_INCREFS", + "BR_ACQUIRE", + "BR_RELEASE", + "BR_DECREFS", + "BR_ATTEMPT_ACQUIRE", + "BR_EVENT_OCCURRED", + "BR_NOOP", + "BR_SPAWN_LOOPER", + "BR_FINISHED", + "BR_DEAD_BINDER", + "BR_CLEAR_DEATH_NOTIFICATION_DONE" +#endif +}; + +static const char *kCommandStrings[] = { +#if 1 /* TODO: error update strings */ + "unknown", +#else + "BC_NOOP", + "BC_TRANSACTION", + "BC_REPLY", + "BC_ACQUIRE_RESULT", + "BC_FREE_BUFFER", + "BC_TRANSACTION_COMPLETE", + "BC_INCREFS", + "BC_ACQUIRE", + "BC_RELEASE", + "BC_DECREFS", + "BC_INCREFS_DONE", + "BC_ACQUIRE_DONE", + "BC_ATTEMPT_ACQUIRE", + "BC_RETRIEVE_ROOT_OBJECT", + "BC_SET_THREAD_ENTRY", + "BC_REGISTER_LOOPER", + "BC_ENTER_LOOPER", + "BC_EXIT_LOOPER", + "BC_SYNC", + "BC_STOP_PROCESS", + "BC_STOP_SELF", + "BC_REQUEST_DEATH_NOTIFICATION", + "BC_CLEAR_DEATH_NOTIFICATION", + "BC_DEAD_BINDER_DONE" +#endif +}; + +static const char* getReturnString(size_t idx) +{ + if (idx < sizeof(kReturnStrings) / sizeof(kReturnStrings[0])) + return kReturnStrings[idx]; + else + return "unknown"; +} + +static const char* getCommandString(size_t idx) +{ + if (idx < sizeof(kCommandStrings) / sizeof(kCommandStrings[0])) + return kCommandStrings[idx]; + else + return "unknown"; +} + +static const void* printBinderTransactionData(TextOutput& out, const void* data) +{ + const binder_transaction_data* btd = + (const binder_transaction_data*)data; + out << "target=" << btd->target.ptr << " (cookie " << btd->cookie << ")" << endl + << "code=" << TypeCode(btd->code) << ", flags=" << (void*)btd->flags << endl + << "data=" << btd->data.ptr.buffer << " (" << (void*)btd->data_size + << " bytes)" << endl + << "offsets=" << btd->data.ptr.offsets << " (" << (void*)btd->offsets_size + << " bytes)" << endl; + return btd+1; +} + +static const void* printReturnCommand(TextOutput& out, const void* _cmd) +{ + static const int32_t N = sizeof(kReturnStrings)/sizeof(kReturnStrings[0]); + + const int32_t* cmd = (const int32_t*)_cmd; + int32_t code = *cmd++; + if (code == BR_ERROR) { + out << "BR_ERROR: " << (void*)(*cmd++) << endl; + return cmd; + } else if (code < 0 || code >= N) { + out << "Unknown reply: " << code << endl; + return cmd; + } + + out << kReturnStrings[code]; + switch (code) { + case BR_TRANSACTION: + case BR_REPLY: { + out << ": " << indent; + cmd = (const int32_t *)printBinderTransactionData(out, cmd); + out << dedent; + } break; + + case BR_ACQUIRE_RESULT: { + const int32_t res = *cmd++; + out << ": " << res << (res ? " (SUCCESS)" : " (FAILURE)"); + } break; + + case BR_INCREFS: + case BR_ACQUIRE: + case BR_RELEASE: + case BR_DECREFS: { + const int32_t b = *cmd++; + const int32_t c = *cmd++; + out << ": target=" << (void*)b << " (cookie " << (void*)c << ")"; + } break; + + case BR_ATTEMPT_ACQUIRE: { + const int32_t p = *cmd++; + const int32_t b = *cmd++; + const int32_t c = *cmd++; + out << ": target=" << (void*)b << " (cookie " << (void*)c + << "), pri=" << p; + } break; + + case BR_DEAD_BINDER: + case BR_CLEAR_DEATH_NOTIFICATION_DONE: { + const int32_t c = *cmd++; + out << ": death cookie " << (void*)c; + } break; + } + + out << endl; + return cmd; +} + +static const void* printCommand(TextOutput& out, const void* _cmd) +{ + static const int32_t N = sizeof(kCommandStrings)/sizeof(kCommandStrings[0]); + + const int32_t* cmd = (const int32_t*)_cmd; + int32_t code = *cmd++; + if (code < 0 || code >= N) { + out << "Unknown command: " << code << endl; + return cmd; + } + + out << kCommandStrings[code]; + switch (code) { + case BC_TRANSACTION: + case BC_REPLY: { + out << ": " << indent; + cmd = (const int32_t *)printBinderTransactionData(out, cmd); + out << dedent; + } break; + + case BC_ACQUIRE_RESULT: { + const int32_t res = *cmd++; + out << ": " << res << (res ? " (SUCCESS)" : " (FAILURE)"); + } break; + + case BC_FREE_BUFFER: { + const int32_t buf = *cmd++; + out << ": buffer=" << (void*)buf; + } break; + + case BC_INCREFS: + case BC_ACQUIRE: + case BC_RELEASE: + case BC_DECREFS: { + const int32_t d = *cmd++; + out << ": descriptor=" << (void*)d; + } break; + + case BC_INCREFS_DONE: + case BC_ACQUIRE_DONE: { + const int32_t b = *cmd++; + const int32_t c = *cmd++; + out << ": target=" << (void*)b << " (cookie " << (void*)c << ")"; + } break; + + case BC_ATTEMPT_ACQUIRE: { + const int32_t p = *cmd++; + const int32_t d = *cmd++; + out << ": decriptor=" << (void*)d << ", pri=" << p; + } break; + + case BC_REQUEST_DEATH_NOTIFICATION: + case BC_CLEAR_DEATH_NOTIFICATION: { + const int32_t h = *cmd++; + const int32_t c = *cmd++; + out << ": handle=" << h << " (death cookie " << (void*)c << ")"; + } break; + + case BC_DEAD_BINDER_DONE: { + const int32_t c = *cmd++; + out << ": death cookie " << (void*)c; + } break; + } + + out << endl; + return cmd; +} +#endif + +static pthread_mutex_t gTLSMutex = PTHREAD_MUTEX_INITIALIZER; +static bool gHaveTLS = false; +static pthread_key_t gTLS = 0; +static bool gShutdown = false; + +IPCThreadState* IPCThreadState::self() +{ + if (gHaveTLS) { +restart: + const pthread_key_t k = gTLS; + IPCThreadState* st = (IPCThreadState*)pthread_getspecific(k); + if (st) return st; + return new IPCThreadState; + } + + if (gShutdown) return NULL; + + pthread_mutex_lock(&gTLSMutex); + if (!gHaveTLS) { + if (pthread_key_create(&gTLS, threadDestructor) != 0) { + pthread_mutex_unlock(&gTLSMutex); + return NULL; + } + gHaveTLS = true; + } + pthread_mutex_unlock(&gTLSMutex); + goto restart; +} + +void IPCThreadState::shutdown() +{ + gShutdown = true; + + if (gHaveTLS) { + // XXX Need to wait for all thread pool threads to exit! + IPCThreadState* st = (IPCThreadState*)pthread_getspecific(gTLS); + if (st) { + delete st; + pthread_setspecific(gTLS, NULL); + } + gHaveTLS = false; + } +} + +sp IPCThreadState::process() +{ + return mProcess; +} + +status_t IPCThreadState::clearLastError() +{ + const status_t err = mLastError; + mLastError = NO_ERROR; + return err; +} + +int IPCThreadState::getCallingPid() +{ + return mCallingPid; +} + +int IPCThreadState::getCallingUid() +{ + return mCallingUid; +} + +int64_t IPCThreadState::clearCallingIdentity() +{ + int64_t token = ((int64_t)mCallingUid<<32) | mCallingPid; + clearCaller(); + return token; +} + +void IPCThreadState::restoreCallingIdentity(int64_t token) +{ + mCallingUid = (int)(token>>32); + mCallingPid = (int)token; +} + +void IPCThreadState::clearCaller() +{ + if (mProcess->supportsProcesses()) { + mCallingPid = getpid(); + mCallingUid = getuid(); + } else { + mCallingPid = -1; + mCallingUid = -1; + } +} + +void IPCThreadState::flushCommands() +{ + if (mProcess->mDriverFD <= 0) + return; + talkWithDriver(false); +} + +void IPCThreadState::joinThreadPool(bool isMain) +{ + LOG_THREADPOOL("**** THREAD %p (PID %d) IS JOINING THE THREAD POOL\n", (void*)pthread_self(), getpid()); + + mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER); + + status_t result; + do { + int32_t cmd; + + // When we've cleared the incoming command queue, process any pending derefs + if (mIn.dataPosition() >= mIn.dataSize()) { + size_t numPending = mPendingWeakDerefs.size(); + if (numPending > 0) { + for (size_t i = 0; i < numPending; i++) { + RefBase::weakref_type* refs = mPendingWeakDerefs[i]; + refs->decWeak(mProcess.get()); + } + mPendingWeakDerefs.clear(); + } + + numPending = mPendingStrongDerefs.size(); + if (numPending > 0) { + for (size_t i = 0; i < numPending; i++) { + BBinder* obj = mPendingStrongDerefs[i]; + obj->decStrong(mProcess.get()); + } + mPendingStrongDerefs.clear(); + } + } + + // now get the next command to be processed, waiting if necessary + result = talkWithDriver(); + if (result >= NO_ERROR) { + size_t IN = mIn.dataAvail(); + if (IN < sizeof(int32_t)) continue; + cmd = mIn.readInt32(); + IF_LOG_COMMANDS() { + alog << "Processing top-level Command: " + << getReturnString(cmd) << endl; + } + result = executeCommand(cmd); + } + + // Let this thread exit the thread pool if it is no longer + // needed and it is not the main process thread. + if(result == TIMED_OUT && !isMain) { + break; + } + } while (result != -ECONNREFUSED && result != -EBADF); + + LOG_THREADPOOL("**** THREAD %p (PID %d) IS LEAVING THE THREAD POOL err=%p\n", + (void*)pthread_self(), getpid(), (void*)result); + + mOut.writeInt32(BC_EXIT_LOOPER); + talkWithDriver(false); +} + +void IPCThreadState::stopProcess(bool immediate) +{ + //LOGI("**** STOPPING PROCESS"); + flushCommands(); + int fd = mProcess->mDriverFD; + mProcess->mDriverFD = -1; + close(fd); + //kill(getpid(), SIGKILL); +} + +status_t IPCThreadState::transact(int32_t handle, + uint32_t code, const Parcel& data, + Parcel* reply, uint32_t flags) +{ + status_t err = data.errorCheck(); + + flags |= TF_ACCEPT_FDS; + + IF_LOG_TRANSACTIONS() { + TextOutput::Bundle _b(alog); + alog << "BC_TRANSACTION thr " << (void*)pthread_self() << " / hand " + << handle << " / code " << TypeCode(code) << ": " + << indent << data << dedent << endl; + } + + if (err == NO_ERROR) { + LOG_ONEWAY(">>>> SEND from pid %d uid %d %s", getpid(), getuid(), + (flags & TF_ONE_WAY) == 0 ? "READ REPLY" : "ONE WAY"); + err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL); + } + + if (err != NO_ERROR) { + if (reply) reply->setError(err); + return (mLastError = err); + } + + if ((flags & TF_ONE_WAY) == 0) { + if (reply) { + err = waitForResponse(reply); + } else { + Parcel fakeReply; + err = waitForResponse(&fakeReply); + } + + IF_LOG_TRANSACTIONS() { + TextOutput::Bundle _b(alog); + alog << "BR_REPLY thr " << (void*)pthread_self() << " / hand " + << handle << ": "; + if (reply) alog << indent << *reply << dedent << endl; + else alog << "(none requested)" << endl; + } + } else { + err = waitForResponse(NULL, NULL); + } + + return err; +} + +void IPCThreadState::incStrongHandle(int32_t handle) +{ + LOG_REMOTEREFS("IPCThreadState::incStrongHandle(%d)\n", handle); + mOut.writeInt32(BC_ACQUIRE); + mOut.writeInt32(handle); +} + +void IPCThreadState::decStrongHandle(int32_t handle) +{ + LOG_REMOTEREFS("IPCThreadState::decStrongHandle(%d)\n", handle); + mOut.writeInt32(BC_RELEASE); + mOut.writeInt32(handle); +} + +void IPCThreadState::incWeakHandle(int32_t handle) +{ + LOG_REMOTEREFS("IPCThreadState::incWeakHandle(%d)\n", handle); + mOut.writeInt32(BC_INCREFS); + mOut.writeInt32(handle); +} + +void IPCThreadState::decWeakHandle(int32_t handle) +{ + LOG_REMOTEREFS("IPCThreadState::decWeakHandle(%d)\n", handle); + mOut.writeInt32(BC_DECREFS); + mOut.writeInt32(handle); +} + +status_t IPCThreadState::attemptIncStrongHandle(int32_t handle) +{ + mOut.writeInt32(BC_ATTEMPT_ACQUIRE); + mOut.writeInt32(0); // xxx was thread priority + mOut.writeInt32(handle); + status_t result = UNKNOWN_ERROR; + + waitForResponse(NULL, &result); + +#if LOG_REFCOUNTS + printf("IPCThreadState::attemptIncStrongHandle(%ld) = %s\n", + handle, result == NO_ERROR ? "SUCCESS" : "FAILURE"); +#endif + + return result; +} + +void IPCThreadState::expungeHandle(int32_t handle, IBinder* binder) +{ +#if LOG_REFCOUNTS + printf("IPCThreadState::expungeHandle(%ld)\n", handle); +#endif + self()->mProcess->expungeHandle(handle, binder); +} + +status_t IPCThreadState::requestDeathNotification(int32_t handle, BpBinder* proxy) +{ + mOut.writeInt32(BC_REQUEST_DEATH_NOTIFICATION); + mOut.writeInt32((int32_t)handle); + mOut.writeInt32((int32_t)proxy); + return NO_ERROR; +} + +status_t IPCThreadState::clearDeathNotification(int32_t handle, BpBinder* proxy) +{ + mOut.writeInt32(BC_CLEAR_DEATH_NOTIFICATION); + mOut.writeInt32((int32_t)handle); + mOut.writeInt32((int32_t)proxy); + return NO_ERROR; +} + +IPCThreadState::IPCThreadState() + : mProcess(ProcessState::self()) +{ + pthread_setspecific(gTLS, this); + clearCaller(); + mIn.setDataCapacity(256); + mOut.setDataCapacity(256); +} + +IPCThreadState::~IPCThreadState() +{ +} + +status_t IPCThreadState::sendReply(const Parcel& reply, uint32_t flags) +{ + status_t err; + status_t statusBuffer; + err = writeTransactionData(BC_REPLY, flags, -1, 0, reply, &statusBuffer); + if (err < NO_ERROR) return err; + + return waitForResponse(NULL, NULL); +} + +status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult) +{ + int32_t cmd; + int32_t err; + + while (1) { + if ((err=talkWithDriver()) < NO_ERROR) break; + err = mIn.errorCheck(); + if (err < NO_ERROR) break; + if (mIn.dataAvail() == 0) continue; + + cmd = mIn.readInt32(); + + IF_LOG_COMMANDS() { + alog << "Processing waitForResponse Command: " + << getReturnString(cmd) << endl; + } + + switch (cmd) { + case BR_TRANSACTION_COMPLETE: + if (!reply && !acquireResult) goto finish; + break; + + case BR_DEAD_REPLY: + err = DEAD_OBJECT; + goto finish; + + case BR_FAILED_REPLY: + err = FAILED_TRANSACTION; + goto finish; + + case BR_ACQUIRE_RESULT: + { + LOG_ASSERT(acquireResult != NULL, "Unexpected brACQUIRE_RESULT"); + const int32_t result = mIn.readInt32(); + if (!acquireResult) continue; + *acquireResult = result ? NO_ERROR : INVALID_OPERATION; + } + goto finish; + + case BR_REPLY: + { + binder_transaction_data tr; + err = mIn.read(&tr, sizeof(tr)); + LOG_ASSERT(err == NO_ERROR, "Not enough command data for brREPLY"); + if (err != NO_ERROR) goto finish; + + if (reply) { + if ((tr.flags & TF_STATUS_CODE) == 0) { + reply->ipcSetDataReference( + reinterpret_cast(tr.data.ptr.buffer), + tr.data_size, + reinterpret_cast(tr.data.ptr.offsets), + tr.offsets_size/sizeof(size_t), + freeBuffer, this); + } else { + err = *static_cast(tr.data.ptr.buffer); + freeBuffer(NULL, + reinterpret_cast(tr.data.ptr.buffer), + tr.data_size, + reinterpret_cast(tr.data.ptr.offsets), + tr.offsets_size/sizeof(size_t), this); + } + } else { + freeBuffer(NULL, + reinterpret_cast(tr.data.ptr.buffer), + tr.data_size, + reinterpret_cast(tr.data.ptr.offsets), + tr.offsets_size/sizeof(size_t), this); + continue; + } + } + goto finish; + + default: + err = executeCommand(cmd); + if (err != NO_ERROR) goto finish; + break; + } + } + +finish: + if (err != NO_ERROR) { + if (acquireResult) *acquireResult = err; + if (reply) reply->setError(err); + mLastError = err; + } + + return err; +} + +status_t IPCThreadState::talkWithDriver(bool doReceive) +{ + LOG_ASSERT(mProcess->mDriverFD >= 0, "Binder driver is not opened"); + + binder_write_read bwr; + + // Is the read buffer empty? + const bool needRead = mIn.dataPosition() >= mIn.dataSize(); + + // We don't want to write anything if we are still reading + // from data left in the input buffer and the caller + // has requested to read the next data. + const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : 0; + + bwr.write_size = outAvail; + bwr.write_buffer = (long unsigned int)mOut.data(); + + // This is what we'll read. + if (doReceive && needRead) { + bwr.read_size = mIn.dataCapacity(); + bwr.read_buffer = (long unsigned int)mIn.data(); + } else { + bwr.read_size = 0; + } + + IF_LOG_COMMANDS() { + TextOutput::Bundle _b(alog); + if (outAvail != 0) { + alog << "Sending commands to driver: " << indent; + const void* cmds = (const void*)bwr.write_buffer; + const void* end = ((const uint8_t*)cmds)+bwr.write_size; + alog << HexDump(cmds, bwr.write_size) << endl; + while (cmds < end) cmds = printCommand(alog, cmds); + alog << dedent; + } + alog << "Size of receive buffer: " << bwr.read_size + << ", needRead: " << needRead << ", doReceive: " << doReceive << endl; + } + + // Return immediately if there is nothing to do. + if ((bwr.write_size == 0) && (bwr.read_size == 0)) return NO_ERROR; + + bwr.write_consumed = 0; + bwr.read_consumed = 0; + status_t err; + do { + IF_LOG_COMMANDS() { + alog << "About to read/write, write size = " << mOut.dataSize() << endl; + } +#if defined(HAVE_ANDROID_OS) + if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0) + err = NO_ERROR; + else + err = -errno; +#else + err = INVALID_OPERATION; +#endif + IF_LOG_COMMANDS() { + alog << "Finished read/write, write size = " << mOut.dataSize() << endl; + } + } while (err == -EINTR); + + IF_LOG_COMMANDS() { + alog << "Our err: " << (void*)err << ", write consumed: " + << bwr.write_consumed << " (of " << mOut.dataSize() + << "), read consumed: " << bwr.read_consumed << endl; + } + + if (err >= NO_ERROR) { + if (bwr.write_consumed > 0) { + if (bwr.write_consumed < (ssize_t)mOut.dataSize()) + mOut.remove(0, bwr.write_consumed); + else + mOut.setDataSize(0); + } + if (bwr.read_consumed > 0) { + mIn.setDataSize(bwr.read_consumed); + mIn.setDataPosition(0); + } + IF_LOG_COMMANDS() { + TextOutput::Bundle _b(alog); + alog << "Remaining data size: " << mOut.dataSize() << endl; + alog << "Received commands from driver: " << indent; + const void* cmds = mIn.data(); + const void* end = mIn.data() + mIn.dataSize(); + alog << HexDump(cmds, mIn.dataSize()) << endl; + while (cmds < end) cmds = printReturnCommand(alog, cmds); + alog << dedent; + } + return NO_ERROR; + } + + return err; +} + +status_t IPCThreadState::writeTransactionData(int32_t cmd, uint32_t binderFlags, + int32_t handle, uint32_t code, const Parcel& data, status_t* statusBuffer) +{ + binder_transaction_data tr; + + tr.target.handle = handle; + tr.code = code; + tr.flags = binderFlags; + + const status_t err = data.errorCheck(); + if (err == NO_ERROR) { + tr.data_size = data.ipcDataSize(); + tr.data.ptr.buffer = data.ipcData(); + tr.offsets_size = data.ipcObjectsCount()*sizeof(size_t); + tr.data.ptr.offsets = data.ipcObjects(); + } else if (statusBuffer) { + tr.flags |= TF_STATUS_CODE; + *statusBuffer = err; + tr.data_size = sizeof(status_t); + tr.data.ptr.buffer = statusBuffer; + tr.offsets_size = 0; + tr.data.ptr.offsets = NULL; + } else { + return (mLastError = err); + } + + mOut.writeInt32(cmd); + mOut.write(&tr, sizeof(tr)); + + return NO_ERROR; +} + +sp the_context_object; + +void setTheContextObject(sp obj) +{ + the_context_object = obj; +} + +status_t IPCThreadState::executeCommand(int32_t cmd) +{ + BBinder* obj; + RefBase::weakref_type* refs; + status_t result = NO_ERROR; + + switch (cmd) { + case BR_ERROR: + result = mIn.readInt32(); + break; + + case BR_OK: + break; + + case BR_ACQUIRE: + refs = (RefBase::weakref_type*)mIn.readInt32(); + obj = (BBinder*)mIn.readInt32(); + LOG_ASSERT(refs->refBase() == obj, + "BR_ACQUIRE: object %p does not match cookie %p (expected %p)", + refs, obj, refs->refBase()); + obj->incStrong(mProcess.get()); + IF_LOG_REMOTEREFS() { + LOG_REMOTEREFS("BR_ACQUIRE from driver on %p", obj); + obj->printRefs(); + } + mOut.writeInt32(BC_ACQUIRE_DONE); + mOut.writeInt32((int32_t)refs); + mOut.writeInt32((int32_t)obj); + break; + + case BR_RELEASE: + refs = (RefBase::weakref_type*)mIn.readInt32(); + obj = (BBinder*)mIn.readInt32(); + LOG_ASSERT(refs->refBase() == obj, + "BR_RELEASE: object %p does not match cookie %p (expected %p)", + refs, obj, refs->refBase()); + IF_LOG_REMOTEREFS() { + LOG_REMOTEREFS("BR_RELEASE from driver on %p", obj); + obj->printRefs(); + } + mPendingStrongDerefs.push(obj); + break; + + case BR_INCREFS: + refs = (RefBase::weakref_type*)mIn.readInt32(); + obj = (BBinder*)mIn.readInt32(); + refs->incWeak(mProcess.get()); + mOut.writeInt32(BC_INCREFS_DONE); + mOut.writeInt32((int32_t)refs); + mOut.writeInt32((int32_t)obj); + break; + + case BR_DECREFS: + refs = (RefBase::weakref_type*)mIn.readInt32(); + obj = (BBinder*)mIn.readInt32(); + // NOTE: This assertion is not valid, because the object may no + // longer exist (thus the (BBinder*)cast above resulting in a different + // memory address). + //LOG_ASSERT(refs->refBase() == obj, + // "BR_DECREFS: object %p does not match cookie %p (expected %p)", + // refs, obj, refs->refBase()); + mPendingWeakDerefs.push(refs); + break; + + case BR_ATTEMPT_ACQUIRE: + refs = (RefBase::weakref_type*)mIn.readInt32(); + obj = (BBinder*)mIn.readInt32(); + + { + const bool success = refs->attemptIncStrong(mProcess.get()); + LOG_ASSERT(success && refs->refBase() == obj, + "BR_ATTEMPT_ACQUIRE: object %p does not match cookie %p (expected %p)", + refs, obj, refs->refBase()); + + mOut.writeInt32(BC_ACQUIRE_RESULT); + mOut.writeInt32((int32_t)success); + } + break; + + case BR_TRANSACTION: + { + binder_transaction_data tr; + result = mIn.read(&tr, sizeof(tr)); + LOG_ASSERT(result == NO_ERROR, + "Not enough command data for brTRANSACTION"); + if (result != NO_ERROR) break; + + Parcel buffer; + buffer.ipcSetDataReference( + reinterpret_cast(tr.data.ptr.buffer), + tr.data_size, + reinterpret_cast(tr.data.ptr.offsets), + tr.offsets_size/sizeof(size_t), freeBuffer, this); + + const pid_t origPid = mCallingPid; + const uid_t origUid = mCallingUid; + + mCallingPid = tr.sender_pid; + mCallingUid = tr.sender_euid; + + //LOGI(">>>> TRANSACT from pid %d uid %d\n", mCallingPid, mCallingUid); + + Parcel reply; + IF_LOG_TRANSACTIONS() { + TextOutput::Bundle _b(alog); + alog << "BR_TRANSACTION thr " << (void*)pthread_self() + << " / obj " << tr.target.ptr << " / code " + << TypeCode(tr.code) << ": " << indent << buffer + << dedent << endl + << "Data addr = " + << reinterpret_cast(tr.data.ptr.buffer) + << ", offsets addr=" + << reinterpret_cast(tr.data.ptr.offsets) << endl; + } + if (tr.target.ptr) { + sp b((BBinder*)tr.cookie); + const status_t error = b->transact(tr.code, buffer, &reply, 0); + if (error < NO_ERROR) reply.setError(error); + + } else { + const status_t error = the_context_object->transact(tr.code, buffer, &reply, 0); + if (error < NO_ERROR) reply.setError(error); + } + + //LOGI("<<<< TRANSACT from pid %d restore pid %d uid %d\n", + // mCallingPid, origPid, origUid); + + if ((tr.flags & TF_ONE_WAY) == 0) { + LOG_ONEWAY("Sending reply to %d!", mCallingPid); + sendReply(reply, 0); + } else { + LOG_ONEWAY("NOT sending reply to %d!", mCallingPid); + } + + mCallingPid = origPid; + mCallingUid = origUid; + + IF_LOG_TRANSACTIONS() { + TextOutput::Bundle _b(alog); + alog << "BC_REPLY thr " << (void*)pthread_self() << " / obj " + << tr.target.ptr << ": " << indent << reply << dedent << endl; + } + + } + break; + + case BR_DEAD_BINDER: + { + BpBinder *proxy = (BpBinder*)mIn.readInt32(); + proxy->sendObituary(); + mOut.writeInt32(BC_DEAD_BINDER_DONE); + mOut.writeInt32((int32_t)proxy); + } break; + + case BR_CLEAR_DEATH_NOTIFICATION_DONE: + { + BpBinder *proxy = (BpBinder*)mIn.readInt32(); + proxy->getWeakRefs()->decWeak(proxy); + } break; + + case BR_FINISHED: + result = TIMED_OUT; + break; + + case BR_NOOP: + break; + + case BR_SPAWN_LOOPER: + mProcess->spawnPooledThread(false); + break; + + default: + printf("*** BAD COMMAND %d received from Binder driver\n", cmd); + result = UNKNOWN_ERROR; + break; + } + + if (result != NO_ERROR) { + mLastError = result; + } + + return result; +} + +void IPCThreadState::threadDestructor(void *st) +{ + IPCThreadState* const self = static_cast(st); + if (self) { + self->flushCommands(); +#if defined(HAVE_ANDROID_OS) + ioctl(self->mProcess->mDriverFD, BINDER_THREAD_EXIT, 0); +#endif + delete self; + } +} + + +void IPCThreadState::freeBuffer(Parcel* parcel, const uint8_t* data, size_t dataSize, + const size_t* objects, size_t objectsSize, + void* cookie) +{ + //LOGI("Freeing parcel %p", &parcel); + IF_LOG_COMMANDS() { + alog << "Writing BC_FREE_BUFFER for " << data << endl; + } + LOG_ASSERT(data != NULL, "Called with NULL data"); + if (parcel != NULL) parcel->closeFileDescriptors(); + IPCThreadState* state = self(); + state->mOut.writeInt32(BC_FREE_BUFFER); + state->mOut.writeInt32((int32_t)data); +} + +}; // namespace android diff --git a/libs/utils/IPermissionController.cpp b/libs/utils/IPermissionController.cpp new file mode 100644 index 000000000..f01d38fd1 --- /dev/null +++ b/libs/utils/IPermissionController.cpp @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2005 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 "PermissionController" + +#include + +#include +#include +#include +#include + +#include + +namespace android { + +// ---------------------------------------------------------------------- + +class BpPermissionController : public BpInterface +{ +public: + BpPermissionController(const sp& impl) + : BpInterface(impl) + { + } + + virtual bool checkPermission(const String16& permission, int32_t pid, int32_t uid) + { + Parcel data, reply; + data.writeInterfaceToken(IPermissionController::getInterfaceDescriptor()); + data.writeString16(permission); + data.writeInt32(pid); + data.writeInt32(uid); + remote()->transact(CHECK_PERMISSION_TRANSACTION, data, &reply); + // fail on exception + if (reply.readInt32() != 0) return 0; + return reply.readInt32() != 0; + } +}; + +IMPLEMENT_META_INTERFACE(PermissionController, "android.os.IPermissionController"); + +// ---------------------------------------------------------------------- + +#define CHECK_INTERFACE(interface, data, reply) \ + do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ + LOGW("Call incorrectly routed to " #interface); \ + return PERMISSION_DENIED; \ + } } while (0) + +status_t BnPermissionController::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + //printf("PermissionController received: "); data.print(); + switch(code) { + case CHECK_PERMISSION_TRANSACTION: { + CHECK_INTERFACE(IPermissionController, data, reply); + String16 permission = data.readString16(); + int32_t pid = data.readInt32(); + int32_t uid = data.readInt32(); + bool res = checkPermission(permission, pid, uid); + // write exception + reply->writeInt32(0); + reply->writeInt32(res ? 1 : 0); + return NO_ERROR; + } break; + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +}; // namespace android + diff --git a/libs/utils/IServiceManager.cpp b/libs/utils/IServiceManager.cpp new file mode 100644 index 000000000..9beeaddde --- /dev/null +++ b/libs/utils/IServiceManager.cpp @@ -0,0 +1,230 @@ +/* + * Copyright (C) 2005 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 "ServiceManager" + +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include + +namespace android { + +sp defaultServiceManager() +{ + if (gDefaultServiceManager != NULL) return gDefaultServiceManager; + + { + AutoMutex _l(gDefaultServiceManagerLock); + if (gDefaultServiceManager == NULL) { + gDefaultServiceManager = interface_cast( + ProcessState::self()->getContextObject(NULL)); + } + } + + return gDefaultServiceManager; +} + +bool checkCallingPermission(const String16& permission) +{ + return checkCallingPermission(permission, NULL, NULL); +} + +static String16 _permission("permission"); + +bool checkCallingPermission(const String16& permission, int32_t* outPid, int32_t* outUid) +{ + IPCThreadState* ipcState = IPCThreadState::self(); + int32_t pid = ipcState->getCallingPid(); + int32_t uid = ipcState->getCallingUid(); + if (outPid) *outPid = pid; + if (outUid) *outUid= uid; + + sp pc; + gDefaultServiceManagerLock.lock(); + pc = gPermissionController; + gDefaultServiceManagerLock.unlock(); + + int64_t startTime = 0; + + while (true) { + if (pc != NULL) { + bool res = pc->checkPermission(permission, pid, uid); + if (res) { + if (startTime != 0) { + LOGI("Check passed after %d seconds for %s from uid=%d pid=%d", + (int)((uptimeMillis()-startTime)/1000), + String8(permission).string(), uid, pid); + } + return res; + } + + // Is this a permission failure, or did the controller go away? + if (pc->asBinder()->isBinderAlive()) { + LOGW("Permission failure: %s from uid=%d pid=%d", + String8(permission).string(), uid, pid); + return false; + } + + // Object is dead! + gDefaultServiceManagerLock.lock(); + if (gPermissionController == pc) { + gPermissionController = NULL; + } + gDefaultServiceManagerLock.unlock(); + } + + // Need to retrieve the permission controller. + sp binder = defaultServiceManager()->checkService(_permission); + if (binder == NULL) { + // Wait for the permission controller to come back... + if (startTime == 0) { + startTime = uptimeMillis(); + LOGI("Waiting to check permission %s from uid=%d pid=%d", + String8(permission).string(), uid, pid); + } + sleep(1); + } else { + pc = interface_cast(binder); + // Install the new permission controller, and try again. + gDefaultServiceManagerLock.lock(); + gPermissionController = pc; + gDefaultServiceManagerLock.unlock(); + } + } +} + +// ---------------------------------------------------------------------- + +class BpServiceManager : public BpInterface +{ +public: + BpServiceManager(const sp& impl) + : BpInterface(impl) + { + } + + virtual sp getService(const String16& name) const + { + unsigned n; + for (n = 0; n < 5; n++){ + sp svc = checkService(name); + if (svc != NULL) return svc; + LOGI("Waiting for sevice %s...\n", String8(name).string()); + sleep(1); + } + return NULL; + } + + virtual sp checkService( const String16& name) const + { + Parcel data, reply; + data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor()); + data.writeString16(name); + remote()->transact(CHECK_SERVICE_TRANSACTION, data, &reply); + return reply.readStrongBinder(); + } + + virtual status_t addService(const String16& name, const sp& service) + { + Parcel data, reply; + data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor()); + data.writeString16(name); + data.writeStrongBinder(service); + status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply); + return err == NO_ERROR ? reply.readInt32() : err; + } + + virtual Vector listServices() + { + Vector res; + int n = 0; + + for (;;) { + Parcel data, reply; + data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor()); + data.writeInt32(n++); + status_t err = remote()->transact(LIST_SERVICES_TRANSACTION, data, &reply); + if (err != NO_ERROR) + break; + res.add(reply.readString16()); + } + return res; + } +}; + +IMPLEMENT_META_INTERFACE(ServiceManager, "android.os.IServiceManager"); + +// ---------------------------------------------------------------------- + +#define CHECK_INTERFACE(interface, data, reply) \ + do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ + LOGW("Call incorrectly routed to " #interface); \ + return PERMISSION_DENIED; \ + } } while (0) + +status_t BnServiceManager::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + //printf("ServiceManager received: "); data.print(); + switch(code) { + case GET_SERVICE_TRANSACTION: { + CHECK_INTERFACE(IServiceManager, data, reply); + String16 which = data.readString16(); + sp b = const_cast(this)->getService(which); + reply->writeStrongBinder(b); + return NO_ERROR; + } break; + case CHECK_SERVICE_TRANSACTION: { + CHECK_INTERFACE(IServiceManager, data, reply); + String16 which = data.readString16(); + sp b = const_cast(this)->checkService(which); + reply->writeStrongBinder(b); + return NO_ERROR; + } break; + case ADD_SERVICE_TRANSACTION: { + CHECK_INTERFACE(IServiceManager, data, reply); + String16 which = data.readString16(); + sp b = data.readStrongBinder(); + status_t err = addService(which, b); + reply->writeInt32(err); + return NO_ERROR; + } break; + case LIST_SERVICES_TRANSACTION: { + CHECK_INTERFACE(IServiceManager, data, reply); + Vector list = listServices(); + const size_t N = list.size(); + reply->writeInt32(N); + for (size_t i=0; iwriteString16(list[i]); + } + return NO_ERROR; + } break; + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +}; // namespace android + diff --git a/libs/utils/InetAddress.cpp b/libs/utils/InetAddress.cpp new file mode 100644 index 000000000..39a0a6839 --- /dev/null +++ b/libs/utils/InetAddress.cpp @@ -0,0 +1,236 @@ +/* + * Copyright (C) 2005 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. + */ + +// +// Internet address class. +// +#ifdef HAVE_WINSOCK +# include +#else +# include +# include +# include +//# include +# include +#endif + +#include +#include +#include + +#include +#include +#include +#include +#include + +using namespace android; + + +/* + * =========================================================================== + * InetAddress + * =========================================================================== + */ + +// lock for the next couple of functions; could tuck into InetAddress +static Mutex* gGHBNLock; + +/* + * Lock/unlock access to the hostent struct returned by gethostbyname(). + */ +static inline void lock_gethostbyname(void) +{ + if (gGHBNLock == NULL) + gGHBNLock = new Mutex; + gGHBNLock->lock(); +} +static inline void unlock_gethostbyname(void) +{ + assert(gGHBNLock != NULL); + gGHBNLock->unlock(); +} + + +/* + * Constructor -- just init members. This is private so that callers + * are required to use getByName(). + */ +InetAddress::InetAddress(void) + : mAddress(NULL), mLength(-1), mName(NULL) +{ +} + +/* + * Destructor -- free address storage. + */ +InetAddress::~InetAddress(void) +{ + delete[] (char*) mAddress; + delete[] mName; +} + +/* + * Copy constructor. + */ +InetAddress::InetAddress(const InetAddress& orig) +{ + *this = orig; // use assignment code +} + +/* + * Assignment operator. + */ +InetAddress& InetAddress::operator=(const InetAddress& addr) +{ + // handle self-assignment + if (this == &addr) + return *this; + // copy mLength and mAddress + mLength = addr.mLength; + if (mLength > 0) { + mAddress = new char[mLength]; + memcpy(mAddress, addr.mAddress, mLength); + LOG(LOG_DEBUG, "socket", + "HEY: copied %d bytes in assignment operator\n", mLength); + } else { + mAddress = NULL; + } + // copy mName + mName = new char[strlen(addr.mName)+1]; + strcpy(mName, addr.mName); + + return *this; +} + +/* + * Create a new object from a name or a dotted-number IP notation. + * + * Returns NULL on failure. + */ +InetAddress* +InetAddress::getByName(const char* host) +{ + InetAddress* newAddr = NULL; + struct sockaddr_in addr; + struct hostent* he; + DurationTimer hostTimer, lockTimer; + + // gethostbyname() isn't reentrant, so we need to lock things until + // we can copy the data out. + lockTimer.start(); + lock_gethostbyname(); + hostTimer.start(); + + he = gethostbyname(host); + if (he == NULL) { + LOG(LOG_WARN, "socket", "WARNING: cannot resolve host %s\n", host); + unlock_gethostbyname(); + return NULL; + } + + memcpy(&addr.sin_addr, he->h_addr, he->h_length); + addr.sin_family = he->h_addrtype; + addr.sin_port = 0; + + // got it, unlock us + hostTimer.stop(); + he = NULL; + unlock_gethostbyname(); + + lockTimer.stop(); + if ((long) lockTimer.durationUsecs() > 100000) { + long lockTime = (long) lockTimer.durationUsecs(); + long hostTime = (long) hostTimer.durationUsecs(); + LOG(LOG_DEBUG, "socket", + "Lookup of %s took %.3fs (gethostbyname=%.3fs lock=%.3fs)\n", + host, lockTime / 1000000.0, hostTime / 1000000.0, + (lockTime - hostTime) / 1000000.0); + } + + // Alloc storage and copy it over. + newAddr = new InetAddress(); + if (newAddr == NULL) + return NULL; + + newAddr->mLength = sizeof(struct sockaddr_in); + newAddr->mAddress = new char[sizeof(struct sockaddr_in)]; + if (newAddr->mAddress == NULL) { + delete newAddr; + return NULL; + } + memcpy(newAddr->mAddress, &addr, newAddr->mLength); + + // Keep this for debug messages. + newAddr->mName = new char[strlen(host)+1]; + if (newAddr->mName == NULL) { + delete newAddr; + return NULL; + } + strcpy(newAddr->mName, host); + + return newAddr; +} + + +/* + * =========================================================================== + * InetSocketAddress + * =========================================================================== + */ + +/* + * Create an address with the host wildcard (INADDR_ANY). + */ +bool InetSocketAddress::create(int port) +{ + assert(mAddress == NULL); + + mAddress = InetAddress::getByName("0.0.0.0"); + if (mAddress == NULL) + return false; + mPort = port; + return true; +} + +/* + * Create address with host and port specified. + */ +bool InetSocketAddress::create(const InetAddress* addr, int port) +{ + assert(mAddress == NULL); + + mAddress = new InetAddress(*addr); // make a copy + if (mAddress == NULL) + return false; + mPort = port; + return true; +} + +/* + * Create address with host and port specified. + */ +bool InetSocketAddress::create(const char* host, int port) +{ + assert(mAddress == NULL); + + mAddress = InetAddress::getByName(host); + if (mAddress == NULL) + return false; + mPort = port; + return true; +} + diff --git a/libs/utils/LogSocket.cpp b/libs/utils/LogSocket.cpp new file mode 100644 index 000000000..55c1b99af --- /dev/null +++ b/libs/utils/LogSocket.cpp @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#ifndef HAVE_WINSOCK +//#define SOCKETLOG +#endif + +#ifdef SOCKETLOG + +#define LOG_TAG "SOCKETLOG" + +#include +#include +#include "utils/LogSocket.h" +#include "utils/logger.h" +#include "cutils/hashmap.h" + +// defined in //device/data/etc/event-log-tags +#define SOCKET_CLOSE_LOG 51000 + +static Hashmap* statsMap = NULL; + +#define LOG_LIST_NUMBER 5 + +typedef struct SocketStats { + int fd; + unsigned int send; + unsigned int recv; + unsigned int ip; + unsigned short port; + short reason; +}SocketStats; + +SocketStats *get_socket_stats(int fd) { + if (statsMap == NULL) { + statsMap = hashmapCreate(8, &hashmapIntHash, &hashmapIntEquals); + } + + SocketStats *s = (SocketStats*) hashmapGet(statsMap, &fd); + if (s == NULL) { + // LOGD("create SocketStats for fd %d", fd); + s = (SocketStats*) malloc(sizeof(SocketStats)); + memset(s, 0, sizeof(SocketStats)); + s->fd = fd; + hashmapPut(statsMap, &s->fd, s); + } + return s; +} + +void log_socket_connect(int fd, unsigned int ip, unsigned short port) { + // LOGD("log_socket_connect for fd %d ip %d port%d", fd, ip, port); + SocketStats *s = get_socket_stats(fd); + s->ip = ip; + s->port = port; +} + +void add_send_stats(int fd, int send) { + if (send <=0) { + LOGE("add_send_stats send %d", send); + return; + } + SocketStats *s = get_socket_stats(fd); + s->send += send; + // LOGD("add_send_stats for fd %d ip %d port%d", fd, s->ip, s->port); +} + +void add_recv_stats(int fd, int recv) { + if (recv <=0) { + LOGE("add_recv_stats recv %d", recv); + return; + } + SocketStats *s = get_socket_stats(fd); + s->recv += recv; + // LOGD("add_recv_stats for fd %d ip %d port%d", fd, s->ip, s->port); +} + +char* put_int(char* buf, int value) { + *buf = EVENT_TYPE_INT; + buf++; + memcpy(buf, &value, sizeof(int)); + return buf + sizeof(int); +} + +void log_socket_close(int fd, short reason) { + if (statsMap) { + SocketStats *s = (SocketStats*) hashmapGet(statsMap, &fd); + if (s != NULL) { + if (s->send != 0 || s->recv != 0) { + s->reason = reason; + // 5 int + list type need 2 bytes + char buf[LOG_LIST_NUMBER * 5 + 2]; + buf[0] = EVENT_TYPE_LIST; + buf[1] = LOG_LIST_NUMBER; + char* writePos = buf + 2; + writePos = put_int(writePos, s->send); + writePos = put_int(writePos, s->recv); + writePos = put_int(writePos, s->ip); + writePos = put_int(writePos, s->port); + writePos = put_int(writePos, s->reason); + + android_bWriteLog(SOCKET_CLOSE_LOG, buf, sizeof(buf)); + // LOGD("send %d recv %d reason %d", s->send, s->recv, s->reason); + } + hashmapRemove(statsMap, &s->fd); + free(s); + } + } +} + +#else +void add_send_stats(int fd, int send) {} +void add_recv_stats(int fd, int recv) {} +void log_socket_close(int fd, short reason) {} +void log_socket_connect(int fd, unsigned int ip, unsigned short port) {} +#endif diff --git a/libs/utils/MODULE_LICENSE_APACHE2 b/libs/utils/MODULE_LICENSE_APACHE2 new file mode 100644 index 000000000..e69de29bb diff --git a/libs/utils/MemoryBase.cpp b/libs/utils/MemoryBase.cpp new file mode 100644 index 000000000..f25e11c6b --- /dev/null +++ b/libs/utils/MemoryBase.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include +#include + +#include + + +namespace android { + +// --------------------------------------------------------------------------- + +MemoryBase::MemoryBase(const sp& heap, + ssize_t offset, size_t size) + : mSize(size), mOffset(offset), mHeap(heap) +{ +} + +sp MemoryBase::getMemory(ssize_t* offset, size_t* size) const +{ + if (offset) *offset = mOffset; + if (size) *size = mSize; + return mHeap; +} + +MemoryBase::~MemoryBase() +{ +} + +// --------------------------------------------------------------------------- +}; // namespace android diff --git a/libs/utils/MemoryDealer.cpp b/libs/utils/MemoryDealer.cpp new file mode 100644 index 000000000..cf8201b85 --- /dev/null +++ b/libs/utils/MemoryDealer.cpp @@ -0,0 +1,409 @@ +/* + * 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. + */ + +#define LOG_TAG "MemoryDealer" + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace android { + + +// ---------------------------------------------------------------------------- + +class SimpleMemory : public MemoryBase { +public: + SimpleMemory(const sp& heap, ssize_t offset, size_t size); + virtual ~SimpleMemory(); +}; + + +// ---------------------------------------------------------------------------- + +MemoryDealer::Allocation::Allocation( + const sp& dealer, ssize_t offset, size_t size, + const sp& memory) + : mDealer(dealer), mOffset(offset), mSize(size), mMemory(memory) +{ +} + +MemoryDealer::Allocation::~Allocation() +{ + if (mSize) { + /* NOTE: it's VERY important to not free allocations of size 0 because + * they're special as they don't have any record in the allocator + * and could alias some real allocation (their offset is zero). */ + mDealer->deallocate(mOffset); + } +} + +sp MemoryDealer::Allocation::getMemory( + ssize_t* offset, size_t* size) const +{ + return mMemory->getMemory(offset, size); +} + +// ---------------------------------------------------------------------------- + +MemoryDealer::MemoryDealer(size_t size, uint32_t flags, const char* name) + : mHeap(new SharedHeap(size, flags, name)), + mAllocator(new SimpleBestFitAllocator(size)) +{ +} + +MemoryDealer::MemoryDealer(const sp& heap) + : mHeap(heap), + mAllocator(new SimpleBestFitAllocator(heap->virtualSize())) +{ +} + +MemoryDealer::MemoryDealer( const sp& heap, + const sp& allocator) + : mHeap(heap), mAllocator(allocator) +{ +} + +MemoryDealer::~MemoryDealer() +{ +} + +sp MemoryDealer::allocate(size_t size, uint32_t flags) +{ + sp memory; + const ssize_t offset = allocator()->allocate(size, flags); + if (offset >= 0) { + sp new_memory = heap()->mapMemory(offset, size); + if (new_memory != 0) { + memory = new Allocation(this, offset, size, new_memory); + } else { + LOGE("couldn't map [%8x, %d]", offset, size); + if (size) { + /* NOTE: it's VERY important to not free allocations of size 0 + * because they're special as they don't have any record in the + * allocator and could alias some real allocation + * (their offset is zero). */ + allocator()->deallocate(offset); + } + } + } + return memory; +} + +void MemoryDealer::deallocate(size_t offset) +{ + allocator()->deallocate(offset); +} + +void MemoryDealer::dump(const char* what, uint32_t flags) const +{ + allocator()->dump(what, flags); +} + +const sp& MemoryDealer::heap() const { + return mHeap; +} + +const sp& MemoryDealer::allocator() const { + return mAllocator; +} + +// ---------------------------------------------------------------------------- + +// align all the memory blocks on a cache-line boundary +const int SimpleBestFitAllocator::kMemoryAlign = 32; + +SimpleBestFitAllocator::SimpleBestFitAllocator(size_t size) +{ + size_t pagesize = getpagesize(); + mHeapSize = ((size + pagesize-1) & ~(pagesize-1)); + + chunk_t* node = new chunk_t(0, mHeapSize / kMemoryAlign); + mList.insertHead(node); +} + +SimpleBestFitAllocator::~SimpleBestFitAllocator() +{ + while(!mList.isEmpty()) { + delete mList.remove(mList.head()); + } +} + +size_t SimpleBestFitAllocator::size() const +{ + return mHeapSize; +} + +size_t SimpleBestFitAllocator::allocate(size_t size, uint32_t flags) +{ + Mutex::Autolock _l(mLock); + ssize_t offset = alloc(size, flags); + return offset; +} + +status_t SimpleBestFitAllocator::deallocate(size_t offset) +{ + Mutex::Autolock _l(mLock); + chunk_t const * const freed = dealloc(offset); + if (freed) { + return NO_ERROR; + } + return NAME_NOT_FOUND; +} + +ssize_t SimpleBestFitAllocator::alloc(size_t size, uint32_t flags) +{ + if (size == 0) { + return 0; + } + size = (size + kMemoryAlign-1) / kMemoryAlign; + chunk_t* free_chunk = 0; + chunk_t* cur = mList.head(); + + size_t pagesize = getpagesize(); + while (cur) { + int extra = 0; + if (flags & PAGE_ALIGNED) + extra = ( -cur->start & ((pagesize/kMemoryAlign)-1) ) ; + + // best fit + if (cur->free && (cur->size >= (size+extra))) { + if ((!free_chunk) || (cur->size < free_chunk->size)) { + free_chunk = cur; + } + if (cur->size == size) { + break; + } + } + cur = cur->next; + } + + if (free_chunk) { + const size_t free_size = free_chunk->size; + free_chunk->free = 0; + free_chunk->size = size; + if (free_size > size) { + int extra = 0; + if (flags & PAGE_ALIGNED) + extra = ( -free_chunk->start & ((pagesize/kMemoryAlign)-1) ) ; + if (extra) { + chunk_t* split = new chunk_t(free_chunk->start, extra); + free_chunk->start += extra; + mList.insertBefore(free_chunk, split); + } + + LOGE_IF((flags&PAGE_ALIGNED) && + ((free_chunk->start*kMemoryAlign)&(pagesize-1)), + "PAGE_ALIGNED requested, but page is not aligned!!!"); + + const ssize_t tail_free = free_size - (size+extra); + if (tail_free > 0) { + chunk_t* split = new chunk_t( + free_chunk->start + free_chunk->size, tail_free); + mList.insertAfter(free_chunk, split); + } + } + return (free_chunk->start)*kMemoryAlign; + } + return NO_MEMORY; +} + +SimpleBestFitAllocator::chunk_t* SimpleBestFitAllocator::dealloc(size_t start) +{ + start = start / kMemoryAlign; + chunk_t* cur = mList.head(); + while (cur) { + if (cur->start == start) { + LOG_FATAL_IF(cur->free, + "block at offset 0x%08lX of size 0x%08lX already freed", + cur->start*kMemoryAlign, cur->size*kMemoryAlign); + + // merge freed blocks together + chunk_t* freed = cur; + cur->free = 1; + do { + chunk_t* const p = cur->prev; + chunk_t* const n = cur->next; + if (p && (p->free || !cur->size)) { + freed = p; + p->size += cur->size; + mList.remove(cur); + delete cur; + } + cur = n; + } while (cur && cur->free); + + #ifndef NDEBUG + if (!freed->free) { + dump_l("dealloc (!freed->free)"); + } + #endif + LOG_FATAL_IF(!freed->free, + "freed block at offset 0x%08lX of size 0x%08lX is not free!", + freed->start * kMemoryAlign, freed->size * kMemoryAlign); + + return freed; + } + cur = cur->next; + } + return 0; +} + +void SimpleBestFitAllocator::dump(const char* what, uint32_t flags) const +{ + Mutex::Autolock _l(mLock); + dump_l(what, flags); +} + +void SimpleBestFitAllocator::dump_l(const char* what, uint32_t flags) const +{ + String8 result; + dump_l(result, what, flags); + LOGD("%s", result.string()); +} + +void SimpleBestFitAllocator::dump(String8& result, + const char* what, uint32_t flags) const +{ + Mutex::Autolock _l(mLock); + dump_l(result, what, flags); +} + +void SimpleBestFitAllocator::dump_l(String8& result, + const char* what, uint32_t flags) const +{ + size_t size = 0; + int32_t i = 0; + chunk_t const* cur = mList.head(); + + const size_t SIZE = 256; + char buffer[SIZE]; + snprintf(buffer, SIZE, " %s (%p, size=%u)\n", + what, this, (unsigned int)mHeapSize); + + result.append(buffer); + + while (cur) { + const char* errs[] = {"", "| link bogus NP", + "| link bogus PN", "| link bogus NP+PN" }; + int np = ((cur->next) && cur->next->prev != cur) ? 1 : 0; + int pn = ((cur->prev) && cur->prev->next != cur) ? 2 : 0; + + snprintf(buffer, SIZE, " %3u: %08x | 0x%08X | 0x%08X | %s %s\n", + i, int(cur), int(cur->start*kMemoryAlign), + int(cur->size*kMemoryAlign), + int(cur->free) ? "F" : "A", + errs[np|pn]); + + result.append(buffer); + + if (!cur->free) + size += cur->size*kMemoryAlign; + + i++; + cur = cur->next; + } + snprintf(buffer, SIZE, " size allocated: %u (%u KB)\n", int(size), int(size/1024)); + result.append(buffer); +} + +// ---------------------------------------------------------------------------- + + +SharedHeap::SharedHeap(size_t size, uint32_t flags, char const * name) + : MemoryHeapBase(size, flags, name) +{ +} + +SharedHeap::~SharedHeap() +{ +} + +sp SharedHeap::mapMemory(size_t offset, size_t size) +{ + return new SimpleMemory(this, offset, size); +} + + +SimpleMemory::SimpleMemory(const sp& heap, + ssize_t offset, size_t size) + : MemoryBase(heap, offset, size) +{ +#ifndef NDEBUG + void* const start_ptr = (void*)(intptr_t(heap->base()) + offset); + memset(start_ptr, 0xda, size); +#endif +} + +SimpleMemory::~SimpleMemory() +{ + size_t freedOffset = getOffset(); + size_t freedSize = getSize(); + + // keep the size to unmap in excess + size_t pagesize = getpagesize(); + size_t start = freedOffset; + size_t end = start + freedSize; + start &= ~(pagesize-1); + end = (end + pagesize-1) & ~(pagesize-1); + + // give back to the kernel the pages we don't need + size_t free_start = freedOffset; + size_t free_end = free_start + freedSize; + if (start < free_start) + start = free_start; + if (end > free_end) + end = free_end; + start = (start + pagesize-1) & ~(pagesize-1); + end &= ~(pagesize-1); + + if (start < end) { + void* const start_ptr = (void*)(intptr_t(getHeap()->base()) + start); + size_t size = end-start; + +#ifndef NDEBUG + memset(start_ptr, 0xdf, size); +#endif + + // MADV_REMOVE is not defined on Dapper based Goobuntu +#ifdef MADV_REMOVE + if (size) { + int err = madvise(start_ptr, size, MADV_REMOVE); + LOGW_IF(err, "madvise(%p, %u, MADV_REMOVE) returned %s", + start_ptr, size, err<0 ? strerror(errno) : "Ok"); + } +#endif + } +} + +}; // namespace android diff --git a/libs/utils/MemoryHeapBase.cpp b/libs/utils/MemoryHeapBase.cpp new file mode 100644 index 000000000..825172819 --- /dev/null +++ b/libs/utils/MemoryHeapBase.cpp @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "MemoryHeapBase" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#if HAVE_ANDROID_OS +#include +#endif + + +namespace android { + +// --------------------------------------------------------------------------- + +MemoryHeapBase::MemoryHeapBase() + : mFD(-1), mSize(0), mBase(MAP_FAILED), + mDevice(NULL), mNeedUnmap(false) +{ +} + +MemoryHeapBase::MemoryHeapBase(size_t size, uint32_t flags, char const * name) + : mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags), + mDevice(0), mNeedUnmap(false) +{ + const size_t pagesize = getpagesize(); + size = ((size + pagesize-1) & ~(pagesize-1)); + int fd = ashmem_create_region(name == NULL ? "MemoryHeapBase" : name, size); + LOGE_IF(fd<0, "error creating ashmem region: %s", strerror(errno)); + if (fd >= 0) { + if (mapfd(fd, size) == NO_ERROR) { + if (flags & READ_ONLY) { + ashmem_set_prot_region(fd, PROT_READ); + } + } + } +} + +MemoryHeapBase::MemoryHeapBase(const char* device, size_t size, uint32_t flags) + : mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags), + mDevice(0), mNeedUnmap(false) +{ + int fd = open(device, O_RDWR); + LOGE_IF(fd<0, "error opening %s: %s", device, strerror(errno)); + if (fd >= 0) { + const size_t pagesize = getpagesize(); + size = ((size + pagesize-1) & ~(pagesize-1)); + if (mapfd(fd, size) == NO_ERROR) { + mDevice = device; + } + } +} + +MemoryHeapBase::MemoryHeapBase(int fd, size_t size, uint32_t flags) + : mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags), + mDevice(0), mNeedUnmap(false) +{ + const size_t pagesize = getpagesize(); + size = ((size + pagesize-1) & ~(pagesize-1)); + mapfd(dup(fd), size); +} + +status_t MemoryHeapBase::init(int fd, void *base, int size, int flags, const char* device) +{ + if (mFD != -1) { + return INVALID_OPERATION; + } + mFD = fd; + mBase = base; + mSize = size; + mFlags = flags; + mDevice = device; + return NO_ERROR; +} + +status_t MemoryHeapBase::mapfd(int fd, size_t size) +{ + if (size == 0) { + // try to figure out the size automatically +#if HAVE_ANDROID_OS + // first try the PMEM ioctl + pmem_region reg; + int err = ioctl(fd, PMEM_GET_TOTAL_SIZE, ®); + if (err == 0) + size = reg.len; +#endif + if (size == 0) { // try fstat + struct stat sb; + if (fstat(fd, &sb) == 0) + size = sb.st_size; + } + // if it didn't work, let mmap() fail. + } + + if ((mFlags & DONT_MAP_LOCALLY) == 0) { + void* base = (uint8_t*)mmap(0, size, + PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); + if (base == MAP_FAILED) { + LOGE("mmap(fd=%d, size=%u) failed (%s)", + fd, uint32_t(size), strerror(errno)); + close(fd); + return -errno; + } + //LOGD("mmap(fd=%d, base=%p, size=%lu)", fd, base, size); + mBase = base; + mNeedUnmap = true; + } else { + mBase = 0; // not MAP_FAILED + mNeedUnmap = false; + } + mFD = fd; + mSize = size; + return NO_ERROR; +} + +MemoryHeapBase::~MemoryHeapBase() +{ + dispose(); +} + +void MemoryHeapBase::dispose() +{ + int fd = android_atomic_or(-1, &mFD); + if (fd >= 0) { + if (mNeedUnmap) { + //LOGD("munmap(fd=%d, base=%p, size=%lu)", fd, mBase, mSize); + munmap(mBase, mSize); + } + mBase = 0; + mSize = 0; + close(fd); + } +} + +int MemoryHeapBase::getHeapID() const { + return mFD; +} + +void* MemoryHeapBase::getBase() const { + return mBase; +} + +size_t MemoryHeapBase::getSize() const { + return mSize; +} + +uint32_t MemoryHeapBase::getFlags() const { + return mFlags; +} + +const char* MemoryHeapBase::getDevice() const { + return mDevice; +} + +// --------------------------------------------------------------------------- +}; // namespace android diff --git a/libs/utils/MemoryHeapPmem.cpp b/libs/utils/MemoryHeapPmem.cpp new file mode 100644 index 000000000..eba2b3055 --- /dev/null +++ b/libs/utils/MemoryHeapPmem.cpp @@ -0,0 +1,248 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "MemoryHeapPmem" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#if HAVE_ANDROID_OS +#include +#endif + +namespace android { + +// --------------------------------------------------------------------------- + +MemoryHeapPmem::MemoryPmem::MemoryPmem(const sp& heap) + : BnMemory(), mClientHeap(heap) +{ +} + +MemoryHeapPmem::MemoryPmem::~MemoryPmem() { + if (mClientHeap != NULL) { + mClientHeap->remove(this); + } +} + +// --------------------------------------------------------------------------- + +class SubRegionMemory : public MemoryHeapPmem::MemoryPmem { +public: + SubRegionMemory(const sp& heap, ssize_t offset, size_t size); + virtual ~SubRegionMemory(); + virtual sp getMemory(ssize_t* offset, size_t* size) const; +private: + friend class MemoryHeapPmem; + void revoke(); + size_t mSize; + ssize_t mOffset; +}; + +SubRegionMemory::SubRegionMemory(const sp& heap, + ssize_t offset, size_t size) + : MemoryHeapPmem::MemoryPmem(heap), mSize(size), mOffset(offset) +{ +#ifndef NDEBUG + void* const start_ptr = (void*)(intptr_t(getHeap()->base()) + offset); + memset(start_ptr, 0xda, size); +#endif + +#if HAVE_ANDROID_OS + if (size > 0) { + const size_t pagesize = getpagesize(); + size = (size + pagesize-1) & ~(pagesize-1); + int our_fd = heap->heapID(); + struct pmem_region sub = { offset, size }; + int err = ioctl(our_fd, PMEM_MAP, &sub); + LOGE_IF(err<0, "PMEM_MAP failed (%s), " + "mFD=%d, sub.offset=%lu, sub.size=%lu", + strerror(errno), our_fd, sub.offset, sub.len); +} +#endif +} + +sp SubRegionMemory::getMemory(ssize_t* offset, size_t* size) const +{ + if (offset) *offset = mOffset; + if (size) *size = mSize; + return getHeap(); +} + +SubRegionMemory::~SubRegionMemory() +{ + revoke(); +} + + +void SubRegionMemory::revoke() +{ + // NOTE: revoke() doesn't need to be protected by a lock because it + // can only be called from MemoryHeapPmem::revoke(), which means + // that we can't be in ~SubRegionMemory(), or in ~SubRegionMemory(), + // which means MemoryHeapPmem::revoke() wouldn't have been able to + // promote() it. + +#if HAVE_ANDROID_OS + if (mSize != NULL) { + const sp& heap(getHeap()); + int our_fd = heap->heapID(); + struct pmem_region sub; + sub.offset = mOffset; + sub.len = mSize; + int err = ioctl(our_fd, PMEM_UNMAP, &sub); + LOGE_IF(err<0, "PMEM_UNMAP failed (%s), " + "mFD=%d, sub.offset=%lu, sub.size=%lu", + strerror(errno), our_fd, sub.offset, sub.len); + mSize = 0; + } +#endif +} + +// --------------------------------------------------------------------------- + +MemoryHeapPmem::MemoryHeapPmem(const sp& pmemHeap, + uint32_t flags) + : HeapInterface(), MemoryHeapBase() +{ + char const * const device = pmemHeap->getDevice(); +#if HAVE_ANDROID_OS + if (device) { + int fd = open(device, O_RDWR); + LOGE_IF(fd<0, "couldn't open %s (%s)", device, strerror(errno)); + if (fd >= 0) { + int err = ioctl(fd, PMEM_CONNECT, pmemHeap->heapID()); + if (err < 0) { + LOGE("PMEM_CONNECT failed (%s), mFD=%d, sub-fd=%d", + strerror(errno), fd, pmemHeap->heapID()); + close(fd); + } else { + // everything went well... + mParentHeap = pmemHeap; + MemoryHeapBase::init(fd, + pmemHeap->getBase(), + pmemHeap->getSize(), + pmemHeap->getFlags() | flags, + device); + } + } + } +#else + mParentHeap = pmemHeap; + MemoryHeapBase::init( + dup(pmemHeap->heapID()), + pmemHeap->getBase(), + pmemHeap->getSize(), + pmemHeap->getFlags() | flags, + device); +#endif +} + +MemoryHeapPmem::~MemoryHeapPmem() +{ +} + +sp MemoryHeapPmem::mapMemory(size_t offset, size_t size) +{ + sp memory = createMemory(offset, size); + if (memory != 0) { + Mutex::Autolock _l(mLock); + mAllocations.add(memory); + } + return memory; +} + +sp MemoryHeapPmem::createMemory( + size_t offset, size_t size) +{ + sp memory; + if (heapID() > 0) + memory = new SubRegionMemory(this, offset, size); + return memory; +} + +status_t MemoryHeapPmem::slap() +{ +#if HAVE_ANDROID_OS + size_t size = getSize(); + const size_t pagesize = getpagesize(); + size = (size + pagesize-1) & ~(pagesize-1); + int our_fd = getHeapID(); + struct pmem_region sub = { 0, size }; + int err = ioctl(our_fd, PMEM_MAP, &sub); + LOGE_IF(err<0, "PMEM_MAP failed (%s), " + "mFD=%d, sub.offset=%lu, sub.size=%lu", + strerror(errno), our_fd, sub.offset, sub.len); + return -errno; +#else + return NO_ERROR; +#endif +} + +status_t MemoryHeapPmem::unslap() +{ +#if HAVE_ANDROID_OS + size_t size = getSize(); + const size_t pagesize = getpagesize(); + size = (size + pagesize-1) & ~(pagesize-1); + int our_fd = getHeapID(); + struct pmem_region sub = { 0, size }; + int err = ioctl(our_fd, PMEM_UNMAP, &sub); + LOGE_IF(err<0, "PMEM_UNMAP failed (%s), " + "mFD=%d, sub.offset=%lu, sub.size=%lu", + strerror(errno), our_fd, sub.offset, sub.len); + return -errno; +#else + return NO_ERROR; +#endif +} + +void MemoryHeapPmem::revoke() +{ + SortedVector< wp > allocations; + + { // scope for lock + Mutex::Autolock _l(mLock); + allocations = mAllocations; + } + + ssize_t count = allocations.size(); + for (ssize_t i=0 ; i memory(allocations[i].promote()); + if (memory != 0) + memory->revoke(); + } +} + +void MemoryHeapPmem::remove(const wp& memory) +{ + Mutex::Autolock _l(mLock); + mAllocations.remove(memory); +} + +// --------------------------------------------------------------------------- +}; // namespace android diff --git a/libs/utils/NOTICE b/libs/utils/NOTICE new file mode 100644 index 000000000..c5b1efa7a --- /dev/null +++ b/libs/utils/NOTICE @@ -0,0 +1,190 @@ + + Copyright (c) 2005-2008, The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + + 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. + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + diff --git a/libs/utils/Parcel.cpp b/libs/utils/Parcel.cpp new file mode 100644 index 000000000..0f4b64730 --- /dev/null +++ b/libs/utils/Parcel.cpp @@ -0,0 +1,1377 @@ +/* + * Copyright (C) 2005 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 "Parcel" +//#define LOG_NDEBUG 0 + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#ifndef INT32_MAX +#define INT32_MAX ((int32_t)(2147483647)) +#endif + +#define LOG_REFS(...) +//#define LOG_REFS(...) LOG(LOG_DEBUG, "Parcel", __VA_ARGS__) + +// --------------------------------------------------------------------------- + +#define PAD_SIZE(s) (((s)+3)&~3) + +// XXX This can be made public if we want to provide +// support for typed data. +struct small_flat_data +{ + uint32_t type; + uint32_t data; +}; + +namespace android { + +void acquire_object(const sp& proc, + const flat_binder_object& obj, const void* who) +{ + switch (obj.type) { + case BINDER_TYPE_BINDER: + if (obj.binder) { + LOG_REFS("Parcel %p acquiring reference on local %p", who, obj.cookie); + static_cast(obj.cookie)->incStrong(who); + } + return; + case BINDER_TYPE_WEAK_BINDER: + if (obj.binder) + static_cast(obj.binder)->incWeak(who); + return; + case BINDER_TYPE_HANDLE: { + const sp b = proc->getStrongProxyForHandle(obj.handle); + if (b != NULL) { + LOG_REFS("Parcel %p acquiring reference on remote %p", who, b.get()); + b->incStrong(who); + } + return; + } + case BINDER_TYPE_WEAK_HANDLE: { + const wp b = proc->getWeakProxyForHandle(obj.handle); + if (b != NULL) b.get_refs()->incWeak(who); + return; + } + case BINDER_TYPE_FD: { + // intentionally blank -- nothing to do to acquire this, but we do + // recognize it as a legitimate object type. + return; + } + } + + LOGD("Invalid object type 0x%08lx", obj.type); +} + +void release_object(const sp& proc, + const flat_binder_object& obj, const void* who) +{ + switch (obj.type) { + case BINDER_TYPE_BINDER: + if (obj.binder) { + LOG_REFS("Parcel %p releasing reference on local %p", who, obj.cookie); + static_cast(obj.cookie)->decStrong(who); + } + return; + case BINDER_TYPE_WEAK_BINDER: + if (obj.binder) + static_cast(obj.binder)->decWeak(who); + return; + case BINDER_TYPE_HANDLE: { + const sp b = proc->getStrongProxyForHandle(obj.handle); + if (b != NULL) { + LOG_REFS("Parcel %p releasing reference on remote %p", who, b.get()); + b->decStrong(who); + } + return; + } + case BINDER_TYPE_WEAK_HANDLE: { + const wp b = proc->getWeakProxyForHandle(obj.handle); + if (b != NULL) b.get_refs()->decWeak(who); + return; + } + case BINDER_TYPE_FD: { + if (obj.cookie != (void*)0) close(obj.handle); + return; + } + } + + LOGE("Invalid object type 0x%08lx", obj.type); +} + +inline static status_t finish_flatten_binder( + const sp& binder, const flat_binder_object& flat, Parcel* out) +{ + return out->writeObject(flat, false); +} + +status_t flatten_binder(const sp& proc, + const sp& binder, Parcel* out) +{ + flat_binder_object obj; + + obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS; + if (binder != NULL) { + IBinder *local = binder->localBinder(); + if (!local) { + BpBinder *proxy = binder->remoteBinder(); + if (proxy == NULL) { + LOGE("null proxy"); + } + const int32_t handle = proxy ? proxy->handle() : 0; + obj.type = BINDER_TYPE_HANDLE; + obj.handle = handle; + obj.cookie = NULL; + } else { + obj.type = BINDER_TYPE_BINDER; + obj.binder = local->getWeakRefs(); + obj.cookie = local; + } + } else { + obj.type = BINDER_TYPE_BINDER; + obj.binder = NULL; + obj.cookie = NULL; + } + + return finish_flatten_binder(binder, obj, out); +} + +status_t flatten_binder(const sp& proc, + const wp& binder, Parcel* out) +{ + flat_binder_object obj; + + obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS; + if (binder != NULL) { + sp real = binder.promote(); + if (real != NULL) { + IBinder *local = real->localBinder(); + if (!local) { + BpBinder *proxy = real->remoteBinder(); + if (proxy == NULL) { + LOGE("null proxy"); + } + const int32_t handle = proxy ? proxy->handle() : 0; + obj.type = BINDER_TYPE_WEAK_HANDLE; + obj.handle = handle; + obj.cookie = NULL; + } else { + obj.type = BINDER_TYPE_WEAK_BINDER; + obj.binder = binder.get_refs(); + obj.cookie = binder.unsafe_get(); + } + return finish_flatten_binder(real, obj, out); + } + + // XXX How to deal? In order to flatten the given binder, + // we need to probe it for information, which requires a primary + // reference... but we don't have one. + // + // The OpenBinder implementation uses a dynamic_cast<> here, + // but we can't do that with the different reference counting + // implementation we are using. + LOGE("Unable to unflatten Binder weak reference!"); + obj.type = BINDER_TYPE_BINDER; + obj.binder = NULL; + obj.cookie = NULL; + return finish_flatten_binder(NULL, obj, out); + + } else { + obj.type = BINDER_TYPE_BINDER; + obj.binder = NULL; + obj.cookie = NULL; + return finish_flatten_binder(NULL, obj, out); + } +} + +inline static status_t finish_unflatten_binder( + BpBinder* proxy, const flat_binder_object& flat, const Parcel& in) +{ + return NO_ERROR; +} + +status_t unflatten_binder(const sp& proc, + const Parcel& in, sp* out) +{ + const flat_binder_object* flat = in.readObject(false); + + if (flat) { + switch (flat->type) { + case BINDER_TYPE_BINDER: + *out = static_cast(flat->cookie); + return finish_unflatten_binder(NULL, *flat, in); + case BINDER_TYPE_HANDLE: + *out = proc->getStrongProxyForHandle(flat->handle); + return finish_unflatten_binder( + static_cast(out->get()), *flat, in); + } + } + return BAD_TYPE; +} + +status_t unflatten_binder(const sp& proc, + const Parcel& in, wp* out) +{ + const flat_binder_object* flat = in.readObject(false); + + if (flat) { + switch (flat->type) { + case BINDER_TYPE_BINDER: + *out = static_cast(flat->cookie); + return finish_unflatten_binder(NULL, *flat, in); + case BINDER_TYPE_WEAK_BINDER: + if (flat->binder != NULL) { + out->set_object_and_refs( + static_cast(flat->cookie), + static_cast(flat->binder)); + } else { + *out = NULL; + } + return finish_unflatten_binder(NULL, *flat, in); + case BINDER_TYPE_HANDLE: + case BINDER_TYPE_WEAK_HANDLE: + *out = proc->getWeakProxyForHandle(flat->handle); + return finish_unflatten_binder( + static_cast(out->unsafe_get()), *flat, in); + } + } + return BAD_TYPE; +} + +// --------------------------------------------------------------------------- + +Parcel::Parcel() +{ + initState(); +} + +Parcel::~Parcel() +{ + freeDataNoInit(); +} + +const uint8_t* Parcel::data() const +{ + return mData; +} + +size_t Parcel::dataSize() const +{ + return (mDataSize > mDataPos ? mDataSize : mDataPos); +} + +size_t Parcel::dataAvail() const +{ + // TODO: decide what to do about the possibility that this can + // report an available-data size that exceeds a Java int's max + // positive value, causing havoc. Fortunately this will only + // happen if someone constructs a Parcel containing more than two + // gigabytes of data, which on typical phone hardware is simply + // not possible. + return dataSize() - dataPosition(); +} + +size_t Parcel::dataPosition() const +{ + return mDataPos; +} + +size_t Parcel::dataCapacity() const +{ + return mDataCapacity; +} + +status_t Parcel::setDataSize(size_t size) +{ + status_t err; + err = continueWrite(size); + if (err == NO_ERROR) { + mDataSize = size; + LOGV("setDataSize Setting data size of %p to %d\n", this, mDataSize); + } + return err; +} + +void Parcel::setDataPosition(size_t pos) const +{ + mDataPos = pos; + mNextObjectHint = 0; +} + +status_t Parcel::setDataCapacity(size_t size) +{ + if (size > mDataSize) return continueWrite(size); + return NO_ERROR; +} + +status_t Parcel::setData(const uint8_t* buffer, size_t len) +{ + status_t err = restartWrite(len); + if (err == NO_ERROR) { + memcpy(const_cast(data()), buffer, len); + mDataSize = len; + mFdsKnown = false; + } + return err; +} + +status_t Parcel::appendFrom(Parcel *parcel, size_t offset, size_t len) +{ + const sp proc(ProcessState::self()); + status_t err; + uint8_t *data = parcel->mData; + size_t *objects = parcel->mObjects; + size_t size = parcel->mObjectsSize; + int startPos = mDataPos; + int firstIndex = -1, lastIndex = -2; + + if (len == 0) { + return NO_ERROR; + } + + // range checks against the source parcel size + if ((offset > parcel->mDataSize) + || (len > parcel->mDataSize) + || (offset + len > parcel->mDataSize)) { + return BAD_VALUE; + } + + // Count objects in range + for (int i = 0; i < (int) size; i++) { + size_t off = objects[i]; + if ((off >= offset) && (off < offset + len)) { + if (firstIndex == -1) { + firstIndex = i; + } + lastIndex = i; + } + } + int numObjects = lastIndex - firstIndex + 1; + + // grow data + err = growData(len); + if (err != NO_ERROR) { + return err; + } + + // append data + memcpy(mData + mDataPos, data + offset, len); + mDataPos += len; + mDataSize += len; + + if (numObjects > 0) { + // grow objects + if (mObjectsCapacity < mObjectsSize + numObjects) { + int newSize = ((mObjectsSize + numObjects)*3)/2; + size_t *objects = + (size_t*)realloc(mObjects, newSize*sizeof(size_t)); + if (objects == (size_t*)0) { + return NO_MEMORY; + } + mObjects = objects; + mObjectsCapacity = newSize; + } + + // append and acquire objects + int idx = mObjectsSize; + for (int i = firstIndex; i <= lastIndex; i++) { + size_t off = objects[i] - offset + startPos; + mObjects[idx++] = off; + mObjectsSize++; + + const flat_binder_object* flat + = reinterpret_cast(mData + off); + acquire_object(proc, *flat, this); + + // take note if the object is a file descriptor + if (flat->type == BINDER_TYPE_FD) { + mHasFds = mFdsKnown = true; + } + } + } + + return NO_ERROR; +} + +bool Parcel::hasFileDescriptors() const +{ + if (!mFdsKnown) { + scanForFds(); + } + return mHasFds; +} + +status_t Parcel::writeInterfaceToken(const String16& interface) +{ + // currently the interface identification token is just its name as a string + return writeString16(interface); +} + +bool Parcel::enforceInterface(const String16& interface) const +{ + String16 str = readString16(); + if (str == interface) { + return true; + } else { + LOGW("**** enforceInterface() expected '%s' but read '%s'\n", + String8(interface).string(), String8(str).string()); + return false; + } +} + +const size_t* Parcel::objects() const +{ + return mObjects; +} + +size_t Parcel::objectsCount() const +{ + return mObjectsSize; +} + +status_t Parcel::errorCheck() const +{ + return mError; +} + +void Parcel::setError(status_t err) +{ + mError = err; +} + +status_t Parcel::finishWrite(size_t len) +{ + //printf("Finish write of %d\n", len); + mDataPos += len; + LOGV("finishWrite Setting data pos of %p to %d\n", this, mDataPos); + if (mDataPos > mDataSize) { + mDataSize = mDataPos; + LOGV("finishWrite Setting data size of %p to %d\n", this, mDataSize); + } + //printf("New pos=%d, size=%d\n", mDataPos, mDataSize); + return NO_ERROR; +} + +status_t Parcel::writeUnpadded(const void* data, size_t len) +{ + size_t end = mDataPos + len; + if (end < mDataPos) { + // integer overflow + return BAD_VALUE; + } + + if (end <= mDataCapacity) { +restart_write: + memcpy(mData+mDataPos, data, len); + return finishWrite(len); + } + + status_t err = growData(len); + if (err == NO_ERROR) goto restart_write; + return err; +} + +status_t Parcel::write(const void* data, size_t len) +{ + void* const d = writeInplace(len); + if (d) { + memcpy(d, data, len); + return NO_ERROR; + } + return mError; +} + +void* Parcel::writeInplace(size_t len) +{ + const size_t padded = PAD_SIZE(len); + + // sanity check for integer overflow + if (mDataPos+padded < mDataPos) { + return NULL; + } + + if ((mDataPos+padded) <= mDataCapacity) { +restart_write: + //printf("Writing %ld bytes, padded to %ld\n", len, padded); + uint8_t* const data = mData+mDataPos; + + // Need to pad at end? + if (padded != len) { +#if BYTE_ORDER == BIG_ENDIAN + static const uint32_t mask[4] = { + 0x00000000, 0xffffff00, 0xffff0000, 0xff000000 + }; +#endif +#if BYTE_ORDER == LITTLE_ENDIAN + static const uint32_t mask[4] = { + 0x00000000, 0x00ffffff, 0x0000ffff, 0x000000ff + }; +#endif + //printf("Applying pad mask: %p to %p\n", (void*)mask[padded-len], + // *reinterpret_cast(data+padded-4)); + *reinterpret_cast(data+padded-4) &= mask[padded-len]; + } + + finishWrite(padded); + return data; + } + + status_t err = growData(padded); + if (err == NO_ERROR) goto restart_write; + return NULL; +} + +status_t Parcel::writeInt32(int32_t val) +{ + if ((mDataPos+sizeof(val)) <= mDataCapacity) { +restart_write: + *reinterpret_cast(mData+mDataPos) = val; + return finishWrite(sizeof(val)); + } + + status_t err = growData(sizeof(val)); + if (err == NO_ERROR) goto restart_write; + return err; +} + +status_t Parcel::writeInt64(int64_t val) +{ + if ((mDataPos+sizeof(val)) <= mDataCapacity) { +restart_write: + *reinterpret_cast(mData+mDataPos) = val; + return finishWrite(sizeof(val)); + } + + status_t err = growData(sizeof(val)); + if (err == NO_ERROR) goto restart_write; + return err; +} + +status_t Parcel::writeFloat(float val) +{ + if ((mDataPos+sizeof(val)) <= mDataCapacity) { +restart_write: + *reinterpret_cast(mData+mDataPos) = val; + return finishWrite(sizeof(val)); + } + + status_t err = growData(sizeof(val)); + if (err == NO_ERROR) goto restart_write; + return err; +} + +status_t Parcel::writeDouble(double val) +{ + if ((mDataPos+sizeof(val)) <= mDataCapacity) { +restart_write: + *reinterpret_cast(mData+mDataPos) = val; + return finishWrite(sizeof(val)); + } + + status_t err = growData(sizeof(val)); + if (err == NO_ERROR) goto restart_write; + return err; +} + +status_t Parcel::writeCString(const char* str) +{ + return write(str, strlen(str)+1); +} + +status_t Parcel::writeString8(const String8& str) +{ + status_t err = writeInt32(str.bytes()); + if (err == NO_ERROR) { + err = write(str.string(), str.bytes()+1); + } + return err; +} + +status_t Parcel::writeString16(const String16& str) +{ + return writeString16(str.string(), str.size()); +} + +status_t Parcel::writeString16(const char16_t* str, size_t len) +{ + if (str == NULL) return writeInt32(-1); + + status_t err = writeInt32(len); + if (err == NO_ERROR) { + len *= sizeof(char16_t); + uint8_t* data = (uint8_t*)writeInplace(len+sizeof(char16_t)); + if (data) { + memcpy(data, str, len); + *reinterpret_cast(data+len) = 0; + return NO_ERROR; + } + err = mError; + } + return err; +} + +status_t Parcel::writeStrongBinder(const sp& val) +{ + return flatten_binder(ProcessState::self(), val, this); +} + +status_t Parcel::writeWeakBinder(const wp& val) +{ + return flatten_binder(ProcessState::self(), val, this); +} + +status_t Parcel::writeNativeHandle(const native_handle& handle) +{ + if (handle.version != sizeof(native_handle)) + return BAD_TYPE; + + status_t err; + err = writeInt32(handle.numFds); + if (err != NO_ERROR) return err; + + err = writeInt32(handle.numInts); + if (err != NO_ERROR) return err; + + for (int i=0 ; err==NO_ERROR && i(mData+mDataPos) = val; + + // Need to write meta-data? + if (nullMetaData || val.binder != NULL) { + mObjects[mObjectsSize] = mDataPos; + acquire_object(ProcessState::self(), val, this); + mObjectsSize++; + } + + // remember if it's a file descriptor + if (val.type == BINDER_TYPE_FD) { + mHasFds = mFdsKnown = true; + } + + return finishWrite(sizeof(flat_binder_object)); + } + + if (!enoughData) { + const status_t err = growData(sizeof(val)); + if (err != NO_ERROR) return err; + } + if (!enoughObjects) { + size_t newSize = ((mObjectsSize+2)*3)/2; + size_t* objects = (size_t*)realloc(mObjects, newSize*sizeof(size_t)); + if (objects == NULL) return NO_MEMORY; + mObjects = objects; + mObjectsCapacity = newSize; + } + + goto restart_write; +} + + +void Parcel::remove(size_t start, size_t amt) +{ + LOG_ALWAYS_FATAL("Parcel::remove() not yet implemented!"); +} + +status_t Parcel::read(void* outData, size_t len) const +{ + if ((mDataPos+PAD_SIZE(len)) >= mDataPos && (mDataPos+PAD_SIZE(len)) <= mDataSize) { + memcpy(outData, mData+mDataPos, len); + mDataPos += PAD_SIZE(len); + LOGV("read Setting data pos of %p to %d\n", this, mDataPos); + return NO_ERROR; + } + return NOT_ENOUGH_DATA; +} + +const void* Parcel::readInplace(size_t len) const +{ + if ((mDataPos+PAD_SIZE(len)) >= mDataPos && (mDataPos+PAD_SIZE(len)) <= mDataSize) { + const void* data = mData+mDataPos; + mDataPos += PAD_SIZE(len); + LOGV("readInplace Setting data pos of %p to %d\n", this, mDataPos); + return data; + } + return NULL; +} + +status_t Parcel::readInt32(int32_t *pArg) const +{ + if ((mDataPos+sizeof(int32_t)) <= mDataSize) { + const void* data = mData+mDataPos; + mDataPos += sizeof(int32_t); + *pArg = *reinterpret_cast(data); + return NO_ERROR; + } else { + return NOT_ENOUGH_DATA; + } +} + +int32_t Parcel::readInt32() const +{ + if ((mDataPos+sizeof(int32_t)) <= mDataSize) { + const void* data = mData+mDataPos; + mDataPos += sizeof(int32_t); + LOGV("readInt32 Setting data pos of %p to %d\n", this, mDataPos); + return *reinterpret_cast(data); + } + return 0; +} + + +status_t Parcel::readInt64(int64_t *pArg) const +{ + if ((mDataPos+sizeof(int64_t)) <= mDataSize) { + const void* data = mData+mDataPos; + mDataPos += sizeof(int64_t); + *pArg = *reinterpret_cast(data); + LOGV("readInt64 Setting data pos of %p to %d\n", this, mDataPos); + return NO_ERROR; + } else { + return NOT_ENOUGH_DATA; + } +} + + +int64_t Parcel::readInt64() const +{ + if ((mDataPos+sizeof(int64_t)) <= mDataSize) { + const void* data = mData+mDataPos; + mDataPos += sizeof(int64_t); + LOGV("readInt64 Setting data pos of %p to %d\n", this, mDataPos); + return *reinterpret_cast(data); + } + return 0; +} + +status_t Parcel::readFloat(float *pArg) const +{ + if ((mDataPos+sizeof(float)) <= mDataSize) { + const void* data = mData+mDataPos; + mDataPos += sizeof(float); + LOGV("readFloat Setting data pos of %p to %d\n", this, mDataPos); + *pArg = *reinterpret_cast(data); + return NO_ERROR; + } else { + return NOT_ENOUGH_DATA; + } +} + + +float Parcel::readFloat() const +{ + if ((mDataPos+sizeof(float)) <= mDataSize) { + const void* data = mData+mDataPos; + mDataPos += sizeof(float); + LOGV("readFloat Setting data pos of %p to %d\n", this, mDataPos); + return *reinterpret_cast(data); + } + return 0; +} + +status_t Parcel::readDouble(double *pArg) const +{ + if ((mDataPos+sizeof(double)) <= mDataSize) { + const void* data = mData+mDataPos; + mDataPos += sizeof(double); + LOGV("readDouble Setting data pos of %p to %d\n", this, mDataPos); + *pArg = *reinterpret_cast(data); + return NO_ERROR; + } else { + return NOT_ENOUGH_DATA; + } +} + + +double Parcel::readDouble() const +{ + if ((mDataPos+sizeof(double)) <= mDataSize) { + const void* data = mData+mDataPos; + mDataPos += sizeof(double); + LOGV("readDouble Setting data pos of %p to %d\n", this, mDataPos); + return *reinterpret_cast(data); + } + return 0; +} + + +const char* Parcel::readCString() const +{ + const size_t avail = mDataSize-mDataPos; + if (avail > 0) { + const char* str = reinterpret_cast(mData+mDataPos); + // is the string's trailing NUL within the parcel's valid bounds? + const char* eos = reinterpret_cast(memchr(str, 0, avail)); + if (eos) { + const size_t len = eos - str; + mDataPos += PAD_SIZE(len+1); + LOGV("readCString Setting data pos of %p to %d\n", this, mDataPos); + return str; + } + } + return NULL; +} + +String8 Parcel::readString8() const +{ + int32_t size = readInt32(); + // watch for potential int overflow adding 1 for trailing NUL + if (size > 0 && size < INT32_MAX) { + const char* str = (const char*)readInplace(size+1); + if (str) return String8(str, size); + } + return String8(); +} + +String16 Parcel::readString16() const +{ + size_t len; + const char16_t* str = readString16Inplace(&len); + if (str) return String16(str, len); + LOGE("Reading a NULL string not supported here."); + return String16(); +} + +const char16_t* Parcel::readString16Inplace(size_t* outLen) const +{ + int32_t size = readInt32(); + // watch for potential int overflow from size+1 + if (size >= 0 && size < INT32_MAX) { + *outLen = size; + const char16_t* str = (const char16_t*)readInplace((size+1)*sizeof(char16_t)); + if (str != NULL) { + return str; + } + } + *outLen = 0; + return NULL; +} + +sp Parcel::readStrongBinder() const +{ + sp val; + unflatten_binder(ProcessState::self(), *this, &val); + return val; +} + +wp Parcel::readWeakBinder() const +{ + wp val; + unflatten_binder(ProcessState::self(), *this, &val); + return val; +} + + +native_handle* Parcel::readNativeHandle(native_handle* (*alloc)(void*, int, int), void* cookie) const +{ + int numFds, numInts; + status_t err; + err = readInt32(&numFds); + if (err != NO_ERROR) return 0; + err = readInt32(&numInts); + if (err != NO_ERROR) return 0; + + native_handle* h; + if (alloc == 0) { + size_t size = sizeof(native_handle) + sizeof(int)*(numFds + numInts); + h = (native_handle*)malloc(size); + h->version = sizeof(native_handle); + h->numFds = numFds; + h->numInts = numInts; + } else { + h = alloc(cookie, numFds, numInts); + if (h->version != sizeof(native_handle)) { + return 0; + } + } + + for (int i=0 ; err==NO_ERROR && idata[i] = dup(readFileDescriptor()); + if (h->data[i] < 0) err = BAD_VALUE; + } + + err = read(h->data + numFds, sizeof(int)*numInts); + + if (err != NO_ERROR) { + if (alloc == 0) { + free(h); + } + h = 0; + } + return h; +} + + +int Parcel::readFileDescriptor() const +{ + const flat_binder_object* flat = readObject(true); + if (flat) { + switch (flat->type) { + case BINDER_TYPE_FD: + //LOGI("Returning file descriptor %ld from parcel %p\n", flat->handle, this); + return flat->handle; + } + } + return BAD_TYPE; +} + +const flat_binder_object* Parcel::readObject(bool nullMetaData) const +{ + const size_t DPOS = mDataPos; + if ((DPOS+sizeof(flat_binder_object)) <= mDataSize) { + const flat_binder_object* obj + = reinterpret_cast(mData+DPOS); + mDataPos = DPOS + sizeof(flat_binder_object); + if (!nullMetaData && (obj->cookie == NULL && obj->binder == NULL)) { + // When transferring a NULL object, we don't write it into + // the object list, so we don't want to check for it when + // reading. + LOGV("readObject Setting data pos of %p to %d\n", this, mDataPos); + return obj; + } + + // Ensure that this object is valid... + size_t* const OBJS = mObjects; + const size_t N = mObjectsSize; + size_t opos = mNextObjectHint; + + if (N > 0) { + LOGV("Parcel %p looking for obj at %d, hint=%d\n", + this, DPOS, opos); + + // Start at the current hint position, looking for an object at + // the current data position. + if (opos < N) { + while (opos < (N-1) && OBJS[opos] < DPOS) { + opos++; + } + } else { + opos = N-1; + } + if (OBJS[opos] == DPOS) { + // Found it! + LOGV("Parcel found obj %d at index %d with forward search", + this, DPOS, opos); + mNextObjectHint = opos+1; + LOGV("readObject Setting data pos of %p to %d\n", this, mDataPos); + return obj; + } + + // Look backwards for it... + while (opos > 0 && OBJS[opos] > DPOS) { + opos--; + } + if (OBJS[opos] == DPOS) { + // Found it! + LOGV("Parcel found obj %d at index %d with backward search", + this, DPOS, opos); + mNextObjectHint = opos+1; + LOGV("readObject Setting data pos of %p to %d\n", this, mDataPos); + return obj; + } + } + LOGW("Attempt to read object from Parcel %p at offset %d that is not in the object list", + this, DPOS); + } + return NULL; +} + +void Parcel::closeFileDescriptors() +{ + size_t i = mObjectsSize; + if (i > 0) { + //LOGI("Closing file descriptors for %d objects...", mObjectsSize); + } + while (i > 0) { + i--; + const flat_binder_object* flat + = reinterpret_cast(mData+mObjects[i]); + if (flat->type == BINDER_TYPE_FD) { + //LOGI("Closing fd: %ld\n", flat->handle); + close(flat->handle); + } + } +} + +const uint8_t* Parcel::ipcData() const +{ + return mData; +} + +size_t Parcel::ipcDataSize() const +{ + return (mDataSize > mDataPos ? mDataSize : mDataPos); +} + +const size_t* Parcel::ipcObjects() const +{ + return mObjects; +} + +size_t Parcel::ipcObjectsCount() const +{ + return mObjectsSize; +} + +void Parcel::ipcSetDataReference(const uint8_t* data, size_t dataSize, + const size_t* objects, size_t objectsCount, release_func relFunc, void* relCookie) +{ + freeDataNoInit(); + mError = NO_ERROR; + mData = const_cast(data); + mDataSize = mDataCapacity = dataSize; + //LOGI("setDataReference Setting data size of %p to %lu (pid=%d)\n", this, mDataSize, getpid()); + mDataPos = 0; + LOGV("setDataReference Setting data pos of %p to %d\n", this, mDataPos); + mObjects = const_cast(objects); + mObjectsSize = mObjectsCapacity = objectsCount; + mNextObjectHint = 0; + mOwner = relFunc; + mOwnerCookie = relCookie; + scanForFds(); +} + +void Parcel::print(TextOutput& to, uint32_t flags) const +{ + to << "Parcel("; + + if (errorCheck() != NO_ERROR) { + const status_t err = errorCheck(); + to << "Error: " << (void*)err << " \"" << strerror(-err) << "\""; + } else if (dataSize() > 0) { + const uint8_t* DATA = data(); + to << indent << HexDump(DATA, dataSize()) << dedent; + const size_t* OBJS = objects(); + const size_t N = objectsCount(); + for (size_t i=0; i(DATA+OBJS[i]); + to << endl << "Object #" << i << " @ " << (void*)OBJS[i] << ": " + << TypeCode(flat->type & 0x7f7f7f00) + << " = " << flat->binder; + } + } else { + to << "NULL"; + } + + to << ")"; +} + +void Parcel::releaseObjects() +{ + const sp proc(ProcessState::self()); + size_t i = mObjectsSize; + uint8_t* const data = mData; + size_t* const objects = mObjects; + while (i > 0) { + i--; + const flat_binder_object* flat + = reinterpret_cast(data+objects[i]); + release_object(proc, *flat, this); + } +} + +void Parcel::acquireObjects() +{ + const sp proc(ProcessState::self()); + size_t i = mObjectsSize; + uint8_t* const data = mData; + size_t* const objects = mObjects; + while (i > 0) { + i--; + const flat_binder_object* flat + = reinterpret_cast(data+objects[i]); + acquire_object(proc, *flat, this); + } +} + +void Parcel::freeData() +{ + freeDataNoInit(); + initState(); +} + +void Parcel::freeDataNoInit() +{ + if (mOwner) { + //LOGI("Freeing data ref of %p (pid=%d)\n", this, getpid()); + mOwner(this, mData, mDataSize, mObjects, mObjectsSize, mOwnerCookie); + } else { + releaseObjects(); + if (mData) free(mData); + if (mObjects) free(mObjects); + } +} + +status_t Parcel::growData(size_t len) +{ + size_t newSize = ((mDataSize+len)*3)/2; + return (newSize <= mDataSize) + ? (status_t) NO_MEMORY + : continueWrite(newSize); +} + +status_t Parcel::restartWrite(size_t desired) +{ + if (mOwner) { + freeData(); + return continueWrite(desired); + } + + uint8_t* data = (uint8_t*)realloc(mData, desired); + if (!data && desired > mDataCapacity) { + mError = NO_MEMORY; + return NO_MEMORY; + } + + releaseObjects(); + + if (data) { + mData = data; + mDataCapacity = desired; + } + + mDataSize = mDataPos = 0; + LOGV("restartWrite Setting data size of %p to %d\n", this, mDataSize); + LOGV("restartWrite Setting data pos of %p to %d\n", this, mDataPos); + + free(mObjects); + mObjects = NULL; + mObjectsSize = mObjectsCapacity = 0; + mNextObjectHint = 0; + mHasFds = false; + mFdsKnown = true; + + return NO_ERROR; +} + +status_t Parcel::continueWrite(size_t desired) +{ + // If shrinking, first adjust for any objects that appear + // after the new data size. + size_t objectsSize = mObjectsSize; + if (desired < mDataSize) { + if (desired == 0) { + objectsSize = 0; + } else { + while (objectsSize > 0) { + if (mObjects[objectsSize-1] < desired) + break; + objectsSize--; + } + } + } + + if (mOwner) { + // If the size is going to zero, just release the owner's data. + if (desired == 0) { + freeData(); + return NO_ERROR; + } + + // If there is a different owner, we need to take + // posession. + uint8_t* data = (uint8_t*)malloc(desired); + if (!data) { + mError = NO_MEMORY; + return NO_MEMORY; + } + size_t* objects = NULL; + + if (objectsSize) { + objects = (size_t*)malloc(objectsSize*sizeof(size_t)); + if (!objects) { + mError = NO_MEMORY; + return NO_MEMORY; + } + + // Little hack to only acquire references on objects + // we will be keeping. + size_t oldObjectsSize = mObjectsSize; + mObjectsSize = objectsSize; + acquireObjects(); + mObjectsSize = oldObjectsSize; + } + + if (mData) { + memcpy(data, mData, mDataSize < desired ? mDataSize : desired); + } + if (objects && mObjects) { + memcpy(objects, mObjects, objectsSize*sizeof(size_t)); + } + //LOGI("Freeing data ref of %p (pid=%d)\n", this, getpid()); + mOwner(this, mData, mDataSize, mObjects, mObjectsSize, mOwnerCookie); + mOwner = NULL; + + mData = data; + mObjects = objects; + mDataSize = (mDataSize < desired) ? mDataSize : desired; + LOGV("continueWrite Setting data size of %p to %d\n", this, mDataSize); + mDataCapacity = desired; + mObjectsSize = mObjectsCapacity = objectsSize; + mNextObjectHint = 0; + + } else if (mData) { + if (objectsSize < mObjectsSize) { + // Need to release refs on any objects we are dropping. + const sp proc(ProcessState::self()); + for (size_t i=objectsSize; i(mData+mObjects[i]); + if (flat->type == BINDER_TYPE_FD) { + // will need to rescan because we may have lopped off the only FDs + mFdsKnown = false; + } + release_object(proc, *flat, this); + } + size_t* objects = + (size_t*)realloc(mObjects, objectsSize*sizeof(size_t)); + if (objects) { + mObjects = objects; + } + mObjectsSize = objectsSize; + mNextObjectHint = 0; + } + + // We own the data, so we can just do a realloc(). + if (desired > mDataCapacity) { + uint8_t* data = (uint8_t*)realloc(mData, desired); + if (data) { + mData = data; + mDataCapacity = desired; + } else if (desired > mDataCapacity) { + mError = NO_MEMORY; + return NO_MEMORY; + } + } else { + mDataSize = desired; + LOGV("continueWrite Setting data size of %p to %d\n", this, mDataSize); + if (mDataPos > desired) { + mDataPos = desired; + LOGV("continueWrite Setting data pos of %p to %d\n", this, mDataPos); + } + } + + } else { + // This is the first data. Easy! + uint8_t* data = (uint8_t*)malloc(desired); + if (!data) { + mError = NO_MEMORY; + return NO_MEMORY; + } + + if(!(mDataCapacity == 0 && mObjects == NULL + && mObjectsCapacity == 0)) { + LOGE("continueWrite: %d/%p/%d/%d", mDataCapacity, mObjects, mObjectsCapacity, desired); + } + + mData = data; + mDataSize = mDataPos = 0; + LOGV("continueWrite Setting data size of %p to %d\n", this, mDataSize); + LOGV("continueWrite Setting data pos of %p to %d\n", this, mDataPos); + mDataCapacity = desired; + } + + return NO_ERROR; +} + +void Parcel::initState() +{ + mError = NO_ERROR; + mData = 0; + mDataSize = 0; + mDataCapacity = 0; + mDataPos = 0; + LOGV("initState Setting data size of %p to %d\n", this, mDataSize); + LOGV("initState Setting data pos of %p to %d\n", this, mDataPos); + mObjects = NULL; + mObjectsSize = 0; + mObjectsCapacity = 0; + mNextObjectHint = 0; + mHasFds = false; + mFdsKnown = true; + mOwner = NULL; +} + +void Parcel::scanForFds() const +{ + bool hasFds = false; + for (size_t i=0; i(mData + mObjects[i]); + if (flat->type == BINDER_TYPE_FD) { + hasFds = true; + break; + } + } + mHasFds = hasFds; + mFdsKnown = true; +} + +}; // namespace android diff --git a/libs/utils/Pipe.cpp b/libs/utils/Pipe.cpp new file mode 100644 index 000000000..613906bed --- /dev/null +++ b/libs/utils/Pipe.cpp @@ -0,0 +1,465 @@ +/* + * Copyright (C) 2005 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. + */ + +// +// Unidirectional pipe. +// + +#include +#include + +#if defined(HAVE_WIN32_IPC) +# include +#else +# include +# include +# include +#endif + +#include +#include +#include +#include + +using namespace android; + +const unsigned long kInvalidHandle = (unsigned long) -1; + + +/* + * Constructor. Do little. + */ +Pipe::Pipe(void) + : mReadNonBlocking(false), mReadHandle(kInvalidHandle), + mWriteHandle(kInvalidHandle) +{ +} + +/* + * Destructor. Use the system-appropriate close call. + */ +Pipe::~Pipe(void) +{ +#if defined(HAVE_WIN32_IPC) + if (mReadHandle != kInvalidHandle) { + if (!CloseHandle((HANDLE)mReadHandle)) + LOG(LOG_WARN, "pipe", "failed closing read handle (%ld)\n", + mReadHandle); + } + if (mWriteHandle != kInvalidHandle) { + FlushFileBuffers((HANDLE)mWriteHandle); + if (!CloseHandle((HANDLE)mWriteHandle)) + LOG(LOG_WARN, "pipe", "failed closing write handle (%ld)\n", + mWriteHandle); + } +#else + if (mReadHandle != kInvalidHandle) { + if (close((int) mReadHandle) != 0) + LOG(LOG_WARN, "pipe", "failed closing read fd (%d)\n", + (int) mReadHandle); + } + if (mWriteHandle != kInvalidHandle) { + if (close((int) mWriteHandle) != 0) + LOG(LOG_WARN, "pipe", "failed closing write fd (%d)\n", + (int) mWriteHandle); + } +#endif +} + +/* + * Create the pipe. + * + * Use the POSIX stuff for everything but Windows. + */ +bool Pipe::create(void) +{ + assert(mReadHandle == kInvalidHandle); + assert(mWriteHandle == kInvalidHandle); + +#if defined(HAVE_WIN32_IPC) + /* we use this across processes, so they need to be inheritable */ + HANDLE handles[2]; + SECURITY_ATTRIBUTES saAttr; + + saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); + saAttr.bInheritHandle = TRUE; + saAttr.lpSecurityDescriptor = NULL; + + if (!CreatePipe(&handles[0], &handles[1], &saAttr, 0)) { + LOG(LOG_ERROR, "pipe", "unable to create pipe\n"); + return false; + } + mReadHandle = (unsigned long) handles[0]; + mWriteHandle = (unsigned long) handles[1]; + return true; +#else + int fds[2]; + + if (pipe(fds) != 0) { + LOG(LOG_ERROR, "pipe", "unable to create pipe\n"); + return false; + } + mReadHandle = fds[0]; + mWriteHandle = fds[1]; + return true; +#endif +} + +/* + * Create a "half pipe". Please, no Segway riding. + */ +bool Pipe::createReader(unsigned long handle) +{ + mReadHandle = handle; + assert(mWriteHandle == kInvalidHandle); + return true; +} + +/* + * Create a "half pipe" for writing. + */ +bool Pipe::createWriter(unsigned long handle) +{ + mWriteHandle = handle; + assert(mReadHandle == kInvalidHandle); + return true; +} + +/* + * Return "true" if create() has been called successfully. + */ +bool Pipe::isCreated(void) +{ + // one or the other should be open + return (mReadHandle != kInvalidHandle || mWriteHandle != kInvalidHandle); +} + + +/* + * Read data from the pipe. + * + * For Linux and Darwin, just call read(). For Windows, implement + * non-blocking reads by calling PeekNamedPipe first. + */ +int Pipe::read(void* buf, int count) +{ + assert(mReadHandle != kInvalidHandle); + +#if defined(HAVE_WIN32_IPC) + DWORD totalBytesAvail = count; + DWORD bytesRead; + + if (mReadNonBlocking) { + // use PeekNamedPipe to adjust read count expectations + if (!PeekNamedPipe((HANDLE) mReadHandle, NULL, 0, NULL, + &totalBytesAvail, NULL)) + { + LOG(LOG_ERROR, "pipe", "PeekNamedPipe failed\n"); + return -1; + } + + if (totalBytesAvail == 0) + return 0; + } + + if (!ReadFile((HANDLE) mReadHandle, buf, totalBytesAvail, &bytesRead, + NULL)) + { + DWORD err = GetLastError(); + if (err == ERROR_HANDLE_EOF || err == ERROR_BROKEN_PIPE) + return 0; + LOG(LOG_ERROR, "pipe", "ReadFile failed (err=%ld)\n", err); + return -1; + } + + return (int) bytesRead; +#else + int cc; + cc = ::read(mReadHandle, buf, count); + if (cc < 0 && errno == EAGAIN) + return 0; + return cc; +#endif +} + +/* + * Write data to the pipe. + * + * POSIX systems are trivial, Windows uses a different call and doesn't + * handle non-blocking writes. + * + * If we add non-blocking support here, we probably want to make it an + * all-or-nothing write. + * + * DO NOT use LOG() here, we could be writing a log message. + */ +int Pipe::write(const void* buf, int count) +{ + assert(mWriteHandle != kInvalidHandle); + +#if defined(HAVE_WIN32_IPC) + DWORD bytesWritten; + + if (mWriteNonBlocking) { + // BUG: can't use PeekNamedPipe() to get the amount of space + // left. Looks like we need to use "overlapped I/O" functions. + // I just don't care that much. + } + + if (!WriteFile((HANDLE) mWriteHandle, buf, count, &bytesWritten, NULL)) { + // can't LOG, use stderr + fprintf(stderr, "WriteFile failed (err=%ld)\n", GetLastError()); + return -1; + } + + return (int) bytesWritten; +#else + int cc; + cc = ::write(mWriteHandle, buf, count); + if (cc < 0 && errno == EAGAIN) + return 0; + return cc; +#endif +} + +/* + * Figure out if there is data available on the read fd. + * + * We return "true" on error because we want the caller to try to read + * from the pipe. They'll notice the read failure and do something + * appropriate. + */ +bool Pipe::readReady(void) +{ + assert(mReadHandle != kInvalidHandle); + +#if defined(HAVE_WIN32_IPC) + DWORD totalBytesAvail; + + if (!PeekNamedPipe((HANDLE) mReadHandle, NULL, 0, NULL, + &totalBytesAvail, NULL)) + { + LOG(LOG_ERROR, "pipe", "PeekNamedPipe failed\n"); + return true; + } + + return (totalBytesAvail != 0); +#else + errno = 0; + fd_set readfds; + struct timeval tv = { 0, 0 }; + int cc; + + FD_ZERO(&readfds); + FD_SET(mReadHandle, &readfds); + + cc = select(mReadHandle+1, &readfds, NULL, NULL, &tv); + if (cc < 0) { + LOG(LOG_ERROR, "pipe", "select() failed\n"); + return true; + } else if (cc == 0) { + /* timed out, nothing available */ + return false; + } else if (cc == 1) { + /* our fd is ready */ + return true; + } else { + LOG(LOG_ERROR, "pipe", "HUH? select() returned > 1\n"); + return true; + } +#endif +} + +/* + * Enable or disable non-blocking mode for the read descriptor. + * + * NOTE: the calls succeed under Mac OS X, but the pipe doesn't appear to + * actually be in non-blocking mode. If this matters -- i.e. you're not + * using a select() call -- put a call to readReady() in front of the + * ::read() call, with a PIPE_NONBLOCK_BROKEN #ifdef in the Makefile for + * Darwin. + */ +bool Pipe::setReadNonBlocking(bool val) +{ + assert(mReadHandle != kInvalidHandle); + +#if defined(HAVE_WIN32_IPC) + // nothing to do +#else + int flags; + + if (fcntl(mReadHandle, F_GETFL, &flags) == -1) { + LOG(LOG_ERROR, "pipe", "couldn't get flags for pipe read fd\n"); + return false; + } + if (val) + flags |= O_NONBLOCK; + else + flags &= ~(O_NONBLOCK); + if (fcntl(mReadHandle, F_SETFL, &flags) == -1) { + LOG(LOG_ERROR, "pipe", "couldn't set flags for pipe read fd\n"); + return false; + } +#endif + + mReadNonBlocking = val; + return true; +} + +/* + * Enable or disable non-blocking mode for the write descriptor. + * + * As with setReadNonBlocking(), this does not work on the Mac. + */ +bool Pipe::setWriteNonBlocking(bool val) +{ + assert(mWriteHandle != kInvalidHandle); + +#if defined(HAVE_WIN32_IPC) + // nothing to do +#else + int flags; + + if (fcntl(mWriteHandle, F_GETFL, &flags) == -1) { + LOG(LOG_WARN, "pipe", + "Warning: couldn't get flags for pipe write fd (errno=%d)\n", + errno); + return false; + } + if (val) + flags |= O_NONBLOCK; + else + flags &= ~(O_NONBLOCK); + if (fcntl(mWriteHandle, F_SETFL, &flags) == -1) { + LOG(LOG_WARN, "pipe", + "Warning: couldn't set flags for pipe write fd (errno=%d)\n", + errno); + return false; + } +#endif + + mWriteNonBlocking = val; + return true; +} + +/* + * Specify whether a file descriptor can be inherited by a child process. + * Under Linux this means setting the close-on-exec flag, under Windows + * this is SetHandleInformation(HANDLE_FLAG_INHERIT). + */ +bool Pipe::disallowReadInherit(void) +{ + if (mReadHandle == kInvalidHandle) + return false; + +#if defined(HAVE_WIN32_IPC) + if (SetHandleInformation((HANDLE) mReadHandle, HANDLE_FLAG_INHERIT, 0) == 0) + return false; +#else + if (fcntl((int) mReadHandle, F_SETFD, FD_CLOEXEC) != 0) + return false; +#endif + return true; +} +bool Pipe::disallowWriteInherit(void) +{ + if (mWriteHandle == kInvalidHandle) + return false; + +#if defined(HAVE_WIN32_IPC) + if (SetHandleInformation((HANDLE) mWriteHandle, HANDLE_FLAG_INHERIT, 0) == 0) + return false; +#else + if (fcntl((int) mWriteHandle, F_SETFD, FD_CLOEXEC) != 0) + return false; +#endif + return true; +} + +/* + * Close read descriptor. + */ +bool Pipe::closeRead(void) +{ + if (mReadHandle == kInvalidHandle) + return false; + +#if defined(HAVE_WIN32_IPC) + if (mReadHandle != kInvalidHandle) { + if (!CloseHandle((HANDLE)mReadHandle)) { + LOG(LOG_WARN, "pipe", "failed closing read handle\n"); + return false; + } + } +#else + if (mReadHandle != kInvalidHandle) { + if (close((int) mReadHandle) != 0) { + LOG(LOG_WARN, "pipe", "failed closing read fd\n"); + return false; + } + } +#endif + mReadHandle = kInvalidHandle; + return true; +} + +/* + * Close write descriptor. + */ +bool Pipe::closeWrite(void) +{ + if (mWriteHandle == kInvalidHandle) + return false; + +#if defined(HAVE_WIN32_IPC) + if (mWriteHandle != kInvalidHandle) { + if (!CloseHandle((HANDLE)mWriteHandle)) { + LOG(LOG_WARN, "pipe", "failed closing write handle\n"); + return false; + } + } +#else + if (mWriteHandle != kInvalidHandle) { + if (close((int) mWriteHandle) != 0) { + LOG(LOG_WARN, "pipe", "failed closing write fd\n"); + return false; + } + } +#endif + mWriteHandle = kInvalidHandle; + return true; +} + +/* + * Get the read handle. + */ +unsigned long Pipe::getReadHandle(void) +{ + assert(mReadHandle != kInvalidHandle); + + return mReadHandle; +} + +/* + * Get the write handle. + */ +unsigned long Pipe::getWriteHandle(void) +{ + assert(mWriteHandle != kInvalidHandle); + + return mWriteHandle; +} + diff --git a/libs/utils/ProcessState.cpp b/libs/utils/ProcessState.cpp new file mode 100644 index 000000000..4567df60f --- /dev/null +++ b/libs/utils/ProcessState.cpp @@ -0,0 +1,398 @@ +/* + * Copyright (C) 2005 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 "ProcessState" + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#define BINDER_VM_SIZE (1*1024*1024) + +static bool gSingleProcess = false; + + +// --------------------------------------------------------------------------- + +namespace android { + +// Global variables +int mArgC; +const char* const* mArgV; +int mArgLen; + +class PoolThread : public Thread +{ +public: + PoolThread(bool isMain) + : mIsMain(isMain) + { + } + +protected: + virtual bool threadLoop() + { + IPCThreadState::self()->joinThreadPool(mIsMain); + return false; + } + + const bool mIsMain; +}; + +sp ProcessState::self() +{ + if (gProcess != NULL) return gProcess; + + AutoMutex _l(gProcessMutex); + if (gProcess == NULL) gProcess = new ProcessState; + return gProcess; +} + +void ProcessState::setSingleProcess(bool singleProcess) +{ + gSingleProcess = singleProcess; +} + + +void ProcessState::setContextObject(const sp& object) +{ + setContextObject(object, String16("default")); +} + +sp ProcessState::getContextObject(const sp& caller) +{ + if (supportsProcesses()) { + return getStrongProxyForHandle(0); + } else { + return getContextObject(String16("default"), caller); + } +} + +void ProcessState::setContextObject(const sp& object, const String16& name) +{ + AutoMutex _l(mLock); + mContexts.add(name, object); +} + +sp ProcessState::getContextObject(const String16& name, const sp& caller) +{ + mLock.lock(); + sp object( + mContexts.indexOfKey(name) >= 0 ? mContexts.valueFor(name) : NULL); + mLock.unlock(); + + //printf("Getting context object %s for %p\n", String8(name).string(), caller.get()); + + if (object != NULL) return object; + + // Don't attempt to retrieve contexts if we manage them + if (mManagesContexts) { + LOGE("getContextObject(%s) failed, but we manage the contexts!\n", + String8(name).string()); + return NULL; + } + + IPCThreadState* ipc = IPCThreadState::self(); + { + Parcel data, reply; + // no interface token on this magic transaction + data.writeString16(name); + data.writeStrongBinder(caller); + status_t result = ipc->transact(0 /*magic*/, 0, data, &reply, 0); + if (result == NO_ERROR) { + object = reply.readStrongBinder(); + } + } + + ipc->flushCommands(); + + if (object != NULL) setContextObject(object, name); + return object; +} + +bool ProcessState::supportsProcesses() const +{ + return mDriverFD >= 0; +} + +void ProcessState::startThreadPool() +{ + AutoMutex _l(mLock); + if (!mThreadPoolStarted) { + mThreadPoolStarted = true; + spawnPooledThread(true); + } +} + +bool ProcessState::isContextManager(void) const +{ + return mManagesContexts; +} + +bool ProcessState::becomeContextManager(context_check_func checkFunc, void* userData) +{ + if (!mManagesContexts) { + AutoMutex _l(mLock); + mBinderContextCheckFunc = checkFunc; + mBinderContextUserData = userData; + if (mDriverFD >= 0) { + int dummy = 0; +#if defined(HAVE_ANDROID_OS) + status_t result = ioctl(mDriverFD, BINDER_SET_CONTEXT_MGR, &dummy); +#else + status_t result = INVALID_OPERATION; +#endif + if (result == 0) { + mManagesContexts = true; + } else if (result == -1) { + mBinderContextCheckFunc = NULL; + mBinderContextUserData = NULL; + LOGE("Binder ioctl to become context manager failed: %s\n", strerror(errno)); + } + } else { + // If there is no driver, our only world is the local + // process so we can always become the context manager there. + mManagesContexts = true; + } + } + return mManagesContexts; +} + +ProcessState::handle_entry* ProcessState::lookupHandleLocked(int32_t handle) +{ + const size_t N=mHandleToObject.size(); + if (N <= (size_t)handle) { + handle_entry e; + e.binder = NULL; + e.refs = NULL; + status_t err = mHandleToObject.insertAt(e, N, handle+1-N); + if (err < NO_ERROR) return NULL; + } + return &mHandleToObject.editItemAt(handle); +} + +sp ProcessState::getStrongProxyForHandle(int32_t handle) +{ + sp result; + + AutoMutex _l(mLock); + + handle_entry* e = lookupHandleLocked(handle); + + if (e != NULL) { + // We need to create a new BpBinder if there isn't currently one, OR we + // are unable to acquire a weak reference on this current one. See comment + // in getWeakProxyForHandle() for more info about this. + IBinder* b = e->binder; + if (b == NULL || !e->refs->attemptIncWeak(this)) { + b = new BpBinder(handle); + e->binder = b; + if (b) e->refs = b->getWeakRefs(); + result = b; + } else { + // This little bit of nastyness is to allow us to add a primary + // reference to the remote proxy when this team doesn't have one + // but another team is sending the handle to us. + result.force_set(b); + e->refs->decWeak(this); + } + } + + return result; +} + +wp ProcessState::getWeakProxyForHandle(int32_t handle) +{ + wp result; + + AutoMutex _l(mLock); + + handle_entry* e = lookupHandleLocked(handle); + + if (e != NULL) { + // We need to create a new BpBinder if there isn't currently one, OR we + // are unable to acquire a weak reference on this current one. The + // attemptIncWeak() is safe because we know the BpBinder destructor will always + // call expungeHandle(), which acquires the same lock we are holding now. + // We need to do this because there is a race condition between someone + // releasing a reference on this BpBinder, and a new reference on its handle + // arriving from the driver. + IBinder* b = e->binder; + if (b == NULL || !e->refs->attemptIncWeak(this)) { + b = new BpBinder(handle); + result = b; + e->binder = b; + if (b) e->refs = b->getWeakRefs(); + } else { + result = b; + e->refs->decWeak(this); + } + } + + return result; +} + +void ProcessState::expungeHandle(int32_t handle, IBinder* binder) +{ + AutoMutex _l(mLock); + + handle_entry* e = lookupHandleLocked(handle); + + // This handle may have already been replaced with a new BpBinder + // (if someone failed the AttemptIncWeak() above); we don't want + // to overwrite it. + if (e && e->binder == binder) e->binder = NULL; +} + +void ProcessState::setArgs(int argc, const char* const argv[]) +{ + mArgC = argc; + mArgV = (const char **)argv; + + mArgLen = 0; + for (int i=0; i t = new PoolThread(isMain); + t->run(buf); + } +} + +static int open_driver() +{ + if (gSingleProcess) { + return -1; + } + + int fd = open("/dev/binder", O_RDWR); + if (fd >= 0) { + fcntl(fd, F_SETFD, FD_CLOEXEC); + int vers; +#if defined(HAVE_ANDROID_OS) + status_t result = ioctl(fd, BINDER_VERSION, &vers); +#else + status_t result = -1; + errno = EPERM; +#endif + if (result == -1) { + LOGE("Binder ioctl to obtain version failed: %s", strerror(errno)); + close(fd); + fd = -1; + } + if (result != 0 || vers != BINDER_CURRENT_PROTOCOL_VERSION) { + LOGE("Binder driver protocol does not match user space protocol!"); + close(fd); + fd = -1; + } +#if defined(HAVE_ANDROID_OS) + size_t maxThreads = 15; + result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads); + if (result == -1) { + LOGE("Binder ioctl to set max threads failed: %s", strerror(errno)); + } +#endif + + } else { + LOGW("Opening '/dev/binder' failed: %s\n", strerror(errno)); + } + return fd; +} + +ProcessState::ProcessState() + : mDriverFD(open_driver()) + , mVMStart(MAP_FAILED) + , mManagesContexts(false) + , mBinderContextCheckFunc(NULL) + , mBinderContextUserData(NULL) + , mThreadPoolStarted(false) + , mThreadPoolSeq(1) +{ + if (mDriverFD >= 0) { + // XXX Ideally, there should be a specific define for whether we + // have mmap (or whether we could possibly have the kernel module + // availabla). +#if !defined(HAVE_WIN32_IPC) + // mmap the binder, providing a chunk of virtual address space to receive transactions. + mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0); + if (mVMStart == MAP_FAILED) { + // *sigh* + LOGE("Using /dev/binder failed: unable to mmap transaction memory.\n"); + close(mDriverFD); + mDriverFD = -1; + } +#else + mDriverFD = -1; +#endif + } + if (mDriverFD < 0) { + // Need to run without the driver, starting our own thread pool. + } +} + +ProcessState::~ProcessState() +{ +} + +}; // namespace android diff --git a/libs/utils/README b/libs/utils/README new file mode 100644 index 000000000..36a706d5c --- /dev/null +++ b/libs/utils/README @@ -0,0 +1,14 @@ +Android Utility Function Library + +If you need a feature that is native to Linux but not present on other +platforms, construct a platform-dependent implementation that shares +the Linux interface. That way the actual device runs as "light" as +possible. + +If that isn't feasible, create a system-independent interface and hide +the details. + +The ultimate goal is *not* to create a super-duper platform abstraction +layer. The goal is to provide an optimized solution for Linux with +reasonable implementations for other platforms. + diff --git a/libs/utils/RefBase.cpp b/libs/utils/RefBase.cpp new file mode 100644 index 000000000..0bd1af4eb --- /dev/null +++ b/libs/utils/RefBase.cpp @@ -0,0 +1,534 @@ +/* + * Copyright (C) 2005 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 "RefBase" + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +// compile with refcounting debugging enabled +#define DEBUG_REFS 0 +#define DEBUG_REFS_ENABLED_BY_DEFAULT 1 +#define DEBUG_REFS_CALLSTACK_ENABLED 1 + +// log all reference counting operations +#define PRINT_REFS 0 + +// --------------------------------------------------------------------------- + +namespace android { + +#define INITIAL_STRONG_VALUE (1<<28) + +// --------------------------------------------------------------------------- + +class RefBase::weakref_impl : public RefBase::weakref_type +{ +public: + volatile int32_t mStrong; + volatile int32_t mWeak; + RefBase* const mBase; + volatile int32_t mFlags; + + +#if !DEBUG_REFS + + weakref_impl(RefBase* base) + : mStrong(INITIAL_STRONG_VALUE) + , mWeak(0) + , mBase(base) + , mFlags(0) + { + } + + void addStrongRef(const void* /*id*/) { } + void removeStrongRef(const void* /*id*/) { } + void addWeakRef(const void* /*id*/) { } + void removeWeakRef(const void* /*id*/) { } + void printRefs() const { } + void trackMe(bool, bool) { } + +#else + + weakref_impl(RefBase* base) + : mStrong(INITIAL_STRONG_VALUE) + , mWeak(0) + , mBase(base) + , mFlags(0) + , mStrongRefs(NULL) + , mWeakRefs(NULL) + , mTrackEnabled(!!DEBUG_REFS_ENABLED_BY_DEFAULT) + , mRetain(false) + { + //LOGI("NEW weakref_impl %p for RefBase %p", this, base); + } + + ~weakref_impl() + { + LOG_ALWAYS_FATAL_IF(!mRetain && mStrongRefs != NULL, "Strong references remain!"); + LOG_ALWAYS_FATAL_IF(!mRetain && mWeakRefs != NULL, "Weak references remain!"); + } + + void addStrongRef(const void* id) + { + addRef(&mStrongRefs, id, mStrong); + } + + void removeStrongRef(const void* id) + { + if (!mRetain) + removeRef(&mStrongRefs, id); + else + addRef(&mStrongRefs, id, -mStrong); + } + + void addWeakRef(const void* id) + { + addRef(&mWeakRefs, id, mWeak); + } + + void removeWeakRef(const void* id) + { + if (!mRetain) + removeRef(&mWeakRefs, id); + else + addRef(&mWeakRefs, id, -mWeak); + } + + void trackMe(bool track, bool retain) + { + mTrackEnabled = track; + mRetain = retain; + } + + void printRefs() const + { + String8 text; + + { + AutoMutex _l(const_cast(this)->mMutex); + + char buf[128]; + sprintf(buf, "Strong references on RefBase %p (weakref_type %p):\n", mBase, this); + text.append(buf); + printRefsLocked(&text, mStrongRefs); + sprintf(buf, "Weak references on RefBase %p (weakref_type %p):\n", mBase, this); + text.append(buf); + printRefsLocked(&text, mWeakRefs); + } + + { + char name[100]; + snprintf(name, 100, "/data/%p.stack", this); + int rc = open(name, O_RDWR | O_CREAT | O_APPEND); + if (rc >= 0) { + write(rc, text.string(), text.length()); + close(rc); + LOGD("STACK TRACE for %p saved in %s", this, name); + } + else LOGE("FAILED TO PRINT STACK TRACE for %p in %s: %s", this, + name, strerror(errno)); + } + } + +private: + struct ref_entry + { + ref_entry* next; + const void* id; +#if DEBUG_REFS_CALLSTACK_ENABLED + CallStack stack; +#endif + int32_t ref; + }; + + void addRef(ref_entry** refs, const void* id, int32_t mRef) + { + if (mTrackEnabled) { + AutoMutex _l(mMutex); + ref_entry* ref = new ref_entry; + // Reference count at the time of the snapshot, but before the + // update. Positive value means we increment, negative--we + // decrement the reference count. + ref->ref = mRef; + ref->id = id; +#if DEBUG_REFS_CALLSTACK_ENABLED + ref->stack.update(2); +#endif + + ref->next = *refs; + *refs = ref; + } + } + + void removeRef(ref_entry** refs, const void* id) + { + if (mTrackEnabled) { + AutoMutex _l(mMutex); + + ref_entry* ref = *refs; + while (ref != NULL) { + if (ref->id == id) { + *refs = ref->next; + delete ref; + return; + } + + refs = &ref->next; + ref = *refs; + } + + LOG_ALWAYS_FATAL("RefBase: removing id %p on RefBase %p (weakref_type %p) that doesn't exist!", + id, mBase, this); + } + } + + void printRefsLocked(String8* out, const ref_entry* refs) const + { + char buf[128]; + while (refs) { + char inc = refs->ref >= 0 ? '+' : '-'; + sprintf(buf, "\t%c ID %p (ref %d):\n", + inc, refs->id, refs->ref); + out->append(buf); +#if DEBUG_REFS_CALLSTACK_ENABLED + out->append(refs->stack.toString("\t\t")); +#else + out->append("\t\t(call stacks disabled)"); +#endif + refs = refs->next; + } + } + + Mutex mMutex; + ref_entry* mStrongRefs; + ref_entry* mWeakRefs; + + bool mTrackEnabled; + // Collect stack traces on addref and removeref, instead of deleting the stack references + // on removeref that match the address ones. + bool mRetain; + +#if 0 + void addRef(KeyedVector* refs, const void* id) + { + AutoMutex _l(mMutex); + ssize_t i = refs->indexOfKey(id); + if (i >= 0) { + ++(refs->editValueAt(i)); + } else { + i = refs->add(id, 1); + } + } + + void removeRef(KeyedVector* refs, const void* id) + { + AutoMutex _l(mMutex); + ssize_t i = refs->indexOfKey(id); + LOG_ALWAYS_FATAL_IF(i < 0, "RefBase: removing id %p that doesn't exist!", id); + if (i >= 0) { + int32_t val = --(refs->editValueAt(i)); + if (val == 0) { + refs->removeItemsAt(i); + } + } + } + + void printRefs(const KeyedVector& refs) + { + const size_t N=refs.size(); + for (size_t i=0; i mStrongRefs; + KeyedVector mWeakRefs; +#endif + +#endif +}; + +// --------------------------------------------------------------------------- + +void RefBase::incStrong(const void* id) const +{ + weakref_impl* const refs = mRefs; + refs->addWeakRef(id); + refs->incWeak(id); + + refs->addStrongRef(id); + const int32_t c = android_atomic_inc(&refs->mStrong); + LOG_ASSERT(c > 0, "incStrong() called on %p after last strong ref", refs); +#if PRINT_REFS + LOGD("incStrong of %p from %p: cnt=%d\n", this, id, c); +#endif + if (c != INITIAL_STRONG_VALUE) { + return; + } + + android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong); + const_cast(this)->onFirstRef(); +} + +void RefBase::decStrong(const void* id) const +{ + weakref_impl* const refs = mRefs; + refs->removeStrongRef(id); + const int32_t c = android_atomic_dec(&refs->mStrong); +#if PRINT_REFS + LOGD("decStrong of %p from %p: cnt=%d\n", this, id, c); +#endif + LOG_ASSERT(c >= 1, "decStrong() called on %p too many times", refs); + if (c == 1) { + const_cast(this)->onLastStrongRef(id); + if ((refs->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) { + delete this; + } + } + refs->removeWeakRef(id); + refs->decWeak(id); +} + +void RefBase::forceIncStrong(const void* id) const +{ + weakref_impl* const refs = mRefs; + refs->addWeakRef(id); + refs->incWeak(id); + + refs->addStrongRef(id); + const int32_t c = android_atomic_inc(&refs->mStrong); + LOG_ASSERT(c >= 0, "forceIncStrong called on %p after ref count underflow", + refs); +#if PRINT_REFS + LOGD("forceIncStrong of %p from %p: cnt=%d\n", this, id, c); +#endif + + switch (c) { + case INITIAL_STRONG_VALUE: + android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong); + // fall through... + case 0: + const_cast(this)->onFirstRef(); + } +} + +int32_t RefBase::getStrongCount() const +{ + return mRefs->mStrong; +} + + + +RefBase* RefBase::weakref_type::refBase() const +{ + return static_cast(this)->mBase; +} + +void RefBase::weakref_type::incWeak(const void* id) +{ + weakref_impl* const impl = static_cast(this); + impl->addWeakRef(id); + const int32_t c = android_atomic_inc(&impl->mWeak); + LOG_ASSERT(c >= 0, "incWeak called on %p after last weak ref", this); +} + +void RefBase::weakref_type::decWeak(const void* id) +{ + weakref_impl* const impl = static_cast(this); + impl->removeWeakRef(id); + const int32_t c = android_atomic_dec(&impl->mWeak); + LOG_ASSERT(c >= 1, "decWeak called on %p too many times", this); + if (c != 1) return; + + if ((impl->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) { + if (impl->mStrong == INITIAL_STRONG_VALUE) + delete impl->mBase; + else { +// LOGV("Freeing refs %p of old RefBase %p\n", this, impl->mBase); + delete impl; + } + } else { + impl->mBase->onLastWeakRef(id); + if ((impl->mFlags&OBJECT_LIFETIME_FOREVER) != OBJECT_LIFETIME_FOREVER) { + delete impl->mBase; + } + } +} + +bool RefBase::weakref_type::attemptIncStrong(const void* id) +{ + incWeak(id); + + weakref_impl* const impl = static_cast(this); + + int32_t curCount = impl->mStrong; + LOG_ASSERT(curCount >= 0, "attemptIncStrong called on %p after underflow", + this); + while (curCount > 0 && curCount != INITIAL_STRONG_VALUE) { + if (android_atomic_cmpxchg(curCount, curCount+1, &impl->mStrong) == 0) { + break; + } + curCount = impl->mStrong; + } + + if (curCount <= 0 || curCount == INITIAL_STRONG_VALUE) { + bool allow; + if (curCount == INITIAL_STRONG_VALUE) { + // Attempting to acquire first strong reference... this is allowed + // if the object does NOT have a longer lifetime (meaning the + // implementation doesn't need to see this), or if the implementation + // allows it to happen. + allow = (impl->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK + || impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id); + } else { + // Attempting to revive the object... this is allowed + // if the object DOES have a longer lifetime (so we can safely + // call the object with only a weak ref) and the implementation + // allows it to happen. + allow = (impl->mFlags&OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_WEAK + && impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id); + } + if (!allow) { + decWeak(id); + return false; + } + curCount = android_atomic_inc(&impl->mStrong); + + // If the strong reference count has already been incremented by + // someone else, the implementor of onIncStrongAttempted() is holding + // an unneeded reference. So call onLastStrongRef() here to remove it. + // (No, this is not pretty.) Note that we MUST NOT do this if we + // are in fact acquiring the first reference. + if (curCount > 0 && curCount < INITIAL_STRONG_VALUE) { + impl->mBase->onLastStrongRef(id); + } + } + + impl->addWeakRef(id); + impl->addStrongRef(id); + +#if PRINT_REFS + LOGD("attemptIncStrong of %p from %p: cnt=%d\n", this, id, curCount); +#endif + + if (curCount == INITIAL_STRONG_VALUE) { + android_atomic_add(-INITIAL_STRONG_VALUE, &impl->mStrong); + impl->mBase->onFirstRef(); + } + + return true; +} + +bool RefBase::weakref_type::attemptIncWeak(const void* id) +{ + weakref_impl* const impl = static_cast(this); + + int32_t curCount = impl->mWeak; + LOG_ASSERT(curCount >= 0, "attemptIncWeak called on %p after underflow", + this); + while (curCount > 0) { + if (android_atomic_cmpxchg(curCount, curCount+1, &impl->mWeak) == 0) { + break; + } + curCount = impl->mWeak; + } + + if (curCount > 0) { + impl->addWeakRef(id); + } + + return curCount > 0; +} + +int32_t RefBase::weakref_type::getWeakCount() const +{ + return static_cast(this)->mWeak; +} + +void RefBase::weakref_type::printRefs() const +{ + static_cast(this)->printRefs(); +} + +void RefBase::weakref_type::trackMe(bool enable, bool retain) +{ + static_cast(this)->trackMe(enable, retain); +} + +RefBase::weakref_type* RefBase::createWeak(const void* id) const +{ + mRefs->incWeak(id); + return mRefs; +} + +RefBase::weakref_type* RefBase::getWeakRefs() const +{ + return mRefs; +} + +RefBase::RefBase() + : mRefs(new weakref_impl(this)) +{ +// LOGV("Creating refs %p with RefBase %p\n", mRefs, this); +} + +RefBase::~RefBase() +{ +// LOGV("Destroying RefBase %p (refs %p)\n", this, mRefs); + if (mRefs->mWeak == 0) { +// LOGV("Freeing refs %p of old RefBase %p\n", mRefs, this); + delete mRefs; + } +} + +void RefBase::extendObjectLifetime(int32_t mode) +{ + android_atomic_or(mode, &mRefs->mFlags); +} + +void RefBase::onFirstRef() +{ +} + +void RefBase::onLastStrongRef(const void* /*id*/) +{ +} + +bool RefBase::onIncStrongAttempted(uint32_t flags, const void* id) +{ + return (flags&FIRST_INC_STRONG) ? true : false; +} + +void RefBase::onLastWeakRef(const void* /*id*/) +{ +} + +}; // namespace android diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp new file mode 100644 index 000000000..71e7cd722 --- /dev/null +++ b/libs/utils/ResourceTypes.cpp @@ -0,0 +1,3983 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "ResourceType" +//#define LOG_NDEBUG 0 + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#ifndef INT32_MAX +#define INT32_MAX ((int32_t)(2147483647)) +#endif + +#define POOL_NOISY(x) //x +#define XML_NOISY(x) //x +#define TABLE_NOISY(x) //x +#define TABLE_GETENTRY(x) //x +#define TABLE_SUPER_NOISY(x) //x +#define LOAD_TABLE_NOISY(x) //x + +namespace android { + +#ifdef HAVE_WINSOCK +#undef nhtol +#undef htonl + +#ifdef HAVE_LITTLE_ENDIAN +#define ntohl(x) ( ((x) << 24) | (((x) >> 24) & 255) | (((x) << 8) & 0xff0000) | (((x) >> 8) & 0xff00) ) +#define htonl(x) ntohl(x) +#define ntohs(x) ( (((x) << 8) & 0xff00) | (((x) >> 8) & 255) ) +#define htons(x) ntohs(x) +#else +#define ntohl(x) (x) +#define htonl(x) (x) +#define ntohs(x) (x) +#define htons(x) (x) +#endif +#endif + +static void printToLogFunc(void* cookie, const char* txt) +{ + LOGV("%s", txt); +} + +// Standard C isspace() is only required to look at the low byte of its input, so +// produces incorrect results for UTF-16 characters. For safety's sake, assume that +// any high-byte UTF-16 code point is not whitespace. +inline int isspace16(char16_t c) { + return (c < 0x0080 && isspace(c)); +} + +// range checked; guaranteed to NUL-terminate within the stated number of available slots +// NOTE: if this truncates the dst string due to running out of space, no attempt is +// made to avoid splitting surrogate pairs. +static void strcpy16_dtoh(uint16_t* dst, const uint16_t* src, size_t avail) +{ + uint16_t* last = dst + avail - 1; + while (*src && (dst < last)) { + char16_t s = dtohs(*src); + *dst++ = s; + src++; + } + *dst = 0; +} + +static status_t validate_chunk(const ResChunk_header* chunk, + size_t minSize, + const uint8_t* dataEnd, + const char* name) +{ + const uint16_t headerSize = dtohs(chunk->headerSize); + const uint32_t size = dtohl(chunk->size); + + if (headerSize >= minSize) { + if (headerSize <= size) { + if (((headerSize|size)&0x3) == 0) { + if ((ssize_t)size <= (dataEnd-((const uint8_t*)chunk))) { + return NO_ERROR; + } + LOGW("%s data size %p extends beyond resource end %p.", + name, (void*)size, + (void*)(dataEnd-((const uint8_t*)chunk))); + return BAD_TYPE; + } + LOGW("%s size 0x%x or headerSize 0x%x is not on an integer boundary.", + name, (int)size, (int)headerSize); + return BAD_TYPE; + } + LOGW("%s size %p is smaller than header size %p.", + name, (void*)size, (void*)(int)headerSize); + return BAD_TYPE; + } + LOGW("%s header size %p is too small.", + name, (void*)(int)headerSize); + return BAD_TYPE; +} + +inline void Res_value::copyFrom_dtoh(const Res_value& src) +{ + size = dtohs(src.size); + res0 = src.res0; + dataType = src.dataType; + data = dtohl(src.data); +} + +void Res_png_9patch::deviceToFile() +{ + for (int i = 0; i < numXDivs; i++) { + xDivs[i] = htonl(xDivs[i]); + } + for (int i = 0; i < numYDivs; i++) { + yDivs[i] = htonl(yDivs[i]); + } + paddingLeft = htonl(paddingLeft); + paddingRight = htonl(paddingRight); + paddingTop = htonl(paddingTop); + paddingBottom = htonl(paddingBottom); + for (int i=0; ixDivs, numXDivs * sizeof(int32_t)); + data += numXDivs * sizeof(int32_t); + memmove(data, this->yDivs, numYDivs * sizeof(int32_t)); + data += numYDivs * sizeof(int32_t); + memmove(data, this->colors, numColors * sizeof(uint32_t)); +} + +static void deserializeInternal(const void* inData, Res_png_9patch* outData) { + char* patch = (char*) inData; + if (inData != outData) { + memmove(&outData->wasDeserialized, patch, 4); // copy wasDeserialized, numXDivs, numYDivs, numColors + memmove(&outData->paddingLeft, patch + 12, 4); // copy wasDeserialized, numXDivs, numYDivs, numColors + } + outData->wasDeserialized = true; + char* data = (char*)outData; + data += sizeof(Res_png_9patch); + outData->xDivs = (int32_t*) data; + data += outData->numXDivs * sizeof(int32_t); + outData->yDivs = (int32_t*) data; + data += outData->numYDivs * sizeof(int32_t); + outData->colors = (uint32_t*) data; +} + +Res_png_9patch* Res_png_9patch::deserialize(const void* inData) +{ + if (sizeof(void*) != sizeof(int32_t)) { + LOGE("Cannot deserialize on non 32-bit system\n"); + return NULL; + } + deserializeInternal(inData, (Res_png_9patch*) inData); + return (Res_png_9patch*) inData; +} + +// -------------------------------------------------------------------- +// -------------------------------------------------------------------- +// -------------------------------------------------------------------- + +ResStringPool::ResStringPool() + : mError(NO_INIT), mOwnedData(NULL) +{ +} + +ResStringPool::ResStringPool(const void* data, size_t size, bool copyData) + : mError(NO_INIT), mOwnedData(NULL) +{ + setTo(data, size, copyData); +} + +ResStringPool::~ResStringPool() +{ + uninit(); +} + +status_t ResStringPool::setTo(const void* data, size_t size, bool copyData) +{ + if (!data || !size) { + return (mError=BAD_TYPE); + } + + uninit(); + + const bool notDeviceEndian = htods(0xf0) != 0xf0; + + if (copyData || notDeviceEndian) { + mOwnedData = malloc(size); + if (mOwnedData == NULL) { + return (mError=NO_MEMORY); + } + memcpy(mOwnedData, data, size); + data = mOwnedData; + } + + mHeader = (const ResStringPool_header*)data; + + if (notDeviceEndian) { + ResStringPool_header* h = const_cast(mHeader); + h->header.headerSize = dtohs(mHeader->header.headerSize); + h->header.type = dtohs(mHeader->header.type); + h->header.size = dtohl(mHeader->header.size); + h->stringCount = dtohl(mHeader->stringCount); + h->styleCount = dtohl(mHeader->styleCount); + h->flags = dtohl(mHeader->flags); + h->stringsStart = dtohl(mHeader->stringsStart); + h->stylesStart = dtohl(mHeader->stylesStart); + } + + if (mHeader->header.headerSize > mHeader->header.size + || mHeader->header.size > size) { + LOGW("Bad string block: header size %d or total size %d is larger than data size %d\n", + (int)mHeader->header.headerSize, (int)mHeader->header.size, (int)size); + return (mError=BAD_TYPE); + } + mSize = mHeader->header.size; + mEntries = (const uint32_t*) + (((const uint8_t*)data)+mHeader->header.headerSize); + + if (mHeader->stringCount > 0) { + if ((mHeader->stringCount*sizeof(uint32_t) < mHeader->stringCount) // uint32 overflow? + || (mHeader->header.headerSize+(mHeader->stringCount*sizeof(uint32_t))) + > size) { + LOGW("Bad string block: entry of %d items extends past data size %d\n", + (int)(mHeader->header.headerSize+(mHeader->stringCount*sizeof(uint32_t))), + (int)size); + return (mError=BAD_TYPE); + } + mStrings = (const char16_t*) + (((const uint8_t*)data)+mHeader->stringsStart); + if (mHeader->stringsStart >= (mHeader->header.size-sizeof(uint16_t))) { + LOGW("Bad string block: string pool starts at %d, after total size %d\n", + (int)mHeader->stringsStart, (int)mHeader->header.size); + return (mError=BAD_TYPE); + } + if (mHeader->styleCount == 0) { + mStringPoolSize = + (mHeader->header.size-mHeader->stringsStart)/sizeof(uint16_t); + } else { + // check invariant: styles follow the strings + if (mHeader->stylesStart <= mHeader->stringsStart) { + LOGW("Bad style block: style block starts at %d, before strings at %d\n", + (int)mHeader->stylesStart, (int)mHeader->stringsStart); + return (mError=BAD_TYPE); + } + mStringPoolSize = + (mHeader->stylesStart-mHeader->stringsStart)/sizeof(uint16_t); + } + + // check invariant: stringCount > 0 requires a string pool to exist + if (mStringPoolSize == 0) { + LOGW("Bad string block: stringCount is %d but pool size is 0\n", (int)mHeader->stringCount); + return (mError=BAD_TYPE); + } + + if (notDeviceEndian) { + size_t i; + uint32_t* e = const_cast(mEntries); + for (i=0; istringCount; i++) { + e[i] = dtohl(mEntries[i]); + } + char16_t* s = const_cast(mStrings); + for (i=0; istyleCount > 0) { + mEntryStyles = mEntries + mHeader->stringCount; + // invariant: integer overflow in calculating mEntryStyles + if (mEntryStyles < mEntries) { + LOGW("Bad string block: integer overflow finding styles\n"); + return (mError=BAD_TYPE); + } + + if (((const uint8_t*)mEntryStyles-(const uint8_t*)mHeader) > (int)size) { + LOGW("Bad string block: entry of %d styles extends past data size %d\n", + (int)((const uint8_t*)mEntryStyles-(const uint8_t*)mHeader), + (int)size); + return (mError=BAD_TYPE); + } + mStyles = (const uint32_t*) + (((const uint8_t*)data)+mHeader->stylesStart); + if (mHeader->stylesStart >= mHeader->header.size) { + LOGW("Bad string block: style pool starts %d, after total size %d\n", + (int)mHeader->stylesStart, (int)mHeader->header.size); + return (mError=BAD_TYPE); + } + mStylePoolSize = + (mHeader->header.size-mHeader->stylesStart)/sizeof(uint32_t); + + if (notDeviceEndian) { + size_t i; + uint32_t* e = const_cast(mEntryStyles); + for (i=0; istyleCount; i++) { + e[i] = dtohl(mEntryStyles[i]); + } + uint32_t* s = const_cast(mStyles); + for (i=0; istringCount) { + const uint32_t off = (mEntries[idx]/sizeof(uint16_t)); + if (off < (mStringPoolSize-1)) { + const char16_t* str = mStrings+off; + *outLen = *str; + if ((*str)&0x8000) { + str++; + *outLen = (((*outLen)&0x7fff)<<16) + *str; + } + if ((uint32_t)(str+1+*outLen-mStrings) < mStringPoolSize) { + return str+1; + } else { + LOGW("Bad string block: string #%d extends to %d, past end at %d\n", + (int)idx, (int)(str+1+*outLen-mStrings), (int)mStringPoolSize); + } + } else { + LOGW("Bad string block: string #%d entry is at %d, past end at %d\n", + (int)idx, (int)(off*sizeof(uint16_t)), + (int)(mStringPoolSize*sizeof(uint16_t))); + } + } + return NULL; +} + +const ResStringPool_span* ResStringPool::styleAt(const ResStringPool_ref& ref) const +{ + return styleAt(ref.index); +} + +const ResStringPool_span* ResStringPool::styleAt(size_t idx) const +{ + if (mError == NO_ERROR && idx < mHeader->styleCount) { + const uint32_t off = (mEntryStyles[idx]/sizeof(uint32_t)); + if (off < mStylePoolSize) { + return (const ResStringPool_span*)(mStyles+off); + } else { + LOGW("Bad string block: style #%d entry is at %d, past end at %d\n", + (int)idx, (int)(off*sizeof(uint32_t)), + (int)(mStylePoolSize*sizeof(uint32_t))); + } + } + return NULL; +} + +ssize_t ResStringPool::indexOfString(const char16_t* str, size_t strLen) const +{ + if (mError != NO_ERROR) { + return mError; + } + + size_t len; + + if (mHeader->flags&ResStringPool_header::SORTED_FLAG) { + // Do a binary search for the string... + ssize_t l = 0; + ssize_t h = mHeader->stringCount-1; + + ssize_t mid; + while (l <= h) { + mid = l + (h - l)/2; + const char16_t* s = stringAt(mid, &len); + int c = s ? strzcmp16(s, len, str, strLen) : -1; + POOL_NOISY(printf("Looking for %s, at %s, cmp=%d, l/mid/h=%d/%d/%d\n", + String8(str).string(), + String8(s).string(), + c, (int)l, (int)mid, (int)h)); + if (c == 0) { + return mid; + } else if (c < 0) { + l = mid + 1; + } else { + h = mid - 1; + } + } + } else { + // It is unusual to get the ID from an unsorted string block... + // most often this happens because we want to get IDs for style + // span tags; since those always appear at the end of the string + // block, start searching at the back. + for (int i=mHeader->stringCount-1; i>=0; i--) { + const char16_t* s = stringAt(i, &len); + POOL_NOISY(printf("Looking for %s, at %s, i=%d\n", + String8(str, strLen).string(), + String8(s).string(), + i)); + if (s && strzcmp16(s, len, str, strLen) == 0) { + return i; + } + } + } + + return NAME_NOT_FOUND; +} + +size_t ResStringPool::size() const +{ + return (mError == NO_ERROR) ? mHeader->stringCount : 0; +} + +// -------------------------------------------------------------------- +// -------------------------------------------------------------------- +// -------------------------------------------------------------------- + +ResXMLParser::ResXMLParser(const ResXMLTree& tree) + : mTree(tree), mEventCode(BAD_DOCUMENT) +{ +} + +void ResXMLParser::restart() +{ + mCurNode = NULL; + mEventCode = mTree.mError == NO_ERROR ? START_DOCUMENT : BAD_DOCUMENT; +} + +ResXMLParser::event_code_t ResXMLParser::getEventType() const +{ + return mEventCode; +} + +ResXMLParser::event_code_t ResXMLParser::next() +{ + if (mEventCode == START_DOCUMENT) { + mCurNode = mTree.mRootNode; + mCurExt = mTree.mRootExt; + return (mEventCode=mTree.mRootCode); + } else if (mEventCode >= FIRST_CHUNK_CODE) { + return nextNode(); + } + return mEventCode; +} + +const int32_t ResXMLParser::getCommentID() const +{ + return mCurNode != NULL ? dtohl(mCurNode->comment.index) : -1; +} + +const uint16_t* ResXMLParser::getComment(size_t* outLen) const +{ + int32_t id = getCommentID(); + return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; +} + +const uint32_t ResXMLParser::getLineNumber() const +{ + return mCurNode != NULL ? dtohl(mCurNode->lineNumber) : -1; +} + +const int32_t ResXMLParser::getTextID() const +{ + if (mEventCode == TEXT) { + return dtohl(((const ResXMLTree_cdataExt*)mCurExt)->data.index); + } + return -1; +} + +const uint16_t* ResXMLParser::getText(size_t* outLen) const +{ + int32_t id = getTextID(); + return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; +} + +ssize_t ResXMLParser::getTextValue(Res_value* outValue) const +{ + if (mEventCode == TEXT) { + outValue->copyFrom_dtoh(((const ResXMLTree_cdataExt*)mCurExt)->typedData); + return sizeof(Res_value); + } + return BAD_TYPE; +} + +const int32_t ResXMLParser::getNamespacePrefixID() const +{ + if (mEventCode == START_NAMESPACE || mEventCode == END_NAMESPACE) { + return dtohl(((const ResXMLTree_namespaceExt*)mCurExt)->prefix.index); + } + return -1; +} + +const uint16_t* ResXMLParser::getNamespacePrefix(size_t* outLen) const +{ + int32_t id = getNamespacePrefixID(); + //printf("prefix=%d event=%p\n", id, mEventCode); + return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; +} + +const int32_t ResXMLParser::getNamespaceUriID() const +{ + if (mEventCode == START_NAMESPACE || mEventCode == END_NAMESPACE) { + return dtohl(((const ResXMLTree_namespaceExt*)mCurExt)->uri.index); + } + return -1; +} + +const uint16_t* ResXMLParser::getNamespaceUri(size_t* outLen) const +{ + int32_t id = getNamespaceUriID(); + //printf("uri=%d event=%p\n", id, mEventCode); + return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; +} + +const int32_t ResXMLParser::getElementNamespaceID() const +{ + if (mEventCode == START_TAG) { + return dtohl(((const ResXMLTree_attrExt*)mCurExt)->ns.index); + } + if (mEventCode == END_TAG) { + return dtohl(((const ResXMLTree_endElementExt*)mCurExt)->ns.index); + } + return -1; +} + +const uint16_t* ResXMLParser::getElementNamespace(size_t* outLen) const +{ + int32_t id = getElementNamespaceID(); + return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; +} + +const int32_t ResXMLParser::getElementNameID() const +{ + if (mEventCode == START_TAG) { + return dtohl(((const ResXMLTree_attrExt*)mCurExt)->name.index); + } + if (mEventCode == END_TAG) { + return dtohl(((const ResXMLTree_endElementExt*)mCurExt)->name.index); + } + return -1; +} + +const uint16_t* ResXMLParser::getElementName(size_t* outLen) const +{ + int32_t id = getElementNameID(); + return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; +} + +size_t ResXMLParser::getAttributeCount() const +{ + if (mEventCode == START_TAG) { + return dtohs(((const ResXMLTree_attrExt*)mCurExt)->attributeCount); + } + return 0; +} + +const int32_t ResXMLParser::getAttributeNamespaceID(size_t idx) const +{ + if (mEventCode == START_TAG) { + const ResXMLTree_attrExt* tag = (const ResXMLTree_attrExt*)mCurExt; + if (idx < dtohs(tag->attributeCount)) { + const ResXMLTree_attribute* attr = (const ResXMLTree_attribute*) + (((const uint8_t*)tag) + + dtohs(tag->attributeStart) + + (dtohs(tag->attributeSize)*idx)); + return dtohl(attr->ns.index); + } + } + return -2; +} + +const uint16_t* ResXMLParser::getAttributeNamespace(size_t idx, size_t* outLen) const +{ + int32_t id = getAttributeNamespaceID(idx); + //printf("attribute namespace=%d idx=%d event=%p\n", id, idx, mEventCode); + //XML_NOISY(printf("getAttributeNamespace 0x%x=0x%x\n", idx, id)); + return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; +} + +const int32_t ResXMLParser::getAttributeNameID(size_t idx) const +{ + if (mEventCode == START_TAG) { + const ResXMLTree_attrExt* tag = (const ResXMLTree_attrExt*)mCurExt; + if (idx < dtohs(tag->attributeCount)) { + const ResXMLTree_attribute* attr = (const ResXMLTree_attribute*) + (((const uint8_t*)tag) + + dtohs(tag->attributeStart) + + (dtohs(tag->attributeSize)*idx)); + return dtohl(attr->name.index); + } + } + return -1; +} + +const uint16_t* ResXMLParser::getAttributeName(size_t idx, size_t* outLen) const +{ + int32_t id = getAttributeNameID(idx); + //printf("attribute name=%d idx=%d event=%p\n", id, idx, mEventCode); + //XML_NOISY(printf("getAttributeName 0x%x=0x%x\n", idx, id)); + return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; +} + +const uint32_t ResXMLParser::getAttributeNameResID(size_t idx) const +{ + int32_t id = getAttributeNameID(idx); + if (id >= 0 && (size_t)id < mTree.mNumResIds) { + return dtohl(mTree.mResIds[id]); + } + return 0; +} + +const int32_t ResXMLParser::getAttributeValueStringID(size_t idx) const +{ + if (mEventCode == START_TAG) { + const ResXMLTree_attrExt* tag = (const ResXMLTree_attrExt*)mCurExt; + if (idx < dtohs(tag->attributeCount)) { + const ResXMLTree_attribute* attr = (const ResXMLTree_attribute*) + (((const uint8_t*)tag) + + dtohs(tag->attributeStart) + + (dtohs(tag->attributeSize)*idx)); + return dtohl(attr->rawValue.index); + } + } + return -1; +} + +const uint16_t* ResXMLParser::getAttributeStringValue(size_t idx, size_t* outLen) const +{ + int32_t id = getAttributeValueStringID(idx); + //XML_NOISY(printf("getAttributeValue 0x%x=0x%x\n", idx, id)); + return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; +} + +int32_t ResXMLParser::getAttributeDataType(size_t idx) const +{ + if (mEventCode == START_TAG) { + const ResXMLTree_attrExt* tag = (const ResXMLTree_attrExt*)mCurExt; + if (idx < dtohs(tag->attributeCount)) { + const ResXMLTree_attribute* attr = (const ResXMLTree_attribute*) + (((const uint8_t*)tag) + + dtohs(tag->attributeStart) + + (dtohs(tag->attributeSize)*idx)); + return attr->typedValue.dataType; + } + } + return Res_value::TYPE_NULL; +} + +int32_t ResXMLParser::getAttributeData(size_t idx) const +{ + if (mEventCode == START_TAG) { + const ResXMLTree_attrExt* tag = (const ResXMLTree_attrExt*)mCurExt; + if (idx < dtohs(tag->attributeCount)) { + const ResXMLTree_attribute* attr = (const ResXMLTree_attribute*) + (((const uint8_t*)tag) + + dtohs(tag->attributeStart) + + (dtohs(tag->attributeSize)*idx)); + return dtohl(attr->typedValue.data); + } + } + return 0; +} + +ssize_t ResXMLParser::getAttributeValue(size_t idx, Res_value* outValue) const +{ + if (mEventCode == START_TAG) { + const ResXMLTree_attrExt* tag = (const ResXMLTree_attrExt*)mCurExt; + if (idx < dtohs(tag->attributeCount)) { + const ResXMLTree_attribute* attr = (const ResXMLTree_attribute*) + (((const uint8_t*)tag) + + dtohs(tag->attributeStart) + + (dtohs(tag->attributeSize)*idx)); + outValue->copyFrom_dtoh(attr->typedValue); + return sizeof(Res_value); + } + } + return BAD_TYPE; +} + +ssize_t ResXMLParser::indexOfAttribute(const char* ns, const char* attr) const +{ + String16 nsStr(ns != NULL ? ns : ""); + String16 attrStr(attr); + return indexOfAttribute(ns ? nsStr.string() : NULL, ns ? nsStr.size() : 0, + attrStr.string(), attrStr.size()); +} + +ssize_t ResXMLParser::indexOfAttribute(const char16_t* ns, size_t nsLen, + const char16_t* attr, size_t attrLen) const +{ + if (mEventCode == START_TAG) { + const size_t N = getAttributeCount(); + for (size_t i=0; i attr=%s, curAttr=%s\n", + // String8(attr).string(), String8(curAttr).string()); + if (attr && curAttr && (strzcmp16(attr, attrLen, curAttr, curAttrLen) == 0)) { + if (ns == NULL) { + if (curNs == NULL) return i; + } else if (curNs != NULL) { + //printf(" --> ns=%s, curNs=%s\n", + // String8(ns).string(), String8(curNs).string()); + if (strzcmp16(ns, nsLen, curNs, curNsLen) == 0) return i; + } + } + } + } + + return NAME_NOT_FOUND; +} + +ssize_t ResXMLParser::indexOfID() const +{ + if (mEventCode == START_TAG) { + const ssize_t idx = dtohs(((const ResXMLTree_attrExt*)mCurExt)->idIndex); + if (idx > 0) return (idx-1); + } + return NAME_NOT_FOUND; +} + +ssize_t ResXMLParser::indexOfClass() const +{ + if (mEventCode == START_TAG) { + const ssize_t idx = dtohs(((const ResXMLTree_attrExt*)mCurExt)->classIndex); + if (idx > 0) return (idx-1); + } + return NAME_NOT_FOUND; +} + +ssize_t ResXMLParser::indexOfStyle() const +{ + if (mEventCode == START_TAG) { + const ssize_t idx = dtohs(((const ResXMLTree_attrExt*)mCurExt)->styleIndex); + if (idx > 0) return (idx-1); + } + return NAME_NOT_FOUND; +} + +ResXMLParser::event_code_t ResXMLParser::nextNode() +{ + if (mEventCode < 0) { + return mEventCode; + } + + do { + const ResXMLTree_node* next = (const ResXMLTree_node*) + (((const uint8_t*)mCurNode) + dtohl(mCurNode->header.size)); + //LOGW("Next node: prev=%p, next=%p\n", mCurNode, next); + + if (((const uint8_t*)next) >= mTree.mDataEnd) { + mCurNode = NULL; + return (mEventCode=END_DOCUMENT); + } + + if (mTree.validateNode(next) != NO_ERROR) { + mCurNode = NULL; + return (mEventCode=BAD_DOCUMENT); + } + + mCurNode = next; + const uint16_t headerSize = dtohs(next->header.headerSize); + const uint32_t totalSize = dtohl(next->header.size); + mCurExt = ((const uint8_t*)next) + headerSize; + size_t minExtSize = 0; + event_code_t eventCode = (event_code_t)dtohs(next->header.type); + switch ((mEventCode=eventCode)) { + case RES_XML_START_NAMESPACE_TYPE: + case RES_XML_END_NAMESPACE_TYPE: + minExtSize = sizeof(ResXMLTree_namespaceExt); + break; + case RES_XML_START_ELEMENT_TYPE: + minExtSize = sizeof(ResXMLTree_attrExt); + break; + case RES_XML_END_ELEMENT_TYPE: + minExtSize = sizeof(ResXMLTree_endElementExt); + break; + case RES_XML_CDATA_TYPE: + minExtSize = sizeof(ResXMLTree_cdataExt); + break; + default: + LOGW("Unknown XML block: header type %d in node at %d\n", + (int)dtohs(next->header.type), + (int)(((const uint8_t*)next)-((const uint8_t*)mTree.mHeader))); + continue; + } + + if ((totalSize-headerSize) < minExtSize) { + LOGW("Bad XML block: header type 0x%x in node at 0x%x has size %d, need %d\n", + (int)dtohs(next->header.type), + (int)(((const uint8_t*)next)-((const uint8_t*)mTree.mHeader)), + (int)(totalSize-headerSize), (int)minExtSize); + return (mEventCode=BAD_DOCUMENT); + } + + //printf("CurNode=%p, CurExt=%p, headerSize=%d, minExtSize=%d\n", + // mCurNode, mCurExt, headerSize, minExtSize); + + return eventCode; + } while (true); +} + +void ResXMLParser::getPosition(ResXMLParser::ResXMLPosition* pos) const +{ + pos->eventCode = mEventCode; + pos->curNode = mCurNode; + pos->curExt = mCurExt; +} + +void ResXMLParser::setPosition(const ResXMLParser::ResXMLPosition& pos) +{ + mEventCode = pos.eventCode; + mCurNode = pos.curNode; + mCurExt = pos.curExt; +} + + +// -------------------------------------------------------------------- + +static volatile int32_t gCount = 0; + +ResXMLTree::ResXMLTree() + : ResXMLParser(*this) + , mError(NO_INIT), mOwnedData(NULL) +{ + //LOGI("Creating ResXMLTree %p #%d\n", this, android_atomic_inc(&gCount)+1); + restart(); +} + +ResXMLTree::ResXMLTree(const void* data, size_t size, bool copyData) + : ResXMLParser(*this) + , mError(NO_INIT), mOwnedData(NULL) +{ + //LOGI("Creating ResXMLTree %p #%d\n", this, android_atomic_inc(&gCount)+1); + setTo(data, size, copyData); +} + +ResXMLTree::~ResXMLTree() +{ + //LOGI("Destroying ResXMLTree in %p #%d\n", this, android_atomic_dec(&gCount)-1); + uninit(); +} + +status_t ResXMLTree::setTo(const void* data, size_t size, bool copyData) +{ + uninit(); + mEventCode = START_DOCUMENT; + + if (copyData) { + mOwnedData = malloc(size); + if (mOwnedData == NULL) { + return (mError=NO_MEMORY); + } + memcpy(mOwnedData, data, size); + data = mOwnedData; + } + + mHeader = (const ResXMLTree_header*)data; + mSize = dtohl(mHeader->header.size); + if (dtohs(mHeader->header.headerSize) > mSize || mSize > size) { + LOGW("Bad XML block: header size %d or total size %d is larger than data size %d\n", + (int)dtohs(mHeader->header.headerSize), + (int)dtohl(mHeader->header.size), (int)size); + mError = BAD_TYPE; + restart(); + return mError; + } + mDataEnd = ((const uint8_t*)mHeader) + mSize; + + mStrings.uninit(); + mRootNode = NULL; + mResIds = NULL; + mNumResIds = 0; + + // First look for a couple interesting chunks: the string block + // and first XML node. + const ResChunk_header* chunk = + (const ResChunk_header*)(((const uint8_t*)mHeader) + dtohs(mHeader->header.headerSize)); + const ResChunk_header* lastChunk = chunk; + while (((const uint8_t*)chunk) < (mDataEnd-sizeof(ResChunk_header)) && + ((const uint8_t*)chunk) < (mDataEnd-dtohl(chunk->size))) { + status_t err = validate_chunk(chunk, sizeof(ResChunk_header), mDataEnd, "XML"); + if (err != NO_ERROR) { + mError = err; + goto done; + } + const uint16_t type = dtohs(chunk->type); + const size_t size = dtohl(chunk->size); + XML_NOISY(printf("Scanning @ %p: type=0x%x, size=0x%x\n", + (void*)(((uint32_t)chunk)-((uint32_t)mHeader)), type, size)); + if (type == RES_STRING_POOL_TYPE) { + mStrings.setTo(chunk, size); + } else if (type == RES_XML_RESOURCE_MAP_TYPE) { + mResIds = (const uint32_t*) + (((const uint8_t*)chunk)+dtohs(chunk->headerSize)); + mNumResIds = (dtohl(chunk->size)-dtohs(chunk->headerSize))/sizeof(uint32_t); + } else if (type >= RES_XML_FIRST_CHUNK_TYPE + && type <= RES_XML_LAST_CHUNK_TYPE) { + if (validateNode((const ResXMLTree_node*)chunk) != NO_ERROR) { + mError = BAD_TYPE; + goto done; + } + mCurNode = (const ResXMLTree_node*)lastChunk; + if (nextNode() == BAD_DOCUMENT) { + mError = BAD_TYPE; + goto done; + } + mRootNode = mCurNode; + mRootExt = mCurExt; + mRootCode = mEventCode; + break; + } else { + XML_NOISY(printf("Skipping unknown chunk!\n")); + } + lastChunk = chunk; + chunk = (const ResChunk_header*) + (((const uint8_t*)chunk) + size); + } + + if (mRootNode == NULL) { + LOGW("Bad XML block: no root element node found\n"); + mError = BAD_TYPE; + goto done; + } + + mError = mStrings.getError(); + +done: + restart(); + return mError; +} + +status_t ResXMLTree::getError() const +{ + return mError; +} + +void ResXMLTree::uninit() +{ + mError = NO_INIT; + if (mOwnedData) { + free(mOwnedData); + mOwnedData = NULL; + } + restart(); +} + +const ResStringPool& ResXMLTree::getStrings() const +{ + return mStrings; +} + +status_t ResXMLTree::validateNode(const ResXMLTree_node* node) const +{ + const uint16_t eventCode = dtohs(node->header.type); + + status_t err = validate_chunk( + &node->header, sizeof(ResXMLTree_node), + mDataEnd, "ResXMLTree_node"); + + if (err >= NO_ERROR) { + // Only perform additional validation on START nodes + if (eventCode != RES_XML_START_ELEMENT_TYPE) { + return NO_ERROR; + } + + const uint16_t headerSize = dtohs(node->header.headerSize); + const uint32_t size = dtohl(node->header.size); + const ResXMLTree_attrExt* attrExt = (const ResXMLTree_attrExt*) + (((const uint8_t*)node) + headerSize); + // check for sensical values pulled out of the stream so far... + if ((size >= headerSize + sizeof(ResXMLTree_attrExt)) + && ((void*)attrExt > (void*)node)) { + const size_t attrSize = ((size_t)dtohs(attrExt->attributeSize)) + * dtohs(attrExt->attributeCount); + if ((dtohs(attrExt->attributeStart)+attrSize) <= (size-headerSize)) { + return NO_ERROR; + } + LOGW("Bad XML block: node attributes use 0x%x bytes, only have 0x%x bytes\n", + (unsigned int)(dtohs(attrExt->attributeStart)+attrSize), + (unsigned int)(size-headerSize)); + } + else { + LOGW("Bad XML start block: node header size 0x%x, size 0x%x\n", + (unsigned int)headerSize, (unsigned int)size); + } + return BAD_TYPE; + } + + return err; + +#if 0 + const bool isStart = dtohs(node->header.type) == RES_XML_START_ELEMENT_TYPE; + + const uint16_t headerSize = dtohs(node->header.headerSize); + const uint32_t size = dtohl(node->header.size); + + if (headerSize >= (isStart ? sizeof(ResXMLTree_attrNode) : sizeof(ResXMLTree_node))) { + if (size >= headerSize) { + if (((const uint8_t*)node) <= (mDataEnd-size)) { + if (!isStart) { + return NO_ERROR; + } + if ((((size_t)dtohs(node->attributeSize))*dtohs(node->attributeCount)) + <= (size-headerSize)) { + return NO_ERROR; + } + LOGW("Bad XML block: node attributes use 0x%x bytes, only have 0x%x bytes\n", + ((int)dtohs(node->attributeSize))*dtohs(node->attributeCount), + (int)(size-headerSize)); + return BAD_TYPE; + } + LOGW("Bad XML block: node at 0x%x extends beyond data end 0x%x\n", + (int)(((const uint8_t*)node)-((const uint8_t*)mHeader)), (int)mSize); + return BAD_TYPE; + } + LOGW("Bad XML block: node at 0x%x header size 0x%x smaller than total size 0x%x\n", + (int)(((const uint8_t*)node)-((const uint8_t*)mHeader)), + (int)headerSize, (int)size); + return BAD_TYPE; + } + LOGW("Bad XML block: node at 0x%x header size 0x%x too small\n", + (int)(((const uint8_t*)node)-((const uint8_t*)mHeader)), + (int)headerSize); + return BAD_TYPE; +#endif +} + +// -------------------------------------------------------------------- +// -------------------------------------------------------------------- +// -------------------------------------------------------------------- + +struct ResTable::Header +{ + Header() : ownedData(NULL), header(NULL) { } + + void* ownedData; + const ResTable_header* header; + size_t size; + const uint8_t* dataEnd; + size_t index; + void* cookie; + + ResStringPool values; +}; + +struct ResTable::Type +{ + Type(const Header* _header, const Package* _package, size_t count) + : header(_header), package(_package), entryCount(count), + typeSpec(NULL), typeSpecFlags(NULL) { } + const Header* const header; + const Package* const package; + const size_t entryCount; + const ResTable_typeSpec* typeSpec; + const uint32_t* typeSpecFlags; + Vector configs; +}; + +struct ResTable::Package +{ + Package(const Header* _header, const ResTable_package* _package) + : header(_header), package(_package) { } + ~Package() + { + size_t i = types.size(); + while (i > 0) { + i--; + delete types[i]; + } + } + + const Header* const header; + const ResTable_package* const package; + Vector types; + + const Type* getType(size_t idx) const { + return idx < types.size() ? types[idx] : NULL; + } +}; + +// A group of objects describing a particular resource package. +// The first in 'package' is always the root object (from the resource +// table that defined the package); the ones after are skins on top of it. +struct ResTable::PackageGroup +{ + PackageGroup(const String16& _name, uint32_t _id) + : name(_name), id(_id), typeCount(0), bags(NULL) { } + ~PackageGroup() { + clearBagCache(); + const size_t N = packages.size(); + for (size_t i=0; igetType(i); + if (type != NULL) { + bag_set** typeBags = bags[i]; + TABLE_NOISY(printf("typeBags=%p\n", typeBags)); + if (typeBags) { + TABLE_NOISY(printf("type->entryCount=%x\n", type->entryCount)); + const size_t N = type->entryCount; + for (size_t j=0; j packages; + + // Taken from the root package. + ResStringPool typeStrings; + ResStringPool keyStrings; + size_t typeCount; + + // Computed attribute bags, first indexed by the type and second + // by the entry in that type. + bag_set*** bags; +}; + +struct ResTable::bag_set +{ + size_t numAttrs; // number in array + size_t availAttrs; // total space in array + uint32_t typeSpecFlags; + // Followed by 'numAttr' bag_entry structures. +}; + +ResTable::Theme::Theme(const ResTable& table) + : mTable(table) +{ + memset(mPackages, 0, sizeof(mPackages)); +} + +ResTable::Theme::~Theme() +{ + for (size_t i=0; inumTypes; j++) { + theme_entry* te = pi->types[j].entries; + if (te != NULL) { + free(te); + } + } + free(pi); +} + +ResTable::Theme::package_info* ResTable::Theme::copy_package(package_info* pi) +{ + package_info* newpi = (package_info*)malloc( + sizeof(package_info) + (pi->numTypes*sizeof(type_info))); + newpi->numTypes = pi->numTypes; + for (size_t j=0; jnumTypes; j++) { + size_t cnt = pi->types[j].numEntries; + newpi->types[j].numEntries = cnt; + theme_entry* te = pi->types[j].entries; + if (te != NULL) { + theme_entry* newte = (theme_entry*)malloc(cnt*sizeof(theme_entry)); + newpi->types[j].entries = newte; + memcpy(newte, te, cnt*sizeof(theme_entry)); + } else { + newpi->types[j].entries = NULL; + } + } + return newpi; +} + +status_t ResTable::Theme::applyStyle(uint32_t resID, bool force) +{ + const bag_entry* bag; + uint32_t bagTypeSpecFlags = 0; + mTable.lock(); + const ssize_t N = mTable.getBagLocked(resID, &bag, &bagTypeSpecFlags); + TABLE_NOISY(LOGV("Applying style 0x%08x to theme %p, count=%d", resID, this, N)); + if (N < 0) { + mTable.unlock(); + return N; + } + + uint32_t curPackage = 0xffffffff; + ssize_t curPackageIndex = 0; + package_info* curPI = NULL; + uint32_t curType = 0xffffffff; + size_t numEntries = 0; + theme_entry* curEntries = NULL; + + const bag_entry* end = bag + N; + while (bag < end) { + const uint32_t attrRes = bag->map.name.ident; + const uint32_t p = Res_GETPACKAGE(attrRes); + const uint32_t t = Res_GETTYPE(attrRes); + const uint32_t e = Res_GETENTRY(attrRes); + + if (curPackage != p) { + const ssize_t pidx = mTable.getResourcePackageIndex(attrRes); + if (pidx < 0) { + LOGE("Style contains key with bad package: 0x%08x\n", attrRes); + bag++; + continue; + } + curPackage = p; + curPackageIndex = pidx; + curPI = mPackages[pidx]; + if (curPI == NULL) { + PackageGroup* const grp = mTable.mPackageGroups[pidx]; + int cnt = grp->typeCount; + curPI = (package_info*)malloc( + sizeof(package_info) + (cnt*sizeof(type_info))); + curPI->numTypes = cnt; + memset(curPI->types, 0, cnt*sizeof(type_info)); + mPackages[pidx] = curPI; + } + curType = 0xffffffff; + } + if (curType != t) { + if (t >= curPI->numTypes) { + LOGE("Style contains key with bad type: 0x%08x\n", attrRes); + bag++; + continue; + } + curType = t; + curEntries = curPI->types[t].entries; + if (curEntries == NULL) { + PackageGroup* const grp = mTable.mPackageGroups[curPackageIndex]; + const Type* type = grp->packages[0]->getType(t); + int cnt = type != NULL ? type->entryCount : 0; + curEntries = (theme_entry*)malloc(cnt*sizeof(theme_entry)); + memset(curEntries, Res_value::TYPE_NULL, cnt*sizeof(theme_entry)); + curPI->types[t].numEntries = cnt; + curPI->types[t].entries = curEntries; + } + numEntries = curPI->types[t].numEntries; + } + if (e >= numEntries) { + LOGE("Style contains key with bad entry: 0x%08x\n", attrRes); + bag++; + continue; + } + theme_entry* curEntry = curEntries + e; + TABLE_NOISY(LOGV("Attr 0x%08x: type=0x%x, data=0x%08x; curType=0x%x", + attrRes, bag->map.value.dataType, bag->map.value.data, + curEntry->value.dataType)); + if (force || curEntry->value.dataType == Res_value::TYPE_NULL) { + curEntry->stringBlock = bag->stringBlock; + curEntry->typeSpecFlags |= bagTypeSpecFlags; + curEntry->value = bag->map.value; + } + + bag++; + } + + mTable.unlock(); + + //LOGI("Applying style 0x%08x (force=%d) theme %p...\n", resID, force, this); + //dumpToLog(); + + return NO_ERROR; +} + +status_t ResTable::Theme::setTo(const Theme& other) +{ + //LOGI("Setting theme %p from theme %p...\n", this, &other); + //dumpToLog(); + //other.dumpToLog(); + + if (&mTable == &other.mTable) { + for (size_t i=0; i= 0) { + const package_info* const pi = mPackages[p]; + if (pi != NULL) { + if (t < pi->numTypes) { + const type_info& ti = pi->types[t]; + if (e < ti.numEntries) { + const theme_entry& te = ti.entries[e]; + if (outTypeSpecFlags != NULL) { + *outTypeSpecFlags |= te.typeSpecFlags; + } + const uint8_t type = te.value.dataType; + if (type == Res_value::TYPE_ATTRIBUTE) { + if (cnt > 0) { + cnt--; + resID = te.value.data; + continue; + } + LOGW("Too many attribute references, stopped at: 0x%08x\n", resID); + return BAD_INDEX; + } else if (type != Res_value::TYPE_NULL) { + *outValue = te.value; + return te.stringBlock; + } + return BAD_INDEX; + } + } + } + } + break; + + } while (true); + + return BAD_INDEX; +} + +ssize_t ResTable::Theme::resolveAttributeReference(Res_value* inOutValue, + ssize_t blockIndex, uint32_t* outLastRef, + uint32_t* inoutTypeSpecFlags) const +{ + //printf("Resolving type=0x%x\n", inOutValue->dataType); + if (inOutValue->dataType == Res_value::TYPE_ATTRIBUTE) { + uint32_t newTypeSpecFlags; + blockIndex = getAttribute(inOutValue->data, inOutValue, &newTypeSpecFlags); + if (inoutTypeSpecFlags != NULL) *inoutTypeSpecFlags |= newTypeSpecFlags; + //printf("Retrieved attribute new type=0x%x\n", inOutValue->dataType); + if (blockIndex < 0) { + return blockIndex; + } + } + return mTable.resolveReference(inOutValue, blockIndex, outLastRef); +} + +void ResTable::Theme::dumpToLog() const +{ + LOGI("Theme %p:\n", this); + for (size_t i=0; inumTypes; j++) { + type_info& ti = pi->types[j]; + if (ti.numEntries == 0) continue; + + LOGI(" Type #0x%02x:\n", (int)(j+1)); + for (size_t k=0; kgetBuffer(true); + if (data == NULL) { + LOGW("Unable to get buffer of resource asset file"); + return UNKNOWN_ERROR; + } + size_t size = (size_t)asset->getLength(); + return add(data, size, cookie, asset, copyData); +} + +status_t ResTable::add(const void* data, size_t size, void* cookie, + Asset* asset, bool copyData) +{ + if (!data) return NO_ERROR; + Header* header = new Header; + header->index = mHeaders.size(); + header->cookie = cookie; + mHeaders.add(header); + + const bool notDeviceEndian = htods(0xf0) != 0xf0; + + LOAD_TABLE_NOISY( + LOGV("Adding resources to ResTable: data=%p, size=0x%x, cookie=%p, asset=%p, copy=%d\n", + data, size, cookie, asset, copyData)); + + if (copyData || notDeviceEndian) { + header->ownedData = malloc(size); + if (header->ownedData == NULL) { + return (mError=NO_MEMORY); + } + memcpy(header->ownedData, data, size); + data = header->ownedData; + } + + header->header = (const ResTable_header*)data; + header->size = dtohl(header->header->header.size); + //LOGI("Got size 0x%x, again size 0x%x, raw size 0x%x\n", header->size, + // dtohl(header->header->header.size), header->header->header.size); + LOAD_TABLE_NOISY(LOGV("Loading ResTable @%p:\n", header->header)); + LOAD_TABLE_NOISY(printHexData(2, header->header, header->size < 256 ? header->size : 256, + 16, 16, 0, false, printToLogFunc)); + if (dtohs(header->header->header.headerSize) > header->size + || header->size > size) { + LOGW("Bad resource table: header size 0x%x or total size 0x%x is larger than data size 0x%x\n", + (int)dtohs(header->header->header.headerSize), + (int)header->size, (int)size); + return (mError=BAD_TYPE); + } + if (((dtohs(header->header->header.headerSize)|header->size)&0x3) != 0) { + LOGW("Bad resource table: header size 0x%x or total size 0x%x is not on an integer boundary\n", + (int)dtohs(header->header->header.headerSize), + (int)header->size); + return (mError=BAD_TYPE); + } + header->dataEnd = ((const uint8_t*)header->header) + header->size; + + // Iterate through all chunks. + size_t curPackage = 0; + + const ResChunk_header* chunk = + (const ResChunk_header*)(((const uint8_t*)header->header) + + dtohs(header->header->header.headerSize)); + while (((const uint8_t*)chunk) <= (header->dataEnd-sizeof(ResChunk_header)) && + ((const uint8_t*)chunk) <= (header->dataEnd-dtohl(chunk->size))) { + status_t err = validate_chunk(chunk, sizeof(ResChunk_header), header->dataEnd, "ResTable"); + if (err != NO_ERROR) { + return (mError=err); + } + TABLE_NOISY(LOGV("Chunk: type=0x%x, headerSize=0x%x, size=0x%x, pos=%p\n", + dtohs(chunk->type), dtohs(chunk->headerSize), dtohl(chunk->size), + (void*)(((const uint8_t*)chunk) - ((const uint8_t*)header->header)))); + const size_t csize = dtohl(chunk->size); + const uint16_t ctype = dtohs(chunk->type); + if (ctype == RES_STRING_POOL_TYPE) { + if (header->values.getError() != NO_ERROR) { + // Only use the first string chunk; ignore any others that + // may appear. + status_t err = header->values.setTo(chunk, csize); + if (err != NO_ERROR) { + return (mError=err); + } + } else { + LOGW("Multiple string chunks found in resource table."); + } + } else if (ctype == RES_TABLE_PACKAGE_TYPE) { + if (curPackage >= dtohl(header->header->packageCount)) { + LOGW("More package chunks were found than the %d declared in the header.", + dtohl(header->header->packageCount)); + return (mError=BAD_TYPE); + } + if (parsePackage((ResTable_package*)chunk, header) != NO_ERROR) { + return mError; + } + curPackage++; + } else { + LOGW("Unknown chunk type %p in table at %p.\n", + (void*)(int)(ctype), + (void*)(((const uint8_t*)chunk) - ((const uint8_t*)header->header))); + } + chunk = (const ResChunk_header*) + (((const uint8_t*)chunk) + csize); + } + + if (curPackage < dtohl(header->header->packageCount)) { + LOGW("Fewer package chunks (%d) were found than the %d declared in the header.", + (int)curPackage, dtohl(header->header->packageCount)); + return (mError=BAD_TYPE); + } + mError = header->values.getError(); + if (mError != NO_ERROR) { + LOGW("No string values found in resource table!"); + } + TABLE_NOISY(LOGV("Returning from add with mError=%d\n", mError)); + return mError; +} + +status_t ResTable::getError() const +{ + return mError; +} + +void ResTable::uninit() +{ + mError = NO_INIT; + size_t N = mPackageGroups.size(); + for (size_t i=0; iownedData) { + free(header->ownedData); + } + delete header; + } + + mPackageGroups.clear(); + mHeaders.clear(); +} + +bool ResTable::getResourceName(uint32_t resID, resource_name* outName) const +{ + if (mError != NO_ERROR) { + return false; + } + + const ssize_t p = getResourcePackageIndex(resID); + const int t = Res_GETTYPE(resID); + const int e = Res_GETENTRY(resID); + + if (p < 0) { + LOGW("No package identifier when getting name for resource number 0x%08x", resID); + return false; + } + if (t < 0) { + LOGW("No type identifier when getting name for resource number 0x%08x", resID); + return false; + } + + const PackageGroup* const grp = mPackageGroups[p]; + if (grp == NULL) { + LOGW("Bad identifier when getting name for resource number 0x%08x", resID); + return false; + } + if (grp->packages.size() > 0) { + const Package* const package = grp->packages[0]; + + const ResTable_type* type; + const ResTable_entry* entry; + ssize_t offset = getEntry(package, t, e, NULL, &type, &entry, NULL); + if (offset <= 0) { + return false; + } + + outName->package = grp->name.string(); + outName->packageLen = grp->name.size(); + outName->type = grp->typeStrings.stringAt(t, &outName->typeLen); + outName->name = grp->keyStrings.stringAt( + dtohl(entry->key.index), &outName->nameLen); + return true; + } + + return false; +} + +ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag, + uint32_t* outSpecFlags, ResTable_config* outConfig) const +{ + if (mError != NO_ERROR) { + return mError; + } + + const ssize_t p = getResourcePackageIndex(resID); + const int t = Res_GETTYPE(resID); + const int e = Res_GETENTRY(resID); + + if (p < 0) { + LOGW("No package identifier when getting value for resource number 0x%08x", resID); + return BAD_INDEX; + } + if (t < 0) { + LOGW("No type identifier when getting value for resource number 0x%08x", resID); + return BAD_INDEX; + } + + const Res_value* bestValue = NULL; + const Package* bestPackage = NULL; + ResTable_config bestItem; + memset(&bestItem, 0, sizeof(bestItem)); // make the compiler shut up + + if (outSpecFlags != NULL) *outSpecFlags = 0; + + // Look through all resource packages, starting with the most + // recently added. + const PackageGroup* const grp = mPackageGroups[p]; + if (grp == NULL) { + LOGW("Bad identifier when getting value for resource number 0x%08x", resID); + return false; + } + size_t ip = grp->packages.size(); + while (ip > 0) { + ip--; + + const Package* const package = grp->packages[ip]; + + const ResTable_type* type; + const ResTable_entry* entry; + const Type* typeClass; + ssize_t offset = getEntry(package, t, e, &mParams, &type, &entry, &typeClass); + if (offset <= 0) { + if (offset < 0) { + LOGW("Failure getting entry for 0x%08x (t=%d e=%d) in package %d: 0x%08x\n", + resID, t, e, (int)ip, (int)offset); + return offset; + } + continue; + } + + if ((dtohs(entry->flags)&entry->FLAG_COMPLEX) != 0) { + if (!mayBeBag) { + LOGW("Requesting resource %p failed because it is complex\n", + (void*)resID); + } + continue; + } + + TABLE_NOISY(aout << "Resource type data: " + << HexDump(type, dtohl(type->header.size)) << endl); + + if ((size_t)offset > (dtohl(type->header.size)-sizeof(Res_value))) { + LOGW("ResTable_item at %d is beyond type chunk data %d", + (int)offset, dtohl(type->header.size)); + return BAD_TYPE; + } + + const Res_value* item = + (const Res_value*)(((const uint8_t*)type) + offset); + ResTable_config thisConfig; + thisConfig.copyFromDtoH(type->config); + + if (outSpecFlags != NULL) { + if (typeClass->typeSpecFlags != NULL) { + *outSpecFlags |= dtohl(typeClass->typeSpecFlags[e]); + } else { + *outSpecFlags = -1; + } + } + + if (bestPackage != NULL && bestItem.isBetterThan(thisConfig)) { + continue; + } + + bestItem = thisConfig; + bestValue = item; + bestPackage = package; + } + + TABLE_NOISY(printf("Found result: package %p\n", bestPackage)); + + if (bestValue) { + outValue->size = dtohs(bestValue->size); + outValue->res0 = bestValue->res0; + outValue->dataType = bestValue->dataType; + outValue->data = dtohl(bestValue->data); + if (outConfig != NULL) { + *outConfig = bestItem; + } + TABLE_NOISY(size_t len; + printf("Found value: pkg=%d, type=%d, str=%s, int=%d\n", + bestPackage->header->index, + outValue->dataType, + outValue->dataType == bestValue->TYPE_STRING + ? String8(bestPackage->header->values.stringAt( + outValue->data, &len)).string() + : "", + outValue->data)); + return bestPackage->header->index; + } + + return BAD_INDEX; +} + +ssize_t ResTable::resolveReference(Res_value* value, ssize_t blockIndex, + uint32_t* outLastRef, uint32_t* inoutTypeSpecFlags) const +{ + int count=0; + while (blockIndex >= 0 && value->dataType == value->TYPE_REFERENCE + && value->data != 0 && count < 20) { + if (outLastRef) *outLastRef = value->data; + uint32_t lastRef = value->data; + uint32_t newFlags = 0; + const ssize_t newIndex = getResource(value->data, value, true, &newFlags); + //LOGI("Resolving reference d=%p: newIndex=%d, t=0x%02x, d=%p\n", + // (void*)lastRef, (int)newIndex, (int)value->dataType, (void*)value->data); + //printf("Getting reference 0x%08x: newIndex=%d\n", value->data, newIndex); + if (inoutTypeSpecFlags != NULL) *inoutTypeSpecFlags |= newFlags; + if (newIndex < 0) { + // This can fail if the resource being referenced is a style... + // in this case, just return the reference, and expect the + // caller to deal with. + return blockIndex; + } + blockIndex = newIndex; + count++; + } + return blockIndex; +} + +const char16_t* ResTable::valueToString( + const Res_value* value, size_t stringBlock, + char16_t tmpBuffer[TMP_BUFFER_SIZE], size_t* outLen) +{ + if (!value) { + return NULL; + } + if (value->dataType == value->TYPE_STRING) { + return getTableStringBlock(stringBlock)->stringAt(value->data, outLen); + } + // XXX do int to string conversions. + return NULL; +} + +ssize_t ResTable::lockBag(uint32_t resID, const bag_entry** outBag) const +{ + mLock.lock(); + ssize_t err = getBagLocked(resID, outBag); + if (err < NO_ERROR) { + //printf("*** get failed! unlocking\n"); + mLock.unlock(); + } + return err; +} + +void ResTable::unlockBag(const bag_entry* bag) const +{ + //printf("<<< unlockBag %p\n", this); + mLock.unlock(); +} + +void ResTable::lock() const +{ + mLock.lock(); +} + +void ResTable::unlock() const +{ + mLock.unlock(); +} + +ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag, + uint32_t* outTypeSpecFlags) const +{ + if (mError != NO_ERROR) { + return mError; + } + + const ssize_t p = getResourcePackageIndex(resID); + const int t = Res_GETTYPE(resID); + const int e = Res_GETENTRY(resID); + + if (p < 0) { + LOGW("Invalid package identifier when getting bag for resource number 0x%08x", resID); + return BAD_INDEX; + } + if (t < 0) { + LOGW("No type identifier when getting bag for resource number 0x%08x", resID); + return BAD_INDEX; + } + + //printf("Get bag: id=0x%08x, p=%d, t=%d\n", resID, p, t); + PackageGroup* const grp = mPackageGroups[p]; + if (grp == NULL) { + LOGW("Bad identifier when getting bag for resource number 0x%08x", resID); + return false; + } + + if (t >= (int)grp->typeCount) { + LOGW("Type identifier 0x%x is larger than type count 0x%x", + t+1, (int)grp->typeCount); + return BAD_INDEX; + } + + const Package* const basePackage = grp->packages[0]; + + const Type* const typeConfigs = basePackage->getType(t); + + const size_t NENTRY = typeConfigs->entryCount; + if (e >= (int)NENTRY) { + LOGW("Entry identifier 0x%x is larger than entry count 0x%x", + e, (int)typeConfigs->entryCount); + return BAD_INDEX; + } + + // First see if we've already computed this bag... + if (grp->bags) { + bag_set** typeSet = grp->bags[t]; + if (typeSet) { + bag_set* set = typeSet[e]; + if (set) { + if (set != (bag_set*)0xFFFFFFFF) { + if (outTypeSpecFlags != NULL) { + *outTypeSpecFlags = set->typeSpecFlags; + } + *outBag = (bag_entry*)(set+1); + //LOGI("Found existing bag for: %p\n", (void*)resID); + return set->numAttrs; + } + LOGW("Attempt to retrieve bag 0x%08x which is invalid or in a cycle.", + resID); + return BAD_INDEX; + } + } + } + + // Bag not found, we need to compute it! + if (!grp->bags) { + grp->bags = (bag_set***)malloc(sizeof(bag_set*)*grp->typeCount); + if (!grp->bags) return NO_MEMORY; + memset(grp->bags, 0, sizeof(bag_set*)*grp->typeCount); + } + + bag_set** typeSet = grp->bags[t]; + if (!typeSet) { + typeSet = (bag_set**)malloc(sizeof(bag_set*)*NENTRY); + if (!typeSet) return NO_MEMORY; + memset(typeSet, 0, sizeof(bag_set*)*NENTRY); + grp->bags[t] = typeSet; + } + + // Mark that we are currently working on this one. + typeSet[e] = (bag_set*)0xFFFFFFFF; + + // This is what we are building. + bag_set* set = NULL; + + TABLE_NOISY(LOGI("Building bag: %p\n", (void*)resID)); + + // Now collect all bag attributes from all packages. + size_t ip = grp->packages.size(); + while (ip > 0) { + ip--; + + const Package* const package = grp->packages[ip]; + + const ResTable_type* type; + const ResTable_entry* entry; + const Type* typeClass; + LOGV("Getting entry pkg=%p, t=%d, e=%d\n", package, t, e); + ssize_t offset = getEntry(package, t, e, &mParams, &type, &entry, &typeClass); + LOGV("Resulting offset=%d\n", offset); + if (offset <= 0) { + if (offset < 0) { + if (set) free(set); + return offset; + } + continue; + } + + if ((dtohs(entry->flags)&entry->FLAG_COMPLEX) == 0) { + LOGW("Skipping entry %p in package table %d because it is not complex!\n", + (void*)resID, (int)ip); + continue; + } + + const uint16_t entrySize = dtohs(entry->size); + const uint32_t parent = entrySize >= sizeof(ResTable_map_entry) + ? dtohl(((const ResTable_map_entry*)entry)->parent.ident) : 0; + const uint32_t count = entrySize >= sizeof(ResTable_map_entry) + ? dtohl(((const ResTable_map_entry*)entry)->count) : 0; + + size_t N = count; + + TABLE_NOISY(LOGI("Found map: size=%p parent=%p count=%d\n", + entrySize, parent, count)); + + if (set == NULL) { + // If this map inherits from another, we need to start + // with its parent's values. Otherwise start out empty. + TABLE_NOISY(printf("Creating new bag, entrySize=0x%08x, parent=0x%08x\n", + entrySize, parent)); + if (parent) { + const bag_entry* parentBag; + uint32_t parentTypeSpecFlags = 0; + const ssize_t NP = getBagLocked(parent, &parentBag, &parentTypeSpecFlags); + const size_t NT = ((NP >= 0) ? NP : 0) + N; + set = (bag_set*)malloc(sizeof(bag_set)+sizeof(bag_entry)*NT); + if (set == NULL) { + return NO_MEMORY; + } + if (NP > 0) { + memcpy(set+1, parentBag, NP*sizeof(bag_entry)); + set->numAttrs = NP; + TABLE_NOISY(LOGI("Initialized new bag with %d inherited attributes.\n", NP)); + } else { + TABLE_NOISY(LOGI("Initialized new bag with no inherited attributes.\n")); + set->numAttrs = 0; + } + set->availAttrs = NT; + set->typeSpecFlags = parentTypeSpecFlags; + } else { + set = (bag_set*)malloc(sizeof(bag_set)+sizeof(bag_entry)*N); + if (set == NULL) { + return NO_MEMORY; + } + set->numAttrs = 0; + set->availAttrs = N; + set->typeSpecFlags = 0; + } + } + + if (typeClass->typeSpecFlags != NULL) { + set->typeSpecFlags |= dtohl(typeClass->typeSpecFlags[e]); + } else { + set->typeSpecFlags = -1; + } + + // Now merge in the new attributes... + ssize_t curOff = offset; + const ResTable_map* map; + bag_entry* entries = (bag_entry*)(set+1); + size_t curEntry = 0; + uint32_t pos = 0; + TABLE_NOISY(LOGI("Starting with set %p, entries=%p, avail=%d\n", + set, entries, set->availAttrs)); + while (pos < count) { + TABLE_NOISY(printf("Now at %p\n", (void*)curOff)); + + if ((size_t)curOff > (dtohl(type->header.size)-sizeof(ResTable_map))) { + LOGW("ResTable_map at %d is beyond type chunk data %d", + (int)curOff, dtohl(type->header.size)); + return BAD_TYPE; + } + map = (const ResTable_map*)(((const uint8_t*)type) + curOff); + N++; + + const uint32_t newName = htodl(map->name.ident); + bool isInside; + uint32_t oldName = 0; + while ((isInside=(curEntry < set->numAttrs)) + && (oldName=entries[curEntry].map.name.ident) < newName) { + TABLE_NOISY(printf("#%d: Keeping existing attribute: 0x%08x\n", + curEntry, entries[curEntry].map.name.ident)); + curEntry++; + } + + if ((!isInside) || oldName != newName) { + // This is a new attribute... figure out what to do with it. + if (set->numAttrs >= set->availAttrs) { + // Need to alloc more memory... + const size_t newAvail = set->availAttrs+N; + set = (bag_set*)realloc(set, + sizeof(bag_set) + + sizeof(bag_entry)*newAvail); + if (set == NULL) { + return NO_MEMORY; + } + set->availAttrs = newAvail; + entries = (bag_entry*)(set+1); + TABLE_NOISY(printf("Reallocated set %p, entries=%p, avail=%d\n", + set, entries, set->availAttrs)); + } + if (isInside) { + // Going in the middle, need to make space. + memmove(entries+curEntry+1, entries+curEntry, + sizeof(bag_entry)*(set->numAttrs-curEntry)); + set->numAttrs++; + } + TABLE_NOISY(printf("#%d: Inserting new attribute: 0x%08x\n", + curEntry, newName)); + } else { + TABLE_NOISY(printf("#%d: Replacing existing attribute: 0x%08x\n", + curEntry, oldName)); + } + + bag_entry* cur = entries+curEntry; + + cur->stringBlock = package->header->index; + cur->map.name.ident = newName; + cur->map.value.copyFrom_dtoh(map->value); + TABLE_NOISY(printf("Setting entry #%d %p: block=%d, name=0x%08x, type=%d, data=0x%08x\n", + curEntry, cur, cur->stringBlock, cur->map.name.ident, + cur->map.value.dataType, cur->map.value.data)); + + // On to the next! + curEntry++; + pos++; + const size_t size = dtohs(map->value.size); + curOff += size + sizeof(*map)-sizeof(map->value); + }; + if (curEntry > set->numAttrs) { + set->numAttrs = curEntry; + } + } + + // And this is it... + typeSet[e] = set; + if (set) { + if (outTypeSpecFlags != NULL) { + *outTypeSpecFlags = set->typeSpecFlags; + } + *outBag = (bag_entry*)(set+1); + TABLE_NOISY(LOGI("Returning %d attrs\n", set->numAttrs)); + return set->numAttrs; + } + return BAD_INDEX; +} + +void ResTable::setParameters(const ResTable_config* params) +{ + mLock.lock(); + TABLE_GETENTRY(LOGI("Setting parameters: imsi:%d/%d lang:%c%c cnt:%c%c " + "orien:%d touch:%d density:%d key:%d inp:%d nav:%d w:%d h:%d\n", + params->mcc, params->mnc, + params->language[0] ? params->language[0] : '-', + params->language[1] ? params->language[1] : '-', + params->country[0] ? params->country[0] : '-', + params->country[1] ? params->country[1] : '-', + params->orientation, + params->touchscreen, + params->density, + params->keyboard, + params->inputFlags, + params->navigation, + params->screenWidth, + params->screenHeight)); + mParams = *params; + for (size_t i=0; iclearBagCache(); + } + mLock.unlock(); +} + +void ResTable::getParameters(ResTable_config* params) const +{ + mLock.lock(); + *params = mParams; + mLock.unlock(); +} + +struct id_name_map { + uint32_t id; + size_t len; + char16_t name[6]; +}; + +const static id_name_map ID_NAMES[] = { + { ResTable_map::ATTR_TYPE, 5, { '^', 't', 'y', 'p', 'e' } }, + { ResTable_map::ATTR_L10N, 5, { '^', 'l', '1', '0', 'n' } }, + { ResTable_map::ATTR_MIN, 4, { '^', 'm', 'i', 'n' } }, + { ResTable_map::ATTR_MAX, 4, { '^', 'm', 'a', 'x' } }, + { ResTable_map::ATTR_OTHER, 6, { '^', 'o', 't', 'h', 'e', 'r' } }, + { ResTable_map::ATTR_ZERO, 5, { '^', 'z', 'e', 'r', 'o' } }, + { ResTable_map::ATTR_ONE, 4, { '^', 'o', 'n', 'e' } }, + { ResTable_map::ATTR_TWO, 4, { '^', 't', 'w', 'o' } }, + { ResTable_map::ATTR_FEW, 4, { '^', 'f', 'e', 'w' } }, + { ResTable_map::ATTR_MANY, 5, { '^', 'm', 'a', 'n', 'y' } }, +}; + +uint32_t ResTable::identifierForName(const char16_t* name, size_t nameLen, + const char16_t* type, size_t typeLen, + const char16_t* package, + size_t packageLen, + uint32_t* outTypeSpecFlags) const +{ + TABLE_SUPER_NOISY(printf("Identifier for name: error=%d\n", mError)); + + // Check for internal resource identifier as the very first thing, so + // that we will always find them even when there are no resources. + if (name[0] == '^') { + const int N = (sizeof(ID_NAMES)/sizeof(ID_NAMES[0])); + size_t len; + for (int i=0; ilen; + if (len != nameLen) { + continue; + } + for (size_t j=1; jname[j] != name[j]) { + goto nope; + } + } + return m->id; +nope: + ; + } + if (nameLen > 7) { + if (name[1] == 'i' && name[2] == 'n' + && name[3] == 'd' && name[4] == 'e' && name[5] == 'x' + && name[6] == '_') { + int index = atoi(String8(name + 7, nameLen - 7).string()); + if (Res_CHECKID(index)) { + LOGW("Array resource index: %d is too large.", + index); + return 0; + } + return Res_MAKEARRAY(index); + } + } + return 0; + } + + if (mError != NO_ERROR) { + return 0; + } + + // Figure out the package and type we are looking in... + + const char16_t* packageEnd = NULL; + const char16_t* typeEnd = NULL; + const char16_t* const nameEnd = name+nameLen; + const char16_t* p = name; + while (p < nameEnd) { + if (*p == ':') packageEnd = p; + else if (*p == '/') typeEnd = p; + p++; + } + if (*name == '@') name++; + if (name >= nameEnd) { + return 0; + } + + if (packageEnd) { + package = name; + packageLen = packageEnd-name; + name = packageEnd+1; + } else if (!package) { + return 0; + } + + if (typeEnd) { + type = name; + typeLen = typeEnd-name; + name = typeEnd+1; + } else if (!type) { + return 0; + } + + if (name >= nameEnd) { + return 0; + } + nameLen = nameEnd-name; + + TABLE_NOISY(printf("Looking for identifier: type=%s, name=%s, package=%s\n", + String8(type, typeLen).string(), + String8(name, nameLen).string(), + String8(package, packageLen).string())); + + const size_t NG = mPackageGroups.size(); + for (size_t ig=0; igname.string(), group->name.size())) { + TABLE_NOISY(printf("Skipping package group: %s\n", String8(group->name).string())); + continue; + } + + const ssize_t ti = group->typeStrings.indexOfString(type, typeLen); + if (ti < 0) { + TABLE_NOISY(printf("Type not found in package %s\n", String8(group->name).string())); + continue; + } + + const ssize_t ei = group->keyStrings.indexOfString(name, nameLen); + if (ei < 0) { + TABLE_NOISY(printf("Name not found in package %s\n", String8(group->name).string())); + continue; + } + + TABLE_NOISY(printf("Search indices: type=%d, name=%d\n", ti, ei)); + + const Type* const typeConfigs = group->packages[0]->getType(ti); + if (typeConfigs == NULL || typeConfigs->configs.size() <= 0) { + TABLE_NOISY(printf("Expected type structure not found in package %s for idnex %d\n", + String8(group->name).string(), ti)); + } + + size_t NTC = typeConfigs->configs.size(); + for (size_t tci=0; tciconfigs[tci]; + const uint32_t typeOffset = dtohl(ty->entriesStart); + + const uint8_t* const end = ((const uint8_t*)ty) + dtohl(ty->header.size); + const uint32_t* const eindex = (const uint32_t*) + (((const uint8_t*)ty) + dtohs(ty->header.headerSize)); + + const size_t NE = dtohl(ty->entryCount); + for (size_t i=0; i (dtohl(ty->header.size)-sizeof(ResTable_entry))) { + LOGW("ResTable_entry at %d is beyond type chunk data %d", + offset, dtohl(ty->header.size)); + return 0; + } + if ((offset&0x3) != 0) { + LOGW("ResTable_entry at %d (pkg=%d type=%d ent=%d) is not on an integer boundary when looking for %s:%s/%s", + (int)offset, (int)group->id, (int)ti+1, (int)i, + String8(package, packageLen).string(), + String8(type, typeLen).string(), + String8(name, nameLen).string()); + return 0; + } + + const ResTable_entry* const entry = (const ResTable_entry*) + (((const uint8_t*)ty) + offset); + if (dtohs(entry->size) < sizeof(*entry)) { + LOGW("ResTable_entry size %d is too small", dtohs(entry->size)); + return BAD_TYPE; + } + + TABLE_SUPER_NOISY(printf("Looking at entry #%d: want str %d, have %d\n", + i, ei, dtohl(entry->key.index))); + if (dtohl(entry->key.index) == (size_t)ei) { + if (outTypeSpecFlags) { + *outTypeSpecFlags = typeConfigs->typeSpecFlags[i]; + } + return Res_MAKEID(group->id-1, ti, i); + } + } + } + } + + return 0; +} + +bool ResTable::expandResourceRef(const uint16_t* refStr, size_t refLen, + String16* outPackage, + String16* outType, + String16* outName, + const String16* defType, + const String16* defPackage, + const char** outErrorMsg) +{ + const char16_t* packageEnd = NULL; + const char16_t* typeEnd = NULL; + const char16_t* p = refStr; + const char16_t* const end = p + refLen; + while (p < end) { + if (*p == ':') packageEnd = p; + else if (*p == '/') { + typeEnd = p; + break; + } + p++; + } + p = refStr; + if (*p == '@') p++; + + if (packageEnd) { + *outPackage = String16(p, packageEnd-p); + p = packageEnd+1; + } else { + if (!defPackage) { + if (outErrorMsg) { + *outErrorMsg = "No resource package specified"; + } + return false; + } + *outPackage = *defPackage; + } + if (typeEnd) { + *outType = String16(p, typeEnd-p); + p = typeEnd+1; + } else { + if (!defType) { + if (outErrorMsg) { + *outErrorMsg = "No resource type specified"; + } + return false; + } + *outType = *defType; + } + *outName = String16(p, end-p); + return true; +} + +static uint32_t get_hex(char c, bool* outError) +{ + if (c >= '0' && c <= '9') { + return c - '0'; + } else if (c >= 'a' && c <= 'f') { + return c - 'a' + 0xa; + } else if (c >= 'A' && c <= 'F') { + return c - 'A' + 0xa; + } + *outError = true; + return 0; +} + +struct unit_entry +{ + const char* name; + size_t len; + uint8_t type; + uint32_t unit; + float scale; +}; + +static const unit_entry unitNames[] = { + { "px", strlen("px"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_PX, 1.0f }, + { "dip", strlen("dip"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_DIP, 1.0f }, + { "dp", strlen("dp"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_DIP, 1.0f }, + { "sp", strlen("sp"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_SP, 1.0f }, + { "pt", strlen("pt"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_PT, 1.0f }, + { "in", strlen("in"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_IN, 1.0f }, + { "mm", strlen("mm"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_MM, 1.0f }, + { "%", strlen("%"), Res_value::TYPE_FRACTION, Res_value::COMPLEX_UNIT_FRACTION, 1.0f/100 }, + { "%p", strlen("%p"), Res_value::TYPE_FRACTION, Res_value::COMPLEX_UNIT_FRACTION_PARENT, 1.0f/100 }, + { NULL, 0, 0, 0, 0 } +}; + +static bool parse_unit(const char* str, Res_value* outValue, + float* outScale, const char** outEnd) +{ + const char* end = str; + while (*end != 0 && !isspace((unsigned char)*end)) { + end++; + } + const size_t len = end-str; + + const char* realEnd = end; + while (*realEnd != 0 && isspace((unsigned char)*realEnd)) { + realEnd++; + } + if (*realEnd != 0) { + return false; + } + + const unit_entry* cur = unitNames; + while (cur->name) { + if (len == cur->len && strncmp(cur->name, str, len) == 0) { + outValue->dataType = cur->type; + outValue->data = cur->unit << Res_value::COMPLEX_UNIT_SHIFT; + *outScale = cur->scale; + *outEnd = end; + //printf("Found unit %s for %s\n", cur->name, str); + return true; + } + cur++; + } + + return false; +} + + +bool ResTable::stringToInt(const char16_t* s, size_t len, Res_value* outValue) +{ + while (len > 0 && isspace16(*s)) { + s++; + len--; + } + + if (len <= 0) { + return false; + } + + size_t i = 0; + int32_t val = 0; + bool neg = false; + + if (*s == '-') { + neg = true; + i++; + } + + if (s[i] < '0' || s[i] > '9') { + return false; + } + + // Decimal or hex? + if (s[i] == '0' && s[i+1] == 'x') { + if (outValue) + outValue->dataType = outValue->TYPE_INT_HEX; + i += 2; + bool error = false; + while (i < len && !error) { + val = (val*16) + get_hex(s[i], &error); + i++; + } + if (error) { + return false; + } + } else { + if (outValue) + outValue->dataType = outValue->TYPE_INT_DEC; + while (i < len) { + if (s[i] < '0' || s[i] > '9') { + return false; + } + val = (val*10) + s[i]-'0'; + i++; + } + } + + if (neg) val = -val; + + while (i < len && isspace16(s[i])) { + i++; + } + + if (i == len) { + if (outValue) + outValue->data = val; + return true; + } + + return false; +} + +bool ResTable::stringToFloat(const char16_t* s, size_t len, Res_value* outValue) +{ + while (len > 0 && isspace16(*s)) { + s++; + len--; + } + + if (len <= 0) { + return false; + } + + char buf[128]; + int i=0; + while (len > 0 && *s != 0 && i < 126) { + if (*s > 255) { + return false; + } + buf[i++] = *s++; + len--; + } + + if (len > 0) { + return false; + } + if (buf[0] < '0' && buf[0] > '9' && buf[0] != '.') { + return false; + } + + buf[i] = 0; + const char* end; + float f = strtof(buf, (char**)&end); + + if (*end != 0 && !isspace((unsigned char)*end)) { + // Might be a unit... + float scale; + if (parse_unit(end, outValue, &scale, &end)) { + f *= scale; + const bool neg = f < 0; + if (neg) f = -f; + uint64_t bits = (uint64_t)(f*(1<<23)+.5f); + uint32_t radix; + uint32_t shift; + if ((bits&0x7fffff) == 0) { + // Always use 23p0 if there is no fraction, just to make + // things easier to read. + radix = Res_value::COMPLEX_RADIX_23p0; + shift = 23; + } else if ((bits&0xffffffffff800000LL) == 0) { + // Magnitude is zero -- can fit in 0 bits of precision. + radix = Res_value::COMPLEX_RADIX_0p23; + shift = 0; + } else if ((bits&0xffffffff80000000LL) == 0) { + // Magnitude can fit in 8 bits of precision. + radix = Res_value::COMPLEX_RADIX_8p15; + shift = 8; + } else if ((bits&0xffffff8000000000LL) == 0) { + // Magnitude can fit in 16 bits of precision. + radix = Res_value::COMPLEX_RADIX_16p7; + shift = 16; + } else { + // Magnitude needs entire range, so no fractional part. + radix = Res_value::COMPLEX_RADIX_23p0; + shift = 23; + } + int32_t mantissa = (int32_t)( + (bits>>shift) & Res_value::COMPLEX_MANTISSA_MASK); + if (neg) { + mantissa = (-mantissa) & Res_value::COMPLEX_MANTISSA_MASK; + } + outValue->data |= + (radix<data); + return true; + } + return false; + } + + while (*end != 0 && isspace((unsigned char)*end)) { + end++; + } + + if (*end == 0) { + if (outValue) { + outValue->dataType = outValue->TYPE_FLOAT; + *(float*)(&outValue->data) = f; + return true; + } + } + + return false; +} + +bool ResTable::stringToValue(Res_value* outValue, String16* outString, + const char16_t* s, size_t len, + bool preserveSpaces, bool coerceType, + uint32_t attrID, + const String16* defType, + const String16* defPackage, + Accessor* accessor, + void* accessorCookie, + uint32_t attrType, + bool enforcePrivate) const +{ + bool localizationSetting = accessor != NULL && accessor->getLocalizationSetting(); + const char* errorMsg = NULL; + + outValue->size = sizeof(Res_value); + outValue->res0 = 0; + + // First strip leading/trailing whitespace. Do this before handling + // escapes, so they can be used to force whitespace into the string. + if (!preserveSpaces) { + while (len > 0 && isspace16(*s)) { + s++; + len--; + } + while (len > 0 && isspace16(s[len-1])) { + len--; + } + // If the string ends with '\', then we keep the space after it. + if (len > 0 && s[len-1] == '\\' && s[len] != 0) { + len++; + } + } + + //printf("Value for: %s\n", String8(s, len).string()); + + uint32_t l10nReq = ResTable_map::L10N_NOT_REQUIRED; + uint32_t attrMin = 0x80000000, attrMax = 0x7fffffff; + bool fromAccessor = false; + if (attrID != 0 && !Res_INTERNALID(attrID)) { + const ssize_t p = getResourcePackageIndex(attrID); + const bag_entry* bag; + ssize_t cnt = p >= 0 ? lockBag(attrID, &bag) : -1; + //printf("For attr 0x%08x got bag of %d\n", attrID, cnt); + if (cnt >= 0) { + while (cnt > 0) { + //printf("Entry 0x%08x = 0x%08x\n", bag->map.name.ident, bag->map.value.data); + switch (bag->map.name.ident) { + case ResTable_map::ATTR_TYPE: + attrType = bag->map.value.data; + break; + case ResTable_map::ATTR_MIN: + attrMin = bag->map.value.data; + break; + case ResTable_map::ATTR_MAX: + attrMax = bag->map.value.data; + break; + case ResTable_map::ATTR_L10N: + l10nReq = bag->map.value.data; + break; + } + bag++; + cnt--; + } + unlockBag(bag); + } else if (accessor && accessor->getAttributeType(attrID, &attrType)) { + fromAccessor = true; + if (attrType == ResTable_map::TYPE_ENUM + || attrType == ResTable_map::TYPE_FLAGS + || attrType == ResTable_map::TYPE_INTEGER) { + accessor->getAttributeMin(attrID, &attrMin); + accessor->getAttributeMax(attrID, &attrMax); + } + if (localizationSetting) { + l10nReq = accessor->getAttributeL10N(attrID); + } + } + } + + const bool canStringCoerce = + coerceType && (attrType&ResTable_map::TYPE_STRING) != 0; + + if (*s == '@') { + outValue->dataType = outValue->TYPE_REFERENCE; + + // Note: we don't check attrType here because the reference can + // be to any other type; we just need to count on the client making + // sure the referenced type is correct. + + //printf("Looking up ref: %s\n", String8(s, len).string()); + + // It's a reference! + if (len == 5 && s[1]=='n' && s[2]=='u' && s[3]=='l' && s[4]=='l') { + outValue->data = 0; + return true; + } else { + bool createIfNotFound = false; + const char16_t* resourceRefName; + int resourceNameLen; + if (len > 2 && s[1] == '+') { + createIfNotFound = true; + resourceRefName = s + 2; + resourceNameLen = len - 2; + } else if (len > 2 && s[1] == '*') { + enforcePrivate = false; + resourceRefName = s + 2; + resourceNameLen = len - 2; + } else { + createIfNotFound = false; + resourceRefName = s + 1; + resourceNameLen = len - 1; + } + String16 package, type, name; + if (!expandResourceRef(resourceRefName,resourceNameLen, &package, &type, &name, + defType, defPackage, &errorMsg)) { + if (accessor != NULL) { + accessor->reportError(accessorCookie, errorMsg); + } + return false; + } + + uint32_t specFlags = 0; + uint32_t rid = identifierForName(name.string(), name.size(), type.string(), + type.size(), package.string(), package.size(), &specFlags); + if (rid != 0) { + if (enforcePrivate) { + if ((specFlags&ResTable_typeSpec::SPEC_PUBLIC) == 0) { + if (accessor != NULL) { + accessor->reportError(accessorCookie, "Resource is not public."); + } + return false; + } + } + if (!accessor) { + outValue->data = rid; + return true; + } + rid = Res_MAKEID( + accessor->getRemappedPackage(Res_GETPACKAGE(rid)), + Res_GETTYPE(rid), Res_GETENTRY(rid)); + TABLE_NOISY(printf("Incl %s:%s/%s: 0x%08x\n", + String8(package).string(), String8(type).string(), + String8(name).string(), rid)); + outValue->data = rid; + return true; + } + + if (accessor) { + uint32_t rid = accessor->getCustomResourceWithCreation(package, type, name, + createIfNotFound); + if (rid != 0) { + TABLE_NOISY(printf("Pckg %s:%s/%s: 0x%08x\n", + String8(package).string(), String8(type).string(), + String8(name).string(), rid)); + outValue->data = rid; + return true; + } + } + } + + if (accessor != NULL) { + accessor->reportError(accessorCookie, "No resource found that matches the given name"); + } + return false; + } + + // if we got to here, and localization is required and it's not a reference, + // complain and bail. + if (l10nReq == ResTable_map::L10N_SUGGESTED) { + if (localizationSetting) { + if (accessor != NULL) { + accessor->reportError(accessorCookie, "This attribute must be localized."); + } + } + } + + if (*s == '#') { + // It's a color! Convert to an integer of the form 0xaarrggbb. + uint32_t color = 0; + bool error = false; + if (len == 4) { + outValue->dataType = outValue->TYPE_INT_COLOR_RGB4; + color |= 0xFF000000; + color |= get_hex(s[1], &error) << 20; + color |= get_hex(s[1], &error) << 16; + color |= get_hex(s[2], &error) << 12; + color |= get_hex(s[2], &error) << 8; + color |= get_hex(s[3], &error) << 4; + color |= get_hex(s[3], &error); + } else if (len == 5) { + outValue->dataType = outValue->TYPE_INT_COLOR_ARGB4; + color |= get_hex(s[1], &error) << 28; + color |= get_hex(s[1], &error) << 24; + color |= get_hex(s[2], &error) << 20; + color |= get_hex(s[2], &error) << 16; + color |= get_hex(s[3], &error) << 12; + color |= get_hex(s[3], &error) << 8; + color |= get_hex(s[4], &error) << 4; + color |= get_hex(s[4], &error); + } else if (len == 7) { + outValue->dataType = outValue->TYPE_INT_COLOR_RGB8; + color |= 0xFF000000; + color |= get_hex(s[1], &error) << 20; + color |= get_hex(s[2], &error) << 16; + color |= get_hex(s[3], &error) << 12; + color |= get_hex(s[4], &error) << 8; + color |= get_hex(s[5], &error) << 4; + color |= get_hex(s[6], &error); + } else if (len == 9) { + outValue->dataType = outValue->TYPE_INT_COLOR_ARGB8; + color |= get_hex(s[1], &error) << 28; + color |= get_hex(s[2], &error) << 24; + color |= get_hex(s[3], &error) << 20; + color |= get_hex(s[4], &error) << 16; + color |= get_hex(s[5], &error) << 12; + color |= get_hex(s[6], &error) << 8; + color |= get_hex(s[7], &error) << 4; + color |= get_hex(s[8], &error); + } else { + error = true; + } + if (!error) { + if ((attrType&ResTable_map::TYPE_COLOR) == 0) { + if (!canStringCoerce) { + if (accessor != NULL) { + accessor->reportError(accessorCookie, + "Color types not allowed"); + } + return false; + } + } else { + outValue->data = color; + //printf("Color input=%s, output=0x%x\n", String8(s, len).string(), color); + return true; + } + } else { + if ((attrType&ResTable_map::TYPE_COLOR) != 0) { + if (accessor != NULL) { + accessor->reportError(accessorCookie, "Color value not valid --" + " must be #rgb, #argb, #rrggbb, or #aarrggbb"); + } + #if 0 + fprintf(stderr, "%s: Color ID %s value %s is not valid\n", + "Resource File", //(const char*)in->getPrintableSource(), + String8(*curTag).string(), + String8(s, len).string()); + #endif + return false; + } + } + } + + if (*s == '?') { + outValue->dataType = outValue->TYPE_ATTRIBUTE; + + // Note: we don't check attrType here because the reference can + // be to any other type; we just need to count on the client making + // sure the referenced type is correct. + + //printf("Looking up attr: %s\n", String8(s, len).string()); + + static const String16 attr16("attr"); + String16 package, type, name; + if (!expandResourceRef(s+1, len-1, &package, &type, &name, + &attr16, defPackage, &errorMsg)) { + if (accessor != NULL) { + accessor->reportError(accessorCookie, errorMsg); + } + return false; + } + + //printf("Pkg: %s, Type: %s, Name: %s\n", + // String8(package).string(), String8(type).string(), + // String8(name).string()); + uint32_t specFlags = 0; + uint32_t rid = + identifierForName(name.string(), name.size(), + type.string(), type.size(), + package.string(), package.size(), &specFlags); + if (rid != 0) { + if (enforcePrivate) { + if ((specFlags&ResTable_typeSpec::SPEC_PUBLIC) == 0) { + if (accessor != NULL) { + accessor->reportError(accessorCookie, "Attribute is not public."); + } + return false; + } + } + if (!accessor) { + outValue->data = rid; + return true; + } + rid = Res_MAKEID( + accessor->getRemappedPackage(Res_GETPACKAGE(rid)), + Res_GETTYPE(rid), Res_GETENTRY(rid)); + //printf("Incl %s:%s/%s: 0x%08x\n", + // String8(package).string(), String8(type).string(), + // String8(name).string(), rid); + outValue->data = rid; + return true; + } + + if (accessor) { + uint32_t rid = accessor->getCustomResource(package, type, name); + if (rid != 0) { + //printf("Mine %s:%s/%s: 0x%08x\n", + // String8(package).string(), String8(type).string(), + // String8(name).string(), rid); + outValue->data = rid; + return true; + } + } + + if (accessor != NULL) { + accessor->reportError(accessorCookie, "No resource found that matches the given name"); + } + return false; + } + + if (stringToInt(s, len, outValue)) { + if ((attrType&ResTable_map::TYPE_INTEGER) == 0) { + // If this type does not allow integers, but does allow floats, + // fall through on this error case because the float type should + // be able to accept any integer value. + if (!canStringCoerce && (attrType&ResTable_map::TYPE_FLOAT) == 0) { + if (accessor != NULL) { + accessor->reportError(accessorCookie, "Integer types not allowed"); + } + return false; + } + } else { + if (((int32_t)outValue->data) < ((int32_t)attrMin) + || ((int32_t)outValue->data) > ((int32_t)attrMax)) { + if (accessor != NULL) { + accessor->reportError(accessorCookie, "Integer value out of range"); + } + return false; + } + return true; + } + } + + if (stringToFloat(s, len, outValue)) { + if (outValue->dataType == Res_value::TYPE_DIMENSION) { + if ((attrType&ResTable_map::TYPE_DIMENSION) != 0) { + return true; + } + if (!canStringCoerce) { + if (accessor != NULL) { + accessor->reportError(accessorCookie, "Dimension types not allowed"); + } + return false; + } + } else if (outValue->dataType == Res_value::TYPE_FRACTION) { + if ((attrType&ResTable_map::TYPE_FRACTION) != 0) { + return true; + } + if (!canStringCoerce) { + if (accessor != NULL) { + accessor->reportError(accessorCookie, "Fraction types not allowed"); + } + return false; + } + } else if ((attrType&ResTable_map::TYPE_FLOAT) == 0) { + if (!canStringCoerce) { + if (accessor != NULL) { + accessor->reportError(accessorCookie, "Float types not allowed"); + } + return false; + } + } else { + return true; + } + } + + if (len == 4) { + if ((s[0] == 't' || s[0] == 'T') && + (s[1] == 'r' || s[1] == 'R') && + (s[2] == 'u' || s[2] == 'U') && + (s[3] == 'e' || s[3] == 'E')) { + if ((attrType&ResTable_map::TYPE_BOOLEAN) == 0) { + if (!canStringCoerce) { + if (accessor != NULL) { + accessor->reportError(accessorCookie, "Boolean types not allowed"); + } + return false; + } + } else { + outValue->dataType = outValue->TYPE_INT_BOOLEAN; + outValue->data = (uint32_t)-1; + return true; + } + } + } + + if (len == 5) { + if ((s[0] == 'f' || s[0] == 'F') && + (s[1] == 'a' || s[1] == 'A') && + (s[2] == 'l' || s[2] == 'L') && + (s[3] == 's' || s[3] == 'S') && + (s[4] == 'e' || s[4] == 'E')) { + if ((attrType&ResTable_map::TYPE_BOOLEAN) == 0) { + if (!canStringCoerce) { + if (accessor != NULL) { + accessor->reportError(accessorCookie, "Boolean types not allowed"); + } + return false; + } + } else { + outValue->dataType = outValue->TYPE_INT_BOOLEAN; + outValue->data = 0; + return true; + } + } + } + + if ((attrType&ResTable_map::TYPE_ENUM) != 0) { + const ssize_t p = getResourcePackageIndex(attrID); + const bag_entry* bag; + ssize_t cnt = p >= 0 ? lockBag(attrID, &bag) : -1; + //printf("Got %d for enum\n", cnt); + if (cnt >= 0) { + resource_name rname; + while (cnt > 0) { + if (!Res_INTERNALID(bag->map.name.ident)) { + //printf("Trying attr #%08x\n", bag->map.name.ident); + if (getResourceName(bag->map.name.ident, &rname)) { + #if 0 + printf("Matching %s against %s (0x%08x)\n", + String8(s, len).string(), + String8(rname.name, rname.nameLen).string(), + bag->map.name.ident); + #endif + if (strzcmp16(s, len, rname.name, rname.nameLen) == 0) { + outValue->dataType = bag->map.value.dataType; + outValue->data = bag->map.value.data; + unlockBag(bag); + return true; + } + } + + } + bag++; + cnt--; + } + unlockBag(bag); + } + + if (fromAccessor) { + if (accessor->getAttributeEnum(attrID, s, len, outValue)) { + return true; + } + } + } + + if ((attrType&ResTable_map::TYPE_FLAGS) != 0) { + const ssize_t p = getResourcePackageIndex(attrID); + const bag_entry* bag; + ssize_t cnt = p >= 0 ? lockBag(attrID, &bag) : -1; + //printf("Got %d for flags\n", cnt); + if (cnt >= 0) { + bool failed = false; + resource_name rname; + outValue->dataType = Res_value::TYPE_INT_HEX; + outValue->data = 0; + const char16_t* end = s + len; + const char16_t* pos = s; + while (pos < end && !failed) { + const char16_t* start = pos; + end++; + while (pos < end && *pos != '|') { + pos++; + } + //printf("Looking for: %s\n", String8(start, pos-start).string()); + const bag_entry* bagi = bag; + ssize_t i; + for (i=0; imap.name.ident)) { + //printf("Trying attr #%08x\n", bagi->map.name.ident); + if (getResourceName(bagi->map.name.ident, &rname)) { + #if 0 + printf("Matching %s against %s (0x%08x)\n", + String8(start,pos-start).string(), + String8(rname.name, rname.nameLen).string(), + bagi->map.name.ident); + #endif + if (strzcmp16(start, pos-start, rname.name, rname.nameLen) == 0) { + outValue->data |= bagi->map.value.data; + break; + } + } + } + } + if (i >= cnt) { + // Didn't find this flag identifier. + failed = true; + } + if (pos < end) { + pos++; + } + } + unlockBag(bag); + if (!failed) { + //printf("Final flag value: 0x%lx\n", outValue->data); + return true; + } + } + + + if (fromAccessor) { + if (accessor->getAttributeFlags(attrID, s, len, outValue)) { + //printf("Final flag value: 0x%lx\n", outValue->data); + return true; + } + } + } + + if ((attrType&ResTable_map::TYPE_STRING) == 0) { + if (accessor != NULL) { + accessor->reportError(accessorCookie, "String types not allowed"); + } + return false; + } + + // Generic string handling... + outValue->dataType = outValue->TYPE_STRING; + if (outString) { + bool failed = collectString(outString, s, len, preserveSpaces, &errorMsg); + if (accessor != NULL) { + accessor->reportError(accessorCookie, errorMsg); + } + return failed; + } + + return true; +} + +bool ResTable::collectString(String16* outString, + const char16_t* s, size_t len, + bool preserveSpaces, + const char** outErrorMsg, + bool append) +{ + String16 tmp; + + char quoted = 0; + const char16_t* p = s; + while (p < (s+len)) { + while (p < (s+len)) { + const char16_t c = *p; + if (c == '\\') { + break; + } + if (!preserveSpaces) { + if (quoted == 0 && isspace16(c) + && (c != ' ' || isspace16(*(p+1)))) { + break; + } + if (c == '"' && (quoted == 0 || quoted == '"')) { + break; + } + if (c == '\'' && (quoted == 0 || quoted == '\'')) { + break; + } + } + p++; + } + if (p < (s+len)) { + if (p > s) { + tmp.append(String16(s, p-s)); + } + if (!preserveSpaces && (*p == '"' || *p == '\'')) { + if (quoted == 0) { + quoted = *p; + } else { + quoted = 0; + } + p++; + } else if (!preserveSpaces && isspace16(*p)) { + // Space outside of a quote -- consume all spaces and + // leave a single plain space char. + tmp.append(String16(" ")); + p++; + while (p < (s+len) && isspace16(*p)) { + p++; + } + } else if (*p == '\\') { + p++; + if (p < (s+len)) { + switch (*p) { + case 't': + tmp.append(String16("\t")); + break; + case 'n': + tmp.append(String16("\n")); + break; + case '#': + tmp.append(String16("#")); + break; + case '@': + tmp.append(String16("@")); + break; + case '?': + tmp.append(String16("?")); + break; + case '"': + tmp.append(String16("\"")); + break; + case '\'': + tmp.append(String16("'")); + break; + case '\\': + tmp.append(String16("\\")); + break; + case 'u': + { + char16_t chr = 0; + int i = 0; + while (i < 4 && p[1] != 0) { + p++; + i++; + int c; + if (*p >= '0' && *p <= '9') { + c = *p - '0'; + } else if (*p >= 'a' && *p <= 'f') { + c = *p - 'a' + 10; + } else if (*p >= 'A' && *p <= 'F') { + c = *p - 'A' + 10; + } else { + if (outErrorMsg) { + *outErrorMsg = "Bad character in \\u unicode escape sequence"; + } + return false; + } + chr = (chr<<4) | c; + } + tmp.append(String16(&chr, 1)); + } break; + default: + // ignore unknown escape chars. + break; + } + p++; + } + } + len -= (p-s); + s = p; + } + } + + if (tmp.size() != 0) { + if (len > 0) { + tmp.append(String16(s, len)); + } + if (append) { + outString->append(tmp); + } else { + outString->setTo(tmp); + } + } else { + if (append) { + outString->append(String16(s, len)); + } else { + outString->setTo(s, len); + } + } + + return true; +} + +size_t ResTable::getBasePackageCount() const +{ + if (mError != NO_ERROR) { + return 0; + } + return mPackageGroups.size(); +} + +const char16_t* ResTable::getBasePackageName(size_t idx) const +{ + if (mError != NO_ERROR) { + return 0; + } + LOG_FATAL_IF(idx >= mPackageGroups.size(), + "Requested package index %d past package count %d", + (int)idx, (int)mPackageGroups.size()); + return mPackageGroups[idx]->name.string(); +} + +uint32_t ResTable::getBasePackageId(size_t idx) const +{ + if (mError != NO_ERROR) { + return 0; + } + LOG_FATAL_IF(idx >= mPackageGroups.size(), + "Requested package index %d past package count %d", + (int)idx, (int)mPackageGroups.size()); + return mPackageGroups[idx]->id; +} + +size_t ResTable::getTableCount() const +{ + return mHeaders.size(); +} + +const ResStringPool* ResTable::getTableStringBlock(size_t index) const +{ + return &mHeaders[index]->values; +} + +void* ResTable::getTableCookie(size_t index) const +{ + return mHeaders[index]->cookie; +} + +void ResTable::getConfigurations(Vector* configs) const +{ + const size_t I = mPackageGroups.size(); + for (size_t i=0; ipackages.size(); + for (size_t j=0; jpackages[j]; + const size_t K = package->types.size(); + for (size_t k=0; ktypes[k]; + if (type == NULL) continue; + const size_t L = type->configs.size(); + for (size_t l=0; lconfigs[l]; + const ResTable_config* cfg = &config->config; + // only insert unique + const size_t M = configs->size(); + size_t m; + for (m=0; madd(*cfg); + } + } + } + } + } +} + +void ResTable::getLocales(Vector* locales) const +{ + Vector configs; + LOGD("calling getConfigurations"); + getConfigurations(&configs); + LOGD("called getConfigurations size=%d", (int)configs.size()); + const size_t I = configs.size(); + for (size_t i=0; isize(); + size_t j; + for (j=0; jadd(String8(locale)); + } + } +} + +ssize_t ResTable::getEntry( + const Package* package, int typeIndex, int entryIndex, + const ResTable_config* config, + const ResTable_type** outType, const ResTable_entry** outEntry, + const Type** outTypeClass) const +{ + LOGV("Getting entry from package %p\n", package); + const ResTable_package* const pkg = package->package; + + const Type* allTypes = package->getType(typeIndex); + LOGV("allTypes=%p\n", allTypes); + if (allTypes == NULL) { + LOGV("Skipping entry type index 0x%02x because type is NULL!\n", typeIndex); + return 0; + } + + if ((size_t)entryIndex >= allTypes->entryCount) { + LOGW("getEntry failing because entryIndex %d is beyond type entryCount %d", + entryIndex, (int)allTypes->entryCount); + return BAD_TYPE; + } + + const ResTable_type* type = NULL; + uint32_t offset = ResTable_type::NO_ENTRY; + ResTable_config bestConfig; + memset(&bestConfig, 0, sizeof(bestConfig)); // make the compiler shut up + + const size_t NT = allTypes->configs.size(); + for (size_t i=0; iconfigs[i]; + if (thisType == NULL) continue; + + ResTable_config thisConfig; + thisConfig.copyFromDtoH(thisType->config); + + TABLE_GETENTRY(LOGI("Match entry 0x%x in type 0x%x (sz 0x%x): imsi:%d/%d=%d/%d lang:%c%c=%c%c cnt:%c%c=%c%c " + "orien:%d=%d touch:%d=%d density:%d=%d key:%d=%d inp:%d=%d nav:%d=%d w:%d=%d h:%d=%d\n", + entryIndex, typeIndex+1, dtohl(thisType->config.size), + thisConfig.mcc, thisConfig.mnc, + config ? config->mcc : 0, config ? config->mnc : 0, + thisConfig.language[0] ? thisConfig.language[0] : '-', + thisConfig.language[1] ? thisConfig.language[1] : '-', + config && config->language[0] ? config->language[0] : '-', + config && config->language[1] ? config->language[1] : '-', + thisConfig.country[0] ? thisConfig.country[0] : '-', + thisConfig.country[1] ? thisConfig.country[1] : '-', + config && config->country[0] ? config->country[0] : '-', + config && config->country[1] ? config->country[1] : '-', + thisConfig.orientation, + config ? config->orientation : 0, + thisConfig.touchscreen, + config ? config->touchscreen : 0, + thisConfig.density, + config ? config->density : 0, + thisConfig.keyboard, + config ? config->keyboard : 0, + thisConfig.inputFlags, + config ? config->inputFlags : 0, + thisConfig.navigation, + config ? config->navigation : 0, + thisConfig.screenWidth, + config ? config->screenWidth : 0, + thisConfig.screenHeight, + config ? config->screenHeight : 0)); + + // Check to make sure this one is valid for the current parameters. + if (config && !thisConfig.match(*config)) { + TABLE_GETENTRY(LOGI("Does not match config!\n")); + continue; + } + + // Check if there is the desired entry in this type. + + const uint8_t* const end = ((const uint8_t*)thisType) + + dtohl(thisType->header.size); + const uint32_t* const eindex = (const uint32_t*) + (((const uint8_t*)thisType) + dtohs(thisType->header.headerSize)); + + uint32_t thisOffset = dtohl(eindex[entryIndex]); + if (thisOffset == ResTable_type::NO_ENTRY) { + TABLE_GETENTRY(LOGI("Skipping because it is not defined!\n")); + continue; + } + + if (type != NULL) { + // Check if this one is less specific than the last found. If so, + // we will skip it. We check starting with things we most care + // about to those we least care about. + if (!thisConfig.isBetterThan(bestConfig, config)) { + TABLE_GETENTRY(LOGI("This config is worse than last!\n")); + continue; + } + } + + type = thisType; + offset = thisOffset; + bestConfig = thisConfig; + TABLE_GETENTRY(LOGI("Best entry so far -- using it!\n")); + if (!config) break; + } + + if (type == NULL) { + TABLE_GETENTRY(LOGI("No value found for requested entry!\n")); + return BAD_INDEX; + } + + offset += dtohl(type->entriesStart); + TABLE_NOISY(aout << "Looking in resource table " << package->header->header + << ", typeOff=" + << (void*)(((const char*)type)-((const char*)package->header->header)) + << ", offset=" << (void*)offset << endl); + + if (offset > (dtohl(type->header.size)-sizeof(ResTable_entry))) { + LOGW("ResTable_entry at 0x%x is beyond type chunk data 0x%x", + offset, dtohl(type->header.size)); + return BAD_TYPE; + } + if ((offset&0x3) != 0) { + LOGW("ResTable_entry at 0x%x is not on an integer boundary", + offset); + return BAD_TYPE; + } + + const ResTable_entry* const entry = (const ResTable_entry*) + (((const uint8_t*)type) + offset); + if (dtohs(entry->size) < sizeof(*entry)) { + LOGW("ResTable_entry size 0x%x is too small", dtohs(entry->size)); + return BAD_TYPE; + } + + *outType = type; + *outEntry = entry; + if (outTypeClass != NULL) { + *outTypeClass = allTypes; + } + return offset + dtohs(entry->size); +} + +status_t ResTable::parsePackage(const ResTable_package* const pkg, + const Header* const header) +{ + const uint8_t* base = (const uint8_t*)pkg; + status_t err = validate_chunk(&pkg->header, sizeof(*pkg), + header->dataEnd, "ResTable_package"); + if (err != NO_ERROR) { + return (mError=err); + } + + const size_t pkgSize = dtohl(pkg->header.size); + + if (dtohl(pkg->typeStrings) >= pkgSize) { + LOGW("ResTable_package type strings at %p are past chunk size %p.", + (void*)dtohl(pkg->typeStrings), (void*)pkgSize); + return (mError=BAD_TYPE); + } + if ((dtohl(pkg->typeStrings)&0x3) != 0) { + LOGW("ResTable_package type strings at %p is not on an integer boundary.", + (void*)dtohl(pkg->typeStrings)); + return (mError=BAD_TYPE); + } + if (dtohl(pkg->keyStrings) >= pkgSize) { + LOGW("ResTable_package key strings at %p are past chunk size %p.", + (void*)dtohl(pkg->keyStrings), (void*)pkgSize); + return (mError=BAD_TYPE); + } + if ((dtohl(pkg->keyStrings)&0x3) != 0) { + LOGW("ResTable_package key strings at %p is not on an integer boundary.", + (void*)dtohl(pkg->keyStrings)); + return (mError=BAD_TYPE); + } + + Package* package = NULL; + PackageGroup* group = NULL; + uint32_t id = dtohl(pkg->id); + if (id != 0 && id < 256) { + size_t idx = mPackageMap[id]; + if (idx == 0) { + idx = mPackageGroups.size()+1; + + char16_t tmpName[sizeof(pkg->name)/sizeof(char16_t)]; + strcpy16_dtoh(tmpName, pkg->name, sizeof(pkg->name)/sizeof(char16_t)); + group = new PackageGroup(String16(tmpName), id); + if (group == NULL) { + return (mError=NO_MEMORY); + } + + err = group->typeStrings.setTo(base+dtohl(pkg->typeStrings), + header->dataEnd-(base+dtohl(pkg->typeStrings))); + if (err != NO_ERROR) { + return (mError=err); + } + err = group->keyStrings.setTo(base+dtohl(pkg->keyStrings), + header->dataEnd-(base+dtohl(pkg->keyStrings))); + if (err != NO_ERROR) { + return (mError=err); + } + + //printf("Adding new package id %d at index %d\n", id, idx); + err = mPackageGroups.add(group); + if (err < NO_ERROR) { + return (mError=err); + } + mPackageMap[id] = (uint8_t)idx; + } else { + group = mPackageGroups.itemAt(idx-1); + if (group == NULL) { + return (mError=UNKNOWN_ERROR); + } + } + package = new Package(header, pkg); + if (package == NULL) { + return (mError=NO_MEMORY); + } + err = group->packages.add(package); + if (err < NO_ERROR) { + return (mError=err); + } + } else { + LOG_ALWAYS_FATAL("Skins not supported!"); + return NO_ERROR; + } + + + // Iterate through all chunks. + size_t curPackage = 0; + + const ResChunk_header* chunk = + (const ResChunk_header*)(((const uint8_t*)pkg) + + dtohs(pkg->header.headerSize)); + const uint8_t* endPos = ((const uint8_t*)pkg) + dtohs(pkg->header.size); + while (((const uint8_t*)chunk) <= (endPos-sizeof(ResChunk_header)) && + ((const uint8_t*)chunk) <= (endPos-dtohl(chunk->size))) { + TABLE_NOISY(LOGV("PackageChunk: type=0x%x, headerSize=0x%x, size=0x%x, pos=%p\n", + dtohs(chunk->type), dtohs(chunk->headerSize), dtohl(chunk->size), + (void*)(((const uint8_t*)chunk) - ((const uint8_t*)header->header)))); + const size_t csize = dtohl(chunk->size); + const uint16_t ctype = dtohs(chunk->type); + if (ctype == RES_TABLE_TYPE_SPEC_TYPE) { + const ResTable_typeSpec* typeSpec = (const ResTable_typeSpec*)(chunk); + err = validate_chunk(&typeSpec->header, sizeof(*typeSpec), + endPos, "ResTable_typeSpec"); + if (err != NO_ERROR) { + return (mError=err); + } + + const size_t typeSpecSize = dtohl(typeSpec->header.size); + + LOAD_TABLE_NOISY(printf("TypeSpec off %p: type=0x%x, headerSize=0x%x, size=%p\n", + (void*)(base-(const uint8_t*)chunk), + dtohs(typeSpec->header.type), + dtohs(typeSpec->header.headerSize), + (void*)typeSize)); + // look for block overrun or int overflow when multiplying by 4 + if ((dtohl(typeSpec->entryCount) > (INT32_MAX/sizeof(uint32_t)) + || dtohs(typeSpec->header.headerSize)+(sizeof(uint32_t)*dtohl(typeSpec->entryCount)) + > typeSpecSize)) { + LOGW("ResTable_typeSpec entry index to %p extends beyond chunk end %p.", + (void*)(dtohs(typeSpec->header.headerSize) + +(sizeof(uint32_t)*dtohl(typeSpec->entryCount))), + (void*)typeSpecSize); + return (mError=BAD_TYPE); + } + + if (typeSpec->id == 0) { + LOGW("ResTable_type has an id of 0."); + return (mError=BAD_TYPE); + } + + while (package->types.size() < typeSpec->id) { + package->types.add(NULL); + } + Type* t = package->types[typeSpec->id-1]; + if (t == NULL) { + t = new Type(header, package, dtohl(typeSpec->entryCount)); + package->types.editItemAt(typeSpec->id-1) = t; + } else if (dtohl(typeSpec->entryCount) != t->entryCount) { + LOGW("ResTable_typeSpec entry count inconsistent: given %d, previously %d", + (int)dtohl(typeSpec->entryCount), (int)t->entryCount); + return (mError=BAD_TYPE); + } + t->typeSpecFlags = (const uint32_t*)( + ((const uint8_t*)typeSpec) + dtohs(typeSpec->header.headerSize)); + t->typeSpec = typeSpec; + + } else if (ctype == RES_TABLE_TYPE_TYPE) { + const ResTable_type* type = (const ResTable_type*)(chunk); + err = validate_chunk(&type->header, sizeof(*type)-sizeof(ResTable_config)+4, + endPos, "ResTable_type"); + if (err != NO_ERROR) { + return (mError=err); + } + + const size_t typeSize = dtohl(type->header.size); + + LOAD_TABLE_NOISY(printf("Type off %p: type=0x%x, headerSize=0x%x, size=%p\n", + (void*)(base-(const uint8_t*)chunk), + dtohs(type->header.type), + dtohs(type->header.headerSize), + (void*)typeSize)); + if (dtohs(type->header.headerSize)+(sizeof(uint32_t)*dtohl(type->entryCount)) + > typeSize) { + LOGW("ResTable_type entry index to %p extends beyond chunk end %p.", + (void*)(dtohs(type->header.headerSize) + +(sizeof(uint32_t)*dtohl(type->entryCount))), + (void*)typeSize); + return (mError=BAD_TYPE); + } + if (dtohl(type->entryCount) != 0 + && dtohl(type->entriesStart) > (typeSize-sizeof(ResTable_entry))) { + LOGW("ResTable_type entriesStart at %p extends beyond chunk end %p.", + (void*)dtohl(type->entriesStart), (void*)typeSize); + return (mError=BAD_TYPE); + } + if (type->id == 0) { + LOGW("ResTable_type has an id of 0."); + return (mError=BAD_TYPE); + } + + while (package->types.size() < type->id) { + package->types.add(NULL); + } + Type* t = package->types[type->id-1]; + if (t == NULL) { + t = new Type(header, package, dtohl(type->entryCount)); + package->types.editItemAt(type->id-1) = t; + } else if (dtohl(type->entryCount) != t->entryCount) { + LOGW("ResTable_type entry count inconsistent: given %d, previously %d", + (int)dtohl(type->entryCount), (int)t->entryCount); + return (mError=BAD_TYPE); + } + + TABLE_GETENTRY( + ResTable_config thisConfig; + thisConfig.copyFromDtoH(type->config); + LOGI("Adding config to type %d: imsi:%d/%d lang:%c%c cnt:%c%c " + "orien:%d touch:%d density:%d key:%d inp:%d nav:%d w:%d h:%d\n", + type->id, + thisConfig.mcc, thisConfig.mnc, + thisConfig.language[0] ? thisConfig.language[0] : '-', + thisConfig.language[1] ? thisConfig.language[1] : '-', + thisConfig.country[0] ? thisConfig.country[0] : '-', + thisConfig.country[1] ? thisConfig.country[1] : '-', + thisConfig.orientation, + thisConfig.touchscreen, + thisConfig.density, + thisConfig.keyboard, + thisConfig.inputFlags, + thisConfig.navigation, + thisConfig.screenWidth, + thisConfig.screenHeight)); + t->configs.add(type); + } else { + status_t err = validate_chunk(chunk, sizeof(ResChunk_header), + endPos, "ResTable_package:unknown"); + if (err != NO_ERROR) { + return (mError=err); + } + } + chunk = (const ResChunk_header*) + (((const uint8_t*)chunk) + csize); + } + + if (group->typeCount == 0) { + group->typeCount = package->types.size(); + } + + return NO_ERROR; +} + +#ifndef HAVE_ANDROID_OS +#define CHAR16_TO_CSTR(c16, len) (String8(String16(c16,len)).string()) + +#define CHAR16_ARRAY_EQ(constant, var, len) \ + ((len == (sizeof(constant)/sizeof(constant[0]))) && (0 == memcmp((var), (constant), (len)))) + +void ResTable::print() const +{ + printf("mError=0x%x (%s)\n", mError, strerror(mError)); +#if 0 + printf("mParams=%c%c-%c%c,\n", + mParams.language[0], mParams.language[1], + mParams.country[0], mParams.country[1]); +#endif + size_t pgCount = mPackageGroups.size(); + printf("Package Groups (%d)\n", (int)pgCount); + for (size_t pgIndex=0; pgIndexid, (int)pg->packages.size(), + String8(pg->name).string()); + + size_t pkgCount = pg->packages.size(); + for (size_t pkgIndex=0; pkgIndexpackages[pkgIndex]; + size_t typeCount = pkg->types.size(); + printf(" Package %d id=%d name=%s typeCount=%d\n", (int)pkgIndex, + pkg->package->id, String8(String16(pkg->package->name)).string(), + (int)typeCount); + for (size_t typeIndex=0; typeIndexgetType(typeIndex); + if (typeConfigs == NULL) { + printf(" type %d NULL\n", (int)typeIndex); + continue; + } + const size_t NTC = typeConfigs->configs.size(); + printf(" type %d configCount=%d entryCount=%d\n", + (int)typeIndex, (int)NTC, (int)typeConfigs->entryCount); + if (typeConfigs->typeSpecFlags != NULL) { + for (size_t entryIndex=0; entryIndexentryCount; entryIndex++) { + uint32_t resID = (0xff000000 & ((pkg->package->id)<<24)) + | (0x00ff0000 & ((typeIndex+1)<<16)) + | (0x0000ffff & (entryIndex)); + resource_name resName; + this->getResourceName(resID, &resName); + printf(" spec resource 0x%08x %s:%s/%s: flags=0x%08x\n", + resID, + CHAR16_TO_CSTR(resName.package, resName.packageLen), + CHAR16_TO_CSTR(resName.type, resName.typeLen), + CHAR16_TO_CSTR(resName.name, resName.nameLen), + dtohl(typeConfigs->typeSpecFlags[entryIndex])); + } + } + for (size_t configIndex=0; configIndexconfigs[configIndex]; + if ((((uint64_t)type)&0x3) != 0) { + printf(" NON-INTEGER ResTable_type ADDRESS: %p\n", type); + continue; + } + printf(" config %d lang=%c%c cnt=%c%c orien=%d touch=%d density=%d key=%d infl=%d nav=%d w=%d h=%d\n", + (int)configIndex, + type->config.language[0] ? type->config.language[0] : '-', + type->config.language[1] ? type->config.language[1] : '-', + type->config.country[0] ? type->config.country[0] : '-', + type->config.country[1] ? type->config.country[1] : '-', + type->config.orientation, + type->config.touchscreen, + dtohs(type->config.density), + type->config.keyboard, + type->config.inputFlags, + type->config.navigation, + dtohs(type->config.screenWidth), + dtohs(type->config.screenHeight)); + size_t entryCount = dtohl(type->entryCount); + uint32_t entriesStart = dtohl(type->entriesStart); + if ((entriesStart&0x3) != 0) { + printf(" NON-INTEGER ResTable_type entriesStart OFFSET: %p\n", (void*)entriesStart); + continue; + } + uint32_t typeSize = dtohl(type->header.size); + if ((typeSize&0x3) != 0) { + printf(" NON-INTEGER ResTable_type header.size: %p\n", (void*)typeSize); + continue; + } + for (size_t entryIndex=0; entryIndexheader.size); + const uint32_t* const eindex = (const uint32_t*) + (((const uint8_t*)type) + dtohs(type->header.headerSize)); + + uint32_t thisOffset = dtohl(eindex[entryIndex]); + if (thisOffset == ResTable_type::NO_ENTRY) { + continue; + } + + uint32_t resID = (0xff000000 & ((pkg->package->id)<<24)) + | (0x00ff0000 & ((typeIndex+1)<<16)) + | (0x0000ffff & (entryIndex)); + resource_name resName; + this->getResourceName(resID, &resName); + printf(" resource 0x%08x %s:%s/%s: ", resID, + CHAR16_TO_CSTR(resName.package, resName.packageLen), + CHAR16_TO_CSTR(resName.type, resName.typeLen), + CHAR16_TO_CSTR(resName.name, resName.nameLen)); + if ((thisOffset&0x3) != 0) { + printf("NON-INTEGER OFFSET: %p\n", (void*)thisOffset); + continue; + } + if ((thisOffset+sizeof(ResTable_entry)) > typeSize) { + printf("OFFSET OUT OF BOUNDS: %p+%p (size is %p)\n", + (void*)entriesStart, (void*)thisOffset, + (void*)typeSize); + continue; + } + + const ResTable_entry* ent = (const ResTable_entry*) + (((const uint8_t*)type) + entriesStart + thisOffset); + if (((entriesStart + thisOffset)&0x3) != 0) { + printf("NON-INTEGER ResTable_entry OFFSET: %p\n", + (void*)(entriesStart + thisOffset)); + continue; + } + if ((dtohs(ent->flags)&ResTable_entry::FLAG_COMPLEX) != 0) { + printf(""); + } 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; + } + + const Res_value* value = (const Res_value*) + (((const uint8_t*)ent) + esize); + 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); + } + + if ((dtohs(ent->flags)&ResTable_entry::FLAG_PUBLIC) != 0) { + printf(" (PUBLIC)"); + } + printf("\n"); + } + } + } + } + } +} + +#endif // HAVE_ANDROID_OS + +} // namespace android diff --git a/libs/utils/SharedBuffer.cpp b/libs/utils/SharedBuffer.cpp new file mode 100644 index 000000000..3555fb712 --- /dev/null +++ b/libs/utils/SharedBuffer.cpp @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2005 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 +#include + +#include +#include + +// --------------------------------------------------------------------------- + +namespace android { + +SharedBuffer* SharedBuffer::alloc(size_t size) +{ + SharedBuffer* sb = static_cast(malloc(sizeof(SharedBuffer) + size)); + if (sb) { + sb->mRefs = 1; + sb->mSize = size; + } + return sb; +} + + +ssize_t SharedBuffer::dealloc(const SharedBuffer* released) +{ + if (released->mRefs != 0) return -1; // XXX: invalid operation + free(const_cast(released)); + return 0; +} + +SharedBuffer* SharedBuffer::edit() const +{ + if (onlyOwner()) { + return const_cast(this); + } + SharedBuffer* sb = alloc(mSize); + if (sb) { + memcpy(sb->data(), data(), size()); + release(); + } + return sb; +} + +SharedBuffer* SharedBuffer::editResize(size_t newSize) const +{ + if (onlyOwner()) { + SharedBuffer* buf = const_cast(this); + if (buf->mSize == newSize) return buf; + buf = (SharedBuffer*)realloc(buf, sizeof(SharedBuffer) + newSize); + if (buf != NULL) { + buf->mSize = newSize; + return buf; + } + } + SharedBuffer* sb = alloc(newSize); + if (sb) { + const size_t mySize = mSize; + memcpy(sb->data(), data(), newSize < mySize ? newSize : mySize); + release(); + } + return sb; +} + +SharedBuffer* SharedBuffer::attemptEdit() const +{ + if (onlyOwner()) { + return const_cast(this); + } + return 0; +} + +SharedBuffer* SharedBuffer::reset(size_t new_size) const +{ + // cheap-o-reset. + SharedBuffer* sb = alloc(new_size); + if (sb) { + release(); + } + return sb; +} + +void SharedBuffer::acquire() const { + android_atomic_inc(&mRefs); +} + +int32_t SharedBuffer::release(uint32_t flags) const +{ + int32_t prev = 1; + if (onlyOwner() || ((prev = android_atomic_dec(&mRefs)) == 1)) { + mRefs = 0; + if ((flags & eKeepStorage) == 0) { + free(const_cast(this)); + } + } + return prev; +} + + +}; // namespace android diff --git a/libs/utils/Socket.cpp b/libs/utils/Socket.cpp new file mode 100644 index 000000000..51509a304 --- /dev/null +++ b/libs/utils/Socket.cpp @@ -0,0 +1,388 @@ +/* + * Copyright (C) 2005 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. + */ + +// +// Internet address class. +// + +#ifdef HAVE_WINSOCK +// This needs to come first, or Cygwin gets concerned about a potential +// clash between WinSock and . +# include +#endif + +#include +#include +#include +#include + +#ifndef HAVE_WINSOCK +# include +# include +# include +# include +#endif + +#include +#include +#include +#include +#include +#include + +using namespace android; + + +/* + * =========================================================================== + * Socket + * =========================================================================== + */ + +#ifndef INVALID_SOCKET +# define INVALID_SOCKET (-1) +#endif +#define UNDEF_SOCKET ((unsigned long) INVALID_SOCKET) + +/*static*/ bool Socket::mBootInitialized = false; + +/* + * Extract system-dependent error code. + */ +static inline int getSocketError(void) { +#ifdef HAVE_WINSOCK + return WSAGetLastError(); +#else + return errno; +#endif +} + +/* + * One-time initialization for socket code. + */ +/*static*/ bool Socket::bootInit(void) +{ +#ifdef HAVE_WINSOCK + WSADATA wsaData; + int err; + + err = WSAStartup(MAKEWORD(2, 0), &wsaData); + if (err != 0) { + LOG(LOG_ERROR, "socket", "Unable to start WinSock\n"); + return false; + } + + LOG(LOG_INFO, "socket", "Using WinSock v%d.%d\n", + LOBYTE(wsaData.wVersion), HIBYTE(wsaData.wVersion)); +#endif + + mBootInitialized = true; + return true; +} + +/* + * One-time shutdown for socket code. + */ +/*static*/ void Socket::finalShutdown(void) +{ +#ifdef HAVE_WINSOCK + WSACleanup(); +#endif + mBootInitialized = false; +} + + +/* + * Simple constructor. Allow the application to create us and then make + * bind/connect calls. + */ +Socket::Socket(void) + : mSock(UNDEF_SOCKET) +{ + if (!mBootInitialized) + LOG(LOG_WARN, "socket", "WARNING: sockets not initialized\n"); +} + +/* + * Destructor. Closes the socket and resets our storage. + */ +Socket::~Socket(void) +{ + close(); +} + + +/* + * Create a socket and connect to the specified host and port. + */ +int Socket::connect(const char* host, int port) +{ + if (mSock != UNDEF_SOCKET) { + LOG(LOG_WARN, "socket", "Socket already connected\n"); + return -1; + } + + InetSocketAddress sockAddr; + if (!sockAddr.create(host, port)) + return -1; + + //return doConnect(sockAddr); + int foo; + foo = doConnect(sockAddr); + return foo; +} + +/* + * Create a socket and connect to the specified host and port. + */ +int Socket::connect(const InetAddress* addr, int port) +{ + if (mSock != UNDEF_SOCKET) { + LOG(LOG_WARN, "socket", "Socket already connected\n"); + return -1; + } + + InetSocketAddress sockAddr; + if (!sockAddr.create(addr, port)) + return -1; + + return doConnect(sockAddr); +} + +/* + * Finish creating a socket by connecting to the remote host. + * + * Returns 0 on success. + */ +int Socket::doConnect(const InetSocketAddress& sockAddr) +{ +#ifdef HAVE_WINSOCK + SOCKET sock; +#else + int sock; +#endif + const InetAddress* addr = sockAddr.getAddress(); + int port = sockAddr.getPort(); + struct sockaddr_in inaddr; + DurationTimer connectTimer; + + assert(sizeof(struct sockaddr_in) == addr->getAddressLength()); + memcpy(&inaddr, addr->getAddress(), addr->getAddressLength()); + inaddr.sin_port = htons(port); + + //fprintf(stderr, "--- connecting to %s:%d\n", + // sockAddr.getHostName(), port); + + sock = ::socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (sock == INVALID_SOCKET) { + int err = getSocketError(); + LOG(LOG_ERROR, "socket", "Unable to create socket (err=%d)\n", err); + return (err != 0) ? err : -1; + } + + connectTimer.start(); + + if (::connect(sock, (struct sockaddr*) &inaddr, sizeof(inaddr)) != 0) { + int err = getSocketError(); + LOG(LOG_WARN, "socket", "Connect to %s:%d failed: %d\n", + sockAddr.getHostName(), port, err); + return (err != 0) ? err : -1; + } + + connectTimer.stop(); + if ((long) connectTimer.durationUsecs() > 100000) { + LOG(LOG_INFO, "socket", + "Connect to %s:%d took %.3fs\n", sockAddr.getHostName(), + port, ((long) connectTimer.durationUsecs()) / 1000000.0); + } + + mSock = (unsigned long) sock; + LOG(LOG_VERBOSE, "socket", + "--- connected to %s:%d\n", sockAddr.getHostName(), port); + return 0; +} + + +/* + * Close the socket if it needs closing. + */ +bool Socket::close(void) +{ + if (mSock != UNDEF_SOCKET) { + //fprintf(stderr, "--- closing socket %lu\n", mSock); +#ifdef HAVE_WINSOCK + if (::closesocket((SOCKET) mSock) != 0) + return false; +#else + if (::close((int) mSock) != 0) + return false; +#endif + } + + mSock = UNDEF_SOCKET; + + return true; +} + +/* + * Read data from socket. + * + * Standard semantics: read up to "len" bytes into "buf". Returns the + * number of bytes read, or less than zero on error. + */ +int Socket::read(void* buf, ssize_t len) const +{ + if (mSock == UNDEF_SOCKET) { + LOG(LOG_ERROR, "socket", "ERROR: read on invalid socket\n"); + return -500; + } + +#ifdef HAVE_WINSOCK + SOCKET sock = (SOCKET) mSock; +#else + int sock = (int) mSock; +#endif + int cc; + + cc = recv(sock, (char*)buf, len, 0); + if (cc < 0) { + int err = getSocketError(); + return (err > 0) ? -err : -1; + } + + return cc; +} + +/* + * Write data to a socket. + * + * Standard semantics: write up to "len" bytes into "buf". Returns the + * number of bytes written, or less than zero on error. + */ +int Socket::write(const void* buf, ssize_t len) const +{ + if (mSock == UNDEF_SOCKET) { + LOG(LOG_ERROR, "socket", "ERROR: write on invalid socket\n"); + return -500; + } + +#ifdef HAVE_WINSOCK + SOCKET sock = (SOCKET) mSock; +#else + int sock = (int) mSock; +#endif + int cc; + + cc = send(sock, (const char*)buf, len, 0); + if (cc < 0) { + int err = getSocketError(); + return (err > 0) ? -err : -1; + } + + return cc; +} + + +/* + * =========================================================================== + * Socket tests + * =========================================================================== + */ + +/* + * Read all data from the socket. The data is read into a buffer that + * expands as needed. + * + * On exit, the buffer is returned, and the length of the data is stored + * in "*sz". A null byte is added to the end, but is not included in + * the length. + */ +static char* socketReadAll(const Socket& s, int *sz) +{ + int max, r; + char *data, *ptr, *tmp; + + data = (char*) malloc(max = 32768); + if (data == NULL) + return NULL; + + ptr = data; + + for (;;) { + if ((ptr - data) == max) { + tmp = (char*) realloc(data, max *= 2); + if(tmp == 0) { + free(data); + return 0; + } + } + r = s.read(ptr, max - (ptr - data)); + if (r == 0) + break; + if (r < 0) { + LOG(LOG_WARN, "socket", "WARNING: socket read failed (res=%d)\n",r); + break; + } + ptr += r; + } + + if ((ptr - data) == max) { + tmp = (char*) realloc(data, max + 1); + if (tmp == NULL) { + free(data); + return NULL; + } + } + *ptr = '\0'; + *sz = (ptr - data); + return data; +} + +/* + * Exercise the Socket class. + */ +void android::TestSockets(void) +{ + printf("----- SOCKET TEST ------\n"); + Socket::bootInit(); + + char* buf = NULL; + int len, cc; + const char* kTestStr = + "GET / HTTP/1.0\n" + "Connection: close\n" + "\n"; + + Socket sock; + if (sock.connect("www.google.com", 80) != 0) { + fprintf(stderr, "socket connected failed\n"); + goto bail; + } + + cc = sock.write(kTestStr, strlen(kTestStr)); + if (cc != (int) strlen(kTestStr)) { + fprintf(stderr, "write failed, res=%d\n", cc); + goto bail; + } + buf = socketReadAll(sock, &len); + + printf("GOT '%s'\n", buf); + +bail: + sock.close(); + free(buf); +} + diff --git a/libs/utils/Static.cpp b/libs/utils/Static.cpp new file mode 100644 index 000000000..93f7e4f0c --- /dev/null +++ b/libs/utils/Static.cpp @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// All static variables go here, to control initialization and +// destruction order in the library. + +#include + +#include +#include +#include + +namespace android { + +class LibUtilsFirstStatics +{ +public: + LibUtilsFirstStatics() + { + initialize_string8(); + initialize_string16(); + } + + ~LibUtilsFirstStatics() + { + terminate_string16(); + terminate_string8(); + } +}; + +static LibUtilsFirstStatics gFirstStatics; +int gDarwinCantLoadAllObjects = 1; + +// ------------ Text output streams + +Vector gTextBuffers; + +class LogTextOutput : public BufferedTextOutput +{ +public: + LogTextOutput() : BufferedTextOutput(MULTITHREADED) { } + virtual ~LogTextOutput() { }; + +protected: + virtual status_t writeLines(const struct iovec& vec, size_t N) + { + android_writevLog(&vec, N); + return NO_ERROR; + } +}; + +class FdTextOutput : public BufferedTextOutput +{ +public: + FdTextOutput(int fd) : BufferedTextOutput(MULTITHREADED), mFD(fd) { } + virtual ~FdTextOutput() { }; + +protected: + virtual status_t writeLines(const struct iovec& vec, size_t N) + { + writev(mFD, &vec, N); + return NO_ERROR; + } + +private: + int mFD; +}; + +static LogTextOutput gLogTextOutput; +static FdTextOutput gStdoutTextOutput(STDOUT_FILENO); +static FdTextOutput gStderrTextOutput(STDERR_FILENO); + +TextOutput& alog(gLogTextOutput); +TextOutput& aout(gStdoutTextOutput); +TextOutput& aerr(gStderrTextOutput); + +#ifndef LIBUTILS_NATIVE + +// ------------ ProcessState.cpp + +Mutex gProcessMutex; +sp gProcess; + +class LibUtilsIPCtStatics +{ +public: + LibUtilsIPCtStatics() + { + } + + ~LibUtilsIPCtStatics() + { + IPCThreadState::shutdown(); + } +}; + +static LibUtilsIPCtStatics gIPCStatics; + +// ------------ ServiceManager.cpp + +Mutex gDefaultServiceManagerLock; +sp gDefaultServiceManager; +sp gPermissionController; + +#endif + +} // namespace android diff --git a/libs/utils/StopWatch.cpp b/libs/utils/StopWatch.cpp new file mode 100644 index 000000000..68a1c5217 --- /dev/null +++ b/libs/utils/StopWatch.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2005 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 "StopWatch" + +#include +#include +#include + +#include +#include +#include + +/*****************************************************************************/ + +namespace android { + + +StopWatch::StopWatch(const char *name, int clock, uint32_t flags) + : mName(name), mClock(clock), mFlags(flags), + mStartTime(0), mNumLaps(0) +{ + mStartTime = systemTime(mClock); +} + +StopWatch::~StopWatch() +{ + nsecs_t elapsed = elapsedTime(); + const int n = mNumLaps; + LOGD("StopWatch %s (us): %lld ", mName, ns2us(elapsed)); + for (int i=0 ; i= 8) { + elapsed = 0; + } else { + const int n = mNumLaps; + mLaps[n].soFar = elapsed; + mLaps[n].thisLap = n ? (elapsed - mLaps[n-1].soFar) : elapsed; + mNumLaps = n+1; + } + return elapsed; +} + +nsecs_t StopWatch::elapsedTime() const +{ + return systemTime(mClock) - mStartTime; +} + + +/*****************************************************************************/ + +}; // namespace android + diff --git a/libs/utils/String16.cpp b/libs/utils/String16.cpp new file mode 100644 index 000000000..1f81cadb7 --- /dev/null +++ b/libs/utils/String16.cpp @@ -0,0 +1,609 @@ +/* + * Copyright (C) 2005 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 + +#include +#include +#include +#include +#include + +#include + +#ifdef HAVE_WINSOCK +# undef nhtol +# undef htonl +# undef nhtos +# undef htons + +# ifdef HAVE_LITTLE_ENDIAN +# define ntohl(x) ( ((x) << 24) | (((x) >> 24) & 255) | (((x) << 8) & 0xff0000) | (((x) >> 8) & 0xff00) ) +# define htonl(x) ntohl(x) +# define ntohs(x) ( (((x) << 8) & 0xff00) | (((x) >> 8) & 255) ) +# define htons(x) ntohs(x) +# else +# define ntohl(x) (x) +# define htonl(x) (x) +# define ntohs(x) (x) +# define htons(x) (x) +# endif +#else +# include +#endif + +#include +#include +#include + +// --------------------------------------------------------------------------- + +int strcmp16(const char16_t *s1, const char16_t *s2) +{ + char16_t ch; + int d = 0; + + while ( 1 ) { + d = (int)(ch = *s1++) - (int)*s2++; + if ( d || !ch ) + break; + } + + return d; +} + +int strncmp16(const char16_t *s1, const char16_t *s2, size_t n) +{ + char16_t ch; + int d = 0; + + while ( n-- ) { + d = (int)(ch = *s1++) - (int)*s2++; + if ( d || !ch ) + break; + } + + return d; +} + +char16_t *strcpy16(char16_t *dst, const char16_t *src) +{ + char16_t *q = dst; + const char16_t *p = src; + char16_t ch; + + do { + *q++ = ch = *p++; + } while ( ch ); + + return dst; +} + +size_t strlen16(const char16_t *s) +{ + const char16_t *ss = s; + while ( *ss ) + ss++; + return ss-s; +} + + +char16_t *strncpy16(char16_t *dst, const char16_t *src, size_t n) +{ + char16_t *q = dst; + const char16_t *p = src; + char ch; + + while (n) { + n--; + *q++ = ch = *p++; + if ( !ch ) + break; + } + + *q = 0; + + return dst; +} + +size_t strnlen16(const char16_t *s, size_t maxlen) +{ + const char16_t *ss = s; + + /* Important: the maxlen test must precede the reference through ss; + since the byte beyond the maximum may segfault */ + while ((maxlen > 0) && *ss) { + ss++; + maxlen--; + } + return ss-s; +} + +int strzcmp16(const char16_t *s1, size_t n1, const char16_t *s2, size_t n2) +{ + const char16_t* e1 = s1+n1; + const char16_t* e2 = s2+n2; + + while (s1 < e1 && s2 < e2) { + const int d = (int)*s1++ - (int)*s2++; + if (d) { + return d; + } + } + + return n1 < n2 + ? (0 - (int)*s2) + : (n1 > n2 + ? ((int)*s1 - 0) + : 0); +} + +int strzcmp16_h_n(const char16_t *s1H, size_t n1, const char16_t *s2N, size_t n2) +{ + const char16_t* e1 = s1H+n1; + const char16_t* e2 = s2N+n2; + + while (s1H < e1 && s2N < e2) { + const char16_t c2 = ntohs(*s2N); + const int d = (int)*s1H++ - (int)c2; + s2N++; + if (d) { + return d; + } + } + + return n1 < n2 + ? (0 - (int)ntohs(*s2N)) + : (n1 > n2 + ? ((int)*s1H - 0) + : 0); +} + +// --------------------------------------------------------------------------- + +namespace android { + +static inline size_t +utf8_char_len(uint8_t ch) +{ + return ((0xe5000000 >> ((ch >> 3) & 0x1e)) & 3) + 1; +} + +#define UTF8_SHIFT_AND_MASK(unicode, byte) (unicode)<<=6; (unicode) |= (0x3f & (byte)); + +static inline uint32_t +utf8_to_utf32(const uint8_t *src, size_t length) +{ + uint32_t unicode; + + switch (length) + { + case 1: + return src[0]; + case 2: + unicode = src[0] & 0x1f; + UTF8_SHIFT_AND_MASK(unicode, src[1]) + return unicode; + case 3: + unicode = src[0] & 0x0f; + UTF8_SHIFT_AND_MASK(unicode, src[1]) + UTF8_SHIFT_AND_MASK(unicode, src[2]) + return unicode; + case 4: + unicode = src[0] & 0x07; + UTF8_SHIFT_AND_MASK(unicode, src[1]) + UTF8_SHIFT_AND_MASK(unicode, src[2]) + UTF8_SHIFT_AND_MASK(unicode, src[3]) + return unicode; + default: + return 0xffff; + } + + //printf("Char at %p: len=%d, utf-16=%p\n", src, length, (void*)result); +} + +// --------------------------------------------------------------------------- + +static SharedBuffer* gEmptyStringBuf = NULL; +static char16_t* gEmptyString = NULL; + +static inline char16_t* getEmptyString() +{ + gEmptyStringBuf->acquire(); + return gEmptyString; +} + +void initialize_string16() +{ + SharedBuffer* buf = SharedBuffer::alloc(sizeof(char16_t)); + char16_t* str = (char16_t*)buf->data(); + *str = 0; + gEmptyStringBuf = buf; + gEmptyString = str; +} + +void terminate_string16() +{ + SharedBuffer::bufferFromData(gEmptyString)->release(); + gEmptyStringBuf = NULL; + gEmptyString = NULL; +} + +// --------------------------------------------------------------------------- + +// Note: not dealing with generating surrogate pairs. +static char16_t* allocFromUTF8(const char* in, size_t len) +{ + if (len == 0) return getEmptyString(); + + size_t chars = 0; + const char* end = in+len; + const char* p = in; + + while (p < end) { + chars++; + p += utf8_char_len(*p); + } + + SharedBuffer* buf = SharedBuffer::alloc((chars+1)*sizeof(char16_t)); + if (buf) { + p = in; + char16_t* str = (char16_t*)buf->data(); + char16_t* d = str; + while (p < end) { + size_t len = utf8_char_len(*p); + *d++ = (char16_t)utf8_to_utf32((const uint8_t*)p, len); + p += len; + } + *d = 0; + + //printf("Created UTF-16 string from UTF-8 \"%s\":", in); + //printHexData(1, str, buf->size(), 16, 1); + //printf("\n"); + + return str; + } + + return getEmptyString(); +} + +// --------------------------------------------------------------------------- + +String16::String16() + : mString(getEmptyString()) +{ +} + +String16::String16(const String16& o) + : mString(o.mString) +{ + SharedBuffer::bufferFromData(mString)->acquire(); +} + +String16::String16(const String16& o, size_t len, size_t begin) + : mString(getEmptyString()) +{ + setTo(o, len, begin); +} + +String16::String16(const char16_t* o) +{ + size_t len = strlen16(o); + SharedBuffer* buf = SharedBuffer::alloc((len+1)*sizeof(char16_t)); + LOG_ASSERT(buf, "Unable to allocate shared buffer"); + if (buf) { + char16_t* str = (char16_t*)buf->data(); + strcpy16(str, o); + mString = str; + return; + } + + mString = getEmptyString(); +} + +String16::String16(const char16_t* o, size_t len) +{ + SharedBuffer* buf = SharedBuffer::alloc((len+1)*sizeof(char16_t)); + LOG_ASSERT(buf, "Unable to allocate shared buffer"); + if (buf) { + char16_t* str = (char16_t*)buf->data(); + memcpy(str, o, len*sizeof(char16_t)); + str[len] = 0; + mString = str; + return; + } + + mString = getEmptyString(); +} + +String16::String16(const String8& o) + : mString(allocFromUTF8(o.string(), o.size())) +{ +} + +String16::String16(const char* o) + : mString(allocFromUTF8(o, strlen(o))) +{ +} + +String16::String16(const char* o, size_t len) + : mString(allocFromUTF8(o, len)) +{ +} + +String16::~String16() +{ + SharedBuffer::bufferFromData(mString)->release(); +} + +void String16::setTo(const String16& other) +{ + SharedBuffer::bufferFromData(other.mString)->acquire(); + SharedBuffer::bufferFromData(mString)->release(); + mString = other.mString; +} + +status_t String16::setTo(const String16& other, size_t len, size_t begin) +{ + const size_t N = other.size(); + if (begin >= N) { + SharedBuffer::bufferFromData(mString)->release(); + mString = getEmptyString(); + return NO_ERROR; + } + if ((begin+len) > N) len = N-begin; + if (begin == 0 && len == N) { + setTo(other); + return NO_ERROR; + } + + if (&other == this) { + LOG_ALWAYS_FATAL("Not implemented"); + } + + return setTo(other.string()+begin, len); +} + +status_t String16::setTo(const char16_t* other) +{ + return setTo(other, strlen16(other)); +} + +status_t String16::setTo(const char16_t* other, size_t len) +{ + SharedBuffer* buf = SharedBuffer::bufferFromData(mString) + ->editResize((len+1)*sizeof(char16_t)); + if (buf) { + char16_t* str = (char16_t*)buf->data(); + memcpy(str, other, len*sizeof(char16_t)); + str[len] = 0; + mString = str; + return NO_ERROR; + } + return NO_MEMORY; +} + +status_t String16::append(const String16& other) +{ + const size_t myLen = size(); + const size_t otherLen = other.size(); + if (myLen == 0) { + setTo(other); + return NO_ERROR; + } else if (otherLen == 0) { + return NO_ERROR; + } + + SharedBuffer* buf = SharedBuffer::bufferFromData(mString) + ->editResize((myLen+otherLen+1)*sizeof(char16_t)); + if (buf) { + char16_t* str = (char16_t*)buf->data(); + memcpy(str+myLen, other, (otherLen+1)*sizeof(char16_t)); + mString = str; + return NO_ERROR; + } + return NO_MEMORY; +} + +status_t String16::append(const char16_t* chrs, size_t otherLen) +{ + const size_t myLen = size(); + if (myLen == 0) { + setTo(chrs, otherLen); + return NO_ERROR; + } else if (otherLen == 0) { + return NO_ERROR; + } + + SharedBuffer* buf = SharedBuffer::bufferFromData(mString) + ->editResize((myLen+otherLen+1)*sizeof(char16_t)); + if (buf) { + char16_t* str = (char16_t*)buf->data(); + memcpy(str+myLen, chrs, otherLen*sizeof(char16_t)); + str[myLen+otherLen] = 0; + mString = str; + return NO_ERROR; + } + return NO_MEMORY; +} + +status_t String16::insert(size_t pos, const char16_t* chrs) +{ + return insert(pos, chrs, strlen16(chrs)); +} + +status_t String16::insert(size_t pos, const char16_t* chrs, size_t len) +{ + const size_t myLen = size(); + if (myLen == 0) { + return setTo(chrs, len); + return NO_ERROR; + } else if (len == 0) { + return NO_ERROR; + } + + if (pos > myLen) pos = myLen; + + #if 0 + printf("Insert in to %s: pos=%d, len=%d, myLen=%d, chrs=%s\n", + String8(*this).string(), pos, + len, myLen, String8(chrs, len).string()); + #endif + + SharedBuffer* buf = SharedBuffer::bufferFromData(mString) + ->editResize((myLen+len+1)*sizeof(char16_t)); + if (buf) { + char16_t* str = (char16_t*)buf->data(); + if (pos < myLen) { + memmove(str+pos+len, str+pos, (myLen-pos)*sizeof(char16_t)); + } + memcpy(str+pos, chrs, len*sizeof(char16_t)); + str[myLen+len] = 0; + mString = str; + #if 0 + printf("Result (%d chrs): %s\n", size(), String8(*this).string()); + #endif + return NO_ERROR; + } + return NO_MEMORY; +} + +ssize_t String16::findFirst(char16_t c) const +{ + const char16_t* str = string(); + const char16_t* p = str; + const char16_t* e = p + size(); + while (p < e) { + if (*p == c) { + return p-str; + } + p++; + } + return -1; +} + +ssize_t String16::findLast(char16_t c) const +{ + const char16_t* str = string(); + const char16_t* p = str; + const char16_t* e = p + size(); + while (p < e) { + e--; + if (*e == c) { + return e-str; + } + } + return -1; +} + +bool String16::startsWith(const String16& prefix) const +{ + const size_t ps = prefix.size(); + if (ps > size()) return false; + return strzcmp16(mString, ps, prefix.string(), ps) == 0; +} + +bool String16::startsWith(const char16_t* prefix) const +{ + const size_t ps = strlen16(prefix); + if (ps > size()) return false; + return strncmp16(mString, prefix, ps) == 0; +} + +status_t String16::makeLower() +{ + const size_t N = size(); + const char16_t* str = string(); + char16_t* edit = NULL; + for (size_t i=0; i= 'A' && v <= 'Z') { + if (!edit) { + SharedBuffer* buf = SharedBuffer::bufferFromData(mString)->edit(); + if (!buf) { + return NO_MEMORY; + } + edit = (char16_t*)buf->data(); + mString = str = edit; + } + edit[i] = tolower((char)v); + } + } + return NO_ERROR; +} + +status_t String16::replaceAll(char16_t replaceThis, char16_t withThis) +{ + const size_t N = size(); + const char16_t* str = string(); + char16_t* edit = NULL; + for (size_t i=0; iedit(); + if (!buf) { + return NO_MEMORY; + } + edit = (char16_t*)buf->data(); + mString = str = edit; + } + edit[i] = withThis; + } + } + return NO_ERROR; +} + +status_t String16::remove(size_t len, size_t begin) +{ + const size_t N = size(); + if (begin >= N) { + SharedBuffer::bufferFromData(mString)->release(); + mString = getEmptyString(); + return NO_ERROR; + } + if ((begin+len) > N) len = N-begin; + if (begin == 0 && len == N) { + return NO_ERROR; + } + + if (begin > 0) { + SharedBuffer* buf = SharedBuffer::bufferFromData(mString) + ->editResize((N+1)*sizeof(char16_t)); + if (!buf) { + return NO_MEMORY; + } + char16_t* str = (char16_t*)buf->data(); + memmove(str, str+begin, (N-begin+1)*sizeof(char16_t)); + mString = str; + } + SharedBuffer* buf = SharedBuffer::bufferFromData(mString) + ->editResize((len+1)*sizeof(char16_t)); + if (buf) { + char16_t* str = (char16_t*)buf->data(); + str[len] = 0; + mString = str; + return NO_ERROR; + } + return NO_MEMORY; +} + +TextOutput& operator<<(TextOutput& to, const String16& val) +{ + to << String8(val).string(); + return to; +} + +}; // namespace android diff --git a/libs/utils/String8.cpp b/libs/utils/String8.cpp new file mode 100644 index 000000000..c50d343a7 --- /dev/null +++ b/libs/utils/String8.cpp @@ -0,0 +1,604 @@ +/* + * Copyright (C) 2005 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 + +#include +#include +#include +#include + +#include + +#include + +namespace android { + +// --------------------------------------------------------------------------- + +static const uint32_t kByteMask = 0x000000BF; +static const uint32_t kByteMark = 0x00000080; + +// Surrogates aren't valid for UTF-32 characters, so define some +// constants that will let us screen them out. +static const uint32_t kUnicodeSurrogateHighStart = 0x0000D800; +static const uint32_t kUnicodeSurrogateHighEnd = 0x0000DBFF; +static const uint32_t kUnicodeSurrogateLowStart = 0x0000DC00; +static const uint32_t kUnicodeSurrogateLowEnd = 0x0000DFFF; +static const uint32_t kUnicodeSurrogateStart = kUnicodeSurrogateHighStart; +static const uint32_t kUnicodeSurrogateEnd = kUnicodeSurrogateLowEnd; + +// Mask used to set appropriate bits in first byte of UTF-8 sequence, +// indexed by number of bytes in the sequence. +static const uint32_t kFirstByteMark[] = { + 0x00000000, 0x00000000, 0x000000C0, 0x000000E0, 0x000000F0 +}; + +// Separator used by resource paths. This is not platform dependent contrary +// to OS_PATH_SEPARATOR. +#define RES_PATH_SEPARATOR '/' + +// Return number of utf8 bytes required for the character. +static size_t utf32_to_utf8_bytes(uint32_t srcChar) +{ + size_t bytesToWrite; + + // Figure out how many bytes the result will require. + if (srcChar < 0x00000080) + { + bytesToWrite = 1; + } + else if (srcChar < 0x00000800) + { + bytesToWrite = 2; + } + else if (srcChar < 0x00010000) + { + if ((srcChar < kUnicodeSurrogateStart) + || (srcChar > kUnicodeSurrogateEnd)) + { + bytesToWrite = 3; + } + else + { + // Surrogates are invalid UTF-32 characters. + return 0; + } + } + // Max code point for Unicode is 0x0010FFFF. + else if (srcChar < 0x00110000) + { + bytesToWrite = 4; + } + else + { + // Invalid UTF-32 character. + return 0; + } + + return bytesToWrite; +} + +// Write out the source character to . + +static void utf32_to_utf8(uint8_t* dstP, uint32_t srcChar, size_t bytes) +{ + dstP += bytes; + switch (bytes) + { /* note: everything falls through. */ + case 4: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6; + case 3: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6; + case 2: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6; + case 1: *--dstP = (uint8_t)(srcChar | kFirstByteMark[bytes]); + } +} + +// --------------------------------------------------------------------------- + +static SharedBuffer* gEmptyStringBuf = NULL; +static char* gEmptyString = NULL; + +extern int gDarwinCantLoadAllObjects; +int gDarwinIsReallyAnnoying; + +static inline char* getEmptyString() +{ + gEmptyStringBuf->acquire(); + return gEmptyString; +} + +void initialize_string8() +{ +#ifdef LIBUTILS_NATIVE + // Bite me, Darwin! + gDarwinIsReallyAnnoying = gDarwinCantLoadAllObjects; +#endif + + SharedBuffer* buf = SharedBuffer::alloc(1); + char* str = (char*)buf->data(); + *str = 0; + gEmptyStringBuf = buf; + gEmptyString = str; +} + +void terminate_string8() +{ + SharedBuffer::bufferFromData(gEmptyString)->release(); + gEmptyStringBuf = NULL; + gEmptyString = NULL; +} + +// --------------------------------------------------------------------------- + +static char* allocFromUTF8(const char* in, size_t len) +{ + if (len > 0) { + SharedBuffer* buf = SharedBuffer::alloc(len+1); + LOG_ASSERT(buf, "Unable to allocate shared buffer"); + if (buf) { + char* str = (char*)buf->data(); + memcpy(str, in, len); + str[len] = 0; + return str; + } + return NULL; + } + + return getEmptyString(); +} + +// Note: not dealing with expanding surrogate pairs. +static char* allocFromUTF16(const char16_t* in, size_t len) +{ + if (len == 0) return getEmptyString(); + + size_t bytes = 0; + const char16_t* end = in+len; + const char16_t* p = in; + + while (p < end) { + bytes += utf32_to_utf8_bytes(*p); + p++; + } + + SharedBuffer* buf = SharedBuffer::alloc(bytes+1); + LOG_ASSERT(buf, "Unable to allocate shared buffer"); + if (buf) { + p = in; + char* str = (char*)buf->data(); + char* d = str; + while (p < end) { + uint32_t c = *p++; + size_t len = utf32_to_utf8_bytes(c); + utf32_to_utf8((uint8_t*)d, c, len); + d += len; + } + *d = 0; + + return str; + } + + return getEmptyString(); +} + +// --------------------------------------------------------------------------- + +String8::String8() + : mString(getEmptyString()) +{ +} + +String8::String8(const String8& o) + : mString(o.mString) +{ + SharedBuffer::bufferFromData(mString)->acquire(); +} + +String8::String8(const char* o) + : mString(allocFromUTF8(o, strlen(o))) +{ + if (mString == NULL) { + mString = getEmptyString(); + } +} + +String8::String8(const char* o, size_t len) + : mString(allocFromUTF8(o, len)) +{ + if (mString == NULL) { + mString = getEmptyString(); + } +} + +String8::String8(const String16& o) + : mString(allocFromUTF16(o.string(), o.size())) +{ +} + +String8::String8(const char16_t* o) + : mString(allocFromUTF16(o, strlen16(o))) +{ +} + +String8::String8(const char16_t* o, size_t len) + : mString(allocFromUTF16(o, len)) +{ +} + +String8::~String8() +{ + SharedBuffer::bufferFromData(mString)->release(); +} + +void String8::setTo(const String8& other) +{ + SharedBuffer::bufferFromData(other.mString)->acquire(); + SharedBuffer::bufferFromData(mString)->release(); + mString = other.mString; +} + +status_t String8::setTo(const char* other) +{ + SharedBuffer::bufferFromData(mString)->release(); + mString = allocFromUTF8(other, strlen(other)); + if (mString) return NO_ERROR; + + mString = getEmptyString(); + return NO_MEMORY; +} + +status_t String8::setTo(const char* other, size_t len) +{ + SharedBuffer::bufferFromData(mString)->release(); + mString = allocFromUTF8(other, len); + if (mString) return NO_ERROR; + + mString = getEmptyString(); + return NO_MEMORY; +} + +status_t String8::setTo(const char16_t* other, size_t len) +{ + SharedBuffer::bufferFromData(mString)->release(); + mString = allocFromUTF16(other, len); + if (mString) return NO_ERROR; + + mString = getEmptyString(); + return NO_MEMORY; +} + +status_t String8::append(const String8& other) +{ + const size_t otherLen = other.bytes(); + if (bytes() == 0) { + setTo(other); + return NO_ERROR; + } else if (otherLen == 0) { + return NO_ERROR; + } + + return real_append(other.string(), otherLen); +} + +status_t String8::append(const char* other) +{ + return append(other, strlen(other)); +} + +status_t String8::append(const char* other, size_t otherLen) +{ + if (bytes() == 0) { + return setTo(other, otherLen); + } else if (otherLen == 0) { + return NO_ERROR; + } + + return real_append(other, otherLen); +} + +status_t String8::real_append(const char* other, size_t otherLen) +{ + const size_t myLen = bytes(); + + SharedBuffer* buf = SharedBuffer::bufferFromData(mString) + ->editResize(myLen+otherLen+1); + if (buf) { + char* str = (char*)buf->data(); + mString = str; + str += myLen; + memcpy(str, other, otherLen); + str[otherLen] = '\0'; + return NO_ERROR; + } + return NO_MEMORY; +} + +char* String8::lockBuffer(size_t size) +{ + SharedBuffer* buf = SharedBuffer::bufferFromData(mString) + ->editResize(size+1); + if (buf) { + char* str = (char*)buf->data(); + mString = str; + return str; + } + return NULL; +} + +void String8::unlockBuffer() +{ + unlockBuffer(strlen(mString)); +} + +status_t String8::unlockBuffer(size_t size) +{ + if (size != this->size()) { + SharedBuffer* buf = SharedBuffer::bufferFromData(mString) + ->editResize(size+1); + if (buf) { + char* str = (char*)buf->data(); + str[size] = 0; + mString = str; + return NO_ERROR; + } + } + + return NO_MEMORY; +} + +ssize_t String8::find(const char* other, size_t start) const +{ + size_t len = size(); + if (start >= len) { + return -1; + } + const char* s = mString+start; + const char* p = strstr(s, other); + return p ? p-mString : -1; +} + +void String8::toLower() +{ + toLower(0, size()); +} + +void String8::toLower(size_t start, size_t length) +{ + const size_t len = size(); + if (start >= len) { + return; + } + if (start+length > len) { + length = len-start; + } + char* buf = lockBuffer(len); + buf += start; + while (length > 0) { + *buf = tolower(*buf); + buf++; + length--; + } + unlockBuffer(len); +} + +void String8::toUpper() +{ + toUpper(0, size()); +} + +void String8::toUpper(size_t start, size_t length) +{ + const size_t len = size(); + if (start >= len) { + return; + } + if (start+length > len) { + length = len-start; + } + char* buf = lockBuffer(len); + buf += start; + while (length > 0) { + *buf = toupper(*buf); + buf++; + length--; + } + unlockBuffer(len); +} + +TextOutput& operator<<(TextOutput& to, const String8& val) +{ + to << val.string(); + return to; +} + +// --------------------------------------------------------------------------- +// Path functions + + +void String8::setPathName(const char* name) +{ + setPathName(name, strlen(name)); +} + +void String8::setPathName(const char* name, size_t len) +{ + char* buf = lockBuffer(len); + + memcpy(buf, name, len); + + // remove trailing path separator, if present + if (len > 0 && buf[len-1] == OS_PATH_SEPARATOR) + len--; + + buf[len] = '\0'; + + unlockBuffer(len); +} + +String8 String8::getPathLeaf(void) const +{ + const char* cp; + const char*const buf = mString; + + cp = strrchr(buf, OS_PATH_SEPARATOR); + if (cp == NULL) + return String8(*this); + else + return String8(cp+1); +} + +String8 String8::getPathDir(void) const +{ + const char* cp; + const char*const str = mString; + + cp = strrchr(str, OS_PATH_SEPARATOR); + if (cp == NULL) + return String8(""); + else + return String8(str, cp - str); +} + +String8 String8::walkPath(String8* outRemains) const +{ + const char* cp; + const char*const str = mString; + const char* buf = str; + + cp = strchr(buf, OS_PATH_SEPARATOR); + if (cp == buf) { + // don't include a leading '/'. + buf = buf+1; + cp = strchr(buf, OS_PATH_SEPARATOR); + } + + if (cp == NULL) { + String8 res = buf != str ? String8(buf) : *this; + if (outRemains) *outRemains = String8(""); + return res; + } + + String8 res(buf, cp-buf); + if (outRemains) *outRemains = String8(cp+1); + return res; +} + +/* + * Helper function for finding the start of an extension in a pathname. + * + * Returns a pointer inside mString, or NULL if no extension was found. + */ +char* String8::find_extension(void) const +{ + const char* lastSlash; + const char* lastDot; + int extLen; + const char* const str = mString; + + // only look at the filename + lastSlash = strrchr(str, OS_PATH_SEPARATOR); + if (lastSlash == NULL) + lastSlash = str; + else + lastSlash++; + + // find the last dot + lastDot = strrchr(lastSlash, '.'); + if (lastDot == NULL) + return NULL; + + // looks good, ship it + return const_cast(lastDot); +} + +String8 String8::getPathExtension(void) const +{ + char* ext; + + ext = find_extension(); + if (ext != NULL) + return String8(ext); + else + return String8(""); +} + +String8 String8::getBasePath(void) const +{ + char* ext; + const char* const str = mString; + + ext = find_extension(); + if (ext == NULL) + return String8(*this); + else + return String8(str, ext - str); +} + +String8& String8::appendPath(const char* name) +{ + // TODO: The test below will fail for Win32 paths. Fix later or ignore. + if (name[0] != OS_PATH_SEPARATOR) { + if (*name == '\0') { + // nothing to do + return *this; + } + + size_t len = length(); + if (len == 0) { + // no existing filename, just use the new one + setPathName(name); + return *this; + } + + // make room for oldPath + '/' + newPath + int newlen = strlen(name); + + char* buf = lockBuffer(len+1+newlen); + + // insert a '/' if needed + if (buf[len-1] != OS_PATH_SEPARATOR) + buf[len++] = OS_PATH_SEPARATOR; + + memcpy(buf+len, name, newlen+1); + len += newlen; + + unlockBuffer(len); + + return *this; + } else { + setPathName(name); + return *this; + } +} + +String8& String8::convertToResPath() +{ +#if OS_PATH_SEPARATOR != RES_PATH_SEPARATOR + size_t len = length(); + if (len > 0) { + char * buf = lockBuffer(len); + for (char * end = buf + len; buf < end; ++buf) { + if (*buf == OS_PATH_SEPARATOR) + *buf = RES_PATH_SEPARATOR; + } + unlockBuffer(len); + } +#endif + return *this; +} + + +}; // namespace android diff --git a/libs/utils/SystemClock.cpp b/libs/utils/SystemClock.cpp new file mode 100644 index 000000000..2bdc0ce27 --- /dev/null +++ b/libs/utils/SystemClock.cpp @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +/* + * System clock functions. + */ + +#if HAVE_ANDROID_OS +#include +#include +#include +#include +#endif + +#include +#include +#include +#include +#include + +#include +#include + +#define LOG_TAG "SystemClock" +#include "utils/Log.h" + +namespace android { + +/* + * Set the current time. This only works when running as root. + */ +int setCurrentTimeMillis(int64_t millis) +{ +#if WIN32 + // not implemented + return -1; +#else + struct timeval tv; +#if HAVE_ANDROID_OS + struct timespec ts; + int fd; + int res; +#endif + int ret = 0; + + if (millis <= 0 || millis / 1000LL >= INT_MAX) { + return -1; + } + + tv.tv_sec = (time_t) (millis / 1000LL); + tv.tv_usec = (suseconds_t) ((millis % 1000LL) * 1000LL); + + LOGD("Setting time of day to sec=%d\n", (int) tv.tv_sec); + +#if HAVE_ANDROID_OS + fd = open("/dev/alarm", O_RDWR); + if(fd < 0) { + LOGW("Unable to open alarm driver: %s\n", strerror(errno)); + return -1; + } + ts.tv_sec = tv.tv_sec; + ts.tv_nsec = tv.tv_usec * 1000; + res = ioctl(fd, ANDROID_ALARM_SET_RTC, &ts); + if(res < 0) { + LOGW("Unable to set rtc to %ld: %s\n", tv.tv_sec, strerror(errno)); + ret = -1; + } + close(fd); +#else + if (settimeofday(&tv, NULL) != 0) { + LOGW("Unable to set clock to %d.%d: %s\n", + (int) tv.tv_sec, (int) tv.tv_usec, strerror(errno)); + ret = -1; + } +#endif + + return ret; +#endif // WIN32 +} + +/* + * native public static long uptimeMillis(); + */ +int64_t uptimeMillis() +{ + int64_t when = systemTime(SYSTEM_TIME_MONOTONIC); + return (int64_t) nanoseconds_to_milliseconds(when); +} + +/* + * native public static long elapsedRealtime(); + */ +int64_t elapsedRealtime() +{ +#if HAVE_ANDROID_OS + static int s_fd = -1; + + if (s_fd == -1) { + int fd = open("/dev/alarm", O_RDONLY); + if (android_atomic_cmpxchg(-1, fd, &s_fd)) { + close(fd); + } + } + + struct timespec ts; + int result = ioctl(s_fd, + ANDROID_ALARM_GET_TIME(ANDROID_ALARM_ELAPSED_REALTIME), &ts); + + if (result == 0) { + int64_t when = seconds_to_nanoseconds(ts.tv_sec) + ts.tv_nsec; + return (int64_t) nanoseconds_to_milliseconds(when); + } else { + // XXX: there was an error, probably because the driver didn't + // exist ... this should return + // a real error, like an exception! + int64_t when = systemTime(SYSTEM_TIME_MONOTONIC); + return (int64_t) nanoseconds_to_milliseconds(when); + } +#else + int64_t when = systemTime(SYSTEM_TIME_MONOTONIC); + return (int64_t) nanoseconds_to_milliseconds(when); +#endif +} + +}; // namespace android diff --git a/libs/utils/TextOutput.cpp b/libs/utils/TextOutput.cpp new file mode 100644 index 000000000..cebee99e5 --- /dev/null +++ b/libs/utils/TextOutput.cpp @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2005 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 + +#include + +#include +#include +#include + +// --------------------------------------------------------------------------- + +namespace android { + +TextOutput& operator<<(TextOutput& to, bool val) +{ + if (val) to.print("true", 4); + else to.print("false", 5); + return to; +} + +TextOutput& operator<<(TextOutput& to, int val) +{ + char buf[16]; + sprintf(buf, "%d", val); + to.print(buf, strlen(buf)); + return to; +} + +TextOutput& operator<<(TextOutput& to, long val) +{ + char buf[16]; + sprintf(buf, "%ld", val); + to.print(buf, strlen(buf)); + return to; +} + +TextOutput& operator<<(TextOutput& to, unsigned int val) +{ + char buf[16]; + sprintf(buf, "%u", val); + to.print(buf, strlen(buf)); + return to; +} + +TextOutput& operator<<(TextOutput& to, unsigned long val) +{ + char buf[16]; + sprintf(buf, "%lu", val); + to.print(buf, strlen(buf)); + return to; +} + +TextOutput& operator<<(TextOutput& to, long long val) +{ + char buf[32]; + sprintf(buf, "%Ld", val); + to.print(buf, strlen(buf)); + return to; +} + +TextOutput& operator<<(TextOutput& to, unsigned long long val) +{ + char buf[32]; + sprintf(buf, "%Lu", val); + to.print(buf, strlen(buf)); + return to; +} + +static TextOutput& print_float(TextOutput& to, double value) +{ + char buf[64]; + sprintf(buf, "%g", value); + if( !strchr(buf, '.') && !strchr(buf, 'e') && + !strchr(buf, 'E') ) { + strncat(buf, ".0", sizeof(buf)-1); + } + to.print(buf, strlen(buf)); + return to; +} + +TextOutput& operator<<(TextOutput& to, float val) +{ + return print_float(to,val); +} + +TextOutput& operator<<(TextOutput& to, double val) +{ + return print_float(to,val); +} + +TextOutput& operator<<(TextOutput& to, const void* val) +{ + char buf[16]; + sprintf(buf, "%p", val); + to.print(buf, strlen(buf)); + return to; +} + +static void textOutputPrinter(void* cookie, const char* txt) +{ + ((TextOutput*)cookie)->print(txt, strlen(txt)); +} + +TextOutput& operator<<(TextOutput& to, const TypeCode& val) +{ + printTypeCode(val.typeCode(), textOutputPrinter, (void*)&to); + return to; +} + +HexDump::HexDump(const void *buf, size_t size, size_t bytesPerLine) + : mBuffer(buf) + , mSize(size) + , mBytesPerLine(bytesPerLine) + , mSingleLineCutoff(16) + , mAlignment(4) + , mCArrayStyle(false) +{ + if (bytesPerLine >= 16) mAlignment = 4; + else if (bytesPerLine >= 8) mAlignment = 2; + else mAlignment = 1; +} + +TextOutput& operator<<(TextOutput& to, const HexDump& val) +{ + printHexData(0, val.buffer(), val.size(), val.bytesPerLine(), + val.singleLineCutoff(), val.alignment(), val.carrayStyle(), + textOutputPrinter, (void*)&to); + return to; +} + +}; // namespace android diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp new file mode 100644 index 000000000..5f407a990 --- /dev/null +++ b/libs/utils/Threads.cpp @@ -0,0 +1,1128 @@ +/* + * 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. + */ + +#define LOG_TAG "libutils.threads" + +#include +#include + +#include +#include +#include +#include +#include +#include + +#if defined(HAVE_PTHREADS) +# include +# include +# include +#elif defined(HAVE_WIN32_THREADS) +# include +# include +# include +# define HAVE_CREATETHREAD // Cygwin, vs. HAVE__BEGINTHREADEX for MinGW +#endif + +#if defined(HAVE_FUTEX) +#include +#endif + +#if defined(HAVE_PRCTL) +#include +#endif + +/* + * =========================================================================== + * Thread wrappers + * =========================================================================== + */ + +using namespace android; + +// ---------------------------------------------------------------------------- +#if defined(HAVE_PTHREADS) +#if 0 +#pragma mark - +#pragma mark PTHREAD +#endif +// ---------------------------------------------------------------------------- + +/* + * Create and run a new thead. + * + * We create it "detached", so it cleans up after itself. + */ + +typedef void* (*android_pthread_entry)(void*); + +struct thread_data_t { + thread_func_t entryFunction; + void* userData; + int priority; + char * threadName; + + // we use this trampoline when we need to set the priority with + // nice/setpriority. + static int trampoline(const thread_data_t* t) { + thread_func_t f = t->entryFunction; + void* u = t->userData; + int prio = t->priority; + char * name = t->threadName; + delete t; + setpriority(PRIO_PROCESS, 0, prio); + if (name) { +#if defined(HAVE_PRCTL) + // Mac OS doesn't have this, and we build libutil for the host too + int hasAt = 0; + int hasDot = 0; + char *s = name; + while (*s) { + if (*s == '.') hasDot = 1; + else if (*s == '@') hasAt = 1; + s++; + } + int len = s - name; + if (len < 15 || hasAt || !hasDot) { + s = name; + } else { + s = name + len - 15; + } + prctl(PR_SET_NAME, (unsigned long) s, 0, 0, 0); +#endif + free(name); + } + return f(u); + } +}; + +int androidCreateRawThreadEtc(android_thread_func_t entryFunction, + void *userData, + const char* threadName, + int32_t threadPriority, + size_t threadStackSize, + android_thread_id_t *threadId) +{ + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + +#ifdef HAVE_ANDROID_OS /* valgrind is rejecting RT-priority create reqs */ + if (threadPriority != PRIORITY_DEFAULT || threadName != NULL) { + // We could avoid the trampoline if there was a way to get to the + // android_thread_id_t (pid) from pthread_t + thread_data_t* t = new thread_data_t; + t->priority = threadPriority; + t->threadName = threadName ? strdup(threadName) : NULL; + t->entryFunction = entryFunction; + t->userData = userData; + entryFunction = (android_thread_func_t)&thread_data_t::trampoline; + userData = t; + } +#endif + + if (threadStackSize) { + pthread_attr_setstacksize(&attr, threadStackSize); + } + + errno = 0; + pthread_t thread; + int result = pthread_create(&thread, &attr, + (android_pthread_entry)entryFunction, userData); + if (result != 0) { + LOGE("androidCreateRawThreadEtc failed (entry=%p, res=%d, errno=%d)\n" + "(android threadPriority=%d)", + entryFunction, result, errno, threadPriority); + return 0; + } + + if (threadId != NULL) { + *threadId = (android_thread_id_t)thread; // XXX: this is not portable + } + return 1; +} + +android_thread_id_t androidGetThreadId() +{ + return (android_thread_id_t)pthread_self(); +} + +// ---------------------------------------------------------------------------- +#elif defined(HAVE_WIN32_THREADS) +#if 0 +#pragma mark - +#pragma mark WIN32_THREADS +#endif +// ---------------------------------------------------------------------------- + +/* + * Trampoline to make us __stdcall-compliant. + * + * We're expected to delete "vDetails" when we're done. + */ +struct threadDetails { + int (*func)(void*); + void* arg; +}; +static __stdcall unsigned int threadIntermediary(void* vDetails) +{ + struct threadDetails* pDetails = (struct threadDetails*) vDetails; + int result; + + result = (*(pDetails->func))(pDetails->arg); + + delete pDetails; + + LOG(LOG_VERBOSE, "thread", "thread exiting\n"); + return (unsigned int) result; +} + +/* + * Create and run a new thread. + */ +static bool doCreateThread(android_thread_func_t fn, void* arg, android_thread_id_t *id) +{ + HANDLE hThread; + struct threadDetails* pDetails = new threadDetails; // must be on heap + unsigned int thrdaddr; + + pDetails->func = fn; + pDetails->arg = arg; + +#if defined(HAVE__BEGINTHREADEX) + hThread = (HANDLE) _beginthreadex(NULL, 0, threadIntermediary, pDetails, 0, + &thrdaddr); + if (hThread == 0) +#elif defined(HAVE_CREATETHREAD) + hThread = CreateThread(NULL, 0, + (LPTHREAD_START_ROUTINE) threadIntermediary, + (void*) pDetails, 0, (DWORD*) &thrdaddr); + if (hThread == NULL) +#endif + { + LOG(LOG_WARN, "thread", "WARNING: thread create failed\n"); + return false; + } + +#if defined(HAVE_CREATETHREAD) + /* close the management handle */ + CloseHandle(hThread); +#endif + + if (id != NULL) { + *id = (android_thread_id_t)thrdaddr; + } + + return true; +} + +int androidCreateRawThreadEtc(android_thread_func_t fn, + void *userData, + const char* threadName, + int32_t threadPriority, + size_t threadStackSize, + android_thread_id_t *threadId) +{ + return doCreateThread( fn, userData, threadId); +} + +android_thread_id_t androidGetThreadId() +{ + return (android_thread_id_t)GetCurrentThreadId(); +} + +// ---------------------------------------------------------------------------- +#else +#error "Threads not supported" +#endif + +// ---------------------------------------------------------------------------- + +#if 0 +#pragma mark - +#pragma mark Common Thread functions +#endif + +int androidCreateThread(android_thread_func_t fn, void* arg) +{ + return createThreadEtc(fn, arg); +} + +int androidCreateThreadGetID(android_thread_func_t fn, void *arg, android_thread_id_t *id) +{ + return createThreadEtc(fn, arg, "android:unnamed_thread", + PRIORITY_DEFAULT, 0, id); +} + +static android_create_thread_fn gCreateThreadFn = androidCreateRawThreadEtc; + +int androidCreateThreadEtc(android_thread_func_t entryFunction, + void *userData, + const char* threadName, + int32_t threadPriority, + size_t threadStackSize, + android_thread_id_t *threadId) +{ + return gCreateThreadFn(entryFunction, userData, threadName, + threadPriority, threadStackSize, threadId); +} + +void androidSetCreateThreadFunc(android_create_thread_fn func) +{ + gCreateThreadFn = func; +} + +namespace android { + +/* + * =========================================================================== + * Mutex class + * =========================================================================== + */ + +#if 0 +#pragma mark - +#pragma mark Mutex +#endif + +#if defined(HAVE_PTHREADS) && !defined(HAVE_FUTEX) +/* + * Simple pthread wrapper. + */ + +Mutex::Mutex() +{ + _init(); +} + +Mutex::Mutex(const char* name) +{ + // XXX: name not used for now + _init(); +} + +void Mutex::_init() +{ + pthread_mutex_t* pMutex = new pthread_mutex_t; + pthread_mutex_init(pMutex, NULL); + mState = pMutex; +} + +Mutex::~Mutex() +{ + delete (pthread_mutex_t*) mState; +} + +status_t Mutex::lock() +{ + int res; + while ((res=pthread_mutex_lock((pthread_mutex_t*) mState)) == EINTR) ; + return -res; +} + +void Mutex::unlock() +{ + pthread_mutex_unlock((pthread_mutex_t*) mState); +} + +status_t Mutex::tryLock() +{ + int res; + while ((res=pthread_mutex_trylock((pthread_mutex_t*) mState)) == EINTR) ; + return -res; +} + +#elif defined(HAVE_FUTEX) +#if 0 +#pragma mark - +#endif + +#define STATE ((futex_mutex_t*) (&mState)) + +Mutex::Mutex() +{ + _init(); +} + +Mutex::Mutex(const char* name) +{ + _init(); +} + +void +Mutex::_init() +{ + futex_mutex_init(STATE); +} + +Mutex::~Mutex() +{ +} + +status_t Mutex::lock() +{ + int res; + while ((res=futex_mutex_lock(STATE, FUTEX_WAIT_INFINITE)) == EINTR) ; + return -res; +} + +void Mutex::unlock() +{ + futex_mutex_unlock(STATE); +} + +status_t Mutex::tryLock() +{ + int res; + while ((res=futex_mutex_trylock(STATE)) == EINTR) ; + return -res; +} +#undef STATE + +#elif defined(HAVE_WIN32_THREADS) +#if 0 +#pragma mark - +#endif + +Mutex::Mutex() +{ + HANDLE hMutex; + + assert(sizeof(hMutex) == sizeof(mState)); + + hMutex = CreateMutex(NULL, FALSE, NULL); + mState = (void*) hMutex; +} + +Mutex::Mutex(const char* name) +{ + // XXX: name not used for now + HANDLE hMutex; + + hMutex = CreateMutex(NULL, FALSE, NULL); + mState = (void*) hMutex; +} + +Mutex::~Mutex() +{ + CloseHandle((HANDLE) mState); +} + +status_t Mutex::lock() +{ + DWORD dwWaitResult; + dwWaitResult = WaitForSingleObject((HANDLE) mState, INFINITE); + return dwWaitResult != WAIT_OBJECT_0 ? -1 : NO_ERROR; +} + +void Mutex::unlock() +{ + if (!ReleaseMutex((HANDLE) mState)) + LOG(LOG_WARN, "thread", "WARNING: bad result from unlocking mutex\n"); +} + +status_t Mutex::tryLock() +{ + DWORD dwWaitResult; + + dwWaitResult = WaitForSingleObject((HANDLE) mState, 0); + if (dwWaitResult != WAIT_OBJECT_0 && dwWaitResult != WAIT_TIMEOUT) + LOG(LOG_WARN, "thread", "WARNING: bad result from try-locking mutex\n"); + return (dwWaitResult == WAIT_OBJECT_0) ? 0 : -1; +} + +#else +#error "Somebody forgot to implement threads for this platform." +#endif + + +/* + * =========================================================================== + * Condition class + * =========================================================================== + */ + +#if 0 +#pragma mark - +#pragma mark Condition +#endif + +#if defined(HAVE_PTHREADS) && !defined(HAVE_FUTEX) + +/* + * Constructor. This is a simple pthread wrapper. + */ +Condition::Condition() +{ + pthread_cond_t* pCond = new pthread_cond_t; + + pthread_cond_init(pCond, NULL); + mState = pCond; +} + +/* + * Destructor. + */ +Condition::~Condition() +{ + pthread_cond_destroy((pthread_cond_t*) mState); + delete (pthread_cond_t*) mState; +} + +/* + * Wait on a condition variable. Lock the mutex before calling. + */ + +status_t Condition::wait(Mutex& mutex) +{ + assert(mutex.mState != NULL); + + int cc; + while ((cc = pthread_cond_wait((pthread_cond_t*)mState, + (pthread_mutex_t*) mutex.mState)) == EINTR) ; + return -cc; +} + +status_t Condition::wait(Mutex& mutex, nsecs_t abstime) +{ + assert(mutex.mState != NULL); + + struct timespec ts; + ts.tv_sec = abstime/1000000000; + ts.tv_nsec = abstime-(ts.tv_sec*1000000000); + + int cc; + while ((cc = pthread_cond_timedwait((pthread_cond_t*)mState, + (pthread_mutex_t*) mutex.mState, &ts)) == EINTR) ; + return -cc; +} + +status_t Condition::waitRelative(Mutex& mutex, nsecs_t reltime) +{ + return wait(mutex, systemTime()+reltime); +} + +/* + * Signal the condition variable, allowing one thread to continue. + */ +void Condition::signal() +{ + pthread_cond_signal((pthread_cond_t*) mState); +} + +/* + * Signal the condition variable, allowing all threads to continue. + */ +void Condition::broadcast() +{ + pthread_cond_broadcast((pthread_cond_t*) mState); +} + +#elif defined(HAVE_FUTEX) +#if 0 +#pragma mark - +#endif + +#define STATE ((futex_cond_t*) (&mState)) + +/* + * Constructor. This is a simple pthread wrapper. + */ +Condition::Condition() +{ + futex_cond_init(STATE); +} + +/* + * Destructor. + */ +Condition::~Condition() +{ +} + +/* + * Wait on a condition variable. Lock the mutex before calling. + */ + +status_t Condition::wait(Mutex& mutex) +{ + assert(mutex.mState != NULL); + + int res; + while ((res = futex_cond_wait(STATE, + (futex_mutex_t*)(&mutex.mState), FUTEX_WAIT_INFINITE)) == -EINTR) ; + + return -res; +} + +status_t Condition::wait(Mutex& mutex, nsecs_t abstime) +{ + nsecs_t reltime = abstime - systemTime(); + if (reltime <= 0) return true; + return waitRelative(mutex, reltime); +} + +status_t Condition::waitRelative(Mutex& mutex, nsecs_t reltime) +{ + assert(mutex.mState != NULL); + int res; + unsigned msec = ns2ms(reltime); + if(msec == 0) + return true; + // This code will not time out at the correct time if interrupted by signals + while ((res = futex_cond_wait(STATE, + (futex_mutex_t*)(&mutex.mState), msec)) == -EINTR) ; + return res; +} + +/* + * Signal the condition variable, allowing one thread to continue. + */ +void Condition::signal() +{ + futex_cond_signal(STATE); +} + +/* + * Signal the condition variable, allowing all threads to continue. + */ +void Condition::broadcast() +{ + futex_cond_broadcast(STATE); +} + +#undef STATE + +#elif defined(HAVE_WIN32_THREADS) +#if 0 +#pragma mark - +#endif + +/* + * Windows doesn't have a condition variable solution. It's possible + * to create one, but it's easy to get it wrong. For a discussion, and + * the origin of this implementation, see: + * + * http://www.cs.wustl.edu/~schmidt/win32-cv-1.html + * + * The implementation shown on the page does NOT follow POSIX semantics. + * As an optimization they require acquiring the external mutex before + * calling signal() and broadcast(), whereas POSIX only requires grabbing + * it before calling wait(). The implementation here has been un-optimized + * to have the correct behavior. + */ +typedef struct WinCondition { + // Number of waiting threads. + int waitersCount; + + // Serialize access to waitersCount. + CRITICAL_SECTION waitersCountLock; + + // Semaphore used to queue up threads waiting for the condition to + // become signaled. + HANDLE sema; + + // An auto-reset event used by the broadcast/signal thread to wait + // for all the waiting thread(s) to wake up and be released from + // the semaphore. + HANDLE waitersDone; + + // This mutex wouldn't be necessary if we required that the caller + // lock the external mutex before calling signal() and broadcast(). + // I'm trying to mimic pthread semantics though. + HANDLE internalMutex; + + // Keeps track of whether we were broadcasting or signaling. This + // allows us to optimize the code if we're just signaling. + bool wasBroadcast; + + status_t wait(WinCondition* condState, HANDLE hMutex, nsecs_t* abstime) + { + // Increment the wait count, avoiding race conditions. + EnterCriticalSection(&condState->waitersCountLock); + condState->waitersCount++; + //printf("+++ wait: incr waitersCount to %d (tid=%ld)\n", + // condState->waitersCount, getThreadId()); + LeaveCriticalSection(&condState->waitersCountLock); + + DWORD timeout = INFINITE; + if (abstime) { + nsecs_t reltime = *abstime - systemTime(); + if (reltime < 0) + reltime = 0; + timeout = reltime/1000000; + } + + // Atomically release the external mutex and wait on the semaphore. + DWORD res = + SignalObjectAndWait(hMutex, condState->sema, timeout, FALSE); + + //printf("+++ wait: awake (tid=%ld)\n", getThreadId()); + + // Reacquire lock to avoid race conditions. + EnterCriticalSection(&condState->waitersCountLock); + + // No longer waiting. + condState->waitersCount--; + + // Check to see if we're the last waiter after a broadcast. + bool lastWaiter = (condState->wasBroadcast && condState->waitersCount == 0); + + //printf("+++ wait: lastWaiter=%d (wasBc=%d wc=%d)\n", + // lastWaiter, condState->wasBroadcast, condState->waitersCount); + + LeaveCriticalSection(&condState->waitersCountLock); + + // If we're the last waiter thread during this particular broadcast + // then signal broadcast() that we're all awake. It'll drop the + // internal mutex. + if (lastWaiter) { + // Atomically signal the "waitersDone" event and wait until we + // can acquire the internal mutex. We want to do this in one step + // because it ensures that everybody is in the mutex FIFO before + // any thread has a chance to run. Without it, another thread + // could wake up, do work, and hop back in ahead of us. + SignalObjectAndWait(condState->waitersDone, condState->internalMutex, + INFINITE, FALSE); + } else { + // Grab the internal mutex. + WaitForSingleObject(condState->internalMutex, INFINITE); + } + + // Release the internal and grab the external. + ReleaseMutex(condState->internalMutex); + WaitForSingleObject(hMutex, INFINITE); + + return res == WAIT_OBJECT_0 ? NO_ERROR : -1; + } +} WinCondition; + +/* + * Constructor. Set up the WinCondition stuff. + */ +Condition::Condition() +{ + WinCondition* condState = new WinCondition; + + condState->waitersCount = 0; + condState->wasBroadcast = false; + // semaphore: no security, initial value of 0 + condState->sema = CreateSemaphore(NULL, 0, 0x7fffffff, NULL); + InitializeCriticalSection(&condState->waitersCountLock); + // auto-reset event, not signaled initially + condState->waitersDone = CreateEvent(NULL, FALSE, FALSE, NULL); + // used so we don't have to lock external mutex on signal/broadcast + condState->internalMutex = CreateMutex(NULL, FALSE, NULL); + + mState = condState; +} + +/* + * Destructor. Free Windows resources as well as our allocated storage. + */ +Condition::~Condition() +{ + WinCondition* condState = (WinCondition*) mState; + if (condState != NULL) { + CloseHandle(condState->sema); + CloseHandle(condState->waitersDone); + delete condState; + } +} + + +status_t Condition::wait(Mutex& mutex) +{ + WinCondition* condState = (WinCondition*) mState; + HANDLE hMutex = (HANDLE) mutex.mState; + + return ((WinCondition*)mState)->wait(condState, hMutex, NULL); +} + +status_t Condition::wait(Mutex& mutex, nsecs_t abstime) +{ + WinCondition* condState = (WinCondition*) mState; + HANDLE hMutex = (HANDLE) mutex.mState; + + return ((WinCondition*)mState)->wait(condState, hMutex, &abstime); +} + +status_t Condition::waitRelative(Mutex& mutex, nsecs_t reltime) +{ + return wait(mutex, systemTime()+reltime); +} + +/* + * Signal the condition variable, allowing one thread to continue. + */ +void Condition::signal() +{ + WinCondition* condState = (WinCondition*) mState; + + // Lock the internal mutex. This ensures that we don't clash with + // broadcast(). + WaitForSingleObject(condState->internalMutex, INFINITE); + + EnterCriticalSection(&condState->waitersCountLock); + bool haveWaiters = (condState->waitersCount > 0); + LeaveCriticalSection(&condState->waitersCountLock); + + // If no waiters, then this is a no-op. Otherwise, knock the semaphore + // down a notch. + if (haveWaiters) + ReleaseSemaphore(condState->sema, 1, 0); + + // Release internal mutex. + ReleaseMutex(condState->internalMutex); +} + +/* + * Signal the condition variable, allowing all threads to continue. + * + * First we have to wake up all threads waiting on the semaphore, then + * we wait until all of the threads have actually been woken before + * releasing the internal mutex. This ensures that all threads are woken. + */ +void Condition::broadcast() +{ + WinCondition* condState = (WinCondition*) mState; + + // Lock the internal mutex. This keeps the guys we're waking up + // from getting too far. + WaitForSingleObject(condState->internalMutex, INFINITE); + + EnterCriticalSection(&condState->waitersCountLock); + bool haveWaiters = false; + + if (condState->waitersCount > 0) { + haveWaiters = true; + condState->wasBroadcast = true; + } + + if (haveWaiters) { + // Wake up all the waiters. + ReleaseSemaphore(condState->sema, condState->waitersCount, 0); + + LeaveCriticalSection(&condState->waitersCountLock); + + // Wait for all awakened threads to acquire the counting semaphore. + // The last guy who was waiting sets this. + WaitForSingleObject(condState->waitersDone, INFINITE); + + // Reset wasBroadcast. (No crit section needed because nobody + // else can wake up to poke at it.) + condState->wasBroadcast = 0; + } else { + // nothing to do + LeaveCriticalSection(&condState->waitersCountLock); + } + + // Release internal mutex. + ReleaseMutex(condState->internalMutex); +} + +#else +#error "condition variables not supported on this platform" +#endif + + +/* + * =========================================================================== + * ReadWriteLock class + * =========================================================================== + */ + +#if 0 +#pragma mark - +#pragma mark ReadWriteLock +#endif + +/* + * Add a reader. Readers are nice. They share. + */ +void ReadWriteLock::lockForRead() +{ + mLock.lock(); + while (mNumWriters > 0) { + LOG(LOG_DEBUG, "thread", "+++ lockForRead: waiting\n"); + mReadWaiter.wait(mLock); + } + assert(mNumWriters == 0); + mNumReaders++; +#if defined(PRINT_RENDER_TIMES) + if (mNumReaders == 1) + mDebugTimer.start(); +#endif + mLock.unlock(); +} + +/* + * Try to add a reader. If it doesn't work right away, return "false". + */ +bool ReadWriteLock::tryLockForRead() +{ + mLock.lock(); + if (mNumWriters > 0) { + mLock.unlock(); + return false; + } + assert(mNumWriters == 0); + mNumReaders++; +#if defined(PRINT_RENDER_TIMES) + if (mNumReaders == 1) + mDebugTimer.start(); +#endif + mLock.unlock(); + return true; +} + +/* + * Remove a reader. + */ +void ReadWriteLock::unlockForRead() +{ + mLock.lock(); + if (mNumReaders == 0) { + mLock.unlock(); + LOG(LOG_WARN, "thread", + "WARNING: unlockForRead requested, but not locked\n"); + return; + } + assert(mNumReaders > 0); + assert(mNumWriters == 0); + mNumReaders--; + if (mNumReaders == 0) { // last reader? +#if defined(PRINT_RENDER_TIMES) + mDebugTimer.stop(); + printf(" rdlk held %.3f msec\n", + (double) mDebugTimer.durationUsecs() / 1000.0); +#endif + //printf("+++ signaling writers (if any)\n"); + mWriteWaiter.signal(); // wake one writer (if any) + } + mLock.unlock(); +} + +/* + * Add a writer. This requires exclusive access to the object. + */ +void ReadWriteLock::lockForWrite() +{ + mLock.lock(); + while (mNumReaders > 0 || mNumWriters > 0) { + LOG(LOG_DEBUG, "thread", "+++ lockForWrite: waiting\n"); + mWriteWaiter.wait(mLock); + } + assert(mNumReaders == 0); + assert(mNumWriters == 0); + mNumWriters++; +#if defined(PRINT_RENDER_TIMES) + mDebugTimer.start(); +#endif + mLock.unlock(); +} + +/* + * Try to add a writer. If it doesn't work right away, return "false". + */ +bool ReadWriteLock::tryLockForWrite() +{ + mLock.lock(); + if (mNumReaders > 0 || mNumWriters > 0) { + mLock.unlock(); + return false; + } + assert(mNumReaders == 0); + assert(mNumWriters == 0); + mNumWriters++; +#if defined(PRINT_RENDER_TIMES) + mDebugTimer.start(); +#endif + mLock.unlock(); + return true; +} + +/* + * Remove a writer. + */ +void ReadWriteLock::unlockForWrite() +{ + mLock.lock(); + if (mNumWriters == 0) { + mLock.unlock(); + LOG(LOG_WARN, "thread", + "WARNING: unlockForWrite requested, but not locked\n"); + return; + } + assert(mNumWriters == 1); + mNumWriters--; +#if defined(PRINT_RENDER_TIMES) + mDebugTimer.stop(); + //printf(" wrlk held %.3f msec\n", + // (double) mDebugTimer.durationUsecs() / 1000.0); +#endif + mWriteWaiter.signal(); // should other writers get first dibs? + //printf("+++ signaling readers (if any)\n"); + mReadWaiter.broadcast(); // wake all readers (if any) + mLock.unlock(); +} + +// ---------------------------------------------------------------------------- + +#if 0 +#pragma mark - +#pragma mark Thread::Thread +#endif + +/* + * This is our thread object! + */ + +Thread::Thread(bool canCallJava) + : mCanCallJava(canCallJava), + mThread(thread_id_t(-1)), + mLock("Thread::mLock"), + mStatus(NO_ERROR), + mExitPending(false), mRunning(false) +{ +} + +Thread::~Thread() +{ +} + +status_t Thread::readyToRun() +{ + return NO_ERROR; +} + +status_t Thread::run(const char* name, int32_t priority, size_t stack) +{ + Mutex::Autolock _l(mLock); + + if (mRunning) { + // thread already started + return INVALID_OPERATION; + } + + // reset status and exitPending to their default value, so we can + // try again after an error happened (either below, or in readyToRun()) + mStatus = NO_ERROR; + mExitPending = false; + mThread = thread_id_t(-1); + + // hold a strong reference on ourself + mHoldSelf = this; + + bool res; + if (mCanCallJava) { + res = createThreadEtc(_threadLoop, + this, name, priority, stack, &mThread); + } else { + res = androidCreateRawThreadEtc(_threadLoop, + this, name, priority, stack, &mThread); + } + + if (res == false) { + mStatus = UNKNOWN_ERROR; // something happened! + mRunning = false; + mThread = thread_id_t(-1); + } + + if (mStatus < 0) { + // something happened, don't leak + mHoldSelf.clear(); + } + + return mStatus; +} + +int Thread::_threadLoop(void* user) +{ + Thread* const self = static_cast(user); + sp strong(self->mHoldSelf); + wp weak(strong); + self->mHoldSelf.clear(); + + // we're about to run... + self->mStatus = self->readyToRun(); + if (self->mStatus!=NO_ERROR || self->mExitPending) { + // pretend the thread never started... + self->mExitPending = false; + self->mRunning = false; + return 0; + } + + // thread is running now + self->mRunning = true; + + do { + bool result = self->threadLoop(); + if (result == false || self->mExitPending) { + self->mExitPending = true; + self->mLock.lock(); + self->mRunning = false; + self->mThreadExitedCondition.signal(); + self->mLock.unlock(); + break; + } + + // Release our strong reference, to let a chance to the thread + // to die a peaceful death. + strong.clear(); + // And immediately, reacquire a strong reference for the next loop + strong = weak.promote(); + } while(strong != 0); + + return 0; +} + +void Thread::requestExit() +{ + mExitPending = true; +} + +status_t Thread::requestExitAndWait() +{ + if (mStatus == OK) { + + if (mThread == getThreadId()) { + LOGW( + "Thread (this=%p): don't call waitForExit() from this " + "Thread object's thread. It's a guaranteed deadlock!", + this); + return WOULD_BLOCK; + } + + requestExit(); + + Mutex::Autolock _l(mLock); + while (mRunning == true) { + mThreadExitedCondition.wait(mLock); + } + mExitPending = false; + } + return mStatus; +} + +bool Thread::exitPending() const +{ + return mExitPending; +} + + + +}; // namespace android diff --git a/libs/utils/TimerProbe.cpp b/libs/utils/TimerProbe.cpp new file mode 100644 index 000000000..835480d36 --- /dev/null +++ b/libs/utils/TimerProbe.cpp @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#if ENABLE_TIMER_PROBE + +#ifdef LOG_TAG +#undef LOG_TAG +#endif +#define LOG_TAG "time" + +namespace android { + +Vector TimerProbe::gBuckets; +TimerProbe* TimerProbe::gExecuteChain; +int TimerProbe::gIndent; +timespec TimerProbe::gRealBase; + +TimerProbe::TimerProbe(const char tag[], int* slot) : mTag(tag) +{ + mNext = gExecuteChain; + gExecuteChain = this; + mIndent = gIndent; + gIndent += 1; + if (mIndent > 0) { + if (*slot == 0) { + int count = gBuckets.add(); + *slot = count; + Bucket& bucket = gBuckets.editItemAt(count); + memset(&bucket, 0, sizeof(Bucket)); + bucket.mTag = tag; + bucket.mSlotPtr = slot; + bucket.mIndent = mIndent; + } + mBucket = *slot; + } + clock_gettime(CLOCK_REALTIME, &mRealStart); + if (gRealBase.tv_sec == 0) + gRealBase = mRealStart; + clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &mPStart); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &mTStart); +} + +void TimerProbe::end() +{ + timespec realEnd, pEnd, tEnd; + clock_gettime(CLOCK_REALTIME, &realEnd); + clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &pEnd); + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tEnd); + print(realEnd, pEnd, tEnd); + mTag = NULL; +} + +TimerProbe::~TimerProbe() +{ + if (mTag != NULL) + end(); + gExecuteChain = mNext; + gIndent--; +} + + +uint32_t TimerProbe::ElapsedTime(const timespec& start, const timespec& end) +{ + int sec = end.tv_sec - start.tv_sec; + int nsec = end.tv_nsec - start.tv_nsec; + if (nsec < 0) { + sec--; + nsec += 1000000000; + } + return sec * 1000000 + nsec / 1000; +} + +void TimerProbe::print(const timespec& r, const timespec& p, + const timespec& t) const +{ + uint32_t es = ElapsedTime(gRealBase, mRealStart); + uint32_t er = ElapsedTime(mRealStart, r); + uint32_t ep = ElapsedTime(mPStart, p); + uint32_t et = ElapsedTime(mTStart, t); + if (mIndent > 0) { + Bucket& bucket = gBuckets.editItemAt(mBucket); + if (bucket.mStart == 0) + bucket.mStart = es; + bucket.mReal += er; + bucket.mProcess += ep; + bucket.mThread += et; + bucket.mCount++; + return; + } + int index = 0; + int buckets = gBuckets.size(); + int count = 1; + const char* tag = mTag; + int indent = mIndent; + do { + LOGD("%-30.30s: (%3d) %-5.*s time=%-10.3f real=%7dus process=%7dus (%3d%%) thread=%7dus (%3d%%)\n", + tag, count, indent > 5 ? 5 : indent, "+++++", es / 1000000.0, + er, ep, ep * 100 / er, et, et * 100 / er); + if (index >= buckets) + break; + Bucket& bucket = gBuckets.editItemAt(index); + count = bucket.mCount; + es = bucket.mStart; + er = bucket.mReal; + ep = bucket.mProcess; + et = bucket.mThread; + tag = bucket.mTag; + indent = bucket.mIndent; + *bucket.mSlotPtr = 0; + } while (++index); // always true + gBuckets.clear(); +} + +}; // namespace android + +#endif diff --git a/libs/utils/Timers.cpp b/libs/utils/Timers.cpp new file mode 100644 index 000000000..2abc811a0 --- /dev/null +++ b/libs/utils/Timers.cpp @@ -0,0 +1,240 @@ +/* + * Copyright (C) 2005 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. + */ + +// +// Timer functions. +// +#include +#include // may need usleep +#include + +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_WIN32_THREADS +#include +#endif + +nsecs_t systemTime(int clock) +{ +#if defined(HAVE_POSIX_CLOCKS) + static const clockid_t clocks[] = { + CLOCK_REALTIME, + CLOCK_MONOTONIC, + CLOCK_PROCESS_CPUTIME_ID, + CLOCK_THREAD_CPUTIME_ID + }; + struct timespec t; + t.tv_sec = t.tv_nsec = 0; + clock_gettime(clocks[clock], &t); + return nsecs_t(t.tv_sec)*1000000000LL + t.tv_nsec; +#else + // we don't support the clocks here. + struct timeval t; + t.tv_sec = t.tv_usec = 0; + gettimeofday(&t, NULL); + return nsecs_t(t.tv_sec)*1000000000LL + nsecs_t(t.tv_usec)*1000LL; +#endif +} + +//#define MONITOR_USLEEP + +/* + * Sleep long enough that we'll wake up "interval" milliseconds after + * the previous snooze. + * + * The "nextTick" argument is updated on each call, and should be passed + * in every time. Set its fields to zero on the first call. + * + * Returns the #of intervals we have overslept, which will be zero if we're + * on time. [Currently just returns 0 or 1.] + */ +int sleepForInterval(long interval, struct timeval* pNextTick) +{ + struct timeval now; + long long timeBeforeNext; + long sleepTime = 0; + bool overSlept = false; + //int usleepBias = 0; + +#ifdef USLEEP_BIAS + /* + * Linux likes to add 9000ms or so. + * [not using this for now] + */ + //usleepBias = USLEEP_BIAS; +#endif + + gettimeofday(&now, NULL); + + if (pNextTick->tv_sec == 0) { + /* special-case for first time through */ + *pNextTick = now; + sleepTime = interval; + android::DurationTimer::addToTimeval(pNextTick, interval); + } else { + /* + * Compute how much time there is before the next tick. If this + * value is negative, we've run over. If we've run over a little + * bit we can shorten the next frame to keep the pace steady, but + * if we've dramatically overshot we need to re-sync. + */ + timeBeforeNext = android::DurationTimer::subtractTimevals(pNextTick, &now); + //printf("TOP: now=%ld.%ld next=%ld.%ld diff=%ld\n", + // now.tv_sec, now.tv_usec, pNextTick->tv_sec, pNextTick->tv_usec, + // (long) timeBeforeNext); + if (timeBeforeNext < -interval) { + /* way over */ + overSlept = true; + sleepTime = 0; + *pNextTick = now; + } else if (timeBeforeNext <= 0) { + /* slightly over, keep the pace steady */ + overSlept = true; + sleepTime = 0; + } else if (timeBeforeNext <= interval) { + /* right on schedule */ + sleepTime = timeBeforeNext; + } else if (timeBeforeNext > interval && timeBeforeNext <= 2*interval) { + /* sleep call returned early; do a longer sleep this time */ + sleepTime = timeBeforeNext; + } else if (timeBeforeNext > interval) { + /* we went back in time -- somebody updated system clock? */ + /* (could also be a *seriously* broken usleep()) */ + LOG(LOG_DEBUG, "", + " Impossible: timeBeforeNext = %ld\n", (long)timeBeforeNext); + sleepTime = 0; + *pNextTick = now; + } + android::DurationTimer::addToTimeval(pNextTick, interval); + } + //printf(" Before sleep: now=%ld.%ld next=%ld.%ld sleepTime=%ld\n", + // now.tv_sec, now.tv_usec, pNextTick->tv_sec, pNextTick->tv_usec, + // sleepTime); + + /* + * Sleep for the designated period of time. + * + * Linux tends to sleep for longer than requested, often by 17-18ms. + * MinGW tends to sleep for less than requested, by as much as 14ms, + * but occasionally oversleeps for 40+ms (looks like some external + * factors plus round-off on a 64Hz clock). Cygwin is pretty steady. + * + * If you start the MinGW version, and then launch the Cygwin version, + * the MinGW clock becomes more erratic. Not entirely sure why. + * + * (There's a lot of stuff here; it's really just a usleep() call with + * a bunch of instrumentation.) + */ + if (sleepTime > 0) { +#if defined(MONITOR_USLEEP) + struct timeval before, after; + long long actual; + + gettimeofday(&before, NULL); + usleep((long) sleepTime); + gettimeofday(&after, NULL); + + /* check usleep() accuracy; default Linux threads are pretty sloppy */ + actual = android::DurationTimer::subtractTimevals(&after, &before); + if ((long) actual < sleepTime - 14000 /*(sleepTime/10)*/ || + (long) actual > sleepTime + 20000 /*(sleepTime/10)*/) + { + LOG(LOG_DEBUG, "", " Odd usleep: req=%ld, actual=%ld\n", sleepTime, + (long) actual); + } +#else +#ifdef HAVE_WIN32_THREADS + Sleep( sleepTime/1000 ); +#else + usleep((long) sleepTime); +#endif +#endif + } + + //printf("slept %d\n", sleepTime); + + if (overSlept) + return 1; // close enough + else + return 0; +} + + +/* + * =========================================================================== + * DurationTimer + * =========================================================================== + */ + +using namespace android; + +// Start the timer. +void DurationTimer::start(void) +{ + gettimeofday(&mStartWhen, NULL); +} + +// Stop the timer. +void DurationTimer::stop(void) +{ + gettimeofday(&mStopWhen, NULL); +} + +// Get the duration in microseconds. +long long DurationTimer::durationUsecs(void) const +{ + return (long) subtractTimevals(&mStopWhen, &mStartWhen); +} + +// Subtract two timevals. Returns the difference (ptv1-ptv2) in +// microseconds. +/*static*/ long long DurationTimer::subtractTimevals(const struct timeval* ptv1, + const struct timeval* ptv2) +{ + long long stop = ((long long) ptv1->tv_sec) * 1000000LL + + ((long long) ptv1->tv_usec); + long long start = ((long long) ptv2->tv_sec) * 1000000LL + + ((long long) ptv2->tv_usec); + return stop - start; +} + +// Add the specified amount of time to the timeval. +/*static*/ void DurationTimer::addToTimeval(struct timeval* ptv, long usec) +{ + if (usec < 0) { + LOG(LOG_WARN, "", "Negative values not supported in addToTimeval\n"); + return; + } + + // normalize tv_usec if necessary + if (ptv->tv_usec >= 1000000) { + ptv->tv_sec += ptv->tv_usec / 1000000; + ptv->tv_usec %= 1000000; + } + + ptv->tv_usec += usec % 1000000; + if (ptv->tv_usec >= 1000000) { + ptv->tv_usec -= 1000000; + ptv->tv_sec++; + } + ptv->tv_sec += usec / 1000000; +} + diff --git a/libs/utils/Unicode.cpp b/libs/utils/Unicode.cpp new file mode 100644 index 000000000..33f535fd1 --- /dev/null +++ b/libs/utils/Unicode.cpp @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "utils/AndroidUnicode.h" +#include "characterData.h" + +#define LOG_TAG "Unicode" +#include "utils/Log.h" + +// ICU headers for using macros +#include + +#define MIN_RADIX 2 +#define MAX_RADIX 36 + +#define TYPE_SHIFT 0 +#define TYPE_MASK ((1<<5)-1) + +#define DIRECTION_SHIFT (TYPE_SHIFT+5) +#define DIRECTION_MASK ((1<<5)-1) + +#define MIRRORED_SHIFT (DIRECTION_SHIFT+5) +#define MIRRORED_MASK ((1<<1)-1) + +#define TOUPPER_SHIFT (MIRRORED_SHIFT+1) +#define TOUPPER_MASK ((1<<6)-1) + +#define TOLOWER_SHIFT (TOUPPER_SHIFT+6) +#define TOLOWER_MASK ((1<<6)-1) + +#define TOTITLE_SHIFT (TOLOWER_SHIFT+6) +#define TOTITLE_MASK ((1<<2)-1) + +#define MIRROR_SHIFT (TOTITLE_SHIFT+2) +#define MIRROR_MASK ((1<<5)-1) + +#define NUMERIC_SHIFT (TOTITLE_SHIFT+2) +#define NUMERIC_MASK ((1<<7)-1) + +#define DECOMPOSITION_SHIFT (11) +#define DECOMPOSITION_MASK ((1<<5)-1) + +/* + * Returns the value stored in the CharacterData tables that contains + * an index into the packed data table and the decomposition type. + */ +static uint16_t findCharacterValue(UChar32 c) +{ + LOG_ASSERT(c >= 0 && c <= 0x10FFFF, "findCharacterValue received an invalid codepoint"); + if (c < 256) + return CharacterData::LATIN1_DATA[c]; + + // Rotate the bits because the tables are separated into even and odd codepoints + c = (c >> 1) | ((c & 1) << 20); + + CharacterData::Range search = CharacterData::FULL_DATA[c >> 16]; + const uint32_t* array = search.array; + + // This trick is so that that compare in the while loop does not + // need to shift the array entry down by 16 + c <<= 16; + c |= 0xFFFF; + + int high = (int)search.length - 1; + int low = 0; + + if (high < 0) + return 0; + + while (low < high - 1) + { + int probe = (high + low) >> 1; + + // The entries contain the codepoint in the high 16 bits and the index + // into PACKED_DATA in the low 16. + if (array[probe] > (unsigned)c) + high = probe; + else + low = probe; + } + + LOG_ASSERT((array[low] <= (unsigned)c), "A suitable range was not found"); + return array[low] & 0xFFFF; +} + +uint32_t android::Unicode::getPackedData(UChar32 c) +{ + // findCharacterValue returns a 16-bit value with the top 5 bits containing a decomposition type + // and the remaining bits containing an index. + return CharacterData::PACKED_DATA[findCharacterValue(c) & 0x7FF]; +} + +android::Unicode::CharType android::Unicode::getType(UChar32 c) +{ + if (c < 0 || c >= 0x10FFFF) + return CHARTYPE_UNASSIGNED; + return (CharType)((getPackedData(c) >> TYPE_SHIFT) & TYPE_MASK); +} + +android::Unicode::DecompositionType android::Unicode::getDecompositionType(UChar32 c) +{ + // findCharacterValue returns a 16-bit value with the top 5 bits containing a decomposition type + // and the remaining bits containing an index. + return (DecompositionType)((findCharacterValue(c) >> DECOMPOSITION_SHIFT) & DECOMPOSITION_MASK); +} + +int android::Unicode::getDigitValue(UChar32 c, int radix) +{ + if (radix < MIN_RADIX || radix > MAX_RADIX) + return -1; + + int tempValue = radix; + + if (c >= '0' && c <= '9') + tempValue = c - '0'; + else if (c >= 'a' && c <= 'z') + tempValue = c - 'a' + 10; + else if (c >= 'A' && c <= 'Z') + tempValue = c - 'A' + 10; + + return tempValue < radix ? tempValue : -1; +} + +int android::Unicode::getNumericValue(UChar32 c) +{ + if (isMirrored(c)) + return -1; + + return (int) CharacterData::NUMERICS[((getPackedData(c) >> NUMERIC_SHIFT) & NUMERIC_MASK)]; +} + +UChar32 android::Unicode::toLower(UChar32 c) +{ + return c + CharacterData::LCDIFF[(getPackedData(c) >> TOLOWER_SHIFT) & TOLOWER_MASK]; +} + +UChar32 android::Unicode::toUpper(UChar32 c) +{ + return c + CharacterData::UCDIFF[(getPackedData(c) >> TOUPPER_SHIFT) & TOUPPER_MASK]; +} + +android::Unicode::Direction android::Unicode::getDirectionality(UChar32 c) +{ + uint32_t data = getPackedData(c); + + if (0 == data) + return DIRECTIONALITY_UNDEFINED; + + Direction d = (Direction) ((data >> DIRECTION_SHIFT) & DIRECTION_MASK); + + if (DIRECTION_MASK == d) + return DIRECTIONALITY_UNDEFINED; + + return d; +} + +bool android::Unicode::isMirrored(UChar32 c) +{ + return ((getPackedData(c) >> MIRRORED_SHIFT) & MIRRORED_MASK) != 0; +} + +UChar32 android::Unicode::toMirror(UChar32 c) +{ + if (!isMirrored(c)) + return c; + + return c + CharacterData::MIRROR_DIFF[(getPackedData(c) >> MIRROR_SHIFT) & MIRROR_MASK]; +} + +UChar32 android::Unicode::toTitle(UChar32 c) +{ + int32_t diff = CharacterData::TCDIFF[(getPackedData(c) >> TOTITLE_SHIFT) & TOTITLE_MASK]; + + if (TOTITLE_MASK == diff) + return toUpper(c); + + return c + diff; +} + + diff --git a/libs/utils/VectorImpl.cpp b/libs/utils/VectorImpl.cpp new file mode 100644 index 000000000..2c2d6675c --- /dev/null +++ b/libs/utils/VectorImpl.cpp @@ -0,0 +1,611 @@ +/* + * Copyright (C) 2005 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 "Vector" + +#include +#include +#include + +#include +#include +#include +#include + +/*****************************************************************************/ + + +namespace android { + +// ---------------------------------------------------------------------------- + +const size_t kMinVectorCapacity = 4; + +static inline size_t max(size_t a, size_t b) { + return a>b ? a : b; +} + +// ---------------------------------------------------------------------------- + +VectorImpl::VectorImpl(size_t itemSize, uint32_t flags) + : mStorage(0), mCount(0), mFlags(flags), mItemSize(itemSize) +{ +} + +VectorImpl::VectorImpl(const VectorImpl& rhs) + : mStorage(rhs.mStorage), mCount(rhs.mCount), + mFlags(rhs.mFlags), mItemSize(rhs.mItemSize) +{ + if (mStorage) { + SharedBuffer::sharedBuffer(mStorage)->acquire(); + } +} + +VectorImpl::~VectorImpl() +{ + LOG_ASSERT(!mCount, + "[%p] " + "subclasses of VectorImpl must call finish_vector()" + " in their destructor. Leaking %d bytes.", + this, (int)(mCount*mItemSize)); + // We can't call _do_destroy() here because the vtable is already gone. +} + +VectorImpl& VectorImpl::operator = (const VectorImpl& rhs) +{ + LOG_ASSERT(mItemSize == rhs.mItemSize, + "Vector<> have different types (this=%p, rhs=%p)", this, &rhs); + if (this != &rhs) { + release_storage(); + if (rhs.mCount) { + mStorage = rhs.mStorage; + mCount = rhs.mCount; + SharedBuffer::sharedBuffer(mStorage)->acquire(); + } else { + mStorage = 0; + mCount = 0; + } + } + return *this; +} + +void* VectorImpl::editArrayImpl() +{ + if (mStorage) { + SharedBuffer* sb = SharedBuffer::sharedBuffer(mStorage)->attemptEdit(); + if (sb == 0) { + sb = SharedBuffer::alloc(capacity() * mItemSize); + if (sb) { + _do_copy(sb->data(), mStorage, mCount); + release_storage(); + mStorage = sb->data(); + } + } + } + return mStorage; +} + +size_t VectorImpl::capacity() const +{ + if (mStorage) { + return SharedBuffer::sharedBuffer(mStorage)->size() / mItemSize; + } + return 0; +} + +ssize_t VectorImpl::insertVectorAt(const VectorImpl& vector, size_t index) +{ + if (index > size()) + return BAD_INDEX; + void* where = _grow(index, vector.size()); + if (where) { + _do_copy(where, vector.arrayImpl(), vector.size()); + } + return where ? index : (ssize_t)NO_MEMORY; +} + +ssize_t VectorImpl::appendVector(const VectorImpl& vector) +{ + return insertVectorAt(vector, size()); +} + +ssize_t VectorImpl::insertAt(size_t index, size_t numItems) +{ + return insertAt(0, index, numItems); +} + +ssize_t VectorImpl::insertAt(const void* item, size_t index, size_t numItems) +{ + if (index > size()) + return BAD_INDEX; + void* where = _grow(index, numItems); + if (where) { + if (item) { + _do_splat(where, item, numItems); + } else { + _do_construct(where, numItems); + } + } + return where ? index : (ssize_t)NO_MEMORY; +} + +static int sortProxy(const void* lhs, const void* rhs, void* func) +{ + return (*(VectorImpl::compar_t)func)(lhs, rhs); +} + +status_t VectorImpl::sort(VectorImpl::compar_t cmp) +{ + return sort(sortProxy, (void*)cmp); +} + +status_t VectorImpl::sort(VectorImpl::compar_r_t cmp, void* state) +{ + // the sort must be stable. we're using insertion sort which + // is well suited for small and already sorted arrays + // for big arrays, it could be better to use mergesort + const ssize_t count = size(); + if (count > 1) { + void* array = const_cast(arrayImpl()); + void* temp = 0; + ssize_t i = 1; + while (i < count) { + void* item = reinterpret_cast(array) + mItemSize*(i); + void* curr = reinterpret_cast(array) + mItemSize*(i-1); + if (cmp(curr, item, state) > 0) { + + if (!temp) { + // we're going to have to modify the array... + array = editArrayImpl(); + if (!array) return NO_MEMORY; + temp = malloc(mItemSize); + if (!temp) return NO_MEMORY; + _do_construct(temp, 1); + item = reinterpret_cast(array) + mItemSize*(i); + curr = reinterpret_cast(array) + mItemSize*(i-1); + } + + _do_copy(temp, item, 1); + + ssize_t j = i-1; + void* next = reinterpret_cast(array) + mItemSize*(i); + do { + _do_copy(next, curr, 1); + next = curr; + --j; + curr = reinterpret_cast(array) + mItemSize*(j); + } while (j>=0 && (cmp(curr, temp, state) > 0)); + + _do_copy(next, temp, 1); + } + i++; + } + + if (temp) { + _do_destroy(temp, 1); + free(temp); + } + } + return NO_ERROR; +} + +void VectorImpl::pop() +{ + if (size()) + removeItemsAt(size()-1, 1); +} + +void VectorImpl::push() +{ + push(0); +} + +void VectorImpl::push(const void* item) +{ + insertAt(item, size()); +} + +ssize_t VectorImpl::add() +{ + return add(0); +} + +ssize_t VectorImpl::add(const void* item) +{ + return insertAt(item, size()); +} + +ssize_t VectorImpl::replaceAt(size_t index) +{ + return replaceAt(0, index); +} + +ssize_t VectorImpl::replaceAt(const void* prototype, size_t index) +{ + LOG_ASSERT(index size()) + return BAD_VALUE; + _shrink(index, count); + return index; +} + +void VectorImpl::finish_vector() +{ + release_storage(); + mStorage = 0; + mCount = 0; +} + +void VectorImpl::clear() +{ + _shrink(0, mCount); +} + +void* VectorImpl::editItemLocation(size_t index) +{ + LOG_ASSERT(index(buffer) + index*mItemSize; + return 0; +} + +const void* VectorImpl::itemLocation(size_t index) const +{ + LOG_ASSERT(index(buffer) + index*mItemSize; + return 0; +} + +ssize_t VectorImpl::setCapacity(size_t new_capacity) +{ + size_t current_capacity = capacity(); + ssize_t amount = new_capacity - size(); + if (amount <= 0) { + // we can't reduce the capacity + return current_capacity; + } + SharedBuffer* sb = SharedBuffer::alloc(new_capacity * mItemSize); + if (sb) { + void* array = sb->data(); + _do_copy(array, mStorage, size()); + release_storage(); + mStorage = const_cast(array); + } else { + return NO_MEMORY; + } + return new_capacity; +} + +void VectorImpl::release_storage() +{ + if (mStorage) { + const SharedBuffer* sb = SharedBuffer::sharedBuffer(mStorage); + if (sb->release(SharedBuffer::eKeepStorage) == 1) { + _do_destroy(mStorage, mCount); + SharedBuffer::dealloc(sb); + } + } +} + +void* VectorImpl::_grow(size_t where, size_t amount) +{ +// LOGV("_grow(this=%p, where=%d, amount=%d) count=%d, capacity=%d", +// this, (int)where, (int)amount, (int)mCount, (int)capacity()); + + if (where > mCount) + where = mCount; + + const size_t new_size = mCount + amount; + if (capacity() < new_size) { + const size_t new_capacity = max(kMinVectorCapacity, ((new_size*3)+1)/2); +// LOGV("grow vector %p, new_capacity=%d", this, (int)new_capacity); + if ((mStorage) && + (mCount==where) && + (mFlags & HAS_TRIVIAL_COPY) && + (mFlags & HAS_TRIVIAL_DTOR)) + { + const SharedBuffer* cur_sb = SharedBuffer::sharedBuffer(mStorage); + SharedBuffer* sb = cur_sb->editResize(new_capacity * mItemSize); + mStorage = sb->data(); + } else { + SharedBuffer* sb = SharedBuffer::alloc(new_capacity * mItemSize); + if (sb) { + void* array = sb->data(); + if (where>0) { + _do_copy(array, mStorage, where); + } + if (mCount>where) { + const void* from = reinterpret_cast(mStorage) + where*mItemSize; + void* dest = reinterpret_cast(array) + (where+amount)*mItemSize; + _do_copy(dest, from, mCount-where); + } + release_storage(); + mStorage = const_cast(array); + } + } + } else { + ssize_t s = mCount-where; + if (s>0) { + void* array = editArrayImpl(); + void* to = reinterpret_cast(array) + (where+amount)*mItemSize; + const void* from = reinterpret_cast(array) + where*mItemSize; + _do_move_forward(to, from, s); + } + } + mCount += amount; + void* free_space = const_cast(itemLocation(where)); + return free_space; +} + +void VectorImpl::_shrink(size_t where, size_t amount) +{ + if (!mStorage) + return; + +// LOGV("_shrink(this=%p, where=%d, amount=%d) count=%d, capacity=%d", +// this, (int)where, (int)amount, (int)mCount, (int)capacity()); + + if (where >= mCount) + where = mCount - amount; + + const size_t new_size = mCount - amount; + if (new_size*3 < capacity()) { + const size_t new_capacity = max(kMinVectorCapacity, new_size*2); +// LOGV("shrink vector %p, new_capacity=%d", this, (int)new_capacity); + if ((where == mCount-amount) && + (mFlags & HAS_TRIVIAL_COPY) && + (mFlags & HAS_TRIVIAL_DTOR)) + { + const SharedBuffer* cur_sb = SharedBuffer::sharedBuffer(mStorage); + SharedBuffer* sb = cur_sb->editResize(new_capacity * mItemSize); + mStorage = sb->data(); + } else { + SharedBuffer* sb = SharedBuffer::alloc(new_capacity * mItemSize); + if (sb) { + void* array = sb->data(); + if (where>0) { + _do_copy(array, mStorage, where); + } + if (mCount > where+amount) { + const void* from = reinterpret_cast(mStorage) + (where+amount)*mItemSize; + void* dest = reinterpret_cast(array) + where*mItemSize; + _do_copy(dest, from, mCount-(where+amount)); + } + release_storage(); + mStorage = const_cast(array); + } + } + } else { + void* array = editArrayImpl(); + void* to = reinterpret_cast(array) + where*mItemSize; + _do_destroy(to, amount); + ssize_t s = mCount-(where+amount); + if (s>0) { + const void* from = reinterpret_cast(array) + (where+amount)*mItemSize; + _do_move_backward(to, from, s); + } + } + + // adjust the number of items... + mCount -= amount; +} + +size_t VectorImpl::itemSize() const { + return mItemSize; +} + +void VectorImpl::_do_construct(void* storage, size_t num) const +{ + if (!(mFlags & HAS_TRIVIAL_CTOR)) { + do_construct(storage, num); + } +} + +void VectorImpl::_do_destroy(void* storage, size_t num) const +{ + if (!(mFlags & HAS_TRIVIAL_DTOR)) { + do_destroy(storage, num); + } +} + +void VectorImpl::_do_copy(void* dest, const void* from, size_t num) const +{ + if (!(mFlags & HAS_TRIVIAL_COPY)) { + do_copy(dest, from, num); + } else { + memcpy(dest, from, num*itemSize()); + } +} + +void VectorImpl::_do_splat(void* dest, const void* item, size_t num) const { + do_splat(dest, item, num); +} + +void VectorImpl::_do_move_forward(void* dest, const void* from, size_t num) const { + do_move_forward(dest, from, num); +} + +void VectorImpl::_do_move_backward(void* dest, const void* from, size_t num) const { + do_move_backward(dest, from, num); +} + +void VectorImpl::reservedVectorImpl1() { } +void VectorImpl::reservedVectorImpl2() { } +void VectorImpl::reservedVectorImpl3() { } +void VectorImpl::reservedVectorImpl4() { } +void VectorImpl::reservedVectorImpl5() { } +void VectorImpl::reservedVectorImpl6() { } +void VectorImpl::reservedVectorImpl7() { } +void VectorImpl::reservedVectorImpl8() { } + +/*****************************************************************************/ + +SortedVectorImpl::SortedVectorImpl(size_t itemSize, uint32_t flags) + : VectorImpl(itemSize, flags) +{ +} + +SortedVectorImpl::SortedVectorImpl(const VectorImpl& rhs) +: VectorImpl(rhs) +{ +} + +SortedVectorImpl::~SortedVectorImpl() +{ +} + +SortedVectorImpl& SortedVectorImpl::operator = (const SortedVectorImpl& rhs) +{ + return static_cast( VectorImpl::operator = (static_cast(rhs)) ); +} + +ssize_t SortedVectorImpl::indexOf(const void* item) const +{ + return _indexOrderOf(item); +} + +size_t SortedVectorImpl::orderOf(const void* item) const +{ + size_t o; + _indexOrderOf(item, &o); + return o; +} + +ssize_t SortedVectorImpl::_indexOrderOf(const void* item, size_t* order) const +{ + // binary search + ssize_t err = NAME_NOT_FOUND; + ssize_t l = 0; + ssize_t h = size()-1; + ssize_t mid; + const void* a = arrayImpl(); + const size_t s = itemSize(); + while (l <= h) { + mid = l + (h - l)/2; + const void* const curr = reinterpret_cast(a) + (mid*s); + const int c = do_compare(curr, item); + if (c == 0) { + err = l = mid; + break; + } else if (c < 0) { + l = mid + 1; + } else { + h = mid - 1; + } + } + if (order) *order = l; + return err; +} + +ssize_t SortedVectorImpl::add(const void* item) +{ + size_t order; + ssize_t index = _indexOrderOf(item, &order); + if (index < 0) { + index = VectorImpl::insertAt(item, order, 1); + } else { + index = VectorImpl::replaceAt(item, index); + } + return index; +} + +ssize_t SortedVectorImpl::merge(const VectorImpl& vector) +{ + // naive merge... + if (!vector.isEmpty()) { + const void* buffer = vector.arrayImpl(); + const size_t is = itemSize(); + size_t s = vector.size(); + for (size_t i=0 ; i(buffer) + i*is ); + if (err<0) { + return err; + } + } + } + return NO_ERROR; +} + +ssize_t SortedVectorImpl::merge(const SortedVectorImpl& vector) +{ + // we've merging a sorted vector... nice! + ssize_t err = NO_ERROR; + if (!vector.isEmpty()) { + // first take care of the case where the vectors are sorted together + if (do_compare(vector.itemLocation(vector.size()-1), arrayImpl()) <= 0) { + err = VectorImpl::insertVectorAt(static_cast(vector), 0); + } else if (do_compare(vector.arrayImpl(), itemLocation(size()-1)) >= 0) { + err = VectorImpl::appendVector(static_cast(vector)); + } else { + // this could be made a little better + err = merge(static_cast(vector)); + } + } + return err; +} + +ssize_t SortedVectorImpl::remove(const void* item) +{ + ssize_t i = indexOf(item); + if (i>=0) { + VectorImpl::removeItemsAt(i, 1); + } + return i; +} + +void SortedVectorImpl::reservedSortedVectorImpl1() { }; +void SortedVectorImpl::reservedSortedVectorImpl2() { }; +void SortedVectorImpl::reservedSortedVectorImpl3() { }; +void SortedVectorImpl::reservedSortedVectorImpl4() { }; +void SortedVectorImpl::reservedSortedVectorImpl5() { }; +void SortedVectorImpl::reservedSortedVectorImpl6() { }; +void SortedVectorImpl::reservedSortedVectorImpl7() { }; +void SortedVectorImpl::reservedSortedVectorImpl8() { }; + + +/*****************************************************************************/ + +}; // namespace android + diff --git a/libs/utils/ZipEntry.cpp b/libs/utils/ZipEntry.cpp new file mode 100644 index 000000000..fbc9e6784 --- /dev/null +++ b/libs/utils/ZipEntry.cpp @@ -0,0 +1,696 @@ +/* + * Copyright (C) 2006 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. + */ + +// +// Access to entries in a Zip archive. +// + +#define LOG_TAG "zip" + +#include "utils/ZipEntry.h" +#include "utils/Log.h" + +#include +#include +#include + +using namespace android; + +/* + * Initialize a new ZipEntry structure from a FILE* positioned at a + * CentralDirectoryEntry. + * + * On exit, the file pointer will be at the start of the next CDE or + * at the EOCD. + */ +status_t ZipEntry::initFromCDE(FILE* fp) +{ + status_t result; + long posn; + bool hasDD; + + //LOGV("initFromCDE ---\n"); + + /* read the CDE */ + result = mCDE.read(fp); + if (result != NO_ERROR) { + LOGD("mCDE.read failed\n"); + return result; + } + + //mCDE.dump(); + + /* using the info in the CDE, go load up the LFH */ + posn = ftell(fp); + if (fseek(fp, mCDE.mLocalHeaderRelOffset, SEEK_SET) != 0) { + LOGD("local header seek failed (%ld)\n", + mCDE.mLocalHeaderRelOffset); + return UNKNOWN_ERROR; + } + + result = mLFH.read(fp); + if (result != NO_ERROR) { + LOGD("mLFH.read failed\n"); + return result; + } + + if (fseek(fp, posn, SEEK_SET) != 0) + return UNKNOWN_ERROR; + + //mLFH.dump(); + + /* + * We *might* need to read the Data Descriptor at this point and + * integrate it into the LFH. If this bit is set, the CRC-32, + * compressed size, and uncompressed size will be zero. In practice + * these seem to be rare. + */ + hasDD = (mLFH.mGPBitFlag & kUsesDataDescr) != 0; + if (hasDD) { + // do something clever + //LOGD("+++ has data descriptor\n"); + } + + /* + * Sanity-check the LFH. Note that this will fail if the "kUsesDataDescr" + * flag is set, because the LFH is incomplete. (Not a problem, since we + * prefer the CDE values.) + */ + if (!hasDD && !compareHeaders()) { + LOGW("WARNING: header mismatch\n"); + // keep going? + } + + /* + * If the mVersionToExtract is greater than 20, we may have an + * issue unpacking the record -- could be encrypted, compressed + * with something we don't support, or use Zip64 extensions. We + * can defer worrying about that to when we're extracting data. + */ + + return NO_ERROR; +} + +/* + * Initialize a new entry. Pass in the file name and an optional comment. + * + * Initializes the CDE and the LFH. + */ +void ZipEntry::initNew(const char* fileName, const char* comment) +{ + assert(fileName != NULL && *fileName != '\0'); // name required + + /* most fields are properly initialized by constructor */ + mCDE.mVersionMadeBy = kDefaultMadeBy; + mCDE.mVersionToExtract = kDefaultVersion; + mCDE.mCompressionMethod = kCompressStored; + mCDE.mFileNameLength = strlen(fileName); + if (comment != NULL) + mCDE.mFileCommentLength = strlen(comment); + mCDE.mExternalAttrs = 0x81b60020; // matches what WinZip does + + if (mCDE.mFileNameLength > 0) { + mCDE.mFileName = new unsigned char[mCDE.mFileNameLength+1]; + strcpy((char*) mCDE.mFileName, fileName); + } + if (mCDE.mFileCommentLength > 0) { + /* TODO: stop assuming null-terminated ASCII here? */ + mCDE.mFileComment = new unsigned char[mCDE.mFileCommentLength+1]; + strcpy((char*) mCDE.mFileComment, comment); + } + + copyCDEtoLFH(); +} + +/* + * Initialize a new entry, starting with the ZipEntry from a different + * archive. + * + * Initializes the CDE and the LFH. + */ +status_t ZipEntry::initFromExternal(const ZipFile* pZipFile, + const ZipEntry* pEntry) +{ + /* + * Copy everything in the CDE over, then fix up the hairy bits. + */ + memcpy(&mCDE, &pEntry->mCDE, sizeof(mCDE)); + + if (mCDE.mFileNameLength > 0) { + mCDE.mFileName = new unsigned char[mCDE.mFileNameLength+1]; + if (mCDE.mFileName == NULL) + return NO_MEMORY; + strcpy((char*) mCDE.mFileName, (char*)pEntry->mCDE.mFileName); + } + if (mCDE.mFileCommentLength > 0) { + mCDE.mFileComment = new unsigned char[mCDE.mFileCommentLength+1]; + if (mCDE.mFileComment == NULL) + return NO_MEMORY; + strcpy((char*) mCDE.mFileComment, (char*)pEntry->mCDE.mFileComment); + } + if (mCDE.mExtraFieldLength > 0) { + /* we null-terminate this, though it may not be a string */ + mCDE.mExtraField = new unsigned char[mCDE.mExtraFieldLength+1]; + if (mCDE.mExtraField == NULL) + return NO_MEMORY; + memcpy(mCDE.mExtraField, pEntry->mCDE.mExtraField, + mCDE.mExtraFieldLength+1); + } + + /* construct the LFH from the CDE */ + copyCDEtoLFH(); + + /* + * The LFH "extra" field is independent of the CDE "extra", so we + * handle it here. + */ + assert(mLFH.mExtraField == NULL); + mLFH.mExtraFieldLength = pEntry->mLFH.mExtraFieldLength; + if (mLFH.mExtraFieldLength > 0) { + mLFH.mExtraField = new unsigned char[mLFH.mExtraFieldLength+1]; + if (mLFH.mExtraField == NULL) + return NO_MEMORY; + memcpy(mLFH.mExtraField, pEntry->mLFH.mExtraField, + mLFH.mExtraFieldLength+1); + } + + return NO_ERROR; +} + +/* + * Insert pad bytes in the LFH by tweaking the "extra" field. This will + * potentially confuse something that put "extra" data in here earlier, + * but I can't find an actual problem. + */ +status_t ZipEntry::addPadding(int padding) +{ + if (padding <= 0) + return INVALID_OPERATION; + + //LOGI("HEY: adding %d pad bytes to existing %d in %s\n", + // padding, mLFH.mExtraFieldLength, mCDE.mFileName); + + if (mLFH.mExtraFieldLength > 0) { + /* extend existing field */ + unsigned char* newExtra; + + newExtra = new unsigned char[mLFH.mExtraFieldLength + padding]; + if (newExtra == NULL) + return NO_MEMORY; + memset(newExtra + mLFH.mExtraFieldLength, 0, padding); + memcpy(newExtra, mLFH.mExtraField, mLFH.mExtraFieldLength); + + delete[] mLFH.mExtraField; + mLFH.mExtraField = newExtra; + mLFH.mExtraFieldLength += padding; + } else { + /* create new field */ + mLFH.mExtraField = new unsigned char[padding]; + memset(mLFH.mExtraField, 0, padding); + mLFH.mExtraFieldLength = padding; + } + + return NO_ERROR; +} + +/* + * Set the fields in the LFH equal to the corresponding fields in the CDE. + * + * This does not touch the LFH "extra" field. + */ +void ZipEntry::copyCDEtoLFH(void) +{ + mLFH.mVersionToExtract = mCDE.mVersionToExtract; + mLFH.mGPBitFlag = mCDE.mGPBitFlag; + mLFH.mCompressionMethod = mCDE.mCompressionMethod; + mLFH.mLastModFileTime = mCDE.mLastModFileTime; + mLFH.mLastModFileDate = mCDE.mLastModFileDate; + mLFH.mCRC32 = mCDE.mCRC32; + mLFH.mCompressedSize = mCDE.mCompressedSize; + mLFH.mUncompressedSize = mCDE.mUncompressedSize; + mLFH.mFileNameLength = mCDE.mFileNameLength; + // the "extra field" is independent + + delete[] mLFH.mFileName; + if (mLFH.mFileNameLength > 0) { + mLFH.mFileName = new unsigned char[mLFH.mFileNameLength+1]; + strcpy((char*) mLFH.mFileName, (const char*) mCDE.mFileName); + } else { + mLFH.mFileName = NULL; + } +} + +/* + * Set some information about a file after we add it. + */ +void ZipEntry::setDataInfo(long uncompLen, long compLen, unsigned long crc32, + int compressionMethod) +{ + mCDE.mCompressionMethod = compressionMethod; + mCDE.mCRC32 = crc32; + mCDE.mCompressedSize = compLen; + mCDE.mUncompressedSize = uncompLen; + mCDE.mCompressionMethod = compressionMethod; + if (compressionMethod == kCompressDeflated) { + mCDE.mGPBitFlag |= 0x0002; // indicates maximum compression used + } + copyCDEtoLFH(); +} + +/* + * See if the data in mCDE and mLFH match up. This is mostly useful for + * debugging these classes, but it can be used to identify damaged + * archives. + * + * Returns "false" if they differ. + */ +bool ZipEntry::compareHeaders(void) const +{ + if (mCDE.mVersionToExtract != mLFH.mVersionToExtract) { + LOGV("cmp: VersionToExtract\n"); + return false; + } + if (mCDE.mGPBitFlag != mLFH.mGPBitFlag) { + LOGV("cmp: GPBitFlag\n"); + return false; + } + if (mCDE.mCompressionMethod != mLFH.mCompressionMethod) { + LOGV("cmp: CompressionMethod\n"); + return false; + } + if (mCDE.mLastModFileTime != mLFH.mLastModFileTime) { + LOGV("cmp: LastModFileTime\n"); + return false; + } + if (mCDE.mLastModFileDate != mLFH.mLastModFileDate) { + LOGV("cmp: LastModFileDate\n"); + return false; + } + if (mCDE.mCRC32 != mLFH.mCRC32) { + LOGV("cmp: CRC32\n"); + return false; + } + if (mCDE.mCompressedSize != mLFH.mCompressedSize) { + LOGV("cmp: CompressedSize\n"); + return false; + } + if (mCDE.mUncompressedSize != mLFH.mUncompressedSize) { + LOGV("cmp: UncompressedSize\n"); + return false; + } + if (mCDE.mFileNameLength != mLFH.mFileNameLength) { + LOGV("cmp: FileNameLength\n"); + return false; + } +#if 0 // this seems to be used for padding, not real data + if (mCDE.mExtraFieldLength != mLFH.mExtraFieldLength) { + LOGV("cmp: ExtraFieldLength\n"); + return false; + } +#endif + if (mCDE.mFileName != NULL) { + if (strcmp((char*) mCDE.mFileName, (char*) mLFH.mFileName) != 0) { + LOGV("cmp: FileName\n"); + return false; + } + } + + return true; +} + + +/* + * Convert the DOS date/time stamp into a UNIX time stamp. + */ +time_t ZipEntry::getModWhen(void) const +{ + struct tm parts; + + parts.tm_sec = (mCDE.mLastModFileTime & 0x001f) << 1; + parts.tm_min = (mCDE.mLastModFileTime & 0x07e0) >> 5; + parts.tm_hour = (mCDE.mLastModFileTime & 0xf800) >> 11; + parts.tm_mday = (mCDE.mLastModFileDate & 0x001f); + parts.tm_mon = ((mCDE.mLastModFileDate & 0x01e0) >> 5) -1; + parts.tm_year = ((mCDE.mLastModFileDate & 0xfe00) >> 9) + 80; + parts.tm_wday = parts.tm_yday = 0; + parts.tm_isdst = -1; // DST info "not available" + + return mktime(&parts); +} + +/* + * Set the CDE/LFH timestamp from UNIX time. + */ +void ZipEntry::setModWhen(time_t when) +{ +#ifdef HAVE_LOCALTIME_R + struct tm tmResult; +#endif + time_t even; + unsigned short zdate, ztime; + + struct tm* ptm; + + /* round up to an even number of seconds */ + even = (time_t)(((unsigned long)(when) + 1) & (~1)); + + /* expand */ +#ifdef HAVE_LOCALTIME_R + ptm = localtime_r(&even, &tmResult); +#else + ptm = localtime(&even); +#endif + + int year; + year = ptm->tm_year; + if (year < 80) + year = 80; + + zdate = (year - 80) << 9 | (ptm->tm_mon+1) << 5 | ptm->tm_mday; + ztime = ptm->tm_hour << 11 | ptm->tm_min << 5 | ptm->tm_sec >> 1; + + mCDE.mLastModFileTime = mLFH.mLastModFileTime = ztime; + mCDE.mLastModFileDate = mLFH.mLastModFileDate = zdate; +} + + +/* + * =========================================================================== + * ZipEntry::LocalFileHeader + * =========================================================================== + */ + +/* + * Read a local file header. + * + * On entry, "fp" points to the signature at the start of the header. + * On exit, "fp" points to the start of data. + */ +status_t ZipEntry::LocalFileHeader::read(FILE* fp) +{ + status_t result = NO_ERROR; + unsigned char buf[kLFHLen]; + + assert(mFileName == NULL); + assert(mExtraField == NULL); + + if (fread(buf, 1, kLFHLen, fp) != kLFHLen) { + result = UNKNOWN_ERROR; + goto bail; + } + + if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) { + LOGD("whoops: didn't find expected signature\n"); + result = UNKNOWN_ERROR; + goto bail; + } + + mVersionToExtract = ZipEntry::getShortLE(&buf[0x04]); + mGPBitFlag = ZipEntry::getShortLE(&buf[0x06]); + mCompressionMethod = ZipEntry::getShortLE(&buf[0x08]); + mLastModFileTime = ZipEntry::getShortLE(&buf[0x0a]); + mLastModFileDate = ZipEntry::getShortLE(&buf[0x0c]); + mCRC32 = ZipEntry::getLongLE(&buf[0x0e]); + mCompressedSize = ZipEntry::getLongLE(&buf[0x12]); + mUncompressedSize = ZipEntry::getLongLE(&buf[0x16]); + mFileNameLength = ZipEntry::getShortLE(&buf[0x1a]); + mExtraFieldLength = ZipEntry::getShortLE(&buf[0x1c]); + + // TODO: validate sizes + + /* grab filename */ + if (mFileNameLength != 0) { + mFileName = new unsigned char[mFileNameLength+1]; + if (mFileName == NULL) { + result = NO_MEMORY; + goto bail; + } + if (fread(mFileName, 1, mFileNameLength, fp) != mFileNameLength) { + result = UNKNOWN_ERROR; + goto bail; + } + mFileName[mFileNameLength] = '\0'; + } + + /* grab extra field */ + if (mExtraFieldLength != 0) { + mExtraField = new unsigned char[mExtraFieldLength+1]; + if (mExtraField == NULL) { + result = NO_MEMORY; + goto bail; + } + if (fread(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) { + result = UNKNOWN_ERROR; + goto bail; + } + mExtraField[mExtraFieldLength] = '\0'; + } + +bail: + return result; +} + +/* + * Write a local file header. + */ +status_t ZipEntry::LocalFileHeader::write(FILE* fp) +{ + unsigned char buf[kLFHLen]; + + ZipEntry::putLongLE(&buf[0x00], kSignature); + ZipEntry::putShortLE(&buf[0x04], mVersionToExtract); + ZipEntry::putShortLE(&buf[0x06], mGPBitFlag); + ZipEntry::putShortLE(&buf[0x08], mCompressionMethod); + ZipEntry::putShortLE(&buf[0x0a], mLastModFileTime); + ZipEntry::putShortLE(&buf[0x0c], mLastModFileDate); + ZipEntry::putLongLE(&buf[0x0e], mCRC32); + ZipEntry::putLongLE(&buf[0x12], mCompressedSize); + ZipEntry::putLongLE(&buf[0x16], mUncompressedSize); + ZipEntry::putShortLE(&buf[0x1a], mFileNameLength); + ZipEntry::putShortLE(&buf[0x1c], mExtraFieldLength); + + if (fwrite(buf, 1, kLFHLen, fp) != kLFHLen) + return UNKNOWN_ERROR; + + /* write filename */ + if (mFileNameLength != 0) { + if (fwrite(mFileName, 1, mFileNameLength, fp) != mFileNameLength) + return UNKNOWN_ERROR; + } + + /* write "extra field" */ + if (mExtraFieldLength != 0) { + if (fwrite(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) + return UNKNOWN_ERROR; + } + + return NO_ERROR; +} + + +/* + * Dump the contents of a LocalFileHeader object. + */ +void ZipEntry::LocalFileHeader::dump(void) const +{ + LOGD(" LocalFileHeader contents:\n"); + LOGD(" versToExt=%u gpBits=0x%04x compression=%u\n", + mVersionToExtract, mGPBitFlag, mCompressionMethod); + LOGD(" modTime=0x%04x modDate=0x%04x crc32=0x%08lx\n", + mLastModFileTime, mLastModFileDate, mCRC32); + LOGD(" compressedSize=%lu uncompressedSize=%lu\n", + mCompressedSize, mUncompressedSize); + LOGD(" filenameLen=%u extraLen=%u\n", + mFileNameLength, mExtraFieldLength); + if (mFileName != NULL) + LOGD(" filename: '%s'\n", mFileName); +} + + +/* + * =========================================================================== + * ZipEntry::CentralDirEntry + * =========================================================================== + */ + +/* + * Read the central dir entry that appears next in the file. + * + * On entry, "fp" should be positioned on the signature bytes for the + * entry. On exit, "fp" will point at the signature word for the next + * entry or for the EOCD. + */ +status_t ZipEntry::CentralDirEntry::read(FILE* fp) +{ + status_t result = NO_ERROR; + unsigned char buf[kCDELen]; + + /* no re-use */ + assert(mFileName == NULL); + assert(mExtraField == NULL); + assert(mFileComment == NULL); + + if (fread(buf, 1, kCDELen, fp) != kCDELen) { + result = UNKNOWN_ERROR; + goto bail; + } + + if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) { + LOGD("Whoops: didn't find expected signature\n"); + result = UNKNOWN_ERROR; + goto bail; + } + + mVersionMadeBy = ZipEntry::getShortLE(&buf[0x04]); + mVersionToExtract = ZipEntry::getShortLE(&buf[0x06]); + mGPBitFlag = ZipEntry::getShortLE(&buf[0x08]); + mCompressionMethod = ZipEntry::getShortLE(&buf[0x0a]); + mLastModFileTime = ZipEntry::getShortLE(&buf[0x0c]); + mLastModFileDate = ZipEntry::getShortLE(&buf[0x0e]); + mCRC32 = ZipEntry::getLongLE(&buf[0x10]); + mCompressedSize = ZipEntry::getLongLE(&buf[0x14]); + mUncompressedSize = ZipEntry::getLongLE(&buf[0x18]); + mFileNameLength = ZipEntry::getShortLE(&buf[0x1c]); + mExtraFieldLength = ZipEntry::getShortLE(&buf[0x1e]); + mFileCommentLength = ZipEntry::getShortLE(&buf[0x20]); + mDiskNumberStart = ZipEntry::getShortLE(&buf[0x22]); + mInternalAttrs = ZipEntry::getShortLE(&buf[0x24]); + mExternalAttrs = ZipEntry::getLongLE(&buf[0x26]); + mLocalHeaderRelOffset = ZipEntry::getLongLE(&buf[0x2a]); + + // TODO: validate sizes and offsets + + /* grab filename */ + if (mFileNameLength != 0) { + mFileName = new unsigned char[mFileNameLength+1]; + if (mFileName == NULL) { + result = NO_MEMORY; + goto bail; + } + if (fread(mFileName, 1, mFileNameLength, fp) != mFileNameLength) { + result = UNKNOWN_ERROR; + goto bail; + } + mFileName[mFileNameLength] = '\0'; + } + + /* read "extra field" */ + if (mExtraFieldLength != 0) { + mExtraField = new unsigned char[mExtraFieldLength+1]; + if (mExtraField == NULL) { + result = NO_MEMORY; + goto bail; + } + if (fread(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) { + result = UNKNOWN_ERROR; + goto bail; + } + mExtraField[mExtraFieldLength] = '\0'; + } + + + /* grab comment, if any */ + if (mFileCommentLength != 0) { + mFileComment = new unsigned char[mFileCommentLength+1]; + if (mFileComment == NULL) { + result = NO_MEMORY; + goto bail; + } + if (fread(mFileComment, 1, mFileCommentLength, fp) != mFileCommentLength) + { + result = UNKNOWN_ERROR; + goto bail; + } + mFileComment[mFileCommentLength] = '\0'; + } + +bail: + return result; +} + +/* + * Write a central dir entry. + */ +status_t ZipEntry::CentralDirEntry::write(FILE* fp) +{ + unsigned char buf[kCDELen]; + + ZipEntry::putLongLE(&buf[0x00], kSignature); + ZipEntry::putShortLE(&buf[0x04], mVersionMadeBy); + ZipEntry::putShortLE(&buf[0x06], mVersionToExtract); + ZipEntry::putShortLE(&buf[0x08], mGPBitFlag); + ZipEntry::putShortLE(&buf[0x0a], mCompressionMethod); + ZipEntry::putShortLE(&buf[0x0c], mLastModFileTime); + ZipEntry::putShortLE(&buf[0x0e], mLastModFileDate); + ZipEntry::putLongLE(&buf[0x10], mCRC32); + ZipEntry::putLongLE(&buf[0x14], mCompressedSize); + ZipEntry::putLongLE(&buf[0x18], mUncompressedSize); + ZipEntry::putShortLE(&buf[0x1c], mFileNameLength); + ZipEntry::putShortLE(&buf[0x1e], mExtraFieldLength); + ZipEntry::putShortLE(&buf[0x20], mFileCommentLength); + ZipEntry::putShortLE(&buf[0x22], mDiskNumberStart); + ZipEntry::putShortLE(&buf[0x24], mInternalAttrs); + ZipEntry::putLongLE(&buf[0x26], mExternalAttrs); + ZipEntry::putLongLE(&buf[0x2a], mLocalHeaderRelOffset); + + if (fwrite(buf, 1, kCDELen, fp) != kCDELen) + return UNKNOWN_ERROR; + + /* write filename */ + if (mFileNameLength != 0) { + if (fwrite(mFileName, 1, mFileNameLength, fp) != mFileNameLength) + return UNKNOWN_ERROR; + } + + /* write "extra field" */ + if (mExtraFieldLength != 0) { + if (fwrite(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) + return UNKNOWN_ERROR; + } + + /* write comment */ + if (mFileCommentLength != 0) { + if (fwrite(mFileComment, 1, mFileCommentLength, fp) != mFileCommentLength) + return UNKNOWN_ERROR; + } + + return NO_ERROR; +} + +/* + * Dump the contents of a CentralDirEntry object. + */ +void ZipEntry::CentralDirEntry::dump(void) const +{ + LOGD(" CentralDirEntry contents:\n"); + LOGD(" versMadeBy=%u versToExt=%u gpBits=0x%04x compression=%u\n", + mVersionMadeBy, mVersionToExtract, mGPBitFlag, mCompressionMethod); + LOGD(" modTime=0x%04x modDate=0x%04x crc32=0x%08lx\n", + mLastModFileTime, mLastModFileDate, mCRC32); + LOGD(" compressedSize=%lu uncompressedSize=%lu\n", + mCompressedSize, mUncompressedSize); + LOGD(" filenameLen=%u extraLen=%u commentLen=%u\n", + mFileNameLength, mExtraFieldLength, mFileCommentLength); + LOGD(" diskNumStart=%u intAttr=0x%04x extAttr=0x%08lx relOffset=%lu\n", + mDiskNumberStart, mInternalAttrs, mExternalAttrs, + mLocalHeaderRelOffset); + + if (mFileName != NULL) + LOGD(" filename: '%s'\n", mFileName); + if (mFileComment != NULL) + LOGD(" comment: '%s'\n", mFileComment); +} + diff --git a/libs/utils/ZipFile.cpp b/libs/utils/ZipFile.cpp new file mode 100644 index 000000000..89aa874b4 --- /dev/null +++ b/libs/utils/ZipFile.cpp @@ -0,0 +1,1296 @@ +/* + * Copyright (C) 2006 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. + */ + +// +// Access to Zip archives. +// + +#define LOG_TAG "zip" + +#include "utils/ZipFile.h" +#include "utils/ZipUtils.h" +#include "utils/Log.h" + +#include +#define DEF_MEM_LEVEL 8 // normally in zutil.h? + +#include +#include +#include +#include + +using namespace android; + +/* + * Some environments require the "b", some choke on it. + */ +#define FILE_OPEN_RO "rb" +#define FILE_OPEN_RW "r+b" +#define FILE_OPEN_RW_CREATE "w+b" + +/* should live somewhere else? */ +static status_t errnoToStatus(int err) +{ + if (err == ENOENT) + return NAME_NOT_FOUND; + else if (err == EACCES) + return PERMISSION_DENIED; + else + return UNKNOWN_ERROR; +} + +/* + * Open a file and parse its guts. + */ +status_t ZipFile::open(const char* zipFileName, int flags) +{ + bool newArchive = false; + + assert(mZipFp == NULL); // no reopen + + if ((flags & kOpenTruncate)) + flags |= kOpenCreate; // trunc implies create + + if ((flags & kOpenReadOnly) && (flags & kOpenReadWrite)) + return INVALID_OPERATION; // not both + if (!((flags & kOpenReadOnly) || (flags & kOpenReadWrite))) + return INVALID_OPERATION; // not neither + if ((flags & kOpenCreate) && !(flags & kOpenReadWrite)) + return INVALID_OPERATION; // create requires write + + if (flags & kOpenTruncate) { + newArchive = true; + } else { + newArchive = (access(zipFileName, F_OK) != 0); + if (!(flags & kOpenCreate) && newArchive) { + /* not creating, must already exist */ + LOGD("File %s does not exist", zipFileName); + return NAME_NOT_FOUND; + } + } + + /* open the file */ + const char* openflags; + if (flags & kOpenReadWrite) { + if (newArchive) + openflags = FILE_OPEN_RW_CREATE; + else + openflags = FILE_OPEN_RW; + } else { + openflags = FILE_OPEN_RO; + } + mZipFp = fopen(zipFileName, openflags); + if (mZipFp == NULL) { + int err = errno; + LOGD("fopen failed: %d\n", err); + return errnoToStatus(err); + } + + status_t result; + if (!newArchive) { + /* + * Load the central directory. If that fails, then this probably + * isn't a Zip archive. + */ + result = readCentralDir(); + } else { + /* + * Newly-created. The EndOfCentralDir constructor actually + * sets everything to be the way we want it (all zeroes). We + * set mNeedCDRewrite so that we create *something* if the + * caller doesn't add any files. (We could also just unlink + * the file if it's brand new and nothing was added, but that's + * probably doing more than we really should -- the user might + * have a need for empty zip files.) + */ + mNeedCDRewrite = true; + result = NO_ERROR; + } + + if (flags & kOpenReadOnly) + mReadOnly = true; + else + assert(!mReadOnly); + + return result; +} + +/* + * Return the Nth entry in the archive. + */ +ZipEntry* ZipFile::getEntryByIndex(int idx) const +{ + if (idx < 0 || idx >= (int) mEntries.size()) + return NULL; + + return mEntries[idx]; +} + +/* + * Find an entry by name. + */ +ZipEntry* ZipFile::getEntryByName(const char* fileName) const +{ + /* + * Do a stupid linear string-compare search. + * + * There are various ways to speed this up, especially since it's rare + * to intermingle changes to the archive with "get by name" calls. We + * don't want to sort the mEntries vector itself, however, because + * it's used to recreate the Central Directory. + * + * (Hash table works, parallel list of pointers in sorted order is good.) + */ + int idx; + + for (idx = mEntries.size()-1; idx >= 0; idx--) { + ZipEntry* pEntry = mEntries[idx]; + if (!pEntry->getDeleted() && + strcmp(fileName, pEntry->getFileName()) == 0) + { + return pEntry; + } + } + + return NULL; +} + +/* + * Empty the mEntries vector. + */ +void ZipFile::discardEntries(void) +{ + int count = mEntries.size(); + + while (--count >= 0) + delete mEntries[count]; + + mEntries.clear(); +} + + +/* + * Find the central directory and read the contents. + * + * The fun thing about ZIP archives is that they may or may not be + * readable from start to end. In some cases, notably for archives + * that were written to stdout, the only length information is in the + * central directory at the end of the file. + * + * Of course, the central directory can be followed by a variable-length + * comment field, so we have to scan through it backwards. The comment + * is at most 64K, plus we have 18 bytes for the end-of-central-dir stuff + * itself, plus apparently sometimes people throw random junk on the end + * just for the fun of it. + * + * This is all a little wobbly. If the wrong value ends up in the EOCD + * area, we're hosed. This appears to be the way that everbody handles + * it though, so we're in pretty good company if this fails. + */ +status_t ZipFile::readCentralDir(void) +{ + status_t result = NO_ERROR; + unsigned char* buf = NULL; + off_t fileLength, seekStart; + long readAmount; + int i; + + fseek(mZipFp, 0, SEEK_END); + fileLength = ftell(mZipFp); + rewind(mZipFp); + + /* too small to be a ZIP archive? */ + if (fileLength < EndOfCentralDir::kEOCDLen) { + LOGD("Length is %ld -- too small\n", (long)fileLength); + result = INVALID_OPERATION; + goto bail; + } + + buf = new unsigned char[EndOfCentralDir::kMaxEOCDSearch]; + if (buf == NULL) { + LOGD("Failure allocating %d bytes for EOCD search", + EndOfCentralDir::kMaxEOCDSearch); + result = NO_MEMORY; + goto bail; + } + + if (fileLength > EndOfCentralDir::kMaxEOCDSearch) { + seekStart = fileLength - EndOfCentralDir::kMaxEOCDSearch; + readAmount = EndOfCentralDir::kMaxEOCDSearch; + } else { + seekStart = 0; + readAmount = (long) fileLength; + } + if (fseek(mZipFp, seekStart, SEEK_SET) != 0) { + LOGD("Failure seeking to end of zip at %ld", (long) seekStart); + result = UNKNOWN_ERROR; + goto bail; + } + + /* read the last part of the file into the buffer */ + if (fread(buf, 1, readAmount, mZipFp) != (size_t) readAmount) { + LOGD("short file? wanted %ld\n", readAmount); + result = UNKNOWN_ERROR; + goto bail; + } + + /* find the end-of-central-dir magic */ + for (i = readAmount - 4; i >= 0; i--) { + if (buf[i] == 0x50 && + ZipEntry::getLongLE(&buf[i]) == EndOfCentralDir::kSignature) + { + LOGV("+++ Found EOCD at buf+%d\n", i); + break; + } + } + if (i < 0) { + LOGD("EOCD not found, not Zip\n"); + result = INVALID_OPERATION; + goto bail; + } + + /* extract eocd values */ + result = mEOCD.readBuf(buf + i, readAmount - i); + if (result != NO_ERROR) { + LOGD("Failure reading %ld bytes of EOCD values", readAmount - i); + goto bail; + } + //mEOCD.dump(); + + if (mEOCD.mDiskNumber != 0 || mEOCD.mDiskWithCentralDir != 0 || + mEOCD.mNumEntries != mEOCD.mTotalNumEntries) + { + LOGD("Archive spanning not supported\n"); + result = INVALID_OPERATION; + goto bail; + } + + /* + * So far so good. "mCentralDirSize" is the size in bytes of the + * central directory, so we can just seek back that far to find it. + * We can also seek forward mCentralDirOffset bytes from the + * start of the file. + * + * We're not guaranteed to have the rest of the central dir in the + * buffer, nor are we guaranteed that the central dir will have any + * sort of convenient size. We need to skip to the start of it and + * read the header, then the other goodies. + * + * The only thing we really need right now is the file comment, which + * we're hoping to preserve. + */ + if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) { + LOGD("Failure seeking to central dir offset %ld\n", + mEOCD.mCentralDirOffset); + result = UNKNOWN_ERROR; + goto bail; + } + + /* + * Loop through and read the central dir entries. + */ + LOGV("Scanning %d entries...\n", mEOCD.mTotalNumEntries); + int entry; + for (entry = 0; entry < mEOCD.mTotalNumEntries; entry++) { + ZipEntry* pEntry = new ZipEntry; + + result = pEntry->initFromCDE(mZipFp); + if (result != NO_ERROR) { + LOGD("initFromCDE failed\n"); + delete pEntry; + goto bail; + } + + mEntries.add(pEntry); + } + + + /* + * If all went well, we should now be back at the EOCD. + */ + { + unsigned char checkBuf[4]; + if (fread(checkBuf, 1, 4, mZipFp) != 4) { + LOGD("EOCD check read failed\n"); + result = INVALID_OPERATION; + goto bail; + } + if (ZipEntry::getLongLE(checkBuf) != EndOfCentralDir::kSignature) { + LOGD("EOCD read check failed\n"); + result = UNKNOWN_ERROR; + goto bail; + } + LOGV("+++ EOCD read check passed\n"); + } + +bail: + delete[] buf; + return result; +} + + +/* + * Add a new file to the archive. + * + * This requires creating and populating a ZipEntry structure, and copying + * the data into the file at the appropriate position. The "appropriate + * position" is the current location of the central directory, which we + * casually overwrite (we can put it back later). + * + * If we were concerned about safety, we would want to make all changes + * in a temp file and then overwrite the original after everything was + * safely written. Not really a concern for us. + */ +status_t ZipFile::addCommon(const char* fileName, const void* data, size_t size, + const char* storageName, int sourceType, int compressionMethod, + ZipEntry** ppEntry) +{ + ZipEntry* pEntry = NULL; + status_t result = NO_ERROR; + long lfhPosn, startPosn, endPosn, uncompressedLen; + FILE* inputFp = NULL; + unsigned long crc; + time_t modWhen; + + if (mReadOnly) + return INVALID_OPERATION; + + assert(compressionMethod == ZipEntry::kCompressDeflated || + compressionMethod == ZipEntry::kCompressStored); + + /* make sure we're in a reasonable state */ + assert(mZipFp != NULL); + assert(mEntries.size() == mEOCD.mTotalNumEntries); + + /* make sure it doesn't already exist */ + if (getEntryByName(storageName) != NULL) + return ALREADY_EXISTS; + + if (!data) { + inputFp = fopen(fileName, FILE_OPEN_RO); + if (inputFp == NULL) + return errnoToStatus(errno); + } + + if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) { + result = UNKNOWN_ERROR; + goto bail; + } + + pEntry = new ZipEntry; + pEntry->initNew(storageName, NULL); + + /* + * From here on out, failures are more interesting. + */ + mNeedCDRewrite = true; + + /* + * Write the LFH, even though it's still mostly blank. We need it + * as a place-holder. In theory the LFH isn't necessary, but in + * practice some utilities demand it. + */ + lfhPosn = ftell(mZipFp); + pEntry->mLFH.write(mZipFp); + startPosn = ftell(mZipFp); + + /* + * Copy the data in, possibly compressing it as we go. + */ + if (sourceType == ZipEntry::kCompressStored) { + if (compressionMethod == ZipEntry::kCompressDeflated) { + bool failed = false; + result = compressFpToFp(mZipFp, inputFp, data, size, &crc); + if (result != NO_ERROR) { + LOGD("compression failed, storing\n"); + failed = true; + } else { + /* + * Make sure it has compressed "enough". This probably ought + * to be set through an API call, but I don't expect our + * criteria to change over time. + */ + long src = inputFp ? ftell(inputFp) : size; + long dst = ftell(mZipFp) - startPosn; + if (dst + (dst / 10) > src) { + LOGD("insufficient compression (src=%ld dst=%ld), storing\n", + src, dst); + failed = true; + } + } + + if (failed) { + compressionMethod = ZipEntry::kCompressStored; + if (inputFp) rewind(inputFp); + fseek(mZipFp, startPosn, SEEK_SET); + /* fall through to kCompressStored case */ + } + } + /* handle "no compression" request, or failed compression from above */ + if (compressionMethod == ZipEntry::kCompressStored) { + if (inputFp) { + result = copyFpToFp(mZipFp, inputFp, &crc); + } else { + result = copyDataToFp(mZipFp, data, size, &crc); + } + if (result != NO_ERROR) { + // don't need to truncate; happens in CDE rewrite + LOGD("failed copying data in\n"); + goto bail; + } + } + + // currently seeked to end of file + uncompressedLen = inputFp ? ftell(inputFp) : size; + } else if (sourceType == ZipEntry::kCompressDeflated) { + /* we should support uncompressed-from-compressed, but it's not + * important right now */ + assert(compressionMethod == ZipEntry::kCompressDeflated); + + bool scanResult; + int method; + long compressedLen; + + scanResult = ZipUtils::examineGzip(inputFp, &method, &uncompressedLen, + &compressedLen, &crc); + if (!scanResult || method != ZipEntry::kCompressDeflated) { + LOGD("this isn't a deflated gzip file?"); + result = UNKNOWN_ERROR; + goto bail; + } + + result = copyPartialFpToFp(mZipFp, inputFp, compressedLen, NULL); + if (result != NO_ERROR) { + LOGD("failed copying gzip data in\n"); + goto bail; + } + } else { + assert(false); + result = UNKNOWN_ERROR; + goto bail; + } + + /* + * We could write the "Data Descriptor", but there doesn't seem to + * be any point since we're going to go back and write the LFH. + * + * Update file offsets. + */ + endPosn = ftell(mZipFp); // seeked to end of compressed data + + /* + * Success! Fill out new values. + */ + pEntry->setDataInfo(uncompressedLen, endPosn - startPosn, crc, + compressionMethod); + modWhen = getModTime(inputFp ? fileno(inputFp) : fileno(mZipFp)); + pEntry->setModWhen(modWhen); + pEntry->setLFHOffset(lfhPosn); + mEOCD.mNumEntries++; + mEOCD.mTotalNumEntries++; + mEOCD.mCentralDirSize = 0; // mark invalid; set by flush() + mEOCD.mCentralDirOffset = endPosn; + + /* + * Go back and write the LFH. + */ + if (fseek(mZipFp, lfhPosn, SEEK_SET) != 0) { + result = UNKNOWN_ERROR; + goto bail; + } + pEntry->mLFH.write(mZipFp); + + /* + * Add pEntry to the list. + */ + mEntries.add(pEntry); + if (ppEntry != NULL) + *ppEntry = pEntry; + pEntry = NULL; + +bail: + if (inputFp != NULL) + fclose(inputFp); + delete pEntry; + return result; +} + +/* + * Add an entry by copying it from another zip file. If "padding" is + * nonzero, the specified number of bytes will be added to the "extra" + * field in the header. + * + * If "ppEntry" is non-NULL, a pointer to the new entry will be returned. + */ +status_t ZipFile::add(const ZipFile* pSourceZip, const ZipEntry* pSourceEntry, + int padding, ZipEntry** ppEntry) +{ + ZipEntry* pEntry = NULL; + status_t result; + long lfhPosn, endPosn; + + if (mReadOnly) + return INVALID_OPERATION; + + /* make sure we're in a reasonable state */ + assert(mZipFp != NULL); + assert(mEntries.size() == mEOCD.mTotalNumEntries); + + if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) { + result = UNKNOWN_ERROR; + goto bail; + } + + pEntry = new ZipEntry; + if (pEntry == NULL) { + result = NO_MEMORY; + goto bail; + } + + result = pEntry->initFromExternal(pSourceZip, pSourceEntry); + if (result != NO_ERROR) + goto bail; + if (padding != 0) { + result = pEntry->addPadding(padding); + if (result != NO_ERROR) + goto bail; + } + + /* + * From here on out, failures are more interesting. + */ + mNeedCDRewrite = true; + + /* + * Write the LFH. Since we're not recompressing the data, we already + * have all of the fields filled out. + */ + lfhPosn = ftell(mZipFp); + pEntry->mLFH.write(mZipFp); + + /* + * Copy the data over. + * + * If the "has data descriptor" flag is set, we want to copy the DD + * fields as well. This is a fixed-size area immediately following + * the data. + */ + if (fseek(pSourceZip->mZipFp, pSourceEntry->getFileOffset(), SEEK_SET) != 0) + { + result = UNKNOWN_ERROR; + goto bail; + } + + off_t copyLen; + copyLen = pSourceEntry->getCompressedLen(); + if ((pSourceEntry->mLFH.mGPBitFlag & ZipEntry::kUsesDataDescr) != 0) + copyLen += ZipEntry::kDataDescriptorLen; + + if (copyPartialFpToFp(mZipFp, pSourceZip->mZipFp, copyLen, NULL) + != NO_ERROR) + { + LOGW("copy of '%s' failed\n", pEntry->mCDE.mFileName); + result = UNKNOWN_ERROR; + goto bail; + } + + /* + * Update file offsets. + */ + endPosn = ftell(mZipFp); + + /* + * Success! Fill out new values. + */ + pEntry->setLFHOffset(lfhPosn); // sets mCDE.mLocalHeaderRelOffset + mEOCD.mNumEntries++; + mEOCD.mTotalNumEntries++; + mEOCD.mCentralDirSize = 0; // mark invalid; set by flush() + mEOCD.mCentralDirOffset = endPosn; + + /* + * Add pEntry to the list. + */ + mEntries.add(pEntry); + if (ppEntry != NULL) + *ppEntry = pEntry; + pEntry = NULL; + + result = NO_ERROR; + +bail: + delete pEntry; + return result; +} + +/* + * Copy all of the bytes in "src" to "dst". + * + * On exit, "srcFp" will be seeked to the end of the file, and "dstFp" + * will be seeked immediately past the data. + */ +status_t ZipFile::copyFpToFp(FILE* dstFp, FILE* srcFp, unsigned long* pCRC32) +{ + unsigned char tmpBuf[32768]; + size_t count; + + *pCRC32 = crc32(0L, Z_NULL, 0); + + while (1) { + count = fread(tmpBuf, 1, sizeof(tmpBuf), srcFp); + if (ferror(srcFp) || ferror(dstFp)) + return errnoToStatus(errno); + if (count == 0) + break; + + *pCRC32 = crc32(*pCRC32, tmpBuf, count); + + if (fwrite(tmpBuf, 1, count, dstFp) != count) { + LOGD("fwrite %d bytes failed\n", (int) count); + return UNKNOWN_ERROR; + } + } + + return NO_ERROR; +} + +/* + * Copy all of the bytes in "src" to "dst". + * + * On exit, "dstFp" will be seeked immediately past the data. + */ +status_t ZipFile::copyDataToFp(FILE* dstFp, + const void* data, size_t size, unsigned long* pCRC32) +{ + size_t count; + + *pCRC32 = crc32(0L, Z_NULL, 0); + if (size > 0) { + *pCRC32 = crc32(*pCRC32, (const unsigned char*)data, size); + if (fwrite(data, 1, size, dstFp) != size) { + LOGD("fwrite %d bytes failed\n", (int) size); + return UNKNOWN_ERROR; + } + } + + return NO_ERROR; +} + +/* + * Copy some of the bytes in "src" to "dst". + * + * If "pCRC32" is NULL, the CRC will not be computed. + * + * On exit, "srcFp" will be seeked to the end of the file, and "dstFp" + * will be seeked immediately past the data just written. + */ +status_t ZipFile::copyPartialFpToFp(FILE* dstFp, FILE* srcFp, long length, + unsigned long* pCRC32) +{ + unsigned char tmpBuf[32768]; + size_t count; + + if (pCRC32 != NULL) + *pCRC32 = crc32(0L, Z_NULL, 0); + + while (length) { + long readSize; + + readSize = sizeof(tmpBuf); + if (readSize > length) + readSize = length; + + count = fread(tmpBuf, 1, readSize, srcFp); + if ((long) count != readSize) { // error or unexpected EOF + LOGD("fread %d bytes failed\n", (int) readSize); + return UNKNOWN_ERROR; + } + + if (pCRC32 != NULL) + *pCRC32 = crc32(*pCRC32, tmpBuf, count); + + if (fwrite(tmpBuf, 1, count, dstFp) != count) { + LOGD("fwrite %d bytes failed\n", (int) count); + return UNKNOWN_ERROR; + } + + length -= readSize; + } + + return NO_ERROR; +} + +/* + * Compress all of the data in "srcFp" and write it to "dstFp". + * + * On exit, "srcFp" will be seeked to the end of the file, and "dstFp" + * will be seeked immediately past the compressed data. + */ +status_t ZipFile::compressFpToFp(FILE* dstFp, FILE* srcFp, + const void* data, size_t size, unsigned long* pCRC32) +{ + status_t result = NO_ERROR; + const size_t kBufSize = 32768; + unsigned char* inBuf = NULL; + unsigned char* outBuf = NULL; + z_stream zstream; + bool atEof = false; // no feof() aviailable yet + unsigned long crc; + int zerr; + + /* + * Create an input buffer and an output buffer. + */ + inBuf = new unsigned char[kBufSize]; + outBuf = new unsigned char[kBufSize]; + if (inBuf == NULL || outBuf == NULL) { + result = NO_MEMORY; + goto bail; + } + + /* + * Initialize the zlib stream. + */ + memset(&zstream, 0, sizeof(zstream)); + zstream.zalloc = Z_NULL; + zstream.zfree = Z_NULL; + zstream.opaque = Z_NULL; + zstream.next_in = NULL; + zstream.avail_in = 0; + zstream.next_out = outBuf; + zstream.avail_out = kBufSize; + zstream.data_type = Z_UNKNOWN; + + zerr = deflateInit2(&zstream, Z_BEST_COMPRESSION, + Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY); + if (zerr != Z_OK) { + result = UNKNOWN_ERROR; + if (zerr == Z_VERSION_ERROR) { + LOGE("Installed zlib is not compatible with linked version (%s)\n", + ZLIB_VERSION); + } else { + LOGD("Call to deflateInit2 failed (zerr=%d)\n", zerr); + } + goto bail; + } + + crc = crc32(0L, Z_NULL, 0); + + /* + * Loop while we have data. + */ + do { + size_t getSize; + int flush; + + /* only read if the input buffer is empty */ + if (zstream.avail_in == 0 && !atEof) { + LOGV("+++ reading %d bytes\n", (int)kBufSize); + if (data) { + getSize = size > kBufSize ? kBufSize : size; + memcpy(inBuf, data, getSize); + data = ((const char*)data) + getSize; + size -= getSize; + } else { + getSize = fread(inBuf, 1, kBufSize, srcFp); + if (ferror(srcFp)) { + LOGD("deflate read failed (errno=%d)\n", errno); + goto z_bail; + } + } + if (getSize < kBufSize) { + LOGV("+++ got %d bytes, EOF reached\n", + (int)getSize); + atEof = true; + } + + crc = crc32(crc, inBuf, getSize); + + zstream.next_in = inBuf; + zstream.avail_in = getSize; + } + + if (atEof) + flush = Z_FINISH; /* tell zlib that we're done */ + else + flush = Z_NO_FLUSH; /* more to come! */ + + zerr = deflate(&zstream, flush); + if (zerr != Z_OK && zerr != Z_STREAM_END) { + LOGD("zlib deflate call failed (zerr=%d)\n", zerr); + result = UNKNOWN_ERROR; + goto z_bail; + } + + /* write when we're full or when we're done */ + if (zstream.avail_out == 0 || + (zerr == Z_STREAM_END && zstream.avail_out != (uInt) kBufSize)) + { + LOGV("+++ writing %d bytes\n", (int) (zstream.next_out - outBuf)); + if (fwrite(outBuf, 1, zstream.next_out - outBuf, dstFp) != + (size_t)(zstream.next_out - outBuf)) + { + LOGD("write %d failed in deflate\n", + (int) (zstream.next_out - outBuf)); + goto z_bail; + } + + zstream.next_out = outBuf; + zstream.avail_out = kBufSize; + } + } while (zerr == Z_OK); + + assert(zerr == Z_STREAM_END); /* other errors should've been caught */ + + *pCRC32 = crc; + +z_bail: + deflateEnd(&zstream); /* free up any allocated structures */ + +bail: + delete[] inBuf; + delete[] outBuf; + + return result; +} + +/* + * Mark an entry as deleted. + * + * We will eventually need to crunch the file down, but if several files + * are being removed (perhaps as part of an "update" process) we can make + * things considerably faster by deferring the removal to "flush" time. + */ +status_t ZipFile::remove(ZipEntry* pEntry) +{ + /* + * Should verify that pEntry is actually part of this archive, and + * not some stray ZipEntry from a different file. + */ + + /* mark entry as deleted, and mark archive as dirty */ + pEntry->setDeleted(); + mNeedCDRewrite = true; + return NO_ERROR; +} + +/* + * Flush any pending writes. + * + * In particular, this will crunch out deleted entries, and write the + * Central Directory and EOCD if we have stomped on them. + */ +status_t ZipFile::flush(void) +{ + status_t result = NO_ERROR; + long eocdPosn; + int i, count; + + if (mReadOnly) + return INVALID_OPERATION; + if (!mNeedCDRewrite) + return NO_ERROR; + + assert(mZipFp != NULL); + + result = crunchArchive(); + if (result != NO_ERROR) + return result; + + if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) + return UNKNOWN_ERROR; + + count = mEntries.size(); + for (i = 0; i < count; i++) { + ZipEntry* pEntry = mEntries[i]; + pEntry->mCDE.write(mZipFp); + } + + eocdPosn = ftell(mZipFp); + mEOCD.mCentralDirSize = eocdPosn - mEOCD.mCentralDirOffset; + + mEOCD.write(mZipFp); + + /* + * If we had some stuff bloat up during compression and get replaced + * with plain files, or if we deleted some entries, there's a lot + * of wasted space at the end of the file. Remove it now. + */ + if (ftruncate(fileno(mZipFp), ftell(mZipFp)) != 0) { + LOGW("ftruncate failed %ld: %s\n", ftell(mZipFp), strerror(errno)); + // not fatal + } + + /* should we clear the "newly added" flag in all entries now? */ + + mNeedCDRewrite = false; + return NO_ERROR; +} + +/* + * Crunch deleted files out of an archive by shifting the later files down. + * + * Because we're not using a temp file, we do the operation inside the + * current file. + */ +status_t ZipFile::crunchArchive(void) +{ + status_t result = NO_ERROR; + int i, count; + long delCount, adjust; + +#if 0 + printf("CONTENTS:\n"); + for (i = 0; i < (int) mEntries.size(); i++) { + printf(" %d: lfhOff=%ld del=%d\n", + i, mEntries[i]->getLFHOffset(), mEntries[i]->getDeleted()); + } + printf(" END is %ld\n", (long) mEOCD.mCentralDirOffset); +#endif + + /* + * Roll through the set of files, shifting them as appropriate. We + * could probably get a slight performance improvement by sliding + * multiple files down at once (because we could use larger reads + * when operating on batches of small files), but it's not that useful. + */ + count = mEntries.size(); + delCount = adjust = 0; + for (i = 0; i < count; i++) { + ZipEntry* pEntry = mEntries[i]; + long span; + + if (pEntry->getLFHOffset() != 0) { + long nextOffset; + + /* Get the length of this entry by finding the offset + * of the next entry. Directory entries don't have + * file offsets, so we need to find the next non-directory + * entry. + */ + nextOffset = 0; + for (int ii = i+1; nextOffset == 0 && ii < count; ii++) + nextOffset = mEntries[ii]->getLFHOffset(); + if (nextOffset == 0) + nextOffset = mEOCD.mCentralDirOffset; + span = nextOffset - pEntry->getLFHOffset(); + + assert(span >= ZipEntry::LocalFileHeader::kLFHLen); + } else { + /* This is a directory entry. It doesn't have + * any actual file contents, so there's no need to + * move anything. + */ + span = 0; + } + + //printf("+++ %d: off=%ld span=%ld del=%d [count=%d]\n", + // i, pEntry->getLFHOffset(), span, pEntry->getDeleted(), count); + + if (pEntry->getDeleted()) { + adjust += span; + delCount++; + + delete pEntry; + mEntries.removeAt(i); + + /* adjust loop control */ + count--; + i--; + } else if (span != 0 && adjust > 0) { + /* shuffle this entry back */ + //printf("+++ Shuffling '%s' back %ld\n", + // pEntry->getFileName(), adjust); + result = filemove(mZipFp, pEntry->getLFHOffset() - adjust, + pEntry->getLFHOffset(), span); + if (result != NO_ERROR) { + /* this is why you use a temp file */ + LOGE("error during crunch - archive is toast\n"); + return result; + } + + pEntry->setLFHOffset(pEntry->getLFHOffset() - adjust); + } + } + + /* + * Fix EOCD info. We have to wait until the end to do some of this + * because we use mCentralDirOffset to determine "span" for the + * last entry. + */ + mEOCD.mCentralDirOffset -= adjust; + mEOCD.mNumEntries -= delCount; + mEOCD.mTotalNumEntries -= delCount; + mEOCD.mCentralDirSize = 0; // mark invalid; set by flush() + + assert(mEOCD.mNumEntries == mEOCD.mTotalNumEntries); + assert(mEOCD.mNumEntries == count); + + return result; +} + +/* + * Works like memmove(), but on pieces of a file. + */ +status_t ZipFile::filemove(FILE* fp, off_t dst, off_t src, size_t n) +{ + if (dst == src || n <= 0) + return NO_ERROR; + + unsigned char readBuf[32768]; + + if (dst < src) { + /* shift stuff toward start of file; must read from start */ + while (n != 0) { + size_t getSize = sizeof(readBuf); + if (getSize > n) + getSize = n; + + if (fseek(fp, (long) src, SEEK_SET) != 0) { + LOGD("filemove src seek %ld failed\n", (long) src); + return UNKNOWN_ERROR; + } + + if (fread(readBuf, 1, getSize, fp) != getSize) { + LOGD("filemove read %ld off=%ld failed\n", + (long) getSize, (long) src); + return UNKNOWN_ERROR; + } + + if (fseek(fp, (long) dst, SEEK_SET) != 0) { + LOGD("filemove dst seek %ld failed\n", (long) dst); + return UNKNOWN_ERROR; + } + + if (fwrite(readBuf, 1, getSize, fp) != getSize) { + LOGD("filemove write %ld off=%ld failed\n", + (long) getSize, (long) dst); + return UNKNOWN_ERROR; + } + + src += getSize; + dst += getSize; + n -= getSize; + } + } else { + /* shift stuff toward end of file; must read from end */ + assert(false); // write this someday, maybe + return UNKNOWN_ERROR; + } + + return NO_ERROR; +} + + +/* + * Get the modification time from a file descriptor. + */ +time_t ZipFile::getModTime(int fd) +{ + struct stat sb; + + if (fstat(fd, &sb) < 0) { + LOGD("HEY: fstat on fd %d failed\n", fd); + return (time_t) -1; + } + + return sb.st_mtime; +} + + +#if 0 /* this is a bad idea */ +/* + * Get a copy of the Zip file descriptor. + * + * We don't allow this if the file was opened read-write because we tend + * to leave the file contents in an uncertain state between calls to + * flush(). The duplicated file descriptor should only be valid for reads. + */ +int ZipFile::getZipFd(void) const +{ + if (!mReadOnly) + return INVALID_OPERATION; + assert(mZipFp != NULL); + + int fd; + fd = dup(fileno(mZipFp)); + if (fd < 0) { + LOGD("didn't work, errno=%d\n", errno); + } + + return fd; +} +#endif + + +#if 0 +/* + * Expand data. + */ +bool ZipFile::uncompress(const ZipEntry* pEntry, void* buf) const +{ + return false; +} +#endif + +// free the memory when you're done +void* ZipFile::uncompress(const ZipEntry* entry) +{ + size_t unlen = entry->getUncompressedLen(); + size_t clen = entry->getCompressedLen(); + + void* buf = malloc(unlen); + if (buf == NULL) { + return NULL; + } + + fseek(mZipFp, 0, SEEK_SET); + + off_t offset = entry->getFileOffset(); + if (fseek(mZipFp, offset, SEEK_SET) != 0) { + goto bail; + } + + switch (entry->getCompressionMethod()) + { + case ZipEntry::kCompressStored: { + ssize_t amt = fread(buf, 1, unlen, mZipFp); + if (amt != (ssize_t)unlen) { + goto bail; + } +#if 0 + printf("data...\n"); + const unsigned char* p = (unsigned char*)buf; + const unsigned char* end = p+unlen; + for (int i=0; i<32 && p < end; i++) { + printf("0x%08x ", (int)(offset+(i*0x10))); + for (int j=0; j<0x10 && p < end; j++) { + printf(" %02x", *p); + p++; + } + printf("\n"); + } +#endif + + } + break; + case ZipEntry::kCompressDeflated: { + if (!ZipUtils::inflateToBuffer(mZipFp, buf, unlen, clen)) { + goto bail; + } + } + break; + default: + goto bail; + } + return buf; + +bail: + free(buf); + return NULL; +} + + +/* + * =========================================================================== + * ZipFile::EndOfCentralDir + * =========================================================================== + */ + +/* + * Read the end-of-central-dir fields. + * + * "buf" should be positioned at the EOCD signature, and should contain + * the entire EOCD area including the comment. + */ +status_t ZipFile::EndOfCentralDir::readBuf(const unsigned char* buf, int len) +{ + /* don't allow re-use */ + assert(mComment == NULL); + + if (len < kEOCDLen) { + /* looks like ZIP file got truncated */ + LOGD(" Zip EOCD: expected >= %d bytes, found %d\n", + kEOCDLen, len); + return INVALID_OPERATION; + } + + /* this should probably be an assert() */ + if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) + return UNKNOWN_ERROR; + + mDiskNumber = ZipEntry::getShortLE(&buf[0x04]); + mDiskWithCentralDir = ZipEntry::getShortLE(&buf[0x06]); + mNumEntries = ZipEntry::getShortLE(&buf[0x08]); + mTotalNumEntries = ZipEntry::getShortLE(&buf[0x0a]); + mCentralDirSize = ZipEntry::getLongLE(&buf[0x0c]); + mCentralDirOffset = ZipEntry::getLongLE(&buf[0x10]); + mCommentLen = ZipEntry::getShortLE(&buf[0x14]); + + // TODO: validate mCentralDirOffset + + if (mCommentLen > 0) { + if (kEOCDLen + mCommentLen > len) { + LOGD("EOCD(%d) + comment(%d) exceeds len (%d)\n", + kEOCDLen, mCommentLen, len); + return UNKNOWN_ERROR; + } + mComment = new unsigned char[mCommentLen]; + memcpy(mComment, buf + kEOCDLen, mCommentLen); + } + + return NO_ERROR; +} + +/* + * Write an end-of-central-directory section. + */ +status_t ZipFile::EndOfCentralDir::write(FILE* fp) +{ + unsigned char buf[kEOCDLen]; + + ZipEntry::putLongLE(&buf[0x00], kSignature); + ZipEntry::putShortLE(&buf[0x04], mDiskNumber); + ZipEntry::putShortLE(&buf[0x06], mDiskWithCentralDir); + ZipEntry::putShortLE(&buf[0x08], mNumEntries); + ZipEntry::putShortLE(&buf[0x0a], mTotalNumEntries); + ZipEntry::putLongLE(&buf[0x0c], mCentralDirSize); + ZipEntry::putLongLE(&buf[0x10], mCentralDirOffset); + ZipEntry::putShortLE(&buf[0x14], mCommentLen); + + if (fwrite(buf, 1, kEOCDLen, fp) != kEOCDLen) + return UNKNOWN_ERROR; + if (mCommentLen > 0) { + assert(mComment != NULL); + if (fwrite(mComment, mCommentLen, 1, fp) != mCommentLen) + return UNKNOWN_ERROR; + } + + return NO_ERROR; +} + +/* + * Dump the contents of an EndOfCentralDir object. + */ +void ZipFile::EndOfCentralDir::dump(void) const +{ + LOGD(" EndOfCentralDir contents:\n"); + LOGD(" diskNum=%u diskWCD=%u numEnt=%u totalNumEnt=%u\n", + mDiskNumber, mDiskWithCentralDir, mNumEntries, mTotalNumEntries); + LOGD(" centDirSize=%lu centDirOff=%lu commentLen=%u\n", + mCentralDirSize, mCentralDirOffset, mCommentLen); +} + diff --git a/libs/utils/ZipFileCRO.cpp b/libs/utils/ZipFileCRO.cpp new file mode 100644 index 000000000..d312dafad --- /dev/null +++ b/libs/utils/ZipFileCRO.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "utils/ZipFileCRO.h" +#include "utils/ZipFileRO.h" + +using namespace android; + +ZipFileCRO ZipFileXRO_open(const char* path) { + ZipFileRO* zip = new ZipFileRO(); + if (zip->open(path) == NO_ERROR) { + return (ZipFileCRO)zip; + } + return NULL; +} + +void ZipFileCRO_destroy(ZipFileCRO zipToken) { + ZipFileRO* zip = (ZipFileRO*)zipToken; + delete zip; +} + +ZipEntryCRO ZipFileCRO_findEntryByName(ZipFileCRO zipToken, + const char* fileName) { + ZipFileRO* zip = (ZipFileRO*)zipToken; + return (ZipEntryCRO)zip->findEntryByName(fileName); +} + +bool ZipFileCRO_getEntryInfo(ZipFileCRO zipToken, ZipEntryRO entryToken, + int* pMethod, long* pUncompLen, + long* pCompLen, off_t* pOffset, long* pModWhen, long* pCrc32) { + ZipFileRO* zip = (ZipFileRO*)zipToken; + ZipEntryRO entry = (ZipEntryRO)entryToken; + return zip->getEntryInfo(entry, pMethod, pUncompLen, pCompLen, pOffset, + pModWhen, pCrc32); +} + +bool ZipFileCRO_uncompressEntry(ZipFileCRO zipToken, ZipEntryRO entryToken, int fd) { + ZipFileRO* zip = (ZipFileRO*)zipToken; + ZipEntryRO entry = (ZipEntryRO)entryToken; + return zip->uncompressEntry(entry, fd); +} diff --git a/libs/utils/ZipFileRO.cpp b/libs/utils/ZipFileRO.cpp new file mode 100644 index 000000000..ae8c71972 --- /dev/null +++ b/libs/utils/ZipFileRO.cpp @@ -0,0 +1,724 @@ +/* + * 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. + */ + +// +// Read-only access to Zip archives, with minimal heap allocation. +// +#define LOG_TAG "zipro" +//#define LOG_NDEBUG 0 +#include "utils/ZipFileRO.h" +#include "utils/Log.h" +#include "utils/misc.h" + +#include + +#include +#include +#include +#include + +using namespace android; + +/* + * Zip file constants. + */ +#define kEOCDSignature 0x06054b50 +#define kEOCDLen 22 +#define kEOCDNumEntries 8 // offset to #of entries in file +#define kEOCDFileOffset 16 // offset to central directory + +#define kMaxCommentLen 65535 // longest possible in ushort +#define kMaxEOCDSearch (kMaxCommentLen + kEOCDLen) + +#define kLFHSignature 0x04034b50 +#define kLFHLen 30 // excluding variable-len fields +#define kLFHNameLen 26 // offset to filename length +#define kLFHExtraLen 28 // offset to extra length + +#define kCDESignature 0x02014b50 +#define kCDELen 46 // excluding variable-len fields +#define kCDEMethod 10 // offset to compression method +#define kCDEModWhen 12 // offset to modification timestamp +#define kCDECRC 16 // offset to entry CRC +#define kCDECompLen 20 // offset to compressed length +#define kCDEUncompLen 24 // offset to uncompressed length +#define kCDENameLen 28 // offset to filename length +#define kCDEExtraLen 30 // offset to extra length +#define kCDECommentLen 32 // offset to comment length +#define kCDELocalOffset 42 // offset to local hdr + +/* + * The values we return for ZipEntryRO use 0 as an invalid value, so we + * want to adjust the hash table index by a fixed amount. Using a large + * value helps insure that people don't mix & match arguments, e.g. to + * findEntryByIndex(). + */ +#define kZipEntryAdj 10000 + +/* + * Convert a ZipEntryRO to a hash table index, verifying that it's in a + * valid range. + */ +int ZipFileRO::entryToIndex(const ZipEntryRO entry) const +{ + long ent = ((long) entry) - kZipEntryAdj; + if (ent < 0 || ent >= mHashTableSize || mHashTable[ent].name == NULL) { + LOGW("Invalid ZipEntryRO %p (%ld)\n", entry, ent); + return -1; + } + return ent; +} + + +/* + * Open the specified file read-only. We memory-map the entire thing and + * close the file before returning. + */ +status_t ZipFileRO::open(const char* zipFileName) +{ + int fd = -1; + off_t length; + + assert(mFileMap == NULL); + + /* + * Open and map the specified file. + */ + fd = ::open(zipFileName, O_RDONLY); + if (fd < 0) { + LOGW("Unable to open zip '%s': %s\n", zipFileName, strerror(errno)); + return NAME_NOT_FOUND; + } + + length = lseek(fd, 0, SEEK_END); + if (length < 0) { + close(fd); + return UNKNOWN_ERROR; + } + + mFileMap = new FileMap(); + if (mFileMap == NULL) { + close(fd); + return NO_MEMORY; + } + if (!mFileMap->create(zipFileName, fd, 0, length, true)) { + LOGW("Unable to map '%s': %s\n", zipFileName, strerror(errno)); + close(fd); + return UNKNOWN_ERROR; + } + + mFd = fd; + + /* + * Got it mapped, verify it and create data structures for fast access. + */ + if (!parseZipArchive()) { + mFileMap->release(); + mFileMap = NULL; + return UNKNOWN_ERROR; + } + + return OK; +} + +/* + * Parse the Zip archive, verifying its contents and initializing internal + * data structures. + */ +bool ZipFileRO::parseZipArchive(void) +{ +#define CHECK_OFFSET(_off) { \ + if ((unsigned int) (_off) >= maxOffset) { \ + LOGE("ERROR: bad offset %u (max %d): %s\n", \ + (unsigned int) (_off), maxOffset, #_off); \ + goto bail; \ + } \ + } + const unsigned char* basePtr = (const unsigned char*)mFileMap->getDataPtr(); + const unsigned char* ptr; + size_t length = mFileMap->getDataLength(); + bool result = false; + unsigned int i, numEntries, cdOffset; + unsigned int val; + + /* + * The first 4 bytes of the file will either be the local header + * signature for the first file (kLFHSignature) or, if the archive doesn't + * have any files in it, the end-of-central-directory signature + * (kEOCDSignature). + */ + val = get4LE(basePtr); + if (val == kEOCDSignature) { + LOGI("Found Zip archive, but it looks empty\n"); + goto bail; + } else if (val != kLFHSignature) { + LOGV("Not a Zip archive (found 0x%08x)\n", val); + goto bail; + } + + /* + * Find the EOCD. We'll find it immediately unless they have a file + * comment. + */ + ptr = basePtr + length - kEOCDLen; + + while (ptr >= basePtr) { + if (*ptr == (kEOCDSignature & 0xff) && get4LE(ptr) == kEOCDSignature) + break; + ptr--; + } + if (ptr < basePtr) { + LOGI("Could not find end-of-central-directory in Zip\n"); + goto bail; + } + + /* + * There are two interesting items in the EOCD block: the number of + * entries in the file, and the file offset of the start of the + * central directory. + * + * (There's actually a count of the #of entries in this file, and for + * all files which comprise a spanned archive, but for our purposes + * we're only interested in the current file. Besides, we expect the + * two to be equivalent for our stuff.) + */ + numEntries = get2LE(ptr + kEOCDNumEntries); + cdOffset = get4LE(ptr + kEOCDFileOffset); + + /* valid offsets are [0,EOCD] */ + unsigned int maxOffset; + maxOffset = (ptr - basePtr) +1; + + LOGV("+++ numEntries=%d cdOffset=%d\n", numEntries, cdOffset); + if (numEntries == 0 || cdOffset >= length) { + LOGW("Invalid entries=%d offset=%d (len=%zd)\n", + numEntries, cdOffset, length); + goto bail; + } + + /* + * Create hash table. We have a minimum 75% load factor, possibly as + * low as 50% after we round off to a power of 2. + */ + mNumEntries = numEntries; + mHashTableSize = roundUpPower2(1 + ((numEntries * 4) / 3)); + mHashTable = (HashEntry*) calloc(1, sizeof(HashEntry) * mHashTableSize); + + /* + * Walk through the central directory, adding entries to the hash + * table. + */ + ptr = basePtr + cdOffset; + for (i = 0; i < numEntries; i++) { + unsigned int fileNameLen, extraLen, commentLen, localHdrOffset; + const unsigned char* localHdr; + unsigned int hash; + + if (get4LE(ptr) != kCDESignature) { + LOGW("Missed a central dir sig (at %d)\n", i); + goto bail; + } + if (ptr + kCDELen > basePtr + length) { + LOGW("Ran off the end (at %d)\n", i); + goto bail; + } + + localHdrOffset = get4LE(ptr + kCDELocalOffset); + CHECK_OFFSET(localHdrOffset); + fileNameLen = get2LE(ptr + kCDENameLen); + extraLen = get2LE(ptr + kCDEExtraLen); + commentLen = get2LE(ptr + kCDECommentLen); + + //LOGV("+++ %d: localHdr=%d fnl=%d el=%d cl=%d\n", + // i, localHdrOffset, fileNameLen, extraLen, commentLen); + //LOGV(" '%.*s'\n", fileNameLen, ptr + kCDELen); + + /* add the CDE filename to the hash table */ + hash = computeHash((const char*)ptr + kCDELen, fileNameLen); + addToHash((const char*)ptr + kCDELen, fileNameLen, hash); + + localHdr = basePtr + localHdrOffset; + if (get4LE(localHdr) != kLFHSignature) { + LOGW("Bad offset to local header: %d (at %d)\n", + localHdrOffset, i); + goto bail; + } + + ptr += kCDELen + fileNameLen + extraLen + commentLen; + CHECK_OFFSET(ptr - basePtr); + } + + result = true; + +bail: + return result; +#undef CHECK_OFFSET +} + + +/* + * Simple string hash function for non-null-terminated strings. + */ +/*static*/ unsigned int ZipFileRO::computeHash(const char* str, int len) +{ + unsigned int hash = 0; + + while (len--) + hash = hash * 31 + *str++; + + return hash; +} + +/* + * Add a new entry to the hash table. + */ +void ZipFileRO::addToHash(const char* str, int strLen, unsigned int hash) +{ + int ent = hash & (mHashTableSize-1); + + /* + * We over-allocate the table, so we're guaranteed to find an empty slot. + */ + while (mHashTable[ent].name != NULL) + ent = (ent + 1) & (mHashTableSize-1); + + mHashTable[ent].name = str; + mHashTable[ent].nameLen = strLen; +} + +/* + * Find a matching entry. + * + * Returns 0 if not found. + */ +ZipEntryRO ZipFileRO::findEntryByName(const char* fileName) const +{ + int nameLen = strlen(fileName); + unsigned int hash = computeHash(fileName, nameLen); + int ent = hash & (mHashTableSize-1); + + while (mHashTable[ent].name != NULL) { + if (mHashTable[ent].nameLen == nameLen && + memcmp(mHashTable[ent].name, fileName, nameLen) == 0) + { + /* match */ + return (ZipEntryRO) (ent + kZipEntryAdj); + } + + ent = (ent + 1) & (mHashTableSize-1); + } + + return NULL; +} + +/* + * Find the Nth entry. + * + * This currently involves walking through the sparse hash table, counting + * non-empty entries. If we need to speed this up we can either allocate + * a parallel lookup table or (perhaps better) provide an iterator interface. + */ +ZipEntryRO ZipFileRO::findEntryByIndex(int idx) const +{ + if (idx < 0 || idx >= mNumEntries) { + LOGW("Invalid index %d\n", idx); + return NULL; + } + + for (int ent = 0; ent < mHashTableSize; ent++) { + if (mHashTable[ent].name != NULL) { + if (idx-- == 0) + return (ZipEntryRO) (ent + kZipEntryAdj); + } + } + + return NULL; +} + +/* + * Get the useful fields from the zip entry. + * + * Returns "false" if the offsets to the fields or the contents of the fields + * appear to be bogus. + */ +bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, long* pUncompLen, + long* pCompLen, off_t* pOffset, long* pModWhen, long* pCrc32) const +{ + int ent = entryToIndex(entry); + if (ent < 0) + return false; + + /* + * Recover the start of the central directory entry from the filename + * pointer. + */ + const unsigned char* basePtr = (const unsigned char*)mFileMap->getDataPtr(); + const unsigned char* ptr = (const unsigned char*) mHashTable[ent].name; + size_t zipLength = mFileMap->getDataLength(); + + ptr -= kCDELen; + + int method = get2LE(ptr + kCDEMethod); + if (pMethod != NULL) + *pMethod = method; + + if (pModWhen != NULL) + *pModWhen = get4LE(ptr + kCDEModWhen); + if (pCrc32 != NULL) + *pCrc32 = get4LE(ptr + kCDECRC); + + /* + * We need to make sure that the lengths are not so large that somebody + * trying to map the compressed or uncompressed data runs off the end + * of the mapped region. + */ + unsigned long localHdrOffset = get4LE(ptr + kCDELocalOffset); + if (localHdrOffset + kLFHLen >= zipLength) { + LOGE("ERROR: bad local hdr offset in zip\n"); + return false; + } + const unsigned char* localHdr = basePtr + localHdrOffset; + off_t dataOffset = localHdrOffset + kLFHLen + + get2LE(localHdr + kLFHNameLen) + get2LE(localHdr + kLFHExtraLen); + if ((unsigned long) dataOffset >= zipLength) { + LOGE("ERROR: bad data offset in zip\n"); + return false; + } + + if (pCompLen != NULL) { + *pCompLen = get4LE(ptr + kCDECompLen); + if (*pCompLen < 0 || (size_t)(dataOffset + *pCompLen) >= zipLength) { + LOGE("ERROR: bad compressed length in zip\n"); + return false; + } + } + if (pUncompLen != NULL) { + *pUncompLen = get4LE(ptr + kCDEUncompLen); + if (*pUncompLen < 0) { + LOGE("ERROR: negative uncompressed length in zip\n"); + return false; + } + if (method == kCompressStored && + (size_t)(dataOffset + *pUncompLen) >= zipLength) + { + LOGE("ERROR: bad uncompressed length in zip\n"); + return false; + } + } + + if (pOffset != NULL) { + *pOffset = dataOffset; + } + return true; +} + +/* + * Copy the entry's filename to the buffer. + */ +int ZipFileRO::getEntryFileName(ZipEntryRO entry, char* buffer, int bufLen) + const +{ + int ent = entryToIndex(entry); + if (ent < 0) + return -1; + + int nameLen = mHashTable[ent].nameLen; + if (bufLen < nameLen+1) + return nameLen+1; + + memcpy(buffer, mHashTable[ent].name, nameLen); + buffer[nameLen] = '\0'; + return 0; +} + +/* + * Create a new FileMap object that spans the data in "entry". + */ +FileMap* ZipFileRO::createEntryFileMap(ZipEntryRO entry) const +{ + /* + * TODO: the efficient way to do this is to modify FileMap to allow + * sub-regions of a file to be mapped. A reference-counting scheme + * can manage the base memory mapping. For now, we just create a brand + * new mapping off of the Zip archive file descriptor. + */ + + FileMap* newMap; + long compLen; + off_t offset; + + if (!getEntryInfo(entry, NULL, NULL, &compLen, &offset, NULL, NULL)) + return NULL; + + newMap = new FileMap(); + if (!newMap->create(mFileMap->getFileName(), mFd, offset, compLen, true)) { + newMap->release(); + return NULL; + } + + return newMap; +} + +/* + * Uncompress an entry, in its entirety, into the provided output buffer. + * + * This doesn't verify the data's CRC, which might be useful for + * uncompressed data. The caller should be able to manage it. + */ +bool ZipFileRO::uncompressEntry(ZipEntryRO entry, void* buffer) const +{ + const int kSequentialMin = 32768; + bool result = false; + int ent = entryToIndex(entry); + if (ent < 0) + return -1; + + const unsigned char* basePtr = (const unsigned char*)mFileMap->getDataPtr(); + int method; + long uncompLen, compLen; + off_t offset; + + getEntryInfo(entry, &method, &uncompLen, &compLen, &offset, NULL, NULL); + + /* + * Experiment with madvise hint. When we want to uncompress a file, + * we pull some stuff out of the central dir entry and then hit a + * bunch of compressed or uncompressed data sequentially. The CDE + * visit will cause a limited amount of read-ahead because it's at + * the end of the file. We could end up doing lots of extra disk + * access if the file we're prying open is small. Bottom line is we + * probably don't want to turn MADV_SEQUENTIAL on and leave it on. + * + * So, if the compressed size of the file is above a certain minimum + * size, temporarily boost the read-ahead in the hope that the extra + * pair of system calls are negated by a reduction in page faults. + */ + if (compLen > kSequentialMin) + mFileMap->advise(FileMap::SEQUENTIAL); + + if (method == kCompressStored) { + memcpy(buffer, basePtr + offset, uncompLen); + } else { + if (!inflateBuffer(buffer, basePtr + offset, uncompLen, compLen)) + goto bail; + } + + if (compLen > kSequentialMin) + mFileMap->advise(FileMap::NORMAL); + + result = true; + +bail: + return result; +} + +/* + * Uncompress an entry, in its entirety, to an open file descriptor. + * + * This doesn't verify the data's CRC, but probably should. + */ +bool ZipFileRO::uncompressEntry(ZipEntryRO entry, int fd) const +{ + bool result = false; + int ent = entryToIndex(entry); + if (ent < 0) + return -1; + + const unsigned char* basePtr = (const unsigned char*)mFileMap->getDataPtr(); + int method; + long uncompLen, compLen; + off_t offset; + + getEntryInfo(entry, &method, &uncompLen, &compLen, &offset, NULL, NULL); + + if (method == kCompressStored) { + ssize_t actual; + + actual = write(fd, basePtr + offset, uncompLen); + if (actual < 0) { + LOGE("Write failed: %s\n", strerror(errno)); + goto bail; + } else if (actual != uncompLen) { + LOGE("Partial write during uncompress (%d of %ld)\n", + (int)actual, uncompLen); + goto bail; + } else { + LOGI("+++ successful write\n"); + } + } else { + if (!inflateBuffer(fd, basePtr+offset, uncompLen, compLen)) + goto bail; + } + + result = true; + +bail: + return result; +} + +/* + * Uncompress "deflate" data from one buffer to another. + */ +/*static*/ bool ZipFileRO::inflateBuffer(void* outBuf, const void* inBuf, + long uncompLen, long compLen) +{ + bool result = false; + z_stream zstream; + int zerr; + + /* + * Initialize the zlib stream struct. + */ + memset(&zstream, 0, sizeof(zstream)); + zstream.zalloc = Z_NULL; + zstream.zfree = Z_NULL; + zstream.opaque = Z_NULL; + zstream.next_in = (Bytef*)inBuf; + zstream.avail_in = compLen; + zstream.next_out = (Bytef*) outBuf; + zstream.avail_out = uncompLen; + zstream.data_type = Z_UNKNOWN; + + /* + * Use the undocumented "negative window bits" feature to tell zlib + * that there's no zlib header waiting for it. + */ + zerr = inflateInit2(&zstream, -MAX_WBITS); + if (zerr != Z_OK) { + if (zerr == Z_VERSION_ERROR) { + LOGE("Installed zlib is not compatible with linked version (%s)\n", + ZLIB_VERSION); + } else { + LOGE("Call to inflateInit2 failed (zerr=%d)\n", zerr); + } + goto bail; + } + + /* + * Expand data. + */ + zerr = inflate(&zstream, Z_FINISH); + if (zerr != Z_STREAM_END) { + LOGW("Zip inflate failed, zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)\n", + zerr, zstream.next_in, zstream.avail_in, + zstream.next_out, zstream.avail_out); + goto z_bail; + } + + /* paranoia */ + if ((long) zstream.total_out != uncompLen) { + LOGW("Size mismatch on inflated file (%ld vs %ld)\n", + zstream.total_out, uncompLen); + goto z_bail; + } + + result = true; + +z_bail: + inflateEnd(&zstream); /* free up any allocated structures */ + +bail: + return result; +} + +/* + * Uncompress "deflate" data from one buffer to an open file descriptor. + */ +/*static*/ bool ZipFileRO::inflateBuffer(int fd, const void* inBuf, + long uncompLen, long compLen) +{ + bool result = false; + const int kWriteBufSize = 32768; + unsigned char writeBuf[kWriteBufSize]; + z_stream zstream; + int zerr; + + /* + * Initialize the zlib stream struct. + */ + memset(&zstream, 0, sizeof(zstream)); + zstream.zalloc = Z_NULL; + zstream.zfree = Z_NULL; + zstream.opaque = Z_NULL; + zstream.next_in = (Bytef*)inBuf; + zstream.avail_in = compLen; + zstream.next_out = (Bytef*) writeBuf; + zstream.avail_out = sizeof(writeBuf); + zstream.data_type = Z_UNKNOWN; + + /* + * Use the undocumented "negative window bits" feature to tell zlib + * that there's no zlib header waiting for it. + */ + zerr = inflateInit2(&zstream, -MAX_WBITS); + if (zerr != Z_OK) { + if (zerr == Z_VERSION_ERROR) { + LOGE("Installed zlib is not compatible with linked version (%s)\n", + ZLIB_VERSION); + } else { + LOGE("Call to inflateInit2 failed (zerr=%d)\n", zerr); + } + goto bail; + } + + /* + * Loop while we have more to do. + */ + do { + /* + * Expand data. + */ + zerr = inflate(&zstream, Z_NO_FLUSH); + if (zerr != Z_OK && zerr != Z_STREAM_END) { + LOGW("zlib inflate: zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)\n", + zerr, zstream.next_in, zstream.avail_in, + zstream.next_out, zstream.avail_out); + goto z_bail; + } + + /* write when we're full or when we're done */ + if (zstream.avail_out == 0 || + (zerr == Z_STREAM_END && zstream.avail_out != sizeof(writeBuf))) + { + long writeSize = zstream.next_out - writeBuf; + int cc = write(fd, writeBuf, writeSize); + if (cc != (int) writeSize) { + LOGW("write failed in inflate (%d vs %ld)\n", cc, writeSize); + goto z_bail; + } + + zstream.next_out = writeBuf; + zstream.avail_out = sizeof(writeBuf); + } + } while (zerr == Z_OK); + + assert(zerr == Z_STREAM_END); /* other errors should've been caught */ + + /* paranoia */ + if ((long) zstream.total_out != uncompLen) { + LOGW("Size mismatch on inflated file (%ld vs %ld)\n", + zstream.total_out, uncompLen); + goto z_bail; + } + + result = true; + +z_bail: + inflateEnd(&zstream); /* free up any allocated structures */ + +bail: + return result; +} diff --git a/libs/utils/ZipUtils.cpp b/libs/utils/ZipUtils.cpp new file mode 100644 index 000000000..bfbacfecd --- /dev/null +++ b/libs/utils/ZipUtils.cpp @@ -0,0 +1,344 @@ +/* + * 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. + */ + +// +// Misc zip/gzip utility functions. +// + +#define LOG_TAG "ziputil" + +#include "utils/ZipUtils.h" +#include "utils/ZipFileRO.h" +#include "utils/Log.h" + +#include +#include +#include + +#include + +using namespace android; + +/* + * Utility function that expands zip/gzip "deflate" compressed data + * into a buffer. + * + * "fd" is an open file positioned at the start of the "deflate" data + * "buf" must hold at least "uncompressedLen" bytes. + */ +/*static*/ bool ZipUtils::inflateToBuffer(int fd, void* buf, + long uncompressedLen, long compressedLen) +{ + bool result = false; + const unsigned long kReadBufSize = 32768; + unsigned char* readBuf = NULL; + z_stream zstream; + int zerr; + unsigned long compRemaining; + + assert(uncompressedLen >= 0); + assert(compressedLen >= 0); + + readBuf = new unsigned char[kReadBufSize]; + if (readBuf == NULL) + goto bail; + compRemaining = compressedLen; + + /* + * Initialize the zlib stream. + */ + memset(&zstream, 0, sizeof(zstream)); + zstream.zalloc = Z_NULL; + zstream.zfree = Z_NULL; + zstream.opaque = Z_NULL; + zstream.next_in = NULL; + zstream.avail_in = 0; + zstream.next_out = (Bytef*) buf; + zstream.avail_out = uncompressedLen; + zstream.data_type = Z_UNKNOWN; + + /* + * Use the undocumented "negative window bits" feature to tell zlib + * that there's no zlib header waiting for it. + */ + zerr = inflateInit2(&zstream, -MAX_WBITS); + if (zerr != Z_OK) { + if (zerr == Z_VERSION_ERROR) { + LOGE("Installed zlib is not compatible with linked version (%s)\n", + ZLIB_VERSION); + } else { + LOGE("Call to inflateInit2 failed (zerr=%d)\n", zerr); + } + goto bail; + } + + /* + * Loop while we have data. + */ + do { + unsigned long getSize; + + /* read as much as we can */ + if (zstream.avail_in == 0) { + getSize = (compRemaining > kReadBufSize) ? + kReadBufSize : compRemaining; + LOGV("+++ reading %ld bytes (%ld left)\n", + getSize, compRemaining); + + int cc = read(fd, readBuf, getSize); + if (cc != (int) getSize) { + LOGD("inflate read failed (%d vs %ld)\n", + cc, getSize); + goto z_bail; + } + + compRemaining -= getSize; + + zstream.next_in = readBuf; + zstream.avail_in = getSize; + } + + /* uncompress the data */ + zerr = inflate(&zstream, Z_NO_FLUSH); + if (zerr != Z_OK && zerr != Z_STREAM_END) { + LOGD("zlib inflate call failed (zerr=%d)\n", zerr); + goto z_bail; + } + + /* output buffer holds all, so no need to write the output */ + } while (zerr == Z_OK); + + assert(zerr == Z_STREAM_END); /* other errors should've been caught */ + + if ((long) zstream.total_out != uncompressedLen) { + LOGW("Size mismatch on inflated file (%ld vs %ld)\n", + zstream.total_out, uncompressedLen); + goto z_bail; + } + + // success! + result = true; + +z_bail: + inflateEnd(&zstream); /* free up any allocated structures */ + +bail: + delete[] readBuf; + return result; +} + +/* + * Utility function that expands zip/gzip "deflate" compressed data + * into a buffer. + * + * (This is a clone of the previous function, but it takes a FILE* instead + * of an fd. We could pass fileno(fd) to the above, but we can run into + * trouble when "fp" has a different notion of what fd's file position is.) + * + * "fp" is an open file positioned at the start of the "deflate" data + * "buf" must hold at least "uncompressedLen" bytes. + */ +/*static*/ bool ZipUtils::inflateToBuffer(FILE* fp, void* buf, + long uncompressedLen, long compressedLen) +{ + bool result = false; + const unsigned long kReadBufSize = 32768; + unsigned char* readBuf = NULL; + z_stream zstream; + int zerr; + unsigned long compRemaining; + + assert(uncompressedLen >= 0); + assert(compressedLen >= 0); + + readBuf = new unsigned char[kReadBufSize]; + if (readBuf == NULL) + goto bail; + compRemaining = compressedLen; + + /* + * Initialize the zlib stream. + */ + memset(&zstream, 0, sizeof(zstream)); + zstream.zalloc = Z_NULL; + zstream.zfree = Z_NULL; + zstream.opaque = Z_NULL; + zstream.next_in = NULL; + zstream.avail_in = 0; + zstream.next_out = (Bytef*) buf; + zstream.avail_out = uncompressedLen; + zstream.data_type = Z_UNKNOWN; + + /* + * Use the undocumented "negative window bits" feature to tell zlib + * that there's no zlib header waiting for it. + */ + zerr = inflateInit2(&zstream, -MAX_WBITS); + if (zerr != Z_OK) { + if (zerr == Z_VERSION_ERROR) { + LOGE("Installed zlib is not compatible with linked version (%s)\n", + ZLIB_VERSION); + } else { + LOGE("Call to inflateInit2 failed (zerr=%d)\n", zerr); + } + goto bail; + } + + /* + * Loop while we have data. + */ + do { + unsigned long getSize; + + /* read as much as we can */ + if (zstream.avail_in == 0) { + getSize = (compRemaining > kReadBufSize) ? + kReadBufSize : compRemaining; + LOGV("+++ reading %ld bytes (%ld left)\n", + getSize, compRemaining); + + int cc = fread(readBuf, getSize, 1, fp); + if (cc != (int) getSize) { + LOGD("inflate read failed (%d vs %ld)\n", + cc, getSize); + goto z_bail; + } + + compRemaining -= getSize; + + zstream.next_in = readBuf; + zstream.avail_in = getSize; + } + + /* uncompress the data */ + zerr = inflate(&zstream, Z_NO_FLUSH); + if (zerr != Z_OK && zerr != Z_STREAM_END) { + LOGD("zlib inflate call failed (zerr=%d)\n", zerr); + goto z_bail; + } + + /* output buffer holds all, so no need to write the output */ + } while (zerr == Z_OK); + + assert(zerr == Z_STREAM_END); /* other errors should've been caught */ + + if ((long) zstream.total_out != uncompressedLen) { + LOGW("Size mismatch on inflated file (%ld vs %ld)\n", + zstream.total_out, uncompressedLen); + goto z_bail; + } + + // success! + result = true; + +z_bail: + inflateEnd(&zstream); /* free up any allocated structures */ + +bail: + delete[] readBuf; + return result; +} + +/* + * Look at the contents of a gzip archive. We want to know where the + * data starts, and how long it will be after it is uncompressed. + * + * We expect to find the CRC and length as the last 8 bytes on the file. + * This is a pretty reasonable thing to expect for locally-compressed + * files, but there's a small chance that some extra padding got thrown + * on (the man page talks about compressed data written to tape). We + * don't currently deal with that here. If "gzip -l" whines, we're going + * to fail too. + * + * On exit, "fp" is pointing at the start of the compressed data. + */ +/*static*/ bool ZipUtils::examineGzip(FILE* fp, int* pCompressionMethod, + long* pUncompressedLen, long* pCompressedLen, unsigned long* pCRC32) +{ + enum { // flags + FTEXT = 0x01, + FHCRC = 0x02, + FEXTRA = 0x04, + FNAME = 0x08, + FCOMMENT = 0x10, + }; + int ic; + int method, flags; + int i; + + ic = getc(fp); + if (ic != 0x1f || getc(fp) != 0x8b) + return false; // not gzip + method = getc(fp); + flags = getc(fp); + + /* quick sanity checks */ + if (method == EOF || flags == EOF) + return false; + if (method != ZipFileRO::kCompressDeflated) + return false; + + /* skip over 4 bytes of mod time, 1 byte XFL, 1 byte OS */ + for (i = 0; i < 6; i++) + (void) getc(fp); + /* consume "extra" field, if present */ + if ((flags & FEXTRA) != 0) { + int len; + + len = getc(fp); + len |= getc(fp) << 8; + while (len-- && getc(fp) != EOF) + ; + } + /* consume filename, if present */ + if ((flags & FNAME) != 0) { + do { + ic = getc(fp); + } while (ic != 0 && ic != EOF); + } + /* consume comment, if present */ + if ((flags & FCOMMENT) != 0) { + do { + ic = getc(fp); + } while (ic != 0 && ic != EOF); + } + /* consume 16-bit header CRC, if present */ + if ((flags & FHCRC) != 0) { + (void) getc(fp); + (void) getc(fp); + } + + if (feof(fp) || ferror(fp)) + return false; + + /* seek to the end; CRC and length are in the last 8 bytes */ + long curPosn = ftell(fp); + unsigned char buf[8]; + fseek(fp, -8, SEEK_END); + *pCompressedLen = ftell(fp) - curPosn; + + if (fread(buf, 1, 8, fp) != 8) + return false; + /* seek back to start of compressed data */ + fseek(fp, curPosn, SEEK_SET); + + *pCompressionMethod = method; + *pCRC32 = ZipFileRO::get4LE(&buf[0]); + *pUncompressedLen = ZipFileRO::get4LE(&buf[4]); + + return true; +} + diff --git a/libs/utils/characterData.h b/libs/utils/characterData.h new file mode 100644 index 000000000..e931d995e --- /dev/null +++ b/libs/utils/characterData.h @@ -0,0 +1,730 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Automatically generated on 07-11-2006 by make-CharacterDataC +// DO NOT EDIT DIRECTLY +namespace CharacterData { + + // Structure containing an array of ranges + struct Range { + int length; + const uint32_t* array; + }; + + // For Latin1 characters just index into this array to get the index and decomposition + static const uint16_t LATIN1_DATA[] = { + 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, + 0x0001, 0x0002, 0x0003, 0x0002, 0x0004, 0x0003, 0x0001, 0x0001, + 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, + 0x0001, 0x0001, 0x0001, 0x0001, 0x0003, 0x0003, 0x0003, 0x0002, + 0x0005, 0x0006, 0x0006, 0x0007, 0x0008, 0x0007, 0x0006, 0x0006, + 0x0009, 0x000A, 0x0006, 0x000B, 0x000C, 0x000D, 0x000C, 0x000C, + 0x000E, 0x000F, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, + 0x0016, 0x0017, 0x000C, 0x0006, 0x0018, 0x0019, 0x001A, 0x0006, + 0x0006, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, 0x0020, 0x0021, + 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, + 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, 0x0030, 0x0031, + 0x0032, 0x0033, 0x0034, 0x0035, 0x0006, 0x0036, 0x0037, 0x0038, + 0x0037, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, + 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, + 0x0050, 0x0051, 0x0052, 0x0035, 0x0019, 0x0036, 0x0019, 0x0001, + 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0003, 0x0001, 0x0001, + 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, + 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, + 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, + 0x5853, 0x0006, 0x0008, 0x0008, 0x0008, 0x0008, 0x0054, 0x0054, + 0x1037, 0x0054, 0x7855, 0x0056, 0x0019, 0x0057, 0x0054, 0x1037, + 0x0058, 0x0059, 0x785A, 0x785B, 0x1037, 0x105C, 0x0054, 0x0006, + 0x1037, 0x785D, 0x7855, 0x005E, 0x305F, 0x305F, 0x305F, 0x0006, + 0x0860, 0x0860, 0x0860, 0x0860, 0x0860, 0x0860, 0x0060, 0x0860, + 0x0860, 0x0860, 0x0860, 0x0860, 0x0860, 0x0860, 0x0860, 0x0860, + 0x0060, 0x0860, 0x0860, 0x0860, 0x0860, 0x0860, 0x0860, 0x0019, + 0x0060, 0x0860, 0x0860, 0x0860, 0x0860, 0x0860, 0x0060, 0x0055, + 0x0861, 0x0861, 0x0861, 0x0861, 0x0861, 0x0861, 0x0061, 0x0861, + 0x0861, 0x0861, 0x0861, 0x0861, 0x0861, 0x0861, 0x0861, 0x0861, + 0x0061, 0x0861, 0x0861, 0x0861, 0x0861, 0x0861, 0x0861, 0x0019, + 0x0061, 0x0861, 0x0861, 0x0861, 0x0861, 0x0861, 0x0061, 0x0862 + }; + + // Each of these arrays is stripped into ranges. In order to build the arrays, each + // codepoint was bit-shifted so that even and odd characters were separated into different + // arrays. The identifier of each array is the top byte after bit-shifting. + // The numbers stored in the array are the bit-shifted codepoint, the decomposition, and an + // index into another array of all possible packed data values. The top 16 bits are the + // codepoint and the bottom 16 are the decomposition and index. The top 5 bits for the decomposition + // and the rest for the index. + static const uint32_t a0[] = { + 0x00800863, 0x00880063, 0x00890863, 0x00930063, 0x00940863, 0x00980864, 0x00991063, 0x009A0863, + 0x009C0055, 0x009D0865, 0x00A01065, 0x00A10065, 0x00A20865, 0x00A50063, 0x00A60863, 0x00A90063, + 0x00AA0863, 0x00B30063, 0x00B40863, 0x00BC0866, 0x00BD0865, 0x00C00055, 0x00C10063, 0x00C30067, + 0x00C40065, 0x00C50068, 0x00C60065, 0x00C70069, 0x00C8006A, 0x00C90065, 0x00CA006B, 0x00CB006C, + 0x00CC0063, 0x00CD006D, 0x00CE006C, 0x00CF006E, 0x00D00863, 0x00D10063, 0x00D3006F, 0x00D40065, + 0x00D50055, 0x00D60063, 0x00D7006F, 0x00D80865, 0x00D90070, 0x00DA0065, 0x00DC0063, 0x00DD0055, + 0x00DE0063, 0x00DF0055, 0x00E00071, 0x00E21072, 0x00E31073, 0x00E41074, 0x00E51072, 0x00E61073, + 0x00E70865, 0x00EF0863, 0x00F20063, 0x00F30863, 0x00F80855, 0x00F91074, 0x00FA0863, 0x00FB0075, + 0x00FC0863, 0x010E0063, 0x010F0863, 0x01100076, 0x01110063, 0x01130863, 0x011A0055, 0x011D0077, + 0x011E0065, 0x011F0077, 0x01200055, 0x01210078, 0x01280055, 0x012A0079, 0x012B007A, 0x012C0055, + 0x0130007A, 0x01310055, 0x0134007B, 0x01350055, 0x0139007C, 0x013A0055, 0x0140007D, 0x01410055, + 0x0144007D, 0x0145007E, 0x01460055, 0x0149007F, 0x014A0080, 0x014B0055, 0x01587881, 0x015D0082, + 0x015E0081, 0x01610037, 0x01630082, 0x01680081, 0x01690037, 0x016C1037, 0x016F0037, 0x01707881, + 0x01730037, 0x01770081, 0x01780037, 0x01800083, 0x01A00883, 0x01A10083, 0x01A20883, 0x01A30083, + 0x01B80078, 0x01BA0837, 0x01BB0078, 0x01BD1081, 0x01BE0078, 0x01BF0806, 0x01C00078, 0x01C21037, + 0x01C30884, 0x01C40885, 0x01C60886, 0x01C70887, 0x01C80855, 0x01C90060, 0x01D10078, 0x01D20060, + 0x01D50860, 0x01D60888, 0x01D70889, 0x01D80855, 0x01D90061, 0x01E1008A, 0x01E20061, 0x01E50861, + 0x01E6088B, 0x01E7088C, 0x01E8108D, 0x01E91077, 0x01EA0877, 0x01EB108E, 0x01EC0063, 0x01F8108F, + 0x01F91090, 0x01FA1091, 0x01FB0019, 0x01FC0065, 0x01FD0063, 0x01FE0055, 0x01FF0077, 0x02000892, + 0x02010092, 0x02060892, 0x02080060, 0x02180061, 0x02280893, 0x02290093, 0x022E0893, 0x02300063, + 0x023B0863, 0x023C0063, 0x02410094, 0x02420083, 0x02440095, 0x02450063, 0x02600077, 0x02610865, + 0x02620065, 0x02680863, 0x026A0063, 0x026B0863, 0x026C0063, 0x026D0863, 0x02700063, 0x02710863, + 0x02740063, 0x02750863, 0x027B0063, 0x027C0863, 0x027D0078, 0x02800063, 0x02880078, 0x02990096, + 0x02AC0078, 0x02AD0097, 0x02B00078, 0x02B10098, 0x02C40078, 0x02C50099, 0x02C60078, 0x02C90083, + 0x02DD0078, 0x02DE0083, 0x02DF009A, 0x02E10083, 0x02E3009A, 0x02E40078, 0x02E8009B, 0x02F60078, + 0x02F8009B, 0x02FA009A, 0x02FB0078, 0x0300009C, 0x03020078, 0x0306000C, 0x03070054, 0x03080083, + 0x030B0078, 0x030F009D, 0x03100078, 0x0311089E, 0x0314009E, 0x031E0078, 0x0320009F, 0x0321009E, + 0x03260083, 0x033000A0, 0x033100A1, 0x033200A2, 0x033300A3, 0x033400A4, 0x03350007, 0x033600A5, + 0x0337009E, 0x03380083, 0x0339009E, 0x033B109E, 0x033D009E, 0x0360089E, 0x0362009E, 0x036A009D, + 0x036B0083, 0x036F0095, 0x03700083, 0x0373009F, 0x03740083, 0x0377009E, 0x0378000E, 0x03790010, + 0x037A0012, 0x037B0014, 0x037C0016, 0x037D009E, 0x037F00A6, 0x0380009D, 0x03870078, 0x0388009E, + 0x03980083, 0x03A60078, 0x03A7009E, 0x03B70078, 0x03C0009E, 0x03D30083, 0x03D90078, 0x04810083, + 0x04820071, 0x049A0871, 0x049B0071, 0x049D0078, 0x049E0083, 0x049F00A7, 0x04A10083, 0x04A500A7, + 0x04A70078, 0x04A80071, 0x04A90083, 0x04AB0078, 0x04AC0871, 0x04B00071, 0x04B10083, 0x04B20097, + 0x04B300A8, 0x04B400A9, 0x04B500AA, 0x04B600AB, 0x04B700AC, 0x04B80097, 0x04B90078, 0x04C100A7, + 0x04C20078, 0x04C30071, 0x04C70078, 0x04C80071, 0x04C90078, 0x04CA0071, 0x04DA0078, 0x04DB0071, + 0x04DD0078, 0x04DE0083, 0x04DF00A7, 0x04E10083, 0x04E30078, 0x04E400A7, 0x04E50078, 0x04E608A7, + 0x04E70071, 0x04E80078, 0x04EE0871, 0x04EF0078, 0x04F00071, 0x04F10083, 0x04F20078, 0x04F300A8, + 0x04F400A9, 0x04F500AA, 0x04F600AB, 0x04F700AC, 0x04F80071, 0x04F90008, 0x04FA00AD, 0x04FB00AE, + 0x04FC00AF, 0x04FD0094, 0x04FE0078, 0x05010083, 0x05020078, 0x05030071, 0x05060078, 0x05080071, + 0x05090078, 0x050A0071, 0x051A0078, 0x051B0871, 0x051C0071, 0x051D0078, 0x051E0083, 0x051F00A7, + 0x05210083, 0x05220078, 0x05240083, 0x05250078, 0x05260083, 0x05270078, 0x052D0871, 0x052E0071, + 0x052F0871, 0x05300078, 0x053300A8, 0x053400A9, 0x053500AA, 0x053600AB, 0x053700AC, 0x05380083, + 0x05390071, 0x053B0078, 0x05410083, 0x05420078, 0x05430071, 0x05470078, 0x05480071, 0x05490078, + 0x054A0071, 0x055A0078, 0x055B0071, 0x055D0078, 0x055E0083, 0x055F00A7, 0x05610083, 0x05630078, + 0x05640083, 0x05650078, 0x056600A7, 0x05670078, 0x05680071, 0x05690078, 0x05700071, 0x05710083, + 0x05720078, 0x057300A8, 0x057400A9, 0x057500AA, 0x057600AB, 0x057700AC, 0x05780078, 0x058100A7, + 0x05820078, 0x05830071, 0x05870078, 0x05880071, 0x05890078, 0x058A0071, 0x059A0078, 0x059B0071, + 0x059D0078, 0x059E0083, 0x059F00A7, 0x05A10083, 0x05A20078, 0x05A408A7, 0x05A50078, 0x05A608A7, + 0x05A70078, 0x05AB0083, 0x05AC0078, 0x05AE0871, 0x05AF0078, 0x05B00071, 0x05B10078, 0x05B300A8, + 0x05B400A9, 0x05B500AA, 0x05B600AB, 0x05B700AC, 0x05B80094, 0x05B90078, 0x05C10083, 0x05C20078, + 0x05C30071, 0x05C60078, 0x05C70071, 0x05CA0871, 0x05CB0078, 0x05CD0071, 0x05D00078, 0x05D20071, + 0x05D30078, 0x05D40071, 0x05D60078, 0x05D70071, 0x05DD0078, 0x05DF00A7, 0x05E00083, 0x05E100A7, + 0x05E20078, 0x05E300A7, 0x05E508A7, 0x05E70078, 0x05F300A8, 0x05F400A9, 0x05F500AA, 0x05F600AB, + 0x05F700AC, 0x05F800B0, 0x05F900B1, 0x05FA0054, 0x05FE0078, 0x060100A7, 0x06020078, 0x06030071, + 0x061A0078, 0x061B0071, 0x061D0078, 0x061F0083, 0x062100A7, 0x06230083, 0x06240883, 0x06250083, + 0x06270078, 0x062B0083, 0x062C0078, 0x06300071, 0x06310078, 0x063300A8, 0x063400A9, 0x063500AA, + 0x063600AB, 0x063700AC, 0x06380078, 0x064100A7, 0x06420078, 0x06430071, 0x065A0078, 0x065B0071, + 0x065D0078, 0x065E0083, 0x065F00A7, 0x066008A7, 0x066100A7, 0x066300B2, 0x066408A7, 0x06660083, + 0x06670078, 0x066B00A7, 0x066C0078, 0x066F0071, 0x06710078, 0x067300A8, 0x067400A9, 0x067500AA, + 0x067600AB, 0x067700AC, 0x06780078, 0x068100A7, 0x06820078, 0x06830071, 0x069D0078, 0x069F00A7, + 0x06A10083, 0x06A20078, 0x06A300A7, 0x06A508A7, 0x06A70078, 0x06B00071, 0x06B10078, 0x06B300A8, + 0x06B400A9, 0x06B500AA, 0x06B600AB, 0x06B700AC, 0x06B80078, 0x06C100A7, 0x06C20078, 0x06C30071, + 0x06CC0078, 0x06CD0071, 0x06D90078, 0x06DA0071, 0x06DE0078, 0x06E00071, 0x06E40078, 0x06E50083, + 0x06E60078, 0x06E800A7, 0x06E90083, 0x06EC00A7, 0x06ED08A7, 0x06F00078, 0x06F900A7, 0x06FA0097, + 0x06FB0078, 0x07010071, 0x071A0083, 0x071E0078, 0x07200071, 0x07230081, 0x07240083, 0x072800A8, + 0x072900A9, 0x072A00AA, 0x072B00AB, 0x072C00AC, 0x072D0097, 0x072E0078, 0x07410071, 0x07430078, + 0x07440071, 0x07460078, 0x074A0071, 0x074C0078, 0x074D0071, 0x07500078, 0x07510071, 0x07520078, + 0x07550071, 0x07560078, 0x07570071, 0x075A0083, 0x075D0078, 0x075E0083, 0x075F0078, 0x07600071, + 0x07630081, 0x07640083, 0x07670078, 0x076800A8, 0x076900A9, 0x076A00AA, 0x076B00AB, 0x076C00AC, + 0x076D0078, 0x076E1071, 0x076F0078, 0x07800071, 0x07810094, 0x07820097, 0x07865897, 0x07870097, + 0x078A0094, 0x078C0083, 0x078D0094, 0x079000A8, 0x079100A9, 0x079200AA, 0x079300AB, 0x079400AC, + 0x079500B3, 0x079A0094, 0x079D00B4, 0x079F00A7, 0x07A00071, 0x07A40078, 0x07A50071, 0x07A90871, + 0x07AA0071, 0x07AE0871, 0x07AF0071, 0x07B60078, 0x07B90083, 0x07BB0883, 0x07BD0083, 0x07C40071, + 0x07C60078, 0x07C80083, 0x07CC0078, 0x07CD0083, 0x07D10883, 0x07D20083, 0x07D60883, 0x07D70083, + 0x07DF0094, 0x07E30083, 0x07E40094, 0x07E70078, 0x07E80097, 0x07E90078, 0x08000071, 0x08110078, + 0x08120071, 0x08130871, 0x08140078, 0x08150071, 0x081600A7, 0x08170083, 0x081A0078, 0x081B0083, + 0x081C00A7, 0x081D0078, 0x082000A8, 0x082100A9, 0x082200AA, 0x082300AB, 0x082400AC, 0x08250097, + 0x08280071, 0x082B00A7, 0x082C0083, 0x082D0078, 0x085000B5, 0x08630078, 0x08680071, 0x087E7881, + 0x087F0078, 0x08800071, 0x08AD0078, 0x08B00071, 0x08D20078, 0x08D40071, 0x08FD0078, 0x09000071, + 0x09270078, 0x09280071, 0x092F0078, 0x09300071, 0x09470078, 0x09480071, 0x095B0078, 0x095C0071, + 0x09630078, 0x09640071, 0x098B0078, 0x098C0071, 0x09AE0078, 0x09B00094, 0x09B10097, 0x09B500B6, + 0x09B600B7, 0x09B700B8, 0x09B800B9, 0x09B900B0, 0x09BA00BA, 0x09BB00BB, 0x09BC00BC, 0x09BD00BD, + 0x09BE00BE, 0x09BF0078, 0x09C00071, 0x09C80054, 0x09CD0078, 0x09D00071, 0x09FB0078, 0x0A010071, + 0x0B370097, 0x0B380071, 0x0B3C0078, 0x0B400005, 0x0B410071, 0x0B4E00BF, 0x0B4F0078, 0x0B500071, + 0x0B760097, 0x0B7700C0, 0x0B7800C1, 0x0B790078, 0x0B800071, 0x0B890083, 0x0B8B0078, 0x0B900071, + 0x0B990083, 0x0B9B0097, 0x0B9C0078, 0x0BA00071, 0x0BA90083, 0x0BAA0078, 0x0BB00071, 0x0BB90083, + 0x0BBA0078, 0x0BC00071, 0x0BDA00C2, 0x0BDB00A7, 0x0BDC0083, 0x0BDF00A7, 0x0BE30083, 0x0BE400A7, + 0x0BE50083, 0x0BEA0097, 0x0BEE0071, 0x0BEF0078, 0x0BF000A8, 0x0BF100A9, 0x0BF200AA, 0x0BF300AB, + 0x0BF400AC, 0x0BF50078, 0x0BF800C3, 0x0BF900C4, 0x0BFA00C5, 0x0BFB00C6, 0x0BFC00C7, 0x0BFD0078, + 0x0C000006, 0x0C030099, 0x0C040006, 0x0C060083, 0x0C070005, 0x0C0800A8, 0x0C0900A9, 0x0C0A00AA, + 0x0C0B00AB, 0x0C0C00AC, 0x0C0D0078, 0x0C100071, 0x0C3C0078, 0x0C400071, 0x0C550078, 0x0C800071, + 0x0C8F0078, 0x0C900083, 0x0C9200A7, 0x0C940083, 0x0C9500C8, 0x0C960078, 0x0C9800A7, 0x0C990083, + 0x0C9A00A7, 0x0C9D0083, 0x0C9E0078, 0x0CA00054, 0x0CA10078, 0x0CA20006, 0x0CA300A8, 0x0CA400A9, + 0x0CA500AA, 0x0CA600AB, 0x0CA700AC, 0x0CA80071, 0x0CB70078, 0x0CB80071, 0x0CBB0078, 0x0CC00071, + 0x0CD50078, 0x0CD800A7, 0x0CE10071, 0x0CE400A7, 0x0CE50078, 0x0CE800A8, 0x0CE900A9, 0x0CEA00AA, + 0x0CEB00AB, 0x0CEC00AC, 0x0CED0078, 0x0CEF0006, 0x0CF00054, 0x0D000071, 0x0D0C0083, 0x0D0D00A7, + 0x0D0E0078, 0x0D0F0097, 0x0D100078, 0x0E800055, 0x0E967881, 0x0EA70081, 0x0EA87881, 0x0EB17055, + 0x0EB60055, 0x0EBC7881, 0x0EBD0055, 0x0ECE7881, 0x0EE00083, 0x0EE20078, 0x0F000863, 0x0F4B0855, + 0x0F4D1055, 0x0F4E0078, 0x0F500863, 0x0F7D0078, 0x0F8008C9, 0x0F8408CA, 0x0F8808C9, 0x0F8B0078, + 0x0F8C08CA, 0x0F8F0078, 0x0F9008C9, 0x0F9408CA, 0x0F9808C9, 0x0F9C08CA, 0x0FA008C9, 0x0FA30078, + 0x0FA408CA, 0x0FA70078, 0x0FA80855, 0x0FAC0078, 0x0FB008C9, 0x0FB408CA, 0x0FB808CB, 0x0FB908CC, + 0x0FBB08CD, 0x0FBC08CE, 0x0FBD08CF, 0x0FBE08D0, 0x0FBF0078, 0x0FC008C9, 0x0FC408D1, 0x0FC808C9, + 0x0FCC08D1, 0x0FD008C9, 0x0FD408D1, 0x0FD808C9, 0x0FD90855, 0x0FDC08CA, 0x0FDD08D2, 0x0FDE08D3, + 0x0FDF08D4, 0x0FE01037, 0x0FE10855, 0x0FE408D5, 0x0FE608D3, 0x0FE70837, 0x0FE808C9, 0x0FE90855, + 0x0FEA0078, 0x0FEB0855, 0x0FEC08CA, 0x0FED08D6, 0x0FEE0078, 0x0FEF0837, 0x0FF008C9, 0x0FF10855, + 0x0FF408CA, 0x0FF508D7, 0x0FF608D8, 0x0FF70837, 0x0FF80078, 0x0FF90855, 0x0FFC08D9, 0x0FFD08DA, + 0x0FFE08D3, 0x0FFF1037, 0x10000805, 0x10011005, 0x10060057, 0x100700C2, 0x10080099, 0x100B0006, + 0x100C00DB, 0x100D00B4, 0x100E00DB, 0x100F00B4, 0x10100006, 0x10121006, 0x101400DC, 0x101500DD, + 0x101600DE, 0x101700DF, 0x10180007, 0x101A1007, 0x101B1006, 0x101C0006, 0x101D00E0, 0x101E1006, + 0x10200038, 0x10210006, 0x102200E1, 0x1023000A, 0x10241006, 0x10250006, 0x10290019, 0x102A0038, + 0x102B0006, 0x10300057, 0x10320078, 0x10350057, 0x103878E2, 0x10390078, 0x103A78E3, 0x103B78E4, + 0x103C78E5, 0x103D780B, 0x103E7819, 0x103F780A, 0x104070E2, 0x1041705A, 0x104270E3, 0x104370E4, + 0x104470E5, 0x1045700B, 0x10467019, 0x1047700A, 0x10487081, 0x104B0078, 0x10500008, 0x10541008, + 0x10550008, 0x105B0078, 0x10680083, 0x106F0095, 0x10730083, 0x10760078, 0x10801054, 0x10812877, + 0x10820054, 0x10831054, 0x10840054, 0x10852855, 0x10862877, 0x10872855, 0x10882877, 0x108A0054, + 0x108B1054, 0x108C0054, 0x108D2877, 0x108F0054, 0x10907854, 0x10922877, 0x109308E6, 0x10942877, + 0x109508E7, 0x10962877, 0x10970058, 0x10982877, 0x10990054, 0x109A2855, 0x109B1071, 0x109D0054, + 0x109E2855, 0x109F2877, 0x10A028E8, 0x10A10019, 0x10A32855, 0x10A50054, 0x10A70078, 0x10AA305F, + 0x10B010E9, 0x10B110EA, 0x10B210EB, 0x10B310EC, 0x10B410ED, 0x10B510EE, 0x10B610EF, 0x10B710F0, + 0x10B810F1, 0x10B910F2, 0x10BA10F3, 0x10BB10F4, 0x10BC10F5, 0x10BD10F6, 0x10BE10F7, 0x10BF10F8, + 0x10C000F9, 0x10C100FA, 0x10C20078, 0x10C80019, 0x10CB0054, 0x10CD0819, 0x10CE0054, 0x10D00019, + 0x10D10054, 0x10D30019, 0x10D40054, 0x10D70819, 0x10D80054, 0x10E70819, 0x10E80054, 0x10E90019, + 0x10EB0054, 0x10FA0019, 0x110100E8, 0x110208E8, 0x11030019, 0x110400FB, 0x110608FC, 0x11070019, + 0x1109000B, 0x110A0019, 0x110B00E8, 0x110C0019, 0x110D00E8, 0x110F0019, 0x111000E8, 0x111208E8, + 0x11140019, 0x111610E8, 0x111700E8, 0x111810E8, 0x111900E8, 0x111A0019, 0x111E00FD, 0x111F00E8, + 0x112208E8, 0x112300E8, 0x11270019, 0x112900FD, 0x112B0019, 0x113008E8, 0x113200FD, 0x11360019, + 0x113708FD, 0x113900FD, 0x113A08FD, 0x113B00FD, 0x113C08FD, 0x113D00FD, 0x114008FD, 0x114100FD, + 0x114208FD, 0x114300FD, 0x114408FD, 0x114500FD, 0x114600E8, 0x11470019, 0x114800FE, 0x114A0019, + 0x114C00FF, 0x114D0019, 0x115100FD, 0x11520019, 0x11530100, 0x11540101, 0x115500E8, 0x115608E8, + 0x115800FD, 0x115C00E8, 0x115D0019, 0x115F00E8, 0x11600019, 0x116500FE, 0x11670019, 0x116800FD, + 0x11690019, 0x116B00FD, 0x117008FD, 0x117200FD, 0x117508FD, 0x11770019, 0x117800FD, 0x11790102, + 0x117B0103, 0x117C00E8, 0x117D0104, 0x117F0105, 0x11800054, 0x118400FD, 0x11860054, 0x119000E8, + 0x11910054, 0x1195080A, 0x11960054, 0x119B0094, 0x11BE0019, 0x11BF0054, 0x11CE0019, 0x11DA00B4, + 0x11DB0006, 0x11DC0054, 0x11EE0078, 0x12000054, 0x12140078, 0x12200054, 0x12260078, 0x12301906, + 0x12311907, 0x12321908, 0x12331909, 0x1234190A, 0x1235190B, 0x1236190C, 0x1237190D, 0x1238190E, + 0x1239190F, 0x123A1106, 0x123B1107, 0x123C1108, 0x123D1109, 0x123E110A, 0x123F110B, 0x1240110C, + 0x1241110D, 0x1242110E, 0x1243110F, 0x1244105D, 0x1245105B, 0x12461110, 0x12471111, 0x12481112, + 0x12491113, 0x124A1114, 0x124B1115, 0x124C1116, 0x124D1117, 0x124E1094, 0x125B1918, 0x12681919, + 0x127518C3, 0x1276011A, 0x1277011B, 0x1278011C, 0x1279011D, 0x127A011E, 0x127B00C4, 0x127C00C5, + 0x127D00C6, 0x127E00C7, 0x127F011F, 0x12800054, 0x12FC0019, 0x13000054, 0x134F0078, 0x13500054, + 0x13560094, 0x13570054, 0x13590078, 0x13810054, 0x13850078, 0x13860054, 0x13940078, 0x13950054, + 0x13A60078, 0x13A80054, 0x13AA0078, 0x13AB0054, 0x13B00078, 0x13B10054, 0x13B40009, 0x13BB0106, + 0x13BC0107, 0x13BD0108, 0x13BE0109, 0x13BF010A, 0x13C00106, 0x13C10107, 0x13C20108, 0x13C30109, + 0x13C4010A, 0x13C50106, 0x13C60107, 0x13C70108, 0x13C80109, 0x13C9010A, 0x13CA0054, 0x13CB0078, + 0x13CC0054, 0x13D80078, 0x13D90054, 0x13E000E8, 0x13E10019, 0x13E200FE, 0x13E3000A, 0x13E40078, + 0x13E80019, 0x13EA00E8, 0x13EB00FE, 0x13EC0019, 0x13EE00E8, 0x13EF00FE, 0x13F00019, 0x13F100FD, + 0x13F30009, 0x13F60078, 0x13F80019, 0x14000094, 0x14800019, 0x14C2000A, 0x14C70120, 0x14C80121, + 0x14C9000A, 0x14CD0019, 0x14CE00E8, 0x14D80019, 0x14DC0122, 0x14DD0019, 0x14E000FD, 0x14E100E8, + 0x14E200FD, 0x14E30019, 0x14E700E8, 0x14E800FE, 0x14EA00FD, 0x14EB0019, 0x14EC0009, 0x14EE00E8, + 0x14EF0019, 0x14F200E8, 0x14F30019, 0x14F400E8, 0x14F50019, 0x14FA00E8, 0x14FC00FD, 0x14FD0019, + 0x14FE0009, 0x14FF0019, 0x150500E8, 0x150610E8, 0x150700E8, 0x15110019, 0x151200E8, 0x15140019, + 0x151600FE, 0x15180019, 0x151A00FD, 0x151B0019, 0x151E00FD, 0x151F00E8, 0x15200019, 0x152C00E8, + 0x152D0019, 0x153200FD, 0x15330019, 0x153500E8, 0x15370019, 0x153800E8, 0x15390019, 0x153A10E8, + 0x153B1019, 0x153C0019, 0x153D00FE, 0x153E00E8, 0x153F00FE, 0x154300E8, 0x154600FE, 0x154700E8, + 0x154900FE, 0x154F00E8, 0x155100FE, 0x15520019, 0x155300FD, 0x15570019, 0x155800FE, 0x155900E8, + 0x155A00FE, 0x155B00E8, 0x155E00FE, 0x156400E8, 0x156700FE, 0x156C0019, 0x156E08E8, 0x156F0123, + 0x15700019, 0x157100E8, 0x15720124, 0x157300E8, 0x15740019, 0x157600FD, 0x157700E8, 0x15780019, + 0x157C00FE, 0x157E0019, 0x15800054, 0x158A0078, 0x16000096, 0x16180098, 0x16300078, 0x16400063, + 0x16720055, 0x16730054, 0x16760078, 0x167D0006, 0x16800125, 0x16930078, 0x16980071, 0x16B30078, + 0x16C00071, 0x16CC0078, 0x16D00071, 0x16F00078, 0x17000006, 0x17010126, 0x17030006, 0x170500E0, + 0x17060126, 0x17070006, 0x170C0078, 0x170E0126, 0x170F0078, 0x17400054, 0x174D0078, 0x174E0054, + 0x177A0078, 0x17801054, 0x17EB0078, 0x17F80054, 0x17FE0078, 0x18008805, 0x18010006, 0x18020054, + 0x18030071, 0x18040009, 0x18090054, 0x180A0009, 0x180E0099, 0x180F00BF, 0x18100054, 0x18110127, + 0x18120128, 0x18130129, 0x1814012A, 0x18150083, 0x18180099, 0x18190081, 0x181B1054, 0x181C112B, + 0x181D112C, 0x181E0071, 0x181F0054, 0x18200078, 0x18210071, 0x18260871, 0x18320071, 0x18380871, + 0x18390071, 0x183A0871, 0x183C0071, 0x183D0871, 0x183F0071, 0x184A0871, 0x184B0071, 0x184C0078, + 0x184D0083, 0x184E1037, 0x184F0881, 0x18500099, 0x18510071, 0x18560871, 0x18620071, 0x18680871, + 0x18690071, 0x186A0871, 0x186C0071, 0x186D0871, 0x186F0071, 0x187A0871, 0x187B0071, 0x187C0871, + 0x187E0081, 0x187F0881, 0x18800078, 0x18830071, 0x18970078, 0x18991071, 0x18C80094, 0x18C978AD, + 0x18CA78AE, 0x18CB7894, 0x18D00071, 0x18DC0078, 0x18E00054, 0x18E80078, 0x18F80071, 0x19001094, + 0x190F1054, 0x191010AD, 0x191110AE, 0x1912112D, 0x1913112E, 0x1914112F, 0x19151094, 0x19220078, + 0x19286854, 0x19291930, 0x192A1931, 0x192B1932, 0x192C1933, 0x192D1934, 0x192E1935, 0x192F1936, + 0x19301894, 0x193E1854, 0x194018AD, 0x194118AE, 0x1942192D, 0x1943192E, 0x1944192F, 0x19451894, + 0x19591937, 0x195A1938, 0x195B1939, 0x195C193A, 0x195D193B, 0x195E193C, 0x195F193D, 0x19601094, + 0x19666854, 0x19681894, 0x19806894, 0x19AC1094, 0x19B96894, 0x19BC6854, 0x19BE6894, 0x19EF6854, + 0x19F01094, 0x1A000071, 0x26DB0078, 0x26E00054, 0x27000071, 0x4FDE0078, 0x50000071, 0x52470078, + 0x52480054, 0x52640078, 0x53800037, 0x538C0078, 0x54000071, 0x540100C8, 0x54020071, 0x54030083, + 0x54040071, 0x541200A7, 0x54130083, 0x54140054, 0x54160078, 0x56000071, 0x6BD20078, 0x6C00013E, + 0x7000013F, 0x7C800871, 0x7D070071, 0x7D080871, 0x7D0A0071, 0x7D0B0871, 0x7D120071, 0x7D130871, + 0x7D140071, 0x7D150871, 0x7D170078, 0x7D180871, 0x7D360078, 0x7D380871, 0x7D6D0078, 0x7D801055, + 0x7D840078, 0x7D8A1055, 0x7D8C0078, 0x7D8F0083, 0x7D90289B, 0x7D95089B, 0x7DA10078, 0x7DA2089B, + 0x7DA8409E, 0x7DAA389E, 0x7DAB409E, 0x7DAC389E, 0x7DAD409E, 0x7DAE389E, 0x7DAF409E, 0x7DB0389E, + 0x7DB1409E, 0x7DB2389E, 0x7DB3409E, 0x7DB4389E, 0x7DB5409E, 0x7DB6389E, 0x7DB7409E, 0x7DB8389E, + 0x7DB9409E, 0x7DBA389E, 0x7DBB409E, 0x7DBC389E, 0x7DBD409E, 0x7DBE389E, 0x7DBF409E, 0x7DC0389E, + 0x7DC1409E, 0x7DC8389E, 0x7DC9409E, 0x7DCA389E, 0x7DCB409E, 0x7DCC389E, 0x7DCD409E, 0x7DCE389E, + 0x7DCF409E, 0x7DD1389E, 0x7DD2409E, 0x7DD4389E, 0x7DD5409E, 0x7DD6389E, 0x7DD7409E, 0x7DD90078, + 0x7DEA209E, 0x7DEB489E, 0x7DEC209E, 0x7DEF409E, 0x7DF3389E, 0x7DF5409E, 0x7DFC389E, 0x7DFD209E, + 0x7DFE409E, 0x7DFF389E, 0x7E00409E, 0x7E32209E, 0x7E4C389E, 0x7E70489E, 0x7E7B409E, 0x7E89209E, + 0x7E97389E, 0x7E9A489E, 0x7E9E209E, 0x7E9F00B4, 0x7EA00078, 0x7EA8389E, 0x7EAC209E, 0x7EAE389E, + 0x7EAF209E, 0x7EB0389E, 0x7EB1209E, 0x7EB4389E, 0x7EB5209E, 0x7EB8389E, 0x7EBA209E, 0x7EC3389E, + 0x7EC80078, 0x7EC9389E, 0x7ECB209E, 0x7ECC389E, 0x7ECD209E, 0x7EDA389E, 0x7EDB209E, 0x7EDC389E, + 0x7EDE209E, 0x7EE2389E, 0x7EE3209E, 0x7EE40078, 0x7EF8409E, 0x7EFE4140, 0x7EFF0078, 0x7F000083, + 0x7F088006, 0x7F0C80BF, 0x7F0D0078, 0x7F100083, 0x7F120078, 0x7F188006, 0x7F198099, 0x7F1A8038, + 0x7F1B80BF, 0x7F230006, 0x7F2480BF, 0x7F251006, 0x7F271038, 0x7F28600C, 0x7F2A6006, 0x7F2C6099, + 0x7F2D60BF, 0x7F306006, 0x7F31600B, 0x7F326019, 0x7F346006, 0x7F356007, 0x7F360078, 0x7F38409E, + 0x7F41209E, 0x7F46489E, 0x7F47209E, 0x7F49489E, 0x7F4A209E, 0x7F4C489E, 0x7F4D209E, 0x7F4E489E, + 0x7F4F209E, 0x7F50489E, 0x7F51209E, 0x7F52489E, 0x7F53209E, 0x7F54489E, 0x7F55209E, 0x7F5A489E, + 0x7F5B209E, 0x7F5C489E, 0x7F5D209E, 0x7F5E489E, 0x7F5F209E, 0x7F60489E, 0x7F61209E, 0x7F62489E, + 0x7F63209E, 0x7F64489E, 0x7F65209E, 0x7F66489E, 0x7F67209E, 0x7F68489E, 0x7F69209E, 0x7F6A489E, + 0x7F6B209E, 0x7F6C489E, 0x7F6D209E, 0x7F6E489E, 0x7F6F209E, 0x7F70489E, 0x7F71209E, 0x7F72489E, + 0x7F73209E, 0x7F74489E, 0x7F75209E, 0x7F76489E, 0x7F77209E, 0x7F7A489E, 0x7F7B209E, 0x7F7F0078, + 0x7F818806, 0x7F828808, 0x7F838806, 0x7F848809, 0x7F858806, 0x7F86880C, 0x7F88880E, 0x7F898810, + 0x7F8A8812, 0x7F8B8814, 0x7F8C8816, 0x7F8D880C, 0x7F8E8818, 0x7F8F881A, 0x7F908806, 0x7F918860, + 0x7F9E8806, 0x7F9F8837, 0x7FA18861, 0x7FAE8819, 0x7FB0880A, 0x7FB15009, 0x7FB25006, 0x7FB35071, + 0x7FB85081, 0x7FB95071, 0x7FCF5081, 0x7FD05071, 0x7FE00078, 0x7FE15071, 0x7FE40078, 0x7FE55071, + 0x7FE80078, 0x7FE95071, 0x7FEC0078, 0x7FED5071, 0x7FEF0078, 0x7FF08808, 0x7FF18819, 0x7FF28854, + 0x7FF38808, 0x7FF45054, 0x7FF55019, 0x7FF75054, 0x7FF80078, 0x7FFD0141, 0x7FFE0054, 0x7FFF0078, + 0x80000071, 0x80060078, 0x80070071, 0x801F0078, 0x80200071, 0x80270078, 0x80280071, 0x802F0078, + 0x80400071, 0x807E0078, 0x80800097, 0x80810094, 0x80820078, 0x808400B6, 0x808500B7, 0x808600B8, + 0x808700B9, 0x808800B0, 0x808900BA, 0x808A00BB, 0x808B00BC, 0x808C00BD, 0x808D0142, 0x808E0143, + 0x808F0144, 0x80900145, 0x809100B1, 0x80920146, 0x80930147, 0x80940148, 0x80950149, 0x8096014A, + 0x8097014B, 0x8098014C, 0x8099014D, 0x809A0078, 0x809C0094, 0x80A0014E, 0x80A1014F, 0x80A20150, + 0x80A30151, 0x80A40152, 0x80A50150, 0x80A60153, 0x80A70151, 0x80A80154, 0x80A90155, 0x80AA0156, + 0x80AB0157, 0x80AC014F, 0x80AE0158, 0x80B00154, 0x80B30150, 0x80B50155, 0x80B60153, 0x80B90151, + 0x80BA0150, 0x80BB005F, 0x80BD0054, 0x80C500C3, 0x80C60078, 0x81800071, 0x819000AD, 0x819100B0, + 0x81920078, 0x81980071, 0x81A50159, 0x81A60078, 0x81C00071, 0x81CF0078, 0x81D00071, 0x81E20078, + 0x81E40071, 0x81E80094, 0x81E90158, 0x81EA015A, 0x81EB0078, 0x8200015B, 0x8214015C, 0x82280071, + 0x824F0078, 0x825000A8, 0x825100A9, 0x825200AA, 0x825300AB, 0x825400AC, 0x82550078, 0x8400009B, + 0x84030078, 0x8404009B, 0x841B0078, 0x841C009B, 0x841D0078, 0x841E009B, 0x841F0078, 0x8500009B, + 0x85010083, 0x85020078, 0x85030083, 0x85040078, 0x85060083, 0x8508009B, 0x850A0078, 0x850B009B, + 0x850C0078, 0x850D009B, 0x851A0078, 0x851C0083, 0x851E0078, 0x8520015D, 0x8521015E, 0x8522015F, + 0x85230160, 0x85240078, 0x8528009A, 0x852D0078, 0xE8000094, 0xE87B0078, 0xE8800094, 0xE8940078, + 0xE8950094, 0xE8AF0894, 0xE8B300A7, 0xE8B40083, 0xE8B50094, 0xE8B700A7, 0xE8BA0057, 0xE8BE0083, + 0xE8C20094, 0xE8C30083, 0xE8C60094, 0xE8D50083, 0xE8D70094, 0xE8DE0894, 0xE8E10094, 0xE8EF0078, + 0xE9000054, 0xE9210083, 0xE9230078, 0xE9800054, 0xE9AC0078, 0xEA002877, 0xEA0D2855, 0xEA1A2877, + 0xEA272855, 0xEA342877, 0xEA412855, 0xEA4E2877, 0xEA500078, 0xEA512877, 0xEA520078, 0xEA532877, + 0xEA540078, 0xEA552877, 0xEA5B2855, 0xEA5D0078, 0xEA5F2855, 0xEA620078, 0xEA632855, 0xEA682877, + 0xEA752855, 0xEA822877, 0xEA830078, 0xEA842877, 0xEA860078, 0xEA872877, 0xEA8F2855, 0xEA9C2877, + 0xEA9D0078, 0xEA9E2877, 0xEAA40078, 0xEAA52877, 0xEAA92855, 0xEAB62877, 0xEAC32855, 0xEAD02877, + 0xEADD2855, 0xEAEA2877, 0xEAF72855, 0xEB042877, 0xEB112855, 0xEB1E2877, 0xEB2B2855, 0xEB382877, + 0xEB452855, 0xEB530078, 0xEB542877, 0xEB612855, 0xEB712877, 0xEB7E2855, 0xEB8E2877, 0xEB9B2855, + 0xEBAB2877, 0xEBB82855, 0xEBC82877, 0xEBD52855, 0xEBE50078, 0xEBE7280E, 0xEBE82810, 0xEBE92812, + 0xEBEA2814, 0xEBEB2816, 0xEBEC280E, 0xEBED2810, 0xEBEE2812, 0xEBEF2814, 0xEBF02816, 0xEBF1280E, + 0xEBF22810, 0xEBF32812, 0xEBF42814, 0xEBF52816, 0xEBF6280E, 0xEBF72810, 0xEBF82812, 0xEBF92814, + 0xEBFA2816, 0xEBFB280E, 0xEBFC2810, 0xEBFD2812, 0xEBFE2814, 0xEBFF2816, 0xEC000078 + }; + + static const uint32_t a1[] = { + 0x00000071, 0x536C0078, 0x7C000871, 0x7D0F0078 + }; + + static const uint32_t a7[] = { + 0x00100057, 0x00400078, 0x00800083, 0x00F80078, 0x8000013F, 0xFFFF0078 + }; + + static const uint32_t a8[] = { + 0x0000013F, 0x7FFF0078 + }; + + static const uint32_t a16[] = { + 0x00800865, 0x00880065, 0x00890865, 0x00930065, 0x00940865, 0x00980161, 0x00991065, 0x009A0865, + 0x009C0863, 0x009F1063, 0x00A00063, 0x00A10863, 0x00A41055, 0x00A50065, 0x00A60865, 0x00A90065, + 0x00AA0865, 0x00B30065, 0x00B40865, 0x00BC0863, 0x00BF1162, 0x00C00163, 0x00C10065, 0x00C30063, + 0x00C40068, 0x00C50063, 0x00C60055, 0x00C70164, 0x00C80063, 0x00C90068, 0x00CA0165, 0x00CB0166, + 0x00CC0065, 0x00CD0055, 0x00CE0167, 0x00CF0168, 0x00D00865, 0x00D10065, 0x00D30063, 0x00D4006F, + 0x00D50055, 0x00D60065, 0x00D70863, 0x00D80070, 0x00D90063, 0x00DB0169, 0x00DC0065, 0x00DD0071, + 0x00DE0065, 0x00DF016A, 0x00E00071, 0x00E21074, 0x00E31072, 0x00E41073, 0x00E51074, 0x00E60863, + 0x00EE016B, 0x00EF0865, 0x00F20065, 0x00F30865, 0x00F81072, 0x00F91073, 0x00FA0865, 0x00FB016C, + 0x00FC0865, 0x010E0065, 0x010F0865, 0x01100055, 0x01110065, 0x01130865, 0x011A0055, 0x011D0063, + 0x011E016D, 0x011F0055, 0x0120016E, 0x01210078, 0x01280055, 0x0129016F, 0x012A0055, 0x012B007A, + 0x012C0170, 0x012D0171, 0x012E0055, 0x01310172, 0x01320055, 0x01340173, 0x01350055, 0x01370173, + 0x01380055, 0x013A0174, 0x013B0055, 0x0141007D, 0x01420055, 0x0145007E, 0x01460055, 0x01587881, + 0x015C0082, 0x015D0081, 0x01610037, 0x01630082, 0x01680081, 0x01690037, 0x016C1037, 0x016F0037, + 0x01707881, 0x01720037, 0x01800083, 0x01A00883, 0x01A20175, 0x01A30083, 0x01B80078, 0x01BA0037, + 0x01BB0078, 0x01C20837, 0x01C30806, 0x01C40885, 0x01C50078, 0x01C70887, 0x01C80060, 0x01D50860, + 0x01D60889, 0x01D80061, 0x01E50861, 0x01E6088C, 0x01E70078, 0x01E81176, 0x01E90877, 0x01EA1177, + 0x01EB0055, 0x01EC0065, 0x01F81093, 0x01F90055, 0x01FA1178, 0x01FB0063, 0x01FC10D8, 0x01FD0065, + 0x01FE0077, 0x02000892, 0x02020092, 0x02030892, 0x02040092, 0x02060892, 0x02070092, 0x02080060, + 0x020C0860, 0x020D0060, 0x02180061, 0x021C0861, 0x021D0061, 0x02280893, 0x022A0093, 0x022B0893, + 0x022C0093, 0x022E0893, 0x022F0093, 0x02300065, 0x023B0865, 0x023C0065, 0x02410083, 0x02430078, + 0x02440095, 0x02450065, 0x02600863, 0x02610063, 0x02670078, 0x02680865, 0x026A0065, 0x026B0865, + 0x026C0065, 0x026D0865, 0x02700065, 0x02710865, 0x02740065, 0x02750865, 0x027B0065, 0x027C0865, + 0x027D0078, 0x02800065, 0x02880078, 0x02980096, 0x02AB0078, 0x02AC0081, 0x02AD0097, 0x02B00098, + 0x02C31055, 0x02C40097, 0x02C50078, 0x02C80083, 0x02E1009A, 0x02E20083, 0x02E40078, 0x02E8009B, + 0x02F50078, 0x02F8009B, 0x02F9009A, 0x02FA0078, 0x0300009C, 0x03020078, 0x03050140, 0x0306009D, + 0x03070054, 0x03080083, 0x030B0078, 0x030D009D, 0x030E0078, 0x030F009D, 0x0310009E, 0x0311089E, + 0x0313009E, 0x031D0078, 0x0320009E, 0x03250083, 0x032F0078, 0x03300179, 0x0331017A, 0x0332017B, + 0x0333017C, 0x0334017D, 0x033500A5, 0x0336009D, 0x0337009E, 0x033A109E, 0x033C009E, 0x0369089E, + 0x036A009E, 0x036B0083, 0x036E009C, 0x036F0083, 0x0372009F, 0x03730083, 0x03740054, 0x03750083, + 0x0377009E, 0x0378000F, 0x03790011, 0x037A0013, 0x037B0015, 0x037C0017, 0x037D009E, 0x037E00A6, + 0x037F009E, 0x0380009D, 0x03870057, 0x03880083, 0x0389009E, 0x03980083, 0x03A50078, 0x03A6009E, + 0x03B70078, 0x03C0009E, 0x03D30083, 0x03D8009E, 0x03D90078, 0x04800083, 0x048100A7, 0x04820071, + 0x04940871, 0x04950071, 0x04980871, 0x04990071, 0x049D0078, 0x049E0071, 0x049F00A7, 0x04A00083, + 0x04A400A7, 0x04A60083, 0x04A70078, 0x04A80083, 0x04AA0078, 0x04AC0871, 0x04B00071, 0x04B10083, + 0x04B20097, 0x04B3017E, 0x04B4017F, 0x04B50180, 0x04B60181, 0x04B70182, 0x04B80078, 0x04BE0071, + 0x04BF0078, 0x04C00083, 0x04C100A7, 0x04C20071, 0x04C60078, 0x04C70071, 0x04C80078, 0x04C90071, + 0x04D40078, 0x04D50071, 0x04D80078, 0x04DB0071, 0x04DD0078, 0x04DE0071, 0x04DF00A7, 0x04E00083, + 0x04E20078, 0x04E300A7, 0x04E40078, 0x04E508A7, 0x04E60083, 0x04E70078, 0x04EB00A7, 0x04EC0078, + 0x04EE0871, 0x04F00071, 0x04F10083, 0x04F20078, 0x04F3017E, 0x04F4017F, 0x04F50180, 0x04F60181, + 0x04F70182, 0x04F80071, 0x04F90008, 0x04FA00B6, 0x04FB00B7, 0x04FC0183, 0x04FD0078, 0x05000083, + 0x050100A7, 0x05020071, 0x05050078, 0x05070071, 0x05080078, 0x05090071, 0x05140078, 0x05150071, + 0x05180078, 0x05190871, 0x051A0071, 0x051B0078, 0x051C0071, 0x051D0078, 0x051F00A7, 0x05200083, + 0x05210078, 0x05230083, 0x05240078, 0x05250083, 0x05270078, 0x052C0871, 0x052E0078, 0x0533017E, + 0x0534017F, 0x05350180, 0x05360181, 0x05370182, 0x05380083, 0x05390071, 0x053A0078, 0x05400083, + 0x054100A7, 0x05420071, 0x05540078, 0x05550071, 0x05580078, 0x05590071, 0x055D0078, 0x055E0071, + 0x055F00A7, 0x05600083, 0x056400A7, 0x05660083, 0x05670078, 0x05700071, 0x05710083, 0x05720078, + 0x0573017E, 0x0574017F, 0x05750180, 0x05760181, 0x05770182, 0x05780008, 0x05790078, 0x05800083, + 0x058100A7, 0x05820071, 0x05860078, 0x05870071, 0x05880078, 0x05890071, 0x05940078, 0x05950071, + 0x05980078, 0x05990071, 0x059D0078, 0x059E0071, 0x059F0083, 0x05A20078, 0x05A300A7, 0x05A40078, + 0x05A508A7, 0x05A60083, 0x05A70078, 0x05AB00A7, 0x05AC0078, 0x05AE0871, 0x05AF0071, 0x05B10078, + 0x05B3017E, 0x05B4017F, 0x05B50180, 0x05B60181, 0x05B70182, 0x05B80071, 0x05B90078, 0x05C10071, + 0x05C50078, 0x05C70071, 0x05C80078, 0x05C90071, 0x05CB0078, 0x05CC0071, 0x05CD0078, 0x05CF0071, + 0x05D00078, 0x05D10071, 0x05D20078, 0x05D40071, 0x05D50078, 0x05D70071, 0x05DD0078, 0x05DF00A7, + 0x05E10078, 0x05E300A7, 0x05E40078, 0x05E508A7, 0x05E60083, 0x05E70078, 0x05EB00A7, 0x05EC0078, + 0x05F3017E, 0x05F4017F, 0x05F50180, 0x05F60181, 0x05F70182, 0x05F80184, 0x05F90054, 0x05FC0008, + 0x05FD0078, 0x060000A7, 0x06020071, 0x06060078, 0x06070071, 0x06080078, 0x06090071, 0x06140078, + 0x06150071, 0x061D0078, 0x061F0083, 0x062000A7, 0x06220078, 0x06230083, 0x06240078, 0x06250083, + 0x06270078, 0x062A0083, 0x062B0078, 0x06300071, 0x06310078, 0x0633017E, 0x0634017F, 0x06350180, + 0x06360181, 0x06370182, 0x06380078, 0x064100A7, 0x06420071, 0x06460078, 0x06470071, 0x06480078, + 0x06490071, 0x06540078, 0x06550071, 0x065D0078, 0x065E0071, 0x065F00B2, 0x066000A7, 0x06620078, + 0x066308A7, 0x06640078, 0x066508A7, 0x06660083, 0x06670078, 0x066A00A7, 0x066B0078, 0x06700071, + 0x06710078, 0x0673017E, 0x0674017F, 0x06750180, 0x06760181, 0x06770182, 0x06780078, 0x068100A7, + 0x06820071, 0x06860078, 0x06870071, 0x06880078, 0x06890071, 0x06940078, 0x06950071, 0x069D0078, + 0x069F00A7, 0x06A00083, 0x06A20078, 0x06A300A7, 0x06A40078, 0x06A508A7, 0x06A60083, 0x06A70078, + 0x06AB00A7, 0x06AC0078, 0x06B00071, 0x06B10078, 0x06B3017E, 0x06B4017F, 0x06B50180, 0x06B60181, + 0x06B70182, 0x06B80078, 0x06C100A7, 0x06C20071, 0x06CB0078, 0x06CD0071, 0x06DF0078, 0x06E00071, + 0x06E30078, 0x06E700A7, 0x06E90083, 0x06EA0078, 0x06EC00A7, 0x06EE08A7, 0x06EF00A7, 0x06F00078, + 0x06F900A7, 0x06FA0078, 0x07000071, 0x07180083, 0x07191071, 0x071A0083, 0x071D0078, 0x071F0008, + 0x07200071, 0x07230083, 0x07270097, 0x0728017E, 0x0729017F, 0x072A0180, 0x072B0181, 0x072C0182, + 0x072D0097, 0x072E0078, 0x07400071, 0x07410078, 0x07430071, 0x07440078, 0x07460071, 0x07470078, + 0x074A0071, 0x07540078, 0x07550071, 0x07580083, 0x07591071, 0x075A0083, 0x075E0071, 0x075F0078, + 0x07600071, 0x07620078, 0x07640083, 0x07670078, 0x0768017E, 0x0769017F, 0x076A0180, 0x076B0181, + 0x076C0182, 0x076D0078, 0x076E1071, 0x076F0078, 0x07800094, 0x07820097, 0x07890094, 0x078C0083, + 0x078D0094, 0x0790017E, 0x0791017F, 0x07920180, 0x07930181, 0x07940182, 0x079500B3, 0x079A0083, + 0x079D00BF, 0x079F00A7, 0x07A00071, 0x07A10871, 0x07A20071, 0x07A60871, 0x07A70071, 0x07AB0871, + 0x07AC0071, 0x07B40871, 0x07B50078, 0x07B80083, 0x07B90883, 0x07BB1083, 0x07BD0083, 0x07BF00A7, + 0x07C00883, 0x07C10083, 0x07C20097, 0x07C30083, 0x07C40071, 0x07C60078, 0x07C80083, 0x07C90883, + 0x07CA0083, 0x07CE0883, 0x07CF0083, 0x07D30883, 0x07D40083, 0x07DC0883, 0x07DD0083, 0x07DE0078, + 0x07DF0094, 0x07E60078, 0x07E70094, 0x07E80097, 0x07E90078, 0x08000071, 0x08150078, 0x08160083, + 0x081800A7, 0x08190078, 0x081B0083, 0x081D0078, 0x0820017E, 0x0821017F, 0x08220180, 0x08230181, + 0x08240182, 0x08250097, 0x08280071, 0x082B00A7, 0x082C0083, 0x082D0078, 0x085000B5, 0x08630078, + 0x08680071, 0x087D0097, 0x087E0078, 0x08800071, 0x08AD0078, 0x08AF0071, 0x08D10078, 0x08D40071, + 0x08FD0078, 0x09000071, 0x09240078, 0x09250071, 0x09270078, 0x09280071, 0x092B0078, 0x092D0071, + 0x092F0078, 0x09300071, 0x09440078, 0x09450071, 0x09470078, 0x09480071, 0x09580078, 0x09590071, + 0x095B0078, 0x095C0071, 0x095F0078, 0x09610071, 0x09630078, 0x09640071, 0x096B0078, 0x096C0071, + 0x09880078, 0x09890071, 0x098B0078, 0x098C0071, 0x09AD0078, 0x09AF0083, 0x09B00097, 0x09B400AD, + 0x09B500AE, 0x09B6012D, 0x09B7012E, 0x09B8012F, 0x09B90185, 0x09BA0186, 0x09BB0187, 0x09BC0188, + 0x09BD0184, 0x09BE0078, 0x09C00071, 0x09C80054, 0x09CD0078, 0x09D00071, 0x09FA0078, 0x0A000071, + 0x0B360097, 0x0B370071, 0x0B3B0078, 0x0B400071, 0x0B4D00B4, 0x0B4E0078, 0x0B500071, 0x0B750097, + 0x0B770189, 0x0B780078, 0x0B800071, 0x0B860078, 0x0B870071, 0x0B890083, 0x0B8A0078, 0x0B900071, + 0x0B990083, 0x0B9A0097, 0x0B9B0078, 0x0BA00071, 0x0BA90083, 0x0BAA0078, 0x0BB00071, 0x0BB60078, + 0x0BB70071, 0x0BB80078, 0x0BB90083, 0x0BBA0078, 0x0BC00071, 0x0BDA00C2, 0x0BDB0083, 0x0BDF00A7, + 0x0BE40083, 0x0BEA0097, 0x0BEB0081, 0x0BEC0097, 0x0BED0008, 0x0BEE0083, 0x0BEF0078, 0x0BF0017E, + 0x0BF1017F, 0x0BF20180, 0x0BF30181, 0x0BF40182, 0x0BF50078, 0x0BF80106, 0x0BF90107, 0x0BFA0108, + 0x0BFB0109, 0x0BFC010A, 0x0BFD0078, 0x0C000006, 0x0C050083, 0x0C070078, 0x0C08017E, 0x0C09017F, + 0x0C0A0180, 0x0C0B0181, 0x0C0C0182, 0x0C0D0078, 0x0C100071, 0x0C210081, 0x0C220071, 0x0C3C0078, + 0x0C400071, 0x0C540083, 0x0C550078, 0x0C800071, 0x0C8E0078, 0x0C900083, 0x0C9100A7, 0x0C930083, + 0x0C9400C8, 0x0C960078, 0x0C9800A7, 0x0C9C0083, 0x0C9E0078, 0x0CA20006, 0x0CA3017E, 0x0CA4017F, + 0x0CA50180, 0x0CA60181, 0x0CA70182, 0x0CA80071, 0x0CB70078, 0x0CB80071, 0x0CBA0078, 0x0CC00071, + 0x0CD50078, 0x0CD800A7, 0x0CE00071, 0x0CE400A7, 0x0CE50078, 0x0CE8017E, 0x0CE9017F, 0x0CEA0180, + 0x0CEB0181, 0x0CEC0182, 0x0CED0078, 0x0CEF0006, 0x0CF00054, 0x0D000071, 0x0D0B0083, 0x0D0C00A7, + 0x0D0E0078, 0x0D0F0097, 0x0D100078, 0x0E800055, 0x0E967881, 0x0E970081, 0x0E987881, 0x0E9D0081, + 0x0E9E7881, 0x0EB17055, 0x0EB50055, 0x0ECD7881, 0x0EE00083, 0x0EE20078, 0x0F000865, 0x0F4B0855, + 0x0F4D098A, 0x0F4E0078, 0x0F500865, 0x0F7D0078, 0x0F8008C9, 0x0F8408CA, 0x0F8808C9, 0x0F8B0078, + 0x0F8C08CA, 0x0F8F0078, 0x0F9008C9, 0x0F9408CA, 0x0F9808C9, 0x0F9C08CA, 0x0FA008C9, 0x0FA30078, + 0x0FA408CA, 0x0FA70078, 0x0FA808C9, 0x0FAC08CA, 0x0FB008C9, 0x0FB408CA, 0x0FB808CB, 0x0FB908CC, + 0x0FBB08CD, 0x0FBC08CE, 0x0FBD08CF, 0x0FBE08D0, 0x0FBF0078, 0x0FC008C9, 0x0FC408D1, 0x0FC808C9, + 0x0FCC08D1, 0x0FD008C9, 0x0FD408D1, 0x0FD808C9, 0x0FD9098B, 0x0FDA0078, 0x0FDB0855, 0x0FDC08CA, + 0x0FDD08D2, 0x0FDE1037, 0x0FE00837, 0x0FE1098B, 0x0FE20078, 0x0FE30855, 0x0FE408D5, 0x0FE60837, + 0x0FE808C9, 0x0FE90855, 0x0FEA0078, 0x0FEB0855, 0x0FEC08CA, 0x0FED08D6, 0x0FEE0837, 0x0FF008C9, + 0x0FF10855, 0x0FF20890, 0x0FF30855, 0x0FF408CA, 0x0FF508D7, 0x0FF60837, 0x0FF80078, 0x0FF9098B, + 0x0FFA0078, 0x0FFB0855, 0x0FFC08D9, 0x0FFD08DA, 0x0FFE0837, 0x0FFF0078, 0x10000805, 0x10011005, + 0x10035805, 0x10041005, 0x10050057, 0x1007018C, 0x10085899, 0x10090099, 0x100B1006, 0x100C018D, + 0x100D00DB, 0x100E018D, 0x100F00DB, 0x10100006, 0x10121006, 0x10130006, 0x1014018E, 0x1015018F, + 0x10160190, 0x10175853, 0x10180007, 0x10191007, 0x101A0006, 0x101B1006, 0x101C0126, 0x101D0006, + 0x101F0038, 0x10200006, 0x10220009, 0x10231006, 0x10250006, 0x102B1006, 0x102C0006, 0x102F1005, + 0x10300057, 0x10320078, 0x10350057, 0x10387855, 0x10390078, 0x103A7910, 0x103B7911, 0x103C7912, + 0x103D780B, 0x103E7809, 0x103F7855, 0x1040705D, 0x1041705B, 0x10427110, 0x10437111, 0x10447112, + 0x1045700B, 0x10467009, 0x10470078, 0x10487081, 0x104A0078, 0x10500008, 0x105B0078, 0x10680083, + 0x106E0095, 0x10700083, 0x10710095, 0x10720083, 0x10760078, 0x10801054, 0x10831077, 0x10841054, + 0x10852877, 0x10872855, 0x10882877, 0x10892855, 0x108A2877, 0x108B0054, 0x108C2877, 0x108F0054, + 0x10901054, 0x10910054, 0x10950991, 0x10962877, 0x10972855, 0x10982877, 0x109A1071, 0x109C2855, + 0x109D1054, 0x109E2855, 0x109F2877, 0x10A00019, 0x10A22877, 0x10A32855, 0x10A50019, 0x10A60078, + 0x10A9305F, 0x10AF3106, 0x10B01192, 0x10B11193, 0x10B21194, 0x10B31195, 0x10B41196, 0x10B51197, + 0x10B61198, 0x10B71199, 0x10B8119A, 0x10B9119B, 0x10BA119C, 0x10BB119D, 0x10BC119E, 0x10BD119F, + 0x10BE11A0, 0x10BF11A1, 0x10C001A2, 0x10C101A3, 0x10C20078, 0x10C80019, 0x10CA0054, 0x10CD0819, + 0x10CE0054, 0x10D10019, 0x10D20054, 0x10E60854, 0x10E70819, 0x10E80054, 0x10FA0019, 0x110000E8, + 0x11020019, 0x110408FB, 0x110500FC, 0x11070019, 0x110800E8, 0x11090059, 0x110A01A4, 0x110B0019, + 0x110D00E8, 0x11110019, 0x111500E8, 0x111610E8, 0x111800E8, 0x111A0019, 0x111C00E8, 0x111E00FE, + 0x111F00E8, 0x112008E8, 0x112101A5, 0x112200E8, 0x112308E8, 0x112500E8, 0x11260019, 0x112900FE, + 0x112B0019, 0x112F00E8, 0x11300019, 0x113200FE, 0x11360819, 0x113708FE, 0x113900FE, 0x113A08FE, + 0x113B00FE, 0x113C08FE, 0x113D00FE, 0x114008FE, 0x114100FE, 0x114208FE, 0x114300FE, 0x114408FE, + 0x114500FE, 0x11460019, 0x114700FD, 0x11490019, 0x115100FE, 0x11520019, 0x115300E8, 0x115401A6, + 0x115608E8, 0x115800FE, 0x115C0019, 0x115F00E8, 0x11600019, 0x116400FD, 0x116601A7, 0x11670019, + 0x116800FE, 0x11690019, 0x116B00FE, 0x117008FE, 0x117200FE, 0x117508FE, 0x11770019, 0x117800FE, + 0x11790102, 0x117A00E8, 0x117B0103, 0x117C00E8, 0x117D0104, 0x117E0105, 0x117F00E8, 0x11800054, + 0x118400FE, 0x11860054, 0x119000E8, 0x11910054, 0x11940809, 0x11950054, 0x119B0094, 0x11BD0054, + 0x11CA0094, 0x11CB0054, 0x11CD0019, 0x11DA00BF, 0x11DB0054, 0x11EE0078, 0x12000054, 0x12130078, + 0x12200054, 0x12250078, 0x123018C4, 0x123118C5, 0x123218C6, 0x123318C7, 0x1234191F, 0x1235191A, + 0x1236191B, 0x1237191C, 0x1238191D, 0x1239191E, 0x123A10C4, 0x123B10C5, 0x123C10C6, 0x123D10C7, + 0x123E111F, 0x123F111A, 0x1240111B, 0x1241111C, 0x1242111D, 0x1243111E, 0x1244105A, 0x124510E3, + 0x124610E4, 0x124710E5, 0x124811A8, 0x124911A9, 0x124A11AA, 0x124B11AB, 0x124C11AC, 0x124D11AD, + 0x124E1094, 0x125B1918, 0x12681919, 0x1275010B, 0x1276010C, 0x1277010D, 0x1278010E, 0x1279010F, + 0x127A0106, 0x127B0107, 0x127C0108, 0x127D0109, 0x127E010A, 0x127F00C3, 0x12800054, 0x12DB0019, + 0x12DC0054, 0x12E00019, 0x12E10054, 0x12FC0019, 0x13000054, 0x13370019, 0x13380054, 0x134E0078, + 0x13500054, 0x13590078, 0x13800054, 0x13820078, 0x13830054, 0x13850078, 0x13860054, 0x13A90078, + 0x13AC0054, 0x13AF0078, 0x13B00054, 0x13B4000A, 0x13BB00C4, 0x13BC00C5, 0x13BD00C6, 0x13BE00C7, + 0x13BF011F, 0x13C000C4, 0x13C100C5, 0x13C200C6, 0x13C300C7, 0x13C4011F, 0x13C500C4, 0x13C600C5, + 0x13C700C6, 0x13C800C7, 0x13C9011F, 0x13CA0078, 0x13CC0054, 0x13DF0078, 0x13E00019, 0x13E100FD, + 0x13E20009, 0x13E30078, 0x13E80019, 0x13E900E8, 0x13EA00FD, 0x13EB0019, 0x13EE00FD, 0x13EF0019, + 0x13F100FE, 0x13F3000A, 0x13F60078, 0x13F80019, 0x14000094, 0x14800019, 0x14C10009, 0x14C601AE, + 0x14C701AF, 0x14C80009, 0x14CC0019, 0x14CD00E8, 0x14D80019, 0x14E000FE, 0x14E100E8, 0x14E200FE, + 0x14E30019, 0x14E400E8, 0x14E50019, 0x14E700FD, 0x14E90019, 0x14EA00FE, 0x14EB0019, 0x14EC000A, + 0x14EE0019, 0x14F000E8, 0x14F30019, 0x14F400E8, 0x14F50019, 0x14FA01B0, 0x14FB00E8, 0x14FC00FE, + 0x14FD0019, 0x14FE000A, 0x14FF0019, 0x150500E8, 0x150E0019, 0x150F00E8, 0x15110019, 0x151400E8, + 0x151500FD, 0x15170019, 0x151A00FE, 0x151B0019, 0x151E00FE, 0x151F0019, 0x152B00E8, 0x152C0019, + 0x153200FE, 0x15330019, 0x153500E8, 0x15380019, 0x153900E8, 0x153A1019, 0x153B0019, 0x153C00FD, + 0x153D00E8, 0x153E00FD, 0x154200E8, 0x154500FD, 0x154600E8, 0x154800FD, 0x154E00E8, 0x155000FD, + 0x155100E8, 0x15520019, 0x155300FE, 0x155700FD, 0x155800E8, 0x155900FD, 0x155A00E8, 0x155D00FD, + 0x156300E8, 0x156600FD, 0x156B0019, 0x157101B1, 0x15730019, 0x157600FE, 0x15770019, 0x157900E8, + 0x157A0019, 0x157B00FD, 0x157D00E8, 0x157F0019, 0x15800054, 0x158A0078, 0x16000096, 0x16170078, + 0x16180098, 0x162F0078, 0x16400065, 0x16720054, 0x16750078, 0x167C0006, 0x167E005F, 0x167F0006, + 0x16800125, 0x16930078, 0x16980071, 0x16B30078, 0x16B77881, 0x16B80078, 0x16C00071, 0x16CB0078, + 0x16D00071, 0x16D30078, 0x16D40071, 0x16D70078, 0x16D80071, 0x16DB0078, 0x16DC0071, 0x16DF0078, + 0x16E00071, 0x16E30078, 0x16E40071, 0x16E70078, 0x16E80071, 0x16EB0078, 0x16EC0071, 0x16EF0078, + 0x17000006, 0x170100E0, 0x17030006, 0x17040126, 0x17050006, 0x170600E0, 0x17070006, 0x170B0099, + 0x170C0078, 0x170E00E0, 0x170F0078, 0x17400054, 0x174F1054, 0x17500054, 0x17791054, 0x177A0078, + 0x17801054, 0x17EB0078, 0x17F80054, 0x17FE0078, 0x18000006, 0x18020081, 0x180301B2, 0x1804000A, + 0x18090054, 0x180A000A, 0x180E00B4, 0x180F00BF, 0x181001B3, 0x181101B4, 0x181201B5, 0x181301B6, + 0x181401B7, 0x18150083, 0x18180081, 0x181B0054, 0x181C11B8, 0x181D0081, 0x181E0006, 0x181F0054, + 0x18200071, 0x18320871, 0x18350071, 0x18380871, 0x183A0071, 0x183B0871, 0x183D0071, 0x183E0871, + 0x183F0071, 0x184B0078, 0x184C0083, 0x184D1037, 0x184E0081, 0x184F8071, 0x18500071, 0x18620871, + 0x18650071, 0x18680871, 0x186A0071, 0x186B0871, 0x186D0071, 0x186E0871, 0x186F0071, 0x187B0871, + 0x187D0006, 0x187E0081, 0x187F8071, 0x18800078, 0x18820071, 0x18960078, 0x18981071, 0x18C70078, + 0x18C80094, 0x18C978B6, 0x18CA78B7, 0x18CB7894, 0x18D00071, 0x18DC0078, 0x18E00054, 0x18E80078, + 0x18F80071, 0x19001094, 0x190E1054, 0x190F0078, 0x191010B6, 0x191110B7, 0x191210B8, 0x191310B9, + 0x191410B0, 0x19151094, 0x19220078, 0x192819B9, 0x192919BA, 0x192A19BB, 0x192B19BC, 0x192C19BD, + 0x192D19BE, 0x192E19BF, 0x192F19C0, 0x19301894, 0x193E1854, 0x193F0094, 0x194018B6, 0x194118B7, + 0x194218B8, 0x194318B9, 0x194418B0, 0x19451894, 0x195819C1, 0x195919C2, 0x195A19C3, 0x195B19C4, + 0x195C19C5, 0x195D19C6, 0x195E19C7, 0x195F19C8, 0x19601094, 0x19666854, 0x19681894, 0x197F0078, + 0x19806894, 0x19AC1094, 0x19B86894, 0x19BB6854, 0x19BD6894, 0x19EF6854, 0x19F01094, 0x19FF6854, + 0x1A000071, 0x26DB0078, 0x26E00054, 0x27000071, 0x4FDE0078, 0x50000071, 0x500A0081, 0x500B0071, + 0x52460078, 0x52480054, 0x52630078, 0x53800037, 0x538B0078, 0x54000071, 0x54050083, 0x54060071, + 0x541100A7, 0x54120083, 0x541300A7, 0x54140054, 0x54160078, 0x56000071, 0x6BD20078, 0x6C00013E, + 0x7000013F, 0x7C800871, 0x7D070071, 0x7D0A0871, 0x7D0F0071, 0x7D120871, 0x7D130071, 0x7D150871, + 0x7D170078, 0x7D180871, 0x7D350078, 0x7D380871, 0x7D6D0078, 0x7D801055, 0x7D830078, 0x7D891055, + 0x7D8C0078, 0x7D8E089B, 0x7D90289B, 0x7D94280B, 0x7D95089B, 0x7D9B0078, 0x7D9C089B, 0x7D9E0078, + 0x7DA0089B, 0x7DA20078, 0x7DA3089B, 0x7DA7109B, 0x7DA8209E, 0x7DAA489E, 0x7DAB209E, 0x7DAC489E, + 0x7DAD209E, 0x7DAE489E, 0x7DAF209E, 0x7DB0489E, 0x7DB1209E, 0x7DB2489E, 0x7DB3209E, 0x7DB4489E, + 0x7DB5209E, 0x7DB6489E, 0x7DB7209E, 0x7DB8489E, 0x7DB9209E, 0x7DBA489E, 0x7DBB209E, 0x7DBC489E, + 0x7DBD209E, 0x7DBE489E, 0x7DBF209E, 0x7DC0489E, 0x7DC1209E, 0x7DC8489E, 0x7DC9209E, 0x7DCA489E, + 0x7DCB209E, 0x7DCC489E, 0x7DCD209E, 0x7DCE489E, 0x7DCF209E, 0x7DD1489E, 0x7DD2209E, 0x7DD4489E, + 0x7DD5209E, 0x7DD6489E, 0x7DD7209E, 0x7DD90078, 0x7DE9409E, 0x7DEA389E, 0x7DEB409E, 0x7DEF209E, + 0x7DF3489E, 0x7DF5209E, 0x7DFC409E, 0x7DFD389E, 0x7DFE209E, 0x7DFF489E, 0x7E00409E, 0x7E32209E, + 0x7E4B389E, 0x7E6F489E, 0x7E7A409E, 0x7E88209E, 0x7E96389E, 0x7E9A489E, 0x7E9E409E, 0x7E9F00BF, + 0x7EA00078, 0x7EA8209E, 0x7EA9389E, 0x7EAD209E, 0x7EAE389E, 0x7EAF209E, 0x7EB0389E, 0x7EB3209E, + 0x7EB5389E, 0x7EB7209E, 0x7EB9389E, 0x7EBA209E, 0x7EBB389E, 0x7EBC209E, 0x7EBE389E, 0x7EBF209E, + 0x7EC1389E, 0x7EC2209E, 0x7EC4389E, 0x7EC5209E, 0x7EC6389E, 0x7EC80078, 0x7EC9389E, 0x7ECB209E, + 0x7ECE389E, 0x7ECF209E, 0x7EDA389E, 0x7EDB209E, 0x7EE1389E, 0x7EE3209E, 0x7EE40078, 0x7EF8409E, + 0x7EFE0054, 0x7EFF0078, 0x7F000083, 0x7F088006, 0x7F0B80B4, 0x7F0C8006, 0x7F0D0078, 0x7F100083, + 0x7F120078, 0x7F188099, 0x7F198038, 0x7F1A80B4, 0x7F220006, 0x7F2380B4, 0x7F241006, 0x7F261038, + 0x7F286006, 0x7F290078, 0x7F2A600C, 0x7F2B6006, 0x7F2C60B4, 0x7F2F6007, 0x7F306006, 0x7F31600D, + 0x7F326019, 0x7F330078, 0x7F346008, 0x7F356006, 0x7F360078, 0x7F38489E, 0x7F39009E, 0x7F3A0078, + 0x7F3B489E, 0x7F40409E, 0x7F45389E, 0x7F46409E, 0x7F48389E, 0x7F49409E, 0x7F4B389E, 0x7F4C409E, + 0x7F4D389E, 0x7F4E409E, 0x7F4F389E, 0x7F50409E, 0x7F51389E, 0x7F52409E, 0x7F53389E, 0x7F54409E, + 0x7F59389E, 0x7F5A409E, 0x7F5B389E, 0x7F5C409E, 0x7F5D389E, 0x7F5E409E, 0x7F5F389E, 0x7F60409E, + 0x7F61389E, 0x7F62409E, 0x7F63389E, 0x7F64409E, 0x7F65389E, 0x7F66409E, 0x7F67389E, 0x7F68409E, + 0x7F69389E, 0x7F6A409E, 0x7F6B389E, 0x7F6C409E, 0x7F6D389E, 0x7F6E409E, 0x7F6F389E, 0x7F70409E, + 0x7F71389E, 0x7F72409E, 0x7F73389E, 0x7F74409E, 0x7F75389E, 0x7F76409E, 0x7F79389E, 0x7F7A409E, + 0x7F7E0078, 0x7F7F0057, 0x7F808806, 0x7F818807, 0x7F838806, 0x7F84880A, 0x7F85880B, 0x7F86880D, + 0x7F87880C, 0x7F88880F, 0x7F898811, 0x7F8A8813, 0x7F8B8815, 0x7F8C8817, 0x7F8D8806, 0x7F8E8819, + 0x7F8F8806, 0x7F908860, 0x7F9D8835, 0x7F9E8836, 0x7F9F8838, 0x7FA08861, 0x7FAD8835, 0x7FAE8836, + 0x7FAF8809, 0x7FB05006, 0x7FB1500A, 0x7FB25006, 0x7FB35071, 0x7FCF5081, 0x7FD05071, 0x7FDF0078, + 0x7FE15071, 0x7FE40078, 0x7FE55071, 0x7FE80078, 0x7FE95071, 0x7FEC0078, 0x7FED5071, 0x7FEE0078, + 0x7FF08808, 0x7FF18837, 0x7FF28808, 0x7FF30078, 0x7FF45019, 0x7FF65054, 0x7FF70078, 0x7FFC0141, + 0x7FFE0054, 0x7FFF0078, 0x80000071, 0x80130078, 0x80140071, 0x801D0078, 0x801E0071, 0x80270078, + 0x80280071, 0x802F0078, 0x80400071, 0x807D0078, 0x80800006, 0x80810078, 0x808300AD, 0x808400AE, + 0x8085012D, 0x8086012E, 0x8087012F, 0x80880185, 0x80890186, 0x808A0187, 0x808B0188, 0x808C0184, + 0x808D01C9, 0x808E01CA, 0x808F01CB, 0x809001CC, 0x809101CD, 0x809201CE, 0x809301CF, 0x809401D0, + 0x809500BE, 0x809601D1, 0x809701D2, 0x809801D3, 0x809901D4, 0x809A0078, 0x809B0094, 0x80A0014E, + 0x80A10152, 0x80A20153, 0x80A30157, 0x80A40154, 0x80A50155, 0x80A60156, 0x80A70152, 0x80A80150, + 0x80A90153, 0x80AA01D5, 0x80AB0154, 0x80AC014F, 0x80AD0158, 0x80AF0152, 0x80B00154, 0x80B201D6, + 0x80B30150, 0x80B501D7, 0x80B60153, 0x80B80156, 0x80B90152, 0x80BA005F, 0x80BC0054, 0x80C50078, + 0x81800071, 0x818F0078, 0x8190012D, 0x819100BB, 0x81920078, 0x81980071, 0x81A50078, 0x81C00071, + 0x81CF0097, 0x81D00071, 0x81E20078, 0x81E40071, 0x81E8014F, 0x81E90154, 0x81EA0155, 0x81EB0078, + 0x8200015B, 0x8214015C, 0x82280071, 0x824F0078, 0x8250017E, 0x8251017F, 0x82520180, 0x82530181, + 0x82540182, 0x82550078, 0x8400009B, 0x84030078, 0x8405009B, 0x841C0078, 0x841F009B, 0x84200078, + 0x85000083, 0x85030078, 0x85060083, 0x8508009B, 0x851A0078, 0x851C0083, 0x851D0078, 0x851F0083, + 0x852001D8, 0x852101D9, 0x852201DA, 0x852301DB, 0x85240078, 0x8528009A, 0x852C0078, 0xE8000094, + 0xE87B0078, 0xE8800094, 0xE8930078, 0xE8950094, 0xE8AF0894, 0xE8B200A7, 0xE8B30083, 0xE8B50094, + 0xE8B600A7, 0xE8B90057, 0xE8BD0083, 0xE8C10094, 0xE8C20083, 0xE8C60094, 0xE8D50083, 0xE8D70094, + 0xE8DD0894, 0xE8E00094, 0xE8EF0078, 0xE9000054, 0xE9210083, 0xE9220054, 0xE9230078, 0xE9800054, + 0xE9AB0078, 0xEA002877, 0xEA0D2855, 0xEA1A2877, 0xEA272855, 0xEA2A0078, 0xEA2B2855, 0xEA342877, + 0xEA412855, 0xEA4E0078, 0xEA4F2877, 0xEA500078, 0xEA522877, 0xEA530078, 0xEA542877, 0xEA560078, + 0xEA572877, 0xEA5B2855, 0xEA682877, 0xEA752855, 0xEA822877, 0xEA850078, 0xEA862877, 0xEA8A0078, + 0xEA8B2877, 0xEA8E0078, 0xEA8F2855, 0xEA9C2877, 0xEA9F0078, 0xEAA02877, 0xEAA20078, 0xEAA52877, + 0xEAA80078, 0xEAA92855, 0xEAB62877, 0xEAC32855, 0xEAD02877, 0xEADD2855, 0xEAEA2877, 0xEAF72855, + 0xEB042877, 0xEB112855, 0xEB1E2877, 0xEB2B2855, 0xEB382877, 0xEB452855, 0xEB530078, 0xEB542877, + 0xEB6029DC, 0xEB612855, 0xEB6D29DC, 0xEB6E2855, 0xEB712877, 0xEB7D29DC, 0xEB7E2855, 0xEB8A29DC, + 0xEB8B2855, 0xEB8E2877, 0xEB9A29DC, 0xEB9B2855, 0xEBA729DC, 0xEBA82855, 0xEBAB2877, 0xEBB729DC, + 0xEBB82855, 0xEBC429DC, 0xEBC52855, 0xEBC82877, 0xEBD429DC, 0xEBD52855, 0xEBE129DC, 0xEBE22855, + 0xEBE50078, 0xEBE7280F, 0xEBE82811, 0xEBE92813, 0xEBEA2815, 0xEBEB2817, 0xEBEC280F, 0xEBED2811, + 0xEBEE2813, 0xEBEF2815, 0xEBF02817, 0xEBF1280F, 0xEBF22811, 0xEBF32813, 0xEBF42815, 0xEBF52817, + 0xEBF6280F, 0xEBF72811, 0xEBF82813, 0xEBF92815, 0xEBFA2817, 0xEBFB280F, 0xEBFC2811, 0xEBFD2813, + 0xEBFE2815, 0xEBFF2817, 0xEC000078 + }; + + static const uint32_t a17[] = { + 0x00000071, 0x536B0078, 0x7C000871, 0x7D0F0078 + }; + + static const uint32_t a23[] = { + 0x00000057, 0x00010078, 0x00100057, 0x00400078, 0x00800083, 0x00F80078, 0x8000013F, 0xFFFF0078 + }; + + static const uint32_t a24[] = { + 0x0000013F, 0x7FFF0078 + }; + + + // The full set of all arrays to be searched. + static const Range FULL_DATA[] = { + {sizeof(a0)/sizeof(uint32_t), a0}, + {sizeof(a1)/sizeof(uint32_t), a1}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {sizeof(a7)/sizeof(uint32_t), a7}, + {sizeof(a8)/sizeof(uint32_t), a8}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {sizeof(a16)/sizeof(uint32_t), a16}, + {sizeof(a17)/sizeof(uint32_t), a17}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {sizeof(a23)/sizeof(uint32_t), a23}, + {sizeof(a24)/sizeof(uint32_t), a24}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0} + }; + + // Array of uppercase differences + static const short UCDIFF[] = { + 0, -32, 743, 121, -1, -232, -300, 97, + 163, 130, 56, -2, -79, -210, -206, -205, + -202, -203, -207, -209, -211, -213, -214, -218, + -217, -219, -83, 84, -38, -37, -31, -64, + -63, -62, -57, -47, -54, -86, -80, 7, + -96, -48, -59, 8, 74, 86, 100, 128, + 112, 126, 9, -7205, -16, -26, -7264, -40 + }; + + // Array of lowercase differences + static const short LCDIFF[] = { + 0, 32, 1, -199, -121, 210, 206, 205, + 79, 202, 203, 207, 211, 209, 213, 214, + 218, 217, 219, 2, -97, -56, -130, -163, + 83, 38, 37, 64, 63, -60, -7, 80, + 48, 7264, -8, -74, -9, -86, -100, -112, + -128, -126, -7517, -8383, -8262, 16, 26, 40 + }; + + // Array of titlecase differences + static const short TCDIFF[] = { + 3, 1, 0, -1 + }; + + // Array of mirrored character differences + static const short MIRROR_DIFF[] = { + 0, 1, -1, 2, -2, 16, -16, 3, + -3, 2016, 138, 1824, 2104, 2108, 2106, -138, + 8, 7, -8, -7, -1824, -2016, -2104, -2106, + -2108 + }; + + // Array of all possible numeric values + static const int NUMERICS[] = { + -1, 0, 1, 2, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, + 23, 24, 25, 26, 27, 28, 29, 30, + 31, 32, 33, 34, 35, -2, 100, 1000, + 40, 50, 60, 70, 80, 90, 10000, 500, + 5000, 36, 37, 38, 39, 41, 42, 43, + 44, 45, 46, 47, 48, 49, 200, 300, + 400, 600, 700, 800, 900, 2000, 3000, 4000, + 6000, 7000, 8000, 9000, 20000, 30000, 40000, 50000, + 60000, 70000, 80000, 90000 + }; + + // All possible packed data values, no duplicates + static const uint32_t PACKED_DATA[] = { + 0x00000000, 0x0000012F, 0x0000016F, 0x0000014F, 0x0000018F, 0x0000018C, 0x000001B8, 0x000000B8, + 0x000000BA, 0x020005B5, 0x040005B6, 0x00000099, 0x000000F8, 0x00000094, 0x02000069, 0x04000069, + 0x06000069, 0x08000069, 0x0A000069, 0x0C000069, 0x0E000069, 0x10000069, 0x12000069, 0x14000069, + 0x060005B9, 0x000001B9, 0x080005B9, 0x16020001, 0x18020001, 0x1A020001, 0x1C020001, 0x1E020001, + 0x20020001, 0x22020001, 0x24020001, 0x26020001, 0x28020001, 0x2A020001, 0x2C020001, 0x2E020001, + 0x30020001, 0x32020001, 0x34020001, 0x36020001, 0x38020001, 0x3A020001, 0x3C020001, 0x3E020001, + 0x40020001, 0x42020001, 0x44020001, 0x46020001, 0x48020001, 0x060005B5, 0x080005B6, 0x000001BB, + 0x000001B7, 0x16000802, 0x18000802, 0x1A000802, 0x1C000802, 0x1E000802, 0x20000802, 0x22000802, + 0x24000802, 0x26000802, 0x28000802, 0x2A000802, 0x2C000802, 0x2E000802, 0x30000802, 0x32000802, + 0x34000802, 0x36000802, 0x38000802, 0x3A000802, 0x3C000802, 0x3E000802, 0x40000802, 0x42000802, + 0x44000802, 0x46000802, 0x48000802, 0x000000EC, 0x000001BC, 0x00000002, 0x0A0005BD, 0x00000130, + 0x000000BC, 0x000000B9, 0x0600006B, 0x0800006B, 0x00001002, 0x0400006B, 0x0C0005BE, 0x4A0001AB, + 0x00020001, 0x00000802, 0x00001802, 0x00040001, 0x00060001, 0x00002002, 0x00080001, 0x000C0001, + 0x000E0001, 0x00100001, 0x00140001, 0x00160001, 0x00180001, 0x00004002, 0x00004802, 0x00200001, + 0x00220001, 0x00000005, 0x00A60001, 0x01805802, 0x01042003, 0x00280001, 0x002C0001, 0x00000001, + 0x00000000, 0x00007002, 0x00007802, 0x00009802, 0x0000A802, 0x0000B802, 0x0000C002, 0x0000C802, + 0x0000D002, 0x00000004, 0x000001A4, 0x00000106, 0x00320001, 0x00340001, 0x00360001, 0x00380001, + 0x0000E002, 0x0000E802, 0x0000F002, 0x0000F802, 0x00010002, 0x00010802, 0x00012002, 0x00012802, + 0x00013802, 0x003A0001, 0x003E0001, 0x00013002, 0x0000001C, 0x00000107, 0x00400001, 0x00000018, + 0x00014802, 0x000001B4, 0x00000038, 0x00000025, 0x00000050, 0x00000058, 0x00000045, 0x00000044, + 0x020000C9, 0x060000C9, 0x0A0000C9, 0x0E0000C9, 0x120000C9, 0x000000D8, 0x0000005C, 0x00000008, + 0x02000009, 0x06000009, 0x0A000009, 0x0E000009, 0x12000009, 0x0400000B, 0x0800000B, 0x0000000B, + 0x1600000B, 0x4E00000B, 0x00000006, 0x4A00000B, 0x000001B5, 0x00420001, 0x0600000B, 0x0A00000B, + 0x0E00000B, 0x1200000B, 0x3E00000B, 0x5200000B, 0x5600000B, 0x5A00000B, 0x5C00000B, 0x000001B6, + 0x2400000A, 0x2800000A, 0x00000010, 0x020001AB, 0x060001AB, 0x0A0001AB, 0x0E0001AB, 0x120001AB, + 0x00000108, 0x00015802, 0x00440001, 0x00016002, 0x00016802, 0x00017002, 0x00017802, 0x00018002, + 0x00018802, 0x00440003, 0x00460001, 0x00480003, 0x00019802, 0x004A0001, 0x004C0001, 0x004E0001, + 0x003C0001, 0x00500001, 0x00520001, 0x000001BD, 0x0000018D, 0x000001D0, 0x00000250, 0x00000230, + 0x040005BE, 0x000000F9, 0x0200006B, 0x0A00006B, 0x0E00006B, 0x1200006B, 0x00540001, 0x00560001, + 0x000005B9, 0x045A000A, 0x085A000A, 0x0C5A000A, 0x105A000A, 0x145A000A, 0x185A000A, 0x525A000A, + 0x5E5A000A, 0x0401A00A, 0x0801A00A, 0x0C01A00A, 0x1001A00A, 0x1401A00A, 0x1801A00A, 0x5201A00A, + 0x5E01A00A, 0x4E00000A, 0x5C00000A, 0x0E0005B9, 0x100005B9, 0x020005B9, 0x040005B9, 0x160005B9, + 0x180005B9, 0x1A0005B9, 0x200005B9, 0x220005B9, 0x240005B9, 0x260005B9, 0x040001AB, 0x080001AB, + 0x0C0001AB, 0x100001AB, 0x140001AB, 0x180001AB, 0x1C0001AB, 0x200001AB, 0x240001AB, 0x280001AB, + 0x0C00006B, 0x1000006B, 0x1400006B, 0x1800006B, 0x1C00006B, 0x2000006B, 0x2400006B, 0x2800006B, + 0x005C001C, 0x0001A81C, 0x1A0001AB, 0x1E0001AB, 0x220001AB, 0x260001AB, 0x2A0001AB, 0x160001AB, + 0x020005B6, 0x100005B6, 0x280005B9, 0x2C0005B9, 0x300005B9, 0x0001B002, 0x020005BD, 0x0600000A, + 0x0A00000A, 0x0E00000A, 0x1200000A, 0x1600000A, 0x3E00000A, 0x0C00000B, 0x1000000B, 0x1400000B, + 0x2E0001AB, 0x320001AB, 0x360001AB, 0x3A0001AB, 0x3E0001AB, 0x420001AB, 0x460001AB, 0x640001AB, + 0x680001AB, 0x6A0001AB, 0x6E0001AB, 0x720001AB, 0x760001AB, 0x7A0001AB, 0x00000013, 0x00000012, + 0x0000005A, 0x000001B0, 0x7C00000B, 0x8000000B, 0x8200000B, 0x8600000B, 0x8C00000B, 0x6000000B, + 0x9200000B, 0x9600000B, 0x9800000B, 0x9C00000B, 0xA000000B, 0xA400000B, 0x4A0001AA, 0x040001AA, + 0x520001AA, 0x600001AA, 0x0C0001AA, 0x5E0001AA, 0x160001AA, 0x4C0001AA, 0x4E0001AA, 0x9E0001AA, + 0x060001AA, 0x8800000A, 0x2A0001AA, 0x005E0001, 0x0001B802, 0x0400002B, 0x0800002B, 0x1600002B, + 0x4C00002B, 0x00002802, 0x00003002, 0x000A0001, 0x00120001, 0x00003802, 0x001A0001, 0x001C0001, + 0x001E0001, 0x00240001, 0x00005002, 0x00006002, 0x002A0001, 0x002E0001, 0x00300001, 0x00006802, + 0x00008002, 0x00008802, 0x00009002, 0x0000A002, 0x0000B002, 0x0000D906, 0x00011002, 0x00011802, + 0x00014002, 0x040000C9, 0x080000C9, 0x0C0000C9, 0x100000C9, 0x140000C9, 0x04000009, 0x08000009, + 0x0C000009, 0x10000009, 0x14000009, 0x2200000B, 0x4C00000B, 0x2A00000B, 0x5000000B, 0x5400000B, + 0x5800000B, 0x2600000A, 0x00015002, 0x00019002, 0x00000030, 0x000001BE, 0x0000014E, 0x00000210, + 0x000001F0, 0x00580001, 0x065A000A, 0x0A5A000A, 0x0E5A000A, 0x125A000A, 0x165A000A, 0x1A5A000A, + 0x4C5A000A, 0x4E5A000A, 0x0601A00A, 0x0A01A00A, 0x0E01A00A, 0x1201A00A, 0x1601A00A, 0x1A01A00A, + 0x4C01A00A, 0x4E01A00A, 0x6000000A, 0x0000000A, 0x120005B9, 0x140005B9, 0x1C0005B9, 0x1E0005B9, + 0x1600006B, 0x1A00006B, 0x1E00006B, 0x2200006B, 0x2600006B, 0x2A00006B, 0x0E0005B5, 0x040005B5, + 0x2A0005B9, 0x2E0005B9, 0x0200000A, 0x0400000A, 0x0800000A, 0x0C00000A, 0x1000000A, 0x1400000A, + 0x2A00000A, 0x2C0001AB, 0x300001AB, 0x340001AB, 0x380001AB, 0x3C0001AB, 0x400001AB, 0x440001AB, + 0x480001AB, 0x620001AB, 0x660001AB, 0x500001AB, 0x6C0001AB, 0x700001AB, 0x740001AB, 0x780001AB, + 0x520001AB, 0x7E00000B, 0x5E00000B, 0x8400000B, 0x8800000B, 0x8A00000B, 0x8E00000B, 0x9000000B, + 0x9400000B, 0x9A00000B, 0x9E00000B, 0xA200000B, 0xA600000B, 0x5C0001AA, 0x3E0001AA, 0x7E0001AA, + 0x0600002B, 0x0A00002B, 0x2A00002B, 0x4E00002B, 0x00000019 + }; +} diff --git a/libs/utils/executablepath_darwin.cpp b/libs/utils/executablepath_darwin.cpp new file mode 100644 index 000000000..2e3c3a01f --- /dev/null +++ b/libs/utils/executablepath_darwin.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#import +#include + +void executablepath(char s[PATH_MAX]) +{ + ProcessSerialNumber psn; + GetCurrentProcess(&psn); + CFDictionaryRef dict; + dict = ProcessInformationCopyDictionary(&psn, 0xffffffff); + CFStringRef value = (CFStringRef)CFDictionaryGetValue(dict, + CFSTR("CFBundleExecutable")); + CFStringGetCString(value, s, PATH_MAX+1, kCFStringEncodingUTF8); +} + diff --git a/libs/utils/executablepath_linux.cpp b/libs/utils/executablepath_linux.cpp new file mode 100644 index 000000000..b8d2a3d6f --- /dev/null +++ b/libs/utils/executablepath_linux.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include + +void executablepath(char exe[PATH_MAX]) +{ + char proc[100]; + sprintf(proc, "/proc/%d/exe", getpid()); + + int err = readlink(proc, exe, PATH_MAX); +} + diff --git a/libs/utils/futex_synchro.c b/libs/utils/futex_synchro.c new file mode 100644 index 000000000..ba1952033 --- /dev/null +++ b/libs/utils/futex_synchro.c @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include +#include + +#include + +#include + + +// This futex glue code is need on desktop linux, but is already part of bionic. +#if !defined(HAVE_FUTEX_WRAPPERS) + +#include +typedef unsigned int u32; +#define asmlinkage +#define __user +#include +#include + + +int futex (int *uaddr, int op, int val, const struct timespec *timeout, int *uaddr2, int val3) +{ + int err = syscall(SYS_futex, uaddr, op, val, timeout, uaddr2, val3); + return err == 0 ? 0 : -errno; +} + +int __futex_wait(volatile void *ftx, int val, const struct timespec *timeout) +{ + return futex((int*)ftx, FUTEX_WAIT, val, timeout, NULL, 0); +} + +int __futex_wake(volatile void *ftx, int count) +{ + return futex((int*)ftx, FUTEX_WAKE, count, NULL, NULL, 0); +} + +int __atomic_cmpxchg(int old, int _new, volatile int *ptr) +{ + return android_atomic_cmpxchg(old, _new, ptr); +} + +int __atomic_swap(int _new, volatile int *ptr) +{ + return android_atomic_swap(_new, ptr); +} + +int __atomic_dec(volatile int *ptr) +{ + return android_atomic_dec(ptr); +} + +#else // !defined(__arm__) + +int __futex_wait(volatile void *ftx, int val, const struct timespec *timeout); +int __futex_wake(volatile void *ftx, int count); + +int __atomic_cmpxchg(int old, int _new, volatile int *ptr); +int __atomic_swap(int _new, volatile int *ptr); +int __atomic_dec(volatile int *ptr); + +#endif // !defined(HAVE_FUTEX_WRAPPERS) + + +// lock states +// +// 0: unlocked +// 1: locked, no waiters +// 2: locked, maybe waiters + +void futex_mutex_init(futex_mutex_t *m) +{ + m->value = 0; +} + +int futex_mutex_lock(futex_mutex_t *m, unsigned msec) +{ + if(__atomic_cmpxchg(0, 1, &m->value) == 0) { + return 0; + } + if(msec == FUTEX_WAIT_INFINITE) { + while(__atomic_swap(2, &m->value) != 0) { + __futex_wait(&m->value, 2, 0); + } + } else { + struct timespec ts; + ts.tv_sec = msec / 1000; + ts.tv_nsec = (msec % 1000) * 1000000; + while(__atomic_swap(2, &m->value) != 0) { + if(__futex_wait(&m->value, 2, &ts) == -ETIMEDOUT) { + return -1; + } + } + } + return 0; +} + +int futex_mutex_trylock(futex_mutex_t *m) +{ + if(__atomic_cmpxchg(0, 1, &m->value) == 0) { + return 0; + } + return -1; +} + +void futex_mutex_unlock(futex_mutex_t *m) +{ + if(__atomic_dec(&m->value) != 1) { + m->value = 0; + __futex_wake(&m->value, 1); + } +} + +/* XXX *technically* there is a race condition that could allow + * XXX a signal to be missed. If thread A is preempted in _wait() + * XXX after unlocking the mutex and before waiting, and if other + * XXX threads call signal or broadcast UINT_MAX times (exactly), + * XXX before thread A is scheduled again and calls futex_wait(), + * XXX then the signal will be lost. + */ + +void futex_cond_init(futex_cond_t *c) +{ + c->value = 0; +} + +int futex_cond_wait(futex_cond_t *c, futex_mutex_t *m, unsigned msec) +{ + if(msec == FUTEX_WAIT_INFINITE){ + int oldvalue = c->value; + futex_mutex_unlock(m); + __futex_wait(&c->value, oldvalue, 0); + futex_mutex_lock(m, FUTEX_WAIT_INFINITE); + return 0; + } else { + int oldvalue = c->value; + struct timespec ts; + ts.tv_sec = msec / 1000; + ts.tv_nsec = (msec % 1000) * 1000000; + futex_mutex_unlock(m); + const int err = __futex_wait(&c->value, oldvalue, &ts); + futex_mutex_lock(m, FUTEX_WAIT_INFINITE); + return err; + } +} + +void futex_cond_signal(futex_cond_t *c) +{ + __atomic_dec(&c->value); + __futex_wake(&c->value, 1); +} + +void futex_cond_broadcast(futex_cond_t *c) +{ + __atomic_dec(&c->value); + __futex_wake(&c->value, INT_MAX); +} + diff --git a/libs/utils/misc.cpp b/libs/utils/misc.cpp new file mode 100644 index 000000000..dc89d15b6 --- /dev/null +++ b/libs/utils/misc.cpp @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2005 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. + */ + +// +// Miscellaneous utility functions. +// +#include + +#include +#include +#include +#include +#include + +using namespace android; + +namespace android { + +/* + * Like strdup(), but uses C++ "new" operator instead of malloc. + */ +char* strdupNew(const char* str) +{ + char* newStr; + int len; + + if (str == NULL) + return NULL; + + len = strlen(str); + newStr = new char[len+1]; + memcpy(newStr, str, len+1); + + return newStr; +} + +/* + * Concatenate an argument vector. + */ +char* concatArgv(int argc, const char* const argv[]) +{ + char* newStr = NULL; + int len, totalLen, posn, idx; + + /* + * First, figure out the total length. + */ + totalLen = idx = 0; + while (1) { + if (idx == argc || argv[idx] == NULL) + break; + if (idx) + totalLen++; // leave a space between args + totalLen += strlen(argv[idx]); + idx++; + } + + /* + * Alloc the string. + */ + newStr = new char[totalLen +1]; + if (newStr == NULL) + return NULL; + + /* + * Finally, allocate the string and copy data over. + */ + idx = posn = 0; + while (1) { + if (idx == argc || argv[idx] == NULL) + break; + if (idx) + newStr[posn++] = ' '; + + len = strlen(argv[idx]); + memcpy(&newStr[posn], argv[idx], len); + posn += len; + + idx++; + } + + assert(posn == totalLen); + newStr[posn] = '\0'; + + return newStr; +} + +/* + * Count the #of args in an argument vector. Don't count the final NULL. + */ +int countArgv(const char* const argv[]) +{ + int count = 0; + + while (argv[count] != NULL) + count++; + + return count; +} + + +#include +/* + * Get a file's type. + */ +FileType getFileType(const char* fileName) +{ + struct stat sb; + + if (stat(fileName, &sb) < 0) { + if (errno == ENOENT || errno == ENOTDIR) + return kFileTypeNonexistent; + else { + fprintf(stderr, "getFileType got errno=%d on '%s'\n", + errno, fileName); + return kFileTypeUnknown; + } + } else { + if (S_ISREG(sb.st_mode)) + return kFileTypeRegular; + else if (S_ISDIR(sb.st_mode)) + return kFileTypeDirectory; + else if (S_ISCHR(sb.st_mode)) + return kFileTypeCharDev; + else if (S_ISBLK(sb.st_mode)) + return kFileTypeBlockDev; + else if (S_ISFIFO(sb.st_mode)) + return kFileTypeFifo; +#ifdef HAVE_SYMLINKS + else if (S_ISLNK(sb.st_mode)) + return kFileTypeSymlink; + else if (S_ISSOCK(sb.st_mode)) + return kFileTypeSocket; +#endif + else + return kFileTypeUnknown; + } +} + +/* + * Get a file's modification date. + */ +time_t getFileModDate(const char* fileName) +{ + struct stat sb; + + if (stat(fileName, &sb) < 0) + return (time_t) -1; + + return sb.st_mtime; +} + +/* + * Round up to the next highest power of 2. + * + * Found on http://graphics.stanford.edu/~seander/bithacks.html. + */ +unsigned int roundUpPower2(unsigned int val) +{ + val--; + val |= val >> 1; + val |= val >> 2; + val |= val >> 4; + val |= val >> 8; + val |= val >> 16; + val++; + + return val; +} + +}; // namespace android + diff --git a/libs/utils/ported.cpp b/libs/utils/ported.cpp new file mode 100644 index 000000000..656e46f09 --- /dev/null +++ b/libs/utils/ported.cpp @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2005 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. + */ + +// +// Ports of standard functions that don't exist on a specific platform. +// +// Note these are NOT in the "android" namespace. +// +#include + +#if defined(NEED_GETTIMEOFDAY) || defined(NEED_USLEEP) +# include +# include +#endif + + +#if defined(NEED_GETTIMEOFDAY) +/* + * Replacement gettimeofday() for Windows environments (primarily MinGW). + * + * Ignores "tz". + */ +int gettimeofday(struct timeval* ptv, struct timezone* tz) +{ + long long nsTime; // time in 100ns units since Jan 1 1601 + FILETIME ft; + + if (tz != NULL) { + // oh well + } + + ::GetSystemTimeAsFileTime(&ft); + nsTime = (long long) ft.dwHighDateTime << 32 | + (long long) ft.dwLowDateTime; + // convert to time in usec since Jan 1 1970 + ptv->tv_usec = (long) ((nsTime / 10LL) % 1000000LL); + ptv->tv_sec = (long) ((nsTime - 116444736000000000LL) / 10000000LL); + + return 0; +} +#endif + +#if defined(NEED_USLEEP) +// +// Replacement usleep for Windows environments (primarily MinGW). +// +void usleep(unsigned long usec) +{ + // Win32 API function Sleep() takes milliseconds + ::Sleep((usec + 500) / 1000); +} +#endif + +#if 0 //defined(NEED_PIPE) +// +// Replacement pipe() command for MinGW +// +// The _O_NOINHERIT flag sets bInheritHandle to FALSE in the +// SecurityAttributes argument to CreatePipe(). This means the handles +// aren't inherited when a new process is created. The examples I've seen +// use it, possibly because there's a lot of junk going on behind the +// scenes. (I'm assuming "process" and "thread" are different here, so +// we should be okay spinning up a thread.) The recommended practice is +// to dup() the descriptor you want the child to have. +// +// It appears that unnamed pipes can't do non-blocking ("overlapped") I/O. +// You can't use select() either, since that only works on sockets. The +// Windows API calls that are useful here all operate on a HANDLE, not +// an integer file descriptor, and I don't think you can get there from +// here. The "named pipe" stuff is insane. +// +int pipe(int filedes[2]) +{ + return _pipe(filedes, 0, _O_BINARY | _O_NOINHERIT); +} +#endif + +#if defined(NEED_SETENV) +/* + * MinGW lacks these. For now, just stub them out so the code compiles. + */ +int setenv(const char* name, const char* value, int overwrite) +{ + return 0; +} +void unsetenv(const char* name) +{ +} +char* getenv(const char* name) +{ + return NULL; +} +#endif From 7a4c83922e551506fdc6c5fbca6d107ebdeef867 Mon Sep 17 00:00:00 2001 From: The Android Open Source Project Date: Thu, 5 Mar 2009 14:34:35 -0800 Subject: [PATCH 080/541] auto import from //depot/cupcake/@136594 --- include/utils/threads.h | 35 ------ libs/utils/CallStack.cpp | 25 +++- libs/utils/ResourceTypes.cpp | 14 ++- libs/utils/String16.cpp | 2 +- libs/utils/Threads.cpp | 226 ++++++++--------------------------- 5 files changed, 78 insertions(+), 224 deletions(-) diff --git a/include/utils/threads.h b/include/utils/threads.h index 7dca81004..8d8d46a05 100644 --- a/include/utils/threads.h +++ b/include/utils/threads.h @@ -248,41 +248,6 @@ private: }; -/* - * Read/write lock. The resource can have multiple readers or one writer, - * but can't be read and written at the same time. - * - * The same thread should not call a lock function while it already has - * a lock. (Should be okay for multiple readers.) - */ -class ReadWriteLock { -public: - ReadWriteLock() - : mNumReaders(0), mNumWriters(0) - {} - ~ReadWriteLock() {} - - void lockForRead(); - bool tryLockForRead(); - void unlockForRead(); - - void lockForWrite(); - bool tryLockForWrite(); - void unlockForWrite(); - -private: - int mNumReaders; - int mNumWriters; - - Mutex mLock; - Condition mReadWaiter; - Condition mWriteWaiter; -#if defined(PRINT_RENDER_TIMES) - DurationTimer mDebugTimer; -#endif -}; - - /* * This is our spiffy thread object! */ diff --git a/libs/utils/CallStack.cpp b/libs/utils/CallStack.cpp index 26fb22abc..2fdaa7118 100644 --- a/libs/utils/CallStack.cpp +++ b/libs/utils/CallStack.cpp @@ -120,13 +120,18 @@ class MapInfo { char name[]; }; - const char *map_to_name(uint64_t pc, const char* def) { + const char *map_to_name(uint64_t pc, const char* def, uint64_t* start) { mapinfo* mi = getMapInfoList(); while(mi) { - if ((pc >= mi->start) && (pc < mi->end)) + if ((pc >= mi->start) && (pc < mi->end)) { + if (start) + *start = mi->start; return mi->name; + } mi = mi->next; } + if (start) + *start = 0; return def; } @@ -183,8 +188,15 @@ public: } } - static const char *mapAddressToName(const void* pc, const char* def) { - return sMapInfo.map_to_name((uint64_t)pc, def); + static const char *mapAddressToName(const void* pc, const char* def, + void const** start) + { + uint64_t s; + char const* name = sMapInfo.map_to_name(uint64_t(uintptr_t(pc)), def, &s); + if (start) { + *start = (void*)s; + } + return name; } }; @@ -297,8 +309,9 @@ String8 CallStack::toStringSingleLevel(const char* prefix, int32_t level) const res.append(name); res.append(tmp2); } else { - name = MapInfo::mapAddressToName(ip, ""); - snprintf(tmp, 256, "pc %p %s", ip, name); + void const* start = 0; + name = MapInfo::mapAddressToName(ip, "", &start); + snprintf(tmp, 256, "pc %08lx %s", uintptr_t(ip)-uintptr_t(start), name); res.append(tmp); } res.append("\n"); diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp index 71e7cd722..2ad3bfe8b 100644 --- a/libs/utils/ResourceTypes.cpp +++ b/libs/utils/ResourceTypes.cpp @@ -176,7 +176,9 @@ size_t Res_png_9patch::serializedSize() void* Res_png_9patch::serialize() { - void* newData = malloc(serializedSize()); + // Use calloc since we're going to leave a few holes in the data + // and want this to run cleanly under valgrind + void* newData = calloc(1, serializedSize()); serialize(newData); return newData; } @@ -3150,13 +3152,13 @@ bool ResTable::stringToValue(Res_value* outValue, String16* outString, const char16_t* pos = s; while (pos < end && !failed) { const char16_t* start = pos; - end++; + pos++; while (pos < end && *pos != '|') { pos++; } - //printf("Looking for: %s\n", String8(start, pos-start).string()); + //printf("Looking for: %s\n", String8(start, pos-start).string()); const bag_entry* bagi = bag; - ssize_t i; + ssize_t i; for (i=0; imap.name.ident)) { //printf("Trying attr #%08x\n", bagi->map.name.ident); @@ -3184,7 +3186,7 @@ bool ResTable::stringToValue(Res_value* outValue, String16* outString, } unlockBag(bag); if (!failed) { - //printf("Final flag value: 0x%lx\n", outValue->data); + //printf("Final flag value: 0x%lx\n", outValue->data); return true; } } @@ -3192,7 +3194,7 @@ bool ResTable::stringToValue(Res_value* outValue, String16* outString, if (fromAccessor) { if (accessor->getAttributeFlags(attrID, s, len, outValue)) { - //printf("Final flag value: 0x%lx\n", outValue->data); + //printf("Final flag value: 0x%lx\n", outValue->data); return true; } } diff --git a/libs/utils/String16.cpp b/libs/utils/String16.cpp index 1f81cadb7..ae0ae1e09 100644 --- a/libs/utils/String16.cpp +++ b/libs/utils/String16.cpp @@ -388,7 +388,7 @@ status_t String16::setTo(const char16_t* other, size_t len) ->editResize((len+1)*sizeof(char16_t)); if (buf) { char16_t* str = (char16_t*)buf->data(); - memcpy(str, other, len*sizeof(char16_t)); + memmove(str, other, len*sizeof(char16_t)); str[len] = 0; mString = str; return NO_ERROR; diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp index 5f407a990..9287c0bba 100644 --- a/libs/utils/Threads.cpp +++ b/libs/utils/Threads.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +// #define LOG_NDEBUG 0 #define LOG_TAG "libutils.threads" #include @@ -838,148 +839,6 @@ void Condition::broadcast() #error "condition variables not supported on this platform" #endif - -/* - * =========================================================================== - * ReadWriteLock class - * =========================================================================== - */ - -#if 0 -#pragma mark - -#pragma mark ReadWriteLock -#endif - -/* - * Add a reader. Readers are nice. They share. - */ -void ReadWriteLock::lockForRead() -{ - mLock.lock(); - while (mNumWriters > 0) { - LOG(LOG_DEBUG, "thread", "+++ lockForRead: waiting\n"); - mReadWaiter.wait(mLock); - } - assert(mNumWriters == 0); - mNumReaders++; -#if defined(PRINT_RENDER_TIMES) - if (mNumReaders == 1) - mDebugTimer.start(); -#endif - mLock.unlock(); -} - -/* - * Try to add a reader. If it doesn't work right away, return "false". - */ -bool ReadWriteLock::tryLockForRead() -{ - mLock.lock(); - if (mNumWriters > 0) { - mLock.unlock(); - return false; - } - assert(mNumWriters == 0); - mNumReaders++; -#if defined(PRINT_RENDER_TIMES) - if (mNumReaders == 1) - mDebugTimer.start(); -#endif - mLock.unlock(); - return true; -} - -/* - * Remove a reader. - */ -void ReadWriteLock::unlockForRead() -{ - mLock.lock(); - if (mNumReaders == 0) { - mLock.unlock(); - LOG(LOG_WARN, "thread", - "WARNING: unlockForRead requested, but not locked\n"); - return; - } - assert(mNumReaders > 0); - assert(mNumWriters == 0); - mNumReaders--; - if (mNumReaders == 0) { // last reader? -#if defined(PRINT_RENDER_TIMES) - mDebugTimer.stop(); - printf(" rdlk held %.3f msec\n", - (double) mDebugTimer.durationUsecs() / 1000.0); -#endif - //printf("+++ signaling writers (if any)\n"); - mWriteWaiter.signal(); // wake one writer (if any) - } - mLock.unlock(); -} - -/* - * Add a writer. This requires exclusive access to the object. - */ -void ReadWriteLock::lockForWrite() -{ - mLock.lock(); - while (mNumReaders > 0 || mNumWriters > 0) { - LOG(LOG_DEBUG, "thread", "+++ lockForWrite: waiting\n"); - mWriteWaiter.wait(mLock); - } - assert(mNumReaders == 0); - assert(mNumWriters == 0); - mNumWriters++; -#if defined(PRINT_RENDER_TIMES) - mDebugTimer.start(); -#endif - mLock.unlock(); -} - -/* - * Try to add a writer. If it doesn't work right away, return "false". - */ -bool ReadWriteLock::tryLockForWrite() -{ - mLock.lock(); - if (mNumReaders > 0 || mNumWriters > 0) { - mLock.unlock(); - return false; - } - assert(mNumReaders == 0); - assert(mNumWriters == 0); - mNumWriters++; -#if defined(PRINT_RENDER_TIMES) - mDebugTimer.start(); -#endif - mLock.unlock(); - return true; -} - -/* - * Remove a writer. - */ -void ReadWriteLock::unlockForWrite() -{ - mLock.lock(); - if (mNumWriters == 0) { - mLock.unlock(); - LOG(LOG_WARN, "thread", - "WARNING: unlockForWrite requested, but not locked\n"); - return; - } - assert(mNumWriters == 1); - mNumWriters--; -#if defined(PRINT_RENDER_TIMES) - mDebugTimer.stop(); - //printf(" wrlk held %.3f msec\n", - // (double) mDebugTimer.durationUsecs() / 1000.0); -#endif - mWriteWaiter.signal(); // should other writers get first dibs? - //printf("+++ signaling readers (if any)\n"); - mReadWaiter.broadcast(); // wake all readers (if any) - mLock.unlock(); -} - // ---------------------------------------------------------------------------- #if 0 @@ -1027,6 +886,8 @@ status_t Thread::run(const char* name, int32_t priority, size_t stack) // hold a strong reference on ourself mHoldSelf = this; + mRunning = true; + bool res; if (mCanCallJava) { res = createThreadEtc(_threadLoop, @@ -1040,14 +901,16 @@ status_t Thread::run(const char* name, int32_t priority, size_t stack) mStatus = UNKNOWN_ERROR; // something happened! mRunning = false; mThread = thread_id_t(-1); + mHoldSelf.clear(); // "this" may have gone away after this. + + return UNKNOWN_ERROR; } - if (mStatus < 0) { - // something happened, don't leak - mHoldSelf.clear(); - } - - return mStatus; + // Do not refer to mStatus here: The thread is already running (may, in fact + // already have exited with a valid mStatus result). The NO_ERROR indication + // here merely indicates successfully starting the thread and does not + // imply successful termination/execution. + return NO_ERROR; } int Thread::_threadLoop(void* user) @@ -1057,20 +920,32 @@ int Thread::_threadLoop(void* user) wp weak(strong); self->mHoldSelf.clear(); - // we're about to run... - self->mStatus = self->readyToRun(); - if (self->mStatus!=NO_ERROR || self->mExitPending) { - // pretend the thread never started... - self->mExitPending = false; - self->mRunning = false; - return 0; - } - - // thread is running now - self->mRunning = true; + bool first = true; do { - bool result = self->threadLoop(); + bool result; + if (first) { + first = false; + self->mStatus = self->readyToRun(); + result = (self->mStatus == NO_ERROR); + + if (result && !self->mExitPending) { + // Binder threads (and maybe others) rely on threadLoop + // running at least once after a successful ::readyToRun() + // (unless, of course, the thread has already been asked to exit + // at that point). + // This is because threads are essentially used like this: + // (new ThreadSubclass())->run(); + // The caller therefore does not retain a strong reference to + // the thread and the thread would simply disappear after the + // successful ::readyToRun() call instead of entering the + // threadLoop at least once. + result = self->threadLoop(); + } + } else { + result = self->threadLoop(); + } + if (result == false || self->mExitPending) { self->mExitPending = true; self->mLock.lock(); @@ -1097,24 +972,23 @@ void Thread::requestExit() status_t Thread::requestExitAndWait() { - if (mStatus == OK) { + if (mThread == getThreadId()) { + LOGW( + "Thread (this=%p): don't call waitForExit() from this " + "Thread object's thread. It's a guaranteed deadlock!", + this); - if (mThread == getThreadId()) { - LOGW( - "Thread (this=%p): don't call waitForExit() from this " - "Thread object's thread. It's a guaranteed deadlock!", - this); - return WOULD_BLOCK; - } - - requestExit(); - - Mutex::Autolock _l(mLock); - while (mRunning == true) { - mThreadExitedCondition.wait(mLock); - } - mExitPending = false; + return WOULD_BLOCK; } + + requestExit(); + + Mutex::Autolock _l(mLock); + while (mRunning == true) { + mThreadExitedCondition.wait(mLock); + } + mExitPending = false; + return mStatus; } From ae31e705de96c769333652d187a896460d27320b Mon Sep 17 00:00:00 2001 From: The Android Open Source Project Date: Wed, 11 Mar 2009 12:11:56 -0700 Subject: [PATCH 081/541] auto import from //branches/cupcake/...@137873 --- libs/utils/String16.cpp | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/libs/utils/String16.cpp b/libs/utils/String16.cpp index ae0ae1e09..aef67f22a 100644 --- a/libs/utils/String16.cpp +++ b/libs/utils/String16.cpp @@ -244,7 +244,6 @@ void terminate_string16() // --------------------------------------------------------------------------- -// Note: not dealing with generating surrogate pairs. static char16_t* allocFromUTF8(const char* in, size_t len) { if (len == 0) return getEmptyString(); @@ -255,7 +254,10 @@ static char16_t* allocFromUTF8(const char* in, size_t len) while (p < end) { chars++; - p += utf8_char_len(*p); + int utf8len = utf8_char_len(*p); + uint32_t codepoint = utf8_to_utf32((const uint8_t*)p, utf8len); + if (codepoint > 0xFFFF) chars++; // this will be a surrogate pair in utf16 + p += utf8len; } SharedBuffer* buf = SharedBuffer::alloc((chars+1)*sizeof(char16_t)); @@ -265,7 +267,19 @@ static char16_t* allocFromUTF8(const char* in, size_t len) char16_t* d = str; while (p < end) { size_t len = utf8_char_len(*p); - *d++ = (char16_t)utf8_to_utf32((const uint8_t*)p, len); + uint32_t codepoint = utf8_to_utf32((const uint8_t*)p, len); + + // Convert the UTF32 codepoint to one or more UTF16 codepoints + if (codepoint <= 0xFFFF) { + // Single UTF16 character + *d++ = (char16_t) codepoint; + } else { + // Multiple UTF16 characters with surrogates + codepoint = codepoint - 0x10000; + *d++ = (char16_t) ((codepoint >> 10) + 0xD800); + *d++ = (char16_t) ((codepoint & 0x3FF) + 0xDC00); + } + p += len; } *d = 0; From 4e3ea4acea9cf4ddc1706003831c8ae4943184be Mon Sep 17 00:00:00 2001 From: The Android Open Source Project Date: Wed, 18 Mar 2009 17:39:46 -0700 Subject: [PATCH 082/541] auto import from //branches/cupcake_rel/...@140373 --- include/utils/ResourceTypes.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h index 7d3fcf2a8..d01d83f83 100644 --- a/include/utils/ResourceTypes.h +++ b/include/utils/ResourceTypes.h @@ -814,7 +814,7 @@ struct ResTable_config ORIENTATION_ANY = 0x0000, ORIENTATION_PORT = 0x0001, ORIENTATION_LAND = 0x0002, - ORIENTATION_SQUARE = 0x0002, + ORIENTATION_SQUARE = 0x0003, }; enum { From 6b35f970ea136a115120ddb55a119f80b151308f Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Fri, 10 Apr 2009 14:24:30 -0700 Subject: [PATCH 083/541] Integrate from //sandbox/mathias/donut/...@145728 SurfaceFlinger rework for new EGL driver model support. --- include/utils/Parcel.h | 18 ++++++------ include/utils/Singleton.h | 59 +++++++++++++++++++++++++++++++++++++++ libs/utils/Parcel.cpp | 40 ++++++++------------------ 3 files changed, 80 insertions(+), 37 deletions(-) create mode 100644 include/utils/Singleton.h diff --git a/include/utils/Parcel.h b/include/utils/Parcel.h index 9087c4465..af1490a02 100644 --- a/include/utils/Parcel.h +++ b/include/utils/Parcel.h @@ -80,8 +80,11 @@ public: status_t writeStrongBinder(const sp& val); status_t writeWeakBinder(const wp& val); - // doesn't take ownership of the native_handle - status_t writeNativeHandle(const native_handle& handle); + // Place a native_handle into the parcel (the native_handle's file- + // descriptors are dup'ed, so it is safe to delete the native_handle + // when this function returns). + // Doesn't take ownership of the native_handle. + status_t writeNativeHandle(const native_handle* handle); // Place a file descriptor into the parcel. The given fd must remain // valid for the lifetime of the parcel. @@ -114,12 +117,11 @@ public: wp readWeakBinder() const; - // if alloc is NULL, native_handle is allocated with malloc(), otherwise - // alloc is used. If the function fails, the effects of alloc() must be - // reverted by the caller. - native_handle* readNativeHandle( - native_handle* (*alloc)(void* cookie, int numFds, int ints), - void* cookie) const; + // Retrieve native_handle from the parcel. This returns a copy of the + // parcel's native_handle (the caller takes ownership). The caller + // must free the native_handle with native_handle_close() and + // native_handle_delete(). + native_handle* readNativeHandle() const; // Retrieve a file descriptor from the parcel. This returns the raw fd diff --git a/include/utils/Singleton.h b/include/utils/Singleton.h new file mode 100644 index 000000000..776f93bb7 --- /dev/null +++ b/include/utils/Singleton.h @@ -0,0 +1,59 @@ +/* + * 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. + */ + +#ifndef ANDROID_UTILS_SINGLETON_H +#define ANDROID_UTILS_SINGLETON_H + +#include +#include +#include + +namespace android { +// --------------------------------------------------------------------------- + +template +class Singleton +{ +public: + static TYPE& getInstance() { + Mutex::Autolock _l(sLock); + TYPE* instance = sInstance; + if (instance == 0) { + instance = new TYPE(); + sInstance = instance; + } + return *instance; + } + +protected: + ~Singleton() { }; + Singleton() { }; + +private: + Singleton(const Singleton&); + Singleton& operator = (const Singleton&); + static Mutex sLock; + static TYPE* sInstance; +}; + +template Mutex Singleton::sLock; +template TYPE* Singleton::sInstance(0); + +// --------------------------------------------------------------------------- +}; // namespace android + +#endif // ANDROID_UTILS_SINGLETON_H + diff --git a/libs/utils/Parcel.cpp b/libs/utils/Parcel.cpp index 0f4b64730..e74ad4ad8 100644 --- a/libs/utils/Parcel.cpp +++ b/libs/utils/Parcel.cpp @@ -650,28 +650,26 @@ status_t Parcel::writeWeakBinder(const wp& val) return flatten_binder(ProcessState::self(), val, this); } -status_t Parcel::writeNativeHandle(const native_handle& handle) +status_t Parcel::writeNativeHandle(const native_handle* handle) { - if (handle.version != sizeof(native_handle)) + if (handle->version != sizeof(native_handle)) return BAD_TYPE; status_t err; - err = writeInt32(handle.numFds); + err = writeInt32(handle->numFds); if (err != NO_ERROR) return err; - err = writeInt32(handle.numInts); + err = writeInt32(handle->numInts); if (err != NO_ERROR) return err; - for (int i=0 ; err==NO_ERROR && inumFds ; i++) + err = writeDupFileDescriptor(handle->data[i]); if (err != NO_ERROR) { LOGD("write native handle, write dup fd failed"); return err; } - - err = write(handle.data + handle.numFds, sizeof(int)*handle.numInts); - + err = write(handle->data + handle->numFds, sizeof(int)*handle->numInts); return err; } @@ -928,7 +926,7 @@ wp Parcel::readWeakBinder() const } -native_handle* Parcel::readNativeHandle(native_handle* (*alloc)(void*, int, int), void* cookie) const +native_handle* Parcel::readNativeHandle() const { int numFds, numInts; status_t err; @@ -937,31 +935,15 @@ native_handle* Parcel::readNativeHandle(native_handle* (*alloc)(void*, int, int) err = readInt32(&numInts); if (err != NO_ERROR) return 0; - native_handle* h; - if (alloc == 0) { - size_t size = sizeof(native_handle) + sizeof(int)*(numFds + numInts); - h = (native_handle*)malloc(size); - h->version = sizeof(native_handle); - h->numFds = numFds; - h->numInts = numInts; - } else { - h = alloc(cookie, numFds, numInts); - if (h->version != sizeof(native_handle)) { - return 0; - } - } - + native_handle* h = native_handle_create(numFds, numInts); for (int i=0 ; err==NO_ERROR && idata[i] = dup(readFileDescriptor()); if (h->data[i] < 0) err = BAD_VALUE; } - err = read(h->data + numFds, sizeof(int)*numInts); - if (err != NO_ERROR) { - if (alloc == 0) { - free(h); - } + native_handle_close(h); + native_handle_delete(h); h = 0; } return h; From cf89aa42ef888ba1f9787d6bec96394bcb529604 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Fri, 10 Apr 2009 20:27:25 -0700 Subject: [PATCH 084/541] fix KeyedVector::replaceValueAt, which wouldn't even compile if used. --- include/utils/KeyedVector.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/utils/KeyedVector.h b/include/utils/KeyedVector.h index f4513ee20..6bcdea4ff 100644 --- a/include/utils/KeyedVector.h +++ b/include/utils/KeyedVector.h @@ -164,7 +164,7 @@ ssize_t KeyedVector::replaceValueFor(const KEY& key, const VALUE& val template inline ssize_t KeyedVector::replaceValueAt(size_t index, const VALUE& item) { if (index Date: Wed, 15 Apr 2009 18:34:24 -0700 Subject: [PATCH 085/541] fix a rookie mistake causing Singleton<> to be a "multiton". Also improve the BufferMapper's debugging, but turn it off. Squashed commit of the following: commit 04e9cae7f806bd65f2cfe35c011b47a36773bbe5 Author: Mathias Agopian Date: Wed Apr 15 18:30:30 2009 -0700 fix and improve BufferMapper's tracking of mapped buffers. commit 1a8deaed15811092b2349cc3c40cafb5f722046c Author: Mathias Agopian Date: Wed Apr 15 00:52:02 2009 -0700 fix some bugs with the Singleton<> class. untested. commit ed01cc06ad70cf640ce1258f01189cb1a96fd3a8 Author: Mathias Agopian Date: Tue Apr 14 19:29:25 2009 -0700 some work to debug the Singleton<> template. --- include/utils/Singleton.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/include/utils/Singleton.h b/include/utils/Singleton.h index 776f93bb7..ee07df11f 100644 --- a/include/utils/Singleton.h +++ b/include/utils/Singleton.h @@ -49,9 +49,6 @@ private: static TYPE* sInstance; }; -template Mutex Singleton::sLock; -template TYPE* Singleton::sInstance(0); - // --------------------------------------------------------------------------- }; // namespace android From 592392f8164e459ecc417b4eca9c7bba551f05d7 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Mon, 20 Apr 2009 12:48:39 -0700 Subject: [PATCH 086/541] AI 146964: tabs -> spaces Automated import of CL 146964 --- libs/utils/ZipFile.cpp | 182 ++++++++++++++++++++--------------------- 1 file changed, 91 insertions(+), 91 deletions(-) diff --git a/libs/utils/ZipFile.cpp b/libs/utils/ZipFile.cpp index 89aa874b4..2132d2224 100644 --- a/libs/utils/ZipFile.cpp +++ b/libs/utils/ZipFile.cpp @@ -94,10 +94,10 @@ status_t ZipFile::open(const char* zipFileName, int flags) } mZipFp = fopen(zipFileName, openflags); if (mZipFp == NULL) { - int err = errno; - LOGD("fopen failed: %d\n", err); + int err = errno; + LOGD("fopen failed: %d\n", err); return errnoToStatus(err); - } + } status_t result; if (!newArchive) { @@ -221,8 +221,8 @@ status_t ZipFile::readCentralDir(void) buf = new unsigned char[EndOfCentralDir::kMaxEOCDSearch]; if (buf == NULL) { - LOGD("Failure allocating %d bytes for EOCD search", - EndOfCentralDir::kMaxEOCDSearch); + LOGD("Failure allocating %d bytes for EOCD search", + EndOfCentralDir::kMaxEOCDSearch); result = NO_MEMORY; goto bail; } @@ -235,7 +235,7 @@ status_t ZipFile::readCentralDir(void) readAmount = (long) fileLength; } if (fseek(mZipFp, seekStart, SEEK_SET) != 0) { - LOGD("Failure seeking to end of zip at %ld", (long) seekStart); + LOGD("Failure seeking to end of zip at %ld", (long) seekStart); result = UNKNOWN_ERROR; goto bail; } @@ -265,9 +265,9 @@ status_t ZipFile::readCentralDir(void) /* extract eocd values */ result = mEOCD.readBuf(buf + i, readAmount - i); if (result != NO_ERROR) { - LOGD("Failure reading %ld bytes of EOCD values", readAmount - i); + LOGD("Failure reading %ld bytes of EOCD values", readAmount - i); goto bail; - } + } //mEOCD.dump(); if (mEOCD.mDiskNumber != 0 || mEOCD.mDiskWithCentralDir != 0 || @@ -293,8 +293,8 @@ status_t ZipFile::readCentralDir(void) * we're hoping to preserve. */ if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) { - LOGD("Failure seeking to central dir offset %ld\n", - mEOCD.mCentralDirOffset); + LOGD("Failure seeking to central dir offset %ld\n", + mEOCD.mCentralDirOffset); result = UNKNOWN_ERROR; goto bail; } @@ -743,61 +743,61 @@ status_t ZipFile::compressFpToFp(FILE* dstFp, FILE* srcFp, const void* data, size_t size, unsigned long* pCRC32) { status_t result = NO_ERROR; - const size_t kBufSize = 32768; - unsigned char* inBuf = NULL; - unsigned char* outBuf = NULL; - z_stream zstream; + const size_t kBufSize = 32768; + unsigned char* inBuf = NULL; + unsigned char* outBuf = NULL; + z_stream zstream; bool atEof = false; // no feof() aviailable yet - unsigned long crc; - int zerr; + unsigned long crc; + int zerr; - /* - * Create an input buffer and an output buffer. - */ - inBuf = new unsigned char[kBufSize]; - outBuf = new unsigned char[kBufSize]; - if (inBuf == NULL || outBuf == NULL) { - result = NO_MEMORY; - goto bail; - } + /* + * Create an input buffer and an output buffer. + */ + inBuf = new unsigned char[kBufSize]; + outBuf = new unsigned char[kBufSize]; + if (inBuf == NULL || outBuf == NULL) { + result = NO_MEMORY; + goto bail; + } - /* - * Initialize the zlib stream. - */ - memset(&zstream, 0, sizeof(zstream)); - zstream.zalloc = Z_NULL; - zstream.zfree = Z_NULL; - zstream.opaque = Z_NULL; - zstream.next_in = NULL; - zstream.avail_in = 0; - zstream.next_out = outBuf; - zstream.avail_out = kBufSize; - zstream.data_type = Z_UNKNOWN; + /* + * Initialize the zlib stream. + */ + memset(&zstream, 0, sizeof(zstream)); + zstream.zalloc = Z_NULL; + zstream.zfree = Z_NULL; + zstream.opaque = Z_NULL; + zstream.next_in = NULL; + zstream.avail_in = 0; + zstream.next_out = outBuf; + zstream.avail_out = kBufSize; + zstream.data_type = Z_UNKNOWN; - zerr = deflateInit2(&zstream, Z_BEST_COMPRESSION, - Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY); - if (zerr != Z_OK) { - result = UNKNOWN_ERROR; - if (zerr == Z_VERSION_ERROR) { - LOGE("Installed zlib is not compatible with linked version (%s)\n", - ZLIB_VERSION); - } else { - LOGD("Call to deflateInit2 failed (zerr=%d)\n", zerr); - } - goto bail; - } + zerr = deflateInit2(&zstream, Z_BEST_COMPRESSION, + Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY); + if (zerr != Z_OK) { + result = UNKNOWN_ERROR; + if (zerr == Z_VERSION_ERROR) { + LOGE("Installed zlib is not compatible with linked version (%s)\n", + ZLIB_VERSION); + } else { + LOGD("Call to deflateInit2 failed (zerr=%d)\n", zerr); + } + goto bail; + } - crc = crc32(0L, Z_NULL, 0); + crc = crc32(0L, Z_NULL, 0); - /* - * Loop while we have data. - */ - do { - size_t getSize; - int flush; + /* + * Loop while we have data. + */ + do { + size_t getSize; + int flush; - /* only read if the input buffer is empty */ - if (zstream.avail_in == 0 && !atEof) { + /* only read if the input buffer is empty */ + if (zstream.avail_in == 0 && !atEof) { LOGV("+++ reading %d bytes\n", (int)kBufSize); if (data) { getSize = size > kBufSize ? kBufSize : size; @@ -817,54 +817,54 @@ status_t ZipFile::compressFpToFp(FILE* dstFp, FILE* srcFp, atEof = true; } - crc = crc32(crc, inBuf, getSize); + crc = crc32(crc, inBuf, getSize); - zstream.next_in = inBuf; - zstream.avail_in = getSize; - } + zstream.next_in = inBuf; + zstream.avail_in = getSize; + } - if (atEof) - flush = Z_FINISH; /* tell zlib that we're done */ - else - flush = Z_NO_FLUSH; /* more to come! */ + if (atEof) + flush = Z_FINISH; /* tell zlib that we're done */ + else + flush = Z_NO_FLUSH; /* more to come! */ - zerr = deflate(&zstream, flush); - if (zerr != Z_OK && zerr != Z_STREAM_END) { - LOGD("zlib deflate call failed (zerr=%d)\n", zerr); - result = UNKNOWN_ERROR; - goto z_bail; - } + zerr = deflate(&zstream, flush); + if (zerr != Z_OK && zerr != Z_STREAM_END) { + LOGD("zlib deflate call failed (zerr=%d)\n", zerr); + result = UNKNOWN_ERROR; + goto z_bail; + } - /* write when we're full or when we're done */ - if (zstream.avail_out == 0 || - (zerr == Z_STREAM_END && zstream.avail_out != (uInt) kBufSize)) - { - LOGV("+++ writing %d bytes\n", (int) (zstream.next_out - outBuf)); + /* write when we're full or when we're done */ + if (zstream.avail_out == 0 || + (zerr == Z_STREAM_END && zstream.avail_out != (uInt) kBufSize)) + { + LOGV("+++ writing %d bytes\n", (int) (zstream.next_out - outBuf)); if (fwrite(outBuf, 1, zstream.next_out - outBuf, dstFp) != (size_t)(zstream.next_out - outBuf)) { - LOGD("write %d failed in deflate\n", + LOGD("write %d failed in deflate\n", (int) (zstream.next_out - outBuf)); - goto z_bail; - } + goto z_bail; + } - zstream.next_out = outBuf; - zstream.avail_out = kBufSize; - } - } while (zerr == Z_OK); + zstream.next_out = outBuf; + zstream.avail_out = kBufSize; + } + } while (zerr == Z_OK); - assert(zerr == Z_STREAM_END); /* other errors should've been caught */ + assert(zerr == Z_STREAM_END); /* other errors should've been caught */ - *pCRC32 = crc; + *pCRC32 = crc; z_bail: - deflateEnd(&zstream); /* free up any allocated structures */ + deflateEnd(&zstream); /* free up any allocated structures */ bail: - delete[] inBuf; - delete[] outBuf; + delete[] inBuf; + delete[] outBuf; - return result; + return result; } /* @@ -1206,7 +1206,7 @@ bail: /* * =========================================================================== - * ZipFile::EndOfCentralDir + * ZipFile::EndOfCentralDir * =========================================================================== */ From 8dbfd35c6065022ee82b69f55b90193ca6990c73 Mon Sep 17 00:00:00 2001 From: Robert Greenwalt Date: Wed, 22 Apr 2009 14:35:11 -0700 Subject: [PATCH 087/541] Squashed commit of the following: commit 012b56fc607cf243cf4b29cb2a5f172bcbe0aecd Author: Robert Greenwalt Date: Wed Apr 22 14:31:26 2009 -0700 Additional fixes and tests for density. commit 91fdc8e187551ae69e0029a4325fb3ad38fe411b Author: Robert Greenwalt Date: Tue Apr 14 14:39:00 2009 -0700 Fix runtime resource selection logic. Fix isBetterThan so that o or this may be supperior at any stage. Used to only handle this-better or tie at each stage, biasing against o. Also allows reset of unit test to succeed. Fixes bug 1709202. --- include/utils/ResourceTypes.h | 285 +++++++++++++++++++++++----------- libs/utils/ResourceTypes.cpp | 2 +- 2 files changed, 196 insertions(+), 91 deletions(-) diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h index d01d83f83..9b8c3026a 100644 --- a/include/utils/ResourceTypes.h +++ b/include/utils/ResourceTypes.h @@ -988,119 +988,225 @@ struct ResTable_config return diffs; } - // Return true if 'this' is more specific than 'o'. Optionally, if - // 'requested' is null, then they will also be compared against the - // requested configuration and true will only be returned if 'this' - // is a better candidate than 'o' for the configuration. This assumes that - // match() has already been used to remove any configurations that don't - // match the requested configuration at all; if they are not first filtered, - // non-matching results can be considered better than matching ones. + // Return true if 'this' is more specific than 'o'. inline bool - isBetterThan(const ResTable_config& o, const ResTable_config* requested = NULL) const { + isMoreSpecificThan(const ResTable_config& o) const { // The order of the following tests defines the importance of one // configuration parameter over another. Those tests first are more // important, trumping any values in those following them. - if (imsi != 0 && (!requested || requested->imsi != 0)) { - if (mcc != 0 && (!requested || requested->mcc != 0)) { - if (o.mcc == 0) { - return true; - } + if (imsi || o.imsi) { + if (mcc != o.mcc) { + if (!mcc) return false; + if (!o.mcc) return true; } - if (mnc != 0 && (!requested || requested->mnc != 0)) { - if (o.mnc == 0) { - return true; - } + + if (mnc != o.mnc) { + if (!mnc) return false; + if (!o.mnc) return true; } } - if (locale != 0 && (!requested || requested->locale != 0)) { - if (language[0] != 0 && (!requested || requested->language[0] != 0)) { - if (o.language[0] == 0) { - return true; - } + + if (locale || o.locale) { + if (language[0] != o.language[0]) { + if (!language[0]) return false; + if (!o.language[0]) return true; } - if (country[0] != 0 && (!requested || requested->country[0] != 0)) { - if (o.country[0] == 0) { - return true; - } + + if (country[0] != o.country[0]) { + if (!country[0]) return false; + if (!o.country[0]) return true; } } - if (screenType != 0 && (!requested || requested->screenType != 0)) { - if (orientation != 0 && (!requested || requested->orientation != 0)) { - if (o.orientation == 0) { - return true; - } + + if (screenType || o.screenType) { + if (orientation != o.orientation) { + if (!orientation) return false; + if (!o.orientation) return true; } - if (density != 0 && (!requested || requested->density != 0)) { - if (o.density == 0) { - return true; - } - } - if (touchscreen != 0 && (!requested || requested->touchscreen != 0)) { - if (o.touchscreen == 0) { - return true; - } + + // density is never 'more specific' + // as the default just equals 160 + + if (touchscreen != o.touchscreen) { + if (!touchscreen) return false; + if (!o.touchscreen) return true; } } - if (input != 0 && (!requested || requested->input != 0)) { - const int keysHidden = inputFlags&MASK_KEYSHIDDEN; - const int reqKeysHidden = requested - ? requested->inputFlags&MASK_KEYSHIDDEN : 0; - if (keysHidden != 0 && reqKeysHidden != 0) { - const int oKeysHidden = o.inputFlags&MASK_KEYSHIDDEN; - //LOGI("isBetterThan keysHidden: cur=%d, given=%d, config=%d\n", - // keysHidden, oKeysHidden, reqKeysHidden); - if (oKeysHidden == 0) { - //LOGI("Better because 0!"); - return true; - } - // For compatibility, we count KEYSHIDDEN_NO as being - // the same as KEYSHIDDEN_SOFT. Here we disambiguate these - // may making an exact match more specific. - if (keysHidden == reqKeysHidden && oKeysHidden != reqKeysHidden) { - // The current configuration is an exact match, and - // the given one is not, so the current one is better. - //LOGI("Better because other not same!"); - return true; - } + + if (input || o.input) { + if (inputFlags != o.inputFlags) { + if (!(inputFlags & MASK_KEYSHIDDEN)) return false; + if (!(o.inputFlags & MASK_KEYSHIDDEN)) return true; } - if (keyboard != 0 && (!requested || requested->keyboard != 0)) { - if (o.keyboard == 0) { - return true; - } + + if (keyboard != o.keyboard) { + if (!keyboard) return false; + if (!o.keyboard) return true; } - if (navigation != 0 && (!requested || requested->navigation != 0)) { - if (o.navigation == 0) { - return true; - } + + if (navigation != o.navigation) { + if (!navigation) return false; + if (!o.navigation) return true; } } - if (screenSize != 0 && (!requested || requested->screenSize != 0)) { - if (screenWidth != 0 && (!requested || requested->screenWidth != 0)) { - if (o.screenWidth == 0) { - return true; - } + + if (screenSize || o.screenSize) { + if (screenWidth != o.screenWidth) { + if (!screenWidth) return false; + if (!o.screenWidth) return true; } - if (screenHeight != 0 && (!requested || requested->screenHeight != 0)) { - if (o.screenHeight == 0) { - return true; - } + + if (screenHeight != o.screenHeight) { + if (!screenHeight) return false; + if (!o.screenHeight) return true; } } - if (version != 0 && (!requested || requested->version != 0)) { - if (sdkVersion != 0 && (!requested || requested->sdkVersion != 0)) { - if (o.sdkVersion == 0) { - return true; - } + + if (version || o.version) { + if (sdkVersion != o.sdkVersion) { + if (!sdkVersion) return false; + if (!o.sdkVersion) return true; } - if (minorVersion != 0 && (!requested || requested->minorVersion != 0)) { - if (o.minorVersion == 0) { - return true; - } + + if (minorVersion != o.minorVersion) { + if (!minorVersion) return false; + if (!o.minorVersion) return true; } } return false; } - + + // Return true if 'this' is a better match than 'o' for the 'requested' + // configuration. This assumes that match() has already been used to + // remove any configurations that don't match the requested configuration + // at all; if they are not first filtered, non-matching results can be + // considered better than matching ones. + // The general rule per attribute: if the request cares about an attribute + // (it normally does), if the two (this and o) are equal it's a tie. If + // they are not equal then one must be generic because only generic and + // '==requested' will pass the match() call. So if this is not generic, + // it wins. If this IS generic, o wins (return false). + inline bool + isBetterThan(const ResTable_config& o, + const ResTable_config* requested) const { + if (requested) { + if (imsi || o.imsi) { + if ((mcc != o.mcc) && requested->mcc) { + return (mcc); + } + + if ((mnc != o.mnc) && requested->mnc) { + return (mnc); + } + } + + if (locale || o.locale) { + if ((language[0] != o.language[0]) && requested->language[0]) { + return (language[0]); + } + + if ((country[0] != o.country[0]) && requested->country[0]) { + return (country[0]); + } + } + + if (screenType || o.screenType) { + if ((orientation != o.orientation) && requested->orientation) { + return (orientation); + } + + if (density != o.density) { + // density is tough. Any density is potentially useful + // because the system will scale it. Scaling down + // is generally better than scaling up. + // Default density counts as 160dpi (the system default) + // TODO - remove 160 constants + int h = (density?density:160); + int l = (o.density?o.density:160); + bool bImBigger = true; + if (l > h) { + int t = h; + h = l; + l = t; + bImBigger = false; + } + + int reqValue = (requested->density?requested->density:160); + if (reqValue >= h) { + // requested value higher than both l and h, give h + return bImBigger; + } + if (l >= reqValue) { + // requested value lower than both l and h, give l + return !bImBigger; + } + // saying that scaling down is 2x better than up + if (((2 * l) - reqValue) * h > reqValue * reqValue) { + return !bImBigger; + } else { + return bImBigger; + } + } + + if ((touchscreen != o.touchscreen) && requested->touchscreen) { + return (touchscreen); + } + } + + if (input || o.input) { + const int keysHidden = inputFlags & MASK_KEYSHIDDEN; + const int oKeysHidden = o.inputFlags & MASK_KEYSHIDDEN; + if (keysHidden != oKeysHidden) { + const int reqKeysHidden = + requested->inputFlags & MASK_KEYSHIDDEN; + if (reqKeysHidden) { + + if (!keysHidden) return false; + if (!oKeysHidden) return true; + // For compatibility, we count KEYSHIDDEN_NO as being + // the same as KEYSHIDDEN_SOFT. Here we disambiguate + // these by making an exact match more specific. + if (reqKeysHidden == keysHidden) return true; + if (reqKeysHidden == oKeysHidden) return false; + } + } + + if ((keyboard != o.keyboard) && requested->keyboard) { + return (keyboard); + } + + if ((navigation != o.navigation) && requested->navigation) { + return (navigation); + } + } + + if (screenSize || o.screenSize) { + if ((screenWidth != o.screenWidth) && requested->screenWidth) { + return (screenWidth); + } + + if ((screenHeight != o.screenHeight) && + requested->screenHeight) { + return (screenHeight); + } + } + + if (version || o.version) { + if ((sdkVersion != o.sdkVersion) && requested->sdkVersion) { + return (sdkVersion); + } + + if ((minorVersion != o.minorVersion) && + requested->minorVersion) { + return (minorVersion); + } + } + + return false; + } + return isMoreSpecificThan(o); + } + // Return true if 'this' can be considered a match for the parameters in // 'settings'. // Note this is asymetric. A default piece of data will match every request @@ -1137,8 +1243,7 @@ struct ResTable_config && orientation != settings.orientation) { return false; } - // Density not taken into account, always match, no matter what - // density is specified for the resource + // density always matches - we can scale it. See isBetterThan if (settings.touchscreen != 0 && touchscreen != 0 && touchscreen != settings.touchscreen) { return false; diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp index 2ad3bfe8b..3d12dca7a 100644 --- a/libs/utils/ResourceTypes.cpp +++ b/libs/utils/ResourceTypes.cpp @@ -1820,7 +1820,7 @@ ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag } } - if (bestPackage != NULL && bestItem.isBetterThan(thisConfig)) { + if (bestPackage != NULL && bestItem.isMoreSpecificThan(thisConfig)) { continue; } From d8c95cc027409a21735a7e910a26116cbc6849ea Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Fri, 17 Apr 2009 14:15:18 -0700 Subject: [PATCH 088/541] Squashed commit of the following: commit e5c24638f98162c3b75b4c67a16b510d38e31341 Author: Mathias Agopian Date: Fri Apr 17 14:09:03 2009 -0700 oops forgot this file. commit 282642632d0cb12882eecf42e0fdfb2343275de1 Author: Mathias Agopian Date: Fri Apr 17 14:07:32 2009 -0700 use a helper macro for creating Singleton<>'s static attributes instances. --- include/utils/Singleton.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/include/utils/Singleton.h b/include/utils/Singleton.h index ee07df11f..2f7c7c2db 100644 --- a/include/utils/Singleton.h +++ b/include/utils/Singleton.h @@ -49,6 +49,18 @@ private: static TYPE* sInstance; }; +/* + * use ANDROID_SINGLETON_STATIC_INSTANCE(TYPE) in your implementation file + * (eg: .cpp) to create the static instance of Singleton<>'s attributes, + * and avoid to have a copy of them in each compilation units Singleton + * is used. + */ + +#define ANDROID_SINGLETON_STATIC_INSTANCE(TYPE) \ + template< class TYPE > Mutex Singleton< TYPE >::sLock; \ + template<> TYPE* Singleton< TYPE >::sInstance(0); + + // --------------------------------------------------------------------------- }; // namespace android From 76f6b453a201cd7547ade56c456bb5c1dbce79ff Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Mon, 20 Apr 2009 19:39:12 -0700 Subject: [PATCH 089/541] a brand new MessageQueue for SurfaceFlinger. --- include/utils/MemoryDealer.h | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/include/utils/MemoryDealer.h b/include/utils/MemoryDealer.h index 454b6270e..79d7883af 100644 --- a/include/utils/MemoryDealer.h +++ b/include/utils/MemoryDealer.h @@ -117,13 +117,22 @@ public: mFirst = mLast = newNode; newNode->prev = newNode->next = 0; } else { - insertBefore(mFirst, newNode); + newNode->prev = 0; + newNode->next = mFirst; + mFirst->prev = newNode; + mFirst = newNode; } } void insertTail(NODE* newNode) { - if (mLast == 0) insertBeginning(newNode); - else insertAfter(mLast, newNode); + if (mLast == 0) { + insertHead(newNode); + } else { + newNode->prev = mLast; + newNode->next = 0; + mLast->next = newNode; + mLast = newNode; + } } NODE* remove(NODE* node) { From 624a35ee485df833765d4367e10975921a92c16e Mon Sep 17 00:00:00 2001 From: San Mehat Date: Tue, 21 Apr 2009 14:06:36 -0700 Subject: [PATCH 090/541] Add support for changing a threads scheduler group. Three groups are available (default, background non interactive, foreground boost). Setting a thread priority to PRIORITY_BACKGROUND will transparently change groups to background Signed-off-by: San Mehat --- include/utils/threads.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/include/utils/threads.h b/include/utils/threads.h index 8d8d46a05..b3209156b 100644 --- a/include/utils/threads.h +++ b/include/utils/threads.h @@ -79,6 +79,13 @@ enum { ANDROID_PRIORITY_LESS_FAVORABLE = +1, }; +enum { + ANDROID_TGROUP_DEFAULT = 0, + ANDROID_TGROUP_BG_NONINTERACT = 1, + ANDROID_TGROUP_FG_BOOST = 2, + ANDROID_TGROUP_MAX = ANDROID_TGROUP_FG_BOOST, +}; + // Create and run a new thread. extern int androidCreateThread(android_thread_func_t, void *); From ebd9dd5521acddb8017380b1483732b2f25e1ccd Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Mon, 27 Apr 2009 21:40:53 -0700 Subject: [PATCH 091/541] small fix to List.h, the post-increment iterators should return "const" objects to disallow constructs such as i++++ --- include/utils/List.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/utils/List.h b/include/utils/List.h index 1a6be9ac9..1b01c417b 100644 --- a/include/utils/List.h +++ b/include/utils/List.h @@ -84,7 +84,7 @@ public: mpNode = mpNode->getNext(); return *this; } - _Iter operator++(int) { // post-increment + const _Iter operator++(int) { // post-increment _Iter tmp = *this; ++*this; return tmp; @@ -93,7 +93,7 @@ public: mpNode = mpNode->getPrev(); return *this; } - _Iter operator--(int) { // post-increment + const _Iter operator--(int) { // post-increment _Iter tmp = *this; --*this; return tmp; From 0077a0dd692e38ee7d90725727f311f0f9c24d04 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Mon, 27 Apr 2009 22:09:49 -0700 Subject: [PATCH 092/541] improvements (I hope) to to List.h implementation: - made the helper Node and Iterator classes protected inner classes of List so they don't pollute the android namespace. - use "int foo()" instead of "int foo(void)" which is more C++ stylish - made distance() a template function, this way we write it once and it will work with combinations of iterator and const_iterator - added the inline keyword on some function to make it clear to the compiler and the programmer that we want/intend these to be small inline functions - added templated comparison operators to Iterator so it can compare iterator and const_iterator - use size_t instead of "unsigned int" at places - distance() should return a ptrdiff_t (it's kind of mening less here because it won't really work if the distance is < 0) - made sure we handle conversions from iterator to const_iterator, but but fail at compile time in the other direction - added operator->() on iterator and const_iterator - made a bunch of private constructors explicit to avoid unwanted conversions --- include/utils/List.h | 280 +++++++++++++++++++++++++------------------ 1 file changed, 166 insertions(+), 114 deletions(-) diff --git a/include/utils/List.h b/include/utils/List.h index 1b01c417b..4041a8912 100644 --- a/include/utils/List.h +++ b/include/utils/List.h @@ -22,147 +22,200 @@ // construction, so if the compiler's auto-generated versions won't work for // you, define your own. // -// The only class you want to use from here is "List". Do not use classes -// starting with "_" directly. +// The only class you want to use from here is "List". // #ifndef _LIBS_UTILS_LIST_H #define _LIBS_UTILS_LIST_H +#include +#include + namespace android { -/* - * One element in the list. - */ -template class _ListNode { -public: - typedef _ListNode _Node; - - _ListNode(const T& val) : mVal(val) {} - ~_ListNode(void) {} - - T& getRef(void) { return mVal; } - void setVal(const T& val) { mVal = val; } - - _Node* getPrev(void) const { return mpPrev; } - void setPrev(_Node* ptr) { mpPrev = ptr; } - _Node* getNext(void) const { return mpNext; } - void setNext(_Node* ptr) { mpNext = ptr; } - -private: - T mVal; - _Node* mpPrev; - _Node* mpNext; -}; - -/* - * Iterator for walking through the list. - */ -template class _ListIterator { -public: - typedef _ListIterator _Iter; - typedef _ListNode _Node; - - _ListIterator(void) {} - _ListIterator(_Node* ptr) : mpNode(ptr) {} - ~_ListIterator(void) {} - - /* - * Dereference operator. Used to get at the juicy insides. - */ - Tref operator*() const { return mpNode->getRef(); } - - /* - * Iterator comparison. - */ - bool operator==(const _Iter& right) const { return mpNode == right.mpNode; } - bool operator!=(const _Iter& right) const { return mpNode != right.mpNode; } - - /* - * Incr/decr, used to move through the list. - */ - _Iter& operator++(void) { // pre-increment - mpNode = mpNode->getNext(); - return *this; - } - const _Iter operator++(int) { // post-increment - _Iter tmp = *this; - ++*this; - return tmp; - } - _Iter& operator--(void) { // pre-increment - mpNode = mpNode->getPrev(); - return *this; - } - const _Iter operator--(int) { // post-increment - _Iter tmp = *this; - --*this; - return tmp; - } - - _Node* getNode(void) const { return mpNode; } - -private: - _Node* mpNode; -}; - - /* * Doubly-linked list. Instantiate with "List myList". * * Objects added to the list are copied using the assignment operator, * so this must be defined. */ -template class List { -public: - typedef _ListNode _Node; +template +class List +{ +protected: + /* + * One element in the list. + */ + class _Node { + public: + explicit _Node(const T& val) : mVal(val) {} + ~_Node() {} + inline T& getRef() { return mVal; } + inline const T& getRef() const { return mVal; } + inline _Node* getPrev() const { return mpPrev; } + inline _Node* getNext() const { return mpNext; } + inline void setVal(const T& val) { mVal = val; } + inline void setPrev(_Node* ptr) { mpPrev = ptr; } + inline void setNext(_Node* ptr) { mpNext = ptr; } + private: + friend class List; + friend class _ListIterator; + T mVal; + _Node* mpPrev; + _Node* mpNext; + }; - List(void) { + /* + * Iterator for walking through the list. + */ + + template + struct CONST_ITERATOR { + typedef _Node const * NodePtr; + typedef const TYPE Type; + }; + + template + struct NON_CONST_ITERATOR { + typedef _Node* NodePtr; + typedef TYPE Type; + }; + + template< + typename U, + template class Constness + > + class _ListIterator { + typedef _ListIterator _Iter; + typedef typename Constness::NodePtr _NodePtr; + typedef typename Constness::Type _Type; + + explicit _ListIterator(_NodePtr ptr) : mpNode(ptr) {} + + public: + _ListIterator() {} + _ListIterator(const _Iter& rhs) : mpNode(rhs.mpNode) {} + ~_ListIterator() {} + + // this will handle conversions from iterator to const_iterator + // (and also all convertible iterators) + // Here, in this implementation, the iterators can be converted + // if the nodes can be converted + template explicit + _ListIterator(const V& rhs) : mpNode(rhs.mpNode) {} + + + /* + * Dereference operator. Used to get at the juicy insides. + */ + _Type& operator*() const { return mpNode->getRef(); } + _Type* operator->() const { return &(mpNode->getRef()); } + + /* + * Iterator comparison. + */ + inline bool operator==(const _Iter& right) const { + return mpNode == right.mpNode; } + + inline bool operator!=(const _Iter& right) const { + return mpNode != right.mpNode; } + + /* + * handle comparisons between iterator and const_iterator + */ + template + inline bool operator==(const OTHER& right) const { + return mpNode == right.mpNode; } + + template + inline bool operator!=(const OTHER& right) const { + return mpNode != right.mpNode; } + + /* + * Incr/decr, used to move through the list. + */ + inline _Iter& operator++() { // pre-increment + mpNode = mpNode->getNext(); + return *this; + } + const _Iter operator++(int) { // post-increment + _Iter tmp(*this); + mpNode = mpNode->getNext(); + return tmp; + } + inline _Iter& operator--() { // pre-increment + mpNode = mpNode->getPrev(); + return *this; + } + const _Iter operator--(int) { // post-increment + _Iter tmp(*this); + mpNode = mpNode->getPrev(); + return tmp; + } + + inline _NodePtr getNode() const { return mpNode; } + + private: + friend class List; + _NodePtr mpNode; + }; + +public: + List() { prep(); } List(const List& src) { // copy-constructor prep(); insert(begin(), src.begin(), src.end()); } - virtual ~List(void) { + virtual ~List() { clear(); delete[] (unsigned char*) mpMiddle; } - typedef _ListIterator iterator; - typedef _ListIterator const_iterator; + typedef _ListIterator iterator; + typedef _ListIterator const_iterator; List& operator=(const List& right); /* returns true if the list is empty */ - bool empty(void) const { return mpMiddle->getNext() == mpMiddle; } + inline bool empty() const { return mpMiddle->getNext() == mpMiddle; } /* return #of elements in list */ - unsigned int size(void) const { - return distance(begin(), end()); + size_t size() const { + return size_t(distance(begin(), end())); } /* * Return the first element or one past the last element. The - * _ListNode* we're returning is converted to an "iterator" by a + * _Node* we're returning is converted to an "iterator" by a * constructor in _ListIterator. */ - iterator begin() { return mpMiddle->getNext(); } - const_iterator begin() const { return mpMiddle->getNext(); } - iterator end() { return mpMiddle; } - const_iterator end() const { return mpMiddle; } + inline iterator begin() { + return iterator(mpMiddle->getNext()); + } + inline const_iterator begin() const { + return const_iterator(const_cast<_Node const*>(mpMiddle->getNext())); + } + inline iterator end() { + return iterator(mpMiddle); + } + inline const_iterator end() const { + return const_iterator(const_cast<_Node const*>(mpMiddle)); + } /* add the object to the head or tail of the list */ void push_front(const T& val) { insert(begin(), val); } void push_back(const T& val) { insert(end(), val); } /* insert before the current node; returns iterator at new node */ - iterator insert(iterator posn, const T& val) { + iterator insert(iterator posn, const T& val) + { _Node* newNode = new _Node(val); // alloc & copy-construct newNode->setNext(posn.getNode()); newNode->setPrev(posn.getNode()->getPrev()); posn.getNode()->getPrev()->setNext(newNode); posn.getNode()->setPrev(newNode); - return newNode; + return iterator(newNode); } /* insert a range of elements before the current node */ @@ -178,18 +231,18 @@ public: pPrev->setNext(pNext); pNext->setPrev(pPrev); delete posn.getNode(); - return pNext; + return iterator(pNext); } /* remove a range of elements */ iterator erase(iterator first, iterator last) { while (first != last) erase(first++); // don't erase than incr later! - return last; + return iterator(last); } /* remove all contents of the list */ - void clear(void) { + void clear() { _Node* pCurrent = mpMiddle->getNext(); _Node* pNext; @@ -207,21 +260,20 @@ public: * will be equal to "last". The iterators must refer to the same * list. * - * (This is actually a generic iterator function. It should be part - * of some other class, possibly an iterator base class. It needs to - * know the difference between a list, which has to march through, - * and a vector, which can just do pointer math.) + * FIXME: This is actually a generic iterator function. It should be a + * template function at the top-level with specializations for things like + * vector<>, which can just do pointer math). Here we limit it to + * _ListIterator of the same type but different constness. */ - unsigned int distance(iterator first, iterator last) { - unsigned int count = 0; - while (first != last) { - ++first; - ++count; - } - return count; - } - unsigned int distance(const_iterator first, const_iterator last) const { - unsigned int count = 0; + template< + typename U, + template class CL, + template class CR + > + ptrdiff_t distance( + _ListIterator first, _ListIterator last) const + { + ptrdiff_t count = 0; while (first != last) { ++first; ++count; @@ -231,12 +283,12 @@ public: private: /* - * I want a _ListNode but don't need it to hold valid data. More + * I want a _Node but don't need it to hold valid data. More * to the point, I don't want T's constructor to fire, since it * might have side-effects or require arguments. So, we do this * slightly uncouth storage alloc. */ - void prep(void) { + void prep() { mpMiddle = (_Node*) new unsigned char[sizeof(_Node)]; mpMiddle->setPrev(mpMiddle); mpMiddle->setNext(mpMiddle); From 019f8ed4274f788c90ca5f8a395fed706af5584c Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Mon, 4 May 2009 14:17:04 -0700 Subject: [PATCH 093/541] update surfaceflinger, libui and libagl to the new gralloc api - Currently the lock/unlock path is naive and is done for each drawing operation (glDrawElements and glDrawArrays). this should be improved eventually. - factor all the lock/unlock code in SurfaceBuffer. - fixed "showupdate" so it works even when we don't have preserving eglSwapBuffers(). - improved the situation with the dirty-region and fixed a problem that caused GL apps to not update. - make use of LightRefBase() where needed, instead of duplicating its implementation - add LightRefBase::getStrongCount() - renamed EGLNativeWindowSurface.cpp to FramebufferNativeWindow.cpp - disabled copybits test, since it clashes with the new gralloc api - Camera/Video will be fixed later when we rework the overlay apis --- include/utils/RefBase.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/utils/RefBase.h b/include/utils/RefBase.h index cbda0fd80..bd7f28cae 100644 --- a/include/utils/RefBase.h +++ b/include/utils/RefBase.h @@ -156,6 +156,10 @@ public: delete static_cast(this); } } + //! DEBUGGING ONLY: Get current strong ref count. + inline int32_t getStrongCount() const { + return mCount; + } protected: inline ~LightRefBase() { } From aaead201642faec52df1424d8ab3fcc21a69224f Mon Sep 17 00:00:00 2001 From: Joe Onorato Date: Tue, 5 May 2009 11:50:51 -0700 Subject: [PATCH 094/541] Add some C++ code to do raw files for backup --- include/utils/backup_helpers.h | 15 + libs/utils/Android.mk | 3 +- libs/utils/file_backup_helper.cpp | 680 ++++++++++++++++++++++++++++++ 3 files changed, 697 insertions(+), 1 deletion(-) create mode 100644 include/utils/backup_helpers.h create mode 100644 libs/utils/file_backup_helper.cpp diff --git a/include/utils/backup_helpers.h b/include/utils/backup_helpers.h new file mode 100644 index 000000000..f70444df6 --- /dev/null +++ b/include/utils/backup_helpers.h @@ -0,0 +1,15 @@ +#ifndef _UTILS_BACKUP_HELPERS_H +#define _UTILS_BACKUP_HELPERS_H + +int back_up_files(int oldSnapshotFD, int newSnapshotFD, int oldDataStream, + char const* fileBase, char const* const* files, int fileCount); + +#define TEST_BACKUP_HELPERS 1 + +#if TEST_BACKUP_HELPERS +int backup_helper_test_empty(); +int backup_helper_test_four(); +int backup_helper_test_files(); +#endif + +#endif // _UTILS_BACKUP_HELPERS_H diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk index cdb8ca2d7..f9fb78038 100644 --- a/libs/utils/Android.mk +++ b/libs/utils/Android.mk @@ -116,7 +116,8 @@ LOCAL_SRC_FILES:= \ ProcessState.cpp \ IPermissionController.cpp \ IServiceManager.cpp \ - Unicode.cpp + Unicode.cpp \ + file_backup_helper.cpp ifeq ($(TARGET_SIMULATOR),true) LOCAL_SRC_FILES += $(hostSources) diff --git a/libs/utils/file_backup_helper.cpp b/libs/utils/file_backup_helper.cpp new file mode 100644 index 000000000..16cbb6ec4 --- /dev/null +++ b/libs/utils/file_backup_helper.cpp @@ -0,0 +1,680 @@ +#define LOG_TAG "file_backup_helper" + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace android; + +#define MAGIC0 0x70616e53 // Snap +#define MAGIC1 0x656c6946 // File + +struct SnapshotHeader { + int magic0; + int fileCount; + int magic1; + int totalSize; +}; + +struct FileState { + int modTime_sec; + int modTime_nsec; + int size; + int crc32; + int nameLen; +}; + +const static int ROUND_UP[4] = { 0, 3, 2, 1 }; + +static inline int +round_up(int n) +{ + return n + ROUND_UP[n % 4]; +} + +static int +read_snapshot_file(int fd, KeyedVector* snapshot) +{ + int bytesRead = 0; + int amt; + SnapshotHeader header; + + amt = read(fd, &header, sizeof(header)); + if (amt != sizeof(header)) { + return errno; + } + bytesRead += amt; + + if (header.magic0 != MAGIC0 || header.magic1 != MAGIC1) { + LOGW("read_snapshot_file header.magic0=0x%08x magic1=0x%08x", header.magic0, header.magic1); + return 1; + } + + for (int i=0; iadd(String8(filename, file.nameLen), file); + } + bytesRead += amt; + if (filename != filenameBuf) { + free(filename); + } + if (amt != nameBufSize) { + LOGW("read_snapshot_file filename truncated/error with read at %d bytes\n", bytesRead); + return 1; + } + } + + if (header.totalSize != bytesRead) { + LOGW("read_snapshot_file length mismatch: header.totalSize=%d bytesRead=%d\n", + header.totalSize, bytesRead); + return 1; + } + + return 0; +} + +static int +write_snapshot_file(int fd, const KeyedVector& snapshot) +{ + int bytesWritten = sizeof(SnapshotHeader); + // preflight size + const int N = snapshot.size(); + for (int i=0; i oldSnapshot; + KeyedVector newSnapshot; + + if (oldSnapshotFD != -1) { + err = read_snapshot_file(oldSnapshotFD, &oldSnapshot); + if (err != 0) { + // On an error, treat this as a full backup. + oldSnapshot.clear(); + } + } + + for (int i=0; i 0) { + // file added + String8 realFilename(base); + realFilename.appendPath(q); + write_update_file(realFilename, q); + m++; + } + else if (cmp < 0) { + // file removed + write_delete_file(p); + n++; + } + else { + // both files exist, check them + String8 realFilename(base); + realFilename.appendPath(q); + const FileState& f = oldSnapshot.valueAt(n); + const FileState& g = newSnapshot.valueAt(m); + + printf("%s\n", q.string()); + printf(" new: modTime=%d,%d size=%-3d crc32=0x%08x\n", + f.modTime_sec, f.modTime_nsec, f.size, f.crc32); + printf(" old: modTime=%d,%d size=%-3d crc32=0x%08x\n", + g.modTime_sec, g.modTime_nsec, g.size, g.crc32); + if (f.modTime_sec != g.modTime_sec || f.modTime_nsec != g.modTime_nsec + || f.size != g.size || f.crc32 != g.crc32) { + write_update_file(realFilename, p); + } + n++; + m++; + } + } + + // these were deleted + while (n snapshot; + const char* filename = SCRATCH_DIR "backup_helper_test_empty.snap"; + + system("rm -r " SCRATCH_DIR); + mkdir(SCRATCH_DIR, 0777); + + // write + fd = creat(filename, 0666); + if (fd == -1) { + fprintf(stderr, "error creating %s\n", filename); + return 1; + } + + err = write_snapshot_file(fd, snapshot); + + close(fd); + + if (err != 0) { + fprintf(stderr, "write_snapshot_file reported error %d (%s)\n", err, strerror(err)); + return err; + } + + static const unsigned char correct_data[] = { + 0x53, 0x6e, 0x61, 0x70, 0x00, 0x00, 0x00, 0x00, + 0x46, 0x69, 0x6c, 0x65, 0x10, 0x00, 0x00, 0x00 + }; + + err = compare_file(filename, correct_data, sizeof(correct_data)); + if (err != 0) { + return err; + } + + // read + fd = open(filename, O_RDONLY); + if (fd == -1) { + fprintf(stderr, "error opening for read %s\n", filename); + return 1; + } + + KeyedVector readSnapshot; + err = read_snapshot_file(fd, &readSnapshot); + if (err != 0) { + fprintf(stderr, "read_snapshot_file failed %d\n", err); + return err; + } + + if (readSnapshot.size() != 0) { + fprintf(stderr, "readSnapshot should be length 0\n"); + return 1; + } + + return 0; +} + +int +backup_helper_test_four() +{ + int err; + int fd; + KeyedVector snapshot; + const char* filename = SCRATCH_DIR "backup_helper_test_four.snap"; + + system("rm -r " SCRATCH_DIR); + mkdir(SCRATCH_DIR, 0777); + + // write + fd = creat(filename, 0666); + if (fd == -1) { + fprintf(stderr, "error opening %s\n", filename); + return 1; + } + + String8 filenames[4]; + FileState states[4]; + + states[0].modTime_sec = 0xfedcba98; + states[0].modTime_nsec = 0xdeadbeef; + states[0].size = 0xababbcbc; + states[0].crc32 = 0x12345678; + states[0].nameLen = -12; + filenames[0] = String8("bytes_of_padding"); + snapshot.add(filenames[0], states[0]); + + states[1].modTime_sec = 0x93400031; + states[1].modTime_nsec = 0xdeadbeef; + states[1].size = 0x88557766; + states[1].crc32 = 0x22334422; + states[1].nameLen = -1; + filenames[1] = String8("bytes_of_padding3"); + snapshot.add(filenames[1], states[1]); + + states[2].modTime_sec = 0x33221144; + states[2].modTime_nsec = 0xdeadbeef; + states[2].size = 0x11223344; + states[2].crc32 = 0x01122334; + states[2].nameLen = 0; + filenames[2] = String8("bytes_of_padding_2"); + snapshot.add(filenames[2], states[2]); + + states[3].modTime_sec = 0x33221144; + states[3].modTime_nsec = 0xdeadbeef; + states[3].size = 0x11223344; + states[3].crc32 = 0x01122334; + states[3].nameLen = 0; + filenames[3] = String8("bytes_of_padding__1"); + snapshot.add(filenames[3], states[3]); + + err = write_snapshot_file(fd, snapshot); + + close(fd); + + if (err != 0) { + fprintf(stderr, "write_snapshot_file reported error %d (%s)\n", err, strerror(err)); + return err; + } + + static const unsigned char correct_data[] = { + // header + 0x53, 0x6e, 0x61, 0x70, 0x04, 0x00, 0x00, 0x00, + 0x46, 0x69, 0x6c, 0x65, 0xac, 0x00, 0x00, 0x00, + + // bytes_of_padding + 0x98, 0xba, 0xdc, 0xfe, 0xef, 0xbe, 0xad, 0xde, + 0xbc, 0xbc, 0xab, 0xab, 0x78, 0x56, 0x34, 0x12, + 0x10, 0x00, 0x00, 0x00, 0x62, 0x79, 0x74, 0x65, + 0x73, 0x5f, 0x6f, 0x66, 0x5f, 0x70, 0x61, 0x64, + 0x64, 0x69, 0x6e, 0x67, + + // bytes_of_padding3 + 0x31, 0x00, 0x40, 0x93, 0xef, 0xbe, 0xad, 0xde, + 0x66, 0x77, 0x55, 0x88, 0x22, 0x44, 0x33, 0x22, + 0x11, 0x00, 0x00, 0x00, 0x62, 0x79, 0x74, 0x65, + 0x73, 0x5f, 0x6f, 0x66, 0x5f, 0x70, 0x61, 0x64, + 0x64, 0x69, 0x6e, 0x67, 0x33, 0xab, 0xab, 0xab, + + // bytes of padding2 + 0x44, 0x11, 0x22, 0x33, 0xef, 0xbe, 0xad, 0xde, + 0x44, 0x33, 0x22, 0x11, 0x34, 0x23, 0x12, 0x01, + 0x12, 0x00, 0x00, 0x00, 0x62, 0x79, 0x74, 0x65, + 0x73, 0x5f, 0x6f, 0x66, 0x5f, 0x70, 0x61, 0x64, + 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x32, 0xab, 0xab, + + // bytes of padding3 + 0x44, 0x11, 0x22, 0x33, 0xef, 0xbe, 0xad, 0xde, + 0x44, 0x33, 0x22, 0x11, 0x34, 0x23, 0x12, 0x01, + 0x13, 0x00, 0x00, 0x00, 0x62, 0x79, 0x74, 0x65, + 0x73, 0x5f, 0x6f, 0x66, 0x5f, 0x70, 0x61, 0x64, + 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x5f, 0x31, 0xab + }; + + err = compare_file(filename, correct_data, sizeof(correct_data)); + if (err != 0) { + return err; + } + + // read + fd = open(filename, O_RDONLY); + if (fd == -1) { + fprintf(stderr, "error opening for read %s\n", filename); + return 1; + } + + + KeyedVector readSnapshot; + err = read_snapshot_file(fd, &readSnapshot); + if (err != 0) { + fprintf(stderr, "read_snapshot_file failed %d\n", err); + return err; + } + + if (readSnapshot.size() != 4) { + fprintf(stderr, "readSnapshot should be length 4 is %d\n", readSnapshot.size()); + return 1; + } + + bool matched = true; + for (size_t i=0; i Date: Wed, 6 May 2009 12:55:46 -0400 Subject: [PATCH 095/541] fix the sim build. disables the tests for now. --- include/utils/backup_helpers.h | 2 +- libs/utils/file_backup_helper.cpp | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/include/utils/backup_helpers.h b/include/utils/backup_helpers.h index f70444df6..61bee340d 100644 --- a/include/utils/backup_helpers.h +++ b/include/utils/backup_helpers.h @@ -4,7 +4,7 @@ int back_up_files(int oldSnapshotFD, int newSnapshotFD, int oldDataStream, char const* fileBase, char const* const* files, int fileCount); -#define TEST_BACKUP_HELPERS 1 +#define TEST_BACKUP_HELPERS 0 #if TEST_BACKUP_HELPERS int backup_helper_test_empty(); diff --git a/libs/utils/file_backup_helper.cpp b/libs/utils/file_backup_helper.cpp index 16cbb6ec4..111f88d9f 100644 --- a/libs/utils/file_backup_helper.cpp +++ b/libs/utils/file_backup_helper.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -224,7 +225,8 @@ back_up_files(int oldSnapshotFD, int newSnapshotFD, int oldDataStream, } s.modTime_sec = st.st_mtime; - s.modTime_nsec = st.st_mtime_nsec; + s.modTime_nsec = 0; // workaround sim breakage + //s.modTime_nsec = st.st_mtime_nsec; s.size = st.st_size; s.crc32 = compute_crc32(realFilename); From b81a9a18e502ec04a10d847322da21389a85868a Mon Sep 17 00:00:00 2001 From: Joe Onorato Date: Wed, 13 May 2009 18:57:29 -0400 Subject: [PATCH 096/541] Get the backup calling through to the file backup helper. This includes some cleanup to make the parameters match between BackupService.onBackup and FileBackupHelper.performBackup. --- include/utils/backup_helpers.h | 2 +- libs/utils/file_backup_helper.cpp | 15 +++++++++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/include/utils/backup_helpers.h b/include/utils/backup_helpers.h index 61bee340d..137c5f104 100644 --- a/include/utils/backup_helpers.h +++ b/include/utils/backup_helpers.h @@ -1,7 +1,7 @@ #ifndef _UTILS_BACKUP_HELPERS_H #define _UTILS_BACKUP_HELPERS_H -int back_up_files(int oldSnapshotFD, int newSnapshotFD, int oldDataStream, +int back_up_files(int oldSnapshotFD, int oldDataStream, int newSnapshotFD, char const* fileBase, char const* const* files, int fileCount); #define TEST_BACKUP_HELPERS 0 diff --git a/libs/utils/file_backup_helper.cpp b/libs/utils/file_backup_helper.cpp index 111f88d9f..453084ace 100644 --- a/libs/utils/file_backup_helper.cpp +++ b/libs/utils/file_backup_helper.cpp @@ -24,6 +24,9 @@ using namespace android; #define MAGIC0 0x70616e53 // Snap #define MAGIC1 0x656c6946 // File +#define LOGP(x...) LOGD(x) +//#define LOGP(x...) printf(x) + struct SnapshotHeader { int magic0; int fileCount; @@ -159,14 +162,14 @@ write_snapshot_file(int fd, const KeyedVector& snapshot) static int write_delete_file(const String8& key) { - printf("write_delete_file %s\n", key.string()); + LOGP("write_delete_file %s\n", key.string()); return 0; } static int write_update_file(const String8& realFilename, const String8& key) { - printf("write_update_file %s (%s)\n", realFilename.string(), key.string()); + LOGP("write_update_file %s (%s)\n", realFilename.string(), key.string()); return 0; } @@ -195,7 +198,7 @@ compute_crc32(const String8& filename) } int -back_up_files(int oldSnapshotFD, int newSnapshotFD, int oldDataStream, +back_up_files(int oldSnapshotFD, int oldDataStream, int newSnapshotFD, char const* fileBase, char const* const* files, int fileCount) { int err; @@ -260,10 +263,10 @@ back_up_files(int oldSnapshotFD, int newSnapshotFD, int oldDataStream, const FileState& f = oldSnapshot.valueAt(n); const FileState& g = newSnapshot.valueAt(m); - printf("%s\n", q.string()); - printf(" new: modTime=%d,%d size=%-3d crc32=0x%08x\n", + LOGP("%s\n", q.string()); + LOGP(" new: modTime=%d,%d size=%-3d crc32=0x%08x\n", f.modTime_sec, f.modTime_nsec, f.size, f.crc32); - printf(" old: modTime=%d,%d size=%-3d crc32=0x%08x\n", + LOGP(" old: modTime=%d,%d size=%-3d crc32=0x%08x\n", g.modTime_sec, g.modTime_nsec, g.size, g.crc32); if (f.modTime_sec != g.modTime_sec || f.modTime_nsec != g.modTime_nsec || f.size != g.size || f.crc32 != g.crc32) { From 62a381bd134ab0b7874ef4ffa6793fc3210ff7c1 Mon Sep 17 00:00:00 2001 From: Joe Onorato Date: Fri, 15 May 2009 09:07:06 -0400 Subject: [PATCH 097/541] Implement the C++ class to write the backed up file data. --- include/utils/ByteOrder.h | 32 ++- include/utils/backup_helpers.h | 54 ++++ libs/utils/Android.mk | 3 +- libs/utils/backup_data.cpp | 236 ++++++++++++++++++ ...ckup_helper.cpp => backup_helper_file.cpp} | 153 +++++++++++- 5 files changed, 461 insertions(+), 17 deletions(-) create mode 100644 libs/utils/backup_data.cpp rename libs/utils/{file_backup_helper.cpp => backup_helper_file.cpp} (79%) diff --git a/include/utils/ByteOrder.h b/include/utils/ByteOrder.h index 4c0606763..baa3a83dd 100644 --- a/include/utils/ByteOrder.h +++ b/include/utils/ByteOrder.h @@ -38,6 +38,16 @@ * intent is to allow us to avoid byte swapping on the device. */ +static inline uint32_t android_swap_long(uint32_t v) +{ + return (v<<24) | ((v<<8)&0x00FF0000) | ((v>>8)&0x0000FF00) | (v>>24); +} + +static inline uint16_t android_swap_short(uint16_t v) +{ + return (v<<8) | (v>>8); +} + #define DEVICE_BYTE_ORDER LITTLE_ENDIAN #if BYTE_ORDER == DEVICE_BYTE_ORDER @@ -49,16 +59,6 @@ #else -static inline uint32_t android_swap_long(uint32_t v) -{ - return (v<<24) | ((v<<8)&0x00FF0000) | ((v>>8)&0x0000FF00) | (v>>24); -} - -static inline uint16_t android_swap_short(uint16_t v) -{ - return (v<<8) | (v>>8); -} - #define dtohl(x) (android_swap_long(x)) #define dtohs(x) (android_swap_short(x)) #define htodl(x) (android_swap_long(x)) @@ -66,4 +66,16 @@ static inline uint16_t android_swap_short(uint16_t v) #endif +#if BYTE_ORDER == LITTLE_ENDIAN +#define fromlel(x) (x) +#define fromles(x) (x) +#define tolel(x) (x) +#define toles(x) (x) +#else +#define fromlel(x) (android_swap_long(x)) +#define fromles(x) (android_swap_short(x)) +#define tolel(x) (android_swap_long(x)) +#define toles(x) (android_swap_short(x)) +#endif + #endif // _LIBS_UTILS_BYTE_ORDER_H diff --git a/include/utils/backup_helpers.h b/include/utils/backup_helpers.h index 137c5f104..73b998912 100644 --- a/include/utils/backup_helpers.h +++ b/include/utils/backup_helpers.h @@ -1,15 +1,69 @@ +/* + * 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 _UTILS_BACKUP_HELPERS_H #define _UTILS_BACKUP_HELPERS_H +#include +#include + +namespace android { + int back_up_files(int oldSnapshotFD, int oldDataStream, int newSnapshotFD, char const* fileBase, char const* const* files, int fileCount); +/** + * Reads the data. + * + * If an error occurs, it poisons this object and all write calls will fail + * with the error that occurred. + */ +class BackupDataWriter +{ +public: + BackupDataWriter(int fd); + // does not close fd + ~BackupDataWriter(); + + status_t WriteAppHeader(const String8& packageName); + + status_t WriteEntityHeader(const String8& key, size_t dataSize); + status_t WriteEntityData(const void* data, size_t size); + + status_t WriteAppFooter(); + +private: + explicit BackupDataWriter(); + status_t write_padding_for(int n); + + int m_fd; + status_t m_status; + ssize_t m_pos; + int m_entityCount; +}; + #define TEST_BACKUP_HELPERS 0 #if TEST_BACKUP_HELPERS int backup_helper_test_empty(); int backup_helper_test_four(); int backup_helper_test_files(); +int backup_helper_test_data_writer(); #endif +} // namespace android + #endif // _UTILS_BACKUP_HELPERS_H diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk index f9fb78038..5a1a89b46 100644 --- a/libs/utils/Android.mk +++ b/libs/utils/Android.mk @@ -117,7 +117,8 @@ LOCAL_SRC_FILES:= \ IPermissionController.cpp \ IServiceManager.cpp \ Unicode.cpp \ - file_backup_helper.cpp + backup_data.cpp \ + backup_helper_file.cpp ifeq ($(TARGET_SIMULATOR),true) LOCAL_SRC_FILES += $(hostSources) diff --git a/libs/utils/backup_data.cpp b/libs/utils/backup_data.cpp new file mode 100644 index 000000000..c7f1fdb58 --- /dev/null +++ b/libs/utils/backup_data.cpp @@ -0,0 +1,236 @@ +/* + * 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 +#include + +#include +#include + +namespace android { + +/* + * File Format (v1): + * + * All ints are stored little-endian. + * + * - An app_header_v1 struct. + * - The name of the package, utf-8, null terminated, padded to 4-byte boundary. + * - A sequence of zero or more key/value paires (entities), each with + * - A entity_header_v1 struct + * - The key, utf-8, null terminated, padded to 4-byte boundary. + * - The value, padded to 4 byte boundary + */ + +#define APP_MAGIC_V1 0x31707041 // App1 (little endian) +#define ENTITY_MAGIC_V1 0x61746144 // Data (little endian) +#define FOOTER_MAGIC_V1 0x746f6f46 // Foot (little endian) + +typedef struct { + int type; // == APP_MAGIC_V1 + int packageLen; // length of the name of the package that follows, not including the null. +} app_header_v1; + +typedef struct { + int type; // ENTITY_MAGIC_V1 + int keyLen; // length of the key name, not including the null terminator + int dataSize; // size of the data, not including the padding +} entity_header_v1; + +typedef struct { + int type; // FOOTER_MAGIC_V1 + int entityCount; // the number of entities that were written +} app_footer_v1; + +const static int ROUND_UP[4] = { 0, 3, 2, 1 }; + +static inline size_t +round_up(size_t n) +{ + return n + ROUND_UP[n % 4]; +} + +static inline size_t +padding_extra(size_t n) +{ + return ROUND_UP[n % 4]; +} + +BackupDataWriter::BackupDataWriter(int fd) + :m_fd(fd), + m_status(NO_ERROR), + m_pos(0), + m_entityCount(0) +{ +} + +BackupDataWriter::~BackupDataWriter() +{ +} + +// Pad out anything they've previously written to the next 4 byte boundary. +status_t +BackupDataWriter::write_padding_for(int n) +{ + ssize_t amt; + ssize_t paddingSize; + + paddingSize = padding_extra(n); + if (paddingSize > 0) { + uint32_t padding = 0xbcbcbcbc; + amt = write(m_fd, &padding, paddingSize); + if (amt != paddingSize) { + m_status = errno; + return m_status; + } + m_pos += amt; + } + return NO_ERROR; +} + +status_t +BackupDataWriter::WriteAppHeader(const String8& packageName) +{ + if (m_status != NO_ERROR) { + return m_status; + } + + ssize_t amt; + + amt = write_padding_for(m_pos); + if (amt != 0) { + return amt; + } + + app_header_v1 header; + ssize_t nameLen; + + nameLen = packageName.length(); + + header.type = tolel(APP_MAGIC_V1); + header.packageLen = tolel(nameLen); + + amt = write(m_fd, &header, sizeof(app_header_v1)); + if (amt != sizeof(app_header_v1)) { + m_status = errno; + return m_status; + } + m_pos += amt; + + amt = write(m_fd, packageName.string(), nameLen+1); + if (amt != nameLen+1) { + m_status = errno; + return m_status; + } + m_pos += amt; + + return NO_ERROR; +} + +status_t +BackupDataWriter::WriteEntityHeader(const String8& key, size_t dataSize) +{ + if (m_status != NO_ERROR) { + return m_status; + } + + ssize_t amt; + + amt = write_padding_for(m_pos); + if (amt != 0) { + return amt; + } + + entity_header_v1 header; + ssize_t keyLen; + + keyLen = key.length(); + + header.type = tolel(ENTITY_MAGIC_V1); + header.keyLen = tolel(keyLen); + header.dataSize = tolel(dataSize); + + amt = write(m_fd, &header, sizeof(entity_header_v1)); + if (amt != sizeof(entity_header_v1)) { + m_status = errno; + return m_status; + } + m_pos += amt; + + amt = write(m_fd, key.string(), keyLen+1); + if (amt != keyLen+1) { + m_status = errno; + return m_status; + } + m_pos += amt; + + amt = write_padding_for(keyLen+1); + + m_entityCount++; + + return amt; +} + +status_t +BackupDataWriter::WriteEntityData(const void* data, size_t size) +{ + if (m_status != NO_ERROR) { + return m_status; + } + + // We don't write padding here, because they're allowed to call this several + // times with smaller buffers. We write it at the end of WriteEntityHeader + // instead. + ssize_t amt = write(m_fd, data, size); + if (amt != (ssize_t)size) { + m_status = errno; + return m_status; + } + m_pos += amt; + return NO_ERROR; +} + +status_t +BackupDataWriter::WriteAppFooter() +{ + if (m_status != NO_ERROR) { + return m_status; + } + + ssize_t amt; + + amt = write_padding_for(m_pos); + if (amt != 0) { + return amt; + } + + app_footer_v1 footer; + ssize_t nameLen; + + footer.type = tolel(FOOTER_MAGIC_V1); + footer.entityCount = tolel(m_entityCount); + + amt = write(m_fd, &footer, sizeof(app_footer_v1)); + if (amt != sizeof(app_footer_v1)) { + m_status = errno; + return m_status; + } + m_pos += amt; + + return NO_ERROR; +} + +} // namespace android diff --git a/libs/utils/file_backup_helper.cpp b/libs/utils/backup_helper_file.cpp similarity index 79% rename from libs/utils/file_backup_helper.cpp rename to libs/utils/backup_helper_file.cpp index 453084ace..1fa6a0f02 100644 --- a/libs/utils/file_backup_helper.cpp +++ b/libs/utils/backup_helper_file.cpp @@ -1,3 +1,19 @@ +/* + * 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. + */ + #define LOG_TAG "file_backup_helper" #include @@ -19,13 +35,16 @@ #include -using namespace android; +namespace android { #define MAGIC0 0x70616e53 // Snap #define MAGIC1 0x656c6946 // File +#if TEST_BACKUP_HELPERS +#define LOGP(x...) printf(x) +#else #define LOGP(x...) LOGD(x) -//#define LOGP(x...) printf(x) +#endif struct SnapshotHeader { int magic0; @@ -118,6 +137,8 @@ write_snapshot_file(int fd, const KeyedVector& snapshot) bytesWritten += sizeof(FileState) + round_up(name.length()); } + LOGP("write_snapshot_file fd=%d\n", fd); + int amt; SnapshotHeader header = { MAGIC0, N, MAGIC1, bytesWritten }; @@ -248,11 +269,13 @@ back_up_files(int oldSnapshotFD, int oldDataStream, int newSnapshotFD, // file added String8 realFilename(base); realFilename.appendPath(q); + LOGP("file added: %s\n", realFilename.string()); write_update_file(realFilename, q); m++; } else if (cmp < 0) { // file removed + LOGP("file removed: %s\n", p.string()); write_delete_file(p); n++; } @@ -573,6 +596,107 @@ backup_helper_test_four() return matched ? 0 : 1; } +// hexdump -v -e '" " 8/1 " 0x%02x," "\n"' data_writer.data +const unsigned char DATA_GOLDEN_FILE[] = { + 0x41, 0x70, 0x70, 0x31, 0x0b, 0x00, 0x00, 0x00, + 0x6e, 0x6f, 0x5f, 0x70, 0x61, 0x64, 0x64, 0x69, + 0x6e, 0x67, 0x5f, 0x00, 0x44, 0x61, 0x74, 0x61, + 0x0b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x6e, 0x6f, 0x5f, 0x70, 0x61, 0x64, 0x64, 0x69, + 0x6e, 0x67, 0x5f, 0x00, 0x6e, 0x6f, 0x5f, 0x70, + 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x00, + 0x41, 0x70, 0x70, 0x31, 0x0c, 0x00, 0x00, 0x00, + 0x70, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x74, + 0x6f, 0x5f, 0x5f, 0x33, 0x00, 0xbc, 0xbc, 0xbc, + 0x44, 0x61, 0x74, 0x61, 0x0c, 0x00, 0x00, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x70, 0x61, 0x64, 0x64, + 0x65, 0x64, 0x5f, 0x74, 0x6f, 0x5f, 0x5f, 0x33, + 0x00, 0xbc, 0xbc, 0xbc, 0x70, 0x61, 0x64, 0x64, + 0x65, 0x64, 0x5f, 0x74, 0x6f, 0x5f, 0x5f, 0x33, + 0x00, 0xbc, 0xbc, 0xbc, 0x41, 0x70, 0x70, 0x31, + 0x0d, 0x00, 0x00, 0x00, 0x70, 0x61, 0x64, 0x64, + 0x65, 0x64, 0x5f, 0x74, 0x6f, 0x5f, 0x32, 0x5f, + 0x5f, 0x00, 0xbc, 0xbc, 0x44, 0x61, 0x74, 0x61, + 0x0d, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x70, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x74, + 0x6f, 0x5f, 0x32, 0x5f, 0x5f, 0x00, 0xbc, 0xbc, + 0x70, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x74, + 0x6f, 0x5f, 0x32, 0x5f, 0x5f, 0x00, 0xbc, 0xbc, + 0x41, 0x70, 0x70, 0x31, 0x0a, 0x00, 0x00, 0x00, + 0x70, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x74, + 0x6f, 0x31, 0x00, 0xbc, 0x44, 0x61, 0x74, 0x61, + 0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x70, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x74, + 0x6f, 0x31, 0x00, 0xbc, 0x70, 0x61, 0x64, 0x64, + 0x65, 0x64, 0x5f, 0x74, 0x6f, 0x31, 0x00, 0xbc, + 0x46, 0x6f, 0x6f, 0x74, 0x04, 0x00, 0x00, 0x00, +}; +const int DATA_GOLDEN_FILE_SIZE = sizeof(DATA_GOLDEN_FILE); + +static int +test_write_header_and_entity(BackupDataWriter& writer, const char* str) +{ + int err; + String8 text(str); + + err = writer.WriteAppHeader(text); + if (err != 0) { + fprintf(stderr, "WriteAppHeader failed with %s\n", strerror(err)); + return err; + } + + err = writer.WriteEntityHeader(text, text.length()+1); + if (err != 0) { + fprintf(stderr, "WriteEntityHeader failed with %s\n", strerror(err)); + return err; + } + + err = writer.WriteEntityData(text.string(), text.length()+1); + if (err != 0) { + fprintf(stderr, "write failed for data '%s'\n", text.string()); + return errno; + } + + return err; +} + +int +backup_helper_test_data_writer() +{ + int err; + int fd; + const char* filename = SCRATCH_DIR "data_writer.data"; + + system("rm -r " SCRATCH_DIR); + mkdir(SCRATCH_DIR, 0777); + mkdir(SCRATCH_DIR "data", 0777); + + fd = creat(filename, 0666); + if (fd == -1) { + fprintf(stderr, "error creating: %s\n", strerror(errno)); + return errno; + } + + BackupDataWriter writer(fd); + + err = 0; + err |= test_write_header_and_entity(writer, "no_padding_"); + err |= test_write_header_and_entity(writer, "padded_to__3"); + err |= test_write_header_and_entity(writer, "padded_to_2__"); + err |= test_write_header_and_entity(writer, "padded_to1"); + + writer.WriteAppFooter(); + + close(fd); + + err = compare_file(filename, DATA_GOLDEN_FILE, DATA_GOLDEN_FILE_SIZE); + if (err != 0) { + return err; + } + + return err; +} + static int get_mod_time(const char* filename, struct timeval times[2]) { @@ -594,8 +718,9 @@ int backup_helper_test_files() { int err; - int newSnapshotFD; int oldSnapshotFD; + int dataStreamFD; + int newSnapshotFD; system("rm -r " SCRATCH_DIR); mkdir(SCRATCH_DIR, 0777); @@ -616,17 +741,24 @@ backup_helper_test_files() "data/f" }; + dataStreamFD = creat(SCRATCH_DIR "1.data", 0666); + if (dataStreamFD == -1) { + fprintf(stderr, "error creating: %s\n", strerror(errno)); + return errno; + } + newSnapshotFD = creat(SCRATCH_DIR "before.snap", 0666); if (newSnapshotFD == -1) { fprintf(stderr, "error creating: %s\n", strerror(errno)); return errno; } - - err = back_up_files(-1, newSnapshotFD, 0, SCRATCH_DIR, files_before, 5); + + err = back_up_files(-1, dataStreamFD, newSnapshotFD, SCRATCH_DIR, files_before, 5); if (err != 0) { return err; } + close(dataStreamFD); close(newSnapshotFD); sleep(3); @@ -665,21 +797,30 @@ backup_helper_test_files() return errno; } + dataStreamFD = creat(SCRATCH_DIR "2.data", 0666); + if (dataStreamFD == -1) { + fprintf(stderr, "error creating: %s\n", strerror(errno)); + return errno; + } + newSnapshotFD = creat(SCRATCH_DIR "after.snap", 0666); if (newSnapshotFD == -1) { fprintf(stderr, "error creating: %s\n", strerror(errno)); return errno; } - err = back_up_files(oldSnapshotFD, newSnapshotFD, 0, SCRATCH_DIR, files_after, 6); + err = back_up_files(oldSnapshotFD, dataStreamFD, newSnapshotFD, SCRATCH_DIR, files_after, 6); if (err != 0) { return err; } close(oldSnapshotFD); + close(dataStreamFD); close(newSnapshotFD); return 0; } #endif // TEST_BACKUP_HELPERS + +} From 9db34413c7326960377c982a1844ffa13e020334 Mon Sep 17 00:00:00 2001 From: Eric Fischer Date: Tue, 12 May 2009 16:29:46 -0700 Subject: [PATCH 098/541] Start using CLDR for some date-and-time strings and formats. Use java.text.DateFormat where possible, since that is already using the CLDR data for the things it supports. Remove an unused date format object from DatePickerDialog. Add a new method for getting the standalone month names from applications, although @hidden for now because it is an API change. Pass the standalone month names down to native code in Time so that tztime's strftime() can use them. And then the bulk of the change: replace all the names for the months and the days of the week, and AM and PM, and yesterday, today, and tomorrow, with strings from CLDR. And replace several of the date format strings with ones derived from CLDR, but reformatted to use strftime() style instead of SimpleDateFormat style. All these resource changes go into new donottranslate-cldr.xml files and are removed from strings.xml so that they aren't part of the normal translation process and the translators don't have to bother with them (and risk messing them up). --- NOTICE | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/NOTICE b/NOTICE index 267a6aafd..bb9c5f269 100644 --- a/NOTICE +++ b/NOTICE @@ -220,3 +220,54 @@ the Apache2 License. END OF TERMS AND CONDITIONS + + +UNICODE, INC. LICENSE AGREEMENT - DATA FILES AND SOFTWARE + +Unicode Data Files include all data files under the directories +http://www.unicode.org/Public/, http://www.unicode.org/reports/, +and http://www.unicode.org/cldr/data/ . Unicode Software includes any +source code published in the Unicode Standard or under the directories +http://www.unicode.org/Public/, http://www.unicode.org/reports/, and +http://www.unicode.org/cldr/data/. + +NOTICE TO USER: Carefully read the following legal agreement. BY +DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING UNICODE INC.'S DATA +FILES ("DATA FILES"), AND/OR SOFTWARE ("SOFTWARE"), YOU UNEQUIVOCALLY +ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE TERMS AND CONDITIONS OF +THIS AGREEMENT. IF YOU DO NOT AGREE, DO NOT DOWNLOAD, INSTALL, COPY, +DISTRIBUTE OR USE THE DATA FILES OR SOFTWARE. + +COPYRIGHT AND PERMISSION NOTICE + +Copyright © 1991-2008 Unicode, Inc. All rights reserved. Distributed +under the Terms of Use in http://www.unicode.org/copyright.html. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Unicode data files and any associated documentation (the +"Data Files") or Unicode software and any associated documentation (the +"Software") to deal in the Data Files or Software without restriction, +including without limitation the rights to use, copy, modify, merge, +publish, distribute, and/or sell copies of the Data Files or Software, +and to permit persons to whom the Data Files or Software are furnished to +do so, provided that (a) the above copyright notice(s) and this permission +notice appear with all copies of the Data Files or Software, (b) both the +above copyright notice(s) and this permission notice appear in associated +documentation, and (c) there is clear notice in each modified Data File +or in the Software as well as in the documentation associated with the +Data File(s) or Software that the data or software has been modified. + +THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS +INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT +OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS +OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE +OR PERFORMANCE OF THE DATA FILES OR SOFTWARE. + +Except as contained in this notice, the name of a copyright holder +shall not be used in advertising or otherwise to promote the sale, use +or other dealings in these Data Files or Software without prior written +authorization of the copyright holder. From cd1c1c86c3cf6a952b5f175753460a8fa0477731 Mon Sep 17 00:00:00 2001 From: Joe Onorato Date: Fri, 15 May 2009 18:20:19 -0400 Subject: [PATCH 099/541] Add the backup data file writer C++ class. --- include/utils/backup_helpers.h | 65 ++++++++- libs/utils/backup_data.cpp | 228 +++++++++++++++++++++++++++--- libs/utils/backup_helper_file.cpp | 191 ++++++++++++++++++++++--- 3 files changed, 447 insertions(+), 37 deletions(-) diff --git a/include/utils/backup_helpers.h b/include/utils/backup_helpers.h index 73b998912..0c59fec59 100644 --- a/include/utils/backup_helpers.h +++ b/include/utils/backup_helpers.h @@ -25,8 +25,28 @@ namespace android { int back_up_files(int oldSnapshotFD, int oldDataStream, int newSnapshotFD, char const* fileBase, char const* const* files, int fileCount); +// the sizes of all of these match. +typedef struct { + int type; // == APP_MAGIC_V1 + int packageLen; // length of the name of the package that follows, not including the null. + int cookie; +} app_header_v1; + +typedef struct { + int type; // ENTITY_MAGIC_V1 + int keyLen; // length of the key name, not including the null terminator + int dataSize; // size of the data, not including the padding +} entity_header_v1; + +typedef struct { + int type; // FOOTER_MAGIC_V1 + int entityCount; // the number of entities that were written + int cookie; +} app_footer_v1; + + /** - * Reads the data. + * Writes the data. * * If an error occurs, it poisons this object and all write calls will fail * with the error that occurred. @@ -38,12 +58,12 @@ public: // does not close fd ~BackupDataWriter(); - status_t WriteAppHeader(const String8& packageName); + status_t WriteAppHeader(const String8& packageName, int cookie); status_t WriteEntityHeader(const String8& key, size_t dataSize); status_t WriteEntityData(const void* data, size_t size); - status_t WriteAppFooter(); + status_t WriteAppFooter(int cookie); private: explicit BackupDataWriter(); @@ -55,6 +75,44 @@ private: int m_entityCount; }; +/** + * Reads the data. + * + * If an error occurs, it poisons this object and all write calls will fail + * with the error that occurred. + */ +class BackupDataReader +{ +public: + BackupDataReader(int fd); + // does not close fd + ~BackupDataReader(); + + status_t Status(); + status_t ReadNextHeader(); + + status_t ReadAppHeader(String8* packageName, int* cookie); + bool HasEntities(); + status_t ReadEntityHeader(String8* key, size_t* dataSize); + status_t ReadEntityData(void* data, size_t size); + status_t ReadAppFooter(int* cookie); + +private: + explicit BackupDataReader(); + status_t skip_padding(); + + int m_fd; + status_t m_status; + ssize_t m_pos; + int m_entityCount; + union { + int type; + app_header_v1 app; + entity_header_v1 entity; + app_footer_v1 footer; + } m_header; +}; + #define TEST_BACKUP_HELPERS 0 #if TEST_BACKUP_HELPERS @@ -62,6 +120,7 @@ int backup_helper_test_empty(); int backup_helper_test_four(); int backup_helper_test_files(); int backup_helper_test_data_writer(); +int backup_helper_test_data_reader(); #endif } // namespace android diff --git a/libs/utils/backup_data.cpp b/libs/utils/backup_data.cpp index c7f1fdb58..dd044496c 100644 --- a/libs/utils/backup_data.cpp +++ b/libs/utils/backup_data.cpp @@ -14,12 +14,16 @@ * limitations under the License. */ +#define LOG_TAG "backup_data" + #include #include #include #include +#include + namespace android { /* @@ -39,22 +43,6 @@ namespace android { #define ENTITY_MAGIC_V1 0x61746144 // Data (little endian) #define FOOTER_MAGIC_V1 0x746f6f46 // Foot (little endian) -typedef struct { - int type; // == APP_MAGIC_V1 - int packageLen; // length of the name of the package that follows, not including the null. -} app_header_v1; - -typedef struct { - int type; // ENTITY_MAGIC_V1 - int keyLen; // length of the key name, not including the null terminator - int dataSize; // size of the data, not including the padding -} entity_header_v1; - -typedef struct { - int type; // FOOTER_MAGIC_V1 - int entityCount; // the number of entities that were written -} app_footer_v1; - const static int ROUND_UP[4] = { 0, 3, 2, 1 }; static inline size_t @@ -102,7 +90,7 @@ BackupDataWriter::write_padding_for(int n) } status_t -BackupDataWriter::WriteAppHeader(const String8& packageName) +BackupDataWriter::WriteAppHeader(const String8& packageName, int cookie) { if (m_status != NO_ERROR) { return m_status; @@ -122,6 +110,7 @@ BackupDataWriter::WriteAppHeader(const String8& packageName) header.type = tolel(APP_MAGIC_V1); header.packageLen = tolel(nameLen); + header.cookie = cookie; amt = write(m_fd, &header, sizeof(app_header_v1)); if (amt != sizeof(app_header_v1)) { @@ -204,7 +193,7 @@ BackupDataWriter::WriteEntityData(const void* data, size_t size) } status_t -BackupDataWriter::WriteAppFooter() +BackupDataWriter::WriteAppFooter(int cookie) { if (m_status != NO_ERROR) { return m_status; @@ -222,6 +211,7 @@ BackupDataWriter::WriteAppFooter() footer.type = tolel(FOOTER_MAGIC_V1); footer.entityCount = tolel(m_entityCount); + footer.cookie = cookie; amt = write(m_fd, &footer, sizeof(app_footer_v1)); if (amt != sizeof(app_footer_v1)) { @@ -233,4 +223,206 @@ BackupDataWriter::WriteAppFooter() return NO_ERROR; } + +BackupDataReader::BackupDataReader(int fd) + :m_fd(fd), + m_status(NO_ERROR), + m_pos(0), + m_entityCount(0) +{ + memset(&m_header, 0, sizeof(m_header)); +} + +BackupDataReader::~BackupDataReader() +{ +} + +status_t +BackupDataReader::Status() +{ + return m_status; +} + +#define CHECK_SIZE(actual, expected) \ + do { \ + if ((actual) != (expected)) { \ + if ((actual) == 0) { \ + m_status = EIO; \ + } else { \ + m_status = errno; \ + } \ + return m_status; \ + } \ + } while(0) +#define SKIP_PADDING() \ + do { \ + status_t err = skip_padding(); \ + if (err != NO_ERROR) { \ + m_status = err; \ + return err; \ + } \ + } while(0) + +status_t +BackupDataReader::ReadNextHeader() +{ + if (m_status != NO_ERROR) { + return m_status; + } + + int amt; + + SKIP_PADDING(); + amt = read(m_fd, &m_header, sizeof(m_header)); + CHECK_SIZE(amt, sizeof(m_header)); + + // validate and fix up the fields. + m_header.type = fromlel(m_header.type); + switch (m_header.type) + { + case APP_MAGIC_V1: + m_header.app.packageLen = fromlel(m_header.app.packageLen); + if (m_header.app.packageLen < 0) { + LOGD("App header at %d has packageLen<0: 0x%08x\n", (int)m_pos, + (int)m_header.app.packageLen); + m_status = EINVAL; + } + m_header.app.cookie = m_header.app.cookie; + break; + case ENTITY_MAGIC_V1: + m_header.entity.keyLen = fromlel(m_header.entity.keyLen); + if (m_header.entity.keyLen <= 0) { + LOGD("Entity header at %d has keyLen<=0: 0x%08x\n", (int)m_pos, + (int)m_header.entity.keyLen); + m_status = EINVAL; + } + m_header.entity.dataSize = fromlel(m_header.entity.dataSize); + if (m_header.entity.dataSize < 0) { + LOGD("Entity header at %d has dataSize<0: 0x%08x\n", (int)m_pos, + (int)m_header.entity.dataSize); + m_status = EINVAL; + } + m_entityCount++; + break; + case FOOTER_MAGIC_V1: + m_header.footer.entityCount = fromlel(m_header.footer.entityCount); + if (m_header.footer.entityCount < 0) { + LOGD("Entity header at %d has entityCount<0: 0x%08x\n", (int)m_pos, + (int)m_header.footer.entityCount); + m_status = EINVAL; + } + m_header.footer.cookie = m_header.footer.cookie; + break; + default: + LOGD("Chunk header at %d has invalid type: 0x%08x", (int)m_pos, (int)m_header.type); + m_status = EINVAL; + } + m_pos += sizeof(m_header); + + return m_status; +} + +status_t +BackupDataReader::ReadAppHeader(String8* packageName, int* cookie) +{ + if (m_status != NO_ERROR) { + return m_status; + } + if (m_header.type != APP_MAGIC_V1) { + return EINVAL; + } + size_t size = m_header.app.packageLen; + char* buf = packageName->lockBuffer(size); + if (packageName == NULL) { + packageName->unlockBuffer(); + m_status = ENOMEM; + return m_status; + } + int amt = read(m_fd, buf, size+1); + CHECK_SIZE(amt, (int)size+1); + packageName->unlockBuffer(size); + m_pos += size+1; + *cookie = m_header.app.cookie; + return NO_ERROR; +} + +bool +BackupDataReader::HasEntities() +{ + return m_status == NO_ERROR && m_header.type == ENTITY_MAGIC_V1; +} + +status_t +BackupDataReader::ReadEntityHeader(String8* key, size_t* dataSize) +{ + if (m_status != NO_ERROR) { + return m_status; + } + if (m_header.type != ENTITY_MAGIC_V1) { + return EINVAL; + } + size_t size = m_header.app.packageLen; + char* buf = key->lockBuffer(size); + if (key == NULL) { + key->unlockBuffer(); + m_status = ENOMEM; + return m_status; + } + int amt = read(m_fd, buf, size+1); + CHECK_SIZE(amt, (int)size+1); + key->unlockBuffer(size); + m_pos += size+1; + *dataSize = m_header.entity.dataSize; + SKIP_PADDING(); + return NO_ERROR; +} + +status_t +BackupDataReader::ReadEntityData(void* data, size_t size) +{ + if (m_status != NO_ERROR) { + return m_status; + } + int amt = read(m_fd, data, size); + CHECK_SIZE(amt, (int)size); + m_pos += size; + return NO_ERROR; +} + +status_t +BackupDataReader::ReadAppFooter(int* cookie) +{ + if (m_status != NO_ERROR) { + return m_status; + } + if (m_header.type != FOOTER_MAGIC_V1) { + return EINVAL; + } + if (m_header.footer.entityCount != m_entityCount) { + LOGD("entity count mismatch actual=%d expected=%d", m_entityCount, + m_header.footer.entityCount); + m_status = EINVAL; + return m_status; + } + *cookie = m_header.footer.cookie; + return NO_ERROR; +} + +status_t +BackupDataReader::skip_padding() +{ + ssize_t amt; + ssize_t paddingSize; + + paddingSize = padding_extra(m_pos); + if (paddingSize > 0) { + uint32_t padding; + amt = read(m_fd, &padding, paddingSize); + CHECK_SIZE(amt, paddingSize); + m_pos += amt; + } + return NO_ERROR; +} + + } // namespace android diff --git a/libs/utils/backup_helper_file.cpp b/libs/utils/backup_helper_file.cpp index 1fa6a0f02..bf569455d 100644 --- a/libs/utils/backup_helper_file.cpp +++ b/libs/utils/backup_helper_file.cpp @@ -599,13 +599,14 @@ backup_helper_test_four() // hexdump -v -e '" " 8/1 " 0x%02x," "\n"' data_writer.data const unsigned char DATA_GOLDEN_FILE[] = { 0x41, 0x70, 0x70, 0x31, 0x0b, 0x00, 0x00, 0x00, - 0x6e, 0x6f, 0x5f, 0x70, 0x61, 0x64, 0x64, 0x69, - 0x6e, 0x67, 0x5f, 0x00, 0x44, 0x61, 0x74, 0x61, - 0x0b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, - 0x6e, 0x6f, 0x5f, 0x70, 0x61, 0x64, 0x64, 0x69, - 0x6e, 0x67, 0x5f, 0x00, 0x6e, 0x6f, 0x5f, 0x70, + 0xdd, 0xcc, 0xbb, 0xaa, 0x6e, 0x6f, 0x5f, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x00, - 0x41, 0x70, 0x70, 0x31, 0x0c, 0x00, 0x00, 0x00, + 0x44, 0x61, 0x74, 0x61, 0x0b, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x6e, 0x6f, 0x5f, 0x70, + 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x00, + 0x6e, 0x6f, 0x5f, 0x70, 0x61, 0x64, 0x64, 0x69, + 0x6e, 0x67, 0x5f, 0x00, 0x41, 0x70, 0x70, 0x31, + 0x0c, 0x00, 0x00, 0x00, 0xdd, 0xcc, 0xbb, 0xaa, 0x70, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x74, 0x6f, 0x5f, 0x5f, 0x33, 0x00, 0xbc, 0xbc, 0xbc, 0x44, 0x61, 0x74, 0x61, 0x0c, 0x00, 0x00, 0x00, @@ -614,15 +615,16 @@ const unsigned char DATA_GOLDEN_FILE[] = { 0x00, 0xbc, 0xbc, 0xbc, 0x70, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x74, 0x6f, 0x5f, 0x5f, 0x33, 0x00, 0xbc, 0xbc, 0xbc, 0x41, 0x70, 0x70, 0x31, - 0x0d, 0x00, 0x00, 0x00, 0x70, 0x61, 0x64, 0x64, + 0x0d, 0x00, 0x00, 0x00, 0xdd, 0xcc, 0xbb, 0xaa, + 0x70, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x74, + 0x6f, 0x5f, 0x32, 0x5f, 0x5f, 0x00, 0xbc, 0xbc, + 0x44, 0x61, 0x74, 0x61, 0x0d, 0x00, 0x00, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x70, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x74, 0x6f, 0x5f, 0x32, 0x5f, - 0x5f, 0x00, 0xbc, 0xbc, 0x44, 0x61, 0x74, 0x61, - 0x0d, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, - 0x70, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x74, - 0x6f, 0x5f, 0x32, 0x5f, 0x5f, 0x00, 0xbc, 0xbc, - 0x70, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x74, - 0x6f, 0x5f, 0x32, 0x5f, 0x5f, 0x00, 0xbc, 0xbc, - 0x41, 0x70, 0x70, 0x31, 0x0a, 0x00, 0x00, 0x00, + 0x5f, 0x00, 0xbc, 0xbc, 0x70, 0x61, 0x64, 0x64, + 0x65, 0x64, 0x5f, 0x74, 0x6f, 0x5f, 0x32, 0x5f, + 0x5f, 0x00, 0xbc, 0xbc, 0x41, 0x70, 0x70, 0x31, + 0x0a, 0x00, 0x00, 0x00, 0xdd, 0xcc, 0xbb, 0xaa, 0x70, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x74, 0x6f, 0x31, 0x00, 0xbc, 0x44, 0x61, 0x74, 0x61, 0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, @@ -630,6 +632,7 @@ const unsigned char DATA_GOLDEN_FILE[] = { 0x6f, 0x31, 0x00, 0xbc, 0x70, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x74, 0x6f, 0x31, 0x00, 0xbc, 0x46, 0x6f, 0x6f, 0x74, 0x04, 0x00, 0x00, 0x00, + 0x99, 0x99, 0x77, 0x77 }; const int DATA_GOLDEN_FILE_SIZE = sizeof(DATA_GOLDEN_FILE); @@ -639,7 +642,7 @@ test_write_header_and_entity(BackupDataWriter& writer, const char* str) int err; String8 text(str); - err = writer.WriteAppHeader(text); + err = writer.WriteAppHeader(text, 0xaabbccdd); if (err != 0) { fprintf(stderr, "WriteAppHeader failed with %s\n", strerror(err)); return err; @@ -685,7 +688,7 @@ backup_helper_test_data_writer() err |= test_write_header_and_entity(writer, "padded_to_2__"); err |= test_write_header_and_entity(writer, "padded_to1"); - writer.WriteAppFooter(); + writer.WriteAppFooter(0x77779999); close(fd); @@ -697,6 +700,162 @@ backup_helper_test_data_writer() return err; } +int +test_read_header_and_entity(BackupDataReader& reader, const char* str) +{ + int err; + int bufSize = strlen(str)+1; + char* buf = (char*)malloc(bufSize); + String8 string; + int cookie = 0x11111111; + size_t actualSize; + + // printf("\n\n---------- test_read_header_and_entity -- %s\n\n", str); + + err = reader.ReadNextHeader(); + if (err != 0) { + fprintf(stderr, "ReadNextHeader (for app header) failed with %s\n", strerror(err)); + goto done; + } + + err = reader.ReadAppHeader(&string, &cookie); + if (err != 0) { + fprintf(stderr, "ReadAppHeader failed with %s\n", strerror(err)); + goto done; + } + if (string != str) { + fprintf(stderr, "ReadAppHeader expected packageName '%s' got '%s'\n", str, string.string()); + err = EINVAL; + goto done; + } + if (cookie != (int)0xaabbccdd) { + fprintf(stderr, "ReadAppHeader expected cookie 0x%08x got 0x%08x\n", 0xaabbccdd, cookie); + err = EINVAL; + goto done; + } + + err = reader.ReadNextHeader(); + if (err != 0) { + fprintf(stderr, "ReadNextHeader (for entity header) failed with %s\n", strerror(err)); + goto done; + } + + err = reader.ReadEntityHeader(&string, &actualSize); + if (err != 0) { + fprintf(stderr, "ReadEntityHeader failed with %s\n", strerror(err)); + goto done; + } + if (string != str) { + fprintf(stderr, "ReadEntityHeader expected key '%s' got '%s'\n", str, string.string()); + err = EINVAL; + goto done; + } + if ((int)actualSize != bufSize) { + fprintf(stderr, "ReadEntityHeader expected dataSize 0x%08x got 0x%08x\n", bufSize, + actualSize); + err = EINVAL; + goto done; + } + + err = reader.ReadEntityData(buf, bufSize); + if (err != NO_ERROR) { + fprintf(stderr, "ReadEntityData failed with %s\n", strerror(err)); + goto done; + } + + if (0 != memcmp(buf, str, bufSize)) { + fprintf(stderr, "ReadEntityData expected '%s' but got something starting with " + "%02x %02x %02x %02x\n", str, buf[0], buf[1], buf[2], buf[3]); + err = EINVAL; + goto done; + } + + // The next read will confirm whether it got the right amount of data. + +done: + if (err != NO_ERROR) { + fprintf(stderr, "test_read_header_and_entity failed with %s\n", strerror(err)); + } + free(buf); + return err; +} + +int +backup_helper_test_data_reader() +{ + int err; + int fd; + const char* filename = SCRATCH_DIR "data_reader.data"; + + system("rm -r " SCRATCH_DIR); + mkdir(SCRATCH_DIR, 0777); + mkdir(SCRATCH_DIR "data", 0777); + + fd = creat(filename, 0666); + if (fd == -1) { + fprintf(stderr, "error creating: %s\n", strerror(errno)); + return errno; + } + + err = write(fd, DATA_GOLDEN_FILE, DATA_GOLDEN_FILE_SIZE); + if (err != DATA_GOLDEN_FILE_SIZE) { + fprintf(stderr, "Error \"%s\" writing golden file %s\n", strerror(errno), filename); + return errno; + } + + close(fd); + + fd = open(filename, O_RDONLY); + if (fd == -1) { + fprintf(stderr, "Error \"%s\" opening golden file %s for read\n", strerror(errno), + filename); + return errno; + } + + { + BackupDataReader reader(fd); + + err = 0; + + if (err == NO_ERROR) { + err = test_read_header_and_entity(reader, "no_padding_"); + } + + if (err == NO_ERROR) { + err = test_read_header_and_entity(reader, "padded_to__3"); + } + + if (err == NO_ERROR) { + err = test_read_header_and_entity(reader, "padded_to_2__"); + } + + if (err == NO_ERROR) { + err = test_read_header_and_entity(reader, "padded_to1"); + } + + if (err == NO_ERROR) { + err = reader.ReadNextHeader(); + if (err != 0) { + fprintf(stderr, "ReadNextHeader (for app header) failed with %s\n", strerror(err)); + } + + if (err == NO_ERROR) { + int cookie; + err |= reader.ReadAppFooter(&cookie); + if (cookie != 0x77779999) { + fprintf(stderr, "app footer cookie expected=0x%08x actual=0x%08x\n", + 0x77779999, cookie); + err = EINVAL; + } + } + } + } + + close(fd); + + return err; +} + static int get_mod_time(const char* filename, struct timeval times[2]) { From 7ac7ecb6bd7358f7e9b159a112d8a0a260145664 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Mon, 18 May 2009 15:22:00 -0700 Subject: [PATCH 100/541] Update aapt badging for native code, configs, density, etc. --- include/utils/AssetManager.h | 12 ++++++++ libs/utils/AssetManager.cpp | 59 ++++++++++++++++++++++++++++++++++-- 2 files changed, 69 insertions(+), 2 deletions(-) diff --git a/include/utils/AssetManager.h b/include/utils/AssetManager.h index e94c0e8fe..c11429eaf 100644 --- a/include/utils/AssetManager.h +++ b/include/utils/AssetManager.h @@ -152,6 +152,18 @@ public: */ AssetDir* openDir(const char* dirName); + /* + * Open a directory within a particular path of the asset manager. + * + * The contents of the directory are an amalgam of vendor-specific, + * locale-specific, and generic assets stored loosely or in asset + * packages. Depending on the cache setting and previous accesses, + * this call may incur significant disk overhead. + * + * To open the top-level directory, pass in "". + */ + AssetDir* openNonAssetDir(void* cookie, const char* dirName); + /* * Get the type of a file in the asset hierarchy. They will either * be "regular" or "directory". [Currently only works for "regular".] diff --git a/libs/utils/AssetManager.cpp b/libs/utils/AssetManager.cpp index 447b80193..4126bfb7a 100644 --- a/libs/utils/AssetManager.cpp +++ b/libs/utils/AssetManager.cpp @@ -900,6 +900,60 @@ AssetDir* AssetManager::openDir(const char* dirName) return pDir; } +/* + * Open a directory in the non-asset namespace. + * + * An "asset directory" is simply the combination of all files in all + * locations, with ".gz" stripped for loose files. With app, locale, and + * vendor defined, we have 8 directories and 2 Zip archives to scan. + * + * Pass in "" for the root dir. + */ +AssetDir* AssetManager::openNonAssetDir(void* cookie, const char* dirName) +{ + AutoMutex _l(mLock); + + AssetDir* pDir = NULL; + SortedVector* pMergedInfo = NULL; + + LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager"); + assert(dirName != NULL); + + //printf("+++ openDir(%s) in '%s'\n", dirName, (const char*) mAssetBase); + + if (mCacheMode != CACHE_OFF && !mCacheValid) + loadFileNameCacheLocked(); + + pDir = new AssetDir; + + pMergedInfo = new SortedVector; + + const size_t which = ((size_t)cookie)-1; + + if (which < mAssetPaths.size()) { + const asset_path& ap = mAssetPaths.itemAt(which); + if (ap.type == kFileTypeRegular) { + LOGV("Adding directory %s from zip %s", dirName, ap.path.string()); + scanAndMergeZipLocked(pMergedInfo, ap, NULL, dirName); + } else { + LOGV("Adding directory %s from dir %s", dirName, ap.path.string()); + scanAndMergeDirLocked(pMergedInfo, ap, NULL, dirName); + } + } + +#if 0 + printf("FILE LIST:\n"); + for (i = 0; i < (size_t) pMergedInfo->size(); i++) { + printf(" %d: (%d) '%s'\n", i, + pMergedInfo->itemAt(i).getFileType(), + (const char*) pMergedInfo->itemAt(i).getFileName()); + } +#endif + + pDir->setFileList(pMergedInfo); + return pDir; +} + /* * Scan the contents of the specified directory and merge them into the * "pMergedInfo" vector, removing previous entries if we find "exclude" @@ -1143,6 +1197,7 @@ bool AssetManager::scanAndMergeZipLocked(SortedVector* pMerg LOGE("ARGH: name too long?\n"); continue; } + //printf("Comparing %s in %s?\n", nameBuf, dirName.string()); if (dirNameLen == 0 || (strncmp(nameBuf, dirName.string(), dirNameLen) == 0 && nameBuf[dirNameLen] == '/')) @@ -1165,7 +1220,7 @@ bool AssetManager::scanAndMergeZipLocked(SortedVector* pMerg createZipSourceNameLocked(zipName, dirName, info.getFileName())); contents.add(info); - //printf("FOUND: file '%s'\n", (const char*) info.mFileName); + //printf("FOUND: file '%s'\n", info.getFileName().string()); } else { /* this is a subdir; add it if we don't already have it*/ String8 subdirName(cp, nextSlash - cp); @@ -1181,7 +1236,7 @@ bool AssetManager::scanAndMergeZipLocked(SortedVector* pMerg dirs.add(subdirName); } - //printf("FOUND: dir '%s'\n", (const char*) subdirName); + //printf("FOUND: dir '%s'\n", subdirName.string()); } } } From 062a488f1e6dd4ad2e1bc1a64bbfbbfdf40b9836 Mon Sep 17 00:00:00 2001 From: Joe Onorato Date: Tue, 19 May 2009 13:41:21 -0700 Subject: [PATCH 101/541] Hook up the backup data writer, and add a utility to read the backup data files. --- include/utils/backup_helpers.h | 24 +++-- libs/utils/backup_data.cpp | 53 ++++++----- libs/utils/backup_helper_file.cpp | 150 +++++++++++++++++++++++------- 3 files changed, 164 insertions(+), 63 deletions(-) diff --git a/include/utils/backup_helpers.h b/include/utils/backup_helpers.h index 0c59fec59..24b6c9e60 100644 --- a/include/utils/backup_helpers.h +++ b/include/utils/backup_helpers.h @@ -22,24 +22,27 @@ namespace android { -int back_up_files(int oldSnapshotFD, int oldDataStream, int newSnapshotFD, - char const* fileBase, char const* const* files, int fileCount); +enum { + BACKUP_HEADER_APP_V1 = 0x31707041, // App1 (little endian) + BACKUP_HEADER_ENTITY_V1 = 0x61746144, // Data (little endian) + BACKUP_FOOTER_APP_V1 = 0x746f6f46, // Foot (little endian) +}; // the sizes of all of these match. typedef struct { - int type; // == APP_MAGIC_V1 + int type; // == BACKUP_HEADER_APP_V1 int packageLen; // length of the name of the package that follows, not including the null. int cookie; } app_header_v1; typedef struct { - int type; // ENTITY_MAGIC_V1 + int type; // BACKUP_HEADER_ENTITY_V1 int keyLen; // length of the key name, not including the null terminator - int dataSize; // size of the data, not including the padding + int dataSize; // size of the data, not including the padding, -1 means delete } entity_header_v1; typedef struct { - int type; // FOOTER_MAGIC_V1 + int type; // BACKUP_FOOTER_APP_V1 int entityCount; // the number of entities that were written int cookie; } app_footer_v1; @@ -89,11 +92,12 @@ public: ~BackupDataReader(); status_t Status(); - status_t ReadNextHeader(); + status_t ReadNextHeader(int* type = NULL); status_t ReadAppHeader(String8* packageName, int* cookie); bool HasEntities(); status_t ReadEntityHeader(String8* key, size_t* dataSize); + status_t SkipEntityData(); // must be called with the pointer at the begining of the data. status_t ReadEntityData(void* data, size_t size); status_t ReadAppFooter(int* cookie); @@ -113,7 +117,11 @@ private: } m_header; }; -#define TEST_BACKUP_HELPERS 0 +int back_up_files(int oldSnapshotFD, BackupDataWriter* dataStream, int newSnapshotFD, + char const* fileBase, char const* const* files, int fileCount); + + +#define TEST_BACKUP_HELPERS 1 #if TEST_BACKUP_HELPERS int backup_helper_test_empty(); diff --git a/libs/utils/backup_data.cpp b/libs/utils/backup_data.cpp index dd044496c..95c05b7ed 100644 --- a/libs/utils/backup_data.cpp +++ b/libs/utils/backup_data.cpp @@ -39,10 +39,6 @@ namespace android { * - The value, padded to 4 byte boundary */ -#define APP_MAGIC_V1 0x31707041 // App1 (little endian) -#define ENTITY_MAGIC_V1 0x61746144 // Data (little endian) -#define FOOTER_MAGIC_V1 0x746f6f46 // Foot (little endian) - const static int ROUND_UP[4] = { 0, 3, 2, 1 }; static inline size_t @@ -108,7 +104,7 @@ BackupDataWriter::WriteAppHeader(const String8& packageName, int cookie) nameLen = packageName.length(); - header.type = tolel(APP_MAGIC_V1); + header.type = tolel(BACKUP_HEADER_APP_V1); header.packageLen = tolel(nameLen); header.cookie = cookie; @@ -148,7 +144,7 @@ BackupDataWriter::WriteEntityHeader(const String8& key, size_t dataSize) keyLen = key.length(); - header.type = tolel(ENTITY_MAGIC_V1); + header.type = tolel(BACKUP_HEADER_ENTITY_V1); header.keyLen = tolel(keyLen); header.dataSize = tolel(dataSize); @@ -209,7 +205,7 @@ BackupDataWriter::WriteAppFooter(int cookie) app_footer_v1 footer; ssize_t nameLen; - footer.type = tolel(FOOTER_MAGIC_V1); + footer.type = tolel(BACKUP_FOOTER_APP_V1); footer.entityCount = tolel(m_entityCount); footer.cookie = cookie; @@ -264,7 +260,7 @@ BackupDataReader::Status() } while(0) status_t -BackupDataReader::ReadNextHeader() +BackupDataReader::ReadNextHeader(int* type) { if (m_status != NO_ERROR) { return m_status; @@ -280,7 +276,7 @@ BackupDataReader::ReadNextHeader() m_header.type = fromlel(m_header.type); switch (m_header.type) { - case APP_MAGIC_V1: + case BACKUP_HEADER_APP_V1: m_header.app.packageLen = fromlel(m_header.app.packageLen); if (m_header.app.packageLen < 0) { LOGD("App header at %d has packageLen<0: 0x%08x\n", (int)m_pos, @@ -289,7 +285,7 @@ BackupDataReader::ReadNextHeader() } m_header.app.cookie = m_header.app.cookie; break; - case ENTITY_MAGIC_V1: + case BACKUP_HEADER_ENTITY_V1: m_header.entity.keyLen = fromlel(m_header.entity.keyLen); if (m_header.entity.keyLen <= 0) { LOGD("Entity header at %d has keyLen<=0: 0x%08x\n", (int)m_pos, @@ -297,14 +293,9 @@ BackupDataReader::ReadNextHeader() m_status = EINVAL; } m_header.entity.dataSize = fromlel(m_header.entity.dataSize); - if (m_header.entity.dataSize < 0) { - LOGD("Entity header at %d has dataSize<0: 0x%08x\n", (int)m_pos, - (int)m_header.entity.dataSize); - m_status = EINVAL; - } m_entityCount++; break; - case FOOTER_MAGIC_V1: + case BACKUP_FOOTER_APP_V1: m_header.footer.entityCount = fromlel(m_header.footer.entityCount); if (m_header.footer.entityCount < 0) { LOGD("Entity header at %d has entityCount<0: 0x%08x\n", (int)m_pos, @@ -318,6 +309,9 @@ BackupDataReader::ReadNextHeader() m_status = EINVAL; } m_pos += sizeof(m_header); + if (type) { + *type = m_header.type; + } return m_status; } @@ -328,7 +322,7 @@ BackupDataReader::ReadAppHeader(String8* packageName, int* cookie) if (m_status != NO_ERROR) { return m_status; } - if (m_header.type != APP_MAGIC_V1) { + if (m_header.type != BACKUP_HEADER_APP_V1) { return EINVAL; } size_t size = m_header.app.packageLen; @@ -349,7 +343,7 @@ BackupDataReader::ReadAppHeader(String8* packageName, int* cookie) bool BackupDataReader::HasEntities() { - return m_status == NO_ERROR && m_header.type == ENTITY_MAGIC_V1; + return m_status == NO_ERROR && m_header.type == BACKUP_HEADER_ENTITY_V1; } status_t @@ -358,10 +352,10 @@ BackupDataReader::ReadEntityHeader(String8* key, size_t* dataSize) if (m_status != NO_ERROR) { return m_status; } - if (m_header.type != ENTITY_MAGIC_V1) { + if (m_header.type != BACKUP_HEADER_ENTITY_V1) { return EINVAL; } - size_t size = m_header.app.packageLen; + size_t size = m_header.entity.keyLen; char* buf = key->lockBuffer(size); if (key == NULL) { key->unlockBuffer(); @@ -377,6 +371,23 @@ BackupDataReader::ReadEntityHeader(String8* key, size_t* dataSize) return NO_ERROR; } +status_t +BackupDataReader::SkipEntityData() +{ + if (m_status != NO_ERROR) { + return m_status; + } + if (m_header.type != BACKUP_HEADER_ENTITY_V1) { + return EINVAL; + } + if (m_header.entity.dataSize > 0) { + int pos = lseek(m_fd, m_header.entity.dataSize, SEEK_CUR); + return pos == -1 ? (int)errno : (int)NO_ERROR; + } else { + return NO_ERROR; + } +} + status_t BackupDataReader::ReadEntityData(void* data, size_t size) { @@ -395,7 +406,7 @@ BackupDataReader::ReadAppFooter(int* cookie) if (m_status != NO_ERROR) { return m_status; } - if (m_header.type != FOOTER_MAGIC_V1) { + if (m_header.type != BACKUP_FOOTER_APP_V1) { return EINVAL; } if (m_header.footer.entityCount != m_entityCount) { diff --git a/libs/utils/backup_helper_file.cpp b/libs/utils/backup_helper_file.cpp index bf569455d..8efb3eb8b 100644 --- a/libs/utils/backup_helper_file.cpp +++ b/libs/utils/backup_helper_file.cpp @@ -40,7 +40,7 @@ namespace android { #define MAGIC0 0x70616e53 // Snap #define MAGIC1 0x656c6946 // File -#if TEST_BACKUP_HELPERS +#if 0 // TEST_BACKUP_HELPERS #define LOGP(x...) printf(x) #else #define LOGP(x...) LOGD(x) @@ -181,45 +181,105 @@ write_snapshot_file(int fd, const KeyedVector& snapshot) } static int -write_delete_file(const String8& key) +write_delete_file(BackupDataWriter* dataStream, const String8& key) { LOGP("write_delete_file %s\n", key.string()); - return 0; + return dataStream->WriteEntityHeader(key, -1); } static int -write_update_file(const String8& realFilename, const String8& key) +write_update_file(BackupDataWriter* dataStream, int fd, const String8& key, + const String8& realFilename) { LOGP("write_update_file %s (%s)\n", realFilename.string(), key.string()); - return 0; + + const int bufsize = 4*1024; + int err; + int amt; + int fileSize; + int bytesLeft; + + char* buf = (char*)malloc(bufsize); + int crc = crc32(0L, Z_NULL, 0); + + + bytesLeft = fileSize = lseek(fd, 0, SEEK_END); + lseek(fd, 0, SEEK_SET); + + err = dataStream->WriteEntityHeader(key, bytesLeft); + if (err != 0) { + return err; + } + + while ((amt = read(fd, buf, bufsize)) != 0 && bytesLeft > 0) { + bytesLeft -= amt; + if (bytesLeft < 0) { + amt += bytesLeft; // Plus a negative is minus. Don't write more than we promised. + } + err = dataStream->WriteEntityData(buf, amt); + if (err != 0) { + return err; + } + } + if (bytesLeft != 0) { + if (bytesLeft > 0) { + // Pad out the space we promised in the buffer. We can't corrupt the buffer, + // even though the data we're sending is probably bad. + memset(buf, 0, bufsize); + while (bytesLeft > 0) { + amt = bytesLeft < bufsize ? bytesLeft : bufsize; + bytesLeft -= amt; + err = dataStream->WriteEntityData(buf, amt); + if (err != 0) { + return err; + } + } + } + LOGE("write_update_file size mismatch for %s. expected=%d actual=%d." + " You aren't doing proper locking!", + realFilename.string(), fileSize, fileSize-bytesLeft); + } + + free(buf); + + return NO_ERROR; } static int -compute_crc32(const String8& filename) +write_update_file(BackupDataWriter* dataStream, const String8& key, const String8& realFilename) +{ + int err; + int fd = open(realFilename.string(), O_RDONLY); + if (fd == -1) { + return errno; + } + err = write_update_file(dataStream, fd, key, realFilename); + close(fd); + return err; +} + +static int +compute_crc32(int fd) { const int bufsize = 4*1024; int amt; - int fd = open(filename.string(), O_RDONLY); - if (fd == -1) { - return -1; - } - char* buf = (char*)malloc(bufsize); int crc = crc32(0L, Z_NULL, 0); + lseek(fd, 0, SEEK_SET); + while ((amt = read(fd, buf, bufsize)) != 0) { crc = crc32(crc, (Bytef*)buf, amt); } - close(fd); free(buf); return crc; } int -back_up_files(int oldSnapshotFD, int oldDataStream, int newSnapshotFD, +back_up_files(int oldSnapshotFD, BackupDataWriter* dataStream, int newSnapshotFD, char const* fileBase, char const* const* files, int fileCount) { int err; @@ -252,7 +312,8 @@ back_up_files(int oldSnapshotFD, int oldDataStream, int newSnapshotFD, s.modTime_nsec = 0; // workaround sim breakage //s.modTime_nsec = st.st_mtime_nsec; s.size = st.st_size; - s.crc32 = compute_crc32(realFilename); + + // we compute the crc32 later down below, when we already have the file open. newSnapshot.add(name, s); } @@ -270,30 +331,42 @@ back_up_files(int oldSnapshotFD, int oldDataStream, int newSnapshotFD, String8 realFilename(base); realFilename.appendPath(q); LOGP("file added: %s\n", realFilename.string()); - write_update_file(realFilename, q); + write_update_file(dataStream, q, realFilename); m++; } else if (cmp < 0) { // file removed LOGP("file removed: %s\n", p.string()); - write_delete_file(p); + dataStream->WriteEntityHeader(p, -1); n++; } else { + // both files exist, check them String8 realFilename(base); realFilename.appendPath(q); const FileState& f = oldSnapshot.valueAt(n); - const FileState& g = newSnapshot.valueAt(m); + FileState& g = newSnapshot.editValueAt(m); - LOGP("%s\n", q.string()); - LOGP(" new: modTime=%d,%d size=%-3d crc32=0x%08x\n", - f.modTime_sec, f.modTime_nsec, f.size, f.crc32); - LOGP(" old: modTime=%d,%d size=%-3d crc32=0x%08x\n", - g.modTime_sec, g.modTime_nsec, g.size, g.crc32); - if (f.modTime_sec != g.modTime_sec || f.modTime_nsec != g.modTime_nsec - || f.size != g.size || f.crc32 != g.crc32) { - write_update_file(realFilename, p); + int fd = open(realFilename.string(), O_RDONLY); + if (fd != -1) { + // We can't open the file. Don't report it as a delete either. Let the + // server keep the old version. Maybe they'll be able to deal with it + // on restore. + } else { + g.crc32 = compute_crc32(fd); + + LOGP("%s\n", q.string()); + LOGP(" new: modTime=%d,%d size=%-3d crc32=0x%08x\n", + f.modTime_sec, f.modTime_nsec, f.size, f.crc32); + LOGP(" old: modTime=%d,%d size=%-3d crc32=0x%08x\n", + g.modTime_sec, g.modTime_nsec, g.size, g.crc32); + if (f.modTime_sec != g.modTime_sec || f.modTime_nsec != g.modTime_nsec + || f.size != g.size || f.crc32 != g.crc32) { + write_update_file(dataStream, fd, p, realFilename); + } + + close(fd); } n++; m++; @@ -302,7 +375,7 @@ back_up_files(int oldSnapshotFD, int oldDataStream, int newSnapshotFD, // these were deleted while (nWriteEntityHeader(oldSnapshot.keyAt(n), -1); n++; } @@ -311,7 +384,7 @@ back_up_files(int oldSnapshotFD, int oldDataStream, int newSnapshotFD, const String8& q = newSnapshot.keyAt(m); String8 realFilename(base); realFilename.appendPath(q); - write_update_file(realFilename, q); + write_update_file(dataStream, q, realFilename); m++; } @@ -911,10 +984,14 @@ backup_helper_test_files() fprintf(stderr, "error creating: %s\n", strerror(errno)); return errno; } + + { + BackupDataWriter dataStream(dataStreamFD); - err = back_up_files(-1, dataStreamFD, newSnapshotFD, SCRATCH_DIR, files_before, 5); - if (err != 0) { - return err; + err = back_up_files(-1, &dataStream, newSnapshotFD, SCRATCH_DIR, files_before, 5); + if (err != 0) { + return err; + } } close(dataStreamFD); @@ -968,10 +1045,15 @@ backup_helper_test_files() return errno; } - err = back_up_files(oldSnapshotFD, dataStreamFD, newSnapshotFD, SCRATCH_DIR, files_after, 6); - if (err != 0) { - return err; - } + { + BackupDataWriter dataStream(dataStreamFD); + + err = back_up_files(oldSnapshotFD, &dataStream, newSnapshotFD, SCRATCH_DIR, + files_after, 6); + if (err != 0) { + return err; + } +} close(oldSnapshotFD); close(dataStreamFD); From a393a7dd947469da6f0d694e938a4c4c7bbd9813 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Queru Date: Wed, 20 May 2009 11:28:04 -0700 Subject: [PATCH 102/541] donut snapshot --- libs/utils/file_backup_helper.cpp | 685 ++++++++++++++++++++++++++++++ 1 file changed, 685 insertions(+) create mode 100644 libs/utils/file_backup_helper.cpp diff --git a/libs/utils/file_backup_helper.cpp b/libs/utils/file_backup_helper.cpp new file mode 100644 index 000000000..453084ace --- /dev/null +++ b/libs/utils/file_backup_helper.cpp @@ -0,0 +1,685 @@ +#define LOG_TAG "file_backup_helper" + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace android; + +#define MAGIC0 0x70616e53 // Snap +#define MAGIC1 0x656c6946 // File + +#define LOGP(x...) LOGD(x) +//#define LOGP(x...) printf(x) + +struct SnapshotHeader { + int magic0; + int fileCount; + int magic1; + int totalSize; +}; + +struct FileState { + int modTime_sec; + int modTime_nsec; + int size; + int crc32; + int nameLen; +}; + +const static int ROUND_UP[4] = { 0, 3, 2, 1 }; + +static inline int +round_up(int n) +{ + return n + ROUND_UP[n % 4]; +} + +static int +read_snapshot_file(int fd, KeyedVector* snapshot) +{ + int bytesRead = 0; + int amt; + SnapshotHeader header; + + amt = read(fd, &header, sizeof(header)); + if (amt != sizeof(header)) { + return errno; + } + bytesRead += amt; + + if (header.magic0 != MAGIC0 || header.magic1 != MAGIC1) { + LOGW("read_snapshot_file header.magic0=0x%08x magic1=0x%08x", header.magic0, header.magic1); + return 1; + } + + for (int i=0; iadd(String8(filename, file.nameLen), file); + } + bytesRead += amt; + if (filename != filenameBuf) { + free(filename); + } + if (amt != nameBufSize) { + LOGW("read_snapshot_file filename truncated/error with read at %d bytes\n", bytesRead); + return 1; + } + } + + if (header.totalSize != bytesRead) { + LOGW("read_snapshot_file length mismatch: header.totalSize=%d bytesRead=%d\n", + header.totalSize, bytesRead); + return 1; + } + + return 0; +} + +static int +write_snapshot_file(int fd, const KeyedVector& snapshot) +{ + int bytesWritten = sizeof(SnapshotHeader); + // preflight size + const int N = snapshot.size(); + for (int i=0; i oldSnapshot; + KeyedVector newSnapshot; + + if (oldSnapshotFD != -1) { + err = read_snapshot_file(oldSnapshotFD, &oldSnapshot); + if (err != 0) { + // On an error, treat this as a full backup. + oldSnapshot.clear(); + } + } + + for (int i=0; i 0) { + // file added + String8 realFilename(base); + realFilename.appendPath(q); + write_update_file(realFilename, q); + m++; + } + else if (cmp < 0) { + // file removed + write_delete_file(p); + n++; + } + else { + // both files exist, check them + String8 realFilename(base); + realFilename.appendPath(q); + const FileState& f = oldSnapshot.valueAt(n); + const FileState& g = newSnapshot.valueAt(m); + + LOGP("%s\n", q.string()); + LOGP(" new: modTime=%d,%d size=%-3d crc32=0x%08x\n", + f.modTime_sec, f.modTime_nsec, f.size, f.crc32); + LOGP(" old: modTime=%d,%d size=%-3d crc32=0x%08x\n", + g.modTime_sec, g.modTime_nsec, g.size, g.crc32); + if (f.modTime_sec != g.modTime_sec || f.modTime_nsec != g.modTime_nsec + || f.size != g.size || f.crc32 != g.crc32) { + write_update_file(realFilename, p); + } + n++; + m++; + } + } + + // these were deleted + while (n snapshot; + const char* filename = SCRATCH_DIR "backup_helper_test_empty.snap"; + + system("rm -r " SCRATCH_DIR); + mkdir(SCRATCH_DIR, 0777); + + // write + fd = creat(filename, 0666); + if (fd == -1) { + fprintf(stderr, "error creating %s\n", filename); + return 1; + } + + err = write_snapshot_file(fd, snapshot); + + close(fd); + + if (err != 0) { + fprintf(stderr, "write_snapshot_file reported error %d (%s)\n", err, strerror(err)); + return err; + } + + static const unsigned char correct_data[] = { + 0x53, 0x6e, 0x61, 0x70, 0x00, 0x00, 0x00, 0x00, + 0x46, 0x69, 0x6c, 0x65, 0x10, 0x00, 0x00, 0x00 + }; + + err = compare_file(filename, correct_data, sizeof(correct_data)); + if (err != 0) { + return err; + } + + // read + fd = open(filename, O_RDONLY); + if (fd == -1) { + fprintf(stderr, "error opening for read %s\n", filename); + return 1; + } + + KeyedVector readSnapshot; + err = read_snapshot_file(fd, &readSnapshot); + if (err != 0) { + fprintf(stderr, "read_snapshot_file failed %d\n", err); + return err; + } + + if (readSnapshot.size() != 0) { + fprintf(stderr, "readSnapshot should be length 0\n"); + return 1; + } + + return 0; +} + +int +backup_helper_test_four() +{ + int err; + int fd; + KeyedVector snapshot; + const char* filename = SCRATCH_DIR "backup_helper_test_four.snap"; + + system("rm -r " SCRATCH_DIR); + mkdir(SCRATCH_DIR, 0777); + + // write + fd = creat(filename, 0666); + if (fd == -1) { + fprintf(stderr, "error opening %s\n", filename); + return 1; + } + + String8 filenames[4]; + FileState states[4]; + + states[0].modTime_sec = 0xfedcba98; + states[0].modTime_nsec = 0xdeadbeef; + states[0].size = 0xababbcbc; + states[0].crc32 = 0x12345678; + states[0].nameLen = -12; + filenames[0] = String8("bytes_of_padding"); + snapshot.add(filenames[0], states[0]); + + states[1].modTime_sec = 0x93400031; + states[1].modTime_nsec = 0xdeadbeef; + states[1].size = 0x88557766; + states[1].crc32 = 0x22334422; + states[1].nameLen = -1; + filenames[1] = String8("bytes_of_padding3"); + snapshot.add(filenames[1], states[1]); + + states[2].modTime_sec = 0x33221144; + states[2].modTime_nsec = 0xdeadbeef; + states[2].size = 0x11223344; + states[2].crc32 = 0x01122334; + states[2].nameLen = 0; + filenames[2] = String8("bytes_of_padding_2"); + snapshot.add(filenames[2], states[2]); + + states[3].modTime_sec = 0x33221144; + states[3].modTime_nsec = 0xdeadbeef; + states[3].size = 0x11223344; + states[3].crc32 = 0x01122334; + states[3].nameLen = 0; + filenames[3] = String8("bytes_of_padding__1"); + snapshot.add(filenames[3], states[3]); + + err = write_snapshot_file(fd, snapshot); + + close(fd); + + if (err != 0) { + fprintf(stderr, "write_snapshot_file reported error %d (%s)\n", err, strerror(err)); + return err; + } + + static const unsigned char correct_data[] = { + // header + 0x53, 0x6e, 0x61, 0x70, 0x04, 0x00, 0x00, 0x00, + 0x46, 0x69, 0x6c, 0x65, 0xac, 0x00, 0x00, 0x00, + + // bytes_of_padding + 0x98, 0xba, 0xdc, 0xfe, 0xef, 0xbe, 0xad, 0xde, + 0xbc, 0xbc, 0xab, 0xab, 0x78, 0x56, 0x34, 0x12, + 0x10, 0x00, 0x00, 0x00, 0x62, 0x79, 0x74, 0x65, + 0x73, 0x5f, 0x6f, 0x66, 0x5f, 0x70, 0x61, 0x64, + 0x64, 0x69, 0x6e, 0x67, + + // bytes_of_padding3 + 0x31, 0x00, 0x40, 0x93, 0xef, 0xbe, 0xad, 0xde, + 0x66, 0x77, 0x55, 0x88, 0x22, 0x44, 0x33, 0x22, + 0x11, 0x00, 0x00, 0x00, 0x62, 0x79, 0x74, 0x65, + 0x73, 0x5f, 0x6f, 0x66, 0x5f, 0x70, 0x61, 0x64, + 0x64, 0x69, 0x6e, 0x67, 0x33, 0xab, 0xab, 0xab, + + // bytes of padding2 + 0x44, 0x11, 0x22, 0x33, 0xef, 0xbe, 0xad, 0xde, + 0x44, 0x33, 0x22, 0x11, 0x34, 0x23, 0x12, 0x01, + 0x12, 0x00, 0x00, 0x00, 0x62, 0x79, 0x74, 0x65, + 0x73, 0x5f, 0x6f, 0x66, 0x5f, 0x70, 0x61, 0x64, + 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x32, 0xab, 0xab, + + // bytes of padding3 + 0x44, 0x11, 0x22, 0x33, 0xef, 0xbe, 0xad, 0xde, + 0x44, 0x33, 0x22, 0x11, 0x34, 0x23, 0x12, 0x01, + 0x13, 0x00, 0x00, 0x00, 0x62, 0x79, 0x74, 0x65, + 0x73, 0x5f, 0x6f, 0x66, 0x5f, 0x70, 0x61, 0x64, + 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x5f, 0x31, 0xab + }; + + err = compare_file(filename, correct_data, sizeof(correct_data)); + if (err != 0) { + return err; + } + + // read + fd = open(filename, O_RDONLY); + if (fd == -1) { + fprintf(stderr, "error opening for read %s\n", filename); + return 1; + } + + + KeyedVector readSnapshot; + err = read_snapshot_file(fd, &readSnapshot); + if (err != 0) { + fprintf(stderr, "read_snapshot_file failed %d\n", err); + return err; + } + + if (readSnapshot.size() != 4) { + fprintf(stderr, "readSnapshot should be length 4 is %d\n", readSnapshot.size()); + return 1; + } + + bool matched = true; + for (size_t i=0; i Date: Mon, 18 May 2009 15:08:03 -0700 Subject: [PATCH 103/541] checkpoint: split libutils into libutils + libbinder --- libs/utils/Android.mk | 13 - libs/utils/Binder.cpp | 242 ----- libs/utils/BpBinder.cpp | 348 ------- libs/utils/IDataConnection.cpp | 89 -- libs/utils/IInterface.cpp | 35 - libs/utils/IMemory.cpp | 486 --------- libs/utils/IPCThreadState.cpp | 1030 ------------------- libs/utils/IPermissionController.cpp | 86 -- libs/utils/IServiceManager.cpp | 230 ----- libs/utils/MemoryBase.cpp | 46 - libs/utils/MemoryDealer.cpp | 409 -------- libs/utils/MemoryHeapBase.cpp | 183 ---- libs/utils/MemoryHeapPmem.cpp | 248 ----- libs/utils/Parcel.cpp | 1359 -------------------------- libs/utils/ProcessState.cpp | 398 -------- libs/utils/Static.cpp | 31 - 16 files changed, 5233 deletions(-) delete mode 100644 libs/utils/Binder.cpp delete mode 100644 libs/utils/BpBinder.cpp delete mode 100644 libs/utils/IDataConnection.cpp delete mode 100644 libs/utils/IInterface.cpp delete mode 100644 libs/utils/IMemory.cpp delete mode 100644 libs/utils/IPCThreadState.cpp delete mode 100644 libs/utils/IPermissionController.cpp delete mode 100644 libs/utils/IServiceManager.cpp delete mode 100644 libs/utils/MemoryBase.cpp delete mode 100644 libs/utils/MemoryDealer.cpp delete mode 100644 libs/utils/MemoryHeapBase.cpp delete mode 100644 libs/utils/MemoryHeapPmem.cpp delete mode 100644 libs/utils/Parcel.cpp delete mode 100644 libs/utils/ProcessState.cpp diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk index 5a1a89b46..30b6733de 100644 --- a/libs/utils/Android.mk +++ b/libs/utils/Android.mk @@ -103,19 +103,6 @@ include $(CLEAR_VARS) # we have the common sources, plus some device-specific stuff LOCAL_SRC_FILES:= \ $(commonSources) \ - Binder.cpp \ - BpBinder.cpp \ - IInterface.cpp \ - IMemory.cpp \ - IPCThreadState.cpp \ - MemoryDealer.cpp \ - MemoryBase.cpp \ - MemoryHeapBase.cpp \ - MemoryHeapPmem.cpp \ - Parcel.cpp \ - ProcessState.cpp \ - IPermissionController.cpp \ - IServiceManager.cpp \ Unicode.cpp \ backup_data.cpp \ backup_helper_file.cpp diff --git a/libs/utils/Binder.cpp b/libs/utils/Binder.cpp deleted file mode 100644 index 37e4685bc..000000000 --- a/libs/utils/Binder.cpp +++ /dev/null @@ -1,242 +0,0 @@ -/* - * Copyright (C) 2005 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 - -#include -#include -#include -#include - -#include - -namespace android { - -// --------------------------------------------------------------------------- - -sp IBinder::queryLocalInterface(const String16& descriptor) -{ - return NULL; -} - -BBinder* IBinder::localBinder() -{ - return NULL; -} - -BpBinder* IBinder::remoteBinder() -{ - return NULL; -} - -bool IBinder::checkSubclass(const void* /*subclassID*/) const -{ - return false; -} - -// --------------------------------------------------------------------------- - -class BBinder::Extras -{ -public: - Mutex mLock; - BpBinder::ObjectManager mObjects; -}; - -// --------------------------------------------------------------------------- - -BBinder::BBinder() - : mExtras(NULL) -{ -} - -bool BBinder::isBinderAlive() const -{ - return true; -} - -status_t BBinder::pingBinder() -{ - return NO_ERROR; -} - -String16 BBinder::getInterfaceDescriptor() const -{ - LOGW("reached BBinder::getInterfaceDescriptor (this=%p)", this); - return String16(); -} - -status_t BBinder::transact( - uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) -{ - data.setDataPosition(0); - - status_t err = NO_ERROR; - switch (code) { - case PING_TRANSACTION: - reply->writeInt32(pingBinder()); - break; - default: - err = onTransact(code, data, reply, flags); - break; - } - - if (reply != NULL) { - reply->setDataPosition(0); - } - - return err; -} - -status_t BBinder::linkToDeath( - const sp& recipient, void* cookie, uint32_t flags) -{ - return INVALID_OPERATION; -} - -status_t BBinder::unlinkToDeath( - const wp& recipient, void* cookie, uint32_t flags, - wp* outRecipient) -{ - return INVALID_OPERATION; -} - -status_t BBinder::dump(int fd, const Vector& args) -{ - return NO_ERROR; -} - -void BBinder::attachObject( - const void* objectID, void* object, void* cleanupCookie, - object_cleanup_func func) -{ - Extras* e = mExtras; - - if (!e) { - e = new Extras; - if (android_atomic_cmpxchg(0, reinterpret_cast(e), - reinterpret_cast(&mExtras)) != 0) { - delete e; - e = mExtras; - } - if (e == 0) return; // out of memory - } - - AutoMutex _l(e->mLock); - e->mObjects.attach(objectID, object, cleanupCookie, func); -} - -void* BBinder::findObject(const void* objectID) const -{ - Extras* e = mExtras; - if (!e) return NULL; - - AutoMutex _l(e->mLock); - return e->mObjects.find(objectID); -} - -void BBinder::detachObject(const void* objectID) -{ - Extras* e = mExtras; - if (!e) return; - - AutoMutex _l(e->mLock); - e->mObjects.detach(objectID); -} - -BBinder* BBinder::localBinder() -{ - return this; -} - -BBinder::~BBinder() -{ - if (mExtras) delete mExtras; -} - - -status_t BBinder::onTransact( - uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) -{ - switch (code) { - case INTERFACE_TRANSACTION: - reply->writeString16(getInterfaceDescriptor()); - return NO_ERROR; - - case DUMP_TRANSACTION: { - int fd = data.readFileDescriptor(); - int argc = data.readInt32(); - Vector args; - for (int i = 0; i < argc && data.dataAvail() > 0; i++) { - args.add(data.readString16()); - } - return dump(fd, args); - } - default: - return UNKNOWN_TRANSACTION; - } -} - -// --------------------------------------------------------------------------- - -enum { - // This is used to transfer ownership of the remote binder from - // the BpRefBase object holding it (when it is constructed), to the - // owner of the BpRefBase object when it first acquires that BpRefBase. - kRemoteAcquired = 0x00000001 -}; - -BpRefBase::BpRefBase(const sp& o) - : mRemote(o.get()), mRefs(NULL), mState(0) -{ - extendObjectLifetime(OBJECT_LIFETIME_WEAK); - - if (mRemote) { - mRemote->incStrong(this); // Removed on first IncStrong(). - mRefs = mRemote->createWeak(this); // Held for our entire lifetime. - } -} - -BpRefBase::~BpRefBase() -{ - if (mRemote) { - if (!(mState&kRemoteAcquired)) { - mRemote->decStrong(this); - } - mRefs->decWeak(this); - } -} - -void BpRefBase::onFirstRef() -{ - android_atomic_or(kRemoteAcquired, &mState); -} - -void BpRefBase::onLastStrongRef(const void* id) -{ - if (mRemote) { - mRemote->decStrong(this); - } -} - -bool BpRefBase::onIncStrongAttempted(uint32_t flags, const void* id) -{ - return mRemote ? mRefs->attemptIncStrong(this) : false; -} - -// --------------------------------------------------------------------------- - -}; // namespace android diff --git a/libs/utils/BpBinder.cpp b/libs/utils/BpBinder.cpp deleted file mode 100644 index 69ab19574..000000000 --- a/libs/utils/BpBinder.cpp +++ /dev/null @@ -1,348 +0,0 @@ -/* - * Copyright (C) 2005 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 "BpBinder" -//#define LOG_NDEBUG 0 - -#include - -#include -#include - -#include - -//#undef LOGV -//#define LOGV(...) fprintf(stderr, __VA_ARGS__) - -namespace android { - -// --------------------------------------------------------------------------- - -BpBinder::ObjectManager::ObjectManager() -{ -} - -BpBinder::ObjectManager::~ObjectManager() -{ - kill(); -} - -void BpBinder::ObjectManager::attach( - const void* objectID, void* object, void* cleanupCookie, - IBinder::object_cleanup_func func) -{ - entry_t e; - e.object = object; - e.cleanupCookie = cleanupCookie; - e.func = func; - - if (mObjects.indexOfKey(objectID) >= 0) { - LOGE("Trying to attach object ID %p to binder ObjectManager %p with object %p, but object ID already in use", - objectID, this, object); - return; - } - - mObjects.add(objectID, e); -} - -void* BpBinder::ObjectManager::find(const void* objectID) const -{ - const ssize_t i = mObjects.indexOfKey(objectID); - if (i < 0) return NULL; - return mObjects.valueAt(i).object; -} - -void BpBinder::ObjectManager::detach(const void* objectID) -{ - mObjects.removeItem(objectID); -} - -void BpBinder::ObjectManager::kill() -{ - const size_t N = mObjects.size(); - LOGV("Killing %d objects in manager %p", N, this); - for (size_t i=0; iincWeakHandle(handle); -} - -String16 BpBinder::getInterfaceDescriptor() const -{ - String16 res; - Parcel send, reply; - status_t err = const_cast(this)->transact( - INTERFACE_TRANSACTION, send, &reply); - if (err == NO_ERROR) { - res = reply.readString16(); - } - return res; -} - -bool BpBinder::isBinderAlive() const -{ - return mAlive != 0; -} - -status_t BpBinder::pingBinder() -{ - Parcel send; - Parcel reply; - status_t err = transact(PING_TRANSACTION, send, &reply); - if (err != NO_ERROR) return err; - if (reply.dataSize() < sizeof(status_t)) return NOT_ENOUGH_DATA; - return (status_t)reply.readInt32(); -} - -status_t BpBinder::dump(int fd, const Vector& args) -{ - Parcel send; - Parcel reply; - send.writeFileDescriptor(fd); - const size_t numArgs = args.size(); - send.writeInt32(numArgs); - for (size_t i = 0; i < numArgs; i++) { - send.writeString16(args[i]); - } - status_t err = transact(DUMP_TRANSACTION, send, &reply); - return err; -} - -status_t BpBinder::transact( - uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) -{ - // Once a binder has died, it will never come back to life. - if (mAlive) { - status_t status = IPCThreadState::self()->transact( - mHandle, code, data, reply, flags); - if (status == DEAD_OBJECT) mAlive = 0; - return status; - } - - return DEAD_OBJECT; -} - -status_t BpBinder::linkToDeath( - const sp& recipient, void* cookie, uint32_t flags) -{ - Obituary ob; - ob.recipient = recipient; - ob.cookie = cookie; - ob.flags = flags; - - LOG_ALWAYS_FATAL_IF(recipient == NULL, - "linkToDeath(): recipient must be non-NULL"); - - { - AutoMutex _l(mLock); - - if (!mObitsSent) { - if (!mObituaries) { - mObituaries = new Vector; - if (!mObituaries) { - return NO_MEMORY; - } - LOGV("Requesting death notification: %p handle %d\n", this, mHandle); - getWeakRefs()->incWeak(this); - IPCThreadState* self = IPCThreadState::self(); - self->requestDeathNotification(mHandle, this); - self->flushCommands(); - } - ssize_t res = mObituaries->add(ob); - return res >= (ssize_t)NO_ERROR ? (status_t)NO_ERROR : res; - } - } - - return DEAD_OBJECT; -} - -status_t BpBinder::unlinkToDeath( - const wp& recipient, void* cookie, uint32_t flags, - wp* outRecipient) -{ - AutoMutex _l(mLock); - - if (mObitsSent) { - return DEAD_OBJECT; - } - - const size_t N = mObituaries ? mObituaries->size() : 0; - for (size_t i=0; iitemAt(i); - if ((obit.recipient == recipient - || (recipient == NULL && obit.cookie == cookie)) - && obit.flags == flags) { - const uint32_t allFlags = obit.flags|flags; - if (outRecipient != NULL) { - *outRecipient = mObituaries->itemAt(i).recipient; - } - mObituaries->removeAt(i); - if (mObituaries->size() == 0) { - LOGV("Clearing death notification: %p handle %d\n", this, mHandle); - IPCThreadState* self = IPCThreadState::self(); - self->clearDeathNotification(mHandle, this); - self->flushCommands(); - delete mObituaries; - mObituaries = NULL; - } - return NO_ERROR; - } - } - - return NAME_NOT_FOUND; -} - -void BpBinder::sendObituary() -{ - LOGV("Sending obituary for proxy %p handle %d, mObitsSent=%s\n", - this, mHandle, mObitsSent ? "true" : "false"); - - mAlive = 0; - if (mObitsSent) return; - - mLock.lock(); - Vector* obits = mObituaries; - if(obits != NULL) { - LOGV("Clearing sent death notification: %p handle %d\n", this, mHandle); - IPCThreadState* self = IPCThreadState::self(); - self->clearDeathNotification(mHandle, this); - self->flushCommands(); - mObituaries = NULL; - } - mObitsSent = 1; - mLock.unlock(); - - LOGV("Reporting death of proxy %p for %d recipients\n", - this, obits ? obits->size() : 0); - - if (obits != NULL) { - const size_t N = obits->size(); - for (size_t i=0; iitemAt(i)); - } - - delete obits; - } -} - -void BpBinder::reportOneDeath(const Obituary& obit) -{ - sp recipient = obit.recipient.promote(); - LOGV("Reporting death to recipient: %p\n", recipient.get()); - if (recipient == NULL) return; - - recipient->binderDied(this); -} - - -void BpBinder::attachObject( - const void* objectID, void* object, void* cleanupCookie, - object_cleanup_func func) -{ - AutoMutex _l(mLock); - LOGV("Attaching object %p to binder %p (manager=%p)", object, this, &mObjects); - mObjects.attach(objectID, object, cleanupCookie, func); -} - -void* BpBinder::findObject(const void* objectID) const -{ - AutoMutex _l(mLock); - return mObjects.find(objectID); -} - -void BpBinder::detachObject(const void* objectID) -{ - AutoMutex _l(mLock); - mObjects.detach(objectID); -} - -BpBinder* BpBinder::remoteBinder() -{ - return this; -} - -BpBinder::~BpBinder() -{ - LOGV("Destroying BpBinder %p handle %d\n", this, mHandle); - - IPCThreadState* ipc = IPCThreadState::self(); - - mLock.lock(); - Vector* obits = mObituaries; - if(obits != NULL) { - if (ipc) ipc->clearDeathNotification(mHandle, this); - mObituaries = NULL; - } - mLock.unlock(); - - if (obits != NULL) { - // XXX Should we tell any remaining DeathRecipient - // objects that the last strong ref has gone away, so they - // are no longer linked? - delete obits; - } - - if (ipc) { - ipc->expungeHandle(mHandle, this); - ipc->decWeakHandle(mHandle); - } -} - -void BpBinder::onFirstRef() -{ - LOGV("onFirstRef BpBinder %p handle %d\n", this, mHandle); - IPCThreadState* ipc = IPCThreadState::self(); - if (ipc) ipc->incStrongHandle(mHandle); -} - -void BpBinder::onLastStrongRef(const void* id) -{ - LOGV("onLastStrongRef BpBinder %p handle %d\n", this, mHandle); - IF_LOGV() { - printRefs(); - } - IPCThreadState* ipc = IPCThreadState::self(); - if (ipc) ipc->decStrongHandle(mHandle); -} - -bool BpBinder::onIncStrongAttempted(uint32_t flags, const void* id) -{ - LOGV("onIncStrongAttempted BpBinder %p handle %d\n", this, mHandle); - IPCThreadState* ipc = IPCThreadState::self(); - return ipc ? ipc->attemptIncStrongHandle(mHandle) == NO_ERROR : false; -} - -// --------------------------------------------------------------------------- - -}; // namespace android diff --git a/libs/utils/IDataConnection.cpp b/libs/utils/IDataConnection.cpp deleted file mode 100644 index c6d49aa48..000000000 --- a/libs/utils/IDataConnection.cpp +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (C) 2006 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 -#include - -#include - -#include - -namespace android { - -// --------------------------------------------------------------------------- - -enum -{ - CONNECT_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION, - DISCONNECT_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION + 1 -}; - -class BpDataConnection : public BpInterface -{ -public: - BpDataConnection::BpDataConnection(const sp& impl) - : BpInterface(impl) - { - } - - virtual void connect() - { - Parcel data, reply; - data.writeInterfaceToken(IDataConnection::descriptor()); - remote()->transact(CONNECT_TRANSACTION, data, &reply); - } - - virtual void disconnect() - { - Parcel data, reply; - remote()->transact(DISCONNECT_TRANSACTION, data, &reply); - } -}; - -IMPLEMENT_META_INTERFACE(DataConnection, "android.utils.IDataConnection"); - -#define CHECK_INTERFACE(interface, data, reply) \ - do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ - LOGW("Call incorrectly routed to " #interface); \ - return PERMISSION_DENIED; \ - } } while (0) - -status_t BnDataConnection::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) -{ - switch(code) - { - case CONNECT_TRANSACTION: - { - CHECK_INTERFACE(IDataConnection, data, reply); - connect(); - return NO_ERROR; - } - - case DISCONNECT_TRANSACTION: - { - CHECK_INTERFACE(IDataConnection, data, reply); - disconnect(); - return NO_ERROR; - } - - default: - return BBinder::onTransact(code, data, reply, flags); - } -} - -// ---------------------------------------------------------------------------- - -}; // namespace android diff --git a/libs/utils/IInterface.cpp b/libs/utils/IInterface.cpp deleted file mode 100644 index 6ea817887..000000000 --- a/libs/utils/IInterface.cpp +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2005 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 - -namespace android { - -// --------------------------------------------------------------------------- - -sp IInterface::asBinder() -{ - return this ? onAsBinder() : NULL; -} - -sp IInterface::asBinder() const -{ - return this ? const_cast(this)->onAsBinder() : NULL; -} - -// --------------------------------------------------------------------------- - -}; // namespace android diff --git a/libs/utils/IMemory.cpp b/libs/utils/IMemory.cpp deleted file mode 100644 index 429bc2b94..000000000 --- a/libs/utils/IMemory.cpp +++ /dev/null @@ -1,486 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "IMemory" - -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include - -#define VERBOSE 0 - -namespace android { -// --------------------------------------------------------------------------- - -class HeapCache : public IBinder::DeathRecipient -{ -public: - HeapCache(); - virtual ~HeapCache(); - - virtual void binderDied(const wp& who); - - sp find_heap(const sp& binder); - void pin_heap(const sp& binder); - void free_heap(const sp& binder); - sp get_heap(const sp& binder); - void dump_heaps(); - -private: - // For IMemory.cpp - struct heap_info_t { - sp heap; - int32_t count; - }; - - void free_heap(const wp& binder); - - Mutex mHeapCacheLock; - KeyedVector< wp, heap_info_t > mHeapCache; -}; - -static sp gHeapCache = new HeapCache(); - -/******************************************************************************/ - -enum { - HEAP_ID = IBinder::FIRST_CALL_TRANSACTION -}; - -class BpMemoryHeap : public BpInterface -{ -public: - BpMemoryHeap(const sp& impl); - virtual ~BpMemoryHeap(); - - virtual int getHeapID() const; - virtual void* getBase() const; - virtual size_t getSize() const; - virtual uint32_t getFlags() const; - -private: - friend class IMemory; - friend class HeapCache; - - // for debugging in this module - static inline sp find_heap(const sp& binder) { - return gHeapCache->find_heap(binder); - } - static inline void free_heap(const sp& binder) { - gHeapCache->free_heap(binder); - } - static inline sp get_heap(const sp& binder) { - return gHeapCache->get_heap(binder); - } - static inline void dump_heaps() { - gHeapCache->dump_heaps(); - } - void inline pin_heap() const { - gHeapCache->pin_heap(const_cast(this)->asBinder()); - } - - void assertMapped() const; - void assertReallyMapped() const; - void pinHeap() const; - - mutable volatile int32_t mHeapId; - mutable void* mBase; - mutable size_t mSize; - mutable uint32_t mFlags; - mutable bool mRealHeap; - mutable Mutex mLock; -}; - -// ---------------------------------------------------------------------------- - -enum { - GET_MEMORY = IBinder::FIRST_CALL_TRANSACTION -}; - -class BpMemory : public BpInterface -{ -public: - BpMemory(const sp& impl); - virtual ~BpMemory(); - virtual sp getMemory(ssize_t* offset=0, size_t* size=0) const; - -private: - mutable sp mHeap; - mutable ssize_t mOffset; - mutable size_t mSize; -}; - -/******************************************************************************/ - -void* IMemory::fastPointer(const sp& binder, ssize_t offset) const -{ - sp realHeap = BpMemoryHeap::get_heap(binder); - void* const base = realHeap->base(); - if (base == MAP_FAILED) - return 0; - return static_cast(base) + offset; -} - -void* IMemory::pointer() const { - ssize_t offset; - sp heap = getMemory(&offset); - void* const base = heap!=0 ? heap->base() : MAP_FAILED; - if (base == MAP_FAILED) - return 0; - return static_cast(base) + offset; -} - -size_t IMemory::size() const { - size_t size; - getMemory(NULL, &size); - return size; -} - -ssize_t IMemory::offset() const { - ssize_t offset; - getMemory(&offset); - return offset; -} - -/******************************************************************************/ - -BpMemory::BpMemory(const sp& impl) - : BpInterface(impl), mOffset(0), mSize(0) -{ -} - -BpMemory::~BpMemory() -{ -} - -sp BpMemory::getMemory(ssize_t* offset, size_t* size) const -{ - if (mHeap == 0) { - Parcel data, reply; - data.writeInterfaceToken(IMemory::getInterfaceDescriptor()); - if (remote()->transact(GET_MEMORY, data, &reply) == NO_ERROR) { - sp heap = reply.readStrongBinder(); - ssize_t o = reply.readInt32(); - size_t s = reply.readInt32(); - if (heap != 0) { - mHeap = interface_cast(heap); - if (mHeap != 0) { - mOffset = o; - mSize = s; - } - } - } - } - if (offset) *offset = mOffset; - if (size) *size = mSize; - return mHeap; -} - -// --------------------------------------------------------------------------- - -IMPLEMENT_META_INTERFACE(Memory, "android.utils.IMemory"); - -#define CHECK_INTERFACE(interface, data, reply) \ - do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ - LOGW("Call incorrectly routed to " #interface); \ - return PERMISSION_DENIED; \ - } } while (0) - -status_t BnMemory::onTransact( - uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) -{ - switch(code) { - case GET_MEMORY: { - CHECK_INTERFACE(IMemory, data, reply); - ssize_t offset; - size_t size; - reply->writeStrongBinder( getMemory(&offset, &size)->asBinder() ); - reply->writeInt32(offset); - reply->writeInt32(size); - return NO_ERROR; - } break; - default: - return BBinder::onTransact(code, data, reply, flags); - } -} - - -/******************************************************************************/ - -BpMemoryHeap::BpMemoryHeap(const sp& impl) - : BpInterface(impl), - mHeapId(-1), mBase(MAP_FAILED), mSize(0), mFlags(0), mRealHeap(false) -{ -} - -BpMemoryHeap::~BpMemoryHeap() { - if (mHeapId != -1) { - close(mHeapId); - if (mRealHeap) { - // by construction we're the last one - if (mBase != MAP_FAILED) { - sp binder = const_cast(this)->asBinder(); - - if (VERBOSE) { - LOGD("UNMAPPING binder=%p, heap=%p, size=%d, fd=%d", - binder.get(), this, mSize, mHeapId); - CallStack stack; - stack.update(); - stack.dump("callstack"); - } - - munmap(mBase, mSize); - } - } else { - // remove from list only if it was mapped before - sp binder = const_cast(this)->asBinder(); - free_heap(binder); - } - } -} - -void BpMemoryHeap::assertMapped() const -{ - if (mHeapId == -1) { - sp binder(const_cast(this)->asBinder()); - sp heap(static_cast(find_heap(binder).get())); - heap->assertReallyMapped(); - if (heap->mBase != MAP_FAILED) { - Mutex::Autolock _l(mLock); - if (mHeapId == -1) { - mBase = heap->mBase; - mSize = heap->mSize; - android_atomic_write( dup( heap->mHeapId ), &mHeapId ); - } - } else { - // something went wrong - free_heap(binder); - } - } -} - -void BpMemoryHeap::assertReallyMapped() const -{ - if (mHeapId == -1) { - - // remote call without mLock held, worse case scenario, we end up - // calling transact() from multiple threads, but that's not a problem, - // only mmap below must be in the critical section. - - Parcel data, reply; - data.writeInterfaceToken(IMemoryHeap::getInterfaceDescriptor()); - status_t err = remote()->transact(HEAP_ID, data, &reply); - int parcel_fd = reply.readFileDescriptor(); - ssize_t size = reply.readInt32(); - uint32_t flags = reply.readInt32(); - - LOGE_IF(err, "binder=%p transaction failed fd=%d, size=%d, err=%d (%s)", - asBinder().get(), parcel_fd, size, err, strerror(-err)); - - int fd = dup( parcel_fd ); - LOGE_IF(fd==-1, "cannot dup fd=%d, size=%d, err=%d (%s)", - parcel_fd, size, err, strerror(errno)); - - int access = PROT_READ; - if (!(flags & READ_ONLY)) { - access |= PROT_WRITE; - } - - Mutex::Autolock _l(mLock); - if (mHeapId == -1) { - mRealHeap = true; - mBase = mmap(0, size, access, MAP_SHARED, fd, 0); - if (mBase == MAP_FAILED) { - LOGE("cannot map BpMemoryHeap (binder=%p), size=%d, fd=%d (%s)", - asBinder().get(), size, fd, strerror(errno)); - close(fd); - } else { - if (flags & MAP_ONCE) { - //LOGD("pinning heap (binder=%p, size=%d, fd=%d", - // asBinder().get(), size, fd); - pin_heap(); - } - mSize = size; - mFlags = flags; - android_atomic_write(fd, &mHeapId); - } - } - } -} - -int BpMemoryHeap::getHeapID() const { - assertMapped(); - return mHeapId; -} - -void* BpMemoryHeap::getBase() const { - assertMapped(); - return mBase; -} - -size_t BpMemoryHeap::getSize() const { - assertMapped(); - return mSize; -} - -uint32_t BpMemoryHeap::getFlags() const { - assertMapped(); - return mFlags; -} - -// --------------------------------------------------------------------------- - -IMPLEMENT_META_INTERFACE(MemoryHeap, "android.utils.IMemoryHeap"); - -status_t BnMemoryHeap::onTransact( - uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) -{ - switch(code) { - case HEAP_ID: { - CHECK_INTERFACE(IMemoryHeap, data, reply); - reply->writeFileDescriptor(getHeapID()); - reply->writeInt32(getSize()); - reply->writeInt32(getFlags()); - return NO_ERROR; - } break; - default: - return BBinder::onTransact(code, data, reply, flags); - } -} - -/*****************************************************************************/ - -HeapCache::HeapCache() - : DeathRecipient() -{ -} - -HeapCache::~HeapCache() -{ -} - -void HeapCache::binderDied(const wp& binder) -{ - //LOGD("binderDied binder=%p", binder.unsafe_get()); - free_heap(binder); -} - -sp HeapCache::find_heap(const sp& binder) -{ - Mutex::Autolock _l(mHeapCacheLock); - ssize_t i = mHeapCache.indexOfKey(binder); - if (i>=0) { - heap_info_t& info = mHeapCache.editValueAt(i); - LOGD_IF(VERBOSE, - "found binder=%p, heap=%p, size=%d, fd=%d, count=%d", - binder.get(), info.heap.get(), - static_cast(info.heap.get())->mSize, - static_cast(info.heap.get())->mHeapId, - info.count); - android_atomic_inc(&info.count); - return info.heap; - } else { - heap_info_t info; - info.heap = interface_cast(binder); - info.count = 1; - //LOGD("adding binder=%p, heap=%p, count=%d", - // binder.get(), info.heap.get(), info.count); - mHeapCache.add(binder, info); - return info.heap; - } -} - -void HeapCache::pin_heap(const sp& binder) -{ - Mutex::Autolock _l(mHeapCacheLock); - ssize_t i = mHeapCache.indexOfKey(binder); - if (i>=0) { - heap_info_t& info(mHeapCache.editValueAt(i)); - android_atomic_inc(&info.count); - binder->linkToDeath(this); - } else { - LOGE("pin_heap binder=%p not found!!!", binder.get()); - } -} - -void HeapCache::free_heap(const sp& binder) { - free_heap( wp(binder) ); -} - -void HeapCache::free_heap(const wp& binder) -{ - sp rel; - { - Mutex::Autolock _l(mHeapCacheLock); - ssize_t i = mHeapCache.indexOfKey(binder); - if (i>=0) { - heap_info_t& info(mHeapCache.editValueAt(i)); - int32_t c = android_atomic_dec(&info.count); - if (c == 1) { - LOGD_IF(VERBOSE, - "removing binder=%p, heap=%p, size=%d, fd=%d, count=%d", - binder.unsafe_get(), info.heap.get(), - static_cast(info.heap.get())->mSize, - static_cast(info.heap.get())->mHeapId, - info.count); - rel = mHeapCache.valueAt(i).heap; - mHeapCache.removeItemsAt(i); - } - } else { - LOGE("free_heap binder=%p not found!!!", binder.unsafe_get()); - } - } -} - -sp HeapCache::get_heap(const sp& binder) -{ - sp realHeap; - Mutex::Autolock _l(mHeapCacheLock); - ssize_t i = mHeapCache.indexOfKey(binder); - if (i>=0) realHeap = mHeapCache.valueAt(i).heap; - else realHeap = interface_cast(binder); - return realHeap; -} - -void HeapCache::dump_heaps() -{ - Mutex::Autolock _l(mHeapCacheLock); - int c = mHeapCache.size(); - for (int i=0 ; i(info.heap.get())); - LOGD("hey=%p, heap=%p, count=%d, (fd=%d, base=%p, size=%d)", - mHeapCache.keyAt(i).unsafe_get(), - info.heap.get(), info.count, - h->mHeapId, h->mBase, h->mSize); - } -} - - -// --------------------------------------------------------------------------- -}; // namespace android diff --git a/libs/utils/IPCThreadState.cpp b/libs/utils/IPCThreadState.cpp deleted file mode 100644 index 04ae1424e..000000000 --- a/libs/utils/IPCThreadState.cpp +++ /dev/null @@ -1,1030 +0,0 @@ -/* - * Copyright (C) 2005 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 - -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include - -#ifdef HAVE_PTHREADS -#include -#include -#include -#endif -#ifdef HAVE_WIN32_THREADS -#include -#endif - - -#if LOG_NDEBUG - -#define IF_LOG_TRANSACTIONS() if (false) -#define IF_LOG_COMMANDS() if (false) -#define LOG_REMOTEREFS(...) -#define IF_LOG_REMOTEREFS() if (false) -#define LOG_THREADPOOL(...) -#define LOG_ONEWAY(...) - -#else - -#define IF_LOG_TRANSACTIONS() IF_LOG(LOG_VERBOSE, "transact") -#define IF_LOG_COMMANDS() IF_LOG(LOG_VERBOSE, "ipc") -#define LOG_REMOTEREFS(...) LOG(LOG_DEBUG, "remoterefs", __VA_ARGS__) -#define IF_LOG_REMOTEREFS() IF_LOG(LOG_DEBUG, "remoterefs") -#define LOG_THREADPOOL(...) LOG(LOG_DEBUG, "threadpool", __VA_ARGS__) -#define LOG_ONEWAY(...) LOG(LOG_DEBUG, "ipc", __VA_ARGS__) - -#endif - -// --------------------------------------------------------------------------- - -namespace android { - -static const char* getReturnString(size_t idx); -static const char* getCommandString(size_t idx); -static const void* printReturnCommand(TextOutput& out, const void* _cmd); -static const void* printCommand(TextOutput& out, const void* _cmd); - -// This will result in a missing symbol failure if the IF_LOG_COMMANDS() -// conditionals don't get stripped... but that is probably what we want. -#if !LOG_NDEBUG -static const char *kReturnStrings[] = { -#if 1 /* TODO: error update strings */ - "unknown", -#else - "BR_OK", - "BR_TIMEOUT", - "BR_WAKEUP", - "BR_TRANSACTION", - "BR_REPLY", - "BR_ACQUIRE_RESULT", - "BR_DEAD_REPLY", - "BR_TRANSACTION_COMPLETE", - "BR_INCREFS", - "BR_ACQUIRE", - "BR_RELEASE", - "BR_DECREFS", - "BR_ATTEMPT_ACQUIRE", - "BR_EVENT_OCCURRED", - "BR_NOOP", - "BR_SPAWN_LOOPER", - "BR_FINISHED", - "BR_DEAD_BINDER", - "BR_CLEAR_DEATH_NOTIFICATION_DONE" -#endif -}; - -static const char *kCommandStrings[] = { -#if 1 /* TODO: error update strings */ - "unknown", -#else - "BC_NOOP", - "BC_TRANSACTION", - "BC_REPLY", - "BC_ACQUIRE_RESULT", - "BC_FREE_BUFFER", - "BC_TRANSACTION_COMPLETE", - "BC_INCREFS", - "BC_ACQUIRE", - "BC_RELEASE", - "BC_DECREFS", - "BC_INCREFS_DONE", - "BC_ACQUIRE_DONE", - "BC_ATTEMPT_ACQUIRE", - "BC_RETRIEVE_ROOT_OBJECT", - "BC_SET_THREAD_ENTRY", - "BC_REGISTER_LOOPER", - "BC_ENTER_LOOPER", - "BC_EXIT_LOOPER", - "BC_SYNC", - "BC_STOP_PROCESS", - "BC_STOP_SELF", - "BC_REQUEST_DEATH_NOTIFICATION", - "BC_CLEAR_DEATH_NOTIFICATION", - "BC_DEAD_BINDER_DONE" -#endif -}; - -static const char* getReturnString(size_t idx) -{ - if (idx < sizeof(kReturnStrings) / sizeof(kReturnStrings[0])) - return kReturnStrings[idx]; - else - return "unknown"; -} - -static const char* getCommandString(size_t idx) -{ - if (idx < sizeof(kCommandStrings) / sizeof(kCommandStrings[0])) - return kCommandStrings[idx]; - else - return "unknown"; -} - -static const void* printBinderTransactionData(TextOutput& out, const void* data) -{ - const binder_transaction_data* btd = - (const binder_transaction_data*)data; - out << "target=" << btd->target.ptr << " (cookie " << btd->cookie << ")" << endl - << "code=" << TypeCode(btd->code) << ", flags=" << (void*)btd->flags << endl - << "data=" << btd->data.ptr.buffer << " (" << (void*)btd->data_size - << " bytes)" << endl - << "offsets=" << btd->data.ptr.offsets << " (" << (void*)btd->offsets_size - << " bytes)" << endl; - return btd+1; -} - -static const void* printReturnCommand(TextOutput& out, const void* _cmd) -{ - static const int32_t N = sizeof(kReturnStrings)/sizeof(kReturnStrings[0]); - - const int32_t* cmd = (const int32_t*)_cmd; - int32_t code = *cmd++; - if (code == BR_ERROR) { - out << "BR_ERROR: " << (void*)(*cmd++) << endl; - return cmd; - } else if (code < 0 || code >= N) { - out << "Unknown reply: " << code << endl; - return cmd; - } - - out << kReturnStrings[code]; - switch (code) { - case BR_TRANSACTION: - case BR_REPLY: { - out << ": " << indent; - cmd = (const int32_t *)printBinderTransactionData(out, cmd); - out << dedent; - } break; - - case BR_ACQUIRE_RESULT: { - const int32_t res = *cmd++; - out << ": " << res << (res ? " (SUCCESS)" : " (FAILURE)"); - } break; - - case BR_INCREFS: - case BR_ACQUIRE: - case BR_RELEASE: - case BR_DECREFS: { - const int32_t b = *cmd++; - const int32_t c = *cmd++; - out << ": target=" << (void*)b << " (cookie " << (void*)c << ")"; - } break; - - case BR_ATTEMPT_ACQUIRE: { - const int32_t p = *cmd++; - const int32_t b = *cmd++; - const int32_t c = *cmd++; - out << ": target=" << (void*)b << " (cookie " << (void*)c - << "), pri=" << p; - } break; - - case BR_DEAD_BINDER: - case BR_CLEAR_DEATH_NOTIFICATION_DONE: { - const int32_t c = *cmd++; - out << ": death cookie " << (void*)c; - } break; - } - - out << endl; - return cmd; -} - -static const void* printCommand(TextOutput& out, const void* _cmd) -{ - static const int32_t N = sizeof(kCommandStrings)/sizeof(kCommandStrings[0]); - - const int32_t* cmd = (const int32_t*)_cmd; - int32_t code = *cmd++; - if (code < 0 || code >= N) { - out << "Unknown command: " << code << endl; - return cmd; - } - - out << kCommandStrings[code]; - switch (code) { - case BC_TRANSACTION: - case BC_REPLY: { - out << ": " << indent; - cmd = (const int32_t *)printBinderTransactionData(out, cmd); - out << dedent; - } break; - - case BC_ACQUIRE_RESULT: { - const int32_t res = *cmd++; - out << ": " << res << (res ? " (SUCCESS)" : " (FAILURE)"); - } break; - - case BC_FREE_BUFFER: { - const int32_t buf = *cmd++; - out << ": buffer=" << (void*)buf; - } break; - - case BC_INCREFS: - case BC_ACQUIRE: - case BC_RELEASE: - case BC_DECREFS: { - const int32_t d = *cmd++; - out << ": descriptor=" << (void*)d; - } break; - - case BC_INCREFS_DONE: - case BC_ACQUIRE_DONE: { - const int32_t b = *cmd++; - const int32_t c = *cmd++; - out << ": target=" << (void*)b << " (cookie " << (void*)c << ")"; - } break; - - case BC_ATTEMPT_ACQUIRE: { - const int32_t p = *cmd++; - const int32_t d = *cmd++; - out << ": decriptor=" << (void*)d << ", pri=" << p; - } break; - - case BC_REQUEST_DEATH_NOTIFICATION: - case BC_CLEAR_DEATH_NOTIFICATION: { - const int32_t h = *cmd++; - const int32_t c = *cmd++; - out << ": handle=" << h << " (death cookie " << (void*)c << ")"; - } break; - - case BC_DEAD_BINDER_DONE: { - const int32_t c = *cmd++; - out << ": death cookie " << (void*)c; - } break; - } - - out << endl; - return cmd; -} -#endif - -static pthread_mutex_t gTLSMutex = PTHREAD_MUTEX_INITIALIZER; -static bool gHaveTLS = false; -static pthread_key_t gTLS = 0; -static bool gShutdown = false; - -IPCThreadState* IPCThreadState::self() -{ - if (gHaveTLS) { -restart: - const pthread_key_t k = gTLS; - IPCThreadState* st = (IPCThreadState*)pthread_getspecific(k); - if (st) return st; - return new IPCThreadState; - } - - if (gShutdown) return NULL; - - pthread_mutex_lock(&gTLSMutex); - if (!gHaveTLS) { - if (pthread_key_create(&gTLS, threadDestructor) != 0) { - pthread_mutex_unlock(&gTLSMutex); - return NULL; - } - gHaveTLS = true; - } - pthread_mutex_unlock(&gTLSMutex); - goto restart; -} - -void IPCThreadState::shutdown() -{ - gShutdown = true; - - if (gHaveTLS) { - // XXX Need to wait for all thread pool threads to exit! - IPCThreadState* st = (IPCThreadState*)pthread_getspecific(gTLS); - if (st) { - delete st; - pthread_setspecific(gTLS, NULL); - } - gHaveTLS = false; - } -} - -sp IPCThreadState::process() -{ - return mProcess; -} - -status_t IPCThreadState::clearLastError() -{ - const status_t err = mLastError; - mLastError = NO_ERROR; - return err; -} - -int IPCThreadState::getCallingPid() -{ - return mCallingPid; -} - -int IPCThreadState::getCallingUid() -{ - return mCallingUid; -} - -int64_t IPCThreadState::clearCallingIdentity() -{ - int64_t token = ((int64_t)mCallingUid<<32) | mCallingPid; - clearCaller(); - return token; -} - -void IPCThreadState::restoreCallingIdentity(int64_t token) -{ - mCallingUid = (int)(token>>32); - mCallingPid = (int)token; -} - -void IPCThreadState::clearCaller() -{ - if (mProcess->supportsProcesses()) { - mCallingPid = getpid(); - mCallingUid = getuid(); - } else { - mCallingPid = -1; - mCallingUid = -1; - } -} - -void IPCThreadState::flushCommands() -{ - if (mProcess->mDriverFD <= 0) - return; - talkWithDriver(false); -} - -void IPCThreadState::joinThreadPool(bool isMain) -{ - LOG_THREADPOOL("**** THREAD %p (PID %d) IS JOINING THE THREAD POOL\n", (void*)pthread_self(), getpid()); - - mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER); - - status_t result; - do { - int32_t cmd; - - // When we've cleared the incoming command queue, process any pending derefs - if (mIn.dataPosition() >= mIn.dataSize()) { - size_t numPending = mPendingWeakDerefs.size(); - if (numPending > 0) { - for (size_t i = 0; i < numPending; i++) { - RefBase::weakref_type* refs = mPendingWeakDerefs[i]; - refs->decWeak(mProcess.get()); - } - mPendingWeakDerefs.clear(); - } - - numPending = mPendingStrongDerefs.size(); - if (numPending > 0) { - for (size_t i = 0; i < numPending; i++) { - BBinder* obj = mPendingStrongDerefs[i]; - obj->decStrong(mProcess.get()); - } - mPendingStrongDerefs.clear(); - } - } - - // now get the next command to be processed, waiting if necessary - result = talkWithDriver(); - if (result >= NO_ERROR) { - size_t IN = mIn.dataAvail(); - if (IN < sizeof(int32_t)) continue; - cmd = mIn.readInt32(); - IF_LOG_COMMANDS() { - alog << "Processing top-level Command: " - << getReturnString(cmd) << endl; - } - result = executeCommand(cmd); - } - - // Let this thread exit the thread pool if it is no longer - // needed and it is not the main process thread. - if(result == TIMED_OUT && !isMain) { - break; - } - } while (result != -ECONNREFUSED && result != -EBADF); - - LOG_THREADPOOL("**** THREAD %p (PID %d) IS LEAVING THE THREAD POOL err=%p\n", - (void*)pthread_self(), getpid(), (void*)result); - - mOut.writeInt32(BC_EXIT_LOOPER); - talkWithDriver(false); -} - -void IPCThreadState::stopProcess(bool immediate) -{ - //LOGI("**** STOPPING PROCESS"); - flushCommands(); - int fd = mProcess->mDriverFD; - mProcess->mDriverFD = -1; - close(fd); - //kill(getpid(), SIGKILL); -} - -status_t IPCThreadState::transact(int32_t handle, - uint32_t code, const Parcel& data, - Parcel* reply, uint32_t flags) -{ - status_t err = data.errorCheck(); - - flags |= TF_ACCEPT_FDS; - - IF_LOG_TRANSACTIONS() { - TextOutput::Bundle _b(alog); - alog << "BC_TRANSACTION thr " << (void*)pthread_self() << " / hand " - << handle << " / code " << TypeCode(code) << ": " - << indent << data << dedent << endl; - } - - if (err == NO_ERROR) { - LOG_ONEWAY(">>>> SEND from pid %d uid %d %s", getpid(), getuid(), - (flags & TF_ONE_WAY) == 0 ? "READ REPLY" : "ONE WAY"); - err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL); - } - - if (err != NO_ERROR) { - if (reply) reply->setError(err); - return (mLastError = err); - } - - if ((flags & TF_ONE_WAY) == 0) { - if (reply) { - err = waitForResponse(reply); - } else { - Parcel fakeReply; - err = waitForResponse(&fakeReply); - } - - IF_LOG_TRANSACTIONS() { - TextOutput::Bundle _b(alog); - alog << "BR_REPLY thr " << (void*)pthread_self() << " / hand " - << handle << ": "; - if (reply) alog << indent << *reply << dedent << endl; - else alog << "(none requested)" << endl; - } - } else { - err = waitForResponse(NULL, NULL); - } - - return err; -} - -void IPCThreadState::incStrongHandle(int32_t handle) -{ - LOG_REMOTEREFS("IPCThreadState::incStrongHandle(%d)\n", handle); - mOut.writeInt32(BC_ACQUIRE); - mOut.writeInt32(handle); -} - -void IPCThreadState::decStrongHandle(int32_t handle) -{ - LOG_REMOTEREFS("IPCThreadState::decStrongHandle(%d)\n", handle); - mOut.writeInt32(BC_RELEASE); - mOut.writeInt32(handle); -} - -void IPCThreadState::incWeakHandle(int32_t handle) -{ - LOG_REMOTEREFS("IPCThreadState::incWeakHandle(%d)\n", handle); - mOut.writeInt32(BC_INCREFS); - mOut.writeInt32(handle); -} - -void IPCThreadState::decWeakHandle(int32_t handle) -{ - LOG_REMOTEREFS("IPCThreadState::decWeakHandle(%d)\n", handle); - mOut.writeInt32(BC_DECREFS); - mOut.writeInt32(handle); -} - -status_t IPCThreadState::attemptIncStrongHandle(int32_t handle) -{ - mOut.writeInt32(BC_ATTEMPT_ACQUIRE); - mOut.writeInt32(0); // xxx was thread priority - mOut.writeInt32(handle); - status_t result = UNKNOWN_ERROR; - - waitForResponse(NULL, &result); - -#if LOG_REFCOUNTS - printf("IPCThreadState::attemptIncStrongHandle(%ld) = %s\n", - handle, result == NO_ERROR ? "SUCCESS" : "FAILURE"); -#endif - - return result; -} - -void IPCThreadState::expungeHandle(int32_t handle, IBinder* binder) -{ -#if LOG_REFCOUNTS - printf("IPCThreadState::expungeHandle(%ld)\n", handle); -#endif - self()->mProcess->expungeHandle(handle, binder); -} - -status_t IPCThreadState::requestDeathNotification(int32_t handle, BpBinder* proxy) -{ - mOut.writeInt32(BC_REQUEST_DEATH_NOTIFICATION); - mOut.writeInt32((int32_t)handle); - mOut.writeInt32((int32_t)proxy); - return NO_ERROR; -} - -status_t IPCThreadState::clearDeathNotification(int32_t handle, BpBinder* proxy) -{ - mOut.writeInt32(BC_CLEAR_DEATH_NOTIFICATION); - mOut.writeInt32((int32_t)handle); - mOut.writeInt32((int32_t)proxy); - return NO_ERROR; -} - -IPCThreadState::IPCThreadState() - : mProcess(ProcessState::self()) -{ - pthread_setspecific(gTLS, this); - clearCaller(); - mIn.setDataCapacity(256); - mOut.setDataCapacity(256); -} - -IPCThreadState::~IPCThreadState() -{ -} - -status_t IPCThreadState::sendReply(const Parcel& reply, uint32_t flags) -{ - status_t err; - status_t statusBuffer; - err = writeTransactionData(BC_REPLY, flags, -1, 0, reply, &statusBuffer); - if (err < NO_ERROR) return err; - - return waitForResponse(NULL, NULL); -} - -status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult) -{ - int32_t cmd; - int32_t err; - - while (1) { - if ((err=talkWithDriver()) < NO_ERROR) break; - err = mIn.errorCheck(); - if (err < NO_ERROR) break; - if (mIn.dataAvail() == 0) continue; - - cmd = mIn.readInt32(); - - IF_LOG_COMMANDS() { - alog << "Processing waitForResponse Command: " - << getReturnString(cmd) << endl; - } - - switch (cmd) { - case BR_TRANSACTION_COMPLETE: - if (!reply && !acquireResult) goto finish; - break; - - case BR_DEAD_REPLY: - err = DEAD_OBJECT; - goto finish; - - case BR_FAILED_REPLY: - err = FAILED_TRANSACTION; - goto finish; - - case BR_ACQUIRE_RESULT: - { - LOG_ASSERT(acquireResult != NULL, "Unexpected brACQUIRE_RESULT"); - const int32_t result = mIn.readInt32(); - if (!acquireResult) continue; - *acquireResult = result ? NO_ERROR : INVALID_OPERATION; - } - goto finish; - - case BR_REPLY: - { - binder_transaction_data tr; - err = mIn.read(&tr, sizeof(tr)); - LOG_ASSERT(err == NO_ERROR, "Not enough command data for brREPLY"); - if (err != NO_ERROR) goto finish; - - if (reply) { - if ((tr.flags & TF_STATUS_CODE) == 0) { - reply->ipcSetDataReference( - reinterpret_cast(tr.data.ptr.buffer), - tr.data_size, - reinterpret_cast(tr.data.ptr.offsets), - tr.offsets_size/sizeof(size_t), - freeBuffer, this); - } else { - err = *static_cast(tr.data.ptr.buffer); - freeBuffer(NULL, - reinterpret_cast(tr.data.ptr.buffer), - tr.data_size, - reinterpret_cast(tr.data.ptr.offsets), - tr.offsets_size/sizeof(size_t), this); - } - } else { - freeBuffer(NULL, - reinterpret_cast(tr.data.ptr.buffer), - tr.data_size, - reinterpret_cast(tr.data.ptr.offsets), - tr.offsets_size/sizeof(size_t), this); - continue; - } - } - goto finish; - - default: - err = executeCommand(cmd); - if (err != NO_ERROR) goto finish; - break; - } - } - -finish: - if (err != NO_ERROR) { - if (acquireResult) *acquireResult = err; - if (reply) reply->setError(err); - mLastError = err; - } - - return err; -} - -status_t IPCThreadState::talkWithDriver(bool doReceive) -{ - LOG_ASSERT(mProcess->mDriverFD >= 0, "Binder driver is not opened"); - - binder_write_read bwr; - - // Is the read buffer empty? - const bool needRead = mIn.dataPosition() >= mIn.dataSize(); - - // We don't want to write anything if we are still reading - // from data left in the input buffer and the caller - // has requested to read the next data. - const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : 0; - - bwr.write_size = outAvail; - bwr.write_buffer = (long unsigned int)mOut.data(); - - // This is what we'll read. - if (doReceive && needRead) { - bwr.read_size = mIn.dataCapacity(); - bwr.read_buffer = (long unsigned int)mIn.data(); - } else { - bwr.read_size = 0; - } - - IF_LOG_COMMANDS() { - TextOutput::Bundle _b(alog); - if (outAvail != 0) { - alog << "Sending commands to driver: " << indent; - const void* cmds = (const void*)bwr.write_buffer; - const void* end = ((const uint8_t*)cmds)+bwr.write_size; - alog << HexDump(cmds, bwr.write_size) << endl; - while (cmds < end) cmds = printCommand(alog, cmds); - alog << dedent; - } - alog << "Size of receive buffer: " << bwr.read_size - << ", needRead: " << needRead << ", doReceive: " << doReceive << endl; - } - - // Return immediately if there is nothing to do. - if ((bwr.write_size == 0) && (bwr.read_size == 0)) return NO_ERROR; - - bwr.write_consumed = 0; - bwr.read_consumed = 0; - status_t err; - do { - IF_LOG_COMMANDS() { - alog << "About to read/write, write size = " << mOut.dataSize() << endl; - } -#if defined(HAVE_ANDROID_OS) - if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0) - err = NO_ERROR; - else - err = -errno; -#else - err = INVALID_OPERATION; -#endif - IF_LOG_COMMANDS() { - alog << "Finished read/write, write size = " << mOut.dataSize() << endl; - } - } while (err == -EINTR); - - IF_LOG_COMMANDS() { - alog << "Our err: " << (void*)err << ", write consumed: " - << bwr.write_consumed << " (of " << mOut.dataSize() - << "), read consumed: " << bwr.read_consumed << endl; - } - - if (err >= NO_ERROR) { - if (bwr.write_consumed > 0) { - if (bwr.write_consumed < (ssize_t)mOut.dataSize()) - mOut.remove(0, bwr.write_consumed); - else - mOut.setDataSize(0); - } - if (bwr.read_consumed > 0) { - mIn.setDataSize(bwr.read_consumed); - mIn.setDataPosition(0); - } - IF_LOG_COMMANDS() { - TextOutput::Bundle _b(alog); - alog << "Remaining data size: " << mOut.dataSize() << endl; - alog << "Received commands from driver: " << indent; - const void* cmds = mIn.data(); - const void* end = mIn.data() + mIn.dataSize(); - alog << HexDump(cmds, mIn.dataSize()) << endl; - while (cmds < end) cmds = printReturnCommand(alog, cmds); - alog << dedent; - } - return NO_ERROR; - } - - return err; -} - -status_t IPCThreadState::writeTransactionData(int32_t cmd, uint32_t binderFlags, - int32_t handle, uint32_t code, const Parcel& data, status_t* statusBuffer) -{ - binder_transaction_data tr; - - tr.target.handle = handle; - tr.code = code; - tr.flags = binderFlags; - - const status_t err = data.errorCheck(); - if (err == NO_ERROR) { - tr.data_size = data.ipcDataSize(); - tr.data.ptr.buffer = data.ipcData(); - tr.offsets_size = data.ipcObjectsCount()*sizeof(size_t); - tr.data.ptr.offsets = data.ipcObjects(); - } else if (statusBuffer) { - tr.flags |= TF_STATUS_CODE; - *statusBuffer = err; - tr.data_size = sizeof(status_t); - tr.data.ptr.buffer = statusBuffer; - tr.offsets_size = 0; - tr.data.ptr.offsets = NULL; - } else { - return (mLastError = err); - } - - mOut.writeInt32(cmd); - mOut.write(&tr, sizeof(tr)); - - return NO_ERROR; -} - -sp the_context_object; - -void setTheContextObject(sp obj) -{ - the_context_object = obj; -} - -status_t IPCThreadState::executeCommand(int32_t cmd) -{ - BBinder* obj; - RefBase::weakref_type* refs; - status_t result = NO_ERROR; - - switch (cmd) { - case BR_ERROR: - result = mIn.readInt32(); - break; - - case BR_OK: - break; - - case BR_ACQUIRE: - refs = (RefBase::weakref_type*)mIn.readInt32(); - obj = (BBinder*)mIn.readInt32(); - LOG_ASSERT(refs->refBase() == obj, - "BR_ACQUIRE: object %p does not match cookie %p (expected %p)", - refs, obj, refs->refBase()); - obj->incStrong(mProcess.get()); - IF_LOG_REMOTEREFS() { - LOG_REMOTEREFS("BR_ACQUIRE from driver on %p", obj); - obj->printRefs(); - } - mOut.writeInt32(BC_ACQUIRE_DONE); - mOut.writeInt32((int32_t)refs); - mOut.writeInt32((int32_t)obj); - break; - - case BR_RELEASE: - refs = (RefBase::weakref_type*)mIn.readInt32(); - obj = (BBinder*)mIn.readInt32(); - LOG_ASSERT(refs->refBase() == obj, - "BR_RELEASE: object %p does not match cookie %p (expected %p)", - refs, obj, refs->refBase()); - IF_LOG_REMOTEREFS() { - LOG_REMOTEREFS("BR_RELEASE from driver on %p", obj); - obj->printRefs(); - } - mPendingStrongDerefs.push(obj); - break; - - case BR_INCREFS: - refs = (RefBase::weakref_type*)mIn.readInt32(); - obj = (BBinder*)mIn.readInt32(); - refs->incWeak(mProcess.get()); - mOut.writeInt32(BC_INCREFS_DONE); - mOut.writeInt32((int32_t)refs); - mOut.writeInt32((int32_t)obj); - break; - - case BR_DECREFS: - refs = (RefBase::weakref_type*)mIn.readInt32(); - obj = (BBinder*)mIn.readInt32(); - // NOTE: This assertion is not valid, because the object may no - // longer exist (thus the (BBinder*)cast above resulting in a different - // memory address). - //LOG_ASSERT(refs->refBase() == obj, - // "BR_DECREFS: object %p does not match cookie %p (expected %p)", - // refs, obj, refs->refBase()); - mPendingWeakDerefs.push(refs); - break; - - case BR_ATTEMPT_ACQUIRE: - refs = (RefBase::weakref_type*)mIn.readInt32(); - obj = (BBinder*)mIn.readInt32(); - - { - const bool success = refs->attemptIncStrong(mProcess.get()); - LOG_ASSERT(success && refs->refBase() == obj, - "BR_ATTEMPT_ACQUIRE: object %p does not match cookie %p (expected %p)", - refs, obj, refs->refBase()); - - mOut.writeInt32(BC_ACQUIRE_RESULT); - mOut.writeInt32((int32_t)success); - } - break; - - case BR_TRANSACTION: - { - binder_transaction_data tr; - result = mIn.read(&tr, sizeof(tr)); - LOG_ASSERT(result == NO_ERROR, - "Not enough command data for brTRANSACTION"); - if (result != NO_ERROR) break; - - Parcel buffer; - buffer.ipcSetDataReference( - reinterpret_cast(tr.data.ptr.buffer), - tr.data_size, - reinterpret_cast(tr.data.ptr.offsets), - tr.offsets_size/sizeof(size_t), freeBuffer, this); - - const pid_t origPid = mCallingPid; - const uid_t origUid = mCallingUid; - - mCallingPid = tr.sender_pid; - mCallingUid = tr.sender_euid; - - //LOGI(">>>> TRANSACT from pid %d uid %d\n", mCallingPid, mCallingUid); - - Parcel reply; - IF_LOG_TRANSACTIONS() { - TextOutput::Bundle _b(alog); - alog << "BR_TRANSACTION thr " << (void*)pthread_self() - << " / obj " << tr.target.ptr << " / code " - << TypeCode(tr.code) << ": " << indent << buffer - << dedent << endl - << "Data addr = " - << reinterpret_cast(tr.data.ptr.buffer) - << ", offsets addr=" - << reinterpret_cast(tr.data.ptr.offsets) << endl; - } - if (tr.target.ptr) { - sp b((BBinder*)tr.cookie); - const status_t error = b->transact(tr.code, buffer, &reply, 0); - if (error < NO_ERROR) reply.setError(error); - - } else { - const status_t error = the_context_object->transact(tr.code, buffer, &reply, 0); - if (error < NO_ERROR) reply.setError(error); - } - - //LOGI("<<<< TRANSACT from pid %d restore pid %d uid %d\n", - // mCallingPid, origPid, origUid); - - if ((tr.flags & TF_ONE_WAY) == 0) { - LOG_ONEWAY("Sending reply to %d!", mCallingPid); - sendReply(reply, 0); - } else { - LOG_ONEWAY("NOT sending reply to %d!", mCallingPid); - } - - mCallingPid = origPid; - mCallingUid = origUid; - - IF_LOG_TRANSACTIONS() { - TextOutput::Bundle _b(alog); - alog << "BC_REPLY thr " << (void*)pthread_self() << " / obj " - << tr.target.ptr << ": " << indent << reply << dedent << endl; - } - - } - break; - - case BR_DEAD_BINDER: - { - BpBinder *proxy = (BpBinder*)mIn.readInt32(); - proxy->sendObituary(); - mOut.writeInt32(BC_DEAD_BINDER_DONE); - mOut.writeInt32((int32_t)proxy); - } break; - - case BR_CLEAR_DEATH_NOTIFICATION_DONE: - { - BpBinder *proxy = (BpBinder*)mIn.readInt32(); - proxy->getWeakRefs()->decWeak(proxy); - } break; - - case BR_FINISHED: - result = TIMED_OUT; - break; - - case BR_NOOP: - break; - - case BR_SPAWN_LOOPER: - mProcess->spawnPooledThread(false); - break; - - default: - printf("*** BAD COMMAND %d received from Binder driver\n", cmd); - result = UNKNOWN_ERROR; - break; - } - - if (result != NO_ERROR) { - mLastError = result; - } - - return result; -} - -void IPCThreadState::threadDestructor(void *st) -{ - IPCThreadState* const self = static_cast(st); - if (self) { - self->flushCommands(); -#if defined(HAVE_ANDROID_OS) - ioctl(self->mProcess->mDriverFD, BINDER_THREAD_EXIT, 0); -#endif - delete self; - } -} - - -void IPCThreadState::freeBuffer(Parcel* parcel, const uint8_t* data, size_t dataSize, - const size_t* objects, size_t objectsSize, - void* cookie) -{ - //LOGI("Freeing parcel %p", &parcel); - IF_LOG_COMMANDS() { - alog << "Writing BC_FREE_BUFFER for " << data << endl; - } - LOG_ASSERT(data != NULL, "Called with NULL data"); - if (parcel != NULL) parcel->closeFileDescriptors(); - IPCThreadState* state = self(); - state->mOut.writeInt32(BC_FREE_BUFFER); - state->mOut.writeInt32((int32_t)data); -} - -}; // namespace android diff --git a/libs/utils/IPermissionController.cpp b/libs/utils/IPermissionController.cpp deleted file mode 100644 index f01d38fd1..000000000 --- a/libs/utils/IPermissionController.cpp +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (C) 2005 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 "PermissionController" - -#include - -#include -#include -#include -#include - -#include - -namespace android { - -// ---------------------------------------------------------------------- - -class BpPermissionController : public BpInterface -{ -public: - BpPermissionController(const sp& impl) - : BpInterface(impl) - { - } - - virtual bool checkPermission(const String16& permission, int32_t pid, int32_t uid) - { - Parcel data, reply; - data.writeInterfaceToken(IPermissionController::getInterfaceDescriptor()); - data.writeString16(permission); - data.writeInt32(pid); - data.writeInt32(uid); - remote()->transact(CHECK_PERMISSION_TRANSACTION, data, &reply); - // fail on exception - if (reply.readInt32() != 0) return 0; - return reply.readInt32() != 0; - } -}; - -IMPLEMENT_META_INTERFACE(PermissionController, "android.os.IPermissionController"); - -// ---------------------------------------------------------------------- - -#define CHECK_INTERFACE(interface, data, reply) \ - do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ - LOGW("Call incorrectly routed to " #interface); \ - return PERMISSION_DENIED; \ - } } while (0) - -status_t BnPermissionController::onTransact( - uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) -{ - //printf("PermissionController received: "); data.print(); - switch(code) { - case CHECK_PERMISSION_TRANSACTION: { - CHECK_INTERFACE(IPermissionController, data, reply); - String16 permission = data.readString16(); - int32_t pid = data.readInt32(); - int32_t uid = data.readInt32(); - bool res = checkPermission(permission, pid, uid); - // write exception - reply->writeInt32(0); - reply->writeInt32(res ? 1 : 0); - return NO_ERROR; - } break; - default: - return BBinder::onTransact(code, data, reply, flags); - } -} - -}; // namespace android - diff --git a/libs/utils/IServiceManager.cpp b/libs/utils/IServiceManager.cpp deleted file mode 100644 index 9beeaddde..000000000 --- a/libs/utils/IServiceManager.cpp +++ /dev/null @@ -1,230 +0,0 @@ -/* - * Copyright (C) 2005 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 "ServiceManager" - -#include - -#include -#include -#include -#include -#include -#include - -#include - -#include - -namespace android { - -sp defaultServiceManager() -{ - if (gDefaultServiceManager != NULL) return gDefaultServiceManager; - - { - AutoMutex _l(gDefaultServiceManagerLock); - if (gDefaultServiceManager == NULL) { - gDefaultServiceManager = interface_cast( - ProcessState::self()->getContextObject(NULL)); - } - } - - return gDefaultServiceManager; -} - -bool checkCallingPermission(const String16& permission) -{ - return checkCallingPermission(permission, NULL, NULL); -} - -static String16 _permission("permission"); - -bool checkCallingPermission(const String16& permission, int32_t* outPid, int32_t* outUid) -{ - IPCThreadState* ipcState = IPCThreadState::self(); - int32_t pid = ipcState->getCallingPid(); - int32_t uid = ipcState->getCallingUid(); - if (outPid) *outPid = pid; - if (outUid) *outUid= uid; - - sp pc; - gDefaultServiceManagerLock.lock(); - pc = gPermissionController; - gDefaultServiceManagerLock.unlock(); - - int64_t startTime = 0; - - while (true) { - if (pc != NULL) { - bool res = pc->checkPermission(permission, pid, uid); - if (res) { - if (startTime != 0) { - LOGI("Check passed after %d seconds for %s from uid=%d pid=%d", - (int)((uptimeMillis()-startTime)/1000), - String8(permission).string(), uid, pid); - } - return res; - } - - // Is this a permission failure, or did the controller go away? - if (pc->asBinder()->isBinderAlive()) { - LOGW("Permission failure: %s from uid=%d pid=%d", - String8(permission).string(), uid, pid); - return false; - } - - // Object is dead! - gDefaultServiceManagerLock.lock(); - if (gPermissionController == pc) { - gPermissionController = NULL; - } - gDefaultServiceManagerLock.unlock(); - } - - // Need to retrieve the permission controller. - sp binder = defaultServiceManager()->checkService(_permission); - if (binder == NULL) { - // Wait for the permission controller to come back... - if (startTime == 0) { - startTime = uptimeMillis(); - LOGI("Waiting to check permission %s from uid=%d pid=%d", - String8(permission).string(), uid, pid); - } - sleep(1); - } else { - pc = interface_cast(binder); - // Install the new permission controller, and try again. - gDefaultServiceManagerLock.lock(); - gPermissionController = pc; - gDefaultServiceManagerLock.unlock(); - } - } -} - -// ---------------------------------------------------------------------- - -class BpServiceManager : public BpInterface -{ -public: - BpServiceManager(const sp& impl) - : BpInterface(impl) - { - } - - virtual sp getService(const String16& name) const - { - unsigned n; - for (n = 0; n < 5; n++){ - sp svc = checkService(name); - if (svc != NULL) return svc; - LOGI("Waiting for sevice %s...\n", String8(name).string()); - sleep(1); - } - return NULL; - } - - virtual sp checkService( const String16& name) const - { - Parcel data, reply; - data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor()); - data.writeString16(name); - remote()->transact(CHECK_SERVICE_TRANSACTION, data, &reply); - return reply.readStrongBinder(); - } - - virtual status_t addService(const String16& name, const sp& service) - { - Parcel data, reply; - data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor()); - data.writeString16(name); - data.writeStrongBinder(service); - status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply); - return err == NO_ERROR ? reply.readInt32() : err; - } - - virtual Vector listServices() - { - Vector res; - int n = 0; - - for (;;) { - Parcel data, reply; - data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor()); - data.writeInt32(n++); - status_t err = remote()->transact(LIST_SERVICES_TRANSACTION, data, &reply); - if (err != NO_ERROR) - break; - res.add(reply.readString16()); - } - return res; - } -}; - -IMPLEMENT_META_INTERFACE(ServiceManager, "android.os.IServiceManager"); - -// ---------------------------------------------------------------------- - -#define CHECK_INTERFACE(interface, data, reply) \ - do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ - LOGW("Call incorrectly routed to " #interface); \ - return PERMISSION_DENIED; \ - } } while (0) - -status_t BnServiceManager::onTransact( - uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) -{ - //printf("ServiceManager received: "); data.print(); - switch(code) { - case GET_SERVICE_TRANSACTION: { - CHECK_INTERFACE(IServiceManager, data, reply); - String16 which = data.readString16(); - sp b = const_cast(this)->getService(which); - reply->writeStrongBinder(b); - return NO_ERROR; - } break; - case CHECK_SERVICE_TRANSACTION: { - CHECK_INTERFACE(IServiceManager, data, reply); - String16 which = data.readString16(); - sp b = const_cast(this)->checkService(which); - reply->writeStrongBinder(b); - return NO_ERROR; - } break; - case ADD_SERVICE_TRANSACTION: { - CHECK_INTERFACE(IServiceManager, data, reply); - String16 which = data.readString16(); - sp b = data.readStrongBinder(); - status_t err = addService(which, b); - reply->writeInt32(err); - return NO_ERROR; - } break; - case LIST_SERVICES_TRANSACTION: { - CHECK_INTERFACE(IServiceManager, data, reply); - Vector list = listServices(); - const size_t N = list.size(); - reply->writeInt32(N); - for (size_t i=0; iwriteString16(list[i]); - } - return NO_ERROR; - } break; - default: - return BBinder::onTransact(code, data, reply, flags); - } -} - -}; // namespace android - diff --git a/libs/utils/MemoryBase.cpp b/libs/utils/MemoryBase.cpp deleted file mode 100644 index f25e11c6b..000000000 --- a/libs/utils/MemoryBase.cpp +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#include -#include - -#include - - -namespace android { - -// --------------------------------------------------------------------------- - -MemoryBase::MemoryBase(const sp& heap, - ssize_t offset, size_t size) - : mSize(size), mOffset(offset), mHeap(heap) -{ -} - -sp MemoryBase::getMemory(ssize_t* offset, size_t* size) const -{ - if (offset) *offset = mOffset; - if (size) *size = mSize; - return mHeap; -} - -MemoryBase::~MemoryBase() -{ -} - -// --------------------------------------------------------------------------- -}; // namespace android diff --git a/libs/utils/MemoryDealer.cpp b/libs/utils/MemoryDealer.cpp deleted file mode 100644 index cf8201b85..000000000 --- a/libs/utils/MemoryDealer.cpp +++ /dev/null @@ -1,409 +0,0 @@ -/* - * 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. - */ - -#define LOG_TAG "MemoryDealer" - -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -namespace android { - - -// ---------------------------------------------------------------------------- - -class SimpleMemory : public MemoryBase { -public: - SimpleMemory(const sp& heap, ssize_t offset, size_t size); - virtual ~SimpleMemory(); -}; - - -// ---------------------------------------------------------------------------- - -MemoryDealer::Allocation::Allocation( - const sp& dealer, ssize_t offset, size_t size, - const sp& memory) - : mDealer(dealer), mOffset(offset), mSize(size), mMemory(memory) -{ -} - -MemoryDealer::Allocation::~Allocation() -{ - if (mSize) { - /* NOTE: it's VERY important to not free allocations of size 0 because - * they're special as they don't have any record in the allocator - * and could alias some real allocation (their offset is zero). */ - mDealer->deallocate(mOffset); - } -} - -sp MemoryDealer::Allocation::getMemory( - ssize_t* offset, size_t* size) const -{ - return mMemory->getMemory(offset, size); -} - -// ---------------------------------------------------------------------------- - -MemoryDealer::MemoryDealer(size_t size, uint32_t flags, const char* name) - : mHeap(new SharedHeap(size, flags, name)), - mAllocator(new SimpleBestFitAllocator(size)) -{ -} - -MemoryDealer::MemoryDealer(const sp& heap) - : mHeap(heap), - mAllocator(new SimpleBestFitAllocator(heap->virtualSize())) -{ -} - -MemoryDealer::MemoryDealer( const sp& heap, - const sp& allocator) - : mHeap(heap), mAllocator(allocator) -{ -} - -MemoryDealer::~MemoryDealer() -{ -} - -sp MemoryDealer::allocate(size_t size, uint32_t flags) -{ - sp memory; - const ssize_t offset = allocator()->allocate(size, flags); - if (offset >= 0) { - sp new_memory = heap()->mapMemory(offset, size); - if (new_memory != 0) { - memory = new Allocation(this, offset, size, new_memory); - } else { - LOGE("couldn't map [%8x, %d]", offset, size); - if (size) { - /* NOTE: it's VERY important to not free allocations of size 0 - * because they're special as they don't have any record in the - * allocator and could alias some real allocation - * (their offset is zero). */ - allocator()->deallocate(offset); - } - } - } - return memory; -} - -void MemoryDealer::deallocate(size_t offset) -{ - allocator()->deallocate(offset); -} - -void MemoryDealer::dump(const char* what, uint32_t flags) const -{ - allocator()->dump(what, flags); -} - -const sp& MemoryDealer::heap() const { - return mHeap; -} - -const sp& MemoryDealer::allocator() const { - return mAllocator; -} - -// ---------------------------------------------------------------------------- - -// align all the memory blocks on a cache-line boundary -const int SimpleBestFitAllocator::kMemoryAlign = 32; - -SimpleBestFitAllocator::SimpleBestFitAllocator(size_t size) -{ - size_t pagesize = getpagesize(); - mHeapSize = ((size + pagesize-1) & ~(pagesize-1)); - - chunk_t* node = new chunk_t(0, mHeapSize / kMemoryAlign); - mList.insertHead(node); -} - -SimpleBestFitAllocator::~SimpleBestFitAllocator() -{ - while(!mList.isEmpty()) { - delete mList.remove(mList.head()); - } -} - -size_t SimpleBestFitAllocator::size() const -{ - return mHeapSize; -} - -size_t SimpleBestFitAllocator::allocate(size_t size, uint32_t flags) -{ - Mutex::Autolock _l(mLock); - ssize_t offset = alloc(size, flags); - return offset; -} - -status_t SimpleBestFitAllocator::deallocate(size_t offset) -{ - Mutex::Autolock _l(mLock); - chunk_t const * const freed = dealloc(offset); - if (freed) { - return NO_ERROR; - } - return NAME_NOT_FOUND; -} - -ssize_t SimpleBestFitAllocator::alloc(size_t size, uint32_t flags) -{ - if (size == 0) { - return 0; - } - size = (size + kMemoryAlign-1) / kMemoryAlign; - chunk_t* free_chunk = 0; - chunk_t* cur = mList.head(); - - size_t pagesize = getpagesize(); - while (cur) { - int extra = 0; - if (flags & PAGE_ALIGNED) - extra = ( -cur->start & ((pagesize/kMemoryAlign)-1) ) ; - - // best fit - if (cur->free && (cur->size >= (size+extra))) { - if ((!free_chunk) || (cur->size < free_chunk->size)) { - free_chunk = cur; - } - if (cur->size == size) { - break; - } - } - cur = cur->next; - } - - if (free_chunk) { - const size_t free_size = free_chunk->size; - free_chunk->free = 0; - free_chunk->size = size; - if (free_size > size) { - int extra = 0; - if (flags & PAGE_ALIGNED) - extra = ( -free_chunk->start & ((pagesize/kMemoryAlign)-1) ) ; - if (extra) { - chunk_t* split = new chunk_t(free_chunk->start, extra); - free_chunk->start += extra; - mList.insertBefore(free_chunk, split); - } - - LOGE_IF((flags&PAGE_ALIGNED) && - ((free_chunk->start*kMemoryAlign)&(pagesize-1)), - "PAGE_ALIGNED requested, but page is not aligned!!!"); - - const ssize_t tail_free = free_size - (size+extra); - if (tail_free > 0) { - chunk_t* split = new chunk_t( - free_chunk->start + free_chunk->size, tail_free); - mList.insertAfter(free_chunk, split); - } - } - return (free_chunk->start)*kMemoryAlign; - } - return NO_MEMORY; -} - -SimpleBestFitAllocator::chunk_t* SimpleBestFitAllocator::dealloc(size_t start) -{ - start = start / kMemoryAlign; - chunk_t* cur = mList.head(); - while (cur) { - if (cur->start == start) { - LOG_FATAL_IF(cur->free, - "block at offset 0x%08lX of size 0x%08lX already freed", - cur->start*kMemoryAlign, cur->size*kMemoryAlign); - - // merge freed blocks together - chunk_t* freed = cur; - cur->free = 1; - do { - chunk_t* const p = cur->prev; - chunk_t* const n = cur->next; - if (p && (p->free || !cur->size)) { - freed = p; - p->size += cur->size; - mList.remove(cur); - delete cur; - } - cur = n; - } while (cur && cur->free); - - #ifndef NDEBUG - if (!freed->free) { - dump_l("dealloc (!freed->free)"); - } - #endif - LOG_FATAL_IF(!freed->free, - "freed block at offset 0x%08lX of size 0x%08lX is not free!", - freed->start * kMemoryAlign, freed->size * kMemoryAlign); - - return freed; - } - cur = cur->next; - } - return 0; -} - -void SimpleBestFitAllocator::dump(const char* what, uint32_t flags) const -{ - Mutex::Autolock _l(mLock); - dump_l(what, flags); -} - -void SimpleBestFitAllocator::dump_l(const char* what, uint32_t flags) const -{ - String8 result; - dump_l(result, what, flags); - LOGD("%s", result.string()); -} - -void SimpleBestFitAllocator::dump(String8& result, - const char* what, uint32_t flags) const -{ - Mutex::Autolock _l(mLock); - dump_l(result, what, flags); -} - -void SimpleBestFitAllocator::dump_l(String8& result, - const char* what, uint32_t flags) const -{ - size_t size = 0; - int32_t i = 0; - chunk_t const* cur = mList.head(); - - const size_t SIZE = 256; - char buffer[SIZE]; - snprintf(buffer, SIZE, " %s (%p, size=%u)\n", - what, this, (unsigned int)mHeapSize); - - result.append(buffer); - - while (cur) { - const char* errs[] = {"", "| link bogus NP", - "| link bogus PN", "| link bogus NP+PN" }; - int np = ((cur->next) && cur->next->prev != cur) ? 1 : 0; - int pn = ((cur->prev) && cur->prev->next != cur) ? 2 : 0; - - snprintf(buffer, SIZE, " %3u: %08x | 0x%08X | 0x%08X | %s %s\n", - i, int(cur), int(cur->start*kMemoryAlign), - int(cur->size*kMemoryAlign), - int(cur->free) ? "F" : "A", - errs[np|pn]); - - result.append(buffer); - - if (!cur->free) - size += cur->size*kMemoryAlign; - - i++; - cur = cur->next; - } - snprintf(buffer, SIZE, " size allocated: %u (%u KB)\n", int(size), int(size/1024)); - result.append(buffer); -} - -// ---------------------------------------------------------------------------- - - -SharedHeap::SharedHeap(size_t size, uint32_t flags, char const * name) - : MemoryHeapBase(size, flags, name) -{ -} - -SharedHeap::~SharedHeap() -{ -} - -sp SharedHeap::mapMemory(size_t offset, size_t size) -{ - return new SimpleMemory(this, offset, size); -} - - -SimpleMemory::SimpleMemory(const sp& heap, - ssize_t offset, size_t size) - : MemoryBase(heap, offset, size) -{ -#ifndef NDEBUG - void* const start_ptr = (void*)(intptr_t(heap->base()) + offset); - memset(start_ptr, 0xda, size); -#endif -} - -SimpleMemory::~SimpleMemory() -{ - size_t freedOffset = getOffset(); - size_t freedSize = getSize(); - - // keep the size to unmap in excess - size_t pagesize = getpagesize(); - size_t start = freedOffset; - size_t end = start + freedSize; - start &= ~(pagesize-1); - end = (end + pagesize-1) & ~(pagesize-1); - - // give back to the kernel the pages we don't need - size_t free_start = freedOffset; - size_t free_end = free_start + freedSize; - if (start < free_start) - start = free_start; - if (end > free_end) - end = free_end; - start = (start + pagesize-1) & ~(pagesize-1); - end &= ~(pagesize-1); - - if (start < end) { - void* const start_ptr = (void*)(intptr_t(getHeap()->base()) + start); - size_t size = end-start; - -#ifndef NDEBUG - memset(start_ptr, 0xdf, size); -#endif - - // MADV_REMOVE is not defined on Dapper based Goobuntu -#ifdef MADV_REMOVE - if (size) { - int err = madvise(start_ptr, size, MADV_REMOVE); - LOGW_IF(err, "madvise(%p, %u, MADV_REMOVE) returned %s", - start_ptr, size, err<0 ? strerror(errno) : "Ok"); - } -#endif - } -} - -}; // namespace android diff --git a/libs/utils/MemoryHeapBase.cpp b/libs/utils/MemoryHeapBase.cpp deleted file mode 100644 index 825172819..000000000 --- a/libs/utils/MemoryHeapBase.cpp +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "MemoryHeapBase" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -#if HAVE_ANDROID_OS -#include -#endif - - -namespace android { - -// --------------------------------------------------------------------------- - -MemoryHeapBase::MemoryHeapBase() - : mFD(-1), mSize(0), mBase(MAP_FAILED), - mDevice(NULL), mNeedUnmap(false) -{ -} - -MemoryHeapBase::MemoryHeapBase(size_t size, uint32_t flags, char const * name) - : mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags), - mDevice(0), mNeedUnmap(false) -{ - const size_t pagesize = getpagesize(); - size = ((size + pagesize-1) & ~(pagesize-1)); - int fd = ashmem_create_region(name == NULL ? "MemoryHeapBase" : name, size); - LOGE_IF(fd<0, "error creating ashmem region: %s", strerror(errno)); - if (fd >= 0) { - if (mapfd(fd, size) == NO_ERROR) { - if (flags & READ_ONLY) { - ashmem_set_prot_region(fd, PROT_READ); - } - } - } -} - -MemoryHeapBase::MemoryHeapBase(const char* device, size_t size, uint32_t flags) - : mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags), - mDevice(0), mNeedUnmap(false) -{ - int fd = open(device, O_RDWR); - LOGE_IF(fd<0, "error opening %s: %s", device, strerror(errno)); - if (fd >= 0) { - const size_t pagesize = getpagesize(); - size = ((size + pagesize-1) & ~(pagesize-1)); - if (mapfd(fd, size) == NO_ERROR) { - mDevice = device; - } - } -} - -MemoryHeapBase::MemoryHeapBase(int fd, size_t size, uint32_t flags) - : mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags), - mDevice(0), mNeedUnmap(false) -{ - const size_t pagesize = getpagesize(); - size = ((size + pagesize-1) & ~(pagesize-1)); - mapfd(dup(fd), size); -} - -status_t MemoryHeapBase::init(int fd, void *base, int size, int flags, const char* device) -{ - if (mFD != -1) { - return INVALID_OPERATION; - } - mFD = fd; - mBase = base; - mSize = size; - mFlags = flags; - mDevice = device; - return NO_ERROR; -} - -status_t MemoryHeapBase::mapfd(int fd, size_t size) -{ - if (size == 0) { - // try to figure out the size automatically -#if HAVE_ANDROID_OS - // first try the PMEM ioctl - pmem_region reg; - int err = ioctl(fd, PMEM_GET_TOTAL_SIZE, ®); - if (err == 0) - size = reg.len; -#endif - if (size == 0) { // try fstat - struct stat sb; - if (fstat(fd, &sb) == 0) - size = sb.st_size; - } - // if it didn't work, let mmap() fail. - } - - if ((mFlags & DONT_MAP_LOCALLY) == 0) { - void* base = (uint8_t*)mmap(0, size, - PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); - if (base == MAP_FAILED) { - LOGE("mmap(fd=%d, size=%u) failed (%s)", - fd, uint32_t(size), strerror(errno)); - close(fd); - return -errno; - } - //LOGD("mmap(fd=%d, base=%p, size=%lu)", fd, base, size); - mBase = base; - mNeedUnmap = true; - } else { - mBase = 0; // not MAP_FAILED - mNeedUnmap = false; - } - mFD = fd; - mSize = size; - return NO_ERROR; -} - -MemoryHeapBase::~MemoryHeapBase() -{ - dispose(); -} - -void MemoryHeapBase::dispose() -{ - int fd = android_atomic_or(-1, &mFD); - if (fd >= 0) { - if (mNeedUnmap) { - //LOGD("munmap(fd=%d, base=%p, size=%lu)", fd, mBase, mSize); - munmap(mBase, mSize); - } - mBase = 0; - mSize = 0; - close(fd); - } -} - -int MemoryHeapBase::getHeapID() const { - return mFD; -} - -void* MemoryHeapBase::getBase() const { - return mBase; -} - -size_t MemoryHeapBase::getSize() const { - return mSize; -} - -uint32_t MemoryHeapBase::getFlags() const { - return mFlags; -} - -const char* MemoryHeapBase::getDevice() const { - return mDevice; -} - -// --------------------------------------------------------------------------- -}; // namespace android diff --git a/libs/utils/MemoryHeapPmem.cpp b/libs/utils/MemoryHeapPmem.cpp deleted file mode 100644 index eba2b3055..000000000 --- a/libs/utils/MemoryHeapPmem.cpp +++ /dev/null @@ -1,248 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "MemoryHeapPmem" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include - -#if HAVE_ANDROID_OS -#include -#endif - -namespace android { - -// --------------------------------------------------------------------------- - -MemoryHeapPmem::MemoryPmem::MemoryPmem(const sp& heap) - : BnMemory(), mClientHeap(heap) -{ -} - -MemoryHeapPmem::MemoryPmem::~MemoryPmem() { - if (mClientHeap != NULL) { - mClientHeap->remove(this); - } -} - -// --------------------------------------------------------------------------- - -class SubRegionMemory : public MemoryHeapPmem::MemoryPmem { -public: - SubRegionMemory(const sp& heap, ssize_t offset, size_t size); - virtual ~SubRegionMemory(); - virtual sp getMemory(ssize_t* offset, size_t* size) const; -private: - friend class MemoryHeapPmem; - void revoke(); - size_t mSize; - ssize_t mOffset; -}; - -SubRegionMemory::SubRegionMemory(const sp& heap, - ssize_t offset, size_t size) - : MemoryHeapPmem::MemoryPmem(heap), mSize(size), mOffset(offset) -{ -#ifndef NDEBUG - void* const start_ptr = (void*)(intptr_t(getHeap()->base()) + offset); - memset(start_ptr, 0xda, size); -#endif - -#if HAVE_ANDROID_OS - if (size > 0) { - const size_t pagesize = getpagesize(); - size = (size + pagesize-1) & ~(pagesize-1); - int our_fd = heap->heapID(); - struct pmem_region sub = { offset, size }; - int err = ioctl(our_fd, PMEM_MAP, &sub); - LOGE_IF(err<0, "PMEM_MAP failed (%s), " - "mFD=%d, sub.offset=%lu, sub.size=%lu", - strerror(errno), our_fd, sub.offset, sub.len); -} -#endif -} - -sp SubRegionMemory::getMemory(ssize_t* offset, size_t* size) const -{ - if (offset) *offset = mOffset; - if (size) *size = mSize; - return getHeap(); -} - -SubRegionMemory::~SubRegionMemory() -{ - revoke(); -} - - -void SubRegionMemory::revoke() -{ - // NOTE: revoke() doesn't need to be protected by a lock because it - // can only be called from MemoryHeapPmem::revoke(), which means - // that we can't be in ~SubRegionMemory(), or in ~SubRegionMemory(), - // which means MemoryHeapPmem::revoke() wouldn't have been able to - // promote() it. - -#if HAVE_ANDROID_OS - if (mSize != NULL) { - const sp& heap(getHeap()); - int our_fd = heap->heapID(); - struct pmem_region sub; - sub.offset = mOffset; - sub.len = mSize; - int err = ioctl(our_fd, PMEM_UNMAP, &sub); - LOGE_IF(err<0, "PMEM_UNMAP failed (%s), " - "mFD=%d, sub.offset=%lu, sub.size=%lu", - strerror(errno), our_fd, sub.offset, sub.len); - mSize = 0; - } -#endif -} - -// --------------------------------------------------------------------------- - -MemoryHeapPmem::MemoryHeapPmem(const sp& pmemHeap, - uint32_t flags) - : HeapInterface(), MemoryHeapBase() -{ - char const * const device = pmemHeap->getDevice(); -#if HAVE_ANDROID_OS - if (device) { - int fd = open(device, O_RDWR); - LOGE_IF(fd<0, "couldn't open %s (%s)", device, strerror(errno)); - if (fd >= 0) { - int err = ioctl(fd, PMEM_CONNECT, pmemHeap->heapID()); - if (err < 0) { - LOGE("PMEM_CONNECT failed (%s), mFD=%d, sub-fd=%d", - strerror(errno), fd, pmemHeap->heapID()); - close(fd); - } else { - // everything went well... - mParentHeap = pmemHeap; - MemoryHeapBase::init(fd, - pmemHeap->getBase(), - pmemHeap->getSize(), - pmemHeap->getFlags() | flags, - device); - } - } - } -#else - mParentHeap = pmemHeap; - MemoryHeapBase::init( - dup(pmemHeap->heapID()), - pmemHeap->getBase(), - pmemHeap->getSize(), - pmemHeap->getFlags() | flags, - device); -#endif -} - -MemoryHeapPmem::~MemoryHeapPmem() -{ -} - -sp MemoryHeapPmem::mapMemory(size_t offset, size_t size) -{ - sp memory = createMemory(offset, size); - if (memory != 0) { - Mutex::Autolock _l(mLock); - mAllocations.add(memory); - } - return memory; -} - -sp MemoryHeapPmem::createMemory( - size_t offset, size_t size) -{ - sp memory; - if (heapID() > 0) - memory = new SubRegionMemory(this, offset, size); - return memory; -} - -status_t MemoryHeapPmem::slap() -{ -#if HAVE_ANDROID_OS - size_t size = getSize(); - const size_t pagesize = getpagesize(); - size = (size + pagesize-1) & ~(pagesize-1); - int our_fd = getHeapID(); - struct pmem_region sub = { 0, size }; - int err = ioctl(our_fd, PMEM_MAP, &sub); - LOGE_IF(err<0, "PMEM_MAP failed (%s), " - "mFD=%d, sub.offset=%lu, sub.size=%lu", - strerror(errno), our_fd, sub.offset, sub.len); - return -errno; -#else - return NO_ERROR; -#endif -} - -status_t MemoryHeapPmem::unslap() -{ -#if HAVE_ANDROID_OS - size_t size = getSize(); - const size_t pagesize = getpagesize(); - size = (size + pagesize-1) & ~(pagesize-1); - int our_fd = getHeapID(); - struct pmem_region sub = { 0, size }; - int err = ioctl(our_fd, PMEM_UNMAP, &sub); - LOGE_IF(err<0, "PMEM_UNMAP failed (%s), " - "mFD=%d, sub.offset=%lu, sub.size=%lu", - strerror(errno), our_fd, sub.offset, sub.len); - return -errno; -#else - return NO_ERROR; -#endif -} - -void MemoryHeapPmem::revoke() -{ - SortedVector< wp > allocations; - - { // scope for lock - Mutex::Autolock _l(mLock); - allocations = mAllocations; - } - - ssize_t count = allocations.size(); - for (ssize_t i=0 ; i memory(allocations[i].promote()); - if (memory != 0) - memory->revoke(); - } -} - -void MemoryHeapPmem::remove(const wp& memory) -{ - Mutex::Autolock _l(mLock); - mAllocations.remove(memory); -} - -// --------------------------------------------------------------------------- -}; // namespace android diff --git a/libs/utils/Parcel.cpp b/libs/utils/Parcel.cpp deleted file mode 100644 index e74ad4ad8..000000000 --- a/libs/utils/Parcel.cpp +++ /dev/null @@ -1,1359 +0,0 @@ -/* - * Copyright (C) 2005 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 "Parcel" -//#define LOG_NDEBUG 0 - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include - -#ifndef INT32_MAX -#define INT32_MAX ((int32_t)(2147483647)) -#endif - -#define LOG_REFS(...) -//#define LOG_REFS(...) LOG(LOG_DEBUG, "Parcel", __VA_ARGS__) - -// --------------------------------------------------------------------------- - -#define PAD_SIZE(s) (((s)+3)&~3) - -// XXX This can be made public if we want to provide -// support for typed data. -struct small_flat_data -{ - uint32_t type; - uint32_t data; -}; - -namespace android { - -void acquire_object(const sp& proc, - const flat_binder_object& obj, const void* who) -{ - switch (obj.type) { - case BINDER_TYPE_BINDER: - if (obj.binder) { - LOG_REFS("Parcel %p acquiring reference on local %p", who, obj.cookie); - static_cast(obj.cookie)->incStrong(who); - } - return; - case BINDER_TYPE_WEAK_BINDER: - if (obj.binder) - static_cast(obj.binder)->incWeak(who); - return; - case BINDER_TYPE_HANDLE: { - const sp b = proc->getStrongProxyForHandle(obj.handle); - if (b != NULL) { - LOG_REFS("Parcel %p acquiring reference on remote %p", who, b.get()); - b->incStrong(who); - } - return; - } - case BINDER_TYPE_WEAK_HANDLE: { - const wp b = proc->getWeakProxyForHandle(obj.handle); - if (b != NULL) b.get_refs()->incWeak(who); - return; - } - case BINDER_TYPE_FD: { - // intentionally blank -- nothing to do to acquire this, but we do - // recognize it as a legitimate object type. - return; - } - } - - LOGD("Invalid object type 0x%08lx", obj.type); -} - -void release_object(const sp& proc, - const flat_binder_object& obj, const void* who) -{ - switch (obj.type) { - case BINDER_TYPE_BINDER: - if (obj.binder) { - LOG_REFS("Parcel %p releasing reference on local %p", who, obj.cookie); - static_cast(obj.cookie)->decStrong(who); - } - return; - case BINDER_TYPE_WEAK_BINDER: - if (obj.binder) - static_cast(obj.binder)->decWeak(who); - return; - case BINDER_TYPE_HANDLE: { - const sp b = proc->getStrongProxyForHandle(obj.handle); - if (b != NULL) { - LOG_REFS("Parcel %p releasing reference on remote %p", who, b.get()); - b->decStrong(who); - } - return; - } - case BINDER_TYPE_WEAK_HANDLE: { - const wp b = proc->getWeakProxyForHandle(obj.handle); - if (b != NULL) b.get_refs()->decWeak(who); - return; - } - case BINDER_TYPE_FD: { - if (obj.cookie != (void*)0) close(obj.handle); - return; - } - } - - LOGE("Invalid object type 0x%08lx", obj.type); -} - -inline static status_t finish_flatten_binder( - const sp& binder, const flat_binder_object& flat, Parcel* out) -{ - return out->writeObject(flat, false); -} - -status_t flatten_binder(const sp& proc, - const sp& binder, Parcel* out) -{ - flat_binder_object obj; - - obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS; - if (binder != NULL) { - IBinder *local = binder->localBinder(); - if (!local) { - BpBinder *proxy = binder->remoteBinder(); - if (proxy == NULL) { - LOGE("null proxy"); - } - const int32_t handle = proxy ? proxy->handle() : 0; - obj.type = BINDER_TYPE_HANDLE; - obj.handle = handle; - obj.cookie = NULL; - } else { - obj.type = BINDER_TYPE_BINDER; - obj.binder = local->getWeakRefs(); - obj.cookie = local; - } - } else { - obj.type = BINDER_TYPE_BINDER; - obj.binder = NULL; - obj.cookie = NULL; - } - - return finish_flatten_binder(binder, obj, out); -} - -status_t flatten_binder(const sp& proc, - const wp& binder, Parcel* out) -{ - flat_binder_object obj; - - obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS; - if (binder != NULL) { - sp real = binder.promote(); - if (real != NULL) { - IBinder *local = real->localBinder(); - if (!local) { - BpBinder *proxy = real->remoteBinder(); - if (proxy == NULL) { - LOGE("null proxy"); - } - const int32_t handle = proxy ? proxy->handle() : 0; - obj.type = BINDER_TYPE_WEAK_HANDLE; - obj.handle = handle; - obj.cookie = NULL; - } else { - obj.type = BINDER_TYPE_WEAK_BINDER; - obj.binder = binder.get_refs(); - obj.cookie = binder.unsafe_get(); - } - return finish_flatten_binder(real, obj, out); - } - - // XXX How to deal? In order to flatten the given binder, - // we need to probe it for information, which requires a primary - // reference... but we don't have one. - // - // The OpenBinder implementation uses a dynamic_cast<> here, - // but we can't do that with the different reference counting - // implementation we are using. - LOGE("Unable to unflatten Binder weak reference!"); - obj.type = BINDER_TYPE_BINDER; - obj.binder = NULL; - obj.cookie = NULL; - return finish_flatten_binder(NULL, obj, out); - - } else { - obj.type = BINDER_TYPE_BINDER; - obj.binder = NULL; - obj.cookie = NULL; - return finish_flatten_binder(NULL, obj, out); - } -} - -inline static status_t finish_unflatten_binder( - BpBinder* proxy, const flat_binder_object& flat, const Parcel& in) -{ - return NO_ERROR; -} - -status_t unflatten_binder(const sp& proc, - const Parcel& in, sp* out) -{ - const flat_binder_object* flat = in.readObject(false); - - if (flat) { - switch (flat->type) { - case BINDER_TYPE_BINDER: - *out = static_cast(flat->cookie); - return finish_unflatten_binder(NULL, *flat, in); - case BINDER_TYPE_HANDLE: - *out = proc->getStrongProxyForHandle(flat->handle); - return finish_unflatten_binder( - static_cast(out->get()), *flat, in); - } - } - return BAD_TYPE; -} - -status_t unflatten_binder(const sp& proc, - const Parcel& in, wp* out) -{ - const flat_binder_object* flat = in.readObject(false); - - if (flat) { - switch (flat->type) { - case BINDER_TYPE_BINDER: - *out = static_cast(flat->cookie); - return finish_unflatten_binder(NULL, *flat, in); - case BINDER_TYPE_WEAK_BINDER: - if (flat->binder != NULL) { - out->set_object_and_refs( - static_cast(flat->cookie), - static_cast(flat->binder)); - } else { - *out = NULL; - } - return finish_unflatten_binder(NULL, *flat, in); - case BINDER_TYPE_HANDLE: - case BINDER_TYPE_WEAK_HANDLE: - *out = proc->getWeakProxyForHandle(flat->handle); - return finish_unflatten_binder( - static_cast(out->unsafe_get()), *flat, in); - } - } - return BAD_TYPE; -} - -// --------------------------------------------------------------------------- - -Parcel::Parcel() -{ - initState(); -} - -Parcel::~Parcel() -{ - freeDataNoInit(); -} - -const uint8_t* Parcel::data() const -{ - return mData; -} - -size_t Parcel::dataSize() const -{ - return (mDataSize > mDataPos ? mDataSize : mDataPos); -} - -size_t Parcel::dataAvail() const -{ - // TODO: decide what to do about the possibility that this can - // report an available-data size that exceeds a Java int's max - // positive value, causing havoc. Fortunately this will only - // happen if someone constructs a Parcel containing more than two - // gigabytes of data, which on typical phone hardware is simply - // not possible. - return dataSize() - dataPosition(); -} - -size_t Parcel::dataPosition() const -{ - return mDataPos; -} - -size_t Parcel::dataCapacity() const -{ - return mDataCapacity; -} - -status_t Parcel::setDataSize(size_t size) -{ - status_t err; - err = continueWrite(size); - if (err == NO_ERROR) { - mDataSize = size; - LOGV("setDataSize Setting data size of %p to %d\n", this, mDataSize); - } - return err; -} - -void Parcel::setDataPosition(size_t pos) const -{ - mDataPos = pos; - mNextObjectHint = 0; -} - -status_t Parcel::setDataCapacity(size_t size) -{ - if (size > mDataSize) return continueWrite(size); - return NO_ERROR; -} - -status_t Parcel::setData(const uint8_t* buffer, size_t len) -{ - status_t err = restartWrite(len); - if (err == NO_ERROR) { - memcpy(const_cast(data()), buffer, len); - mDataSize = len; - mFdsKnown = false; - } - return err; -} - -status_t Parcel::appendFrom(Parcel *parcel, size_t offset, size_t len) -{ - const sp proc(ProcessState::self()); - status_t err; - uint8_t *data = parcel->mData; - size_t *objects = parcel->mObjects; - size_t size = parcel->mObjectsSize; - int startPos = mDataPos; - int firstIndex = -1, lastIndex = -2; - - if (len == 0) { - return NO_ERROR; - } - - // range checks against the source parcel size - if ((offset > parcel->mDataSize) - || (len > parcel->mDataSize) - || (offset + len > parcel->mDataSize)) { - return BAD_VALUE; - } - - // Count objects in range - for (int i = 0; i < (int) size; i++) { - size_t off = objects[i]; - if ((off >= offset) && (off < offset + len)) { - if (firstIndex == -1) { - firstIndex = i; - } - lastIndex = i; - } - } - int numObjects = lastIndex - firstIndex + 1; - - // grow data - err = growData(len); - if (err != NO_ERROR) { - return err; - } - - // append data - memcpy(mData + mDataPos, data + offset, len); - mDataPos += len; - mDataSize += len; - - if (numObjects > 0) { - // grow objects - if (mObjectsCapacity < mObjectsSize + numObjects) { - int newSize = ((mObjectsSize + numObjects)*3)/2; - size_t *objects = - (size_t*)realloc(mObjects, newSize*sizeof(size_t)); - if (objects == (size_t*)0) { - return NO_MEMORY; - } - mObjects = objects; - mObjectsCapacity = newSize; - } - - // append and acquire objects - int idx = mObjectsSize; - for (int i = firstIndex; i <= lastIndex; i++) { - size_t off = objects[i] - offset + startPos; - mObjects[idx++] = off; - mObjectsSize++; - - const flat_binder_object* flat - = reinterpret_cast(mData + off); - acquire_object(proc, *flat, this); - - // take note if the object is a file descriptor - if (flat->type == BINDER_TYPE_FD) { - mHasFds = mFdsKnown = true; - } - } - } - - return NO_ERROR; -} - -bool Parcel::hasFileDescriptors() const -{ - if (!mFdsKnown) { - scanForFds(); - } - return mHasFds; -} - -status_t Parcel::writeInterfaceToken(const String16& interface) -{ - // currently the interface identification token is just its name as a string - return writeString16(interface); -} - -bool Parcel::enforceInterface(const String16& interface) const -{ - String16 str = readString16(); - if (str == interface) { - return true; - } else { - LOGW("**** enforceInterface() expected '%s' but read '%s'\n", - String8(interface).string(), String8(str).string()); - return false; - } -} - -const size_t* Parcel::objects() const -{ - return mObjects; -} - -size_t Parcel::objectsCount() const -{ - return mObjectsSize; -} - -status_t Parcel::errorCheck() const -{ - return mError; -} - -void Parcel::setError(status_t err) -{ - mError = err; -} - -status_t Parcel::finishWrite(size_t len) -{ - //printf("Finish write of %d\n", len); - mDataPos += len; - LOGV("finishWrite Setting data pos of %p to %d\n", this, mDataPos); - if (mDataPos > mDataSize) { - mDataSize = mDataPos; - LOGV("finishWrite Setting data size of %p to %d\n", this, mDataSize); - } - //printf("New pos=%d, size=%d\n", mDataPos, mDataSize); - return NO_ERROR; -} - -status_t Parcel::writeUnpadded(const void* data, size_t len) -{ - size_t end = mDataPos + len; - if (end < mDataPos) { - // integer overflow - return BAD_VALUE; - } - - if (end <= mDataCapacity) { -restart_write: - memcpy(mData+mDataPos, data, len); - return finishWrite(len); - } - - status_t err = growData(len); - if (err == NO_ERROR) goto restart_write; - return err; -} - -status_t Parcel::write(const void* data, size_t len) -{ - void* const d = writeInplace(len); - if (d) { - memcpy(d, data, len); - return NO_ERROR; - } - return mError; -} - -void* Parcel::writeInplace(size_t len) -{ - const size_t padded = PAD_SIZE(len); - - // sanity check for integer overflow - if (mDataPos+padded < mDataPos) { - return NULL; - } - - if ((mDataPos+padded) <= mDataCapacity) { -restart_write: - //printf("Writing %ld bytes, padded to %ld\n", len, padded); - uint8_t* const data = mData+mDataPos; - - // Need to pad at end? - if (padded != len) { -#if BYTE_ORDER == BIG_ENDIAN - static const uint32_t mask[4] = { - 0x00000000, 0xffffff00, 0xffff0000, 0xff000000 - }; -#endif -#if BYTE_ORDER == LITTLE_ENDIAN - static const uint32_t mask[4] = { - 0x00000000, 0x00ffffff, 0x0000ffff, 0x000000ff - }; -#endif - //printf("Applying pad mask: %p to %p\n", (void*)mask[padded-len], - // *reinterpret_cast(data+padded-4)); - *reinterpret_cast(data+padded-4) &= mask[padded-len]; - } - - finishWrite(padded); - return data; - } - - status_t err = growData(padded); - if (err == NO_ERROR) goto restart_write; - return NULL; -} - -status_t Parcel::writeInt32(int32_t val) -{ - if ((mDataPos+sizeof(val)) <= mDataCapacity) { -restart_write: - *reinterpret_cast(mData+mDataPos) = val; - return finishWrite(sizeof(val)); - } - - status_t err = growData(sizeof(val)); - if (err == NO_ERROR) goto restart_write; - return err; -} - -status_t Parcel::writeInt64(int64_t val) -{ - if ((mDataPos+sizeof(val)) <= mDataCapacity) { -restart_write: - *reinterpret_cast(mData+mDataPos) = val; - return finishWrite(sizeof(val)); - } - - status_t err = growData(sizeof(val)); - if (err == NO_ERROR) goto restart_write; - return err; -} - -status_t Parcel::writeFloat(float val) -{ - if ((mDataPos+sizeof(val)) <= mDataCapacity) { -restart_write: - *reinterpret_cast(mData+mDataPos) = val; - return finishWrite(sizeof(val)); - } - - status_t err = growData(sizeof(val)); - if (err == NO_ERROR) goto restart_write; - return err; -} - -status_t Parcel::writeDouble(double val) -{ - if ((mDataPos+sizeof(val)) <= mDataCapacity) { -restart_write: - *reinterpret_cast(mData+mDataPos) = val; - return finishWrite(sizeof(val)); - } - - status_t err = growData(sizeof(val)); - if (err == NO_ERROR) goto restart_write; - return err; -} - -status_t Parcel::writeCString(const char* str) -{ - return write(str, strlen(str)+1); -} - -status_t Parcel::writeString8(const String8& str) -{ - status_t err = writeInt32(str.bytes()); - if (err == NO_ERROR) { - err = write(str.string(), str.bytes()+1); - } - return err; -} - -status_t Parcel::writeString16(const String16& str) -{ - return writeString16(str.string(), str.size()); -} - -status_t Parcel::writeString16(const char16_t* str, size_t len) -{ - if (str == NULL) return writeInt32(-1); - - status_t err = writeInt32(len); - if (err == NO_ERROR) { - len *= sizeof(char16_t); - uint8_t* data = (uint8_t*)writeInplace(len+sizeof(char16_t)); - if (data) { - memcpy(data, str, len); - *reinterpret_cast(data+len) = 0; - return NO_ERROR; - } - err = mError; - } - return err; -} - -status_t Parcel::writeStrongBinder(const sp& val) -{ - return flatten_binder(ProcessState::self(), val, this); -} - -status_t Parcel::writeWeakBinder(const wp& val) -{ - return flatten_binder(ProcessState::self(), val, this); -} - -status_t Parcel::writeNativeHandle(const native_handle* handle) -{ - if (handle->version != sizeof(native_handle)) - return BAD_TYPE; - - status_t err; - err = writeInt32(handle->numFds); - if (err != NO_ERROR) return err; - - err = writeInt32(handle->numInts); - if (err != NO_ERROR) return err; - - for (int i=0 ; err==NO_ERROR && inumFds ; i++) - err = writeDupFileDescriptor(handle->data[i]); - - if (err != NO_ERROR) { - LOGD("write native handle, write dup fd failed"); - return err; - } - err = write(handle->data + handle->numFds, sizeof(int)*handle->numInts); - return err; -} - -status_t Parcel::writeFileDescriptor(int fd) -{ - flat_binder_object obj; - obj.type = BINDER_TYPE_FD; - obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS; - obj.handle = fd; - obj.cookie = (void*)0; - return writeObject(obj, true); -} - -status_t Parcel::writeDupFileDescriptor(int fd) -{ - flat_binder_object obj; - obj.type = BINDER_TYPE_FD; - obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS; - obj.handle = dup(fd); - obj.cookie = (void*)1; - return writeObject(obj, true); -} - -status_t Parcel::writeObject(const flat_binder_object& val, bool nullMetaData) -{ - const bool enoughData = (mDataPos+sizeof(val)) <= mDataCapacity; - const bool enoughObjects = mObjectsSize < mObjectsCapacity; - if (enoughData && enoughObjects) { -restart_write: - *reinterpret_cast(mData+mDataPos) = val; - - // Need to write meta-data? - if (nullMetaData || val.binder != NULL) { - mObjects[mObjectsSize] = mDataPos; - acquire_object(ProcessState::self(), val, this); - mObjectsSize++; - } - - // remember if it's a file descriptor - if (val.type == BINDER_TYPE_FD) { - mHasFds = mFdsKnown = true; - } - - return finishWrite(sizeof(flat_binder_object)); - } - - if (!enoughData) { - const status_t err = growData(sizeof(val)); - if (err != NO_ERROR) return err; - } - if (!enoughObjects) { - size_t newSize = ((mObjectsSize+2)*3)/2; - size_t* objects = (size_t*)realloc(mObjects, newSize*sizeof(size_t)); - if (objects == NULL) return NO_MEMORY; - mObjects = objects; - mObjectsCapacity = newSize; - } - - goto restart_write; -} - - -void Parcel::remove(size_t start, size_t amt) -{ - LOG_ALWAYS_FATAL("Parcel::remove() not yet implemented!"); -} - -status_t Parcel::read(void* outData, size_t len) const -{ - if ((mDataPos+PAD_SIZE(len)) >= mDataPos && (mDataPos+PAD_SIZE(len)) <= mDataSize) { - memcpy(outData, mData+mDataPos, len); - mDataPos += PAD_SIZE(len); - LOGV("read Setting data pos of %p to %d\n", this, mDataPos); - return NO_ERROR; - } - return NOT_ENOUGH_DATA; -} - -const void* Parcel::readInplace(size_t len) const -{ - if ((mDataPos+PAD_SIZE(len)) >= mDataPos && (mDataPos+PAD_SIZE(len)) <= mDataSize) { - const void* data = mData+mDataPos; - mDataPos += PAD_SIZE(len); - LOGV("readInplace Setting data pos of %p to %d\n", this, mDataPos); - return data; - } - return NULL; -} - -status_t Parcel::readInt32(int32_t *pArg) const -{ - if ((mDataPos+sizeof(int32_t)) <= mDataSize) { - const void* data = mData+mDataPos; - mDataPos += sizeof(int32_t); - *pArg = *reinterpret_cast(data); - return NO_ERROR; - } else { - return NOT_ENOUGH_DATA; - } -} - -int32_t Parcel::readInt32() const -{ - if ((mDataPos+sizeof(int32_t)) <= mDataSize) { - const void* data = mData+mDataPos; - mDataPos += sizeof(int32_t); - LOGV("readInt32 Setting data pos of %p to %d\n", this, mDataPos); - return *reinterpret_cast(data); - } - return 0; -} - - -status_t Parcel::readInt64(int64_t *pArg) const -{ - if ((mDataPos+sizeof(int64_t)) <= mDataSize) { - const void* data = mData+mDataPos; - mDataPos += sizeof(int64_t); - *pArg = *reinterpret_cast(data); - LOGV("readInt64 Setting data pos of %p to %d\n", this, mDataPos); - return NO_ERROR; - } else { - return NOT_ENOUGH_DATA; - } -} - - -int64_t Parcel::readInt64() const -{ - if ((mDataPos+sizeof(int64_t)) <= mDataSize) { - const void* data = mData+mDataPos; - mDataPos += sizeof(int64_t); - LOGV("readInt64 Setting data pos of %p to %d\n", this, mDataPos); - return *reinterpret_cast(data); - } - return 0; -} - -status_t Parcel::readFloat(float *pArg) const -{ - if ((mDataPos+sizeof(float)) <= mDataSize) { - const void* data = mData+mDataPos; - mDataPos += sizeof(float); - LOGV("readFloat Setting data pos of %p to %d\n", this, mDataPos); - *pArg = *reinterpret_cast(data); - return NO_ERROR; - } else { - return NOT_ENOUGH_DATA; - } -} - - -float Parcel::readFloat() const -{ - if ((mDataPos+sizeof(float)) <= mDataSize) { - const void* data = mData+mDataPos; - mDataPos += sizeof(float); - LOGV("readFloat Setting data pos of %p to %d\n", this, mDataPos); - return *reinterpret_cast(data); - } - return 0; -} - -status_t Parcel::readDouble(double *pArg) const -{ - if ((mDataPos+sizeof(double)) <= mDataSize) { - const void* data = mData+mDataPos; - mDataPos += sizeof(double); - LOGV("readDouble Setting data pos of %p to %d\n", this, mDataPos); - *pArg = *reinterpret_cast(data); - return NO_ERROR; - } else { - return NOT_ENOUGH_DATA; - } -} - - -double Parcel::readDouble() const -{ - if ((mDataPos+sizeof(double)) <= mDataSize) { - const void* data = mData+mDataPos; - mDataPos += sizeof(double); - LOGV("readDouble Setting data pos of %p to %d\n", this, mDataPos); - return *reinterpret_cast(data); - } - return 0; -} - - -const char* Parcel::readCString() const -{ - const size_t avail = mDataSize-mDataPos; - if (avail > 0) { - const char* str = reinterpret_cast(mData+mDataPos); - // is the string's trailing NUL within the parcel's valid bounds? - const char* eos = reinterpret_cast(memchr(str, 0, avail)); - if (eos) { - const size_t len = eos - str; - mDataPos += PAD_SIZE(len+1); - LOGV("readCString Setting data pos of %p to %d\n", this, mDataPos); - return str; - } - } - return NULL; -} - -String8 Parcel::readString8() const -{ - int32_t size = readInt32(); - // watch for potential int overflow adding 1 for trailing NUL - if (size > 0 && size < INT32_MAX) { - const char* str = (const char*)readInplace(size+1); - if (str) return String8(str, size); - } - return String8(); -} - -String16 Parcel::readString16() const -{ - size_t len; - const char16_t* str = readString16Inplace(&len); - if (str) return String16(str, len); - LOGE("Reading a NULL string not supported here."); - return String16(); -} - -const char16_t* Parcel::readString16Inplace(size_t* outLen) const -{ - int32_t size = readInt32(); - // watch for potential int overflow from size+1 - if (size >= 0 && size < INT32_MAX) { - *outLen = size; - const char16_t* str = (const char16_t*)readInplace((size+1)*sizeof(char16_t)); - if (str != NULL) { - return str; - } - } - *outLen = 0; - return NULL; -} - -sp Parcel::readStrongBinder() const -{ - sp val; - unflatten_binder(ProcessState::self(), *this, &val); - return val; -} - -wp Parcel::readWeakBinder() const -{ - wp val; - unflatten_binder(ProcessState::self(), *this, &val); - return val; -} - - -native_handle* Parcel::readNativeHandle() const -{ - int numFds, numInts; - status_t err; - err = readInt32(&numFds); - if (err != NO_ERROR) return 0; - err = readInt32(&numInts); - if (err != NO_ERROR) return 0; - - native_handle* h = native_handle_create(numFds, numInts); - for (int i=0 ; err==NO_ERROR && idata[i] = dup(readFileDescriptor()); - if (h->data[i] < 0) err = BAD_VALUE; - } - err = read(h->data + numFds, sizeof(int)*numInts); - if (err != NO_ERROR) { - native_handle_close(h); - native_handle_delete(h); - h = 0; - } - return h; -} - - -int Parcel::readFileDescriptor() const -{ - const flat_binder_object* flat = readObject(true); - if (flat) { - switch (flat->type) { - case BINDER_TYPE_FD: - //LOGI("Returning file descriptor %ld from parcel %p\n", flat->handle, this); - return flat->handle; - } - } - return BAD_TYPE; -} - -const flat_binder_object* Parcel::readObject(bool nullMetaData) const -{ - const size_t DPOS = mDataPos; - if ((DPOS+sizeof(flat_binder_object)) <= mDataSize) { - const flat_binder_object* obj - = reinterpret_cast(mData+DPOS); - mDataPos = DPOS + sizeof(flat_binder_object); - if (!nullMetaData && (obj->cookie == NULL && obj->binder == NULL)) { - // When transferring a NULL object, we don't write it into - // the object list, so we don't want to check for it when - // reading. - LOGV("readObject Setting data pos of %p to %d\n", this, mDataPos); - return obj; - } - - // Ensure that this object is valid... - size_t* const OBJS = mObjects; - const size_t N = mObjectsSize; - size_t opos = mNextObjectHint; - - if (N > 0) { - LOGV("Parcel %p looking for obj at %d, hint=%d\n", - this, DPOS, opos); - - // Start at the current hint position, looking for an object at - // the current data position. - if (opos < N) { - while (opos < (N-1) && OBJS[opos] < DPOS) { - opos++; - } - } else { - opos = N-1; - } - if (OBJS[opos] == DPOS) { - // Found it! - LOGV("Parcel found obj %d at index %d with forward search", - this, DPOS, opos); - mNextObjectHint = opos+1; - LOGV("readObject Setting data pos of %p to %d\n", this, mDataPos); - return obj; - } - - // Look backwards for it... - while (opos > 0 && OBJS[opos] > DPOS) { - opos--; - } - if (OBJS[opos] == DPOS) { - // Found it! - LOGV("Parcel found obj %d at index %d with backward search", - this, DPOS, opos); - mNextObjectHint = opos+1; - LOGV("readObject Setting data pos of %p to %d\n", this, mDataPos); - return obj; - } - } - LOGW("Attempt to read object from Parcel %p at offset %d that is not in the object list", - this, DPOS); - } - return NULL; -} - -void Parcel::closeFileDescriptors() -{ - size_t i = mObjectsSize; - if (i > 0) { - //LOGI("Closing file descriptors for %d objects...", mObjectsSize); - } - while (i > 0) { - i--; - const flat_binder_object* flat - = reinterpret_cast(mData+mObjects[i]); - if (flat->type == BINDER_TYPE_FD) { - //LOGI("Closing fd: %ld\n", flat->handle); - close(flat->handle); - } - } -} - -const uint8_t* Parcel::ipcData() const -{ - return mData; -} - -size_t Parcel::ipcDataSize() const -{ - return (mDataSize > mDataPos ? mDataSize : mDataPos); -} - -const size_t* Parcel::ipcObjects() const -{ - return mObjects; -} - -size_t Parcel::ipcObjectsCount() const -{ - return mObjectsSize; -} - -void Parcel::ipcSetDataReference(const uint8_t* data, size_t dataSize, - const size_t* objects, size_t objectsCount, release_func relFunc, void* relCookie) -{ - freeDataNoInit(); - mError = NO_ERROR; - mData = const_cast(data); - mDataSize = mDataCapacity = dataSize; - //LOGI("setDataReference Setting data size of %p to %lu (pid=%d)\n", this, mDataSize, getpid()); - mDataPos = 0; - LOGV("setDataReference Setting data pos of %p to %d\n", this, mDataPos); - mObjects = const_cast(objects); - mObjectsSize = mObjectsCapacity = objectsCount; - mNextObjectHint = 0; - mOwner = relFunc; - mOwnerCookie = relCookie; - scanForFds(); -} - -void Parcel::print(TextOutput& to, uint32_t flags) const -{ - to << "Parcel("; - - if (errorCheck() != NO_ERROR) { - const status_t err = errorCheck(); - to << "Error: " << (void*)err << " \"" << strerror(-err) << "\""; - } else if (dataSize() > 0) { - const uint8_t* DATA = data(); - to << indent << HexDump(DATA, dataSize()) << dedent; - const size_t* OBJS = objects(); - const size_t N = objectsCount(); - for (size_t i=0; i(DATA+OBJS[i]); - to << endl << "Object #" << i << " @ " << (void*)OBJS[i] << ": " - << TypeCode(flat->type & 0x7f7f7f00) - << " = " << flat->binder; - } - } else { - to << "NULL"; - } - - to << ")"; -} - -void Parcel::releaseObjects() -{ - const sp proc(ProcessState::self()); - size_t i = mObjectsSize; - uint8_t* const data = mData; - size_t* const objects = mObjects; - while (i > 0) { - i--; - const flat_binder_object* flat - = reinterpret_cast(data+objects[i]); - release_object(proc, *flat, this); - } -} - -void Parcel::acquireObjects() -{ - const sp proc(ProcessState::self()); - size_t i = mObjectsSize; - uint8_t* const data = mData; - size_t* const objects = mObjects; - while (i > 0) { - i--; - const flat_binder_object* flat - = reinterpret_cast(data+objects[i]); - acquire_object(proc, *flat, this); - } -} - -void Parcel::freeData() -{ - freeDataNoInit(); - initState(); -} - -void Parcel::freeDataNoInit() -{ - if (mOwner) { - //LOGI("Freeing data ref of %p (pid=%d)\n", this, getpid()); - mOwner(this, mData, mDataSize, mObjects, mObjectsSize, mOwnerCookie); - } else { - releaseObjects(); - if (mData) free(mData); - if (mObjects) free(mObjects); - } -} - -status_t Parcel::growData(size_t len) -{ - size_t newSize = ((mDataSize+len)*3)/2; - return (newSize <= mDataSize) - ? (status_t) NO_MEMORY - : continueWrite(newSize); -} - -status_t Parcel::restartWrite(size_t desired) -{ - if (mOwner) { - freeData(); - return continueWrite(desired); - } - - uint8_t* data = (uint8_t*)realloc(mData, desired); - if (!data && desired > mDataCapacity) { - mError = NO_MEMORY; - return NO_MEMORY; - } - - releaseObjects(); - - if (data) { - mData = data; - mDataCapacity = desired; - } - - mDataSize = mDataPos = 0; - LOGV("restartWrite Setting data size of %p to %d\n", this, mDataSize); - LOGV("restartWrite Setting data pos of %p to %d\n", this, mDataPos); - - free(mObjects); - mObjects = NULL; - mObjectsSize = mObjectsCapacity = 0; - mNextObjectHint = 0; - mHasFds = false; - mFdsKnown = true; - - return NO_ERROR; -} - -status_t Parcel::continueWrite(size_t desired) -{ - // If shrinking, first adjust for any objects that appear - // after the new data size. - size_t objectsSize = mObjectsSize; - if (desired < mDataSize) { - if (desired == 0) { - objectsSize = 0; - } else { - while (objectsSize > 0) { - if (mObjects[objectsSize-1] < desired) - break; - objectsSize--; - } - } - } - - if (mOwner) { - // If the size is going to zero, just release the owner's data. - if (desired == 0) { - freeData(); - return NO_ERROR; - } - - // If there is a different owner, we need to take - // posession. - uint8_t* data = (uint8_t*)malloc(desired); - if (!data) { - mError = NO_MEMORY; - return NO_MEMORY; - } - size_t* objects = NULL; - - if (objectsSize) { - objects = (size_t*)malloc(objectsSize*sizeof(size_t)); - if (!objects) { - mError = NO_MEMORY; - return NO_MEMORY; - } - - // Little hack to only acquire references on objects - // we will be keeping. - size_t oldObjectsSize = mObjectsSize; - mObjectsSize = objectsSize; - acquireObjects(); - mObjectsSize = oldObjectsSize; - } - - if (mData) { - memcpy(data, mData, mDataSize < desired ? mDataSize : desired); - } - if (objects && mObjects) { - memcpy(objects, mObjects, objectsSize*sizeof(size_t)); - } - //LOGI("Freeing data ref of %p (pid=%d)\n", this, getpid()); - mOwner(this, mData, mDataSize, mObjects, mObjectsSize, mOwnerCookie); - mOwner = NULL; - - mData = data; - mObjects = objects; - mDataSize = (mDataSize < desired) ? mDataSize : desired; - LOGV("continueWrite Setting data size of %p to %d\n", this, mDataSize); - mDataCapacity = desired; - mObjectsSize = mObjectsCapacity = objectsSize; - mNextObjectHint = 0; - - } else if (mData) { - if (objectsSize < mObjectsSize) { - // Need to release refs on any objects we are dropping. - const sp proc(ProcessState::self()); - for (size_t i=objectsSize; i(mData+mObjects[i]); - if (flat->type == BINDER_TYPE_FD) { - // will need to rescan because we may have lopped off the only FDs - mFdsKnown = false; - } - release_object(proc, *flat, this); - } - size_t* objects = - (size_t*)realloc(mObjects, objectsSize*sizeof(size_t)); - if (objects) { - mObjects = objects; - } - mObjectsSize = objectsSize; - mNextObjectHint = 0; - } - - // We own the data, so we can just do a realloc(). - if (desired > mDataCapacity) { - uint8_t* data = (uint8_t*)realloc(mData, desired); - if (data) { - mData = data; - mDataCapacity = desired; - } else if (desired > mDataCapacity) { - mError = NO_MEMORY; - return NO_MEMORY; - } - } else { - mDataSize = desired; - LOGV("continueWrite Setting data size of %p to %d\n", this, mDataSize); - if (mDataPos > desired) { - mDataPos = desired; - LOGV("continueWrite Setting data pos of %p to %d\n", this, mDataPos); - } - } - - } else { - // This is the first data. Easy! - uint8_t* data = (uint8_t*)malloc(desired); - if (!data) { - mError = NO_MEMORY; - return NO_MEMORY; - } - - if(!(mDataCapacity == 0 && mObjects == NULL - && mObjectsCapacity == 0)) { - LOGE("continueWrite: %d/%p/%d/%d", mDataCapacity, mObjects, mObjectsCapacity, desired); - } - - mData = data; - mDataSize = mDataPos = 0; - LOGV("continueWrite Setting data size of %p to %d\n", this, mDataSize); - LOGV("continueWrite Setting data pos of %p to %d\n", this, mDataPos); - mDataCapacity = desired; - } - - return NO_ERROR; -} - -void Parcel::initState() -{ - mError = NO_ERROR; - mData = 0; - mDataSize = 0; - mDataCapacity = 0; - mDataPos = 0; - LOGV("initState Setting data size of %p to %d\n", this, mDataSize); - LOGV("initState Setting data pos of %p to %d\n", this, mDataPos); - mObjects = NULL; - mObjectsSize = 0; - mObjectsCapacity = 0; - mNextObjectHint = 0; - mHasFds = false; - mFdsKnown = true; - mOwner = NULL; -} - -void Parcel::scanForFds() const -{ - bool hasFds = false; - for (size_t i=0; i(mData + mObjects[i]); - if (flat->type == BINDER_TYPE_FD) { - hasFds = true; - break; - } - } - mHasFds = hasFds; - mFdsKnown = true; -} - -}; // namespace android diff --git a/libs/utils/ProcessState.cpp b/libs/utils/ProcessState.cpp deleted file mode 100644 index 4567df60f..000000000 --- a/libs/utils/ProcessState.cpp +++ /dev/null @@ -1,398 +0,0 @@ -/* - * Copyright (C) 2005 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 "ProcessState" - -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#define BINDER_VM_SIZE (1*1024*1024) - -static bool gSingleProcess = false; - - -// --------------------------------------------------------------------------- - -namespace android { - -// Global variables -int mArgC; -const char* const* mArgV; -int mArgLen; - -class PoolThread : public Thread -{ -public: - PoolThread(bool isMain) - : mIsMain(isMain) - { - } - -protected: - virtual bool threadLoop() - { - IPCThreadState::self()->joinThreadPool(mIsMain); - return false; - } - - const bool mIsMain; -}; - -sp ProcessState::self() -{ - if (gProcess != NULL) return gProcess; - - AutoMutex _l(gProcessMutex); - if (gProcess == NULL) gProcess = new ProcessState; - return gProcess; -} - -void ProcessState::setSingleProcess(bool singleProcess) -{ - gSingleProcess = singleProcess; -} - - -void ProcessState::setContextObject(const sp& object) -{ - setContextObject(object, String16("default")); -} - -sp ProcessState::getContextObject(const sp& caller) -{ - if (supportsProcesses()) { - return getStrongProxyForHandle(0); - } else { - return getContextObject(String16("default"), caller); - } -} - -void ProcessState::setContextObject(const sp& object, const String16& name) -{ - AutoMutex _l(mLock); - mContexts.add(name, object); -} - -sp ProcessState::getContextObject(const String16& name, const sp& caller) -{ - mLock.lock(); - sp object( - mContexts.indexOfKey(name) >= 0 ? mContexts.valueFor(name) : NULL); - mLock.unlock(); - - //printf("Getting context object %s for %p\n", String8(name).string(), caller.get()); - - if (object != NULL) return object; - - // Don't attempt to retrieve contexts if we manage them - if (mManagesContexts) { - LOGE("getContextObject(%s) failed, but we manage the contexts!\n", - String8(name).string()); - return NULL; - } - - IPCThreadState* ipc = IPCThreadState::self(); - { - Parcel data, reply; - // no interface token on this magic transaction - data.writeString16(name); - data.writeStrongBinder(caller); - status_t result = ipc->transact(0 /*magic*/, 0, data, &reply, 0); - if (result == NO_ERROR) { - object = reply.readStrongBinder(); - } - } - - ipc->flushCommands(); - - if (object != NULL) setContextObject(object, name); - return object; -} - -bool ProcessState::supportsProcesses() const -{ - return mDriverFD >= 0; -} - -void ProcessState::startThreadPool() -{ - AutoMutex _l(mLock); - if (!mThreadPoolStarted) { - mThreadPoolStarted = true; - spawnPooledThread(true); - } -} - -bool ProcessState::isContextManager(void) const -{ - return mManagesContexts; -} - -bool ProcessState::becomeContextManager(context_check_func checkFunc, void* userData) -{ - if (!mManagesContexts) { - AutoMutex _l(mLock); - mBinderContextCheckFunc = checkFunc; - mBinderContextUserData = userData; - if (mDriverFD >= 0) { - int dummy = 0; -#if defined(HAVE_ANDROID_OS) - status_t result = ioctl(mDriverFD, BINDER_SET_CONTEXT_MGR, &dummy); -#else - status_t result = INVALID_OPERATION; -#endif - if (result == 0) { - mManagesContexts = true; - } else if (result == -1) { - mBinderContextCheckFunc = NULL; - mBinderContextUserData = NULL; - LOGE("Binder ioctl to become context manager failed: %s\n", strerror(errno)); - } - } else { - // If there is no driver, our only world is the local - // process so we can always become the context manager there. - mManagesContexts = true; - } - } - return mManagesContexts; -} - -ProcessState::handle_entry* ProcessState::lookupHandleLocked(int32_t handle) -{ - const size_t N=mHandleToObject.size(); - if (N <= (size_t)handle) { - handle_entry e; - e.binder = NULL; - e.refs = NULL; - status_t err = mHandleToObject.insertAt(e, N, handle+1-N); - if (err < NO_ERROR) return NULL; - } - return &mHandleToObject.editItemAt(handle); -} - -sp ProcessState::getStrongProxyForHandle(int32_t handle) -{ - sp result; - - AutoMutex _l(mLock); - - handle_entry* e = lookupHandleLocked(handle); - - if (e != NULL) { - // We need to create a new BpBinder if there isn't currently one, OR we - // are unable to acquire a weak reference on this current one. See comment - // in getWeakProxyForHandle() for more info about this. - IBinder* b = e->binder; - if (b == NULL || !e->refs->attemptIncWeak(this)) { - b = new BpBinder(handle); - e->binder = b; - if (b) e->refs = b->getWeakRefs(); - result = b; - } else { - // This little bit of nastyness is to allow us to add a primary - // reference to the remote proxy when this team doesn't have one - // but another team is sending the handle to us. - result.force_set(b); - e->refs->decWeak(this); - } - } - - return result; -} - -wp ProcessState::getWeakProxyForHandle(int32_t handle) -{ - wp result; - - AutoMutex _l(mLock); - - handle_entry* e = lookupHandleLocked(handle); - - if (e != NULL) { - // We need to create a new BpBinder if there isn't currently one, OR we - // are unable to acquire a weak reference on this current one. The - // attemptIncWeak() is safe because we know the BpBinder destructor will always - // call expungeHandle(), which acquires the same lock we are holding now. - // We need to do this because there is a race condition between someone - // releasing a reference on this BpBinder, and a new reference on its handle - // arriving from the driver. - IBinder* b = e->binder; - if (b == NULL || !e->refs->attemptIncWeak(this)) { - b = new BpBinder(handle); - result = b; - e->binder = b; - if (b) e->refs = b->getWeakRefs(); - } else { - result = b; - e->refs->decWeak(this); - } - } - - return result; -} - -void ProcessState::expungeHandle(int32_t handle, IBinder* binder) -{ - AutoMutex _l(mLock); - - handle_entry* e = lookupHandleLocked(handle); - - // This handle may have already been replaced with a new BpBinder - // (if someone failed the AttemptIncWeak() above); we don't want - // to overwrite it. - if (e && e->binder == binder) e->binder = NULL; -} - -void ProcessState::setArgs(int argc, const char* const argv[]) -{ - mArgC = argc; - mArgV = (const char **)argv; - - mArgLen = 0; - for (int i=0; i t = new PoolThread(isMain); - t->run(buf); - } -} - -static int open_driver() -{ - if (gSingleProcess) { - return -1; - } - - int fd = open("/dev/binder", O_RDWR); - if (fd >= 0) { - fcntl(fd, F_SETFD, FD_CLOEXEC); - int vers; -#if defined(HAVE_ANDROID_OS) - status_t result = ioctl(fd, BINDER_VERSION, &vers); -#else - status_t result = -1; - errno = EPERM; -#endif - if (result == -1) { - LOGE("Binder ioctl to obtain version failed: %s", strerror(errno)); - close(fd); - fd = -1; - } - if (result != 0 || vers != BINDER_CURRENT_PROTOCOL_VERSION) { - LOGE("Binder driver protocol does not match user space protocol!"); - close(fd); - fd = -1; - } -#if defined(HAVE_ANDROID_OS) - size_t maxThreads = 15; - result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads); - if (result == -1) { - LOGE("Binder ioctl to set max threads failed: %s", strerror(errno)); - } -#endif - - } else { - LOGW("Opening '/dev/binder' failed: %s\n", strerror(errno)); - } - return fd; -} - -ProcessState::ProcessState() - : mDriverFD(open_driver()) - , mVMStart(MAP_FAILED) - , mManagesContexts(false) - , mBinderContextCheckFunc(NULL) - , mBinderContextUserData(NULL) - , mThreadPoolStarted(false) - , mThreadPoolSeq(1) -{ - if (mDriverFD >= 0) { - // XXX Ideally, there should be a specific define for whether we - // have mmap (or whether we could possibly have the kernel module - // availabla). -#if !defined(HAVE_WIN32_IPC) - // mmap the binder, providing a chunk of virtual address space to receive transactions. - mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0); - if (mVMStart == MAP_FAILED) { - // *sigh* - LOGE("Using /dev/binder failed: unable to mmap transaction memory.\n"); - close(mDriverFD); - mDriverFD = -1; - } -#else - mDriverFD = -1; -#endif - } - if (mDriverFD < 0) { - // Need to run without the driver, starting our own thread pool. - } -} - -ProcessState::~ProcessState() -{ -} - -}; // namespace android diff --git a/libs/utils/Static.cpp b/libs/utils/Static.cpp index 93f7e4f0c..4dfa57830 100644 --- a/libs/utils/Static.cpp +++ b/libs/utils/Static.cpp @@ -20,7 +20,6 @@ #include #include -#include #include namespace android { @@ -87,34 +86,4 @@ TextOutput& alog(gLogTextOutput); TextOutput& aout(gStdoutTextOutput); TextOutput& aerr(gStderrTextOutput); -#ifndef LIBUTILS_NATIVE - -// ------------ ProcessState.cpp - -Mutex gProcessMutex; -sp gProcess; - -class LibUtilsIPCtStatics -{ -public: - LibUtilsIPCtStatics() - { - } - - ~LibUtilsIPCtStatics() - { - IPCThreadState::shutdown(); - } -}; - -static LibUtilsIPCtStatics gIPCStatics; - -// ------------ ServiceManager.cpp - -Mutex gDefaultServiceManagerLock; -sp gDefaultServiceManager; -sp gPermissionController; - -#endif - } // namespace android From 61c60b1a81ea43fcefa743fba97a11ef2999cd19 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Tue, 19 May 2009 19:08:10 -0700 Subject: [PATCH 104/541] move libbinder's header files under includes/binder --- include/utils/Binder.h | 103 ----------- include/utils/BpBinder.h | 122 ------------- include/utils/IBinder.h | 159 ----------------- include/utils/IInterface.h | 135 -------------- include/utils/IMemory.h | 94 ---------- include/utils/IPCThreadState.h | 110 ------------ include/utils/IPermissionController.h | 56 ------ include/utils/IServiceManager.h | 98 ---------- include/utils/MemoryBase.h | 51 ------ include/utils/MemoryDealer.h | 247 -------------------------- include/utils/MemoryHeapBase.h | 98 ---------- include/utils/MemoryHeapPmem.h | 80 --------- include/utils/Parcel.h | 211 ---------------------- include/utils/ProcessState.h | 117 ------------ 14 files changed, 1681 deletions(-) delete mode 100644 include/utils/Binder.h delete mode 100644 include/utils/BpBinder.h delete mode 100644 include/utils/IBinder.h delete mode 100644 include/utils/IInterface.h delete mode 100644 include/utils/IMemory.h delete mode 100644 include/utils/IPCThreadState.h delete mode 100644 include/utils/IPermissionController.h delete mode 100644 include/utils/IServiceManager.h delete mode 100644 include/utils/MemoryBase.h delete mode 100644 include/utils/MemoryDealer.h delete mode 100644 include/utils/MemoryHeapBase.h delete mode 100644 include/utils/MemoryHeapPmem.h delete mode 100644 include/utils/Parcel.h delete mode 100644 include/utils/ProcessState.h diff --git a/include/utils/Binder.h b/include/utils/Binder.h deleted file mode 100644 index b5b8d9851..000000000 --- a/include/utils/Binder.h +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_BINDER_H -#define ANDROID_BINDER_H - -#include - -// --------------------------------------------------------------------------- -namespace android { - -class BBinder : public IBinder -{ -public: - BBinder(); - - virtual String16 getInterfaceDescriptor() const; - virtual bool isBinderAlive() const; - virtual status_t pingBinder(); - virtual status_t dump(int fd, const Vector& args); - - virtual status_t transact( uint32_t code, - const Parcel& data, - Parcel* reply, - uint32_t flags = 0); - - virtual status_t linkToDeath(const sp& recipient, - void* cookie = NULL, - uint32_t flags = 0); - - virtual status_t unlinkToDeath( const wp& recipient, - void* cookie = NULL, - uint32_t flags = 0, - wp* outRecipient = NULL); - - virtual void attachObject( const void* objectID, - void* object, - void* cleanupCookie, - object_cleanup_func func); - virtual void* findObject(const void* objectID) const; - virtual void detachObject(const void* objectID); - - virtual BBinder* localBinder(); - -protected: - virtual ~BBinder(); - - virtual status_t onTransact( uint32_t code, - const Parcel& data, - Parcel* reply, - uint32_t flags = 0); - -private: - BBinder(const BBinder& o); - BBinder& operator=(const BBinder& o); - - class Extras; - - Extras* mExtras; - void* mReserved0; -}; - -// --------------------------------------------------------------------------- - -class BpRefBase : public virtual RefBase -{ -protected: - BpRefBase(const sp& o); - virtual ~BpRefBase(); - virtual void onFirstRef(); - virtual void onLastStrongRef(const void* id); - virtual bool onIncStrongAttempted(uint32_t flags, const void* id); - - inline IBinder* remote() { return mRemote; } - inline IBinder* remote() const { return mRemote; } - -private: - BpRefBase(const BpRefBase& o); - BpRefBase& operator=(const BpRefBase& o); - - IBinder* const mRemote; - RefBase::weakref_type* mRefs; - volatile int32_t mState; -}; - -}; // namespace android - -// --------------------------------------------------------------------------- - -#endif // ANDROID_BINDER_H diff --git a/include/utils/BpBinder.h b/include/utils/BpBinder.h deleted file mode 100644 index 7b96e296f..000000000 --- a/include/utils/BpBinder.h +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (C) 2005 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_BPBINDER_H -#define ANDROID_BPBINDER_H - -#include -#include -#include - -// --------------------------------------------------------------------------- -namespace android { - -class BpBinder : public IBinder -{ -public: - BpBinder(int32_t handle); - - inline int32_t handle() const { return mHandle; } - - virtual String16 getInterfaceDescriptor() const; - virtual bool isBinderAlive() const; - virtual status_t pingBinder(); - virtual status_t dump(int fd, const Vector& args); - - virtual status_t transact( uint32_t code, - const Parcel& data, - Parcel* reply, - uint32_t flags = 0); - - virtual status_t linkToDeath(const sp& recipient, - void* cookie = NULL, - uint32_t flags = 0); - virtual status_t unlinkToDeath( const wp& recipient, - void* cookie = NULL, - uint32_t flags = 0, - wp* outRecipient = NULL); - - virtual void attachObject( const void* objectID, - void* object, - void* cleanupCookie, - object_cleanup_func func); - virtual void* findObject(const void* objectID) const; - virtual void detachObject(const void* objectID); - - virtual BpBinder* remoteBinder(); - - status_t setConstantData(const void* data, size_t size); - void sendObituary(); - - class ObjectManager - { - public: - ObjectManager(); - ~ObjectManager(); - - void attach( const void* objectID, - void* object, - void* cleanupCookie, - IBinder::object_cleanup_func func); - void* find(const void* objectID) const; - void detach(const void* objectID); - - void kill(); - - private: - ObjectManager(const ObjectManager&); - ObjectManager& operator=(const ObjectManager&); - - struct entry_t - { - void* object; - void* cleanupCookie; - IBinder::object_cleanup_func func; - }; - - KeyedVector mObjects; - }; - -protected: - virtual ~BpBinder(); - virtual void onFirstRef(); - virtual void onLastStrongRef(const void* id); - virtual bool onIncStrongAttempted(uint32_t flags, const void* id); - -private: - const int32_t mHandle; - - struct Obituary { - wp recipient; - void* cookie; - uint32_t flags; - }; - - void reportOneDeath(const Obituary& obit); - - mutable Mutex mLock; - volatile int32_t mAlive; - volatile int32_t mObitsSent; - Vector* mObituaries; - ObjectManager mObjects; - Parcel* mConstantData; -}; - -}; // namespace android - -// --------------------------------------------------------------------------- - -#endif // ANDROID_BPBINDER_H diff --git a/include/utils/IBinder.h b/include/utils/IBinder.h deleted file mode 100644 index 737033090..000000000 --- a/include/utils/IBinder.h +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_IBINDER_H -#define ANDROID_IBINDER_H - -#include -#include -#include -#include - - -#define B_PACK_CHARS(c1, c2, c3, c4) \ - ((((c1)<<24)) | (((c2)<<16)) | (((c3)<<8)) | (c4)) - -// --------------------------------------------------------------------------- -namespace android { - -class BBinder; -class BpBinder; -class IInterface; -class Parcel; - -/** - * Base class and low-level protocol for a remotable object. - * You can derive from this class to create an object for which other - * processes can hold references to it. Communication between processes - * (method calls, property get and set) is down through a low-level - * protocol implemented on top of the transact() API. - */ -class IBinder : public virtual RefBase -{ -public: - enum { - FIRST_CALL_TRANSACTION = 0x00000001, - LAST_CALL_TRANSACTION = 0x00ffffff, - - PING_TRANSACTION = B_PACK_CHARS('_','P','N','G'), - DUMP_TRANSACTION = B_PACK_CHARS('_','D','M','P'), - INTERFACE_TRANSACTION = B_PACK_CHARS('_', 'N', 'T', 'F'), - - // Corresponds to tfOneWay -- an asynchronous call. - FLAG_ONEWAY = 0x00000001 - }; - - inline IBinder() { } - - /** - * Check if this IBinder implements the interface named by - * @a descriptor. If it does, the base pointer to it is returned, - * which you can safely static_cast<> to the concrete C++ interface. - */ - virtual sp queryLocalInterface(const String16& descriptor); - - /** - * Return the canonical name of the interface provided by this IBinder - * object. - */ - virtual String16 getInterfaceDescriptor() const = 0; - - virtual bool isBinderAlive() const = 0; - virtual status_t pingBinder() = 0; - virtual status_t dump(int fd, const Vector& args) = 0; - - virtual status_t transact( uint32_t code, - const Parcel& data, - Parcel* reply, - uint32_t flags = 0) = 0; - - /** - * This method allows you to add data that is transported through - * IPC along with your IBinder pointer. When implementing a Binder - * object, override it to write your desired data in to @a outData. - * You can then call getConstantData() on your IBinder to retrieve - * that data, from any process. You MUST return the number of bytes - * written in to the parcel (including padding). - */ - class DeathRecipient : public virtual RefBase - { - public: - virtual void binderDied(const wp& who) = 0; - }; - - /** - * Register the @a recipient for a notification if this binder - * goes away. If this binder object unexpectedly goes away - * (typically because its hosting process has been killed), - * then DeathRecipient::binderDied() will be called with a referene - * to this. - * - * The @a cookie is optional -- if non-NULL, it should be a - * memory address that you own (that is, you know it is unique). - * - * @note You will only receive death notifications for remote binders, - * as local binders by definition can't die without you dying as well. - * Trying to use this function on a local binder will result in an - * INVALID_OPERATION code being returned and nothing happening. - * - * @note This link always holds a weak reference to its recipient. - * - * @note You will only receive a weak reference to the dead - * binder. You should not try to promote this to a strong reference. - * (Nor should you need to, as there is nothing useful you can - * directly do with it now that it has passed on.) - */ - virtual status_t linkToDeath(const sp& recipient, - void* cookie = NULL, - uint32_t flags = 0) = 0; - - /** - * Remove a previously registered death notification. - * The @a recipient will no longer be called if this object - * dies. The @a cookie is optional. If non-NULL, you can - * supply a NULL @a recipient, and the recipient previously - * added with that cookie will be unlinked. - */ - virtual status_t unlinkToDeath( const wp& recipient, - void* cookie = NULL, - uint32_t flags = 0, - wp* outRecipient = NULL) = 0; - - virtual bool checkSubclass(const void* subclassID) const; - - typedef void (*object_cleanup_func)(const void* id, void* obj, void* cleanupCookie); - - virtual void attachObject( const void* objectID, - void* object, - void* cleanupCookie, - object_cleanup_func func) = 0; - virtual void* findObject(const void* objectID) const = 0; - virtual void detachObject(const void* objectID) = 0; - - virtual BBinder* localBinder(); - virtual BpBinder* remoteBinder(); - -protected: - inline virtual ~IBinder() { } - -private: -}; - -}; // namespace android - -// --------------------------------------------------------------------------- - -#endif // ANDROID_IBINDER_H diff --git a/include/utils/IInterface.h b/include/utils/IInterface.h deleted file mode 100644 index 959722a4d..000000000 --- a/include/utils/IInterface.h +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright (C) 2005 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_IINTERFACE_H -#define ANDROID_IINTERFACE_H - -#include - -namespace android { - -// ---------------------------------------------------------------------- - -class IInterface : public virtual RefBase -{ -public: - sp asBinder(); - sp asBinder() const; - -protected: - virtual IBinder* onAsBinder() = 0; -}; - -// ---------------------------------------------------------------------- - -template -inline sp interface_cast(const sp& obj) -{ - return INTERFACE::asInterface(obj); -} - -// ---------------------------------------------------------------------- - -template -class BnInterface : public INTERFACE, public BBinder -{ -public: - virtual sp queryLocalInterface(const String16& _descriptor); - virtual String16 getInterfaceDescriptor() const; - -protected: - virtual IBinder* onAsBinder(); -}; - -// ---------------------------------------------------------------------- - -template -class BpInterface : public INTERFACE, public BpRefBase -{ -public: - BpInterface(const sp& remote); - -protected: - virtual IBinder* onAsBinder(); -}; - -// ---------------------------------------------------------------------- - -#define DECLARE_META_INTERFACE(INTERFACE) \ - static const String16 descriptor; \ - static sp asInterface(const sp& obj); \ - virtual String16 getInterfaceDescriptor() const; \ - -#define IMPLEMENT_META_INTERFACE(INTERFACE, NAME) \ - const String16 I##INTERFACE::descriptor(NAME); \ - String16 I##INTERFACE::getInterfaceDescriptor() const { \ - return I##INTERFACE::descriptor; \ - } \ - sp I##INTERFACE::asInterface(const sp& obj) \ - { \ - sp intr; \ - if (obj != NULL) { \ - intr = static_cast( \ - obj->queryLocalInterface( \ - I##INTERFACE::descriptor).get()); \ - if (intr == NULL) { \ - intr = new Bp##INTERFACE(obj); \ - } \ - } \ - return intr; \ - } \ - -// ---------------------------------------------------------------------- -// No user-servicable parts after this... - -template -inline sp BnInterface::queryLocalInterface( - const String16& _descriptor) -{ - if (_descriptor == INTERFACE::descriptor) return this; - return NULL; -} - -template -inline String16 BnInterface::getInterfaceDescriptor() const -{ - return INTERFACE::getInterfaceDescriptor(); -} - -template -IBinder* BnInterface::onAsBinder() -{ - return this; -} - -template -inline BpInterface::BpInterface(const sp& remote) - : BpRefBase(remote) -{ -} - -template -inline IBinder* BpInterface::onAsBinder() -{ - return remote(); -} - -// ---------------------------------------------------------------------- - -}; // namespace android - -#endif // ANDROID_IINTERFACE_H diff --git a/include/utils/IMemory.h b/include/utils/IMemory.h deleted file mode 100644 index 35a3fd7da..000000000 --- a/include/utils/IMemory.h +++ /dev/null @@ -1,94 +0,0 @@ -/* - * 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. - */ - -#ifndef ANDROID_IMEMORY_H -#define ANDROID_IMEMORY_H - -#include -#include -#include - -#include -#include -#include - -namespace android { - -// ---------------------------------------------------------------------------- - -class IMemoryHeap : public IInterface -{ -public: - DECLARE_META_INTERFACE(MemoryHeap); - - // flags returned by getFlags() - enum { - READ_ONLY = 0x00000001, - MAP_ONCE = 0x00000002 - }; - - virtual int getHeapID() const = 0; - virtual void* getBase() const = 0; - virtual size_t getSize() const = 0; - virtual uint32_t getFlags() const = 0; - - // these are there just for backward source compatibility - int32_t heapID() const { return getHeapID(); } - void* base() const { return getBase(); } - size_t virtualSize() const { return getSize(); } -}; - -class BnMemoryHeap : public BnInterface -{ -public: - virtual status_t onTransact( - uint32_t code, - const Parcel& data, - Parcel* reply, - uint32_t flags = 0); -}; - -// ---------------------------------------------------------------------------- - -class IMemory : public IInterface -{ -public: - DECLARE_META_INTERFACE(Memory); - - virtual sp getMemory(ssize_t* offset=0, size_t* size=0) const = 0; - - // helpers - void* fastPointer(const sp& heap, ssize_t offset) const; - void* pointer() const; - size_t size() const; - ssize_t offset() const; -}; - -class BnMemory : public BnInterface -{ -public: - virtual status_t onTransact( - uint32_t code, - const Parcel& data, - Parcel* reply, - uint32_t flags = 0); -}; - -// ---------------------------------------------------------------------------- - -}; // namespace android - -#endif // ANDROID_IMEMORY_H diff --git a/include/utils/IPCThreadState.h b/include/utils/IPCThreadState.h deleted file mode 100644 index 0490fd3ec..000000000 --- a/include/utils/IPCThreadState.h +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (C) 2005 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_IPC_THREAD_STATE_H -#define ANDROID_IPC_THREAD_STATE_H - -#include -#include -#include -#include - -#ifdef HAVE_WIN32_PROC -typedef int uid_t; -#endif - -// --------------------------------------------------------------------------- -namespace android { - -class IPCThreadState -{ -public: - static IPCThreadState* self(); - - sp process(); - - status_t clearLastError(); - - int getCallingPid(); - int getCallingUid(); - - int64_t clearCallingIdentity(); - void restoreCallingIdentity(int64_t token); - - void flushCommands(); - - void joinThreadPool(bool isMain = true); - - // Stop the local process. - void stopProcess(bool immediate = true); - - status_t transact(int32_t handle, - uint32_t code, const Parcel& data, - Parcel* reply, uint32_t flags); - - void incStrongHandle(int32_t handle); - void decStrongHandle(int32_t handle); - void incWeakHandle(int32_t handle); - void decWeakHandle(int32_t handle); - status_t attemptIncStrongHandle(int32_t handle); - static void expungeHandle(int32_t handle, IBinder* binder); - status_t requestDeathNotification( int32_t handle, - BpBinder* proxy); - status_t clearDeathNotification( int32_t handle, - BpBinder* proxy); - - static void shutdown(); - -private: - IPCThreadState(); - ~IPCThreadState(); - - status_t sendReply(const Parcel& reply, uint32_t flags); - status_t waitForResponse(Parcel *reply, - status_t *acquireResult=NULL); - status_t talkWithDriver(bool doReceive=true); - status_t writeTransactionData(int32_t cmd, - uint32_t binderFlags, - int32_t handle, - uint32_t code, - const Parcel& data, - status_t* statusBuffer); - status_t executeCommand(int32_t command); - - void clearCaller(); - - static void threadDestructor(void *st); - static void freeBuffer(Parcel* parcel, - const uint8_t* data, size_t dataSize, - const size_t* objects, size_t objectsSize, - void* cookie); - - const sp mProcess; - Vector mPendingStrongDerefs; - Vector mPendingWeakDerefs; - - Parcel mIn; - Parcel mOut; - status_t mLastError; - pid_t mCallingPid; - uid_t mCallingUid; -}; - -}; // namespace android - -// --------------------------------------------------------------------------- - -#endif // ANDROID_IPC_THREAD_STATE_H diff --git a/include/utils/IPermissionController.h b/include/utils/IPermissionController.h deleted file mode 100644 index cb1dd345d..000000000 --- a/include/utils/IPermissionController.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2005 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_IPERMISSION_CONTROLLER_H -#define ANDROID_IPERMISSION_CONTROLLER_H - -#include - -namespace android { - -// ---------------------------------------------------------------------- - -class IPermissionController : public IInterface -{ -public: - DECLARE_META_INTERFACE(PermissionController); - - virtual bool checkPermission(const String16& permission, - int32_t pid, int32_t uid) = 0; - - enum { - CHECK_PERMISSION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION - }; -}; - -// ---------------------------------------------------------------------- - -class BnPermissionController : public BnInterface -{ -public: - virtual status_t onTransact( uint32_t code, - const Parcel& data, - Parcel* reply, - uint32_t flags = 0); -}; - -// ---------------------------------------------------------------------- - -}; // namespace android - -#endif // ANDROID_IPERMISSION_CONTROLLER_H - diff --git a/include/utils/IServiceManager.h b/include/utils/IServiceManager.h deleted file mode 100644 index e3d99fe7e..000000000 --- a/include/utils/IServiceManager.h +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (C) 2005 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_ISERVICE_MANAGER_H -#define ANDROID_ISERVICE_MANAGER_H - -#include -#include -#include -#include - -namespace android { - -// ---------------------------------------------------------------------- - -class IServiceManager : public IInterface -{ -public: - DECLARE_META_INTERFACE(ServiceManager); - - /** - * Retrieve an existing service, blocking for a few seconds - * if it doesn't yet exist. - */ - virtual sp getService( const String16& name) const = 0; - - /** - * Retrieve an existing service, non-blocking. - */ - virtual sp checkService( const String16& name) const = 0; - - /** - * Register a service. - */ - virtual status_t addService( const String16& name, - const sp& service) = 0; - - /** - * Return list of all existing services. - */ - virtual Vector listServices() = 0; - - enum { - GET_SERVICE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION, - CHECK_SERVICE_TRANSACTION, - ADD_SERVICE_TRANSACTION, - LIST_SERVICES_TRANSACTION, - }; -}; - -sp defaultServiceManager(); - -template -status_t getService(const String16& name, sp* outService) -{ - const sp sm = defaultServiceManager(); - if (sm != NULL) { - *outService = interface_cast(sm->getService(name)); - if ((*outService) != NULL) return NO_ERROR; - } - return NAME_NOT_FOUND; -} - -bool checkCallingPermission(const String16& permission); -bool checkCallingPermission(const String16& permission, - int32_t* outPid, int32_t* outUid); - -// ---------------------------------------------------------------------- - -class BnServiceManager : public BnInterface -{ -public: - virtual status_t onTransact( uint32_t code, - const Parcel& data, - Parcel* reply, - uint32_t flags = 0); -}; - -// ---------------------------------------------------------------------- - -}; // namespace android - -#endif // ANDROID_ISERVICE_MANAGER_H - diff --git a/include/utils/MemoryBase.h b/include/utils/MemoryBase.h deleted file mode 100644 index eb5a9d275..000000000 --- a/include/utils/MemoryBase.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_MEMORY_BASE_H -#define ANDROID_MEMORY_BASE_H - -#include -#include - -#include - - -namespace android { - -// --------------------------------------------------------------------------- - -class MemoryBase : public BnMemory -{ -public: - MemoryBase(const sp& heap, ssize_t offset, size_t size); - virtual ~MemoryBase(); - virtual sp getMemory(ssize_t* offset, size_t* size) const; - -protected: - size_t getSize() const { return mSize; } - ssize_t getOffset() const { return mOffset; } - const sp& getHeap() const { return mHeap; } - -private: - size_t mSize; - ssize_t mOffset; - sp mHeap; -}; - -// --------------------------------------------------------------------------- -}; // namespace android - -#endif // ANDROID_MEMORY_BASE_H diff --git a/include/utils/MemoryDealer.h b/include/utils/MemoryDealer.h deleted file mode 100644 index 79d7883af..000000000 --- a/include/utils/MemoryDealer.h +++ /dev/null @@ -1,247 +0,0 @@ -/* - * 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. - */ - -#ifndef ANDROID_MEMORY_DEALER_H -#define ANDROID_MEMORY_DEALER_H - - -#include -#include - -#include -#include -#include - -namespace android { -// ---------------------------------------------------------------------------- -class String8; - -/* - * interface for implementing a "heap". A heap basically provides - * the IMemoryHeap interface for cross-process sharing and the - * ability to map/unmap pages within the heap. - */ -class HeapInterface : public virtual BnMemoryHeap -{ -public: - // all values must be page-aligned - virtual sp mapMemory(size_t offset, size_t size) = 0; -}; - -// ---------------------------------------------------------------------------- - -/* - * interface for implementing an allocator. An allocator provides - * methods for allocating and freeing memory blocks and dumping - * its state. - */ -class AllocatorInterface : public RefBase -{ -public: - enum { - PAGE_ALIGNED = 0x00000001 - }; - - virtual size_t allocate(size_t size, uint32_t flags = 0) = 0; - virtual status_t deallocate(size_t offset) = 0; - virtual size_t size() const = 0; - virtual void dump(const char* what, uint32_t flags = 0) const = 0; - virtual void dump(String8& res, - const char* what, uint32_t flags = 0) const = 0; -}; - -// ---------------------------------------------------------------------------- - -/* - * concrete implementation of HeapInterface on top of mmap() - */ -class SharedHeap : public HeapInterface, public MemoryHeapBase -{ -public: - SharedHeap(size_t size, uint32_t flags = 0, char const * name = NULL); - virtual ~SharedHeap(); - virtual sp mapMemory(size_t offset, size_t size); -}; - -// ---------------------------------------------------------------------------- - -/* - * A simple templatized doubly linked-list implementation - */ - -template -class LinkedList -{ - NODE* mFirst; - NODE* mLast; - -public: - LinkedList() : mFirst(0), mLast(0) { } - bool isEmpty() const { return mFirst == 0; } - NODE const* head() const { return mFirst; } - NODE* head() { return mFirst; } - NODE const* tail() const { return mLast; } - NODE* tail() { return mLast; } - - void insertAfter(NODE* node, NODE* newNode) { - newNode->prev = node; - newNode->next = node->next; - if (node->next == 0) mLast = newNode; - else node->next->prev = newNode; - node->next = newNode; - } - - void insertBefore(NODE* node, NODE* newNode) { - newNode->prev = node->prev; - newNode->next = node; - if (node->prev == 0) mFirst = newNode; - else node->prev->next = newNode; - node->prev = newNode; - } - - void insertHead(NODE* newNode) { - if (mFirst == 0) { - mFirst = mLast = newNode; - newNode->prev = newNode->next = 0; - } else { - newNode->prev = 0; - newNode->next = mFirst; - mFirst->prev = newNode; - mFirst = newNode; - } - } - - void insertTail(NODE* newNode) { - if (mLast == 0) { - insertHead(newNode); - } else { - newNode->prev = mLast; - newNode->next = 0; - mLast->next = newNode; - mLast = newNode; - } - } - - NODE* remove(NODE* node) { - if (node->prev == 0) mFirst = node->next; - else node->prev->next = node->next; - if (node->next == 0) mLast = node->prev; - else node->next->prev = node->prev; - return node; - } -}; - - -/* - * concrete implementation of AllocatorInterface using a simple - * best-fit allocation scheme - */ -class SimpleBestFitAllocator : public AllocatorInterface -{ -public: - - SimpleBestFitAllocator(size_t size); - virtual ~SimpleBestFitAllocator(); - - virtual size_t allocate(size_t size, uint32_t flags = 0); - virtual status_t deallocate(size_t offset); - virtual size_t size() const; - virtual void dump(const char* what, uint32_t flags = 0) const; - virtual void dump(String8& res, - const char* what, uint32_t flags = 0) const; - -private: - - struct chunk_t { - chunk_t(size_t start, size_t size) - : start(start), size(size), free(1), prev(0), next(0) { - } - size_t start; - size_t size : 28; - int free : 4; - mutable chunk_t* prev; - mutable chunk_t* next; - }; - - ssize_t alloc(size_t size, uint32_t flags); - chunk_t* dealloc(size_t start); - void dump_l(const char* what, uint32_t flags = 0) const; - void dump_l(String8& res, const char* what, uint32_t flags = 0) const; - - static const int kMemoryAlign; - mutable Mutex mLock; - LinkedList mList; - size_t mHeapSize; -}; - -// ---------------------------------------------------------------------------- - -class MemoryDealer : public RefBase -{ -public: - - enum { - READ_ONLY = MemoryHeapBase::READ_ONLY, - PAGE_ALIGNED = AllocatorInterface::PAGE_ALIGNED - }; - - // creates a memory dealer with the SharedHeap and SimpleBestFitAllocator - MemoryDealer(size_t size, uint32_t flags = 0, const char* name = 0); - - // provide a custom heap but use the SimpleBestFitAllocator - MemoryDealer(const sp& heap); - - // provide both custom heap and allocotar - MemoryDealer( - const sp& heap, - const sp& allocator); - - virtual ~MemoryDealer(); - - virtual sp allocate(size_t size, uint32_t flags = 0); - virtual void deallocate(size_t offset); - virtual void dump(const char* what, uint32_t flags = 0) const; - - - sp getMemoryHeap() const { return heap(); } - sp getAllocator() const { return allocator(); } - -private: - const sp& heap() const; - const sp& allocator() const; - - class Allocation : public BnMemory { - public: - Allocation(const sp& dealer, - ssize_t offset, size_t size, const sp& memory); - virtual ~Allocation(); - virtual sp getMemory(ssize_t* offset, size_t* size) const; - private: - sp mDealer; - ssize_t mOffset; - size_t mSize; - sp mMemory; - }; - - sp mHeap; - sp mAllocator; -}; - - -// ---------------------------------------------------------------------------- -}; // namespace android - -#endif // ANDROID_MEMORY_DEALER_H diff --git a/include/utils/MemoryHeapBase.h b/include/utils/MemoryHeapBase.h deleted file mode 100644 index 574acf4f9..000000000 --- a/include/utils/MemoryHeapBase.h +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_MEMORY_HEAP_BASE_H -#define ANDROID_MEMORY_HEAP_BASE_H - -#include -#include - -#include - - -namespace android { - -// --------------------------------------------------------------------------- - -class MemoryHeapBase : public virtual BnMemoryHeap -{ -public: - enum { - READ_ONLY = IMemoryHeap::READ_ONLY, - MAP_ONCE = IMemoryHeap::MAP_ONCE, - // memory won't be mapped locally, but will be mapped in the remote - // process. - DONT_MAP_LOCALLY = 0x00000100 - }; - - /* - * maps the memory referenced by fd. but DOESN'T take ownership - * of the filedescriptor (it makes a copy with dup() - */ - MemoryHeapBase(int fd, size_t size, uint32_t flags = 0); - - /* - * maps memory from the given device - */ - MemoryHeapBase(const char* device, size_t size = 0, uint32_t flags = 0); - - /* - * maps memory from ashmem, with the given name for debugging - */ - MemoryHeapBase(size_t size, uint32_t flags = 0, char const* name = NULL); - - virtual ~MemoryHeapBase(); - - /* implement IMemoryHeap interface */ - virtual int getHeapID() const; - virtual void* getBase() const; - virtual size_t getSize() const; - virtual uint32_t getFlags() const; - - const char* getDevice() const; - - /* this closes this heap -- use carefully */ - void dispose(); - - /* this is only needed as a workaround, use only if you know - * what you are doing */ - status_t setDevice(const char* device) { - if (mDevice == 0) - mDevice = device; - return mDevice ? NO_ERROR : ALREADY_EXISTS; - } - -protected: - MemoryHeapBase(); - // init() takes ownership of fd - status_t init(int fd, void *base, int size, - int flags = 0, const char* device = NULL); - -private: - status_t mapfd(int fd, size_t size); - - int mFD; - size_t mSize; - void* mBase; - uint32_t mFlags; - const char* mDevice; - bool mNeedUnmap; -}; - -// --------------------------------------------------------------------------- -}; // namespace android - -#endif // ANDROID_MEMORY_HEAP_BASE_H diff --git a/include/utils/MemoryHeapPmem.h b/include/utils/MemoryHeapPmem.h deleted file mode 100644 index 60335adae..000000000 --- a/include/utils/MemoryHeapPmem.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_MEMORY_HEAP_PMEM_H -#define ANDROID_MEMORY_HEAP_PMEM_H - -#include -#include - -#include -#include -#include -#include - -namespace android { - -class MemoryHeapBase; - -// --------------------------------------------------------------------------- - -class MemoryHeapPmem : public HeapInterface, public MemoryHeapBase -{ -public: - class MemoryPmem : public BnMemory { - public: - MemoryPmem(const sp& heap); - ~MemoryPmem(); - protected: - const sp& getHeap() const { return mClientHeap; } - private: - friend class MemoryHeapPmem; - virtual void revoke() = 0; - sp mClientHeap; - }; - - MemoryHeapPmem(const sp& pmemHeap, - uint32_t flags = IMemoryHeap::MAP_ONCE); - ~MemoryHeapPmem(); - - /* HeapInterface additions */ - virtual sp mapMemory(size_t offset, size_t size); - - /* make the whole heap visible (you know who you are) */ - virtual status_t slap(); - - /* hide (revoke) the whole heap (the client will see the garbage page) */ - virtual status_t unslap(); - - /* revoke all allocations made by this heap */ - virtual void revoke(); - -private: - /* use this to create your own IMemory for mapMemory */ - virtual sp createMemory(size_t offset, size_t size); - void remove(const wp& memory); - -private: - sp mParentHeap; - mutable Mutex mLock; - SortedVector< wp > mAllocations; -}; - - -// --------------------------------------------------------------------------- -}; // namespace android - -#endif // ANDROID_MEMORY_HEAP_PMEM_H diff --git a/include/utils/Parcel.h b/include/utils/Parcel.h deleted file mode 100644 index af1490a02..000000000 --- a/include/utils/Parcel.h +++ /dev/null @@ -1,211 +0,0 @@ -/* - * Copyright (C) 2005 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_PARCEL_H -#define ANDROID_PARCEL_H - -#include -#include -#include -#include -#include - -// --------------------------------------------------------------------------- -namespace android { - -class IBinder; -class ProcessState; -class String8; -class TextOutput; - -struct flat_binder_object; // defined in support_p/binder_module.h - -class Parcel -{ -public: - Parcel(); - ~Parcel(); - - const uint8_t* data() const; - size_t dataSize() const; - size_t dataAvail() const; - size_t dataPosition() const; - size_t dataCapacity() const; - - status_t setDataSize(size_t size); - void setDataPosition(size_t pos) const; - status_t setDataCapacity(size_t size); - - status_t setData(const uint8_t* buffer, size_t len); - - status_t appendFrom(Parcel *parcel, size_t start, size_t len); - - bool hasFileDescriptors() const; - - status_t writeInterfaceToken(const String16& interface); - bool enforceInterface(const String16& interface) const; - - void freeData(); - - const size_t* objects() const; - size_t objectsCount() const; - - status_t errorCheck() const; - void setError(status_t err); - - status_t write(const void* data, size_t len); - void* writeInplace(size_t len); - status_t writeUnpadded(const void* data, size_t len); - status_t writeInt32(int32_t val); - status_t writeInt64(int64_t val); - status_t writeFloat(float val); - status_t writeDouble(double val); - status_t writeCString(const char* str); - status_t writeString8(const String8& str); - status_t writeString16(const String16& str); - status_t writeString16(const char16_t* str, size_t len); - status_t writeStrongBinder(const sp& val); - status_t writeWeakBinder(const wp& val); - - // Place a native_handle into the parcel (the native_handle's file- - // descriptors are dup'ed, so it is safe to delete the native_handle - // when this function returns). - // Doesn't take ownership of the native_handle. - status_t writeNativeHandle(const native_handle* handle); - - // Place a file descriptor into the parcel. The given fd must remain - // valid for the lifetime of the parcel. - status_t writeFileDescriptor(int fd); - - // Place a file descriptor into the parcel. A dup of the fd is made, which - // will be closed once the parcel is destroyed. - status_t writeDupFileDescriptor(int fd); - - status_t writeObject(const flat_binder_object& val, bool nullMetaData); - - void remove(size_t start, size_t amt); - - status_t read(void* outData, size_t len) const; - const void* readInplace(size_t len) const; - int32_t readInt32() const; - status_t readInt32(int32_t *pArg) const; - int64_t readInt64() const; - status_t readInt64(int64_t *pArg) const; - float readFloat() const; - status_t readFloat(float *pArg) const; - double readDouble() const; - status_t readDouble(double *pArg) const; - - const char* readCString() const; - String8 readString8() const; - String16 readString16() const; - const char16_t* readString16Inplace(size_t* outLen) const; - sp readStrongBinder() const; - wp readWeakBinder() const; - - - // Retrieve native_handle from the parcel. This returns a copy of the - // parcel's native_handle (the caller takes ownership). The caller - // must free the native_handle with native_handle_close() and - // native_handle_delete(). - native_handle* readNativeHandle() const; - - - // Retrieve a file descriptor from the parcel. This returns the raw fd - // in the parcel, which you do not own -- use dup() to get your own copy. - int readFileDescriptor() const; - - const flat_binder_object* readObject(bool nullMetaData) const; - - // Explicitly close all file descriptors in the parcel. - void closeFileDescriptors(); - - typedef void (*release_func)(Parcel* parcel, - const uint8_t* data, size_t dataSize, - const size_t* objects, size_t objectsSize, - void* cookie); - - const uint8_t* ipcData() const; - size_t ipcDataSize() const; - const size_t* ipcObjects() const; - size_t ipcObjectsCount() const; - void ipcSetDataReference(const uint8_t* data, size_t dataSize, - const size_t* objects, size_t objectsCount, - release_func relFunc, void* relCookie); - - void print(TextOutput& to, uint32_t flags = 0) const; - -private: - Parcel(const Parcel& o); - Parcel& operator=(const Parcel& o); - - status_t finishWrite(size_t len); - void releaseObjects(); - void acquireObjects(); - status_t growData(size_t len); - status_t restartWrite(size_t desired); - status_t continueWrite(size_t desired); - void freeDataNoInit(); - void initState(); - void scanForFds() const; - - status_t mError; - uint8_t* mData; - size_t mDataSize; - size_t mDataCapacity; - mutable size_t mDataPos; - size_t* mObjects; - size_t mObjectsSize; - size_t mObjectsCapacity; - mutable size_t mNextObjectHint; - - mutable bool mFdsKnown; - mutable bool mHasFds; - - release_func mOwner; - void* mOwnerCookie; -}; - -// --------------------------------------------------------------------------- - -inline TextOutput& operator<<(TextOutput& to, const Parcel& parcel) -{ - parcel.print(to); - return to; -} - -// --------------------------------------------------------------------------- - -// Generic acquire and release of objects. -void acquire_object(const sp& proc, - const flat_binder_object& obj, const void* who); -void release_object(const sp& proc, - const flat_binder_object& obj, const void* who); - -void flatten_binder(const sp& proc, - const sp& binder, flat_binder_object* out); -void flatten_binder(const sp& proc, - const wp& binder, flat_binder_object* out); -status_t unflatten_binder(const sp& proc, - const flat_binder_object& flat, sp* out); -status_t unflatten_binder(const sp& proc, - const flat_binder_object& flat, wp* out); - -}; // namespace android - -// --------------------------------------------------------------------------- - -#endif // ANDROID_PARCEL_H diff --git a/include/utils/ProcessState.h b/include/utils/ProcessState.h deleted file mode 100644 index 39584f42c..000000000 --- a/include/utils/ProcessState.h +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (C) 2005 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_PROCESS_STATE_H -#define ANDROID_PROCESS_STATE_H - -#include -#include -#include -#include - -#include - -// --------------------------------------------------------------------------- -namespace android { - -// Global variables -extern int mArgC; -extern const char* const* mArgV; -extern int mArgLen; - -class IPCThreadState; - -class ProcessState : public virtual RefBase -{ -public: - static sp self(); - - static void setSingleProcess(bool singleProcess); - - void setContextObject(const sp& object); - sp getContextObject(const sp& caller); - - void setContextObject(const sp& object, - const String16& name); - sp getContextObject(const String16& name, - const sp& caller); - - bool supportsProcesses() const; - - void startThreadPool(); - - typedef bool (*context_check_func)(const String16& name, - const sp& caller, - void* userData); - - bool isContextManager(void) const; - bool becomeContextManager( - context_check_func checkFunc, - void* userData); - - sp getStrongProxyForHandle(int32_t handle); - wp getWeakProxyForHandle(int32_t handle); - void expungeHandle(int32_t handle, IBinder* binder); - - void setArgs(int argc, const char* const argv[]); - int getArgC() const; - const char* const* getArgV() const; - - void setArgV0(const char* txt); - - void spawnPooledThread(bool isMain); - -private: - friend class IPCThreadState; - - ProcessState(); - ~ProcessState(); - - ProcessState(const ProcessState& o); - ProcessState& operator=(const ProcessState& o); - - struct handle_entry { - IBinder* binder; - RefBase::weakref_type* refs; - }; - - handle_entry* lookupHandleLocked(int32_t handle); - - int mDriverFD; - void* mVMStart; - - mutable Mutex mLock; // protects everything below. - - VectormHandleToObject; - - bool mManagesContexts; - context_check_func mBinderContextCheckFunc; - void* mBinderContextUserData; - - KeyedVector > - mContexts; - - - String8 mRootDir; - bool mThreadPoolStarted; - volatile int32_t mThreadPoolSeq; -}; - -}; // namespace android - -// --------------------------------------------------------------------------- - -#endif // ANDROID_PROCESS_STATE_H From a7470e498c8cb94f704cc9c5a2951b43748cba73 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Fri, 22 May 2009 12:25:56 -0700 Subject: [PATCH 105/541] This should fix the simulator build. --- libs/utils/futex_synchro.c | 1 + 1 file changed, 1 insertion(+) diff --git a/libs/utils/futex_synchro.c b/libs/utils/futex_synchro.c index ba1952033..ab48c6921 100644 --- a/libs/utils/futex_synchro.c +++ b/libs/utils/futex_synchro.c @@ -28,6 +28,7 @@ // This futex glue code is need on desktop linux, but is already part of bionic. #if !defined(HAVE_FUTEX_WRAPPERS) +#include #include typedef unsigned int u32; #define asmlinkage From d5e0fd44bcaeefe6f4ad516e130f95ef4cf68ce4 Mon Sep 17 00:00:00 2001 From: Nicolas Catania Date: Fri, 22 May 2009 13:41:38 -0700 Subject: [PATCH 106/541] Fix for the simultor build breakage. Added missing include sys/time.h for utimes. Detects when stat64 uses a timespec for the modif and access times and work around the missing st_*time_nsec. Apologies for the whitespace changes, emacs removed them automatically. --- libs/utils/backup_helper_file.cpp | 37 ++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/libs/utils/backup_helper_file.cpp b/libs/utils/backup_helper_file.cpp index 8efb3eb8b..7ec2ce865 100644 --- a/libs/utils/backup_helper_file.cpp +++ b/libs/utils/backup_helper_file.cpp @@ -26,6 +26,7 @@ #include #include #include +#include // for utimes #include #include #include @@ -607,14 +608,14 @@ backup_helper_test_four() 0x11, 0x00, 0x00, 0x00, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x6f, 0x66, 0x5f, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x33, 0xab, 0xab, 0xab, - + // bytes of padding2 0x44, 0x11, 0x22, 0x33, 0xef, 0xbe, 0xad, 0xde, 0x44, 0x33, 0x22, 0x11, 0x34, 0x23, 0x12, 0x01, 0x12, 0x00, 0x00, 0x00, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x6f, 0x66, 0x5f, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x32, 0xab, 0xab, - + // bytes of padding3 0x44, 0x11, 0x22, 0x33, 0xef, 0xbe, 0xad, 0xde, 0x44, 0x33, 0x22, 0x11, 0x34, 0x23, 0x12, 0x01, @@ -627,7 +628,7 @@ backup_helper_test_four() if (err != 0) { return err; } - + // read fd = open(filename, O_RDONLY); if (fd == -1) { @@ -665,7 +666,7 @@ backup_helper_test_four() matched = false; } } - + return matched ? 0 : 1; } @@ -746,7 +747,7 @@ backup_helper_test_data_writer() system("rm -r " SCRATCH_DIR); mkdir(SCRATCH_DIR, 0777); mkdir(SCRATCH_DIR "data", 0777); - + fd = creat(filename, 0666); if (fd == -1) { fprintf(stderr, "error creating: %s\n", strerror(errno)); @@ -863,7 +864,7 @@ backup_helper_test_data_reader() system("rm -r " SCRATCH_DIR); mkdir(SCRATCH_DIR, 0777); mkdir(SCRATCH_DIR "data", 0777); - + fd = creat(filename, 0666); if (fd == -1) { fprintf(stderr, "error creating: %s\n", strerror(errno)); @@ -940,9 +941,23 @@ get_mod_time(const char* filename, struct timeval times[2]) return errno; } times[0].tv_sec = st.st_atime; - times[0].tv_usec = st.st_atime_nsec / 1000; times[1].tv_sec = st.st_mtime; + + // If st_atime is a macro then struct stat64 uses struct timespec + // to store the access and modif time values and typically + // st_*time_nsec is not defined. In glibc, this is controlled by + // __USE_MISC. +#ifdef __USE_MISC +#if !defined(st_atime) || defined(st_atime_nsec) +#error "Check if this __USE_MISC conditional is still needed." +#endif + times[0].tv_usec = st.st_atim.tv_nsec / 1000; + times[1].tv_usec = st.st_mtim.tv_nsec / 1000; +#else + times[0].tv_usec = st.st_atime_nsec / 1000; times[1].tv_usec = st.st_mtime_nsec / 1000; +#endif + return 0; } @@ -987,7 +1002,7 @@ backup_helper_test_files() { BackupDataWriter dataStream(dataStreamFD); - + err = back_up_files(-1, &dataStream, newSnapshotFD, SCRATCH_DIR, files_before, 5); if (err != 0) { return err; @@ -1017,7 +1032,7 @@ backup_helper_test_files() utimes(SCRATCH_DIR "data/e", e_times); write_text_file(SCRATCH_DIR "data/g", "g\ngg\n"); unlink(SCRATCH_DIR "data/f"); - + char const* files_after[] = { "data/a", // added "data/b", // same @@ -1047,7 +1062,7 @@ backup_helper_test_files() { BackupDataWriter dataStream(dataStreamFD); - + err = back_up_files(oldSnapshotFD, &dataStream, newSnapshotFD, SCRATCH_DIR, files_after, 6); if (err != 0) { @@ -1058,7 +1073,7 @@ backup_helper_test_files() close(oldSnapshotFD); close(dataStreamFD); close(newSnapshotFD); - + return 0; } From d0ef3d46f90b030b4b6d1a667e99ba92d7cf84bd Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Fri, 22 May 2009 19:00:22 -0700 Subject: [PATCH 107/541] some work to try to reduce the code size of some native libraries - make sure that all binder Bn classes define a ctor and dtor in their respective library. This avoids duplication of the ctor/dtor in libraries where these objects are instantiated. This is also cleaner, should we want these ctor/dtor to do something one day. - same change as above for some Bp classes and various other non-binder classes - moved the definition of CHECK_INTERFACE() in IInterface.h instead of having it everywhere. - improved the CHECK_INTERFACE() macro so it calls a single method in Parcel, instead of inlining its code everywhere - IBinder::getInterfaceDescriptor() now returns a "const String16&" instead of String16, which saves calls to String16 and ~String16 - implemented a cache for BpBinder::getInterfaceDescriptor(), since this does an IPC. HOWEVER, this method never seems to be called. The cache makes BpBinder bigger, so we need to figure out if we need this method at all. --- include/utils/TextOutput.h | 4 ++-- include/utils/Timers.h | 10 +++++----- include/utils/threads.h | 10 +++++----- libs/utils/CallStack.cpp | 3 ++- libs/utils/TextOutput.cpp | 10 +++++++++- 5 files changed, 23 insertions(+), 14 deletions(-) diff --git a/include/utils/TextOutput.h b/include/utils/TextOutput.h index d8d86ba82..de2fbbee1 100644 --- a/include/utils/TextOutput.h +++ b/include/utils/TextOutput.h @@ -28,8 +28,8 @@ namespace android { class TextOutput { public: - TextOutput() { } - virtual ~TextOutput() { } + TextOutput(); + virtual ~TextOutput(); virtual status_t print(const char* txt, size_t len) = 0; virtual void moveIndent(int delta) = 0; diff --git a/include/utils/Timers.h b/include/utils/Timers.h index 96103995b..577325f5c 100644 --- a/include/utils/Timers.h +++ b/include/utils/Timers.h @@ -108,15 +108,15 @@ namespace android { */ class DurationTimer { public: - DurationTimer(void) {} - ~DurationTimer(void) {} + DurationTimer() {} + ~DurationTimer() {} // Start the timer. - void start(void); + void start(); // Stop the timer. - void stop(void); + void stop(); // Get the duration in microseconds. - long long durationUsecs(void) const; + long long durationUsecs() const; // Subtract two timevals. Returns the difference (ptv1-ptv2) in // microseconds. diff --git a/include/utils/threads.h b/include/utils/threads.h index b3209156b..e0cb66423 100644 --- a/include/utils/threads.h +++ b/include/utils/threads.h @@ -199,11 +199,11 @@ public: // constructed and released when Autolock goes out of scope. class Autolock { public: - inline Autolock(Mutex& mutex) : mpMutex(&mutex) { mutex.lock(); } - inline Autolock(Mutex* mutex) : mpMutex(mutex) { mutex->lock(); } - inline ~Autolock() { mpMutex->unlock(); } + inline Autolock(Mutex& mutex) : mLock(mutex) { mLock.lock(); } + inline Autolock(Mutex* mutex) : mLock(*mutex) { mLock.lock(); } + inline ~Autolock() { mLock.unlock(); } private: - Mutex* mpMutex; + Mutex& mLock; }; private: @@ -291,7 +291,7 @@ protected: bool exitPending() const; private: - // Derived class must implemtent threadLoop(). The thread starts its life + // Derived class must implement threadLoop(). The thread starts its life // here. There are two ways of using the Thread object: // 1) loop: if threadLoop() returns true, it will be called again if // requestExit() wasn't called. diff --git a/libs/utils/CallStack.cpp b/libs/utils/CallStack.cpp index 2fdaa7118..55b6024f6 100644 --- a/libs/utils/CallStack.cpp +++ b/libs/utils/CallStack.cpp @@ -311,7 +311,8 @@ String8 CallStack::toStringSingleLevel(const char* prefix, int32_t level) const } else { void const* start = 0; name = MapInfo::mapAddressToName(ip, "", &start); - snprintf(tmp, 256, "pc %08lx %s", uintptr_t(ip)-uintptr_t(start), name); + snprintf(tmp, 256, "pc %08lx %s", + long(uintptr_t(ip)-uintptr_t(start)), name); res.append(tmp); } res.append("\n"); diff --git a/libs/utils/TextOutput.cpp b/libs/utils/TextOutput.cpp index cebee99e5..e04823d2b 100644 --- a/libs/utils/TextOutput.cpp +++ b/libs/utils/TextOutput.cpp @@ -22,9 +22,17 @@ #include #include +namespace android { + // --------------------------------------------------------------------------- -namespace android { +TextOutput::TextOutput() { +} + +TextOutput::~TextOutput() { +} + +// --------------------------------------------------------------------------- TextOutput& operator<<(TextOutput& to, bool val) { From 69197b60200c571cc43b0e8726ed8f1e18c39882 Mon Sep 17 00:00:00 2001 From: Andy McFadden Date: Wed, 27 May 2009 16:01:39 -0700 Subject: [PATCH 108/541] Sim-only files move, part 2/2. Move Pipe and executablepath from libutils to the simulator, since nothing else uses them. --- libs/utils/Android.mk | 8 +- libs/utils/Pipe.cpp | 465 --------------------------- libs/utils/executablepath_darwin.cpp | 31 -- libs/utils/executablepath_linux.cpp | 30 -- 4 files changed, 1 insertion(+), 533 deletions(-) delete mode 100644 libs/utils/Pipe.cpp delete mode 100644 libs/utils/executablepath_darwin.cpp delete mode 100644 libs/utils/executablepath_linux.cpp diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk index 30b6733de..afad48e67 100644 --- a/libs/utils/Android.mk +++ b/libs/utils/Android.mk @@ -55,7 +55,6 @@ commonSources:= \ # hostSources:= \ InetAddress.cpp \ - Pipe.cpp \ Socket.cpp \ ZipEntry.cpp \ ZipFile.cpp @@ -71,12 +70,7 @@ ifeq ($(HOST_OS),linux) # Use the futex based mutex and condition variable # implementation from android-arm because it's shared mem safe LOCAL_SRC_FILES += \ - futex_synchro.c \ - executablepath_linux.cpp -endif -ifeq ($(HOST_OS),darwin) - LOCAL_SRC_FILES += \ - executablepath_darwin.cpp + futex_synchro.c endif LOCAL_MODULE:= libutils diff --git a/libs/utils/Pipe.cpp b/libs/utils/Pipe.cpp deleted file mode 100644 index 613906bed..000000000 --- a/libs/utils/Pipe.cpp +++ /dev/null @@ -1,465 +0,0 @@ -/* - * Copyright (C) 2005 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. - */ - -// -// Unidirectional pipe. -// - -#include -#include - -#if defined(HAVE_WIN32_IPC) -# include -#else -# include -# include -# include -#endif - -#include -#include -#include -#include - -using namespace android; - -const unsigned long kInvalidHandle = (unsigned long) -1; - - -/* - * Constructor. Do little. - */ -Pipe::Pipe(void) - : mReadNonBlocking(false), mReadHandle(kInvalidHandle), - mWriteHandle(kInvalidHandle) -{ -} - -/* - * Destructor. Use the system-appropriate close call. - */ -Pipe::~Pipe(void) -{ -#if defined(HAVE_WIN32_IPC) - if (mReadHandle != kInvalidHandle) { - if (!CloseHandle((HANDLE)mReadHandle)) - LOG(LOG_WARN, "pipe", "failed closing read handle (%ld)\n", - mReadHandle); - } - if (mWriteHandle != kInvalidHandle) { - FlushFileBuffers((HANDLE)mWriteHandle); - if (!CloseHandle((HANDLE)mWriteHandle)) - LOG(LOG_WARN, "pipe", "failed closing write handle (%ld)\n", - mWriteHandle); - } -#else - if (mReadHandle != kInvalidHandle) { - if (close((int) mReadHandle) != 0) - LOG(LOG_WARN, "pipe", "failed closing read fd (%d)\n", - (int) mReadHandle); - } - if (mWriteHandle != kInvalidHandle) { - if (close((int) mWriteHandle) != 0) - LOG(LOG_WARN, "pipe", "failed closing write fd (%d)\n", - (int) mWriteHandle); - } -#endif -} - -/* - * Create the pipe. - * - * Use the POSIX stuff for everything but Windows. - */ -bool Pipe::create(void) -{ - assert(mReadHandle == kInvalidHandle); - assert(mWriteHandle == kInvalidHandle); - -#if defined(HAVE_WIN32_IPC) - /* we use this across processes, so they need to be inheritable */ - HANDLE handles[2]; - SECURITY_ATTRIBUTES saAttr; - - saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); - saAttr.bInheritHandle = TRUE; - saAttr.lpSecurityDescriptor = NULL; - - if (!CreatePipe(&handles[0], &handles[1], &saAttr, 0)) { - LOG(LOG_ERROR, "pipe", "unable to create pipe\n"); - return false; - } - mReadHandle = (unsigned long) handles[0]; - mWriteHandle = (unsigned long) handles[1]; - return true; -#else - int fds[2]; - - if (pipe(fds) != 0) { - LOG(LOG_ERROR, "pipe", "unable to create pipe\n"); - return false; - } - mReadHandle = fds[0]; - mWriteHandle = fds[1]; - return true; -#endif -} - -/* - * Create a "half pipe". Please, no Segway riding. - */ -bool Pipe::createReader(unsigned long handle) -{ - mReadHandle = handle; - assert(mWriteHandle == kInvalidHandle); - return true; -} - -/* - * Create a "half pipe" for writing. - */ -bool Pipe::createWriter(unsigned long handle) -{ - mWriteHandle = handle; - assert(mReadHandle == kInvalidHandle); - return true; -} - -/* - * Return "true" if create() has been called successfully. - */ -bool Pipe::isCreated(void) -{ - // one or the other should be open - return (mReadHandle != kInvalidHandle || mWriteHandle != kInvalidHandle); -} - - -/* - * Read data from the pipe. - * - * For Linux and Darwin, just call read(). For Windows, implement - * non-blocking reads by calling PeekNamedPipe first. - */ -int Pipe::read(void* buf, int count) -{ - assert(mReadHandle != kInvalidHandle); - -#if defined(HAVE_WIN32_IPC) - DWORD totalBytesAvail = count; - DWORD bytesRead; - - if (mReadNonBlocking) { - // use PeekNamedPipe to adjust read count expectations - if (!PeekNamedPipe((HANDLE) mReadHandle, NULL, 0, NULL, - &totalBytesAvail, NULL)) - { - LOG(LOG_ERROR, "pipe", "PeekNamedPipe failed\n"); - return -1; - } - - if (totalBytesAvail == 0) - return 0; - } - - if (!ReadFile((HANDLE) mReadHandle, buf, totalBytesAvail, &bytesRead, - NULL)) - { - DWORD err = GetLastError(); - if (err == ERROR_HANDLE_EOF || err == ERROR_BROKEN_PIPE) - return 0; - LOG(LOG_ERROR, "pipe", "ReadFile failed (err=%ld)\n", err); - return -1; - } - - return (int) bytesRead; -#else - int cc; - cc = ::read(mReadHandle, buf, count); - if (cc < 0 && errno == EAGAIN) - return 0; - return cc; -#endif -} - -/* - * Write data to the pipe. - * - * POSIX systems are trivial, Windows uses a different call and doesn't - * handle non-blocking writes. - * - * If we add non-blocking support here, we probably want to make it an - * all-or-nothing write. - * - * DO NOT use LOG() here, we could be writing a log message. - */ -int Pipe::write(const void* buf, int count) -{ - assert(mWriteHandle != kInvalidHandle); - -#if defined(HAVE_WIN32_IPC) - DWORD bytesWritten; - - if (mWriteNonBlocking) { - // BUG: can't use PeekNamedPipe() to get the amount of space - // left. Looks like we need to use "overlapped I/O" functions. - // I just don't care that much. - } - - if (!WriteFile((HANDLE) mWriteHandle, buf, count, &bytesWritten, NULL)) { - // can't LOG, use stderr - fprintf(stderr, "WriteFile failed (err=%ld)\n", GetLastError()); - return -1; - } - - return (int) bytesWritten; -#else - int cc; - cc = ::write(mWriteHandle, buf, count); - if (cc < 0 && errno == EAGAIN) - return 0; - return cc; -#endif -} - -/* - * Figure out if there is data available on the read fd. - * - * We return "true" on error because we want the caller to try to read - * from the pipe. They'll notice the read failure and do something - * appropriate. - */ -bool Pipe::readReady(void) -{ - assert(mReadHandle != kInvalidHandle); - -#if defined(HAVE_WIN32_IPC) - DWORD totalBytesAvail; - - if (!PeekNamedPipe((HANDLE) mReadHandle, NULL, 0, NULL, - &totalBytesAvail, NULL)) - { - LOG(LOG_ERROR, "pipe", "PeekNamedPipe failed\n"); - return true; - } - - return (totalBytesAvail != 0); -#else - errno = 0; - fd_set readfds; - struct timeval tv = { 0, 0 }; - int cc; - - FD_ZERO(&readfds); - FD_SET(mReadHandle, &readfds); - - cc = select(mReadHandle+1, &readfds, NULL, NULL, &tv); - if (cc < 0) { - LOG(LOG_ERROR, "pipe", "select() failed\n"); - return true; - } else if (cc == 0) { - /* timed out, nothing available */ - return false; - } else if (cc == 1) { - /* our fd is ready */ - return true; - } else { - LOG(LOG_ERROR, "pipe", "HUH? select() returned > 1\n"); - return true; - } -#endif -} - -/* - * Enable or disable non-blocking mode for the read descriptor. - * - * NOTE: the calls succeed under Mac OS X, but the pipe doesn't appear to - * actually be in non-blocking mode. If this matters -- i.e. you're not - * using a select() call -- put a call to readReady() in front of the - * ::read() call, with a PIPE_NONBLOCK_BROKEN #ifdef in the Makefile for - * Darwin. - */ -bool Pipe::setReadNonBlocking(bool val) -{ - assert(mReadHandle != kInvalidHandle); - -#if defined(HAVE_WIN32_IPC) - // nothing to do -#else - int flags; - - if (fcntl(mReadHandle, F_GETFL, &flags) == -1) { - LOG(LOG_ERROR, "pipe", "couldn't get flags for pipe read fd\n"); - return false; - } - if (val) - flags |= O_NONBLOCK; - else - flags &= ~(O_NONBLOCK); - if (fcntl(mReadHandle, F_SETFL, &flags) == -1) { - LOG(LOG_ERROR, "pipe", "couldn't set flags for pipe read fd\n"); - return false; - } -#endif - - mReadNonBlocking = val; - return true; -} - -/* - * Enable or disable non-blocking mode for the write descriptor. - * - * As with setReadNonBlocking(), this does not work on the Mac. - */ -bool Pipe::setWriteNonBlocking(bool val) -{ - assert(mWriteHandle != kInvalidHandle); - -#if defined(HAVE_WIN32_IPC) - // nothing to do -#else - int flags; - - if (fcntl(mWriteHandle, F_GETFL, &flags) == -1) { - LOG(LOG_WARN, "pipe", - "Warning: couldn't get flags for pipe write fd (errno=%d)\n", - errno); - return false; - } - if (val) - flags |= O_NONBLOCK; - else - flags &= ~(O_NONBLOCK); - if (fcntl(mWriteHandle, F_SETFL, &flags) == -1) { - LOG(LOG_WARN, "pipe", - "Warning: couldn't set flags for pipe write fd (errno=%d)\n", - errno); - return false; - } -#endif - - mWriteNonBlocking = val; - return true; -} - -/* - * Specify whether a file descriptor can be inherited by a child process. - * Under Linux this means setting the close-on-exec flag, under Windows - * this is SetHandleInformation(HANDLE_FLAG_INHERIT). - */ -bool Pipe::disallowReadInherit(void) -{ - if (mReadHandle == kInvalidHandle) - return false; - -#if defined(HAVE_WIN32_IPC) - if (SetHandleInformation((HANDLE) mReadHandle, HANDLE_FLAG_INHERIT, 0) == 0) - return false; -#else - if (fcntl((int) mReadHandle, F_SETFD, FD_CLOEXEC) != 0) - return false; -#endif - return true; -} -bool Pipe::disallowWriteInherit(void) -{ - if (mWriteHandle == kInvalidHandle) - return false; - -#if defined(HAVE_WIN32_IPC) - if (SetHandleInformation((HANDLE) mWriteHandle, HANDLE_FLAG_INHERIT, 0) == 0) - return false; -#else - if (fcntl((int) mWriteHandle, F_SETFD, FD_CLOEXEC) != 0) - return false; -#endif - return true; -} - -/* - * Close read descriptor. - */ -bool Pipe::closeRead(void) -{ - if (mReadHandle == kInvalidHandle) - return false; - -#if defined(HAVE_WIN32_IPC) - if (mReadHandle != kInvalidHandle) { - if (!CloseHandle((HANDLE)mReadHandle)) { - LOG(LOG_WARN, "pipe", "failed closing read handle\n"); - return false; - } - } -#else - if (mReadHandle != kInvalidHandle) { - if (close((int) mReadHandle) != 0) { - LOG(LOG_WARN, "pipe", "failed closing read fd\n"); - return false; - } - } -#endif - mReadHandle = kInvalidHandle; - return true; -} - -/* - * Close write descriptor. - */ -bool Pipe::closeWrite(void) -{ - if (mWriteHandle == kInvalidHandle) - return false; - -#if defined(HAVE_WIN32_IPC) - if (mWriteHandle != kInvalidHandle) { - if (!CloseHandle((HANDLE)mWriteHandle)) { - LOG(LOG_WARN, "pipe", "failed closing write handle\n"); - return false; - } - } -#else - if (mWriteHandle != kInvalidHandle) { - if (close((int) mWriteHandle) != 0) { - LOG(LOG_WARN, "pipe", "failed closing write fd\n"); - return false; - } - } -#endif - mWriteHandle = kInvalidHandle; - return true; -} - -/* - * Get the read handle. - */ -unsigned long Pipe::getReadHandle(void) -{ - assert(mReadHandle != kInvalidHandle); - - return mReadHandle; -} - -/* - * Get the write handle. - */ -unsigned long Pipe::getWriteHandle(void) -{ - assert(mWriteHandle != kInvalidHandle); - - return mWriteHandle; -} - diff --git a/libs/utils/executablepath_darwin.cpp b/libs/utils/executablepath_darwin.cpp deleted file mode 100644 index 2e3c3a01f..000000000 --- a/libs/utils/executablepath_darwin.cpp +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#import -#include - -void executablepath(char s[PATH_MAX]) -{ - ProcessSerialNumber psn; - GetCurrentProcess(&psn); - CFDictionaryRef dict; - dict = ProcessInformationCopyDictionary(&psn, 0xffffffff); - CFStringRef value = (CFStringRef)CFDictionaryGetValue(dict, - CFSTR("CFBundleExecutable")); - CFStringGetCString(value, s, PATH_MAX+1, kCFStringEncodingUTF8); -} - diff --git a/libs/utils/executablepath_linux.cpp b/libs/utils/executablepath_linux.cpp deleted file mode 100644 index b8d2a3d6f..000000000 --- a/libs/utils/executablepath_linux.cpp +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include -#include -#include - -void executablepath(char exe[PATH_MAX]) -{ - char proc[100]; - sprintf(proc, "/proc/%d/exe", getpid()); - - int err = readlink(proc, exe, PATH_MAX); -} - From 624756e7ede9e9f64f7f2a0b85c29b213ef3d26f Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Thu, 28 May 2009 12:31:31 -0700 Subject: [PATCH 109/541] force explicit instantiation of Singleton<> objects --- include/utils/Singleton.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/utils/Singleton.h b/include/utils/Singleton.h index 2f7c7c2db..bc7626a82 100644 --- a/include/utils/Singleton.h +++ b/include/utils/Singleton.h @@ -57,6 +57,7 @@ private: */ #define ANDROID_SINGLETON_STATIC_INSTANCE(TYPE) \ + template class Singleton< TYPE >; \ template< class TYPE > Mutex Singleton< TYPE >::sLock; \ template<> TYPE* Singleton< TYPE >::sInstance(0); From cbae9a27ce48019c9a930780068b7e3aadbd725f Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Sun, 31 May 2009 18:49:44 -0700 Subject: [PATCH 110/541] get rid of utils/executablepath.h, which now lives in the simulator --- include/utils/executablepath.h | 28 ---------------------------- 1 file changed, 28 deletions(-) delete mode 100644 include/utils/executablepath.h diff --git a/include/utils/executablepath.h b/include/utils/executablepath.h deleted file mode 100644 index c979432ba..000000000 --- a/include/utils/executablepath.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef _UTILS_EXECUTABLEPATH_H -#define _UTILS_EXECUTABLEPATH_H - -#include - -// returns the path to this executable -#if __cplusplus -extern "C" -#endif -void executablepath(char s[PATH_MAX]); - -#endif // _UTILS_EXECUTABLEPATH_H From ec8fb949cad6576d3de6d62f02e52d6994d97607 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Sun, 31 May 2009 19:13:00 -0700 Subject: [PATCH 111/541] get rid of utils.h --- include/utils.h | 33 --------------------------------- include/utils/misc.h | 2 +- 2 files changed, 1 insertion(+), 34 deletions(-) delete mode 100644 include/utils.h diff --git a/include/utils.h b/include/utils.h deleted file mode 100644 index 30648b185..000000000 --- a/include/utils.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2005 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. - */ - -// -// Handy utility functions and portability code. This file includes all -// of the generally-useful headers in the "utils" directory. -// -#ifndef _LIBS_UTILS_H -#define _LIBS_UTILS_H - -#include -#include -#include -#include -#include -#include -#include -#include - -#endif // _LIBS_UTILS_H diff --git a/include/utils/misc.h b/include/utils/misc.h index 62e84b489..23f2a4c7c 100644 --- a/include/utils/misc.h +++ b/include/utils/misc.h @@ -21,7 +21,7 @@ #define _LIBS_UTILS_MISC_H #include -#include "utils/Endian.h" +#include namespace android { From d12529a1492f2fb5609a8934dea68e5533756d39 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Sun, 31 May 2009 23:29:06 -0700 Subject: [PATCH 112/541] get rid of sleepForInterval() which didn't seem to be used anywhere in the source tree. Also get rid of ported.h which seem to be used only (possibly) in the sim. moved the implementation there. --- include/utils/Timers.h | 3 - include/utils/ported.h | 50 ----------------- libs/utils/Android.mk | 1 - libs/utils/Timers.cpp | 125 ----------------------------------------- libs/utils/ported.cpp | 106 ---------------------------------- 5 files changed, 285 deletions(-) delete mode 100644 include/utils/ported.h delete mode 100644 libs/utils/ported.cpp diff --git a/include/utils/Timers.h b/include/utils/Timers.h index 577325f5c..9a9e07c60 100644 --- a/include/utils/Timers.h +++ b/include/utils/Timers.h @@ -88,9 +88,6 @@ nsecs_t systemTime(int clock = SYSTEM_TIME_MONOTONIC); nsecs_t systemTime(int clock); #endif // def __cplusplus -// return the system-time according to the specified clock -int sleepForInterval(long interval, struct timeval* pNextTick); - #ifdef __cplusplus } // extern "C" #endif diff --git a/include/utils/ported.h b/include/utils/ported.h deleted file mode 100644 index eb3be01e9..000000000 --- a/include/utils/ported.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2005 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. - */ - -// -// Standard functions ported to the current platform. Note these are NOT -// in the "android" namespace. -// -#ifndef _LIBS_UTILS_PORTED_H -#define _LIBS_UTILS_PORTED_H - -#include // for timeval - -#ifdef __cplusplus -extern "C" { -#endif - -/* library replacement functions */ -#if defined(NEED_GETTIMEOFDAY) -int gettimeofday(struct timeval* tv, struct timezone* tz); -#endif -#if defined(NEED_USLEEP) -void usleep(unsigned long usec); -#endif -#if defined(NEED_PIPE) -int pipe(int filedes[2]); -#endif -#if defined(NEED_SETENV) -int setenv(const char* name, const char* value, int overwrite); -void unsetenv(const char* name); -char* getenv(const char* name); -#endif - -#ifdef __cplusplus -} -#endif - -#endif // _LIBS_UTILS_PORTED_H diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk index afad48e67..00395f69c 100644 --- a/libs/utils/Android.mk +++ b/libs/utils/Android.mk @@ -42,7 +42,6 @@ commonSources:= \ ZipFileRO.cpp \ ZipUtils.cpp \ misc.cpp \ - ported.cpp \ LogSocket.cpp # diff --git a/libs/utils/Timers.cpp b/libs/utils/Timers.cpp index 2abc811a0..784f035dc 100644 --- a/libs/utils/Timers.cpp +++ b/libs/utils/Timers.cpp @@ -18,7 +18,6 @@ // Timer functions. // #include -#include // may need usleep #include #include @@ -54,130 +53,6 @@ nsecs_t systemTime(int clock) #endif } -//#define MONITOR_USLEEP - -/* - * Sleep long enough that we'll wake up "interval" milliseconds after - * the previous snooze. - * - * The "nextTick" argument is updated on each call, and should be passed - * in every time. Set its fields to zero on the first call. - * - * Returns the #of intervals we have overslept, which will be zero if we're - * on time. [Currently just returns 0 or 1.] - */ -int sleepForInterval(long interval, struct timeval* pNextTick) -{ - struct timeval now; - long long timeBeforeNext; - long sleepTime = 0; - bool overSlept = false; - //int usleepBias = 0; - -#ifdef USLEEP_BIAS - /* - * Linux likes to add 9000ms or so. - * [not using this for now] - */ - //usleepBias = USLEEP_BIAS; -#endif - - gettimeofday(&now, NULL); - - if (pNextTick->tv_sec == 0) { - /* special-case for first time through */ - *pNextTick = now; - sleepTime = interval; - android::DurationTimer::addToTimeval(pNextTick, interval); - } else { - /* - * Compute how much time there is before the next tick. If this - * value is negative, we've run over. If we've run over a little - * bit we can shorten the next frame to keep the pace steady, but - * if we've dramatically overshot we need to re-sync. - */ - timeBeforeNext = android::DurationTimer::subtractTimevals(pNextTick, &now); - //printf("TOP: now=%ld.%ld next=%ld.%ld diff=%ld\n", - // now.tv_sec, now.tv_usec, pNextTick->tv_sec, pNextTick->tv_usec, - // (long) timeBeforeNext); - if (timeBeforeNext < -interval) { - /* way over */ - overSlept = true; - sleepTime = 0; - *pNextTick = now; - } else if (timeBeforeNext <= 0) { - /* slightly over, keep the pace steady */ - overSlept = true; - sleepTime = 0; - } else if (timeBeforeNext <= interval) { - /* right on schedule */ - sleepTime = timeBeforeNext; - } else if (timeBeforeNext > interval && timeBeforeNext <= 2*interval) { - /* sleep call returned early; do a longer sleep this time */ - sleepTime = timeBeforeNext; - } else if (timeBeforeNext > interval) { - /* we went back in time -- somebody updated system clock? */ - /* (could also be a *seriously* broken usleep()) */ - LOG(LOG_DEBUG, "", - " Impossible: timeBeforeNext = %ld\n", (long)timeBeforeNext); - sleepTime = 0; - *pNextTick = now; - } - android::DurationTimer::addToTimeval(pNextTick, interval); - } - //printf(" Before sleep: now=%ld.%ld next=%ld.%ld sleepTime=%ld\n", - // now.tv_sec, now.tv_usec, pNextTick->tv_sec, pNextTick->tv_usec, - // sleepTime); - - /* - * Sleep for the designated period of time. - * - * Linux tends to sleep for longer than requested, often by 17-18ms. - * MinGW tends to sleep for less than requested, by as much as 14ms, - * but occasionally oversleeps for 40+ms (looks like some external - * factors plus round-off on a 64Hz clock). Cygwin is pretty steady. - * - * If you start the MinGW version, and then launch the Cygwin version, - * the MinGW clock becomes more erratic. Not entirely sure why. - * - * (There's a lot of stuff here; it's really just a usleep() call with - * a bunch of instrumentation.) - */ - if (sleepTime > 0) { -#if defined(MONITOR_USLEEP) - struct timeval before, after; - long long actual; - - gettimeofday(&before, NULL); - usleep((long) sleepTime); - gettimeofday(&after, NULL); - - /* check usleep() accuracy; default Linux threads are pretty sloppy */ - actual = android::DurationTimer::subtractTimevals(&after, &before); - if ((long) actual < sleepTime - 14000 /*(sleepTime/10)*/ || - (long) actual > sleepTime + 20000 /*(sleepTime/10)*/) - { - LOG(LOG_DEBUG, "", " Odd usleep: req=%ld, actual=%ld\n", sleepTime, - (long) actual); - } -#else -#ifdef HAVE_WIN32_THREADS - Sleep( sleepTime/1000 ); -#else - usleep((long) sleepTime); -#endif -#endif - } - - //printf("slept %d\n", sleepTime); - - if (overSlept) - return 1; // close enough - else - return 0; -} - - /* * =========================================================================== * DurationTimer diff --git a/libs/utils/ported.cpp b/libs/utils/ported.cpp deleted file mode 100644 index 656e46f09..000000000 --- a/libs/utils/ported.cpp +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (C) 2005 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. - */ - -// -// Ports of standard functions that don't exist on a specific platform. -// -// Note these are NOT in the "android" namespace. -// -#include - -#if defined(NEED_GETTIMEOFDAY) || defined(NEED_USLEEP) -# include -# include -#endif - - -#if defined(NEED_GETTIMEOFDAY) -/* - * Replacement gettimeofday() for Windows environments (primarily MinGW). - * - * Ignores "tz". - */ -int gettimeofday(struct timeval* ptv, struct timezone* tz) -{ - long long nsTime; // time in 100ns units since Jan 1 1601 - FILETIME ft; - - if (tz != NULL) { - // oh well - } - - ::GetSystemTimeAsFileTime(&ft); - nsTime = (long long) ft.dwHighDateTime << 32 | - (long long) ft.dwLowDateTime; - // convert to time in usec since Jan 1 1970 - ptv->tv_usec = (long) ((nsTime / 10LL) % 1000000LL); - ptv->tv_sec = (long) ((nsTime - 116444736000000000LL) / 10000000LL); - - return 0; -} -#endif - -#if defined(NEED_USLEEP) -// -// Replacement usleep for Windows environments (primarily MinGW). -// -void usleep(unsigned long usec) -{ - // Win32 API function Sleep() takes milliseconds - ::Sleep((usec + 500) / 1000); -} -#endif - -#if 0 //defined(NEED_PIPE) -// -// Replacement pipe() command for MinGW -// -// The _O_NOINHERIT flag sets bInheritHandle to FALSE in the -// SecurityAttributes argument to CreatePipe(). This means the handles -// aren't inherited when a new process is created. The examples I've seen -// use it, possibly because there's a lot of junk going on behind the -// scenes. (I'm assuming "process" and "thread" are different here, so -// we should be okay spinning up a thread.) The recommended practice is -// to dup() the descriptor you want the child to have. -// -// It appears that unnamed pipes can't do non-blocking ("overlapped") I/O. -// You can't use select() either, since that only works on sockets. The -// Windows API calls that are useful here all operate on a HANDLE, not -// an integer file descriptor, and I don't think you can get there from -// here. The "named pipe" stuff is insane. -// -int pipe(int filedes[2]) -{ - return _pipe(filedes, 0, _O_BINARY | _O_NOINHERIT); -} -#endif - -#if defined(NEED_SETENV) -/* - * MinGW lacks these. For now, just stub them out so the code compiles. - */ -int setenv(const char* name, const char* value, int overwrite) -{ - return 0; -} -void unsetenv(const char* name) -{ -} -char* getenv(const char* name) -{ - return NULL; -} -#endif From 861db31eb9dd3e47855fdd9d5f7e57a4e1d8799b Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Mon, 1 Jun 2009 13:55:28 -0700 Subject: [PATCH 113/541] get rid of TimerProbe which is never used --- include/utils/TimerProbe.h | 72 -------------------- libs/utils/Android.mk | 1 - libs/utils/TimerProbe.cpp | 131 ------------------------------------- 3 files changed, 204 deletions(-) delete mode 100644 include/utils/TimerProbe.h delete mode 100644 libs/utils/TimerProbe.cpp diff --git a/include/utils/TimerProbe.h b/include/utils/TimerProbe.h deleted file mode 100644 index f2e32b21a..000000000 --- a/include/utils/TimerProbe.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * 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. - */ - -#ifndef ANDROID_TIMER_PROBE_H -#define ANDROID_TIMER_PROBE_H - -#if 0 && defined(HAVE_POSIX_CLOCKS) -#define ENABLE_TIMER_PROBE 1 -#else -#define ENABLE_TIMER_PROBE 0 -#endif - -#if ENABLE_TIMER_PROBE - -#include -#include -#include - -#define TIMER_PROBE(tag) \ - static int _timer_slot_; \ - android::TimerProbe probe(tag, &_timer_slot_) -#define TIMER_PROBE_END() probe.end() -#else -#define TIMER_PROBE(tag) -#define TIMER_PROBE_END() -#endif - -#if ENABLE_TIMER_PROBE -namespace android { - -class TimerProbe { -public: - TimerProbe(const char tag[], int* slot); - void end(); - ~TimerProbe(); -private: - struct Bucket { - int mStart, mReal, mProcess, mThread, mCount; - const char* mTag; - int* mSlotPtr; - int mIndent; - }; - static Vector gBuckets; - static TimerProbe* gExecuteChain; - static int gIndent; - static timespec gRealBase; - TimerProbe* mNext; - static uint32_t ElapsedTime(const timespec& start, const timespec& end); - void print(const timespec& r, const timespec& p, const timespec& t) const; - timespec mRealStart, mPStart, mTStart; - const char* mTag; - int mIndent; - int mBucket; -}; - -}; // namespace android - -#endif -#endif diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk index 00395f69c..1acc1a43a 100644 --- a/libs/utils/Android.mk +++ b/libs/utils/Android.mk @@ -35,7 +35,6 @@ commonSources:= \ SystemClock.cpp \ TextOutput.cpp \ Threads.cpp \ - TimerProbe.cpp \ Timers.cpp \ VectorImpl.cpp \ ZipFileCRO.cpp \ diff --git a/libs/utils/TimerProbe.cpp b/libs/utils/TimerProbe.cpp deleted file mode 100644 index 835480d36..000000000 --- a/libs/utils/TimerProbe.cpp +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#if ENABLE_TIMER_PROBE - -#ifdef LOG_TAG -#undef LOG_TAG -#endif -#define LOG_TAG "time" - -namespace android { - -Vector TimerProbe::gBuckets; -TimerProbe* TimerProbe::gExecuteChain; -int TimerProbe::gIndent; -timespec TimerProbe::gRealBase; - -TimerProbe::TimerProbe(const char tag[], int* slot) : mTag(tag) -{ - mNext = gExecuteChain; - gExecuteChain = this; - mIndent = gIndent; - gIndent += 1; - if (mIndent > 0) { - if (*slot == 0) { - int count = gBuckets.add(); - *slot = count; - Bucket& bucket = gBuckets.editItemAt(count); - memset(&bucket, 0, sizeof(Bucket)); - bucket.mTag = tag; - bucket.mSlotPtr = slot; - bucket.mIndent = mIndent; - } - mBucket = *slot; - } - clock_gettime(CLOCK_REALTIME, &mRealStart); - if (gRealBase.tv_sec == 0) - gRealBase = mRealStart; - clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &mPStart); - clock_gettime(CLOCK_THREAD_CPUTIME_ID, &mTStart); -} - -void TimerProbe::end() -{ - timespec realEnd, pEnd, tEnd; - clock_gettime(CLOCK_REALTIME, &realEnd); - clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &pEnd); - clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tEnd); - print(realEnd, pEnd, tEnd); - mTag = NULL; -} - -TimerProbe::~TimerProbe() -{ - if (mTag != NULL) - end(); - gExecuteChain = mNext; - gIndent--; -} - - -uint32_t TimerProbe::ElapsedTime(const timespec& start, const timespec& end) -{ - int sec = end.tv_sec - start.tv_sec; - int nsec = end.tv_nsec - start.tv_nsec; - if (nsec < 0) { - sec--; - nsec += 1000000000; - } - return sec * 1000000 + nsec / 1000; -} - -void TimerProbe::print(const timespec& r, const timespec& p, - const timespec& t) const -{ - uint32_t es = ElapsedTime(gRealBase, mRealStart); - uint32_t er = ElapsedTime(mRealStart, r); - uint32_t ep = ElapsedTime(mPStart, p); - uint32_t et = ElapsedTime(mTStart, t); - if (mIndent > 0) { - Bucket& bucket = gBuckets.editItemAt(mBucket); - if (bucket.mStart == 0) - bucket.mStart = es; - bucket.mReal += er; - bucket.mProcess += ep; - bucket.mThread += et; - bucket.mCount++; - return; - } - int index = 0; - int buckets = gBuckets.size(); - int count = 1; - const char* tag = mTag; - int indent = mIndent; - do { - LOGD("%-30.30s: (%3d) %-5.*s time=%-10.3f real=%7dus process=%7dus (%3d%%) thread=%7dus (%3d%%)\n", - tag, count, indent > 5 ? 5 : indent, "+++++", es / 1000000.0, - er, ep, ep * 100 / er, et, et * 100 / er); - if (index >= buckets) - break; - Bucket& bucket = gBuckets.editItemAt(index); - count = bucket.mCount; - es = bucket.mStart; - er = bucket.mReal; - ep = bucket.mProcess; - et = bucket.mThread; - tag = bucket.mTag; - indent = bucket.mIndent; - *bucket.mSlotPtr = 0; - } while (++index); // always true - gBuckets.clear(); -} - -}; // namespace android - -#endif From 9f2dc70fd18bcf30a4ae3143c5bd502dc07df4cd Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Mon, 1 Jun 2009 13:59:42 -0700 Subject: [PATCH 114/541] move utils/Pipe.h to the simulator --- include/utils/Pipe.h | 108 ------------------------------------------- 1 file changed, 108 deletions(-) delete mode 100644 include/utils/Pipe.h diff --git a/include/utils/Pipe.h b/include/utils/Pipe.h deleted file mode 100644 index 6404168a2..000000000 --- a/include/utils/Pipe.h +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright (C) 2005 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. - */ - -// -// FIFO I/O. -// -#ifndef _LIBS_UTILS_PIPE_H -#define _LIBS_UTILS_PIPE_H - -#ifdef HAVE_ANDROID_OS -#error DO NOT USE THIS FILE IN THE DEVICE BUILD -#endif - -namespace android { - -/* - * Simple anonymous unidirectional pipe. - * - * The primary goal is to create an implementation with minimal overhead - * under Linux. Making Windows, Mac OS X, and Linux all work the same way - * is a secondary goal. Part of this goal is to have something that can - * be fed to a select() call, so that the application can sleep in the - * kernel until something interesting happens. - */ -class Pipe { -public: - Pipe(void); - virtual ~Pipe(void); - - /* Create the pipe */ - bool create(void); - - /* Create a read-only pipe, using the supplied handle as read handle */ - bool createReader(unsigned long handle); - /* Create a write-only pipe, using the supplied handle as write handle */ - bool createWriter(unsigned long handle); - - /* Is this object ready to go? */ - bool isCreated(void); - - /* - * Read "count" bytes from the pipe. Returns the amount of data read, - * or 0 if no data available and we're non-blocking. - * Returns -1 on error. - */ - int read(void* buf, int count); - - /* - * Write "count" bytes into the pipe. Returns number of bytes written, - * or 0 if there's no room for more data and we're non-blocking. - * Returns -1 on error. - */ - int write(const void* buf, int count); - - /* Returns "true" if data is available to read */ - bool readReady(void); - - /* Enable or disable non-blocking I/O for reads */ - bool setReadNonBlocking(bool val); - /* Enable or disable non-blocking I/O for writes. Only works on Linux. */ - bool setWriteNonBlocking(bool val); - - /* - * Get the handle. Only useful in some platform-specific situations. - */ - unsigned long getReadHandle(void); - unsigned long getWriteHandle(void); - - /* - * Modify inheritance, i.e. whether or not a child process will get - * copies of the descriptors. Systems with fork+exec allow us to close - * the descriptors before launching the child process, but Win32 - * doesn't allow it. - */ - bool disallowReadInherit(void); - bool disallowWriteInherit(void); - - /* - * Close one side or the other. Useful in the parent after launching - * a child process. - */ - bool closeRead(void); - bool closeWrite(void); - -private: - bool mReadNonBlocking; - bool mWriteNonBlocking; - - unsigned long mReadHandle; - unsigned long mWriteHandle; -}; - -}; // android - -#endif // _LIBS_UTILS_PIPE_H From 7c88914df4623b8f11bd5341369af60397734537 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Thu, 4 Jun 2009 13:53:57 -0700 Subject: [PATCH 115/541] rename a few files to camel-case, add copyright notices --- include/utils/{backup_helpers.h => BackupHelpers.h} | 0 libs/utils/Android.mk | 4 ++-- libs/utils/{backup_data.cpp => BackupData.cpp} | 2 +- libs/utils/{backup_helper_file.cpp => BackupHelpers.cpp} | 2 +- libs/utils/{characterData.h => CharacterData.h} | 0 libs/utils/Unicode.cpp | 6 +++--- libs/utils/ZipEntry.cpp | 4 ++-- libs/utils/ZipFile.cpp | 6 +++--- libs/utils/ZipFileCRO.cpp | 4 ++-- libs/utils/ZipFileRO.cpp | 6 +++--- libs/utils/ZipUtils.cpp | 6 +++--- 11 files changed, 20 insertions(+), 20 deletions(-) rename include/utils/{backup_helpers.h => BackupHelpers.h} (100%) rename libs/utils/{backup_data.cpp => BackupData.cpp} (99%) rename libs/utils/{backup_helper_file.cpp => BackupHelpers.cpp} (99%) rename libs/utils/{characterData.h => CharacterData.h} (100%) diff --git a/include/utils/backup_helpers.h b/include/utils/BackupHelpers.h similarity index 100% rename from include/utils/backup_helpers.h rename to include/utils/BackupHelpers.h diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk index 1acc1a43a..0b4c95bb3 100644 --- a/libs/utils/Android.mk +++ b/libs/utils/Android.mk @@ -96,8 +96,8 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ $(commonSources) \ Unicode.cpp \ - backup_data.cpp \ - backup_helper_file.cpp + BackupData.cpp \ + BackupHelpers.cpp ifeq ($(TARGET_SIMULATOR),true) LOCAL_SRC_FILES += $(hostSources) diff --git a/libs/utils/backup_data.cpp b/libs/utils/BackupData.cpp similarity index 99% rename from libs/utils/backup_data.cpp rename to libs/utils/BackupData.cpp index 95c05b7ed..120f23d36 100644 --- a/libs/utils/backup_data.cpp +++ b/libs/utils/BackupData.cpp @@ -16,7 +16,7 @@ #define LOG_TAG "backup_data" -#include +#include #include #include diff --git a/libs/utils/backup_helper_file.cpp b/libs/utils/BackupHelpers.cpp similarity index 99% rename from libs/utils/backup_helper_file.cpp rename to libs/utils/BackupHelpers.cpp index 7ec2ce865..e8e6c4572 100644 --- a/libs/utils/backup_helper_file.cpp +++ b/libs/utils/BackupHelpers.cpp @@ -16,7 +16,7 @@ #define LOG_TAG "file_backup_helper" -#include +#include #include #include diff --git a/libs/utils/characterData.h b/libs/utils/CharacterData.h similarity index 100% rename from libs/utils/characterData.h rename to libs/utils/CharacterData.h diff --git a/libs/utils/Unicode.cpp b/libs/utils/Unicode.cpp index 33f535fd1..f92703e7f 100644 --- a/libs/utils/Unicode.cpp +++ b/libs/utils/Unicode.cpp @@ -14,11 +14,11 @@ * limitations under the License. */ -#include "utils/AndroidUnicode.h" -#include "characterData.h" +#include +#include "CharacterData.h" #define LOG_TAG "Unicode" -#include "utils/Log.h" +#include // ICU headers for using macros #include diff --git a/libs/utils/ZipEntry.cpp b/libs/utils/ZipEntry.cpp index fbc9e6784..96f9fc4d6 100644 --- a/libs/utils/ZipEntry.cpp +++ b/libs/utils/ZipEntry.cpp @@ -20,8 +20,8 @@ #define LOG_TAG "zip" -#include "utils/ZipEntry.h" -#include "utils/Log.h" +#include +#include #include #include diff --git a/libs/utils/ZipFile.cpp b/libs/utils/ZipFile.cpp index 2132d2224..eaa0b208c 100644 --- a/libs/utils/ZipFile.cpp +++ b/libs/utils/ZipFile.cpp @@ -20,9 +20,9 @@ #define LOG_TAG "zip" -#include "utils/ZipFile.h" -#include "utils/ZipUtils.h" -#include "utils/Log.h" +#include +#include +#include #include #define DEF_MEM_LEVEL 8 // normally in zutil.h? diff --git a/libs/utils/ZipFileCRO.cpp b/libs/utils/ZipFileCRO.cpp index d312dafad..45f6c8baa 100644 --- a/libs/utils/ZipFileCRO.cpp +++ b/libs/utils/ZipFileCRO.cpp @@ -14,8 +14,8 @@ * limitations under the License. */ -#include "utils/ZipFileCRO.h" -#include "utils/ZipFileRO.h" +#include +#include using namespace android; diff --git a/libs/utils/ZipFileRO.cpp b/libs/utils/ZipFileRO.cpp index ae8c71972..6c701dd0b 100644 --- a/libs/utils/ZipFileRO.cpp +++ b/libs/utils/ZipFileRO.cpp @@ -19,9 +19,9 @@ // #define LOG_TAG "zipro" //#define LOG_NDEBUG 0 -#include "utils/ZipFileRO.h" -#include "utils/Log.h" -#include "utils/misc.h" +#include +#include +#include #include diff --git a/libs/utils/ZipUtils.cpp b/libs/utils/ZipUtils.cpp index bfbacfecd..5df94cbbd 100644 --- a/libs/utils/ZipUtils.cpp +++ b/libs/utils/ZipUtils.cpp @@ -20,9 +20,9 @@ #define LOG_TAG "ziputil" -#include "utils/ZipUtils.h" -#include "utils/ZipFileRO.h" -#include "utils/Log.h" +#include +#include +#include #include #include From 2972a14f48e9240582535d9870ac5d5bed078f21 Mon Sep 17 00:00:00 2001 From: Christopher Tate Date: Thu, 4 Jun 2009 17:01:06 -0700 Subject: [PATCH 116/541] Fix back_up_files() error detection when opening/CRCing the file --- libs/utils/BackupHelpers.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libs/utils/BackupHelpers.cpp b/libs/utils/BackupHelpers.cpp index e8e6c4572..7f423a8e8 100644 --- a/libs/utils/BackupHelpers.cpp +++ b/libs/utils/BackupHelpers.cpp @@ -350,10 +350,11 @@ back_up_files(int oldSnapshotFD, BackupDataWriter* dataStream, int newSnapshotFD FileState& g = newSnapshot.editValueAt(m); int fd = open(realFilename.string(), O_RDONLY); - if (fd != -1) { + if (fd < 0) { // We can't open the file. Don't report it as a delete either. Let the // server keep the old version. Maybe they'll be able to deal with it // on restore. + LOGP("Unable to open file %s - skipping", realFilename.string()); } else { g.crc32 = compute_crc32(fd); From ba6a87f916cb706e510d541b1272edad1fb0b061 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Thu, 4 Jun 2009 23:29:29 -0700 Subject: [PATCH 117/541] cleanup Debug.h a bit --- include/utils/Debug.h | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/include/utils/Debug.h b/include/utils/Debug.h index a662b9cc2..21d04bdce 100644 --- a/include/utils/Debug.h +++ b/include/utils/Debug.h @@ -14,10 +14,6 @@ * limitations under the License. */ -// -// Debugging tools. These should be able to be stripped -// in release builds. -// #ifndef ANDROID_DEBUG_H #define ANDROID_DEBUG_H @@ -25,9 +21,30 @@ #include namespace android { +// --------------------------------------------------------------------------- +#ifdef __cplusplus template struct CompileTimeAssert; template<> struct CompileTimeAssert {}; +#define COMPILE_TIME_ASSERT(_exp) \ + template class CompileTimeAssert< (_exp) >; +#endif + +// --------------------------------------------------------------------------- + +#ifdef __cplusplus +template struct CompileTimeIfElse; +template +struct CompileTimeIfElse { typedef LHS TYPE; }; +template +struct CompileTimeIfElse { typedef RHS TYPE; }; +#endif + +// --------------------------------------------------------------------------- + +#ifdef __cplusplus +extern "C" { +#endif const char* stringForIndent(int32_t indentLevel); @@ -35,11 +52,17 @@ typedef void (*debugPrintFunc)(void* cookie, const char* txt); void printTypeCode(uint32_t typeCode, debugPrintFunc func = 0, void* cookie = 0); + void printHexData(int32_t indent, const void *buf, size_t length, size_t bytesPerLine=16, int32_t singleLineBytesCutoff=16, size_t alignment=0, bool cArrayStyle=false, debugPrintFunc func = 0, void* cookie = 0); +#ifdef __cplusplus +} +#endif + +// --------------------------------------------------------------------------- }; // namespace android #endif // ANDROID_DEBUG_H From 254406e313f8fcb2b63e63494573673e82036dd9 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Fri, 5 Jun 2009 01:26:23 -0700 Subject: [PATCH 118/541] rename string_array.h to StringArray.h and move the implementation from the header file to a new cpp file. StringArray is used in two places in framework/base and in the Sim. Ideally we should get rid of it and use Vector instead of creating new code. --- include/utils/StringArray.h | 83 +++++++++++++++++++++ include/utils/string_array.h | 135 ----------------------------------- libs/utils/Android.mk | 1 + libs/utils/StringArray.cpp | 113 +++++++++++++++++++++++++++++ 4 files changed, 197 insertions(+), 135 deletions(-) create mode 100644 include/utils/StringArray.h delete mode 100644 include/utils/string_array.h create mode 100644 libs/utils/StringArray.cpp diff --git a/include/utils/StringArray.h b/include/utils/StringArray.h new file mode 100644 index 000000000..c24458718 --- /dev/null +++ b/include/utils/StringArray.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. + */ + +// +// Sortable array of strings. STL-ish, but STL-free. +// +#ifndef _LIBS_UTILS_STRING_ARRAY_H +#define _LIBS_UTILS_STRING_ARRAY_H + +#include +#include + +namespace android { + +// +// An expanding array of strings. Add, get, sort, delete. +// +class StringArray { +public: + StringArray(); + virtual ~StringArray(); + + // + // Add a string. A copy of the string is made. + // + bool push_back(const char* str); + + // + // Delete an entry. + // + void erase(int idx); + + // + // Sort the array. + // + void sort(int (*compare)(const void*, const void*)); + + // + // Pass this to the sort routine to do an ascending alphabetical sort. + // + static int cmpAscendingAlpha(const void* pstr1, const void* pstr2); + + // + // Get the #of items in the array. + // + inline int size(void) const { return mCurrent; } + + // + // Return entry N. + // [should use operator[] here] + // + const char* getEntry(int idx) const { + return (unsigned(idx) >= unsigned(mCurrent)) ? NULL : mArray[idx]; + } + + // + // Set entry N to specified string. + // [should use operator[] here] + // + void setEntry(int idx, const char* str); + +private: + int mMax; + int mCurrent; + char** mArray; +}; + +}; // namespace android + +#endif // _LIBS_UTILS_STRING_ARRAY_H diff --git a/include/utils/string_array.h b/include/utils/string_array.h deleted file mode 100644 index 064dda224..000000000 --- a/include/utils/string_array.h +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright (C) 2005 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. - */ - -// -// Sortable array of strings. STL-ish, but STL-free. -// -#ifndef _LIBS_UTILS_STRING_ARRAY_H -#define _LIBS_UTILS_STRING_ARRAY_H - -#include -#include - -namespace android { - -// -// An expanding array of strings. Add, get, sort, delete. -// -class StringArray { -public: - StringArray() - : mMax(0), mCurrent(0), mArray(NULL) - {} - virtual ~StringArray() { - for (int i = 0; i < mCurrent; i++) - delete[] mArray[i]; - delete[] mArray; - } - - // - // Add a string. A copy of the string is made. - // - bool push_back(const char* str) { - if (mCurrent >= mMax) { - char** tmp; - - if (mMax == 0) - mMax = 16; // initial storage - else - mMax *= 2; - - tmp = new char*[mMax]; - if (tmp == NULL) - return false; - - memcpy(tmp, mArray, mCurrent * sizeof(char*)); - delete[] mArray; - mArray = tmp; - } - - int len = strlen(str); - mArray[mCurrent] = new char[len+1]; - memcpy(mArray[mCurrent], str, len+1); - mCurrent++; - - return true; - } - - // - // Delete an entry. - // - void erase(int idx) { - if (idx < 0 || idx >= mCurrent) - return; - delete[] mArray[idx]; - if (idx < mCurrent-1) { - memmove(&mArray[idx], &mArray[idx+1], - (mCurrent-1 - idx) * sizeof(char*)); - } - mCurrent--; - } - - // - // Sort the array. - // - void sort(int (*compare)(const void*, const void*)) { - qsort(mArray, mCurrent, sizeof(char*), compare); - } - - // - // Pass this to the sort routine to do an ascending alphabetical sort. - // - static int cmpAscendingAlpha(const void* pstr1, const void* pstr2) { - return strcmp(*(const char**)pstr1, *(const char**)pstr2); - } - - // - // Get the #of items in the array. - // - inline int size(void) const { return mCurrent; } - - // - // Return entry N. - // [should use operator[] here] - // - const char* getEntry(int idx) const { - if (idx < 0 || idx >= mCurrent) - return NULL; - return mArray[idx]; - } - - // - // Set entry N to specified string. - // [should use operator[] here] - // - void setEntry(int idx, const char* str) { - if (idx < 0 || idx >= mCurrent) - return; - delete[] mArray[idx]; - int len = strlen(str); - mArray[idx] = new char[len+1]; - memcpy(mArray[idx], str, len+1); - } - -private: - int mMax; - int mCurrent; - char** mArray; -}; - -}; // namespace android - -#endif // _LIBS_UTILS_STRING_ARRAY_H diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk index 0b4c95bb3..6605c9fde 100644 --- a/libs/utils/Android.mk +++ b/libs/utils/Android.mk @@ -32,6 +32,7 @@ commonSources:= \ StopWatch.cpp \ String8.cpp \ String16.cpp \ + StringArray.cpp \ SystemClock.cpp \ TextOutput.cpp \ Threads.cpp \ diff --git a/libs/utils/StringArray.cpp b/libs/utils/StringArray.cpp new file mode 100644 index 000000000..aa42d6837 --- /dev/null +++ b/libs/utils/StringArray.cpp @@ -0,0 +1,113 @@ +/* + * 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. + */ + +// +// Sortable array of strings. STL-ish, but STL-free. +// + +#include +#include + +#include + +namespace android { + +// +// An expanding array of strings. Add, get, sort, delete. +// +StringArray::StringArray() + : mMax(0), mCurrent(0), mArray(NULL) +{ +} + +StringArray:: ~StringArray() { + for (int i = 0; i < mCurrent; i++) + delete[] mArray[i]; + delete[] mArray; +} + +// +// Add a string. A copy of the string is made. +// +bool StringArray::push_back(const char* str) { + if (mCurrent >= mMax) { + char** tmp; + + if (mMax == 0) + mMax = 16; // initial storage + else + mMax *= 2; + + tmp = new char*[mMax]; + if (tmp == NULL) + return false; + + memcpy(tmp, mArray, mCurrent * sizeof(char*)); + delete[] mArray; + mArray = tmp; + } + + int len = strlen(str); + mArray[mCurrent] = new char[len+1]; + memcpy(mArray[mCurrent], str, len+1); + mCurrent++; + + return true; +} + +// +// Delete an entry. +// +void StringArray::erase(int idx) { + if (idx < 0 || idx >= mCurrent) + return; + delete[] mArray[idx]; + if (idx < mCurrent-1) { + memmove(&mArray[idx], &mArray[idx+1], + (mCurrent-1 - idx) * sizeof(char*)); + } + mCurrent--; +} + +// +// Sort the array. +// +void StringArray::sort(int (*compare)(const void*, const void*)) { + qsort(mArray, mCurrent, sizeof(char*), compare); +} + +// +// Pass this to the sort routine to do an ascending alphabetical sort. +// +int StringArray::cmpAscendingAlpha(const void* pstr1, const void* pstr2) { + return strcmp(*(const char**)pstr1, *(const char**)pstr2); +} + +// +// Set entry N to specified string. +// [should use operator[] here] +// +void StringArray::setEntry(int idx, const char* str) { + if (idx < 0 || idx >= mCurrent) + return; + delete[] mArray[idx]; + int len = strlen(str); + mArray[idx] = new char[len+1]; + memcpy(mArray[idx], str, len+1); +} + + +}; // namespace android From 09e2b145ff574261da93fc7980d2beb22ae55a46 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Fri, 5 Jun 2009 14:56:35 -0700 Subject: [PATCH 119/541] break dependency on utils/ZipEntry.h and utils/ZipFile.h, get rid of inet_address.h and Socket.h which were not used --- include/utils/Socket.h | 80 --- include/utils/ZipEntry.h | 345 --------- include/utils/ZipFile.h | 269 ------- include/utils/inet_address.h | 103 --- libs/utils/Android.mk | 23 +- libs/utils/InetAddress.cpp | 236 ------- libs/utils/Socket.cpp | 388 ---------- libs/utils/ZipEntry.cpp | 696 ------------------ libs/utils/ZipFile.cpp | 1296 ---------------------------------- 9 files changed, 1 insertion(+), 3435 deletions(-) delete mode 100644 include/utils/Socket.h delete mode 100644 include/utils/ZipEntry.h delete mode 100644 include/utils/ZipFile.h delete mode 100644 include/utils/inet_address.h delete mode 100644 libs/utils/InetAddress.cpp delete mode 100644 libs/utils/Socket.cpp delete mode 100644 libs/utils/ZipEntry.cpp delete mode 100644 libs/utils/ZipFile.cpp diff --git a/include/utils/Socket.h b/include/utils/Socket.h deleted file mode 100644 index 8b7f40617..000000000 --- a/include/utils/Socket.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (C) 2005 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. - */ - -// -// Socket class. Modeled after Java classes. -// -#ifndef _RUNTIME_SOCKET_H -#define _RUNTIME_SOCKET_H - -#include -#include - -namespace android { - -/* - * Basic socket class, needed to abstract away the differences between - * BSD sockets and WinSock. This establishes a streaming network - * connection (TCP/IP) to somebody. - */ -class Socket { -public: - Socket(void); - ~Socket(void); - - // Create a connection to somewhere. - // Return 0 on success. - int connect(const char* host, int port); - int connect(const InetAddress* addr, int port); - - - // Close the socket. Don't try to use this object again after - // calling this. Returns false on failure. - bool close(void); - - // If we created the socket without an address, we can use these - // to finish the connection. Returns 0 on success. - int bind(const SocketAddress& bindPoint); - int connect(const SocketAddress& endPoint); - - // Here we deviate from the traditional object-oriented fanciness - // and just provide read/write operators instead of getters for - // objects that abstract a stream. - // - // Standard read/write semantics. - int read(void* buf, ssize_t len) const; - int write(const void* buf, ssize_t len) const; - - // This must be called once, at program startup. - static bool bootInit(void); - static void finalShutdown(void); - -private: - // Internal function that establishes a connection. - int doConnect(const InetSocketAddress& addr); - - unsigned long mSock; // holds SOCKET or int - - static bool mBootInitialized; -}; - - -// debug -- unit tests -void TestSockets(void); - -}; // namespace android - -#endif // _RUNTIME_SOCKET_H diff --git a/include/utils/ZipEntry.h b/include/utils/ZipEntry.h deleted file mode 100644 index e4698dfbb..000000000 --- a/include/utils/ZipEntry.h +++ /dev/null @@ -1,345 +0,0 @@ -/* - * Copyright (C) 2006 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. - */ - -// -// Zip archive entries. -// -// The ZipEntry class is tightly meshed with the ZipFile class. -// -#ifndef __LIBS_ZIPENTRY_H -#define __LIBS_ZIPENTRY_H - -#include "Errors.h" - -#include -#include - -namespace android { - -class ZipFile; - -/* - * ZipEntry objects represent a single entry in a Zip archive. - * - * You can use one of these to get or set information about an entry, but - * there are no functions here for accessing the data itself. (We could - * tuck a pointer to the ZipFile in here for convenience, but that raises - * the likelihood of using ZipEntry objects after discarding the ZipFile.) - * - * File information is stored in two places: next to the file data (the Local - * File Header, and possibly a Data Descriptor), and at the end of the file - * (the Central Directory Entry). The two must be kept in sync. - */ -class ZipEntry { -public: - friend class ZipFile; - - ZipEntry(void) - : mDeleted(false), mMarked(false) - {} - ~ZipEntry(void) {} - - /* - * Returns "true" if the data is compressed. - */ - bool isCompressed(void) const { - return mCDE.mCompressionMethod != kCompressStored; - } - int getCompressionMethod(void) const { return mCDE.mCompressionMethod; } - - /* - * Return the uncompressed length. - */ - off_t getUncompressedLen(void) const { return mCDE.mUncompressedSize; } - - /* - * Return the compressed length. For uncompressed data, this returns - * the same thing as getUncompresesdLen(). - */ - off_t getCompressedLen(void) const { return mCDE.mCompressedSize; } - - /* - * Return the absolute file offset of the start of the compressed or - * uncompressed data. - */ - off_t getFileOffset(void) const { - return mCDE.mLocalHeaderRelOffset + - LocalFileHeader::kLFHLen + - mLFH.mFileNameLength + - mLFH.mExtraFieldLength; - } - - /* - * Return the data CRC. - */ - unsigned long getCRC32(void) const { return mCDE.mCRC32; } - - /* - * Return file modification time in UNIX seconds-since-epoch. - */ - time_t getModWhen(void) const; - - /* - * Return the archived file name. - */ - const char* getFileName(void) const { return (const char*) mCDE.mFileName; } - - /* - * Application-defined "mark". Can be useful when synchronizing the - * contents of an archive with contents on disk. - */ - bool getMarked(void) const { return mMarked; } - void setMarked(bool val) { mMarked = val; } - - /* - * Some basic functions for raw data manipulation. "LE" means - * Little Endian. - */ - static inline unsigned short getShortLE(const unsigned char* buf) { - return buf[0] | (buf[1] << 8); - } - static inline unsigned long getLongLE(const unsigned char* buf) { - return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); - } - static inline void putShortLE(unsigned char* buf, short val) { - buf[0] = (unsigned char) val; - buf[1] = (unsigned char) (val >> 8); - } - static inline void putLongLE(unsigned char* buf, long val) { - buf[0] = (unsigned char) val; - buf[1] = (unsigned char) (val >> 8); - buf[2] = (unsigned char) (val >> 16); - buf[3] = (unsigned char) (val >> 24); - } - - /* defined for Zip archives */ - enum { - kCompressStored = 0, // no compression - // shrunk = 1, - // reduced 1 = 2, - // reduced 2 = 3, - // reduced 3 = 4, - // reduced 4 = 5, - // imploded = 6, - // tokenized = 7, - kCompressDeflated = 8, // standard deflate - // Deflate64 = 9, - // lib imploded = 10, - // reserved = 11, - // bzip2 = 12, - }; - - /* - * Deletion flag. If set, the entry will be removed on the next - * call to "flush". - */ - bool getDeleted(void) const { return mDeleted; } - -protected: - /* - * Initialize the structure from the file, which is pointing at - * our Central Directory entry. - */ - status_t initFromCDE(FILE* fp); - - /* - * Initialize the structure for a new file. We need the filename - * and comment so that we can properly size the LFH area. The - * filename is mandatory, the comment is optional. - */ - void initNew(const char* fileName, const char* comment); - - /* - * Initialize the structure with the contents of a ZipEntry from - * another file. - */ - status_t initFromExternal(const ZipFile* pZipFile, const ZipEntry* pEntry); - - /* - * Add some pad bytes to the LFH. We do this by adding or resizing - * the "extra" field. - */ - status_t addPadding(int padding); - - /* - * Set information about the data for this entry. - */ - void setDataInfo(long uncompLen, long compLen, unsigned long crc32, - int compressionMethod); - - /* - * Set the modification date. - */ - void setModWhen(time_t when); - - /* - * Return the offset of the local file header. - */ - off_t getLFHOffset(void) const { return mCDE.mLocalHeaderRelOffset; } - - /* - * Set the offset of the local file header, relative to the start of - * the current file. - */ - void setLFHOffset(off_t offset) { - mCDE.mLocalHeaderRelOffset = (long) offset; - } - - /* mark for deletion; used by ZipFile::remove() */ - void setDeleted(void) { mDeleted = true; } - -private: - /* these are private and not defined */ - ZipEntry(const ZipEntry& src); - ZipEntry& operator=(const ZipEntry& src); - - /* returns "true" if the CDE and the LFH agree */ - bool compareHeaders(void) const; - void copyCDEtoLFH(void); - - bool mDeleted; // set if entry is pending deletion - bool mMarked; // app-defined marker - - /* - * Every entry in the Zip archive starts off with one of these. - */ - class LocalFileHeader { - public: - LocalFileHeader(void) : - mVersionToExtract(0), - mGPBitFlag(0), - mCompressionMethod(0), - mLastModFileTime(0), - mLastModFileDate(0), - mCRC32(0), - mCompressedSize(0), - mUncompressedSize(0), - mFileNameLength(0), - mExtraFieldLength(0), - mFileName(NULL), - mExtraField(NULL) - {} - virtual ~LocalFileHeader(void) { - delete[] mFileName; - delete[] mExtraField; - } - - status_t read(FILE* fp); - status_t write(FILE* fp); - - // unsigned long mSignature; - unsigned short mVersionToExtract; - unsigned short mGPBitFlag; - unsigned short mCompressionMethod; - unsigned short mLastModFileTime; - unsigned short mLastModFileDate; - unsigned long mCRC32; - unsigned long mCompressedSize; - unsigned long mUncompressedSize; - unsigned short mFileNameLength; - unsigned short mExtraFieldLength; - unsigned char* mFileName; - unsigned char* mExtraField; - - enum { - kSignature = 0x04034b50, - kLFHLen = 30, // LocalFileHdr len, excl. var fields - }; - - void dump(void) const; - }; - - /* - * Every entry in the Zip archive has one of these in the "central - * directory" at the end of the file. - */ - class CentralDirEntry { - public: - CentralDirEntry(void) : - mVersionMadeBy(0), - mVersionToExtract(0), - mGPBitFlag(0), - mCompressionMethod(0), - mLastModFileTime(0), - mLastModFileDate(0), - mCRC32(0), - mCompressedSize(0), - mUncompressedSize(0), - mFileNameLength(0), - mExtraFieldLength(0), - mFileCommentLength(0), - mDiskNumberStart(0), - mInternalAttrs(0), - mExternalAttrs(0), - mLocalHeaderRelOffset(0), - mFileName(NULL), - mExtraField(NULL), - mFileComment(NULL) - {} - virtual ~CentralDirEntry(void) { - delete[] mFileName; - delete[] mExtraField; - delete[] mFileComment; - } - - status_t read(FILE* fp); - status_t write(FILE* fp); - - // unsigned long mSignature; - unsigned short mVersionMadeBy; - unsigned short mVersionToExtract; - unsigned short mGPBitFlag; - unsigned short mCompressionMethod; - unsigned short mLastModFileTime; - unsigned short mLastModFileDate; - unsigned long mCRC32; - unsigned long mCompressedSize; - unsigned long mUncompressedSize; - unsigned short mFileNameLength; - unsigned short mExtraFieldLength; - unsigned short mFileCommentLength; - unsigned short mDiskNumberStart; - unsigned short mInternalAttrs; - unsigned long mExternalAttrs; - unsigned long mLocalHeaderRelOffset; - unsigned char* mFileName; - unsigned char* mExtraField; - unsigned char* mFileComment; - - void dump(void) const; - - enum { - kSignature = 0x02014b50, - kCDELen = 46, // CentralDirEnt len, excl. var fields - }; - }; - - enum { - //kDataDescriptorSignature = 0x08074b50, // currently unused - kDataDescriptorLen = 16, // four 32-bit fields - - kDefaultVersion = 20, // need deflate, nothing much else - kDefaultMadeBy = 0x0317, // 03=UNIX, 17=spec v2.3 - kUsesDataDescr = 0x0008, // GPBitFlag bit 3 - }; - - LocalFileHeader mLFH; - CentralDirEntry mCDE; -}; - -}; // namespace android - -#endif // __LIBS_ZIPENTRY_H diff --git a/include/utils/ZipFile.h b/include/utils/ZipFile.h deleted file mode 100644 index 44df5bbaa..000000000 --- a/include/utils/ZipFile.h +++ /dev/null @@ -1,269 +0,0 @@ -/* - * Copyright (C) 2006 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. - */ - -// -// General-purpose Zip archive access. This class allows both reading and -// writing to Zip archives, including deletion of existing entries. -// -#ifndef __LIBS_ZIPFILE_H -#define __LIBS_ZIPFILE_H - -#include "ZipEntry.h" -#include "Vector.h" -#include "Errors.h" -#include - -namespace android { - -/* - * Manipulate a Zip archive. - * - * Some changes will not be visible in the until until "flush" is called. - * - * The correct way to update a file archive is to make all changes to a - * copy of the archive in a temporary file, and then unlink/rename over - * the original after everything completes. Because we're only interested - * in using this for packaging, we don't worry about such things. Crashing - * after making changes and before flush() completes could leave us with - * an unusable Zip archive. - */ -class ZipFile { -public: - ZipFile(void) - : mZipFp(NULL), mReadOnly(false), mNeedCDRewrite(false) - {} - ~ZipFile(void) { - if (!mReadOnly) - flush(); - if (mZipFp != NULL) - fclose(mZipFp); - discardEntries(); - } - - /* - * Open a new or existing archive. - */ - typedef enum { - kOpenReadOnly = 0x01, - kOpenReadWrite = 0x02, - kOpenCreate = 0x04, // create if it doesn't exist - kOpenTruncate = 0x08, // if it exists, empty it - }; - status_t open(const char* zipFileName, int flags); - - /* - * Add a file to the end of the archive. Specify whether you want the - * library to try to store it compressed. - * - * If "storageName" is specified, the archive will use that instead - * of "fileName". - * - * If there is already an entry with the same name, the call fails. - * Existing entries with the same name must be removed first. - * - * If "ppEntry" is non-NULL, a pointer to the new entry will be returned. - */ - status_t add(const char* fileName, int compressionMethod, - ZipEntry** ppEntry) - { - return add(fileName, fileName, compressionMethod, ppEntry); - } - status_t add(const char* fileName, const char* storageName, - int compressionMethod, ZipEntry** ppEntry) - { - return addCommon(fileName, NULL, 0, storageName, - ZipEntry::kCompressStored, - compressionMethod, ppEntry); - } - - /* - * Add a file that is already compressed with gzip. - * - * If "ppEntry" is non-NULL, a pointer to the new entry will be returned. - */ - status_t addGzip(const char* fileName, const char* storageName, - ZipEntry** ppEntry) - { - return addCommon(fileName, NULL, 0, storageName, - ZipEntry::kCompressDeflated, - ZipEntry::kCompressDeflated, ppEntry); - } - - /* - * Add a file from an in-memory data buffer. - * - * If "ppEntry" is non-NULL, a pointer to the new entry will be returned. - */ - status_t add(const void* data, size_t size, const char* storageName, - int compressionMethod, ZipEntry** ppEntry) - { - return addCommon(NULL, data, size, storageName, - ZipEntry::kCompressStored, - compressionMethod, ppEntry); - } - - /* - * Add an entry by copying it from another zip file. If "padding" is - * nonzero, the specified number of bytes will be added to the "extra" - * field in the header. - * - * If "ppEntry" is non-NULL, a pointer to the new entry will be returned. - */ - status_t add(const ZipFile* pSourceZip, const ZipEntry* pSourceEntry, - int padding, ZipEntry** ppEntry); - - /* - * Mark an entry as having been removed. It is not actually deleted - * from the archive or our internal data structures until flush() is - * called. - */ - status_t remove(ZipEntry* pEntry); - - /* - * Flush changes. If mNeedCDRewrite is set, this writes the central dir. - */ - status_t flush(void); - - /* - * Expand the data into the buffer provided. The buffer must hold - * at least bytes. Variation expands directly - * to a file. - * - * Returns "false" if an error was encountered in the compressed data. - */ - //bool uncompress(const ZipEntry* pEntry, void* buf) const; - //bool uncompress(const ZipEntry* pEntry, FILE* fp) const; - void* uncompress(const ZipEntry* pEntry); - - /* - * Get an entry, by name. Returns NULL if not found. - * - * Does not return entries pending deletion. - */ - ZipEntry* getEntryByName(const char* fileName) const; - - /* - * Get the Nth entry in the archive. - * - * This will return an entry that is pending deletion. - */ - int getNumEntries(void) const { return mEntries.size(); } - ZipEntry* getEntryByIndex(int idx) const; - -private: - /* these are private and not defined */ - ZipFile(const ZipFile& src); - ZipFile& operator=(const ZipFile& src); - - class EndOfCentralDir { - public: - EndOfCentralDir(void) : - mDiskNumber(0), - mDiskWithCentralDir(0), - mNumEntries(0), - mTotalNumEntries(0), - mCentralDirSize(0), - mCentralDirOffset(0), - mCommentLen(0), - mComment(NULL) - {} - virtual ~EndOfCentralDir(void) { - delete[] mComment; - } - - status_t readBuf(const unsigned char* buf, int len); - status_t write(FILE* fp); - - //unsigned long mSignature; - unsigned short mDiskNumber; - unsigned short mDiskWithCentralDir; - unsigned short mNumEntries; - unsigned short mTotalNumEntries; - unsigned long mCentralDirSize; - unsigned long mCentralDirOffset; // offset from first disk - unsigned short mCommentLen; - unsigned char* mComment; - - enum { - kSignature = 0x06054b50, - kEOCDLen = 22, // EndOfCentralDir len, excl. comment - - kMaxCommentLen = 65535, // longest possible in ushort - kMaxEOCDSearch = kMaxCommentLen + EndOfCentralDir::kEOCDLen, - - }; - - void dump(void) const; - }; - - - /* read all entries in the central dir */ - status_t readCentralDir(void); - - /* crunch deleted entries out */ - status_t crunchArchive(void); - - /* clean up mEntries */ - void discardEntries(void); - - /* common handler for all "add" functions */ - status_t addCommon(const char* fileName, const void* data, size_t size, - const char* storageName, int sourceType, int compressionMethod, - ZipEntry** ppEntry); - - /* copy all of "srcFp" into "dstFp" */ - status_t copyFpToFp(FILE* dstFp, FILE* srcFp, unsigned long* pCRC32); - /* copy all of "data" into "dstFp" */ - status_t copyDataToFp(FILE* dstFp, - const void* data, size_t size, unsigned long* pCRC32); - /* copy some of "srcFp" into "dstFp" */ - status_t copyPartialFpToFp(FILE* dstFp, FILE* srcFp, long length, - unsigned long* pCRC32); - /* like memmove(), but on parts of a single file */ - status_t filemove(FILE* fp, off_t dest, off_t src, size_t n); - /* compress all of "srcFp" into "dstFp", using Deflate */ - status_t compressFpToFp(FILE* dstFp, FILE* srcFp, - const void* data, size_t size, unsigned long* pCRC32); - - /* get modification date from a file descriptor */ - time_t getModTime(int fd); - - /* - * We use stdio FILE*, which gives us buffering but makes dealing - * with files >2GB awkward. Until we support Zip64, we're fine. - */ - FILE* mZipFp; // Zip file pointer - - /* one of these per file */ - EndOfCentralDir mEOCD; - - /* did we open this read-only? */ - bool mReadOnly; - - /* set this when we trash the central dir */ - bool mNeedCDRewrite; - - /* - * One ZipEntry per entry in the zip file. I'm using pointers instead - * of objects because it's easier than making operator= work for the - * classes and sub-classes. - */ - Vector mEntries; -}; - -}; // namespace android - -#endif // __LIBS_ZIPFILE_H diff --git a/include/utils/inet_address.h b/include/utils/inet_address.h deleted file mode 100644 index dbd8672e0..000000000 --- a/include/utils/inet_address.h +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (C) 2005 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. - */ - -// -// Internet address classes. Modeled after Java classes. -// -#ifndef _RUNTIME_INET_ADDRESS_H -#define _RUNTIME_INET_ADDRESS_H - -#ifdef HAVE_ANDROID_OS -#error DO NOT USE THIS FILE IN THE DEVICE BUILD -#endif - - -namespace android { - -/* - * This class holds Internet addresses. Perhaps more useful is its - * ability to look up addresses by name. - * - * Invoke one of the static factory methods to create a new object. - */ -class InetAddress { -public: - virtual ~InetAddress(void); - - // create from w.x.y.z or foo.bar.com notation - static InetAddress* getByName(const char* host); - - // copy-construction - InetAddress(const InetAddress& orig); - - const void* getAddress(void) const { return mAddress; } - int getAddressLength(void) const { return mLength; } - const char* getHostName(void) const { return mName; } - -private: - InetAddress(void); - // assignment (private) - InetAddress& operator=(const InetAddress& addr); - - // use a void* here so we don't have to expose actual socket headers - void* mAddress; // this is really a ptr to sockaddr_in - int mLength; - char* mName; -}; - - -/* - * Base class for socket addresses. - */ -class SocketAddress { -public: - SocketAddress() {} - virtual ~SocketAddress() {} -}; - - -/* - * Internet address class. This combines an InetAddress with a port. - */ -class InetSocketAddress : public SocketAddress { -public: - InetSocketAddress() : - mAddress(0), mPort(-1) - {} - ~InetSocketAddress(void) { - delete mAddress; - } - - // Create an address with a host wildcard (useful for servers). - bool create(int port); - // Create an address with the specified host and port. - bool create(const InetAddress* addr, int port); - // Create an address with the specified host and port. Does the - // hostname lookup. - bool create(const char* host, int port); - - const InetAddress* getAddress(void) const { return mAddress; } - const int getPort(void) const { return mPort; } - const char* getHostName(void) const { return mAddress->getHostName(); } - -private: - InetAddress* mAddress; - int mPort; -}; - -}; // namespace android - -#endif // _RUNTIME_INET_ADDRESS_H diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk index 6605c9fde..70d440797 100644 --- a/libs/utils/Android.mk +++ b/libs/utils/Android.mk @@ -44,26 +44,13 @@ commonSources:= \ misc.cpp \ LogSocket.cpp -# -# The cpp files listed here do not belong in the device -# build. Consult with the swetland before even thinking about -# putting them in commonSources. -# -# They're used by the simulator runtime and by host-side tools like -# aapt and the simulator front-end. -# -hostSources:= \ - InetAddress.cpp \ - Socket.cpp \ - ZipEntry.cpp \ - ZipFile.cpp # For the host # ===================================================== include $(CLEAR_VARS) -LOCAL_SRC_FILES:= $(commonSources) $(hostSources) +LOCAL_SRC_FILES:= $(commonSources) ifeq ($(HOST_OS),linux) # Use the futex based mutex and condition variable @@ -100,10 +87,6 @@ LOCAL_SRC_FILES:= \ BackupData.cpp \ BackupHelpers.cpp -ifeq ($(TARGET_SIMULATOR),true) -LOCAL_SRC_FILES += $(hostSources) -endif - ifeq ($(TARGET_OS),linux) # Use the futex based mutex and condition variable # implementation from android-arm because it's shared mem safe @@ -130,9 +113,5 @@ endif # linux-x86 endif # sim LOCAL_MODULE:= libutils - -#LOCAL_CFLAGS+= -#LOCAL_LDFLAGS:= - include $(BUILD_SHARED_LIBRARY) diff --git a/libs/utils/InetAddress.cpp b/libs/utils/InetAddress.cpp deleted file mode 100644 index 39a0a6839..000000000 --- a/libs/utils/InetAddress.cpp +++ /dev/null @@ -1,236 +0,0 @@ -/* - * Copyright (C) 2005 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. - */ - -// -// Internet address class. -// -#ifdef HAVE_WINSOCK -# include -#else -# include -# include -# include -//# include -# include -#endif - -#include -#include -#include - -#include -#include -#include -#include -#include - -using namespace android; - - -/* - * =========================================================================== - * InetAddress - * =========================================================================== - */ - -// lock for the next couple of functions; could tuck into InetAddress -static Mutex* gGHBNLock; - -/* - * Lock/unlock access to the hostent struct returned by gethostbyname(). - */ -static inline void lock_gethostbyname(void) -{ - if (gGHBNLock == NULL) - gGHBNLock = new Mutex; - gGHBNLock->lock(); -} -static inline void unlock_gethostbyname(void) -{ - assert(gGHBNLock != NULL); - gGHBNLock->unlock(); -} - - -/* - * Constructor -- just init members. This is private so that callers - * are required to use getByName(). - */ -InetAddress::InetAddress(void) - : mAddress(NULL), mLength(-1), mName(NULL) -{ -} - -/* - * Destructor -- free address storage. - */ -InetAddress::~InetAddress(void) -{ - delete[] (char*) mAddress; - delete[] mName; -} - -/* - * Copy constructor. - */ -InetAddress::InetAddress(const InetAddress& orig) -{ - *this = orig; // use assignment code -} - -/* - * Assignment operator. - */ -InetAddress& InetAddress::operator=(const InetAddress& addr) -{ - // handle self-assignment - if (this == &addr) - return *this; - // copy mLength and mAddress - mLength = addr.mLength; - if (mLength > 0) { - mAddress = new char[mLength]; - memcpy(mAddress, addr.mAddress, mLength); - LOG(LOG_DEBUG, "socket", - "HEY: copied %d bytes in assignment operator\n", mLength); - } else { - mAddress = NULL; - } - // copy mName - mName = new char[strlen(addr.mName)+1]; - strcpy(mName, addr.mName); - - return *this; -} - -/* - * Create a new object from a name or a dotted-number IP notation. - * - * Returns NULL on failure. - */ -InetAddress* -InetAddress::getByName(const char* host) -{ - InetAddress* newAddr = NULL; - struct sockaddr_in addr; - struct hostent* he; - DurationTimer hostTimer, lockTimer; - - // gethostbyname() isn't reentrant, so we need to lock things until - // we can copy the data out. - lockTimer.start(); - lock_gethostbyname(); - hostTimer.start(); - - he = gethostbyname(host); - if (he == NULL) { - LOG(LOG_WARN, "socket", "WARNING: cannot resolve host %s\n", host); - unlock_gethostbyname(); - return NULL; - } - - memcpy(&addr.sin_addr, he->h_addr, he->h_length); - addr.sin_family = he->h_addrtype; - addr.sin_port = 0; - - // got it, unlock us - hostTimer.stop(); - he = NULL; - unlock_gethostbyname(); - - lockTimer.stop(); - if ((long) lockTimer.durationUsecs() > 100000) { - long lockTime = (long) lockTimer.durationUsecs(); - long hostTime = (long) hostTimer.durationUsecs(); - LOG(LOG_DEBUG, "socket", - "Lookup of %s took %.3fs (gethostbyname=%.3fs lock=%.3fs)\n", - host, lockTime / 1000000.0, hostTime / 1000000.0, - (lockTime - hostTime) / 1000000.0); - } - - // Alloc storage and copy it over. - newAddr = new InetAddress(); - if (newAddr == NULL) - return NULL; - - newAddr->mLength = sizeof(struct sockaddr_in); - newAddr->mAddress = new char[sizeof(struct sockaddr_in)]; - if (newAddr->mAddress == NULL) { - delete newAddr; - return NULL; - } - memcpy(newAddr->mAddress, &addr, newAddr->mLength); - - // Keep this for debug messages. - newAddr->mName = new char[strlen(host)+1]; - if (newAddr->mName == NULL) { - delete newAddr; - return NULL; - } - strcpy(newAddr->mName, host); - - return newAddr; -} - - -/* - * =========================================================================== - * InetSocketAddress - * =========================================================================== - */ - -/* - * Create an address with the host wildcard (INADDR_ANY). - */ -bool InetSocketAddress::create(int port) -{ - assert(mAddress == NULL); - - mAddress = InetAddress::getByName("0.0.0.0"); - if (mAddress == NULL) - return false; - mPort = port; - return true; -} - -/* - * Create address with host and port specified. - */ -bool InetSocketAddress::create(const InetAddress* addr, int port) -{ - assert(mAddress == NULL); - - mAddress = new InetAddress(*addr); // make a copy - if (mAddress == NULL) - return false; - mPort = port; - return true; -} - -/* - * Create address with host and port specified. - */ -bool InetSocketAddress::create(const char* host, int port) -{ - assert(mAddress == NULL); - - mAddress = InetAddress::getByName(host); - if (mAddress == NULL) - return false; - mPort = port; - return true; -} - diff --git a/libs/utils/Socket.cpp b/libs/utils/Socket.cpp deleted file mode 100644 index 51509a304..000000000 --- a/libs/utils/Socket.cpp +++ /dev/null @@ -1,388 +0,0 @@ -/* - * Copyright (C) 2005 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. - */ - -// -// Internet address class. -// - -#ifdef HAVE_WINSOCK -// This needs to come first, or Cygwin gets concerned about a potential -// clash between WinSock and . -# include -#endif - -#include -#include -#include -#include - -#ifndef HAVE_WINSOCK -# include -# include -# include -# include -#endif - -#include -#include -#include -#include -#include -#include - -using namespace android; - - -/* - * =========================================================================== - * Socket - * =========================================================================== - */ - -#ifndef INVALID_SOCKET -# define INVALID_SOCKET (-1) -#endif -#define UNDEF_SOCKET ((unsigned long) INVALID_SOCKET) - -/*static*/ bool Socket::mBootInitialized = false; - -/* - * Extract system-dependent error code. - */ -static inline int getSocketError(void) { -#ifdef HAVE_WINSOCK - return WSAGetLastError(); -#else - return errno; -#endif -} - -/* - * One-time initialization for socket code. - */ -/*static*/ bool Socket::bootInit(void) -{ -#ifdef HAVE_WINSOCK - WSADATA wsaData; - int err; - - err = WSAStartup(MAKEWORD(2, 0), &wsaData); - if (err != 0) { - LOG(LOG_ERROR, "socket", "Unable to start WinSock\n"); - return false; - } - - LOG(LOG_INFO, "socket", "Using WinSock v%d.%d\n", - LOBYTE(wsaData.wVersion), HIBYTE(wsaData.wVersion)); -#endif - - mBootInitialized = true; - return true; -} - -/* - * One-time shutdown for socket code. - */ -/*static*/ void Socket::finalShutdown(void) -{ -#ifdef HAVE_WINSOCK - WSACleanup(); -#endif - mBootInitialized = false; -} - - -/* - * Simple constructor. Allow the application to create us and then make - * bind/connect calls. - */ -Socket::Socket(void) - : mSock(UNDEF_SOCKET) -{ - if (!mBootInitialized) - LOG(LOG_WARN, "socket", "WARNING: sockets not initialized\n"); -} - -/* - * Destructor. Closes the socket and resets our storage. - */ -Socket::~Socket(void) -{ - close(); -} - - -/* - * Create a socket and connect to the specified host and port. - */ -int Socket::connect(const char* host, int port) -{ - if (mSock != UNDEF_SOCKET) { - LOG(LOG_WARN, "socket", "Socket already connected\n"); - return -1; - } - - InetSocketAddress sockAddr; - if (!sockAddr.create(host, port)) - return -1; - - //return doConnect(sockAddr); - int foo; - foo = doConnect(sockAddr); - return foo; -} - -/* - * Create a socket and connect to the specified host and port. - */ -int Socket::connect(const InetAddress* addr, int port) -{ - if (mSock != UNDEF_SOCKET) { - LOG(LOG_WARN, "socket", "Socket already connected\n"); - return -1; - } - - InetSocketAddress sockAddr; - if (!sockAddr.create(addr, port)) - return -1; - - return doConnect(sockAddr); -} - -/* - * Finish creating a socket by connecting to the remote host. - * - * Returns 0 on success. - */ -int Socket::doConnect(const InetSocketAddress& sockAddr) -{ -#ifdef HAVE_WINSOCK - SOCKET sock; -#else - int sock; -#endif - const InetAddress* addr = sockAddr.getAddress(); - int port = sockAddr.getPort(); - struct sockaddr_in inaddr; - DurationTimer connectTimer; - - assert(sizeof(struct sockaddr_in) == addr->getAddressLength()); - memcpy(&inaddr, addr->getAddress(), addr->getAddressLength()); - inaddr.sin_port = htons(port); - - //fprintf(stderr, "--- connecting to %s:%d\n", - // sockAddr.getHostName(), port); - - sock = ::socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); - if (sock == INVALID_SOCKET) { - int err = getSocketError(); - LOG(LOG_ERROR, "socket", "Unable to create socket (err=%d)\n", err); - return (err != 0) ? err : -1; - } - - connectTimer.start(); - - if (::connect(sock, (struct sockaddr*) &inaddr, sizeof(inaddr)) != 0) { - int err = getSocketError(); - LOG(LOG_WARN, "socket", "Connect to %s:%d failed: %d\n", - sockAddr.getHostName(), port, err); - return (err != 0) ? err : -1; - } - - connectTimer.stop(); - if ((long) connectTimer.durationUsecs() > 100000) { - LOG(LOG_INFO, "socket", - "Connect to %s:%d took %.3fs\n", sockAddr.getHostName(), - port, ((long) connectTimer.durationUsecs()) / 1000000.0); - } - - mSock = (unsigned long) sock; - LOG(LOG_VERBOSE, "socket", - "--- connected to %s:%d\n", sockAddr.getHostName(), port); - return 0; -} - - -/* - * Close the socket if it needs closing. - */ -bool Socket::close(void) -{ - if (mSock != UNDEF_SOCKET) { - //fprintf(stderr, "--- closing socket %lu\n", mSock); -#ifdef HAVE_WINSOCK - if (::closesocket((SOCKET) mSock) != 0) - return false; -#else - if (::close((int) mSock) != 0) - return false; -#endif - } - - mSock = UNDEF_SOCKET; - - return true; -} - -/* - * Read data from socket. - * - * Standard semantics: read up to "len" bytes into "buf". Returns the - * number of bytes read, or less than zero on error. - */ -int Socket::read(void* buf, ssize_t len) const -{ - if (mSock == UNDEF_SOCKET) { - LOG(LOG_ERROR, "socket", "ERROR: read on invalid socket\n"); - return -500; - } - -#ifdef HAVE_WINSOCK - SOCKET sock = (SOCKET) mSock; -#else - int sock = (int) mSock; -#endif - int cc; - - cc = recv(sock, (char*)buf, len, 0); - if (cc < 0) { - int err = getSocketError(); - return (err > 0) ? -err : -1; - } - - return cc; -} - -/* - * Write data to a socket. - * - * Standard semantics: write up to "len" bytes into "buf". Returns the - * number of bytes written, or less than zero on error. - */ -int Socket::write(const void* buf, ssize_t len) const -{ - if (mSock == UNDEF_SOCKET) { - LOG(LOG_ERROR, "socket", "ERROR: write on invalid socket\n"); - return -500; - } - -#ifdef HAVE_WINSOCK - SOCKET sock = (SOCKET) mSock; -#else - int sock = (int) mSock; -#endif - int cc; - - cc = send(sock, (const char*)buf, len, 0); - if (cc < 0) { - int err = getSocketError(); - return (err > 0) ? -err : -1; - } - - return cc; -} - - -/* - * =========================================================================== - * Socket tests - * =========================================================================== - */ - -/* - * Read all data from the socket. The data is read into a buffer that - * expands as needed. - * - * On exit, the buffer is returned, and the length of the data is stored - * in "*sz". A null byte is added to the end, but is not included in - * the length. - */ -static char* socketReadAll(const Socket& s, int *sz) -{ - int max, r; - char *data, *ptr, *tmp; - - data = (char*) malloc(max = 32768); - if (data == NULL) - return NULL; - - ptr = data; - - for (;;) { - if ((ptr - data) == max) { - tmp = (char*) realloc(data, max *= 2); - if(tmp == 0) { - free(data); - return 0; - } - } - r = s.read(ptr, max - (ptr - data)); - if (r == 0) - break; - if (r < 0) { - LOG(LOG_WARN, "socket", "WARNING: socket read failed (res=%d)\n",r); - break; - } - ptr += r; - } - - if ((ptr - data) == max) { - tmp = (char*) realloc(data, max + 1); - if (tmp == NULL) { - free(data); - return NULL; - } - } - *ptr = '\0'; - *sz = (ptr - data); - return data; -} - -/* - * Exercise the Socket class. - */ -void android::TestSockets(void) -{ - printf("----- SOCKET TEST ------\n"); - Socket::bootInit(); - - char* buf = NULL; - int len, cc; - const char* kTestStr = - "GET / HTTP/1.0\n" - "Connection: close\n" - "\n"; - - Socket sock; - if (sock.connect("www.google.com", 80) != 0) { - fprintf(stderr, "socket connected failed\n"); - goto bail; - } - - cc = sock.write(kTestStr, strlen(kTestStr)); - if (cc != (int) strlen(kTestStr)) { - fprintf(stderr, "write failed, res=%d\n", cc); - goto bail; - } - buf = socketReadAll(sock, &len); - - printf("GOT '%s'\n", buf); - -bail: - sock.close(); - free(buf); -} - diff --git a/libs/utils/ZipEntry.cpp b/libs/utils/ZipEntry.cpp deleted file mode 100644 index 96f9fc4d6..000000000 --- a/libs/utils/ZipEntry.cpp +++ /dev/null @@ -1,696 +0,0 @@ -/* - * Copyright (C) 2006 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. - */ - -// -// Access to entries in a Zip archive. -// - -#define LOG_TAG "zip" - -#include -#include - -#include -#include -#include - -using namespace android; - -/* - * Initialize a new ZipEntry structure from a FILE* positioned at a - * CentralDirectoryEntry. - * - * On exit, the file pointer will be at the start of the next CDE or - * at the EOCD. - */ -status_t ZipEntry::initFromCDE(FILE* fp) -{ - status_t result; - long posn; - bool hasDD; - - //LOGV("initFromCDE ---\n"); - - /* read the CDE */ - result = mCDE.read(fp); - if (result != NO_ERROR) { - LOGD("mCDE.read failed\n"); - return result; - } - - //mCDE.dump(); - - /* using the info in the CDE, go load up the LFH */ - posn = ftell(fp); - if (fseek(fp, mCDE.mLocalHeaderRelOffset, SEEK_SET) != 0) { - LOGD("local header seek failed (%ld)\n", - mCDE.mLocalHeaderRelOffset); - return UNKNOWN_ERROR; - } - - result = mLFH.read(fp); - if (result != NO_ERROR) { - LOGD("mLFH.read failed\n"); - return result; - } - - if (fseek(fp, posn, SEEK_SET) != 0) - return UNKNOWN_ERROR; - - //mLFH.dump(); - - /* - * We *might* need to read the Data Descriptor at this point and - * integrate it into the LFH. If this bit is set, the CRC-32, - * compressed size, and uncompressed size will be zero. In practice - * these seem to be rare. - */ - hasDD = (mLFH.mGPBitFlag & kUsesDataDescr) != 0; - if (hasDD) { - // do something clever - //LOGD("+++ has data descriptor\n"); - } - - /* - * Sanity-check the LFH. Note that this will fail if the "kUsesDataDescr" - * flag is set, because the LFH is incomplete. (Not a problem, since we - * prefer the CDE values.) - */ - if (!hasDD && !compareHeaders()) { - LOGW("WARNING: header mismatch\n"); - // keep going? - } - - /* - * If the mVersionToExtract is greater than 20, we may have an - * issue unpacking the record -- could be encrypted, compressed - * with something we don't support, or use Zip64 extensions. We - * can defer worrying about that to when we're extracting data. - */ - - return NO_ERROR; -} - -/* - * Initialize a new entry. Pass in the file name and an optional comment. - * - * Initializes the CDE and the LFH. - */ -void ZipEntry::initNew(const char* fileName, const char* comment) -{ - assert(fileName != NULL && *fileName != '\0'); // name required - - /* most fields are properly initialized by constructor */ - mCDE.mVersionMadeBy = kDefaultMadeBy; - mCDE.mVersionToExtract = kDefaultVersion; - mCDE.mCompressionMethod = kCompressStored; - mCDE.mFileNameLength = strlen(fileName); - if (comment != NULL) - mCDE.mFileCommentLength = strlen(comment); - mCDE.mExternalAttrs = 0x81b60020; // matches what WinZip does - - if (mCDE.mFileNameLength > 0) { - mCDE.mFileName = new unsigned char[mCDE.mFileNameLength+1]; - strcpy((char*) mCDE.mFileName, fileName); - } - if (mCDE.mFileCommentLength > 0) { - /* TODO: stop assuming null-terminated ASCII here? */ - mCDE.mFileComment = new unsigned char[mCDE.mFileCommentLength+1]; - strcpy((char*) mCDE.mFileComment, comment); - } - - copyCDEtoLFH(); -} - -/* - * Initialize a new entry, starting with the ZipEntry from a different - * archive. - * - * Initializes the CDE and the LFH. - */ -status_t ZipEntry::initFromExternal(const ZipFile* pZipFile, - const ZipEntry* pEntry) -{ - /* - * Copy everything in the CDE over, then fix up the hairy bits. - */ - memcpy(&mCDE, &pEntry->mCDE, sizeof(mCDE)); - - if (mCDE.mFileNameLength > 0) { - mCDE.mFileName = new unsigned char[mCDE.mFileNameLength+1]; - if (mCDE.mFileName == NULL) - return NO_MEMORY; - strcpy((char*) mCDE.mFileName, (char*)pEntry->mCDE.mFileName); - } - if (mCDE.mFileCommentLength > 0) { - mCDE.mFileComment = new unsigned char[mCDE.mFileCommentLength+1]; - if (mCDE.mFileComment == NULL) - return NO_MEMORY; - strcpy((char*) mCDE.mFileComment, (char*)pEntry->mCDE.mFileComment); - } - if (mCDE.mExtraFieldLength > 0) { - /* we null-terminate this, though it may not be a string */ - mCDE.mExtraField = new unsigned char[mCDE.mExtraFieldLength+1]; - if (mCDE.mExtraField == NULL) - return NO_MEMORY; - memcpy(mCDE.mExtraField, pEntry->mCDE.mExtraField, - mCDE.mExtraFieldLength+1); - } - - /* construct the LFH from the CDE */ - copyCDEtoLFH(); - - /* - * The LFH "extra" field is independent of the CDE "extra", so we - * handle it here. - */ - assert(mLFH.mExtraField == NULL); - mLFH.mExtraFieldLength = pEntry->mLFH.mExtraFieldLength; - if (mLFH.mExtraFieldLength > 0) { - mLFH.mExtraField = new unsigned char[mLFH.mExtraFieldLength+1]; - if (mLFH.mExtraField == NULL) - return NO_MEMORY; - memcpy(mLFH.mExtraField, pEntry->mLFH.mExtraField, - mLFH.mExtraFieldLength+1); - } - - return NO_ERROR; -} - -/* - * Insert pad bytes in the LFH by tweaking the "extra" field. This will - * potentially confuse something that put "extra" data in here earlier, - * but I can't find an actual problem. - */ -status_t ZipEntry::addPadding(int padding) -{ - if (padding <= 0) - return INVALID_OPERATION; - - //LOGI("HEY: adding %d pad bytes to existing %d in %s\n", - // padding, mLFH.mExtraFieldLength, mCDE.mFileName); - - if (mLFH.mExtraFieldLength > 0) { - /* extend existing field */ - unsigned char* newExtra; - - newExtra = new unsigned char[mLFH.mExtraFieldLength + padding]; - if (newExtra == NULL) - return NO_MEMORY; - memset(newExtra + mLFH.mExtraFieldLength, 0, padding); - memcpy(newExtra, mLFH.mExtraField, mLFH.mExtraFieldLength); - - delete[] mLFH.mExtraField; - mLFH.mExtraField = newExtra; - mLFH.mExtraFieldLength += padding; - } else { - /* create new field */ - mLFH.mExtraField = new unsigned char[padding]; - memset(mLFH.mExtraField, 0, padding); - mLFH.mExtraFieldLength = padding; - } - - return NO_ERROR; -} - -/* - * Set the fields in the LFH equal to the corresponding fields in the CDE. - * - * This does not touch the LFH "extra" field. - */ -void ZipEntry::copyCDEtoLFH(void) -{ - mLFH.mVersionToExtract = mCDE.mVersionToExtract; - mLFH.mGPBitFlag = mCDE.mGPBitFlag; - mLFH.mCompressionMethod = mCDE.mCompressionMethod; - mLFH.mLastModFileTime = mCDE.mLastModFileTime; - mLFH.mLastModFileDate = mCDE.mLastModFileDate; - mLFH.mCRC32 = mCDE.mCRC32; - mLFH.mCompressedSize = mCDE.mCompressedSize; - mLFH.mUncompressedSize = mCDE.mUncompressedSize; - mLFH.mFileNameLength = mCDE.mFileNameLength; - // the "extra field" is independent - - delete[] mLFH.mFileName; - if (mLFH.mFileNameLength > 0) { - mLFH.mFileName = new unsigned char[mLFH.mFileNameLength+1]; - strcpy((char*) mLFH.mFileName, (const char*) mCDE.mFileName); - } else { - mLFH.mFileName = NULL; - } -} - -/* - * Set some information about a file after we add it. - */ -void ZipEntry::setDataInfo(long uncompLen, long compLen, unsigned long crc32, - int compressionMethod) -{ - mCDE.mCompressionMethod = compressionMethod; - mCDE.mCRC32 = crc32; - mCDE.mCompressedSize = compLen; - mCDE.mUncompressedSize = uncompLen; - mCDE.mCompressionMethod = compressionMethod; - if (compressionMethod == kCompressDeflated) { - mCDE.mGPBitFlag |= 0x0002; // indicates maximum compression used - } - copyCDEtoLFH(); -} - -/* - * See if the data in mCDE and mLFH match up. This is mostly useful for - * debugging these classes, but it can be used to identify damaged - * archives. - * - * Returns "false" if they differ. - */ -bool ZipEntry::compareHeaders(void) const -{ - if (mCDE.mVersionToExtract != mLFH.mVersionToExtract) { - LOGV("cmp: VersionToExtract\n"); - return false; - } - if (mCDE.mGPBitFlag != mLFH.mGPBitFlag) { - LOGV("cmp: GPBitFlag\n"); - return false; - } - if (mCDE.mCompressionMethod != mLFH.mCompressionMethod) { - LOGV("cmp: CompressionMethod\n"); - return false; - } - if (mCDE.mLastModFileTime != mLFH.mLastModFileTime) { - LOGV("cmp: LastModFileTime\n"); - return false; - } - if (mCDE.mLastModFileDate != mLFH.mLastModFileDate) { - LOGV("cmp: LastModFileDate\n"); - return false; - } - if (mCDE.mCRC32 != mLFH.mCRC32) { - LOGV("cmp: CRC32\n"); - return false; - } - if (mCDE.mCompressedSize != mLFH.mCompressedSize) { - LOGV("cmp: CompressedSize\n"); - return false; - } - if (mCDE.mUncompressedSize != mLFH.mUncompressedSize) { - LOGV("cmp: UncompressedSize\n"); - return false; - } - if (mCDE.mFileNameLength != mLFH.mFileNameLength) { - LOGV("cmp: FileNameLength\n"); - return false; - } -#if 0 // this seems to be used for padding, not real data - if (mCDE.mExtraFieldLength != mLFH.mExtraFieldLength) { - LOGV("cmp: ExtraFieldLength\n"); - return false; - } -#endif - if (mCDE.mFileName != NULL) { - if (strcmp((char*) mCDE.mFileName, (char*) mLFH.mFileName) != 0) { - LOGV("cmp: FileName\n"); - return false; - } - } - - return true; -} - - -/* - * Convert the DOS date/time stamp into a UNIX time stamp. - */ -time_t ZipEntry::getModWhen(void) const -{ - struct tm parts; - - parts.tm_sec = (mCDE.mLastModFileTime & 0x001f) << 1; - parts.tm_min = (mCDE.mLastModFileTime & 0x07e0) >> 5; - parts.tm_hour = (mCDE.mLastModFileTime & 0xf800) >> 11; - parts.tm_mday = (mCDE.mLastModFileDate & 0x001f); - parts.tm_mon = ((mCDE.mLastModFileDate & 0x01e0) >> 5) -1; - parts.tm_year = ((mCDE.mLastModFileDate & 0xfe00) >> 9) + 80; - parts.tm_wday = parts.tm_yday = 0; - parts.tm_isdst = -1; // DST info "not available" - - return mktime(&parts); -} - -/* - * Set the CDE/LFH timestamp from UNIX time. - */ -void ZipEntry::setModWhen(time_t when) -{ -#ifdef HAVE_LOCALTIME_R - struct tm tmResult; -#endif - time_t even; - unsigned short zdate, ztime; - - struct tm* ptm; - - /* round up to an even number of seconds */ - even = (time_t)(((unsigned long)(when) + 1) & (~1)); - - /* expand */ -#ifdef HAVE_LOCALTIME_R - ptm = localtime_r(&even, &tmResult); -#else - ptm = localtime(&even); -#endif - - int year; - year = ptm->tm_year; - if (year < 80) - year = 80; - - zdate = (year - 80) << 9 | (ptm->tm_mon+1) << 5 | ptm->tm_mday; - ztime = ptm->tm_hour << 11 | ptm->tm_min << 5 | ptm->tm_sec >> 1; - - mCDE.mLastModFileTime = mLFH.mLastModFileTime = ztime; - mCDE.mLastModFileDate = mLFH.mLastModFileDate = zdate; -} - - -/* - * =========================================================================== - * ZipEntry::LocalFileHeader - * =========================================================================== - */ - -/* - * Read a local file header. - * - * On entry, "fp" points to the signature at the start of the header. - * On exit, "fp" points to the start of data. - */ -status_t ZipEntry::LocalFileHeader::read(FILE* fp) -{ - status_t result = NO_ERROR; - unsigned char buf[kLFHLen]; - - assert(mFileName == NULL); - assert(mExtraField == NULL); - - if (fread(buf, 1, kLFHLen, fp) != kLFHLen) { - result = UNKNOWN_ERROR; - goto bail; - } - - if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) { - LOGD("whoops: didn't find expected signature\n"); - result = UNKNOWN_ERROR; - goto bail; - } - - mVersionToExtract = ZipEntry::getShortLE(&buf[0x04]); - mGPBitFlag = ZipEntry::getShortLE(&buf[0x06]); - mCompressionMethod = ZipEntry::getShortLE(&buf[0x08]); - mLastModFileTime = ZipEntry::getShortLE(&buf[0x0a]); - mLastModFileDate = ZipEntry::getShortLE(&buf[0x0c]); - mCRC32 = ZipEntry::getLongLE(&buf[0x0e]); - mCompressedSize = ZipEntry::getLongLE(&buf[0x12]); - mUncompressedSize = ZipEntry::getLongLE(&buf[0x16]); - mFileNameLength = ZipEntry::getShortLE(&buf[0x1a]); - mExtraFieldLength = ZipEntry::getShortLE(&buf[0x1c]); - - // TODO: validate sizes - - /* grab filename */ - if (mFileNameLength != 0) { - mFileName = new unsigned char[mFileNameLength+1]; - if (mFileName == NULL) { - result = NO_MEMORY; - goto bail; - } - if (fread(mFileName, 1, mFileNameLength, fp) != mFileNameLength) { - result = UNKNOWN_ERROR; - goto bail; - } - mFileName[mFileNameLength] = '\0'; - } - - /* grab extra field */ - if (mExtraFieldLength != 0) { - mExtraField = new unsigned char[mExtraFieldLength+1]; - if (mExtraField == NULL) { - result = NO_MEMORY; - goto bail; - } - if (fread(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) { - result = UNKNOWN_ERROR; - goto bail; - } - mExtraField[mExtraFieldLength] = '\0'; - } - -bail: - return result; -} - -/* - * Write a local file header. - */ -status_t ZipEntry::LocalFileHeader::write(FILE* fp) -{ - unsigned char buf[kLFHLen]; - - ZipEntry::putLongLE(&buf[0x00], kSignature); - ZipEntry::putShortLE(&buf[0x04], mVersionToExtract); - ZipEntry::putShortLE(&buf[0x06], mGPBitFlag); - ZipEntry::putShortLE(&buf[0x08], mCompressionMethod); - ZipEntry::putShortLE(&buf[0x0a], mLastModFileTime); - ZipEntry::putShortLE(&buf[0x0c], mLastModFileDate); - ZipEntry::putLongLE(&buf[0x0e], mCRC32); - ZipEntry::putLongLE(&buf[0x12], mCompressedSize); - ZipEntry::putLongLE(&buf[0x16], mUncompressedSize); - ZipEntry::putShortLE(&buf[0x1a], mFileNameLength); - ZipEntry::putShortLE(&buf[0x1c], mExtraFieldLength); - - if (fwrite(buf, 1, kLFHLen, fp) != kLFHLen) - return UNKNOWN_ERROR; - - /* write filename */ - if (mFileNameLength != 0) { - if (fwrite(mFileName, 1, mFileNameLength, fp) != mFileNameLength) - return UNKNOWN_ERROR; - } - - /* write "extra field" */ - if (mExtraFieldLength != 0) { - if (fwrite(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) - return UNKNOWN_ERROR; - } - - return NO_ERROR; -} - - -/* - * Dump the contents of a LocalFileHeader object. - */ -void ZipEntry::LocalFileHeader::dump(void) const -{ - LOGD(" LocalFileHeader contents:\n"); - LOGD(" versToExt=%u gpBits=0x%04x compression=%u\n", - mVersionToExtract, mGPBitFlag, mCompressionMethod); - LOGD(" modTime=0x%04x modDate=0x%04x crc32=0x%08lx\n", - mLastModFileTime, mLastModFileDate, mCRC32); - LOGD(" compressedSize=%lu uncompressedSize=%lu\n", - mCompressedSize, mUncompressedSize); - LOGD(" filenameLen=%u extraLen=%u\n", - mFileNameLength, mExtraFieldLength); - if (mFileName != NULL) - LOGD(" filename: '%s'\n", mFileName); -} - - -/* - * =========================================================================== - * ZipEntry::CentralDirEntry - * =========================================================================== - */ - -/* - * Read the central dir entry that appears next in the file. - * - * On entry, "fp" should be positioned on the signature bytes for the - * entry. On exit, "fp" will point at the signature word for the next - * entry or for the EOCD. - */ -status_t ZipEntry::CentralDirEntry::read(FILE* fp) -{ - status_t result = NO_ERROR; - unsigned char buf[kCDELen]; - - /* no re-use */ - assert(mFileName == NULL); - assert(mExtraField == NULL); - assert(mFileComment == NULL); - - if (fread(buf, 1, kCDELen, fp) != kCDELen) { - result = UNKNOWN_ERROR; - goto bail; - } - - if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) { - LOGD("Whoops: didn't find expected signature\n"); - result = UNKNOWN_ERROR; - goto bail; - } - - mVersionMadeBy = ZipEntry::getShortLE(&buf[0x04]); - mVersionToExtract = ZipEntry::getShortLE(&buf[0x06]); - mGPBitFlag = ZipEntry::getShortLE(&buf[0x08]); - mCompressionMethod = ZipEntry::getShortLE(&buf[0x0a]); - mLastModFileTime = ZipEntry::getShortLE(&buf[0x0c]); - mLastModFileDate = ZipEntry::getShortLE(&buf[0x0e]); - mCRC32 = ZipEntry::getLongLE(&buf[0x10]); - mCompressedSize = ZipEntry::getLongLE(&buf[0x14]); - mUncompressedSize = ZipEntry::getLongLE(&buf[0x18]); - mFileNameLength = ZipEntry::getShortLE(&buf[0x1c]); - mExtraFieldLength = ZipEntry::getShortLE(&buf[0x1e]); - mFileCommentLength = ZipEntry::getShortLE(&buf[0x20]); - mDiskNumberStart = ZipEntry::getShortLE(&buf[0x22]); - mInternalAttrs = ZipEntry::getShortLE(&buf[0x24]); - mExternalAttrs = ZipEntry::getLongLE(&buf[0x26]); - mLocalHeaderRelOffset = ZipEntry::getLongLE(&buf[0x2a]); - - // TODO: validate sizes and offsets - - /* grab filename */ - if (mFileNameLength != 0) { - mFileName = new unsigned char[mFileNameLength+1]; - if (mFileName == NULL) { - result = NO_MEMORY; - goto bail; - } - if (fread(mFileName, 1, mFileNameLength, fp) != mFileNameLength) { - result = UNKNOWN_ERROR; - goto bail; - } - mFileName[mFileNameLength] = '\0'; - } - - /* read "extra field" */ - if (mExtraFieldLength != 0) { - mExtraField = new unsigned char[mExtraFieldLength+1]; - if (mExtraField == NULL) { - result = NO_MEMORY; - goto bail; - } - if (fread(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) { - result = UNKNOWN_ERROR; - goto bail; - } - mExtraField[mExtraFieldLength] = '\0'; - } - - - /* grab comment, if any */ - if (mFileCommentLength != 0) { - mFileComment = new unsigned char[mFileCommentLength+1]; - if (mFileComment == NULL) { - result = NO_MEMORY; - goto bail; - } - if (fread(mFileComment, 1, mFileCommentLength, fp) != mFileCommentLength) - { - result = UNKNOWN_ERROR; - goto bail; - } - mFileComment[mFileCommentLength] = '\0'; - } - -bail: - return result; -} - -/* - * Write a central dir entry. - */ -status_t ZipEntry::CentralDirEntry::write(FILE* fp) -{ - unsigned char buf[kCDELen]; - - ZipEntry::putLongLE(&buf[0x00], kSignature); - ZipEntry::putShortLE(&buf[0x04], mVersionMadeBy); - ZipEntry::putShortLE(&buf[0x06], mVersionToExtract); - ZipEntry::putShortLE(&buf[0x08], mGPBitFlag); - ZipEntry::putShortLE(&buf[0x0a], mCompressionMethod); - ZipEntry::putShortLE(&buf[0x0c], mLastModFileTime); - ZipEntry::putShortLE(&buf[0x0e], mLastModFileDate); - ZipEntry::putLongLE(&buf[0x10], mCRC32); - ZipEntry::putLongLE(&buf[0x14], mCompressedSize); - ZipEntry::putLongLE(&buf[0x18], mUncompressedSize); - ZipEntry::putShortLE(&buf[0x1c], mFileNameLength); - ZipEntry::putShortLE(&buf[0x1e], mExtraFieldLength); - ZipEntry::putShortLE(&buf[0x20], mFileCommentLength); - ZipEntry::putShortLE(&buf[0x22], mDiskNumberStart); - ZipEntry::putShortLE(&buf[0x24], mInternalAttrs); - ZipEntry::putLongLE(&buf[0x26], mExternalAttrs); - ZipEntry::putLongLE(&buf[0x2a], mLocalHeaderRelOffset); - - if (fwrite(buf, 1, kCDELen, fp) != kCDELen) - return UNKNOWN_ERROR; - - /* write filename */ - if (mFileNameLength != 0) { - if (fwrite(mFileName, 1, mFileNameLength, fp) != mFileNameLength) - return UNKNOWN_ERROR; - } - - /* write "extra field" */ - if (mExtraFieldLength != 0) { - if (fwrite(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) - return UNKNOWN_ERROR; - } - - /* write comment */ - if (mFileCommentLength != 0) { - if (fwrite(mFileComment, 1, mFileCommentLength, fp) != mFileCommentLength) - return UNKNOWN_ERROR; - } - - return NO_ERROR; -} - -/* - * Dump the contents of a CentralDirEntry object. - */ -void ZipEntry::CentralDirEntry::dump(void) const -{ - LOGD(" CentralDirEntry contents:\n"); - LOGD(" versMadeBy=%u versToExt=%u gpBits=0x%04x compression=%u\n", - mVersionMadeBy, mVersionToExtract, mGPBitFlag, mCompressionMethod); - LOGD(" modTime=0x%04x modDate=0x%04x crc32=0x%08lx\n", - mLastModFileTime, mLastModFileDate, mCRC32); - LOGD(" compressedSize=%lu uncompressedSize=%lu\n", - mCompressedSize, mUncompressedSize); - LOGD(" filenameLen=%u extraLen=%u commentLen=%u\n", - mFileNameLength, mExtraFieldLength, mFileCommentLength); - LOGD(" diskNumStart=%u intAttr=0x%04x extAttr=0x%08lx relOffset=%lu\n", - mDiskNumberStart, mInternalAttrs, mExternalAttrs, - mLocalHeaderRelOffset); - - if (mFileName != NULL) - LOGD(" filename: '%s'\n", mFileName); - if (mFileComment != NULL) - LOGD(" comment: '%s'\n", mFileComment); -} - diff --git a/libs/utils/ZipFile.cpp b/libs/utils/ZipFile.cpp deleted file mode 100644 index eaa0b208c..000000000 --- a/libs/utils/ZipFile.cpp +++ /dev/null @@ -1,1296 +0,0 @@ -/* - * Copyright (C) 2006 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. - */ - -// -// Access to Zip archives. -// - -#define LOG_TAG "zip" - -#include -#include -#include - -#include -#define DEF_MEM_LEVEL 8 // normally in zutil.h? - -#include -#include -#include -#include - -using namespace android; - -/* - * Some environments require the "b", some choke on it. - */ -#define FILE_OPEN_RO "rb" -#define FILE_OPEN_RW "r+b" -#define FILE_OPEN_RW_CREATE "w+b" - -/* should live somewhere else? */ -static status_t errnoToStatus(int err) -{ - if (err == ENOENT) - return NAME_NOT_FOUND; - else if (err == EACCES) - return PERMISSION_DENIED; - else - return UNKNOWN_ERROR; -} - -/* - * Open a file and parse its guts. - */ -status_t ZipFile::open(const char* zipFileName, int flags) -{ - bool newArchive = false; - - assert(mZipFp == NULL); // no reopen - - if ((flags & kOpenTruncate)) - flags |= kOpenCreate; // trunc implies create - - if ((flags & kOpenReadOnly) && (flags & kOpenReadWrite)) - return INVALID_OPERATION; // not both - if (!((flags & kOpenReadOnly) || (flags & kOpenReadWrite))) - return INVALID_OPERATION; // not neither - if ((flags & kOpenCreate) && !(flags & kOpenReadWrite)) - return INVALID_OPERATION; // create requires write - - if (flags & kOpenTruncate) { - newArchive = true; - } else { - newArchive = (access(zipFileName, F_OK) != 0); - if (!(flags & kOpenCreate) && newArchive) { - /* not creating, must already exist */ - LOGD("File %s does not exist", zipFileName); - return NAME_NOT_FOUND; - } - } - - /* open the file */ - const char* openflags; - if (flags & kOpenReadWrite) { - if (newArchive) - openflags = FILE_OPEN_RW_CREATE; - else - openflags = FILE_OPEN_RW; - } else { - openflags = FILE_OPEN_RO; - } - mZipFp = fopen(zipFileName, openflags); - if (mZipFp == NULL) { - int err = errno; - LOGD("fopen failed: %d\n", err); - return errnoToStatus(err); - } - - status_t result; - if (!newArchive) { - /* - * Load the central directory. If that fails, then this probably - * isn't a Zip archive. - */ - result = readCentralDir(); - } else { - /* - * Newly-created. The EndOfCentralDir constructor actually - * sets everything to be the way we want it (all zeroes). We - * set mNeedCDRewrite so that we create *something* if the - * caller doesn't add any files. (We could also just unlink - * the file if it's brand new and nothing was added, but that's - * probably doing more than we really should -- the user might - * have a need for empty zip files.) - */ - mNeedCDRewrite = true; - result = NO_ERROR; - } - - if (flags & kOpenReadOnly) - mReadOnly = true; - else - assert(!mReadOnly); - - return result; -} - -/* - * Return the Nth entry in the archive. - */ -ZipEntry* ZipFile::getEntryByIndex(int idx) const -{ - if (idx < 0 || idx >= (int) mEntries.size()) - return NULL; - - return mEntries[idx]; -} - -/* - * Find an entry by name. - */ -ZipEntry* ZipFile::getEntryByName(const char* fileName) const -{ - /* - * Do a stupid linear string-compare search. - * - * There are various ways to speed this up, especially since it's rare - * to intermingle changes to the archive with "get by name" calls. We - * don't want to sort the mEntries vector itself, however, because - * it's used to recreate the Central Directory. - * - * (Hash table works, parallel list of pointers in sorted order is good.) - */ - int idx; - - for (idx = mEntries.size()-1; idx >= 0; idx--) { - ZipEntry* pEntry = mEntries[idx]; - if (!pEntry->getDeleted() && - strcmp(fileName, pEntry->getFileName()) == 0) - { - return pEntry; - } - } - - return NULL; -} - -/* - * Empty the mEntries vector. - */ -void ZipFile::discardEntries(void) -{ - int count = mEntries.size(); - - while (--count >= 0) - delete mEntries[count]; - - mEntries.clear(); -} - - -/* - * Find the central directory and read the contents. - * - * The fun thing about ZIP archives is that they may or may not be - * readable from start to end. In some cases, notably for archives - * that were written to stdout, the only length information is in the - * central directory at the end of the file. - * - * Of course, the central directory can be followed by a variable-length - * comment field, so we have to scan through it backwards. The comment - * is at most 64K, plus we have 18 bytes for the end-of-central-dir stuff - * itself, plus apparently sometimes people throw random junk on the end - * just for the fun of it. - * - * This is all a little wobbly. If the wrong value ends up in the EOCD - * area, we're hosed. This appears to be the way that everbody handles - * it though, so we're in pretty good company if this fails. - */ -status_t ZipFile::readCentralDir(void) -{ - status_t result = NO_ERROR; - unsigned char* buf = NULL; - off_t fileLength, seekStart; - long readAmount; - int i; - - fseek(mZipFp, 0, SEEK_END); - fileLength = ftell(mZipFp); - rewind(mZipFp); - - /* too small to be a ZIP archive? */ - if (fileLength < EndOfCentralDir::kEOCDLen) { - LOGD("Length is %ld -- too small\n", (long)fileLength); - result = INVALID_OPERATION; - goto bail; - } - - buf = new unsigned char[EndOfCentralDir::kMaxEOCDSearch]; - if (buf == NULL) { - LOGD("Failure allocating %d bytes for EOCD search", - EndOfCentralDir::kMaxEOCDSearch); - result = NO_MEMORY; - goto bail; - } - - if (fileLength > EndOfCentralDir::kMaxEOCDSearch) { - seekStart = fileLength - EndOfCentralDir::kMaxEOCDSearch; - readAmount = EndOfCentralDir::kMaxEOCDSearch; - } else { - seekStart = 0; - readAmount = (long) fileLength; - } - if (fseek(mZipFp, seekStart, SEEK_SET) != 0) { - LOGD("Failure seeking to end of zip at %ld", (long) seekStart); - result = UNKNOWN_ERROR; - goto bail; - } - - /* read the last part of the file into the buffer */ - if (fread(buf, 1, readAmount, mZipFp) != (size_t) readAmount) { - LOGD("short file? wanted %ld\n", readAmount); - result = UNKNOWN_ERROR; - goto bail; - } - - /* find the end-of-central-dir magic */ - for (i = readAmount - 4; i >= 0; i--) { - if (buf[i] == 0x50 && - ZipEntry::getLongLE(&buf[i]) == EndOfCentralDir::kSignature) - { - LOGV("+++ Found EOCD at buf+%d\n", i); - break; - } - } - if (i < 0) { - LOGD("EOCD not found, not Zip\n"); - result = INVALID_OPERATION; - goto bail; - } - - /* extract eocd values */ - result = mEOCD.readBuf(buf + i, readAmount - i); - if (result != NO_ERROR) { - LOGD("Failure reading %ld bytes of EOCD values", readAmount - i); - goto bail; - } - //mEOCD.dump(); - - if (mEOCD.mDiskNumber != 0 || mEOCD.mDiskWithCentralDir != 0 || - mEOCD.mNumEntries != mEOCD.mTotalNumEntries) - { - LOGD("Archive spanning not supported\n"); - result = INVALID_OPERATION; - goto bail; - } - - /* - * So far so good. "mCentralDirSize" is the size in bytes of the - * central directory, so we can just seek back that far to find it. - * We can also seek forward mCentralDirOffset bytes from the - * start of the file. - * - * We're not guaranteed to have the rest of the central dir in the - * buffer, nor are we guaranteed that the central dir will have any - * sort of convenient size. We need to skip to the start of it and - * read the header, then the other goodies. - * - * The only thing we really need right now is the file comment, which - * we're hoping to preserve. - */ - if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) { - LOGD("Failure seeking to central dir offset %ld\n", - mEOCD.mCentralDirOffset); - result = UNKNOWN_ERROR; - goto bail; - } - - /* - * Loop through and read the central dir entries. - */ - LOGV("Scanning %d entries...\n", mEOCD.mTotalNumEntries); - int entry; - for (entry = 0; entry < mEOCD.mTotalNumEntries; entry++) { - ZipEntry* pEntry = new ZipEntry; - - result = pEntry->initFromCDE(mZipFp); - if (result != NO_ERROR) { - LOGD("initFromCDE failed\n"); - delete pEntry; - goto bail; - } - - mEntries.add(pEntry); - } - - - /* - * If all went well, we should now be back at the EOCD. - */ - { - unsigned char checkBuf[4]; - if (fread(checkBuf, 1, 4, mZipFp) != 4) { - LOGD("EOCD check read failed\n"); - result = INVALID_OPERATION; - goto bail; - } - if (ZipEntry::getLongLE(checkBuf) != EndOfCentralDir::kSignature) { - LOGD("EOCD read check failed\n"); - result = UNKNOWN_ERROR; - goto bail; - } - LOGV("+++ EOCD read check passed\n"); - } - -bail: - delete[] buf; - return result; -} - - -/* - * Add a new file to the archive. - * - * This requires creating and populating a ZipEntry structure, and copying - * the data into the file at the appropriate position. The "appropriate - * position" is the current location of the central directory, which we - * casually overwrite (we can put it back later). - * - * If we were concerned about safety, we would want to make all changes - * in a temp file and then overwrite the original after everything was - * safely written. Not really a concern for us. - */ -status_t ZipFile::addCommon(const char* fileName, const void* data, size_t size, - const char* storageName, int sourceType, int compressionMethod, - ZipEntry** ppEntry) -{ - ZipEntry* pEntry = NULL; - status_t result = NO_ERROR; - long lfhPosn, startPosn, endPosn, uncompressedLen; - FILE* inputFp = NULL; - unsigned long crc; - time_t modWhen; - - if (mReadOnly) - return INVALID_OPERATION; - - assert(compressionMethod == ZipEntry::kCompressDeflated || - compressionMethod == ZipEntry::kCompressStored); - - /* make sure we're in a reasonable state */ - assert(mZipFp != NULL); - assert(mEntries.size() == mEOCD.mTotalNumEntries); - - /* make sure it doesn't already exist */ - if (getEntryByName(storageName) != NULL) - return ALREADY_EXISTS; - - if (!data) { - inputFp = fopen(fileName, FILE_OPEN_RO); - if (inputFp == NULL) - return errnoToStatus(errno); - } - - if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) { - result = UNKNOWN_ERROR; - goto bail; - } - - pEntry = new ZipEntry; - pEntry->initNew(storageName, NULL); - - /* - * From here on out, failures are more interesting. - */ - mNeedCDRewrite = true; - - /* - * Write the LFH, even though it's still mostly blank. We need it - * as a place-holder. In theory the LFH isn't necessary, but in - * practice some utilities demand it. - */ - lfhPosn = ftell(mZipFp); - pEntry->mLFH.write(mZipFp); - startPosn = ftell(mZipFp); - - /* - * Copy the data in, possibly compressing it as we go. - */ - if (sourceType == ZipEntry::kCompressStored) { - if (compressionMethod == ZipEntry::kCompressDeflated) { - bool failed = false; - result = compressFpToFp(mZipFp, inputFp, data, size, &crc); - if (result != NO_ERROR) { - LOGD("compression failed, storing\n"); - failed = true; - } else { - /* - * Make sure it has compressed "enough". This probably ought - * to be set through an API call, but I don't expect our - * criteria to change over time. - */ - long src = inputFp ? ftell(inputFp) : size; - long dst = ftell(mZipFp) - startPosn; - if (dst + (dst / 10) > src) { - LOGD("insufficient compression (src=%ld dst=%ld), storing\n", - src, dst); - failed = true; - } - } - - if (failed) { - compressionMethod = ZipEntry::kCompressStored; - if (inputFp) rewind(inputFp); - fseek(mZipFp, startPosn, SEEK_SET); - /* fall through to kCompressStored case */ - } - } - /* handle "no compression" request, or failed compression from above */ - if (compressionMethod == ZipEntry::kCompressStored) { - if (inputFp) { - result = copyFpToFp(mZipFp, inputFp, &crc); - } else { - result = copyDataToFp(mZipFp, data, size, &crc); - } - if (result != NO_ERROR) { - // don't need to truncate; happens in CDE rewrite - LOGD("failed copying data in\n"); - goto bail; - } - } - - // currently seeked to end of file - uncompressedLen = inputFp ? ftell(inputFp) : size; - } else if (sourceType == ZipEntry::kCompressDeflated) { - /* we should support uncompressed-from-compressed, but it's not - * important right now */ - assert(compressionMethod == ZipEntry::kCompressDeflated); - - bool scanResult; - int method; - long compressedLen; - - scanResult = ZipUtils::examineGzip(inputFp, &method, &uncompressedLen, - &compressedLen, &crc); - if (!scanResult || method != ZipEntry::kCompressDeflated) { - LOGD("this isn't a deflated gzip file?"); - result = UNKNOWN_ERROR; - goto bail; - } - - result = copyPartialFpToFp(mZipFp, inputFp, compressedLen, NULL); - if (result != NO_ERROR) { - LOGD("failed copying gzip data in\n"); - goto bail; - } - } else { - assert(false); - result = UNKNOWN_ERROR; - goto bail; - } - - /* - * We could write the "Data Descriptor", but there doesn't seem to - * be any point since we're going to go back and write the LFH. - * - * Update file offsets. - */ - endPosn = ftell(mZipFp); // seeked to end of compressed data - - /* - * Success! Fill out new values. - */ - pEntry->setDataInfo(uncompressedLen, endPosn - startPosn, crc, - compressionMethod); - modWhen = getModTime(inputFp ? fileno(inputFp) : fileno(mZipFp)); - pEntry->setModWhen(modWhen); - pEntry->setLFHOffset(lfhPosn); - mEOCD.mNumEntries++; - mEOCD.mTotalNumEntries++; - mEOCD.mCentralDirSize = 0; // mark invalid; set by flush() - mEOCD.mCentralDirOffset = endPosn; - - /* - * Go back and write the LFH. - */ - if (fseek(mZipFp, lfhPosn, SEEK_SET) != 0) { - result = UNKNOWN_ERROR; - goto bail; - } - pEntry->mLFH.write(mZipFp); - - /* - * Add pEntry to the list. - */ - mEntries.add(pEntry); - if (ppEntry != NULL) - *ppEntry = pEntry; - pEntry = NULL; - -bail: - if (inputFp != NULL) - fclose(inputFp); - delete pEntry; - return result; -} - -/* - * Add an entry by copying it from another zip file. If "padding" is - * nonzero, the specified number of bytes will be added to the "extra" - * field in the header. - * - * If "ppEntry" is non-NULL, a pointer to the new entry will be returned. - */ -status_t ZipFile::add(const ZipFile* pSourceZip, const ZipEntry* pSourceEntry, - int padding, ZipEntry** ppEntry) -{ - ZipEntry* pEntry = NULL; - status_t result; - long lfhPosn, endPosn; - - if (mReadOnly) - return INVALID_OPERATION; - - /* make sure we're in a reasonable state */ - assert(mZipFp != NULL); - assert(mEntries.size() == mEOCD.mTotalNumEntries); - - if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) { - result = UNKNOWN_ERROR; - goto bail; - } - - pEntry = new ZipEntry; - if (pEntry == NULL) { - result = NO_MEMORY; - goto bail; - } - - result = pEntry->initFromExternal(pSourceZip, pSourceEntry); - if (result != NO_ERROR) - goto bail; - if (padding != 0) { - result = pEntry->addPadding(padding); - if (result != NO_ERROR) - goto bail; - } - - /* - * From here on out, failures are more interesting. - */ - mNeedCDRewrite = true; - - /* - * Write the LFH. Since we're not recompressing the data, we already - * have all of the fields filled out. - */ - lfhPosn = ftell(mZipFp); - pEntry->mLFH.write(mZipFp); - - /* - * Copy the data over. - * - * If the "has data descriptor" flag is set, we want to copy the DD - * fields as well. This is a fixed-size area immediately following - * the data. - */ - if (fseek(pSourceZip->mZipFp, pSourceEntry->getFileOffset(), SEEK_SET) != 0) - { - result = UNKNOWN_ERROR; - goto bail; - } - - off_t copyLen; - copyLen = pSourceEntry->getCompressedLen(); - if ((pSourceEntry->mLFH.mGPBitFlag & ZipEntry::kUsesDataDescr) != 0) - copyLen += ZipEntry::kDataDescriptorLen; - - if (copyPartialFpToFp(mZipFp, pSourceZip->mZipFp, copyLen, NULL) - != NO_ERROR) - { - LOGW("copy of '%s' failed\n", pEntry->mCDE.mFileName); - result = UNKNOWN_ERROR; - goto bail; - } - - /* - * Update file offsets. - */ - endPosn = ftell(mZipFp); - - /* - * Success! Fill out new values. - */ - pEntry->setLFHOffset(lfhPosn); // sets mCDE.mLocalHeaderRelOffset - mEOCD.mNumEntries++; - mEOCD.mTotalNumEntries++; - mEOCD.mCentralDirSize = 0; // mark invalid; set by flush() - mEOCD.mCentralDirOffset = endPosn; - - /* - * Add pEntry to the list. - */ - mEntries.add(pEntry); - if (ppEntry != NULL) - *ppEntry = pEntry; - pEntry = NULL; - - result = NO_ERROR; - -bail: - delete pEntry; - return result; -} - -/* - * Copy all of the bytes in "src" to "dst". - * - * On exit, "srcFp" will be seeked to the end of the file, and "dstFp" - * will be seeked immediately past the data. - */ -status_t ZipFile::copyFpToFp(FILE* dstFp, FILE* srcFp, unsigned long* pCRC32) -{ - unsigned char tmpBuf[32768]; - size_t count; - - *pCRC32 = crc32(0L, Z_NULL, 0); - - while (1) { - count = fread(tmpBuf, 1, sizeof(tmpBuf), srcFp); - if (ferror(srcFp) || ferror(dstFp)) - return errnoToStatus(errno); - if (count == 0) - break; - - *pCRC32 = crc32(*pCRC32, tmpBuf, count); - - if (fwrite(tmpBuf, 1, count, dstFp) != count) { - LOGD("fwrite %d bytes failed\n", (int) count); - return UNKNOWN_ERROR; - } - } - - return NO_ERROR; -} - -/* - * Copy all of the bytes in "src" to "dst". - * - * On exit, "dstFp" will be seeked immediately past the data. - */ -status_t ZipFile::copyDataToFp(FILE* dstFp, - const void* data, size_t size, unsigned long* pCRC32) -{ - size_t count; - - *pCRC32 = crc32(0L, Z_NULL, 0); - if (size > 0) { - *pCRC32 = crc32(*pCRC32, (const unsigned char*)data, size); - if (fwrite(data, 1, size, dstFp) != size) { - LOGD("fwrite %d bytes failed\n", (int) size); - return UNKNOWN_ERROR; - } - } - - return NO_ERROR; -} - -/* - * Copy some of the bytes in "src" to "dst". - * - * If "pCRC32" is NULL, the CRC will not be computed. - * - * On exit, "srcFp" will be seeked to the end of the file, and "dstFp" - * will be seeked immediately past the data just written. - */ -status_t ZipFile::copyPartialFpToFp(FILE* dstFp, FILE* srcFp, long length, - unsigned long* pCRC32) -{ - unsigned char tmpBuf[32768]; - size_t count; - - if (pCRC32 != NULL) - *pCRC32 = crc32(0L, Z_NULL, 0); - - while (length) { - long readSize; - - readSize = sizeof(tmpBuf); - if (readSize > length) - readSize = length; - - count = fread(tmpBuf, 1, readSize, srcFp); - if ((long) count != readSize) { // error or unexpected EOF - LOGD("fread %d bytes failed\n", (int) readSize); - return UNKNOWN_ERROR; - } - - if (pCRC32 != NULL) - *pCRC32 = crc32(*pCRC32, tmpBuf, count); - - if (fwrite(tmpBuf, 1, count, dstFp) != count) { - LOGD("fwrite %d bytes failed\n", (int) count); - return UNKNOWN_ERROR; - } - - length -= readSize; - } - - return NO_ERROR; -} - -/* - * Compress all of the data in "srcFp" and write it to "dstFp". - * - * On exit, "srcFp" will be seeked to the end of the file, and "dstFp" - * will be seeked immediately past the compressed data. - */ -status_t ZipFile::compressFpToFp(FILE* dstFp, FILE* srcFp, - const void* data, size_t size, unsigned long* pCRC32) -{ - status_t result = NO_ERROR; - const size_t kBufSize = 32768; - unsigned char* inBuf = NULL; - unsigned char* outBuf = NULL; - z_stream zstream; - bool atEof = false; // no feof() aviailable yet - unsigned long crc; - int zerr; - - /* - * Create an input buffer and an output buffer. - */ - inBuf = new unsigned char[kBufSize]; - outBuf = new unsigned char[kBufSize]; - if (inBuf == NULL || outBuf == NULL) { - result = NO_MEMORY; - goto bail; - } - - /* - * Initialize the zlib stream. - */ - memset(&zstream, 0, sizeof(zstream)); - zstream.zalloc = Z_NULL; - zstream.zfree = Z_NULL; - zstream.opaque = Z_NULL; - zstream.next_in = NULL; - zstream.avail_in = 0; - zstream.next_out = outBuf; - zstream.avail_out = kBufSize; - zstream.data_type = Z_UNKNOWN; - - zerr = deflateInit2(&zstream, Z_BEST_COMPRESSION, - Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY); - if (zerr != Z_OK) { - result = UNKNOWN_ERROR; - if (zerr == Z_VERSION_ERROR) { - LOGE("Installed zlib is not compatible with linked version (%s)\n", - ZLIB_VERSION); - } else { - LOGD("Call to deflateInit2 failed (zerr=%d)\n", zerr); - } - goto bail; - } - - crc = crc32(0L, Z_NULL, 0); - - /* - * Loop while we have data. - */ - do { - size_t getSize; - int flush; - - /* only read if the input buffer is empty */ - if (zstream.avail_in == 0 && !atEof) { - LOGV("+++ reading %d bytes\n", (int)kBufSize); - if (data) { - getSize = size > kBufSize ? kBufSize : size; - memcpy(inBuf, data, getSize); - data = ((const char*)data) + getSize; - size -= getSize; - } else { - getSize = fread(inBuf, 1, kBufSize, srcFp); - if (ferror(srcFp)) { - LOGD("deflate read failed (errno=%d)\n", errno); - goto z_bail; - } - } - if (getSize < kBufSize) { - LOGV("+++ got %d bytes, EOF reached\n", - (int)getSize); - atEof = true; - } - - crc = crc32(crc, inBuf, getSize); - - zstream.next_in = inBuf; - zstream.avail_in = getSize; - } - - if (atEof) - flush = Z_FINISH; /* tell zlib that we're done */ - else - flush = Z_NO_FLUSH; /* more to come! */ - - zerr = deflate(&zstream, flush); - if (zerr != Z_OK && zerr != Z_STREAM_END) { - LOGD("zlib deflate call failed (zerr=%d)\n", zerr); - result = UNKNOWN_ERROR; - goto z_bail; - } - - /* write when we're full or when we're done */ - if (zstream.avail_out == 0 || - (zerr == Z_STREAM_END && zstream.avail_out != (uInt) kBufSize)) - { - LOGV("+++ writing %d bytes\n", (int) (zstream.next_out - outBuf)); - if (fwrite(outBuf, 1, zstream.next_out - outBuf, dstFp) != - (size_t)(zstream.next_out - outBuf)) - { - LOGD("write %d failed in deflate\n", - (int) (zstream.next_out - outBuf)); - goto z_bail; - } - - zstream.next_out = outBuf; - zstream.avail_out = kBufSize; - } - } while (zerr == Z_OK); - - assert(zerr == Z_STREAM_END); /* other errors should've been caught */ - - *pCRC32 = crc; - -z_bail: - deflateEnd(&zstream); /* free up any allocated structures */ - -bail: - delete[] inBuf; - delete[] outBuf; - - return result; -} - -/* - * Mark an entry as deleted. - * - * We will eventually need to crunch the file down, but if several files - * are being removed (perhaps as part of an "update" process) we can make - * things considerably faster by deferring the removal to "flush" time. - */ -status_t ZipFile::remove(ZipEntry* pEntry) -{ - /* - * Should verify that pEntry is actually part of this archive, and - * not some stray ZipEntry from a different file. - */ - - /* mark entry as deleted, and mark archive as dirty */ - pEntry->setDeleted(); - mNeedCDRewrite = true; - return NO_ERROR; -} - -/* - * Flush any pending writes. - * - * In particular, this will crunch out deleted entries, and write the - * Central Directory and EOCD if we have stomped on them. - */ -status_t ZipFile::flush(void) -{ - status_t result = NO_ERROR; - long eocdPosn; - int i, count; - - if (mReadOnly) - return INVALID_OPERATION; - if (!mNeedCDRewrite) - return NO_ERROR; - - assert(mZipFp != NULL); - - result = crunchArchive(); - if (result != NO_ERROR) - return result; - - if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) - return UNKNOWN_ERROR; - - count = mEntries.size(); - for (i = 0; i < count; i++) { - ZipEntry* pEntry = mEntries[i]; - pEntry->mCDE.write(mZipFp); - } - - eocdPosn = ftell(mZipFp); - mEOCD.mCentralDirSize = eocdPosn - mEOCD.mCentralDirOffset; - - mEOCD.write(mZipFp); - - /* - * If we had some stuff bloat up during compression and get replaced - * with plain files, or if we deleted some entries, there's a lot - * of wasted space at the end of the file. Remove it now. - */ - if (ftruncate(fileno(mZipFp), ftell(mZipFp)) != 0) { - LOGW("ftruncate failed %ld: %s\n", ftell(mZipFp), strerror(errno)); - // not fatal - } - - /* should we clear the "newly added" flag in all entries now? */ - - mNeedCDRewrite = false; - return NO_ERROR; -} - -/* - * Crunch deleted files out of an archive by shifting the later files down. - * - * Because we're not using a temp file, we do the operation inside the - * current file. - */ -status_t ZipFile::crunchArchive(void) -{ - status_t result = NO_ERROR; - int i, count; - long delCount, adjust; - -#if 0 - printf("CONTENTS:\n"); - for (i = 0; i < (int) mEntries.size(); i++) { - printf(" %d: lfhOff=%ld del=%d\n", - i, mEntries[i]->getLFHOffset(), mEntries[i]->getDeleted()); - } - printf(" END is %ld\n", (long) mEOCD.mCentralDirOffset); -#endif - - /* - * Roll through the set of files, shifting them as appropriate. We - * could probably get a slight performance improvement by sliding - * multiple files down at once (because we could use larger reads - * when operating on batches of small files), but it's not that useful. - */ - count = mEntries.size(); - delCount = adjust = 0; - for (i = 0; i < count; i++) { - ZipEntry* pEntry = mEntries[i]; - long span; - - if (pEntry->getLFHOffset() != 0) { - long nextOffset; - - /* Get the length of this entry by finding the offset - * of the next entry. Directory entries don't have - * file offsets, so we need to find the next non-directory - * entry. - */ - nextOffset = 0; - for (int ii = i+1; nextOffset == 0 && ii < count; ii++) - nextOffset = mEntries[ii]->getLFHOffset(); - if (nextOffset == 0) - nextOffset = mEOCD.mCentralDirOffset; - span = nextOffset - pEntry->getLFHOffset(); - - assert(span >= ZipEntry::LocalFileHeader::kLFHLen); - } else { - /* This is a directory entry. It doesn't have - * any actual file contents, so there's no need to - * move anything. - */ - span = 0; - } - - //printf("+++ %d: off=%ld span=%ld del=%d [count=%d]\n", - // i, pEntry->getLFHOffset(), span, pEntry->getDeleted(), count); - - if (pEntry->getDeleted()) { - adjust += span; - delCount++; - - delete pEntry; - mEntries.removeAt(i); - - /* adjust loop control */ - count--; - i--; - } else if (span != 0 && adjust > 0) { - /* shuffle this entry back */ - //printf("+++ Shuffling '%s' back %ld\n", - // pEntry->getFileName(), adjust); - result = filemove(mZipFp, pEntry->getLFHOffset() - adjust, - pEntry->getLFHOffset(), span); - if (result != NO_ERROR) { - /* this is why you use a temp file */ - LOGE("error during crunch - archive is toast\n"); - return result; - } - - pEntry->setLFHOffset(pEntry->getLFHOffset() - adjust); - } - } - - /* - * Fix EOCD info. We have to wait until the end to do some of this - * because we use mCentralDirOffset to determine "span" for the - * last entry. - */ - mEOCD.mCentralDirOffset -= adjust; - mEOCD.mNumEntries -= delCount; - mEOCD.mTotalNumEntries -= delCount; - mEOCD.mCentralDirSize = 0; // mark invalid; set by flush() - - assert(mEOCD.mNumEntries == mEOCD.mTotalNumEntries); - assert(mEOCD.mNumEntries == count); - - return result; -} - -/* - * Works like memmove(), but on pieces of a file. - */ -status_t ZipFile::filemove(FILE* fp, off_t dst, off_t src, size_t n) -{ - if (dst == src || n <= 0) - return NO_ERROR; - - unsigned char readBuf[32768]; - - if (dst < src) { - /* shift stuff toward start of file; must read from start */ - while (n != 0) { - size_t getSize = sizeof(readBuf); - if (getSize > n) - getSize = n; - - if (fseek(fp, (long) src, SEEK_SET) != 0) { - LOGD("filemove src seek %ld failed\n", (long) src); - return UNKNOWN_ERROR; - } - - if (fread(readBuf, 1, getSize, fp) != getSize) { - LOGD("filemove read %ld off=%ld failed\n", - (long) getSize, (long) src); - return UNKNOWN_ERROR; - } - - if (fseek(fp, (long) dst, SEEK_SET) != 0) { - LOGD("filemove dst seek %ld failed\n", (long) dst); - return UNKNOWN_ERROR; - } - - if (fwrite(readBuf, 1, getSize, fp) != getSize) { - LOGD("filemove write %ld off=%ld failed\n", - (long) getSize, (long) dst); - return UNKNOWN_ERROR; - } - - src += getSize; - dst += getSize; - n -= getSize; - } - } else { - /* shift stuff toward end of file; must read from end */ - assert(false); // write this someday, maybe - return UNKNOWN_ERROR; - } - - return NO_ERROR; -} - - -/* - * Get the modification time from a file descriptor. - */ -time_t ZipFile::getModTime(int fd) -{ - struct stat sb; - - if (fstat(fd, &sb) < 0) { - LOGD("HEY: fstat on fd %d failed\n", fd); - return (time_t) -1; - } - - return sb.st_mtime; -} - - -#if 0 /* this is a bad idea */ -/* - * Get a copy of the Zip file descriptor. - * - * We don't allow this if the file was opened read-write because we tend - * to leave the file contents in an uncertain state between calls to - * flush(). The duplicated file descriptor should only be valid for reads. - */ -int ZipFile::getZipFd(void) const -{ - if (!mReadOnly) - return INVALID_OPERATION; - assert(mZipFp != NULL); - - int fd; - fd = dup(fileno(mZipFp)); - if (fd < 0) { - LOGD("didn't work, errno=%d\n", errno); - } - - return fd; -} -#endif - - -#if 0 -/* - * Expand data. - */ -bool ZipFile::uncompress(const ZipEntry* pEntry, void* buf) const -{ - return false; -} -#endif - -// free the memory when you're done -void* ZipFile::uncompress(const ZipEntry* entry) -{ - size_t unlen = entry->getUncompressedLen(); - size_t clen = entry->getCompressedLen(); - - void* buf = malloc(unlen); - if (buf == NULL) { - return NULL; - } - - fseek(mZipFp, 0, SEEK_SET); - - off_t offset = entry->getFileOffset(); - if (fseek(mZipFp, offset, SEEK_SET) != 0) { - goto bail; - } - - switch (entry->getCompressionMethod()) - { - case ZipEntry::kCompressStored: { - ssize_t amt = fread(buf, 1, unlen, mZipFp); - if (amt != (ssize_t)unlen) { - goto bail; - } -#if 0 - printf("data...\n"); - const unsigned char* p = (unsigned char*)buf; - const unsigned char* end = p+unlen; - for (int i=0; i<32 && p < end; i++) { - printf("0x%08x ", (int)(offset+(i*0x10))); - for (int j=0; j<0x10 && p < end; j++) { - printf(" %02x", *p); - p++; - } - printf("\n"); - } -#endif - - } - break; - case ZipEntry::kCompressDeflated: { - if (!ZipUtils::inflateToBuffer(mZipFp, buf, unlen, clen)) { - goto bail; - } - } - break; - default: - goto bail; - } - return buf; - -bail: - free(buf); - return NULL; -} - - -/* - * =========================================================================== - * ZipFile::EndOfCentralDir - * =========================================================================== - */ - -/* - * Read the end-of-central-dir fields. - * - * "buf" should be positioned at the EOCD signature, and should contain - * the entire EOCD area including the comment. - */ -status_t ZipFile::EndOfCentralDir::readBuf(const unsigned char* buf, int len) -{ - /* don't allow re-use */ - assert(mComment == NULL); - - if (len < kEOCDLen) { - /* looks like ZIP file got truncated */ - LOGD(" Zip EOCD: expected >= %d bytes, found %d\n", - kEOCDLen, len); - return INVALID_OPERATION; - } - - /* this should probably be an assert() */ - if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) - return UNKNOWN_ERROR; - - mDiskNumber = ZipEntry::getShortLE(&buf[0x04]); - mDiskWithCentralDir = ZipEntry::getShortLE(&buf[0x06]); - mNumEntries = ZipEntry::getShortLE(&buf[0x08]); - mTotalNumEntries = ZipEntry::getShortLE(&buf[0x0a]); - mCentralDirSize = ZipEntry::getLongLE(&buf[0x0c]); - mCentralDirOffset = ZipEntry::getLongLE(&buf[0x10]); - mCommentLen = ZipEntry::getShortLE(&buf[0x14]); - - // TODO: validate mCentralDirOffset - - if (mCommentLen > 0) { - if (kEOCDLen + mCommentLen > len) { - LOGD("EOCD(%d) + comment(%d) exceeds len (%d)\n", - kEOCDLen, mCommentLen, len); - return UNKNOWN_ERROR; - } - mComment = new unsigned char[mCommentLen]; - memcpy(mComment, buf + kEOCDLen, mCommentLen); - } - - return NO_ERROR; -} - -/* - * Write an end-of-central-directory section. - */ -status_t ZipFile::EndOfCentralDir::write(FILE* fp) -{ - unsigned char buf[kEOCDLen]; - - ZipEntry::putLongLE(&buf[0x00], kSignature); - ZipEntry::putShortLE(&buf[0x04], mDiskNumber); - ZipEntry::putShortLE(&buf[0x06], mDiskWithCentralDir); - ZipEntry::putShortLE(&buf[0x08], mNumEntries); - ZipEntry::putShortLE(&buf[0x0a], mTotalNumEntries); - ZipEntry::putLongLE(&buf[0x0c], mCentralDirSize); - ZipEntry::putLongLE(&buf[0x10], mCentralDirOffset); - ZipEntry::putShortLE(&buf[0x14], mCommentLen); - - if (fwrite(buf, 1, kEOCDLen, fp) != kEOCDLen) - return UNKNOWN_ERROR; - if (mCommentLen > 0) { - assert(mComment != NULL); - if (fwrite(mComment, mCommentLen, 1, fp) != mCommentLen) - return UNKNOWN_ERROR; - } - - return NO_ERROR; -} - -/* - * Dump the contents of an EndOfCentralDir object. - */ -void ZipFile::EndOfCentralDir::dump(void) const -{ - LOGD(" EndOfCentralDir contents:\n"); - LOGD(" diskNum=%u diskWCD=%u numEnt=%u totalNumEnt=%u\n", - mDiskNumber, mDiskWithCentralDir, mNumEntries, mTotalNumEntries); - LOGD(" centDirSize=%lu centDirOff=%lu commentLen=%u\n", - mCentralDirSize, mCentralDirOffset, mCommentLen); -} - From 14d978d02d93f8976ff8b01c2f827fc7c83f4fbd Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Fri, 5 Jun 2009 15:11:23 -0700 Subject: [PATCH 120/541] get rid of LogSocket which wasn't even implemented (enabled) --- include/utils/LogSocket.h | 20 ------ libs/utils/Android.mk | 3 +- libs/utils/LogSocket.cpp | 129 -------------------------------------- 3 files changed, 1 insertion(+), 151 deletions(-) delete mode 100644 include/utils/LogSocket.h delete mode 100644 libs/utils/LogSocket.cpp diff --git a/include/utils/LogSocket.h b/include/utils/LogSocket.h deleted file mode 100644 index 01fbfb50e..000000000 --- a/include/utils/LogSocket.h +++ /dev/null @@ -1,20 +0,0 @@ -/* utils/LogSocket.h -** -** Copyright 2008, The Android Open Source Project -** -** This file is dual licensed. It may be redistributed and/or modified -** under the terms of the Apache 2.0 License OR version 2 of the GNU -** General Public License. -*/ - -#ifndef _UTILS_LOGSOCKET_H -#define _UTILS_LOGSOCKET_H - -#define SOCKET_CLOSE_LOCAL 0 - -void add_send_stats(int fd, int send); -void add_recv_stats(int fd, int recv); -void log_socket_close(int fd, short reason); -void log_socket_connect(int fd, unsigned int ip, unsigned short port); - -#endif /* _UTILS_LOGSOCKET_H */ diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk index 70d440797..3f5cb85ac 100644 --- a/libs/utils/Android.mk +++ b/libs/utils/Android.mk @@ -41,8 +41,7 @@ commonSources:= \ ZipFileCRO.cpp \ ZipFileRO.cpp \ ZipUtils.cpp \ - misc.cpp \ - LogSocket.cpp + misc.cpp # For the host diff --git a/libs/utils/LogSocket.cpp b/libs/utils/LogSocket.cpp deleted file mode 100644 index 55c1b99af..000000000 --- a/libs/utils/LogSocket.cpp +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#ifndef HAVE_WINSOCK -//#define SOCKETLOG -#endif - -#ifdef SOCKETLOG - -#define LOG_TAG "SOCKETLOG" - -#include -#include -#include "utils/LogSocket.h" -#include "utils/logger.h" -#include "cutils/hashmap.h" - -// defined in //device/data/etc/event-log-tags -#define SOCKET_CLOSE_LOG 51000 - -static Hashmap* statsMap = NULL; - -#define LOG_LIST_NUMBER 5 - -typedef struct SocketStats { - int fd; - unsigned int send; - unsigned int recv; - unsigned int ip; - unsigned short port; - short reason; -}SocketStats; - -SocketStats *get_socket_stats(int fd) { - if (statsMap == NULL) { - statsMap = hashmapCreate(8, &hashmapIntHash, &hashmapIntEquals); - } - - SocketStats *s = (SocketStats*) hashmapGet(statsMap, &fd); - if (s == NULL) { - // LOGD("create SocketStats for fd %d", fd); - s = (SocketStats*) malloc(sizeof(SocketStats)); - memset(s, 0, sizeof(SocketStats)); - s->fd = fd; - hashmapPut(statsMap, &s->fd, s); - } - return s; -} - -void log_socket_connect(int fd, unsigned int ip, unsigned short port) { - // LOGD("log_socket_connect for fd %d ip %d port%d", fd, ip, port); - SocketStats *s = get_socket_stats(fd); - s->ip = ip; - s->port = port; -} - -void add_send_stats(int fd, int send) { - if (send <=0) { - LOGE("add_send_stats send %d", send); - return; - } - SocketStats *s = get_socket_stats(fd); - s->send += send; - // LOGD("add_send_stats for fd %d ip %d port%d", fd, s->ip, s->port); -} - -void add_recv_stats(int fd, int recv) { - if (recv <=0) { - LOGE("add_recv_stats recv %d", recv); - return; - } - SocketStats *s = get_socket_stats(fd); - s->recv += recv; - // LOGD("add_recv_stats for fd %d ip %d port%d", fd, s->ip, s->port); -} - -char* put_int(char* buf, int value) { - *buf = EVENT_TYPE_INT; - buf++; - memcpy(buf, &value, sizeof(int)); - return buf + sizeof(int); -} - -void log_socket_close(int fd, short reason) { - if (statsMap) { - SocketStats *s = (SocketStats*) hashmapGet(statsMap, &fd); - if (s != NULL) { - if (s->send != 0 || s->recv != 0) { - s->reason = reason; - // 5 int + list type need 2 bytes - char buf[LOG_LIST_NUMBER * 5 + 2]; - buf[0] = EVENT_TYPE_LIST; - buf[1] = LOG_LIST_NUMBER; - char* writePos = buf + 2; - writePos = put_int(writePos, s->send); - writePos = put_int(writePos, s->recv); - writePos = put_int(writePos, s->ip); - writePos = put_int(writePos, s->port); - writePos = put_int(writePos, s->reason); - - android_bWriteLog(SOCKET_CLOSE_LOG, buf, sizeof(buf)); - // LOGD("send %d recv %d reason %d", s->send, s->recv, s->reason); - } - hashmapRemove(statsMap, &s->fd); - free(s); - } - } -} - -#else -void add_send_stats(int fd, int send) {} -void add_recv_stats(int fd, int recv) {} -void log_socket_close(int fd, short reason) {} -void log_socket_connect(int fd, unsigned int ip, unsigned short port) {} -#endif From 0d1d7ae49a69cd885b7728add8be0bf6984dc5f8 Mon Sep 17 00:00:00 2001 From: Joe Onorato Date: Wed, 10 Jun 2009 17:07:15 -0700 Subject: [PATCH 121/541] Fix SharedPrefsBackupHelper so it doesn't hard code the paths to the files. This took quite a bit of refactoring. --- include/utils/BackupHelpers.h | 3 +- libs/utils/BackupHelpers.cpp | 196 +++++++++++++++++++++++----------- 2 files changed, 136 insertions(+), 63 deletions(-) diff --git a/include/utils/BackupHelpers.h b/include/utils/BackupHelpers.h index 24b6c9e60..1d0daa741 100644 --- a/include/utils/BackupHelpers.h +++ b/include/utils/BackupHelpers.h @@ -118,7 +118,7 @@ private: }; int back_up_files(int oldSnapshotFD, BackupDataWriter* dataStream, int newSnapshotFD, - char const* fileBase, char const* const* files, int fileCount); + char const* const* files, char const* const *keys, int fileCount); #define TEST_BACKUP_HELPERS 1 @@ -127,6 +127,7 @@ int back_up_files(int oldSnapshotFD, BackupDataWriter* dataStream, int newSnapsh int backup_helper_test_empty(); int backup_helper_test_four(); int backup_helper_test_files(); +int backup_helper_test_null_base(); int backup_helper_test_data_writer(); int backup_helper_test_data_reader(); #endif diff --git a/libs/utils/BackupHelpers.cpp b/libs/utils/BackupHelpers.cpp index 7f423a8e8..ffe4dff9f 100644 --- a/libs/utils/BackupHelpers.cpp +++ b/libs/utils/BackupHelpers.cpp @@ -41,8 +41,8 @@ namespace android { #define MAGIC0 0x70616e53 // Snap #define MAGIC1 0x656c6946 // File -#if 0 // TEST_BACKUP_HELPERS -#define LOGP(x...) printf(x) +#if 1 // TEST_BACKUP_HELPERS +#define LOGP(f, x...) printf(f "\n", x) #else #define LOGP(x...) LOGD(x) #endif @@ -62,6 +62,11 @@ struct FileState { int nameLen; }; +struct FileRec { + char const* file; // this object does not own this string + FileState s; +}; + const static int ROUND_UP[4] = { 0, 3, 2, 1 }; static inline int @@ -92,8 +97,8 @@ read_snapshot_file(int fd, KeyedVector* snapshot) FileState file; char filenameBuf[128]; - amt = read(fd, &file, sizeof(file)); - if (amt != sizeof(file)) { + amt = read(fd, &file, sizeof(FileState)); + if (amt != sizeof(FileState)) { LOGW("read_snapshot_file FileState truncated/error with read at %d bytes\n", bytesRead); return 1; } @@ -128,7 +133,7 @@ read_snapshot_file(int fd, KeyedVector* snapshot) } static int -write_snapshot_file(int fd, const KeyedVector& snapshot) +write_snapshot_file(int fd, const KeyedVector& snapshot) { int bytesWritten = sizeof(SnapshotHeader); // preflight size @@ -151,11 +156,11 @@ write_snapshot_file(int fd, const KeyedVector& snapshot) for (int i=0; i oldSnapshot; - KeyedVector newSnapshot; + KeyedVector newSnapshot; if (oldSnapshotFD != -1) { err = read_snapshot_file(oldSnapshotFD, &oldSnapshot); @@ -297,26 +300,28 @@ back_up_files(int oldSnapshotFD, BackupDataWriter* dataStream, int newSnapshotFD } for (int i=0; i= 0) { + LOGP("back_up_files key already in use '%s'", key.string()); + return -1; + } + newSnapshot.add(key, r); } int n = 0; @@ -329,43 +334,39 @@ back_up_files(int oldSnapshotFD, BackupDataWriter* dataStream, int newSnapshotFD int cmp = p.compare(q); if (cmp > 0) { // file added - String8 realFilename(base); - realFilename.appendPath(q); - LOGP("file added: %s\n", realFilename.string()); - write_update_file(dataStream, q, realFilename); + const FileRec& g = newSnapshot.valueAt(m); + LOGP("file added: %s", g.file); + write_update_file(dataStream, q, g.file); m++; } else if (cmp < 0) { // file removed - LOGP("file removed: %s\n", p.string()); + LOGP("file removed: %s", p.string()); dataStream->WriteEntityHeader(p, -1); n++; } else { - // both files exist, check them - String8 realFilename(base); - realFilename.appendPath(q); const FileState& f = oldSnapshot.valueAt(n); - FileState& g = newSnapshot.editValueAt(m); + FileRec& g = newSnapshot.editValueAt(m); - int fd = open(realFilename.string(), O_RDONLY); + int fd = open(g.file, O_RDONLY); if (fd < 0) { // We can't open the file. Don't report it as a delete either. Let the // server keep the old version. Maybe they'll be able to deal with it // on restore. - LOGP("Unable to open file %s - skipping", realFilename.string()); + LOGP("Unable to open file %s - skipping", g.file); } else { - g.crc32 = compute_crc32(fd); + g.s.crc32 = compute_crc32(fd); - LOGP("%s\n", q.string()); - LOGP(" new: modTime=%d,%d size=%-3d crc32=0x%08x\n", + LOGP("%s", q.string()); + LOGP(" new: modTime=%d,%d size=%-3d crc32=0x%08x", f.modTime_sec, f.modTime_nsec, f.size, f.crc32); - LOGP(" old: modTime=%d,%d size=%-3d crc32=0x%08x\n", - g.modTime_sec, g.modTime_nsec, g.size, g.crc32); - if (f.modTime_sec != g.modTime_sec || f.modTime_nsec != g.modTime_nsec - || f.size != g.size || f.crc32 != g.crc32) { - write_update_file(dataStream, fd, p, realFilename); + LOGP(" old: modTime=%d,%d size=%-3d crc32=0x%08x", + g.s.modTime_sec, g.s.modTime_nsec, g.s.size, g.s.crc32); + if (f.modTime_sec != g.s.modTime_sec || f.modTime_nsec != g.s.modTime_nsec + || f.size != g.s.size || f.crc32 != g.s.crc32) { + write_update_file(dataStream, fd, p, g.file); } close(fd); @@ -384,9 +385,8 @@ back_up_files(int oldSnapshotFD, BackupDataWriter* dataStream, int newSnapshotFD // these were added while (m snapshot; + KeyedVector snapshot; const char* filename = SCRATCH_DIR "backup_helper_test_empty.snap"; system("rm -r " SCRATCH_DIR); @@ -534,7 +534,7 @@ backup_helper_test_four() { int err; int fd; - KeyedVector snapshot; + KeyedVector snapshot; const char* filename = SCRATCH_DIR "backup_helper_test_four.snap"; system("rm -r " SCRATCH_DIR); @@ -549,38 +549,44 @@ backup_helper_test_four() String8 filenames[4]; FileState states[4]; + FileRec r; + r.file = NULL; states[0].modTime_sec = 0xfedcba98; states[0].modTime_nsec = 0xdeadbeef; states[0].size = 0xababbcbc; states[0].crc32 = 0x12345678; states[0].nameLen = -12; + r.s = states[0]; filenames[0] = String8("bytes_of_padding"); - snapshot.add(filenames[0], states[0]); + snapshot.add(filenames[0], r); states[1].modTime_sec = 0x93400031; states[1].modTime_nsec = 0xdeadbeef; states[1].size = 0x88557766; states[1].crc32 = 0x22334422; states[1].nameLen = -1; + r.s = states[1]; filenames[1] = String8("bytes_of_padding3"); - snapshot.add(filenames[1], states[1]); + snapshot.add(filenames[1], r); states[2].modTime_sec = 0x33221144; states[2].modTime_nsec = 0xdeadbeef; states[2].size = 0x11223344; states[2].crc32 = 0x01122334; states[2].nameLen = 0; + r.s = states[2]; filenames[2] = String8("bytes_of_padding_2"); - snapshot.add(filenames[2], states[2]); + snapshot.add(filenames[2], r); states[3].modTime_sec = 0x33221144; states[3].modTime_nsec = 0xdeadbeef; states[3].size = 0x11223344; states[3].crc32 = 0x01122334; states[3].nameLen = 0; + r.s = states[3]; filenames[3] = String8("bytes_of_padding__1"); - snapshot.add(filenames[3], states[3]); + snapshot.add(filenames[3], r); err = write_snapshot_file(fd, snapshot); @@ -982,6 +988,14 @@ backup_helper_test_files() write_text_file(SCRATCH_DIR "data/h", "h\nhh\n"); char const* files_before[] = { + SCRATCH_DIR "data/b", + SCRATCH_DIR "data/c", + SCRATCH_DIR "data/d", + SCRATCH_DIR "data/e", + SCRATCH_DIR "data/f" + }; + + char const* keys_before[] = { "data/b", "data/c", "data/d", @@ -1004,7 +1018,7 @@ backup_helper_test_files() { BackupDataWriter dataStream(dataStreamFD); - err = back_up_files(-1, &dataStream, newSnapshotFD, SCRATCH_DIR, files_before, 5); + err = back_up_files(-1, &dataStream, newSnapshotFD, files_before, keys_before, 5); if (err != 0) { return err; } @@ -1035,6 +1049,15 @@ backup_helper_test_files() unlink(SCRATCH_DIR "data/f"); char const* files_after[] = { + SCRATCH_DIR "data/a", // added + SCRATCH_DIR "data/b", // same + SCRATCH_DIR "data/c", // different mod time + SCRATCH_DIR "data/d", // different size (same mod time) + SCRATCH_DIR "data/e", // different contents (same mod time, same size) + SCRATCH_DIR "data/g" // added + }; + + char const* keys_after[] = { "data/a", // added "data/b", // same "data/c", // different mod time @@ -1064,8 +1087,7 @@ backup_helper_test_files() { BackupDataWriter dataStream(dataStreamFD); - err = back_up_files(oldSnapshotFD, &dataStream, newSnapshotFD, SCRATCH_DIR, - files_after, 6); + err = back_up_files(oldSnapshotFD, &dataStream, newSnapshotFD, files_after, keys_after, 6); if (err != 0) { return err; } @@ -1078,6 +1100,56 @@ backup_helper_test_files() return 0; } +int +backup_helper_test_null_base() +{ + int err; + int oldSnapshotFD; + int dataStreamFD; + int newSnapshotFD; + + system("rm -r " SCRATCH_DIR); + mkdir(SCRATCH_DIR, 0777); + mkdir(SCRATCH_DIR "data", 0777); + + write_text_file(SCRATCH_DIR "data/a", "a\naa\n"); + + char const* files[] = { + SCRATCH_DIR "data/a", + }; + + char const* keys[] = { + "a", + }; + + dataStreamFD = creat(SCRATCH_DIR "null_base.data", 0666); + if (dataStreamFD == -1) { + fprintf(stderr, "error creating: %s\n", strerror(errno)); + return errno; + } + + newSnapshotFD = creat(SCRATCH_DIR "null_base.snap", 0666); + if (newSnapshotFD == -1) { + fprintf(stderr, "error creating: %s\n", strerror(errno)); + return errno; + } + + { + BackupDataWriter dataStream(dataStreamFD); + + err = back_up_files(-1, &dataStream, newSnapshotFD, files, keys, 1); + if (err != 0) { + return err; + } + } + + close(dataStreamFD); + close(newSnapshotFD); + + return 0; +} + + #endif // TEST_BACKUP_HELPERS } From 9becbac780d2389c552c4776688c258321e7c938 Mon Sep 17 00:00:00 2001 From: Joe Onorato Date: Thu, 11 Jun 2009 11:27:16 -0700 Subject: [PATCH 122/541] Make the file backup helper not crash if a file you requested can't be stated. This means you don't need to know if the files you are backing up exist or not -- we'll figure it out for you. --- include/utils/BackupHelpers.h | 1 + libs/utils/BackupHelpers.cpp | 153 ++++++++++++++++++++++++---------- 2 files changed, 109 insertions(+), 45 deletions(-) diff --git a/include/utils/BackupHelpers.h b/include/utils/BackupHelpers.h index 1d0daa741..f60f4ead6 100644 --- a/include/utils/BackupHelpers.h +++ b/include/utils/BackupHelpers.h @@ -128,6 +128,7 @@ int backup_helper_test_empty(); int backup_helper_test_four(); int backup_helper_test_files(); int backup_helper_test_null_base(); +int backup_helper_test_missing_file(); int backup_helper_test_data_writer(); int backup_helper_test_data_reader(); #endif diff --git a/libs/utils/BackupHelpers.cpp b/libs/utils/BackupHelpers.cpp index ffe4dff9f..4c3e37dd1 100644 --- a/libs/utils/BackupHelpers.cpp +++ b/libs/utils/BackupHelpers.cpp @@ -64,6 +64,7 @@ struct FileState { struct FileRec { char const* file; // this object does not own this string + bool deleted; FileState s; }; @@ -135,18 +136,23 @@ read_snapshot_file(int fd, KeyedVector* snapshot) static int write_snapshot_file(int fd, const KeyedVector& snapshot) { + int fileCount = 0; int bytesWritten = sizeof(SnapshotHeader); // preflight size const int N = snapshot.size(); for (int i=0; i& snapshot) return errno; } - for (int i=0; i= 0) { - LOGP("back_up_files key already in use '%s'", key.string()); - return -1; + if (newSnapshot.indexOfKey(key) >= 0) { + LOGP("back_up_files key already in use '%s'", key.string()); + return -1; + } } newSnapshot.add(key, r); } @@ -331,24 +340,24 @@ back_up_files(int oldSnapshotFD, BackupDataWriter* dataStream, int newSnapshotFD while (n 0) { + if (g.deleted || cmp < 0) { + // file removed + LOGP("file removed: %s", p.string()); + g.deleted = true; // They didn't mention the file, but we noticed that it's gone. + dataStream->WriteEntityHeader(p, -1); + n++; + } + else if (cmp > 0) { // file added - const FileRec& g = newSnapshot.valueAt(m); LOGP("file added: %s", g.file); write_update_file(dataStream, q, g.file); m++; } - else if (cmp < 0) { - // file removed - LOGP("file removed: %s", p.string()); - dataStream->WriteEntityHeader(p, -1); - n++; - } else { // both files exist, check them const FileState& f = oldSnapshot.valueAt(n); - FileRec& g = newSnapshot.editValueAt(m); int fd = open(g.file, O_RDONLY); if (fd < 0) { @@ -550,6 +559,7 @@ backup_helper_test_four() String8 filenames[4]; FileState states[4]; FileRec r; + r.deleted = false; r.file = NULL; states[0].modTime_sec = 0xfedcba98; @@ -1149,6 +1159,59 @@ backup_helper_test_null_base() return 0; } +int +backup_helper_test_missing_file() +{ + int err; + int oldSnapshotFD; + int dataStreamFD; + int newSnapshotFD; + + system("rm -r " SCRATCH_DIR); + mkdir(SCRATCH_DIR, 0777); + mkdir(SCRATCH_DIR "data", 0777); + + write_text_file(SCRATCH_DIR "data/b", "b\nbb\n"); + + char const* files[] = { + SCRATCH_DIR "data/a", + SCRATCH_DIR "data/b", + SCRATCH_DIR "data/c", + }; + + char const* keys[] = { + "a", + "b", + "c", + }; + + dataStreamFD = creat(SCRATCH_DIR "null_base.data", 0666); + if (dataStreamFD == -1) { + fprintf(stderr, "error creating: %s\n", strerror(errno)); + return errno; + } + + newSnapshotFD = creat(SCRATCH_DIR "null_base.snap", 0666); + if (newSnapshotFD == -1) { + fprintf(stderr, "error creating: %s\n", strerror(errno)); + return errno; + } + + { + BackupDataWriter dataStream(dataStreamFD); + + err = back_up_files(-1, &dataStream, newSnapshotFD, files, keys, 1); + if (err != 0) { + return err; + } + } + + close(dataStreamFD); + close(newSnapshotFD); + + return 0; +} + #endif // TEST_BACKUP_HELPERS From 6890e0bc52eff1419619f7fab7594e9a0af03b6e Mon Sep 17 00:00:00 2001 From: Joe Onorato Date: Fri, 12 Jun 2009 11:06:24 -0700 Subject: [PATCH 123/541] Add RestoreFileHelper, BackupDataInput, and add java wrappers for the methods on BackupDataOutput. --- libs/utils/BackupData.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/utils/BackupData.cpp b/libs/utils/BackupData.cpp index 120f23d36..8c9f87528 100644 --- a/libs/utils/BackupData.cpp +++ b/libs/utils/BackupData.cpp @@ -327,7 +327,7 @@ BackupDataReader::ReadAppHeader(String8* packageName, int* cookie) } size_t size = m_header.app.packageLen; char* buf = packageName->lockBuffer(size); - if (packageName == NULL) { + if (buf == NULL) { packageName->unlockBuffer(); m_status = ENOMEM; return m_status; From 1781fd1ae77d28917f91daa265449532f8329c8a Mon Sep 17 00:00:00 2001 From: Joe Onorato Date: Tue, 16 Jun 2009 16:31:35 -0400 Subject: [PATCH 124/541] checkpoint BackupDatAInput / RestoreHelper --- include/utils/BackupHelpers.h | 27 +----- libs/utils/BackupData.cpp | 151 ++++------------------------------ libs/utils/BackupHelpers.cpp | 98 ++++++---------------- 3 files changed, 44 insertions(+), 232 deletions(-) diff --git a/include/utils/BackupHelpers.h b/include/utils/BackupHelpers.h index f60f4ead6..3ca8ad2d5 100644 --- a/include/utils/BackupHelpers.h +++ b/include/utils/BackupHelpers.h @@ -23,30 +23,15 @@ namespace android { enum { - BACKUP_HEADER_APP_V1 = 0x31707041, // App1 (little endian) BACKUP_HEADER_ENTITY_V1 = 0x61746144, // Data (little endian) - BACKUP_FOOTER_APP_V1 = 0x746f6f46, // Foot (little endian) }; -// the sizes of all of these match. -typedef struct { - int type; // == BACKUP_HEADER_APP_V1 - int packageLen; // length of the name of the package that follows, not including the null. - int cookie; -} app_header_v1; - typedef struct { int type; // BACKUP_HEADER_ENTITY_V1 int keyLen; // length of the key name, not including the null terminator int dataSize; // size of the data, not including the padding, -1 means delete } entity_header_v1; -typedef struct { - int type; // BACKUP_FOOTER_APP_V1 - int entityCount; // the number of entities that were written - int cookie; -} app_footer_v1; - /** * Writes the data. @@ -61,13 +46,9 @@ public: // does not close fd ~BackupDataWriter(); - status_t WriteAppHeader(const String8& packageName, int cookie); - status_t WriteEntityHeader(const String8& key, size_t dataSize); status_t WriteEntityData(const void* data, size_t size); - status_t WriteAppFooter(int cookie); - private: explicit BackupDataWriter(); status_t write_padding_for(int n); @@ -92,28 +73,26 @@ public: ~BackupDataReader(); status_t Status(); - status_t ReadNextHeader(int* type = NULL); + status_t ReadNextHeader(bool* done, int* type); - status_t ReadAppHeader(String8* packageName, int* cookie); bool HasEntities(); status_t ReadEntityHeader(String8* key, size_t* dataSize); status_t SkipEntityData(); // must be called with the pointer at the begining of the data. status_t ReadEntityData(void* data, size_t size); - status_t ReadAppFooter(int* cookie); private: explicit BackupDataReader(); status_t skip_padding(); int m_fd; + bool m_done; status_t m_status; ssize_t m_pos; + ssize_t m_dataEndPos; int m_entityCount; union { int type; - app_header_v1 app; entity_header_v1 entity; - app_footer_v1 footer; } m_header; }; diff --git a/libs/utils/BackupData.cpp b/libs/utils/BackupData.cpp index 8c9f87528..16ff1e55e 100644 --- a/libs/utils/BackupData.cpp +++ b/libs/utils/BackupData.cpp @@ -85,46 +85,6 @@ BackupDataWriter::write_padding_for(int n) return NO_ERROR; } -status_t -BackupDataWriter::WriteAppHeader(const String8& packageName, int cookie) -{ - if (m_status != NO_ERROR) { - return m_status; - } - - ssize_t amt; - - amt = write_padding_for(m_pos); - if (amt != 0) { - return amt; - } - - app_header_v1 header; - ssize_t nameLen; - - nameLen = packageName.length(); - - header.type = tolel(BACKUP_HEADER_APP_V1); - header.packageLen = tolel(nameLen); - header.cookie = cookie; - - amt = write(m_fd, &header, sizeof(app_header_v1)); - if (amt != sizeof(app_header_v1)) { - m_status = errno; - return m_status; - } - m_pos += amt; - - amt = write(m_fd, packageName.string(), nameLen+1); - if (amt != nameLen+1) { - m_status = errno; - return m_status; - } - m_pos += amt; - - return NO_ERROR; -} - status_t BackupDataWriter::WriteEntityHeader(const String8& key, size_t dataSize) { @@ -188,40 +148,11 @@ BackupDataWriter::WriteEntityData(const void* data, size_t size) return NO_ERROR; } -status_t -BackupDataWriter::WriteAppFooter(int cookie) -{ - if (m_status != NO_ERROR) { - return m_status; - } - - ssize_t amt; - - amt = write_padding_for(m_pos); - if (amt != 0) { - return amt; - } - - app_footer_v1 footer; - ssize_t nameLen; - - footer.type = tolel(BACKUP_FOOTER_APP_V1); - footer.entityCount = tolel(m_entityCount); - footer.cookie = cookie; - - amt = write(m_fd, &footer, sizeof(app_footer_v1)); - if (amt != sizeof(app_footer_v1)) { - m_status = errno; - return m_status; - } - m_pos += amt; - - return NO_ERROR; -} BackupDataReader::BackupDataReader(int fd) :m_fd(fd), + m_done(false), m_status(NO_ERROR), m_pos(0), m_entityCount(0) @@ -260,31 +191,25 @@ BackupDataReader::Status() } while(0) status_t -BackupDataReader::ReadNextHeader(int* type) +BackupDataReader::ReadNextHeader(bool* done, int* type) { + *done = m_done; if (m_status != NO_ERROR) { return m_status; } int amt; - SKIP_PADDING(); + // No error checking here, in case we're at the end of the stream. Just let read() fail. + skip_padding(); amt = read(m_fd, &m_header, sizeof(m_header)); + *done = m_done = (amt == 0); CHECK_SIZE(amt, sizeof(m_header)); // validate and fix up the fields. m_header.type = fromlel(m_header.type); switch (m_header.type) { - case BACKUP_HEADER_APP_V1: - m_header.app.packageLen = fromlel(m_header.app.packageLen); - if (m_header.app.packageLen < 0) { - LOGD("App header at %d has packageLen<0: 0x%08x\n", (int)m_pos, - (int)m_header.app.packageLen); - m_status = EINVAL; - } - m_header.app.cookie = m_header.app.cookie; - break; case BACKUP_HEADER_ENTITY_V1: m_header.entity.keyLen = fromlel(m_header.entity.keyLen); if (m_header.entity.keyLen <= 0) { @@ -295,15 +220,6 @@ BackupDataReader::ReadNextHeader(int* type) m_header.entity.dataSize = fromlel(m_header.entity.dataSize); m_entityCount++; break; - case BACKUP_FOOTER_APP_V1: - m_header.footer.entityCount = fromlel(m_header.footer.entityCount); - if (m_header.footer.entityCount < 0) { - LOGD("Entity header at %d has entityCount<0: 0x%08x\n", (int)m_pos, - (int)m_header.footer.entityCount); - m_status = EINVAL; - } - m_header.footer.cookie = m_header.footer.cookie; - break; default: LOGD("Chunk header at %d has invalid type: 0x%08x", (int)m_pos, (int)m_header.type); m_status = EINVAL; @@ -316,30 +232,6 @@ BackupDataReader::ReadNextHeader(int* type) return m_status; } -status_t -BackupDataReader::ReadAppHeader(String8* packageName, int* cookie) -{ - if (m_status != NO_ERROR) { - return m_status; - } - if (m_header.type != BACKUP_HEADER_APP_V1) { - return EINVAL; - } - size_t size = m_header.app.packageLen; - char* buf = packageName->lockBuffer(size); - if (buf == NULL) { - packageName->unlockBuffer(); - m_status = ENOMEM; - return m_status; - } - int amt = read(m_fd, buf, size+1); - CHECK_SIZE(amt, (int)size+1); - packageName->unlockBuffer(size); - m_pos += size+1; - *cookie = m_header.app.cookie; - return NO_ERROR; -} - bool BackupDataReader::HasEntities() { @@ -368,6 +260,7 @@ BackupDataReader::ReadEntityHeader(String8* key, size_t* dataSize) m_pos += size+1; *dataSize = m_header.entity.dataSize; SKIP_PADDING(); + m_dataEndPos = m_pos + *dataSize; return NO_ERROR; } @@ -381,7 +274,7 @@ BackupDataReader::SkipEntityData() return EINVAL; } if (m_header.entity.dataSize > 0) { - int pos = lseek(m_fd, m_header.entity.dataSize, SEEK_CUR); + int pos = lseek(m_fd, m_dataEndPos, SEEK_SET); return pos == -1 ? (int)errno : (int)NO_ERROR; } else { return NO_ERROR; @@ -394,31 +287,21 @@ BackupDataReader::ReadEntityData(void* data, size_t size) if (m_status != NO_ERROR) { return m_status; } + int remaining = m_dataEndPos - m_pos; + if (size > remaining) { + printf("size=%d m_pos=0x%x m_dataEndPos=0x%x remaining=%d\n", + size, m_pos, m_dataEndPos, remaining); + size = remaining; + } + if (remaining <= 0) { + return 0; + } int amt = read(m_fd, data, size); CHECK_SIZE(amt, (int)size); m_pos += size; return NO_ERROR; } -status_t -BackupDataReader::ReadAppFooter(int* cookie) -{ - if (m_status != NO_ERROR) { - return m_status; - } - if (m_header.type != BACKUP_FOOTER_APP_V1) { - return EINVAL; - } - if (m_header.footer.entityCount != m_entityCount) { - LOGD("entity count mismatch actual=%d expected=%d", m_entityCount, - m_header.footer.entityCount); - m_status = EINVAL; - return m_status; - } - *cookie = m_header.footer.cookie; - return NO_ERROR; -} - status_t BackupDataReader::skip_padding() { diff --git a/libs/utils/BackupHelpers.cpp b/libs/utils/BackupHelpers.cpp index 4c3e37dd1..c1d5404ee 100644 --- a/libs/utils/BackupHelpers.cpp +++ b/libs/utils/BackupHelpers.cpp @@ -689,41 +689,27 @@ backup_helper_test_four() // hexdump -v -e '" " 8/1 " 0x%02x," "\n"' data_writer.data const unsigned char DATA_GOLDEN_FILE[] = { - 0x41, 0x70, 0x70, 0x31, 0x0b, 0x00, 0x00, 0x00, - 0xdd, 0xcc, 0xbb, 0xaa, 0x6e, 0x6f, 0x5f, 0x70, - 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x00, 0x44, 0x61, 0x74, 0x61, 0x0b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x6e, 0x6f, 0x5f, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x00, 0x6e, 0x6f, 0x5f, 0x70, 0x61, 0x64, 0x64, 0x69, - 0x6e, 0x67, 0x5f, 0x00, 0x41, 0x70, 0x70, 0x31, - 0x0c, 0x00, 0x00, 0x00, 0xdd, 0xcc, 0xbb, 0xaa, + 0x6e, 0x67, 0x5f, 0x00, 0x44, 0x61, 0x74, 0x61, + 0x0c, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x70, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x74, 0x6f, 0x5f, 0x5f, 0x33, 0x00, 0xbc, 0xbc, 0xbc, - 0x44, 0x61, 0x74, 0x61, 0x0c, 0x00, 0x00, 0x00, - 0x0d, 0x00, 0x00, 0x00, 0x70, 0x61, 0x64, 0x64, - 0x65, 0x64, 0x5f, 0x74, 0x6f, 0x5f, 0x5f, 0x33, - 0x00, 0xbc, 0xbc, 0xbc, 0x70, 0x61, 0x64, 0x64, - 0x65, 0x64, 0x5f, 0x74, 0x6f, 0x5f, 0x5f, 0x33, - 0x00, 0xbc, 0xbc, 0xbc, 0x41, 0x70, 0x70, 0x31, - 0x0d, 0x00, 0x00, 0x00, 0xdd, 0xcc, 0xbb, 0xaa, 0x70, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x74, - 0x6f, 0x5f, 0x32, 0x5f, 0x5f, 0x00, 0xbc, 0xbc, + 0x6f, 0x5f, 0x5f, 0x33, 0x00, 0xbc, 0xbc, 0xbc, 0x44, 0x61, 0x74, 0x61, 0x0d, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x70, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x74, 0x6f, 0x5f, 0x32, 0x5f, 0x5f, 0x00, 0xbc, 0xbc, 0x70, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x74, 0x6f, 0x5f, 0x32, 0x5f, - 0x5f, 0x00, 0xbc, 0xbc, 0x41, 0x70, 0x70, 0x31, - 0x0a, 0x00, 0x00, 0x00, 0xdd, 0xcc, 0xbb, 0xaa, - 0x70, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x74, - 0x6f, 0x31, 0x00, 0xbc, 0x44, 0x61, 0x74, 0x61, + 0x5f, 0x00, 0xbc, 0xbc, 0x44, 0x61, 0x74, 0x61, 0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x70, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x74, 0x6f, 0x31, 0x00, 0xbc, 0x70, 0x61, 0x64, 0x64, - 0x65, 0x64, 0x5f, 0x74, 0x6f, 0x31, 0x00, 0xbc, - 0x46, 0x6f, 0x6f, 0x74, 0x04, 0x00, 0x00, 0x00, - 0x99, 0x99, 0x77, 0x77 + 0x65, 0x64, 0x5f, 0x74, 0x6f, 0x31, 0x00 + }; const int DATA_GOLDEN_FILE_SIZE = sizeof(DATA_GOLDEN_FILE); @@ -733,12 +719,6 @@ test_write_header_and_entity(BackupDataWriter& writer, const char* str) int err; String8 text(str); - err = writer.WriteAppHeader(text, 0xaabbccdd); - if (err != 0) { - fprintf(stderr, "WriteAppHeader failed with %s\n", strerror(err)); - return err; - } - err = writer.WriteEntityHeader(text, text.length()+1); if (err != 0) { fprintf(stderr, "WriteEntityHeader failed with %s\n", strerror(err)); @@ -779,8 +759,6 @@ backup_helper_test_data_writer() err |= test_write_header_and_entity(writer, "padded_to_2__"); err |= test_write_header_and_entity(writer, "padded_to1"); - writer.WriteAppFooter(0x77779999); - close(fd); err = compare_file(filename, DATA_GOLDEN_FILE, DATA_GOLDEN_FILE_SIZE); @@ -800,70 +778,59 @@ test_read_header_and_entity(BackupDataReader& reader, const char* str) String8 string; int cookie = 0x11111111; size_t actualSize; + bool done; + int type; // printf("\n\n---------- test_read_header_and_entity -- %s\n\n", str); - err = reader.ReadNextHeader(); + err = reader.ReadNextHeader(&done, &type); + if (done) { + fprintf(stderr, "should not be done yet\n"); + goto finished; + } if (err != 0) { fprintf(stderr, "ReadNextHeader (for app header) failed with %s\n", strerror(err)); - goto done; + goto finished; } - - err = reader.ReadAppHeader(&string, &cookie); - if (err != 0) { - fprintf(stderr, "ReadAppHeader failed with %s\n", strerror(err)); - goto done; - } - if (string != str) { - fprintf(stderr, "ReadAppHeader expected packageName '%s' got '%s'\n", str, string.string()); + if (type != BACKUP_HEADER_ENTITY_V1) { err = EINVAL; - goto done; - } - if (cookie != (int)0xaabbccdd) { - fprintf(stderr, "ReadAppHeader expected cookie 0x%08x got 0x%08x\n", 0xaabbccdd, cookie); - err = EINVAL; - goto done; - } - - err = reader.ReadNextHeader(); - if (err != 0) { - fprintf(stderr, "ReadNextHeader (for entity header) failed with %s\n", strerror(err)); - goto done; + fprintf(stderr, "type=0x%08x expected 0x%08x\n", type, BACKUP_HEADER_ENTITY_V1); } err = reader.ReadEntityHeader(&string, &actualSize); if (err != 0) { fprintf(stderr, "ReadEntityHeader failed with %s\n", strerror(err)); - goto done; + goto finished; } if (string != str) { fprintf(stderr, "ReadEntityHeader expected key '%s' got '%s'\n", str, string.string()); err = EINVAL; - goto done; + goto finished; } if ((int)actualSize != bufSize) { fprintf(stderr, "ReadEntityHeader expected dataSize 0x%08x got 0x%08x\n", bufSize, actualSize); err = EINVAL; - goto done; + goto finished; } err = reader.ReadEntityData(buf, bufSize); if (err != NO_ERROR) { fprintf(stderr, "ReadEntityData failed with %s\n", strerror(err)); - goto done; + goto finished; } if (0 != memcmp(buf, str, bufSize)) { fprintf(stderr, "ReadEntityData expected '%s' but got something starting with " - "%02x %02x %02x %02x\n", str, buf[0], buf[1], buf[2], buf[3]); + "%02x %02x %02x %02x '%c%c%c%c'\n", str, buf[0], buf[1], buf[2], buf[3], + buf[0], buf[1], buf[2], buf[3]); err = EINVAL; - goto done; + goto finished; } // The next read will confirm whether it got the right amount of data. -done: +finished: if (err != NO_ERROR) { fprintf(stderr, "test_read_header_and_entity failed with %s\n", strerror(err)); } @@ -923,23 +890,6 @@ backup_helper_test_data_reader() if (err == NO_ERROR) { err = test_read_header_and_entity(reader, "padded_to1"); } - - if (err == NO_ERROR) { - err = reader.ReadNextHeader(); - if (err != 0) { - fprintf(stderr, "ReadNextHeader (for app header) failed with %s\n", strerror(err)); - } - - if (err == NO_ERROR) { - int cookie; - err |= reader.ReadAppFooter(&cookie); - if (cookie != 0x77779999) { - fprintf(stderr, "app footer cookie expected=0x%08x actual=0x%08x\n", - 0x77779999, cookie); - err = EINVAL; - } - } - } } close(fd); From f9f76ac29179b9eee521760e4f876b7c15a20dba Mon Sep 17 00:00:00 2001 From: Joe Onorato Date: Wed, 17 Jun 2009 16:20:55 -0700 Subject: [PATCH 125/541] FileRestoreHelper and RestoreHelperDispatcher work. --- include/utils/BackupHelpers.h | 2 +- libs/utils/BackupData.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/utils/BackupHelpers.h b/include/utils/BackupHelpers.h index 3ca8ad2d5..fa7f8d5cf 100644 --- a/include/utils/BackupHelpers.h +++ b/include/utils/BackupHelpers.h @@ -78,7 +78,7 @@ public: bool HasEntities(); status_t ReadEntityHeader(String8* key, size_t* dataSize); status_t SkipEntityData(); // must be called with the pointer at the begining of the data. - status_t ReadEntityData(void* data, size_t size); + ssize_t ReadEntityData(void* data, size_t size); private: explicit BackupDataReader(); diff --git a/libs/utils/BackupData.cpp b/libs/utils/BackupData.cpp index 16ff1e55e..34b37edd2 100644 --- a/libs/utils/BackupData.cpp +++ b/libs/utils/BackupData.cpp @@ -281,16 +281,16 @@ BackupDataReader::SkipEntityData() } } -status_t +ssize_t BackupDataReader::ReadEntityData(void* data, size_t size) { if (m_status != NO_ERROR) { return m_status; } int remaining = m_dataEndPos - m_pos; + //LOGD("ReadEntityData size=%d m_pos=0x%x m_dataEndPos=0x%x remaining=%d\n", + // size, m_pos, m_dataEndPos, remaining); if (size > remaining) { - printf("size=%d m_pos=0x%x m_dataEndPos=0x%x remaining=%d\n", - size, m_pos, m_dataEndPos, remaining); size = remaining; } if (remaining <= 0) { @@ -299,7 +299,7 @@ BackupDataReader::ReadEntityData(void* data, size_t size) int amt = read(m_fd, data, size); CHECK_SIZE(amt, (int)size); m_pos += size; - return NO_ERROR; + return amt; } status_t From b90ae5522a1be6bd25894f5773f5e19a4e7a0d9e Mon Sep 17 00:00:00 2001 From: Joe Onorato Date: Thu, 18 Jun 2009 13:11:18 -0700 Subject: [PATCH 126/541] Make RestoreHelper and friends also write out the snapshot state. --- include/utils/BackupHelpers.h | 35 +++++++++++ libs/utils/BackupHelpers.cpp | 115 +++++++++++++++++++++++++--------- 2 files changed, 121 insertions(+), 29 deletions(-) diff --git a/include/utils/BackupHelpers.h b/include/utils/BackupHelpers.h index fa7f8d5cf..fc701fd3a 100644 --- a/include/utils/BackupHelpers.h +++ b/include/utils/BackupHelpers.h @@ -19,6 +19,7 @@ #include #include +#include namespace android { @@ -32,6 +33,27 @@ typedef struct { int dataSize; // size of the data, not including the padding, -1 means delete } entity_header_v1; +struct SnapshotHeader { + int magic0; + int fileCount; + int magic1; + int totalSize; +}; + +struct FileState { + int modTime_sec; + int modTime_nsec; + int size; + int crc32; + int nameLen; +}; + +struct FileRec { + String8 file; + bool deleted; + FileState s; +}; + /** * Writes the data. @@ -99,6 +121,19 @@ private: int back_up_files(int oldSnapshotFD, BackupDataWriter* dataStream, int newSnapshotFD, char const* const* files, char const* const *keys, int fileCount); +class RestoreHelperBase +{ +public: + RestoreHelperBase(); + ~RestoreHelperBase(); + + status_t WriteFile(const String8& filename, BackupDataReader* in); + status_t WriteSnapshot(int fd); + +private: + void* m_buf; + KeyedVector m_files; +}; #define TEST_BACKUP_HELPERS 1 diff --git a/libs/utils/BackupHelpers.cpp b/libs/utils/BackupHelpers.cpp index c1d5404ee..99687bc8f 100644 --- a/libs/utils/BackupHelpers.cpp +++ b/libs/utils/BackupHelpers.cpp @@ -47,27 +47,6 @@ namespace android { #define LOGP(x...) LOGD(x) #endif -struct SnapshotHeader { - int magic0; - int fileCount; - int magic1; - int totalSize; -}; - -struct FileState { - int modTime_sec; - int modTime_nsec; - int size; - int crc32; - int nameLen; -}; - -struct FileRec { - char const* file; // this object does not own this string - bool deleted; - FileState s; -}; - const static int ROUND_UP[4] = { 0, 3, 2, 1 }; static inline int @@ -310,7 +289,8 @@ back_up_files(int oldSnapshotFD, BackupDataWriter* dataStream, int newSnapshotFD for (int i=0; i 0) { // file added - LOGP("file added: %s", g.file); - write_update_file(dataStream, q, g.file); + LOGP("file added: %s", g.file.string()); + write_update_file(dataStream, q, g.file.string()); m++; } else { // both files exist, check them const FileState& f = oldSnapshot.valueAt(n); - int fd = open(g.file, O_RDONLY); + int fd = open(g.file.string(), O_RDONLY); if (fd < 0) { // We can't open the file. Don't report it as a delete either. Let the // server keep the old version. Maybe they'll be able to deal with it // on restore. - LOGP("Unable to open file %s - skipping", g.file); + LOGP("Unable to open file %s - skipping", g.file.string()); } else { g.s.crc32 = compute_crc32(fd); @@ -375,7 +355,7 @@ back_up_files(int oldSnapshotFD, BackupDataWriter* dataStream, int newSnapshotFD g.s.modTime_sec, g.s.modTime_nsec, g.s.size, g.s.crc32); if (f.modTime_sec != g.s.modTime_sec || f.modTime_nsec != g.s.modTime_nsec || f.size != g.s.size || f.crc32 != g.s.crc32) { - write_update_file(dataStream, fd, p, g.file); + write_update_file(dataStream, fd, p, g.file.string()); } close(fd); @@ -395,7 +375,7 @@ back_up_files(int oldSnapshotFD, BackupDataWriter* dataStream, int newSnapshotFD while (mReadEntityHeader(&key, &dataSize); + if (err != NO_ERROR) { + return err; + } + + // TODO: World readable/writable for now. + mode = 0666; + + // Write the file and compute the crc + crc = crc32(0L, Z_NULL, 0); + fd = open(filename.string(), O_CREAT|O_RDWR, mode); + if (fd != -1) { + return errno; + } + + while ((amt = in->ReadEntityData(buf, RESTORE_BUF_SIZE)) > 0) { + err = write(fd, buf, amt); + if (err != amt) { + close(fd); + return errno; + } + crc = crc32(crc, (Bytef*)buf, amt); + } + + close(fd); + + // Record for the snapshot + err = stat(filename.string(), &st); + if (err != 0) { + LOGW("Error stating file that we just created %s", filename.string()); + return errno; + } + + r.file = filename; + r.deleted = false; + r.s.modTime_sec = st.st_mtime; + r.s.modTime_nsec = 0; // workaround sim breakage + //r.s.modTime_nsec = st.st_mtime_nsec; + r.s.size = st.st_size; + r.s.crc32 = crc; + + m_files.add(key, r); + + return NO_ERROR; +} + +status_t +RestoreHelperBase::WriteSnapshot(int fd) +{ + return write_snapshot_file(fd, m_files);; +} + #if TEST_BACKUP_HELPERS #define SCRATCH_DIR "/data/backup_helper_test/" @@ -560,7 +618,6 @@ backup_helper_test_four() FileState states[4]; FileRec r; r.deleted = false; - r.file = NULL; states[0].modTime_sec = 0xfedcba98; states[0].modTime_nsec = 0xdeadbeef; From aebb1ce05e5a76012c6600713645846da29d4300 Mon Sep 17 00:00:00 2001 From: Joe Onorato Date: Thu, 18 Jun 2009 18:23:43 -0700 Subject: [PATCH 127/541] backup stuff --- include/utils/BackupHelpers.h | 1 + libs/utils/BackupData.cpp | 55 ++++++++++++++++++++--------------- libs/utils/BackupHelpers.cpp | 8 +++-- 3 files changed, 38 insertions(+), 26 deletions(-) diff --git a/include/utils/BackupHelpers.h b/include/utils/BackupHelpers.h index fc701fd3a..c78b99a8d 100644 --- a/include/utils/BackupHelpers.h +++ b/include/utils/BackupHelpers.h @@ -116,6 +116,7 @@ private: int type; entity_header_v1 entity; } m_header; + String8 m_key; }; int back_up_files(int oldSnapshotFD, BackupDataWriter* dataStream, int newSnapshotFD, diff --git a/libs/utils/BackupData.cpp b/libs/utils/BackupData.cpp index 34b37edd2..6a7f05646 100644 --- a/libs/utils/BackupData.cpp +++ b/libs/utils/BackupData.cpp @@ -205,12 +205,17 @@ BackupDataReader::ReadNextHeader(bool* done, int* type) amt = read(m_fd, &m_header, sizeof(m_header)); *done = m_done = (amt == 0); CHECK_SIZE(amt, sizeof(m_header)); + m_pos += sizeof(m_header); + if (type) { + *type = m_header.type; + } // validate and fix up the fields. m_header.type = fromlel(m_header.type); switch (m_header.type) { case BACKUP_HEADER_ENTITY_V1: + { m_header.entity.keyLen = fromlel(m_header.entity.keyLen); if (m_header.entity.keyLen <= 0) { LOGD("Entity header at %d has keyLen<=0: 0x%08x\n", (int)m_pos, @@ -219,15 +224,27 @@ BackupDataReader::ReadNextHeader(bool* done, int* type) } m_header.entity.dataSize = fromlel(m_header.entity.dataSize); m_entityCount++; + + // read the rest of the header (filename) + size_t size = m_header.entity.keyLen; + char* buf = m_key.lockBuffer(size); + if (buf == NULL) { + m_status = ENOMEM; + return m_status; + } + int amt = read(m_fd, buf, size+1); + CHECK_SIZE(amt, (int)size+1); + m_key.unlockBuffer(size); + m_pos += size+1; + SKIP_PADDING(); + m_dataEndPos = m_pos + m_header.entity.dataSize; + break; + } default: LOGD("Chunk header at %d has invalid type: 0x%08x", (int)m_pos, (int)m_header.type); m_status = EINVAL; } - m_pos += sizeof(m_header); - if (type) { - *type = m_header.type; - } return m_status; } @@ -247,20 +264,8 @@ BackupDataReader::ReadEntityHeader(String8* key, size_t* dataSize) if (m_header.type != BACKUP_HEADER_ENTITY_V1) { return EINVAL; } - size_t size = m_header.entity.keyLen; - char* buf = key->lockBuffer(size); - if (key == NULL) { - key->unlockBuffer(); - m_status = ENOMEM; - return m_status; - } - int amt = read(m_fd, buf, size+1); - CHECK_SIZE(amt, (int)size+1); - key->unlockBuffer(size); - m_pos += size+1; + *key = m_key; *dataSize = m_header.entity.dataSize; - SKIP_PADDING(); - m_dataEndPos = m_pos + *dataSize; return NO_ERROR; } @@ -285,20 +290,24 @@ ssize_t BackupDataReader::ReadEntityData(void* data, size_t size) { if (m_status != NO_ERROR) { - return m_status; + return -1; } int remaining = m_dataEndPos - m_pos; //LOGD("ReadEntityData size=%d m_pos=0x%x m_dataEndPos=0x%x remaining=%d\n", // size, m_pos, m_dataEndPos, remaining); - if (size > remaining) { - size = remaining; - } if (remaining <= 0) { return 0; } + if (size > remaining) { + size = remaining; + } + //LOGD(" reading %d bytes", size); int amt = read(m_fd, data, size); - CHECK_SIZE(amt, (int)size); - m_pos += size; + if (amt < 0) { + m_status = errno; + return -1; + } + m_pos += amt; return amt; } diff --git a/libs/utils/BackupHelpers.cpp b/libs/utils/BackupHelpers.cpp index 99687bc8f..d65a457f3 100644 --- a/libs/utils/BackupHelpers.cpp +++ b/libs/utils/BackupHelpers.cpp @@ -414,14 +414,15 @@ RestoreHelperBase::WriteFile(const String8& filename, BackupDataReader* in) if (err != NO_ERROR) { return err; } - + // TODO: World readable/writable for now. mode = 0666; // Write the file and compute the crc crc = crc32(0L, Z_NULL, 0); - fd = open(filename.string(), O_CREAT|O_RDWR, mode); - if (fd != -1) { + fd = open(filename.string(), O_CREAT|O_RDWR|O_TRUNC, mode); + if (fd == -1) { + LOGW("Could not open file %s -- %s", filename.string(), strerror(errno)); return errno; } @@ -429,6 +430,7 @@ RestoreHelperBase::WriteFile(const String8& filename, BackupDataReader* in) err = write(fd, buf, amt); if (err != amt) { close(fd); + LOGW("Error '%s' writing '%s'", strerror(errno), filename.string()); return errno; } crc = crc32(crc, (Bytef*)buf, amt); From 4538dd85da70de8e654a558a1f4cf156502855c8 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Fri, 19 Jun 2009 15:13:28 -0700 Subject: [PATCH 128/541] Report densities in badging, debugging for nine patch bug. The aapt tool now reports all available densities like it already did for locales. Also this includes more resource data output, which I was using to examine bug #1867049 (which at this point I am unable to reproduce). --- include/utils/ResourceTypes.h | 2 +- libs/utils/ResourceTypes.cpp | 87 +++++++++++++++++++++++++++++++++-- 2 files changed, 85 insertions(+), 4 deletions(-) diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h index 9b8c3026a..68f923326 100644 --- a/include/utils/ResourceTypes.h +++ b/include/utils/ResourceTypes.h @@ -1781,7 +1781,7 @@ public: void getLocales(Vector* locales) const; #ifndef HAVE_ANDROID_OS - void print() const; + void print(bool inclValues) const; #endif private: diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp index 3d12dca7a..69d47f0c8 100644 --- a/libs/utils/ResourceTypes.cpp +++ b/libs/utils/ResourceTypes.cpp @@ -3830,9 +3830,45 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg, #define CHAR16_ARRAY_EQ(constant, var, len) \ ((len == (sizeof(constant)/sizeof(constant[0]))) && (0 == memcmp((var), (constant), (len)))) -void ResTable::print() const +void print_complex(uint32_t complex, bool isFraction) { - printf("mError=0x%x (%s)\n", mError, strerror(mError)); + const float MANTISSA_MULT = + 1.0f / (1<>Res_value::COMPLEX_RADIX_SHIFT) + & Res_value::COMPLEX_RADIX_MASK]; + printf("%f", value); + + 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; + case Res_value::COMPLEX_UNIT_SP: printf("sp"); break; + case Res_value::COMPLEX_UNIT_PT: printf("pt"); break; + case Res_value::COMPLEX_UNIT_IN: printf("in"); break; + case Res_value::COMPLEX_UNIT_MM: printf("mm"); break; + default: printf(" (unknown unit)"); break; + } + } else { + switch ((complex>>Res_value::COMPLEX_UNIT_SHIFT)&Res_value::COMPLEX_UNIT_MASK) { + case Res_value::COMPLEX_UNIT_FRACTION: printf("%%"); break; + case Res_value::COMPLEX_UNIT_FRACTION_PARENT: printf("%%p"); break; + default: printf(" (unknown unit)"); break; + } + } +} + +void ResTable::print(bool inclValues) const +{ + if (mError != 0) { + printf("mError=0x%x (%s)\n", mError, strerror(mError)); + } #if 0 printf("mParams=%c%c-%c%c,\n", mParams.language[0], mParams.language[1], @@ -3947,6 +3983,8 @@ void ResTable::print() const (void*)(entriesStart + thisOffset)); continue; } + + const Res_value* value = NULL; if ((dtohs(ent->flags)&ResTable_entry::FLAG_COMPLEX) != 0) { printf(""); } else { @@ -3962,7 +4000,7 @@ void ResTable::print() const continue; } - const Res_value* value = (const Res_value*) + value = (const Res_value*) (((const uint8_t*)ent) + esize); printf("t=0x%02x d=0x%08x (s=0x%04x r=0x%02x)", (int)value->dataType, (int)dtohl(value->data), @@ -3973,6 +4011,49 @@ void ResTable::print() const printf(" (PUBLIC)"); } printf("\n"); + + if (inclValues) { + if (value != 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"); + } + } + } } } } From a33bd1672fab3ac2ca72f2921716bf860d500aa3 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Mon, 22 Jun 2009 01:17:46 -0700 Subject: [PATCH 129/541] improve Vector<> with types that can be trivially moved and remove some unused code. This optimization applies to sp<> and wp<> which should now perform about the same as regular pointers when placed in to Vector<>. --- include/utils/SortedVector.h | 3 +- include/utils/TypeHelpers.h | 135 +++++++++++++++++++---------------- include/utils/Vector.h | 3 +- include/utils/VectorImpl.h | 1 - 4 files changed, 74 insertions(+), 68 deletions(-) diff --git a/include/utils/SortedVector.h b/include/utils/SortedVector.h index c8a61531f..8beec5732 100644 --- a/include/utils/SortedVector.h +++ b/include/utils/SortedVector.h @@ -141,8 +141,7 @@ SortedVector::SortedVector() : SortedVectorImpl(sizeof(TYPE), ((traits::has_trivial_ctor ? HAS_TRIVIAL_CTOR : 0) |(traits::has_trivial_dtor ? HAS_TRIVIAL_DTOR : 0) - |(traits::has_trivial_copy ? HAS_TRIVIAL_COPY : 0) - |(traits::has_trivial_assign ? HAS_TRIVIAL_ASSIGN : 0)) + |(traits::has_trivial_copy ? HAS_TRIVIAL_COPY : 0)) ) { } diff --git a/include/utils/TypeHelpers.h b/include/utils/TypeHelpers.h index c04c37fa9..2ff2749ba 100644 --- a/include/utils/TypeHelpers.h +++ b/include/utils/TypeHelpers.h @@ -29,35 +29,39 @@ namespace android { /* * Types traits */ - -template struct trait_trivial_ctor { enum { value = false }; }; -template struct trait_trivial_dtor { enum { value = false }; }; -template struct trait_trivial_copy { enum { value = false }; }; -template struct trait_trivial_assign{ enum { value = false }; }; -template struct trait_pointer { enum { value = false }; }; -template struct trait_pointer { enum { value = true }; }; +template struct trait_trivial_ctor { enum { value = false }; }; +template struct trait_trivial_dtor { enum { value = false }; }; +template struct trait_trivial_copy { enum { value = false }; }; +template struct trait_trivial_move { enum { value = false }; }; +template struct trait_pointer { enum { value = false }; }; +template struct trait_pointer { enum { value = true }; }; -#define ANDROID_BASIC_TYPES_TRAITS( T ) \ - template<> struct trait_trivial_ctor< T > { enum { value = true }; }; \ - template<> struct trait_trivial_dtor< T > { enum { value = true }; }; \ - template<> struct trait_trivial_copy< T > { enum { value = true }; }; \ - template<> struct trait_trivial_assign< T >{ enum { value = true }; }; +// sp<> can be trivially moved +template class sp; +template struct trait_trivial_move< sp >{ + enum { value = true }; +}; -#define ANDROID_TYPE_TRAITS( T, ctor, dtor, copy, assign ) \ - template<> struct trait_trivial_ctor< T > { enum { value = ctor }; }; \ - template<> struct trait_trivial_dtor< T > { enum { value = dtor }; }; \ - template<> struct trait_trivial_copy< T > { enum { value = copy }; }; \ - template<> struct trait_trivial_assign< T >{ enum { value = assign }; }; +// wp<> can be trivially moved +template class wp; +template struct trait_trivial_move< wp >{ + enum { value = true }; +}; template struct traits { enum { + // whether this type is a pointer is_pointer = trait_pointer::value, + // whether this type's constructor is a no-op has_trivial_ctor = is_pointer || trait_trivial_ctor::value, + // whether this type's destructor is a no-op has_trivial_dtor = is_pointer || trait_trivial_dtor::value, + // whether this type type can be copy-constructed with memcpy has_trivial_copy = is_pointer || trait_trivial_copy::value, - has_trivial_assign = is_pointer || trait_trivial_assign::value + // whether this type can be moved with memmove + has_trivial_move = is_pointer || trait_trivial_move::value }; }; @@ -65,37 +69,47 @@ template struct aggregate_traits { enum { is_pointer = false, - has_trivial_ctor = traits::has_trivial_ctor && traits::has_trivial_ctor, - has_trivial_dtor = traits::has_trivial_dtor && traits::has_trivial_dtor, - has_trivial_copy = traits::has_trivial_copy && traits::has_trivial_copy, - has_trivial_assign = traits::has_trivial_assign && traits::has_trivial_assign + has_trivial_ctor = + traits::has_trivial_ctor && traits::has_trivial_ctor, + has_trivial_dtor = + traits::has_trivial_dtor && traits::has_trivial_dtor, + has_trivial_copy = + traits::has_trivial_copy && traits::has_trivial_copy, + has_trivial_move = + traits::has_trivial_move && traits::has_trivial_move }; }; +#define ANDROID_BASIC_TYPES_TRAITS( T ) \ + template<> struct trait_trivial_ctor< T > { enum { value = true }; }; \ + template<> struct trait_trivial_dtor< T > { enum { value = true }; }; \ + template<> struct trait_trivial_copy< T > { enum { value = true }; }; \ + template<> struct trait_trivial_move< T > { enum { value = true }; }; + // --------------------------------------------------------------------------- /* * basic types traits */ - -ANDROID_BASIC_TYPES_TRAITS( void ); -ANDROID_BASIC_TYPES_TRAITS( bool ); -ANDROID_BASIC_TYPES_TRAITS( char ); -ANDROID_BASIC_TYPES_TRAITS( unsigned char ); -ANDROID_BASIC_TYPES_TRAITS( short ); -ANDROID_BASIC_TYPES_TRAITS( unsigned short ); -ANDROID_BASIC_TYPES_TRAITS( int ); -ANDROID_BASIC_TYPES_TRAITS( unsigned int ); -ANDROID_BASIC_TYPES_TRAITS( long ); -ANDROID_BASIC_TYPES_TRAITS( unsigned long ); -ANDROID_BASIC_TYPES_TRAITS( long long ); -ANDROID_BASIC_TYPES_TRAITS( unsigned long long ); -ANDROID_BASIC_TYPES_TRAITS( float ); -ANDROID_BASIC_TYPES_TRAITS( double ); + +ANDROID_BASIC_TYPES_TRAITS( void ) +ANDROID_BASIC_TYPES_TRAITS( bool ) +ANDROID_BASIC_TYPES_TRAITS( char ) +ANDROID_BASIC_TYPES_TRAITS( unsigned char ) +ANDROID_BASIC_TYPES_TRAITS( short ) +ANDROID_BASIC_TYPES_TRAITS( unsigned short ) +ANDROID_BASIC_TYPES_TRAITS( int ) +ANDROID_BASIC_TYPES_TRAITS( unsigned int ) +ANDROID_BASIC_TYPES_TRAITS( long ) +ANDROID_BASIC_TYPES_TRAITS( unsigned long ) +ANDROID_BASIC_TYPES_TRAITS( long long ) +ANDROID_BASIC_TYPES_TRAITS( unsigned long long ) +ANDROID_BASIC_TYPES_TRAITS( float ) +ANDROID_BASIC_TYPES_TRAITS( double ) // --------------------------------------------------------------------------- - + /* * compare and order types */ @@ -111,9 +125,9 @@ int compare_type(const TYPE& lhs, const TYPE& rhs) { } /* - * create, destroy, copy and assign types... + * create, destroy, copy and move types... */ - + template inline void construct_type(TYPE* p, size_t n) { if (!traits::has_trivial_ctor) { @@ -145,17 +159,6 @@ void copy_type(TYPE* d, const TYPE* s, size_t n) { } } -template inline -void assign_type(TYPE* d, const TYPE* s, size_t n) { - if (!traits::has_trivial_assign) { - while (n--) { - *d++ = *s++; - } - } else { - memcpy(d,s,n*sizeof(TYPE)); - } -} - template inline void splat_type(TYPE* where, const TYPE* what, size_t n) { if (!traits::has_trivial_copy) { @@ -164,15 +167,19 @@ void splat_type(TYPE* where, const TYPE* what, size_t n) { where++; } } else { - while (n--) { - *where++ = *what; + while (n--) { + *where++ = *what; } } } template inline void move_forward_type(TYPE* d, const TYPE* s, size_t n = 1) { - if (!traits::has_trivial_copy || !traits::has_trivial_dtor) { + if ((traits::has_trivial_dtor && traits::has_trivial_copy) + || traits::has_trivial_move) + { + memmove(d,s,n*sizeof(TYPE)); + } else { d += n; s += n; while (n--) { @@ -180,35 +187,37 @@ void move_forward_type(TYPE* d, const TYPE* s, size_t n = 1) { if (!traits::has_trivial_copy) { new(d) TYPE(*s); } else { - *d = *s; + *d = *s; } if (!traits::has_trivial_dtor) { s->~TYPE(); } } - } else { - memmove(d,s,n*sizeof(TYPE)); } } template inline void move_backward_type(TYPE* d, const TYPE* s, size_t n = 1) { - if (!traits::has_trivial_copy || !traits::has_trivial_dtor) { + if ((traits::has_trivial_dtor && traits::has_trivial_copy) + || traits::has_trivial_move) + { + memmove(d,s,n*sizeof(TYPE)); + } else { while (n--) { if (!traits::has_trivial_copy) { new(d) TYPE(*s); } else { - *d = *s; + *d = *s; } if (!traits::has_trivial_dtor) { s->~TYPE(); } d++, s++; } - } else { - memmove(d,s,n*sizeof(TYPE)); } } + + // --------------------------------------------------------------------------- /* @@ -242,8 +251,8 @@ struct trait_trivial_copy< key_value_pair_t > { enum { value = aggregate_traits::has_trivial_copy }; }; template<> template -struct trait_trivial_assign< key_value_pair_t > -{ enum { value = aggregate_traits::has_trivial_assign};}; +struct trait_trivial_move< key_value_pair_t > +{ enum { value = aggregate_traits::has_trivial_move }; }; // --------------------------------------------------------------------------- diff --git a/include/utils/Vector.h b/include/utils/Vector.h index be365d83e..ad59fd63e 100644 --- a/include/utils/Vector.h +++ b/include/utils/Vector.h @@ -175,8 +175,7 @@ Vector::Vector() : VectorImpl(sizeof(TYPE), ((traits::has_trivial_ctor ? HAS_TRIVIAL_CTOR : 0) |(traits::has_trivial_dtor ? HAS_TRIVIAL_DTOR : 0) - |(traits::has_trivial_copy ? HAS_TRIVIAL_COPY : 0) - |(traits::has_trivial_assign ? HAS_TRIVIAL_ASSIGN : 0)) + |(traits::has_trivial_copy ? HAS_TRIVIAL_COPY : 0)) ) { } diff --git a/include/utils/VectorImpl.h b/include/utils/VectorImpl.h index 2525229be..49b03f10b 100644 --- a/include/utils/VectorImpl.h +++ b/include/utils/VectorImpl.h @@ -44,7 +44,6 @@ public: HAS_TRIVIAL_CTOR = 0x00000001, HAS_TRIVIAL_DTOR = 0x00000002, HAS_TRIVIAL_COPY = 0x00000004, - HAS_TRIVIAL_ASSIGN = 0x00000008 }; VectorImpl(size_t itemSize, uint32_t flags); From fe3b5edcf13e1aee1c411693dc29e362856e9aa1 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Mon, 22 Jun 2009 02:35:32 -0700 Subject: [PATCH 130/541] fix warnings that will show up with GCC 4.4 (in master) --- include/utils/ResourceTypes.h | 26 +++++++++++++------------- libs/utils/ResourceTypes.cpp | 22 +++++++++++----------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h index 68f923326..f1029b7bc 100644 --- a/include/utils/ResourceTypes.h +++ b/include/utils/ResourceTypes.h @@ -71,7 +71,7 @@ namespace android { * The relative sizes of the stretchy segments indicates the relative * amount of stretchiness of the regions bordered by the segments. For * example, regions 3, 7 and 11 above will take up more horizontal space - * than regions 1, 5 and 9 since the horizonal segment associated with + * than regions 1, 5 and 9 since the horizontal segment associated with * the first set of regions is larger than the other set of regions. The * ratios of the amount of horizontal (or vertical) space taken by any * two stretchable slices is exactly the ratio of their corresponding @@ -87,7 +87,7 @@ namespace android { * the leftmost slices always start at x=0 and the rightmost slices * always end at the end of the image. So, for example, the regions 0, * 4 and 8 (which are fixed along the X axis) start at x value 0 and - * go to xDiv[0] amd slices 2, 6 and 10 start at xDiv[1] and end at + * go to xDiv[0] and slices 2, 6 and 10 start at xDiv[1] and end at * xDiv[2]. * * The array pointed to by the colors field lists contains hints for @@ -626,25 +626,25 @@ public: event_code_t next(); // These are available for all nodes: - const int32_t getCommentID() const; + int32_t getCommentID() const; const uint16_t* getComment(size_t* outLen) const; - const uint32_t getLineNumber() const; + uint32_t getLineNumber() const; // This is available for TEXT: - const int32_t getTextID() const; + int32_t getTextID() const; const uint16_t* getText(size_t* outLen) const; ssize_t getTextValue(Res_value* outValue) const; // These are available for START_NAMESPACE and END_NAMESPACE: - const int32_t getNamespacePrefixID() const; + int32_t getNamespacePrefixID() const; const uint16_t* getNamespacePrefix(size_t* outLen) const; - const int32_t getNamespaceUriID() const; + int32_t getNamespaceUriID() const; const uint16_t* getNamespaceUri(size_t* outLen) const; // These are available for START_TAG and END_TAG: - const int32_t getElementNamespaceID() const; + int32_t getElementNamespaceID() const; const uint16_t* getElementNamespace(size_t* outLen) const; - const int32_t getElementNameID() const; + int32_t getElementNameID() const; const uint16_t* getElementName(size_t* outLen) const; // Remaining methods are for retrieving information about attributes @@ -653,14 +653,14 @@ public: size_t getAttributeCount() const; // Returns -1 if no namespace, -2 if idx out of range. - const int32_t getAttributeNamespaceID(size_t idx) const; + int32_t getAttributeNamespaceID(size_t idx) const; const uint16_t* getAttributeNamespace(size_t idx, size_t* outLen) const; - const int32_t getAttributeNameID(size_t idx) const; + int32_t getAttributeNameID(size_t idx) const; const uint16_t* getAttributeName(size_t idx, size_t* outLen) const; - const uint32_t getAttributeNameResID(size_t idx) const; + uint32_t getAttributeNameResID(size_t idx) const; - const int32_t getAttributeValueStringID(size_t idx) const; + int32_t getAttributeValueStringID(size_t idx) const; const uint16_t* getAttributeStringValue(size_t idx, size_t* outLen) const; int32_t getAttributeDataType(size_t idx) const; diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp index 69d47f0c8..e4f9f0f9f 100644 --- a/libs/utils/ResourceTypes.cpp +++ b/libs/utils/ResourceTypes.cpp @@ -544,7 +544,7 @@ ResXMLParser::event_code_t ResXMLParser::next() return mEventCode; } -const int32_t ResXMLParser::getCommentID() const +int32_t ResXMLParser::getCommentID() const { return mCurNode != NULL ? dtohl(mCurNode->comment.index) : -1; } @@ -555,12 +555,12 @@ const uint16_t* ResXMLParser::getComment(size_t* outLen) const return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; } -const uint32_t ResXMLParser::getLineNumber() const +uint32_t ResXMLParser::getLineNumber() const { return mCurNode != NULL ? dtohl(mCurNode->lineNumber) : -1; } -const int32_t ResXMLParser::getTextID() const +int32_t ResXMLParser::getTextID() const { if (mEventCode == TEXT) { return dtohl(((const ResXMLTree_cdataExt*)mCurExt)->data.index); @@ -583,7 +583,7 @@ ssize_t ResXMLParser::getTextValue(Res_value* outValue) const return BAD_TYPE; } -const int32_t ResXMLParser::getNamespacePrefixID() const +int32_t ResXMLParser::getNamespacePrefixID() const { if (mEventCode == START_NAMESPACE || mEventCode == END_NAMESPACE) { return dtohl(((const ResXMLTree_namespaceExt*)mCurExt)->prefix.index); @@ -598,7 +598,7 @@ const uint16_t* ResXMLParser::getNamespacePrefix(size_t* outLen) const return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; } -const int32_t ResXMLParser::getNamespaceUriID() const +int32_t ResXMLParser::getNamespaceUriID() const { if (mEventCode == START_NAMESPACE || mEventCode == END_NAMESPACE) { return dtohl(((const ResXMLTree_namespaceExt*)mCurExt)->uri.index); @@ -613,7 +613,7 @@ const uint16_t* ResXMLParser::getNamespaceUri(size_t* outLen) const return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; } -const int32_t ResXMLParser::getElementNamespaceID() const +int32_t ResXMLParser::getElementNamespaceID() const { if (mEventCode == START_TAG) { return dtohl(((const ResXMLTree_attrExt*)mCurExt)->ns.index); @@ -630,7 +630,7 @@ const uint16_t* ResXMLParser::getElementNamespace(size_t* outLen) const return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; } -const int32_t ResXMLParser::getElementNameID() const +int32_t ResXMLParser::getElementNameID() const { if (mEventCode == START_TAG) { return dtohl(((const ResXMLTree_attrExt*)mCurExt)->name.index); @@ -655,7 +655,7 @@ size_t ResXMLParser::getAttributeCount() const return 0; } -const int32_t ResXMLParser::getAttributeNamespaceID(size_t idx) const +int32_t ResXMLParser::getAttributeNamespaceID(size_t idx) const { if (mEventCode == START_TAG) { const ResXMLTree_attrExt* tag = (const ResXMLTree_attrExt*)mCurExt; @@ -678,7 +678,7 @@ const uint16_t* ResXMLParser::getAttributeNamespace(size_t idx, size_t* outLen) return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; } -const int32_t ResXMLParser::getAttributeNameID(size_t idx) const +int32_t ResXMLParser::getAttributeNameID(size_t idx) const { if (mEventCode == START_TAG) { const ResXMLTree_attrExt* tag = (const ResXMLTree_attrExt*)mCurExt; @@ -701,7 +701,7 @@ const uint16_t* ResXMLParser::getAttributeName(size_t idx, size_t* outLen) const return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; } -const uint32_t ResXMLParser::getAttributeNameResID(size_t idx) const +uint32_t ResXMLParser::getAttributeNameResID(size_t idx) const { int32_t id = getAttributeNameID(idx); if (id >= 0 && (size_t)id < mTree.mNumResIds) { @@ -710,7 +710,7 @@ const uint32_t ResXMLParser::getAttributeNameResID(size_t idx) const return 0; } -const int32_t ResXMLParser::getAttributeValueStringID(size_t idx) const +int32_t ResXMLParser::getAttributeValueStringID(size_t idx) const { if (mEventCode == START_TAG) { const ResXMLTree_attrExt* tag = (const ResXMLTree_attrExt*)mCurExt; From 109518880c9f5fa8f7564bcc644403565e6203d3 Mon Sep 17 00:00:00 2001 From: Joe Onorato Date: Thu, 18 Jun 2009 20:10:37 -0700 Subject: [PATCH 131/541] Helper API cleanup. Allows multiple helpers to function, because they'll always go in the same order, and this lets us not have to write headers to keep them paired. --- include/utils/BackupHelpers.h | 3 +++ libs/utils/BackupData.cpp | 21 ++++++++++++++++++--- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/include/utils/BackupHelpers.h b/include/utils/BackupHelpers.h index c78b99a8d..a21359f55 100644 --- a/include/utils/BackupHelpers.h +++ b/include/utils/BackupHelpers.h @@ -71,6 +71,8 @@ public: status_t WriteEntityHeader(const String8& key, size_t dataSize); status_t WriteEntityData(const void* data, size_t size); + void SetKeyPrefix(const String8& keyPrefix); + private: explicit BackupDataWriter(); status_t write_padding_for(int n); @@ -79,6 +81,7 @@ private: status_t m_status; ssize_t m_pos; int m_entityCount; + String8 m_keyPrefix; }; /** diff --git a/libs/utils/BackupData.cpp b/libs/utils/BackupData.cpp index 6a7f05646..0868cff6c 100644 --- a/libs/utils/BackupData.cpp +++ b/libs/utils/BackupData.cpp @@ -99,10 +99,20 @@ BackupDataWriter::WriteEntityHeader(const String8& key, size_t dataSize) return amt; } + String8 k; + if (m_keyPrefix.length() > 0) { + k = m_keyPrefix; + k += ":"; + k += key; + } else { + k = key; + } + LOGD("m_keyPrefix=%s key=%s k=%s", m_keyPrefix.string(), key.string(), k.string()); + entity_header_v1 header; ssize_t keyLen; - keyLen = key.length(); + keyLen = k.length(); header.type = tolel(BACKUP_HEADER_ENTITY_V1); header.keyLen = tolel(keyLen); @@ -115,7 +125,7 @@ BackupDataWriter::WriteEntityHeader(const String8& key, size_t dataSize) } m_pos += amt; - amt = write(m_fd, key.string(), keyLen+1); + amt = write(m_fd, k.string(), keyLen+1); if (amt != keyLen+1) { m_status = errno; return m_status; @@ -148,6 +158,11 @@ BackupDataWriter::WriteEntityData(const void* data, size_t size) return NO_ERROR; } +void +BackupDataWriter::SetKeyPrefix(const String8& keyPrefix) +{ + m_keyPrefix = keyPrefix; +} BackupDataReader::BackupDataReader(int fd) @@ -298,7 +313,7 @@ BackupDataReader::ReadEntityData(void* data, size_t size) if (remaining <= 0) { return 0; } - if (size > remaining) { + if (((int)size) > remaining) { size = remaining; } //LOGD(" reading %d bytes", size); From 02abe53d8a8610d7704ed5a9e90f6393251898a1 Mon Sep 17 00:00:00 2001 From: Christopher Tate Date: Tue, 23 Jun 2009 13:03:00 -0700 Subject: [PATCH 132/541] Add file mode to the file-backup saved state blobs This change puts the file's access mode into the saved-state blob used by the file backup helpers. The tests have been updated for the new blob content format. What this change *doesn't* do is actually backup/restore the file mode. This change is a prerequisite for that, but mode preservation in backup/restore will require adding metadata to the backup data stream itself, so will be approached a bit more carefully. (Also fixed one outright bug in the test program: ReadEntityData() had been changed to return a ssize_t union of either a byte-count or a negative number indicating error, but the test program was still assuming that nonzero == error, and was spuriously failing.) --- include/utils/BackupHelpers.h | 1 + libs/utils/BackupHelpers.cpp | 73 ++++++++++++++++++++--------------- 2 files changed, 43 insertions(+), 31 deletions(-) diff --git a/include/utils/BackupHelpers.h b/include/utils/BackupHelpers.h index a21359f55..759a0cc6d 100644 --- a/include/utils/BackupHelpers.h +++ b/include/utils/BackupHelpers.h @@ -43,6 +43,7 @@ struct SnapshotHeader { struct FileState { int modTime_sec; int modTime_nsec; + int mode; int size; int crc32; int nameLen; diff --git a/libs/utils/BackupHelpers.cpp b/libs/utils/BackupHelpers.cpp index d65a457f3..67d07fed8 100644 --- a/libs/utils/BackupHelpers.cpp +++ b/libs/utils/BackupHelpers.cpp @@ -302,6 +302,7 @@ back_up_files(int oldSnapshotFD, BackupDataWriter* dataStream, int newSnapshotFD r.s.modTime_sec = st.st_mtime; r.s.modTime_nsec = 0; // workaround sim breakage //r.s.modTime_nsec = st.st_mtime_nsec; + r.s.mode = st.st_mode; r.s.size = st.st_size; // we compute the crc32 later down below, when we already have the file open. @@ -349,12 +350,12 @@ back_up_files(int oldSnapshotFD, BackupDataWriter* dataStream, int newSnapshotFD g.s.crc32 = compute_crc32(fd); LOGP("%s", q.string()); - LOGP(" new: modTime=%d,%d size=%-3d crc32=0x%08x", - f.modTime_sec, f.modTime_nsec, f.size, f.crc32); - LOGP(" old: modTime=%d,%d size=%-3d crc32=0x%08x", - g.s.modTime_sec, g.s.modTime_nsec, g.s.size, g.s.crc32); + LOGP(" new: modTime=%d,%d mode=%04o size=%-3d crc32=0x%08x", + f.modTime_sec, f.modTime_nsec, f.mode, f.size, f.crc32); + LOGP(" old: modTime=%d,%d mode=%04o size=%-3d crc32=0x%08x", + g.s.modTime_sec, g.s.modTime_nsec, g.s.mode, g.s.size, g.s.crc32); if (f.modTime_sec != g.s.modTime_sec || f.modTime_nsec != g.s.modTime_nsec - || f.size != g.s.size || f.crc32 != g.s.crc32) { + || f.mode != g.s.mode || f.size != g.s.size || f.crc32 != g.s.crc32) { write_update_file(dataStream, fd, p, g.file.string()); } @@ -450,6 +451,7 @@ RestoreHelperBase::WriteFile(const String8& filename, BackupDataReader* in) r.s.modTime_sec = st.st_mtime; r.s.modTime_nsec = 0; // workaround sim breakage //r.s.modTime_nsec = st.st_mtime_nsec; + r.s.mode = st.st_mode; r.s.size = st.st_size; r.s.crc32 = crc; @@ -623,6 +625,7 @@ backup_helper_test_four() states[0].modTime_sec = 0xfedcba98; states[0].modTime_nsec = 0xdeadbeef; + states[0].mode = 0777; // decimal 511, hex 0x000001ff states[0].size = 0xababbcbc; states[0].crc32 = 0x12345678; states[0].nameLen = -12; @@ -632,6 +635,7 @@ backup_helper_test_four() states[1].modTime_sec = 0x93400031; states[1].modTime_nsec = 0xdeadbeef; + states[1].mode = 0666; // decimal 438, hex 0x000001b6 states[1].size = 0x88557766; states[1].crc32 = 0x22334422; states[1].nameLen = -1; @@ -641,6 +645,7 @@ backup_helper_test_four() states[2].modTime_sec = 0x33221144; states[2].modTime_nsec = 0xdeadbeef; + states[2].mode = 0744; // decimal 484, hex 0x000001e4 states[2].size = 0x11223344; states[2].crc32 = 0x01122334; states[2].nameLen = 0; @@ -650,6 +655,7 @@ backup_helper_test_four() states[3].modTime_sec = 0x33221144; states[3].modTime_nsec = 0xdeadbeef; + states[3].mode = 0755; // decimal 493, hex 0x000001ed states[3].size = 0x11223344; states[3].crc32 = 0x01122334; states[3].nameLen = 0; @@ -669,35 +675,38 @@ backup_helper_test_four() static const unsigned char correct_data[] = { // header 0x53, 0x6e, 0x61, 0x70, 0x04, 0x00, 0x00, 0x00, - 0x46, 0x69, 0x6c, 0x65, 0xac, 0x00, 0x00, 0x00, + 0x46, 0x69, 0x6c, 0x65, 0xbc, 0x00, 0x00, 0x00, // bytes_of_padding 0x98, 0xba, 0xdc, 0xfe, 0xef, 0xbe, 0xad, 0xde, - 0xbc, 0xbc, 0xab, 0xab, 0x78, 0x56, 0x34, 0x12, - 0x10, 0x00, 0x00, 0x00, 0x62, 0x79, 0x74, 0x65, - 0x73, 0x5f, 0x6f, 0x66, 0x5f, 0x70, 0x61, 0x64, - 0x64, 0x69, 0x6e, 0x67, + 0xff, 0x01, 0x00, 0x00, 0xbc, 0xbc, 0xab, 0xab, + 0x78, 0x56, 0x34, 0x12, 0x10, 0x00, 0x00, 0x00, + 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x6f, 0x66, + 0x5f, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, // bytes_of_padding3 0x31, 0x00, 0x40, 0x93, 0xef, 0xbe, 0xad, 0xde, - 0x66, 0x77, 0x55, 0x88, 0x22, 0x44, 0x33, 0x22, - 0x11, 0x00, 0x00, 0x00, 0x62, 0x79, 0x74, 0x65, - 0x73, 0x5f, 0x6f, 0x66, 0x5f, 0x70, 0x61, 0x64, - 0x64, 0x69, 0x6e, 0x67, 0x33, 0xab, 0xab, 0xab, + 0xb6, 0x01, 0x00, 0x00, 0x66, 0x77, 0x55, 0x88, + 0x22, 0x44, 0x33, 0x22, 0x11, 0x00, 0x00, 0x00, + 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x6f, 0x66, + 0x5f, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, + 0x33, 0xab, 0xab, 0xab, // bytes of padding2 0x44, 0x11, 0x22, 0x33, 0xef, 0xbe, 0xad, 0xde, - 0x44, 0x33, 0x22, 0x11, 0x34, 0x23, 0x12, 0x01, - 0x12, 0x00, 0x00, 0x00, 0x62, 0x79, 0x74, 0x65, - 0x73, 0x5f, 0x6f, 0x66, 0x5f, 0x70, 0x61, 0x64, - 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x32, 0xab, 0xab, + 0xe4, 0x01, 0x00, 0x00, 0x44, 0x33, 0x22, 0x11, + 0x34, 0x23, 0x12, 0x01, 0x12, 0x00, 0x00, 0x00, + 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x6f, 0x66, + 0x5f, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, + 0x5f, 0x32, 0xab, 0xab, // bytes of padding3 0x44, 0x11, 0x22, 0x33, 0xef, 0xbe, 0xad, 0xde, - 0x44, 0x33, 0x22, 0x11, 0x34, 0x23, 0x12, 0x01, - 0x13, 0x00, 0x00, 0x00, 0x62, 0x79, 0x74, 0x65, - 0x73, 0x5f, 0x6f, 0x66, 0x5f, 0x70, 0x61, 0x64, - 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x5f, 0x31, 0xab + 0xed, 0x01, 0x00, 0x00, 0x44, 0x33, 0x22, 0x11, + 0x34, 0x23, 0x12, 0x01, 0x13, 0x00, 0x00, 0x00, + 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x6f, 0x66, + 0x5f, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, + 0x5f, 0x5f, 0x31, 0xab }; err = compare_file(filename, correct_data, sizeof(correct_data)); @@ -731,14 +740,14 @@ backup_helper_test_four() const FileState state = readSnapshot.valueAt(i); if (name != filenames[i] || states[i].modTime_sec != state.modTime_sec - || states[i].modTime_nsec != state.modTime_nsec + || states[i].modTime_nsec != state.modTime_nsec || states[i].mode != state.mode || states[i].size != state.size || states[i].crc32 != states[i].crc32) { - fprintf(stderr, "state %d expected={%d/%d, 0x%08x, 0x%08x, %3d} '%s'\n" - " actual={%d/%d, 0x%08x, 0x%08x, %3d} '%s'\n", i, - states[i].modTime_sec, states[i].modTime_nsec, states[i].size, states[i].crc32, - name.length(), filenames[i].string(), - state.modTime_sec, state.modTime_nsec, state.size, state.crc32, state.nameLen, - name.string()); + fprintf(stderr, "state %d expected={%d/%d, 0x%08x, %04o, 0x%08x, %3d} '%s'\n" + " actual={%d/%d, 0x%08x, %04o, 0x%08x, %3d} '%s'\n", i, + states[i].modTime_sec, states[i].modTime_nsec, states[i].mode, states[i].size, + states[i].crc32, name.length(), filenames[i].string(), + state.modTime_sec, state.modTime_nsec, state.mode, state.size, state.crc32, + state.nameLen, name.string()); matched = false; } } @@ -839,6 +848,7 @@ test_read_header_and_entity(BackupDataReader& reader, const char* str) size_t actualSize; bool done; int type; + ssize_t nRead; // printf("\n\n---------- test_read_header_and_entity -- %s\n\n", str); @@ -873,8 +883,9 @@ test_read_header_and_entity(BackupDataReader& reader, const char* str) goto finished; } - err = reader.ReadEntityData(buf, bufSize); - if (err != NO_ERROR) { + nRead = reader.ReadEntityData(buf, bufSize); + if (nRead < 0) { + err = reader.Status(); fprintf(stderr, "ReadEntityData failed with %s\n", strerror(err)); goto finished; } From ad2030d99e904d4ed97ffd43786e730998cdccb0 Mon Sep 17 00:00:00 2001 From: Christopher Tate Date: Tue, 23 Jun 2009 17:35:11 -0700 Subject: [PATCH 133/541] Preserve file access mode when backing up / restoring files This change adds a fixed-size metadata block at the head of each file's content entity. The block is versioned, and fixed-size on the theory that it might be nice to be able to recover the content (if not the full metadata) of the files if we're ever confronted with data backed up some hypothetical future helper that stored expanded metadata. The net effect is that now on restore, we assign the same access mode to the file that it originally had when backed up. Also, some of the code was failing to properly free transient heap-based buffers when it encountered errors. This has been fixed with the addition of a tiny stack-based object whose job it is to free() its designated pointer from its destructor. --- libs/utils/BackupHelpers.cpp | 102 ++++++++++++++++++++++++++++++----- 1 file changed, 89 insertions(+), 13 deletions(-) diff --git a/libs/utils/BackupHelpers.cpp b/libs/utils/BackupHelpers.cpp index 67d07fed8..334661476 100644 --- a/libs/utils/BackupHelpers.cpp +++ b/libs/utils/BackupHelpers.cpp @@ -41,7 +41,43 @@ namespace android { #define MAGIC0 0x70616e53 // Snap #define MAGIC1 0x656c6946 // File -#if 1 // TEST_BACKUP_HELPERS +/* + * File entity data format (v1): + * + * - 4-byte version number of the metadata, little endian (0x00000001 for v1) + * - 12 bytes of metadata + * - the file data itself + * + * i.e. a 16-byte metadata header followed by the raw file data. If the + * restore code does not recognize the metadata version, it can still + * interpret the file data itself correctly. + * + * file_metadata_v1: + * + * - 4 byte version number === 0x00000001 (little endian) + * - 4-byte access mode (little-endian) + * - undefined (8 bytes) + */ + +struct file_metadata_v1 { + int version; + int mode; + int undefined_1; + int undefined_2; +}; + +const static int CURRENT_METADATA_VERSION = 1; + +// auto-free buffer management object +class StAutoFree { +public: + StAutoFree(void* buffer) { mBuf = buffer; } + ~StAutoFree() { free(mBuf); } +private: + void* mBuf; +}; + +#if 0 // TEST_BACKUP_HELPERS #define LOGP(f, x...) printf(f "\n", x) #else #define LOGP(x...) LOGD(x) @@ -181,29 +217,48 @@ write_delete_file(BackupDataWriter* dataStream, const String8& key) } static int -write_update_file(BackupDataWriter* dataStream, int fd, const String8& key, +write_update_file(BackupDataWriter* dataStream, int fd, int mode, const String8& key, char const* realFilename) { - LOGP("write_update_file %s (%s)\n", realFilename, key.string()); + LOGP("write_update_file %s (%s) : mode 0%o\n", realFilename, key.string(), mode); const int bufsize = 4*1024; int err; int amt; int fileSize; int bytesLeft; + file_metadata_v1 metadata; char* buf = (char*)malloc(bufsize); + StAutoFree _autoFree(buf); + int crc = crc32(0L, Z_NULL, 0); - bytesLeft = fileSize = lseek(fd, 0, SEEK_END); + fileSize = lseek(fd, 0, SEEK_END); lseek(fd, 0, SEEK_SET); + if (sizeof(metadata) != 16) { + LOGE("ERROR: metadata block is the wrong size!"); + } + + bytesLeft = fileSize + sizeof(metadata); err = dataStream->WriteEntityHeader(key, bytesLeft); if (err != 0) { return err; } + // store the file metadata first + metadata.version = tolel(CURRENT_METADATA_VERSION); + metadata.mode = tolel(mode); + metadata.undefined_1 = metadata.undefined_2 = 0; + err = dataStream->WriteEntityData(&metadata, sizeof(metadata)); + if (err != 0) { + return err; + } + bytesLeft -= sizeof(metadata); // bytesLeft should == fileSize now + + // now store the file content while ((amt = read(fd, buf, bufsize)) != 0 && bytesLeft > 0) { bytesLeft -= amt; if (bytesLeft < 0) { @@ -232,8 +287,6 @@ write_update_file(BackupDataWriter* dataStream, int fd, const String8& key, " You aren't doing proper locking!", realFilename, fileSize, fileSize-bytesLeft); } - free(buf); - return NO_ERROR; } @@ -241,11 +294,19 @@ static int write_update_file(BackupDataWriter* dataStream, const String8& key, char const* realFilename) { int err; + struct stat st; + + err = stat(realFilename, &st); + if (err < 0) { + return errno; + } + int fd = open(realFilename, O_RDONLY); if (fd == -1) { return errno; } - err = write_update_file(dataStream, fd, key, realFilename); + + err = write_update_file(dataStream, fd, st.st_mode, key, realFilename); close(fd); return err; } @@ -257,6 +318,8 @@ compute_crc32(int fd) int amt; char* buf = (char*)malloc(bufsize); + StAutoFree _autoFree(buf); + int crc = crc32(0L, Z_NULL, 0); lseek(fd, 0, SEEK_SET); @@ -265,8 +328,6 @@ compute_crc32(int fd) crc = crc32(crc, (Bytef*)buf, amt); } - free(buf); - return crc; } @@ -356,7 +417,7 @@ back_up_files(int oldSnapshotFD, BackupDataWriter* dataStream, int newSnapshotFD g.s.modTime_sec, g.s.modTime_nsec, g.s.mode, g.s.size, g.s.crc32); if (f.modTime_sec != g.s.modTime_sec || f.modTime_nsec != g.s.modTime_nsec || f.mode != g.s.mode || f.size != g.s.size || f.crc32 != g.s.crc32) { - write_update_file(dataStream, fd, p, g.file.string()); + write_update_file(dataStream, fd, g.s.mode, p, g.file.string()); } close(fd); @@ -416,8 +477,22 @@ RestoreHelperBase::WriteFile(const String8& filename, BackupDataReader* in) return err; } - // TODO: World readable/writable for now. - mode = 0666; + // Get the metadata block off the head of the file entity and use that to + // set up the output file + file_metadata_v1 metadata; + amt = in->ReadEntityData(&metadata, sizeof(metadata)); + if (amt != sizeof(metadata)) { + LOGW("Could not read metadata for %s -- %ld / %s", filename.string(), + (long)amt, strerror(errno)); + return EIO; + } + metadata.version = fromlel(metadata.version); + metadata.mode = fromlel(metadata.mode); + if (metadata.version > CURRENT_METADATA_VERSION) { + LOGW("Restoring file with unsupported metadata version %d (currently %d)", + metadata.version, CURRENT_METADATA_VERSION); + } + mode = metadata.mode; // Write the file and compute the crc crc = crc32(0L, Z_NULL, 0); @@ -512,6 +587,7 @@ compare_file(const char* path, const unsigned char* data, int len) fprintf(stderr, "malloc(%d) failed\n", len); return ENOMEM; } + StAutoFree _autoFree(contents); bool sizesMatch = true; amt = lseek(fd, 0, SEEK_END); @@ -843,6 +919,7 @@ test_read_header_and_entity(BackupDataReader& reader, const char* str) int err; int bufSize = strlen(str)+1; char* buf = (char*)malloc(bufSize); + StAutoFree _autoFree(buf); String8 string; int cookie = 0x11111111; size_t actualSize; @@ -904,7 +981,6 @@ finished: if (err != NO_ERROR) { fprintf(stderr, "test_read_header_and_entity failed with %s\n", strerror(err)); } - free(buf); return err; } From aacacf7f7502198606ab4615ed88ab6f6174b7e6 Mon Sep 17 00:00:00 2001 From: Christopher Tate Date: Wed, 24 Jun 2009 11:20:51 -0700 Subject: [PATCH 134/541] Put back LOGP -> printf in the backup helper code --- libs/utils/BackupHelpers.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/utils/BackupHelpers.cpp b/libs/utils/BackupHelpers.cpp index 334661476..b309dbf43 100644 --- a/libs/utils/BackupHelpers.cpp +++ b/libs/utils/BackupHelpers.cpp @@ -77,7 +77,7 @@ private: void* mBuf; }; -#if 0 // TEST_BACKUP_HELPERS +#if 1 // TEST_BACKUP_HELPERS #define LOGP(f, x...) printf(f "\n", x) #else #define LOGP(x...) LOGD(x) From 585d4f410e818434f41fa024e3db6ce562f86d54 Mon Sep 17 00:00:00 2001 From: Christopher Tate Date: Wed, 24 Jun 2009 13:57:29 -0700 Subject: [PATCH 135/541] Only report "unknown metadata" once per restore helper Also removes the auto-free object, replacing it with direct memory manipulation. --- include/utils/BackupHelpers.h | 1 + libs/utils/BackupHelpers.cpp | 31 ++++++++++++++----------------- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/include/utils/BackupHelpers.h b/include/utils/BackupHelpers.h index 759a0cc6d..b1f504512 100644 --- a/include/utils/BackupHelpers.h +++ b/include/utils/BackupHelpers.h @@ -137,6 +137,7 @@ public: private: void* m_buf; + bool m_loggedUnknownMetadata; KeyedVector m_files; }; diff --git a/libs/utils/BackupHelpers.cpp b/libs/utils/BackupHelpers.cpp index b309dbf43..99a4abcb6 100644 --- a/libs/utils/BackupHelpers.cpp +++ b/libs/utils/BackupHelpers.cpp @@ -68,15 +68,6 @@ struct file_metadata_v1 { const static int CURRENT_METADATA_VERSION = 1; -// auto-free buffer management object -class StAutoFree { -public: - StAutoFree(void* buffer) { mBuf = buffer; } - ~StAutoFree() { free(mBuf); } -private: - void* mBuf; -}; - #if 1 // TEST_BACKUP_HELPERS #define LOGP(f, x...) printf(f "\n", x) #else @@ -230,8 +221,6 @@ write_update_file(BackupDataWriter* dataStream, int fd, int mode, const String8& file_metadata_v1 metadata; char* buf = (char*)malloc(bufsize); - StAutoFree _autoFree(buf); - int crc = crc32(0L, Z_NULL, 0); @@ -245,6 +234,7 @@ write_update_file(BackupDataWriter* dataStream, int fd, int mode, const String8& bytesLeft = fileSize + sizeof(metadata); err = dataStream->WriteEntityHeader(key, bytesLeft); if (err != 0) { + free(buf); return err; } @@ -254,6 +244,7 @@ write_update_file(BackupDataWriter* dataStream, int fd, int mode, const String8& metadata.undefined_1 = metadata.undefined_2 = 0; err = dataStream->WriteEntityData(&metadata, sizeof(metadata)); if (err != 0) { + free(buf); return err; } bytesLeft -= sizeof(metadata); // bytesLeft should == fileSize now @@ -266,6 +257,7 @@ write_update_file(BackupDataWriter* dataStream, int fd, int mode, const String8& } err = dataStream->WriteEntityData(buf, amt); if (err != 0) { + free(buf); return err; } } @@ -279,6 +271,7 @@ write_update_file(BackupDataWriter* dataStream, int fd, int mode, const String8& bytesLeft -= amt; err = dataStream->WriteEntityData(buf, amt); if (err != 0) { + free(buf); return err; } } @@ -287,6 +280,7 @@ write_update_file(BackupDataWriter* dataStream, int fd, int mode, const String8& " You aren't doing proper locking!", realFilename, fileSize, fileSize-bytesLeft); } + free(buf); return NO_ERROR; } @@ -318,8 +312,6 @@ compute_crc32(int fd) int amt; char* buf = (char*)malloc(bufsize); - StAutoFree _autoFree(buf); - int crc = crc32(0L, Z_NULL, 0); lseek(fd, 0, SEEK_SET); @@ -328,6 +320,7 @@ compute_crc32(int fd) crc = crc32(crc, (Bytef*)buf, amt); } + free(buf); return crc; } @@ -451,6 +444,7 @@ back_up_files(int oldSnapshotFD, BackupDataWriter* dataStream, int newSnapshotFD RestoreHelperBase::RestoreHelperBase() { m_buf = malloc(RESTORE_BUF_SIZE); + m_loggedUnknownMetadata = false; } RestoreHelperBase::~RestoreHelperBase() @@ -489,8 +483,11 @@ RestoreHelperBase::WriteFile(const String8& filename, BackupDataReader* in) metadata.version = fromlel(metadata.version); metadata.mode = fromlel(metadata.mode); if (metadata.version > CURRENT_METADATA_VERSION) { - LOGW("Restoring file with unsupported metadata version %d (currently %d)", - metadata.version, CURRENT_METADATA_VERSION); + if (!m_loggedUnknownMetadata) { + m_loggedUnknownMetadata = true; + LOGW("Restoring file with unsupported metadata version %d (currently %d)", + metadata.version, CURRENT_METADATA_VERSION); + } } mode = metadata.mode; @@ -587,7 +584,6 @@ compare_file(const char* path, const unsigned char* data, int len) fprintf(stderr, "malloc(%d) failed\n", len); return ENOMEM; } - StAutoFree _autoFree(contents); bool sizesMatch = true; amt = lseek(fd, 0, SEEK_END); @@ -614,6 +610,7 @@ compare_file(const char* path, const unsigned char* data, int len) } } + free(contents); return contentsMatch && sizesMatch ? 0 : 1; } @@ -919,7 +916,6 @@ test_read_header_and_entity(BackupDataReader& reader, const char* str) int err; int bufSize = strlen(str)+1; char* buf = (char*)malloc(bufSize); - StAutoFree _autoFree(buf); String8 string; int cookie = 0x11111111; size_t actualSize; @@ -981,6 +977,7 @@ finished: if (err != NO_ERROR) { fprintf(stderr, "test_read_header_and_entity failed with %s\n", strerror(err)); } + free(buf); return err; } From 35b4039b48e9edf1be2fff916a8e8f0430e619f3 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Wed, 24 Jun 2009 23:12:06 -0700 Subject: [PATCH 136/541] move ui/Time.cpp to core/jni, since this is the only place it is used --- include/utils/TimeUtils.h | 89 --------------------------------------- 1 file changed, 89 deletions(-) delete mode 100644 include/utils/TimeUtils.h diff --git a/include/utils/TimeUtils.h b/include/utils/TimeUtils.h deleted file mode 100644 index b19e02126..000000000 --- a/include/utils/TimeUtils.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (C) 2005 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_TIME_H -#define ANDROID_TIME_H - -#include -#include -#include -#include -#include -#include -#include - -namespace android { - -/* - * This class is the core implementation of the android.util.Time java - * class. It doesn't implement some of the methods that are implemented - * in Java. They could be done here, but it's not expected that this class - * will be used. If that assumption is incorrect, feel free to update this - * file. The reason to do it here is to not mix the implementation of this - * class and the jni glue code. - */ -class Time -{ -public: - struct tm t; - - // this object doesn't own this string - const char *timezone; - - enum { - SEC = 1, - MIN = 2, - HOUR = 3, - MDAY = 4, - MON = 5, - YEAR = 6, - WDAY = 7, - YDAY = 8 - }; - - static int compare(Time& a, Time& b); - - Time(); - - void switchTimezone(const char *timezone); - String8 format(const char *format, const struct strftime_locale *locale) const; - void format2445(short* buf, bool hasTime) const; - String8 toString() const; - void setToNow(); - int64_t toMillis(bool ignoreDst); - void set(int64_t millis); - - inline void set(int sec, int min, int hour, int mday, int mon, int year, - int isdst) - { - this->t.tm_sec = sec; - this->t.tm_min = min; - this->t.tm_hour = hour; - this->t.tm_mday = mday; - this->t.tm_mon = mon; - this->t.tm_year = year; - this->t.tm_isdst = isdst; -#ifdef HAVE_TM_GMTOFF - this->t.tm_gmtoff = 0; -#endif - this->t.tm_wday = 0; - this->t.tm_yday = 0; - } -}; - -}; // namespace android - -#endif // ANDROID_TIME_H From 8b72a9bb4e06e447b3dee91ba9b82b08bc8783f4 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Thu, 25 Jun 2009 19:48:04 -0700 Subject: [PATCH 137/541] Expand support for different screen sizes. Applications can now declare that they support small, normal, or large screens. Resource selection can also be done based on these sizes. By default, pre-Donut apps are false for small and large, and Donut or later apps are assumed to support all sizes. In either case they can use in their manifest to declare what they actually support. --- include/utils/ResourceTypes.h | 50 +++++++++++++++++++++++++++++++---- libs/utils/ResourceTypes.cpp | 5 ++-- 2 files changed, 48 insertions(+), 7 deletions(-) diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h index f1029b7bc..5c41ead4a 100644 --- a/include/utils/ResourceTypes.h +++ b/include/utils/ResourceTypes.h @@ -866,7 +866,7 @@ struct ResTable_config uint8_t keyboard; uint8_t navigation; uint8_t inputFlags; - uint8_t pad0; + uint8_t inputPad0; }; uint32_t input; }; @@ -905,6 +905,23 @@ struct ResTable_config uint32_t version; }; + enum { + SCREENLAYOUT_ANY = 0x0000, + SCREENLAYOUT_SMALL = 0x0001, + SCREENLAYOUT_NORMAL = 0x0002, + SCREENLAYOUT_LARGE = 0x0003, + }; + + union { + struct { + uint8_t screenLayout; + uint8_t screenConfigPad0; + uint8_t screenConfigPad1; + uint8_t screenConfigPad2; + }; + uint32_t screenConfig; + }; + inline void copyFromDeviceNoSwap(const ResTable_config& o) { const size_t size = dtohl(o.size); if (size >= sizeof(ResTable_config)) { @@ -950,6 +967,8 @@ struct ResTable_config diff = (int32_t)(screenSize - o.screenSize); if (diff != 0) return diff; diff = (int32_t)(version - o.version); + if (diff != 0) return diff; + diff = (int32_t)(screenLayout - o.screenLayout); return (int)diff; } @@ -967,7 +986,8 @@ struct ResTable_config CONFIG_ORIENTATION = 0x0080, CONFIG_DENSITY = 0x0100, CONFIG_SCREEN_SIZE = 0x0200, - CONFIG_VERSION = 0x0400 + CONFIG_VERSION = 0x0400, + CONFIG_SCREEN_LAYOUT = 0x0800 }; // Compare two configuration, returning CONFIG_* flags set for each value @@ -985,6 +1005,7 @@ struct ResTable_config if (navigation != o.navigation) diffs |= CONFIG_NAVIGATION; if (screenSize != o.screenSize) diffs |= CONFIG_SCREEN_SIZE; if (version != o.version) diffs |= CONFIG_VERSION; + if (screenLayout != o.screenLayout) diffs |= CONFIG_SCREEN_LAYOUT; return diffs; } @@ -1062,6 +1083,13 @@ struct ResTable_config } } + if (screenConfig || o.screenConfig) { + if (screenLayout != o.screenLayout) { + if (!screenLayout) return false; + if (!o.screenLayout) return true; + } + } + if (version || o.version) { if (sdkVersion != o.sdkVersion) { if (!sdkVersion) return false; @@ -1191,6 +1219,12 @@ struct ResTable_config } } + if (screenConfig || o.screenConfig) { + if ((screenLayout != o.screenLayout) && requested->screenLayout) { + return (screenLayout); + } + } + if (version || o.version) { if ((sdkVersion != o.sdkVersion) && requested->sdkVersion) { return (sdkVersion); @@ -1282,6 +1316,12 @@ struct ResTable_config return false; } } + if (screenConfig != 0) { + if (settings.screenLayout != 0 && screenLayout != 0 + && screenLayout != settings.screenLayout) { + return false; + } + } if (version != 0) { if (settings.sdkVersion != 0 && sdkVersion != 0 && sdkVersion != settings.sdkVersion) { @@ -1310,13 +1350,13 @@ struct ResTable_config String8 toString() const { char buf[200]; - sprintf(buf, "imsi=%d/%d lang=%c%c reg=%c%c orient=0x%02x touch=0x%02x dens=0x%02x " - "kbd=0x%02x nav=0x%02x input=0x%02x screenW=0x%04x screenH=0x%04x vers=%d.%d", + sprintf(buf, "imsi=%d/%d lang=%c%c reg=%c%c orient=%d touch=%d dens=%d " + "kbd=%d nav=%d input=%d scrnW=%d scrnH=%d layout=%d vers=%d.%d", mcc, mnc, language[0] ? language[0] : '-', language[1] ? language[1] : '-', country[0] ? country[0] : '-', country[1] ? country[1] : '-', orientation, touchscreen, density, keyboard, navigation, inputFlags, - screenWidth, screenHeight, sdkVersion, minorVersion); + screenWidth, screenHeight, screenLayout, sdkVersion, minorVersion); return String8(buf); } }; diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp index e4f9f0f9f..7a33220d1 100644 --- a/libs/utils/ResourceTypes.cpp +++ b/libs/utils/ResourceTypes.cpp @@ -3919,7 +3919,7 @@ void ResTable::print(bool inclValues) const printf(" NON-INTEGER ResTable_type ADDRESS: %p\n", type); continue; } - printf(" config %d lang=%c%c cnt=%c%c orien=%d touch=%d density=%d key=%d infl=%d nav=%d w=%d h=%d\n", + printf(" config %d lang=%c%c cnt=%c%c orien=%d touch=%d density=%d key=%d infl=%d nav=%d w=%d h=%d lyt=%d\n", (int)configIndex, type->config.language[0] ? type->config.language[0] : '-', type->config.language[1] ? type->config.language[1] : '-', @@ -3932,7 +3932,8 @@ void ResTable::print(bool inclValues) const type->config.inputFlags, type->config.navigation, dtohs(type->config.screenWidth), - dtohs(type->config.screenHeight)); + dtohs(type->config.screenHeight), + type->config.screenLayout); size_t entryCount = dtohl(type->entryCount); uint32_t entriesStart = dtohl(type->entriesStart); if ((entriesStart&0x3) != 0) { From ff574337456993b671b503bf1f65efcfc3ac6794 Mon Sep 17 00:00:00 2001 From: Joe Onorato Date: Fri, 26 Jun 2009 17:19:11 -0400 Subject: [PATCH 138/541] Better (and less) logging from backup. --- libs/utils/BackupData.cpp | 5 ++++- libs/utils/BackupHelpers.cpp | 7 +++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/libs/utils/BackupData.cpp b/libs/utils/BackupData.cpp index 0868cff6c..cce754a08 100644 --- a/libs/utils/BackupData.cpp +++ b/libs/utils/BackupData.cpp @@ -107,7 +107,10 @@ BackupDataWriter::WriteEntityHeader(const String8& key, size_t dataSize) } else { k = key; } - LOGD("m_keyPrefix=%s key=%s k=%s", m_keyPrefix.string(), key.string(), k.string()); + if (true) { + LOGD("Writing entity: prefix='%s' key='%s' dataSize=%d", m_keyPrefix.string(), key.string(), + dataSize); + } entity_header_v1 header; ssize_t keyLen; diff --git a/libs/utils/BackupHelpers.cpp b/libs/utils/BackupHelpers.cpp index 99a4abcb6..4ad9b5179 100644 --- a/libs/utils/BackupHelpers.cpp +++ b/libs/utils/BackupHelpers.cpp @@ -68,11 +68,15 @@ struct file_metadata_v1 { const static int CURRENT_METADATA_VERSION = 1; -#if 1 // TEST_BACKUP_HELPERS +#if 1 +#define LOGP(f, x...) +#else +#if TEST_BACKUP_HELPERS #define LOGP(f, x...) printf(f "\n", x) #else #define LOGP(x...) LOGD(x) #endif +#endif const static int ROUND_UP[4] = { 0, 3, 2, 1 }; @@ -349,7 +353,6 @@ back_up_files(int oldSnapshotFD, BackupDataWriter* dataStream, int newSnapshotFD err = stat(file, &st); if (err != 0) { - LOGW("Error stating file %s", file); r.deleted = true; } else { r.deleted = false; From cfd03822f0ac8a78407dd85876e4d532591f620f Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Tue, 30 Jun 2009 13:27:30 -0700 Subject: [PATCH 139/541] Fix issue #1673793: Theme styles don't apply. It turns out this was not a problem in the resource code at all. Rather, the system process has a cache of pre-loaded attributes it uses to avoid continually reloading things as it needs them. Well it turns out this cache wasn't flushed after a package was uninstalled or a configuration changed, so you could re-install an app where you change its style resources so its theme now points to one that is inconsistent in the cache. This is mostly a problem for developers, where they continually install new versions of an app where resources have changed. This could possibly show up when updating an app on a normal phone, although the problem would eventually correct itself since this cache uses weak references. Anyway, the cache is now reworked to be flushed appropriately. This change also includes an update to aapt to be able to dump the contents of bags in resources. --- include/utils/ResourceTypes.h | 4 +- libs/utils/ResourceTypes.cpp | 134 ++++++++++++++++++++-------------- 2 files changed, 83 insertions(+), 55 deletions(-) diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h index 5c41ead4a..eb4151a1a 100644 --- a/include/utils/ResourceTypes.h +++ b/include/utils/ResourceTypes.h @@ -1441,7 +1441,7 @@ struct ResTable_type * 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 @@ private: 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/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp index 7a33220d1..4a5063a30 100644 --- a/libs/utils/ResourceTypes.cpp +++ b/libs/utils/ResourceTypes.cpp @@ -3845,7 +3845,7 @@ void print_complex(uint32_t complex, bool isFraction) & 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 print_complex(uint32_t complex, bool isFraction) } } +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 @@ void ResTable::print(bool inclValues) const 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(""); + 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 @@ void ResTable::print(bool inclValues) const 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; iname.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)); } } } From bd875d2638625ef959a98d8bd69c58e9a52d6cf4 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Mon, 6 Jul 2009 11:07:40 -0700 Subject: [PATCH 140/541] Don't re-parse the framework resources all the time. A small optimization to the resource code, to not re-parse the framework resources every time we build a new AssetManager. Instead, you can now construct a ResTable from a previously created one... of course, like the existing code for using the data in-place, you can't delete the original ResTable until you have deleted the one that has been constructed from it. --- include/utils/AssetManager.h | 11 +++- include/utils/ResourceTypes.h | 1 + libs/utils/Asset.cpp | 5 +- libs/utils/AssetManager.cpp | 97 ++++++++++++++++++++++++++++++----- libs/utils/ResourceTypes.cpp | 96 +++++++++++++++++++++++++--------- 5 files changed, 168 insertions(+), 42 deletions(-) diff --git a/include/utils/AssetManager.h b/include/utils/AssetManager.h index c11429eaf..d8994e05a 100644 --- a/include/utils/AssetManager.h +++ b/include/utils/AssetManager.h @@ -251,6 +251,9 @@ private: Asset* getResourceTableAsset(); Asset* setResourceTableAsset(Asset* asset); + ResTable* getResourceTable(); + ResTable* setResourceTable(ResTable* res); + bool isUpToDate(); protected: @@ -265,6 +268,7 @@ private: time_t mModWhen; Asset* mResourceTableAsset; + ResTable* mResourceTable; static Mutex gLock; static DefaultKeyedVector > gOpen; @@ -288,8 +292,11 @@ private: */ ZipFileRO* getZip(const String8& path); - Asset* getZipResourceTable(const String8& path); - Asset* setZipResourceTable(const String8& path, Asset* asset); + Asset* getZipResourceTableAsset(const String8& path); + Asset* setZipResourceTableAsset(const String8& path, Asset* asset); + + ResTable* getZipResourceTable(const String8& path); + ResTable* setZipResourceTable(const String8& path, ResTable* res); // generate path, e.g. "common/en-US-noogle.zip" static String8 getPathName(const char* path); diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h index eb4151a1a..93bca4aef 100644 --- a/include/utils/ResourceTypes.h +++ b/include/utils/ResourceTypes.h @@ -1580,6 +1580,7 @@ public: bool copyData=false); status_t add(Asset* asset, void* cookie, bool copyData=false); + status_t add(ResTable* src); status_t getError() const; diff --git a/libs/utils/Asset.cpp b/libs/utils/Asset.cpp index 91203ddb4..23cb72d4e 100644 --- a/libs/utils/Asset.cpp +++ b/libs/utils/Asset.cpp @@ -582,11 +582,14 @@ const void* _FileAsset::ensureAlignment(FileMap* map) if ((((size_t)data)&0x3) == 0) { // We can return this directly if it is aligned on a word // boundary. + LOGV("Returning aligned FileAsset %p (%s).", this, + getAssetSource()); return data; } // If not aligned on a word boundary, then we need to copy it into // our own buffer. - LOGV("Copying FileAsset %p to buffer size %d to make it aligned.", this, (int)mLength); + LOGV("Copying FileAsset %p (%s) to buffer size %d to make it aligned.", this, + getAssetSource(), (int)mLength); unsigned char* buf = new unsigned char[mLength]; if (buf == NULL) { LOGE("alloc of %ld bytes failed\n", (long) mLength); diff --git a/libs/utils/AssetManager.cpp b/libs/utils/AssetManager.cpp index 4126bfb7a..5a05e6a61 100644 --- a/libs/utils/AssetManager.cpp +++ b/libs/utils/AssetManager.cpp @@ -395,21 +395,41 @@ const ResTable* AssetManager::getResTable(bool required) const const size_t N = mAssetPaths.size(); for (size_t i=0; i(this)-> - mZipSet.getZipResourceTable(ap.path); - if (ass == NULL) { - LOGV("loading resource table %s\n", ap.path.string()); + if (i == 0) { + // The first item is typically the framework resources, + // which we want to avoid parsing every time. + sharedRes = const_cast(this)-> + mZipSet.getZipResourceTable(ap.path); + } + if (sharedRes == NULL) { ass = const_cast(this)-> - openNonAssetInPathLocked("resources.arsc", - Asset::ACCESS_BUFFER, - ap); - if (ass != NULL && ass != kExcludedAsset) { + mZipSet.getZipResourceTableAsset(ap.path); + if (ass == NULL) { + LOGV("loading resource table %s\n", ap.path.string()); ass = const_cast(this)-> - mZipSet.setZipResourceTable(ap.path, ass); + openNonAssetInPathLocked("resources.arsc", + Asset::ACCESS_BUFFER, + ap); + if (ass != NULL && ass != kExcludedAsset) { + ass = const_cast(this)-> + mZipSet.setZipResourceTableAsset(ap.path, ass); + } + } + + if (i == 0 && ass != NULL) { + // If this is the first resource table in the asset + // manager, then we are going to cache it so that we + // can quickly copy it out for others. + LOGV("Creating shared resources for %s", ap.path.string()); + sharedRes = new ResTable(); + sharedRes->add(ass, (void*)(i+1), false); + sharedRes = const_cast(this)-> + mZipSet.setZipResourceTable(ap.path, sharedRes); } } } else { @@ -420,13 +440,19 @@ const ResTable* AssetManager::getResTable(bool required) const ap); shared = false; } - if (ass != NULL && ass != kExcludedAsset) { + if ((ass != NULL || sharedRes != NULL) && ass != kExcludedAsset) { if (rt == NULL) { mResources = rt = new ResTable(); updateResourceParamsLocked(); } LOGV("Installing resource asset %p in to table %p\n", ass, mResources); - rt->add(ass, (void*)(i+1), !shared); + if (sharedRes != NULL) { + LOGV("Copying existing resources for %s", ap.path.string()); + rt->add(sharedRes); + } else { + LOGV("Parsing resources for %s", ap.path.string()); + rt->add(ass, (void*)(i+1), !shared); + } if (!shared) { delete ass; @@ -1510,7 +1536,8 @@ Mutex AssetManager::SharedZip::gLock; DefaultKeyedVector > AssetManager::SharedZip::gOpen; AssetManager::SharedZip::SharedZip(const String8& path, time_t modWhen) - : mPath(path), mZipFile(NULL), mModWhen(modWhen), mResourceTableAsset(NULL) + : mPath(path), mZipFile(NULL), mModWhen(modWhen), + mResourceTableAsset(NULL), mResourceTable(NULL) { //LOGI("Creating SharedZip %p %s\n", this, (const char*)mPath); mZipFile = new ZipFileRO; @@ -1563,6 +1590,25 @@ Asset* AssetManager::SharedZip::setResourceTableAsset(Asset* asset) return mResourceTableAsset; } +ResTable* AssetManager::SharedZip::getResourceTable() +{ + LOGV("Getting from SharedZip %p resource table %p\n", this, mResourceTable); + return mResourceTable; +} + +ResTable* AssetManager::SharedZip::setResourceTable(ResTable* res) +{ + { + AutoMutex _l(gLock); + if (mResourceTable == NULL) { + mResourceTable = res; + return res; + } + } + delete res; + return mResourceTable; +} + bool AssetManager::SharedZip::isUpToDate() { time_t modWhen = getFileModDate(mPath.string()); @@ -1572,6 +1618,9 @@ bool AssetManager::SharedZip::isUpToDate() AssetManager::SharedZip::~SharedZip() { //LOGI("Destroying SharedZip %p %s\n", this, (const char*)mPath); + if (mResourceTable != NULL) { + delete mResourceTable; + } if (mResourceTableAsset != NULL) { delete mResourceTableAsset; } @@ -1627,7 +1676,7 @@ ZipFileRO* AssetManager::ZipSet::getZip(const String8& path) return zip->getZip(); } -Asset* AssetManager::ZipSet::getZipResourceTable(const String8& path) +Asset* AssetManager::ZipSet::getZipResourceTableAsset(const String8& path) { int idx = getIndex(path); sp zip = mZipFile[idx]; @@ -1638,7 +1687,7 @@ Asset* AssetManager::ZipSet::getZipResourceTable(const String8& path) return zip->getResourceTableAsset(); } -Asset* AssetManager::ZipSet::setZipResourceTable(const String8& path, +Asset* AssetManager::ZipSet::setZipResourceTableAsset(const String8& path, Asset* asset) { int idx = getIndex(path); @@ -1647,6 +1696,26 @@ Asset* AssetManager::ZipSet::setZipResourceTable(const String8& path, return zip->setResourceTableAsset(asset); } +ResTable* AssetManager::ZipSet::getZipResourceTable(const String8& path) +{ + int idx = getIndex(path); + sp zip = mZipFile[idx]; + if (zip == NULL) { + zip = SharedZip::get(path); + mZipFile.editItemAt(idx) = zip; + } + return zip->getResourceTable(); +} + +ResTable* AssetManager::ZipSet::setZipResourceTable(const String8& path, + ResTable* res) +{ + int idx = getIndex(path); + sp zip = mZipFile[idx]; + // doesn't make sense to call before previously accessing. + return zip->setResourceTable(res); +} + /* * Generate the partial pathname for the specified archive. The caller * gets to prepend the asset root directory. diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp index 4a5063a30..109f28d30 100644 --- a/libs/utils/ResourceTypes.cpp +++ b/libs/utils/ResourceTypes.cpp @@ -1136,8 +1136,9 @@ status_t ResXMLTree::validateNode(const ResXMLTree_node* node) const struct ResTable::Header { - Header() : ownedData(NULL), header(NULL) { } + Header(ResTable* _owner) : owner(_owner), ownedData(NULL), header(NULL) { } + ResTable* const owner; void* ownedData; const ResTable_header* header; size_t size; @@ -1163,8 +1164,8 @@ struct ResTable::Type struct ResTable::Package { - Package(const Header* _header, const ResTable_package* _package) - : header(_header), package(_package) { } + Package(ResTable* _owner, const Header* _header, const ResTable_package* _package) + : owner(_owner), header(_header), package(_package) { } ~Package() { size_t i = types.size(); @@ -1174,10 +1175,14 @@ struct ResTable::Package } } + ResTable* const owner; const Header* const header; const ResTable_package* const package; Vector types; + ResStringPool typeStrings; + ResStringPool keyStrings; + const Type* getType(size_t idx) const { return idx < types.size() ? types[idx] : NULL; } @@ -1188,13 +1193,16 @@ struct ResTable::Package // table that defined the package); the ones after are skins on top of it. struct ResTable::PackageGroup { - PackageGroup(const String16& _name, uint32_t _id) - : name(_name), id(_id), typeCount(0), bags(NULL) { } + PackageGroup(ResTable* _owner, const String16& _name, uint32_t _id) + : owner(_owner), name(_name), id(_id), typeCount(0), bags(NULL) { } ~PackageGroup() { clearBagCache(); const size_t N = packages.size(); for (size_t i=0; iowner == owner) { + delete pkg; + } } } @@ -1225,15 +1233,17 @@ struct ResTable::PackageGroup } } + ResTable* const owner; String16 const name; uint32_t const id; Vector packages; + + // This is for finding typeStrings and other common package stuff. + Package* basePackage; - // Taken from the root package. - ResStringPool typeStrings; - ResStringPool keyStrings; + // For quick access. size_t typeCount; - + // Computed attribute bags, first indexed by the type and second // by the entry in that type. bag_set*** bags; @@ -1560,11 +1570,36 @@ status_t ResTable::add(Asset* asset, void* cookie, bool copyData) return add(data, size, cookie, asset, copyData); } +status_t ResTable::add(ResTable* src) +{ + mError = src->mError; + mParams = src->mParams; + + for (size_t i=0; imHeaders.size(); i++) { + mHeaders.add(src->mHeaders[i]); + } + + for (size_t i=0; imPackageGroups.size(); i++) { + PackageGroup* srcPg = src->mPackageGroups[i]; + PackageGroup* pg = new PackageGroup(this, srcPg->name, srcPg->id); + for (size_t j=0; jpackages.size(); j++) { + pg->packages.add(srcPg->packages[j]); + } + pg->basePackage = srcPg->basePackage; + pg->typeCount = srcPg->typeCount; + mPackageGroups.add(pg); + } + + memcpy(mPackageMap, src->mPackageMap, sizeof(mPackageMap)); + + return mError; +} + status_t ResTable::add(const void* data, size_t size, void* cookie, Asset* asset, bool copyData) { if (!data) return NO_ERROR; - Header* header = new Header; + Header* header = new Header(this); header->index = mHeaders.size(); header->cookie = cookie; mHeaders.add(header); @@ -1682,10 +1717,12 @@ void ResTable::uninit() N = mHeaders.size(); for (size_t i=0; iownedData) { - free(header->ownedData); + if (header->owner == this) { + if (header->ownedData) { + free(header->ownedData); + } + delete header; } - delete header; } mPackageGroups.clear(); @@ -1728,8 +1765,8 @@ bool ResTable::getResourceName(uint32_t resID, resource_name* outName) const outName->package = grp->name.string(); outName->packageLen = grp->name.size(); - outName->type = grp->typeStrings.stringAt(t, &outName->typeLen); - outName->name = grp->keyStrings.stringAt( + outName->type = grp->basePackage->typeStrings.stringAt(t, &outName->typeLen); + outName->name = grp->basePackage->keyStrings.stringAt( dtohl(entry->key.index), &outName->nameLen); return true; } @@ -2331,13 +2368,13 @@ nope: continue; } - const ssize_t ti = group->typeStrings.indexOfString(type, typeLen); + const ssize_t ti = group->basePackage->typeStrings.indexOfString(type, typeLen); if (ti < 0) { TABLE_NOISY(printf("Type not found in package %s\n", String8(group->name).string())); continue; } - const ssize_t ei = group->keyStrings.indexOfString(name, nameLen); + const ssize_t ei = group->basePackage->keyStrings.indexOfString(name, nameLen); if (ei < 0) { TABLE_NOISY(printf("Name not found in package %s\n", String8(group->name).string())); continue; @@ -3630,25 +3667,36 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg, PackageGroup* group = NULL; uint32_t id = dtohl(pkg->id); if (id != 0 && id < 256) { + + package = new Package(this, header, pkg); + if (package == NULL) { + return (mError=NO_MEMORY); + } + size_t idx = mPackageMap[id]; if (idx == 0) { idx = mPackageGroups.size()+1; char16_t tmpName[sizeof(pkg->name)/sizeof(char16_t)]; strcpy16_dtoh(tmpName, pkg->name, sizeof(pkg->name)/sizeof(char16_t)); - group = new PackageGroup(String16(tmpName), id); + group = new PackageGroup(this, String16(tmpName), id); if (group == NULL) { + delete package; return (mError=NO_MEMORY); } - err = group->typeStrings.setTo(base+dtohl(pkg->typeStrings), + err = package->typeStrings.setTo(base+dtohl(pkg->typeStrings), header->dataEnd-(base+dtohl(pkg->typeStrings))); if (err != NO_ERROR) { + delete group; + delete package; return (mError=err); } - err = group->keyStrings.setTo(base+dtohl(pkg->keyStrings), + err = package->keyStrings.setTo(base+dtohl(pkg->keyStrings), header->dataEnd-(base+dtohl(pkg->keyStrings))); if (err != NO_ERROR) { + delete group; + delete package; return (mError=err); } @@ -3657,6 +3705,8 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg, if (err < NO_ERROR) { return (mError=err); } + group->basePackage = package; + mPackageMap[id] = (uint8_t)idx; } else { group = mPackageGroups.itemAt(idx-1); @@ -3664,10 +3714,6 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg, return (mError=UNKNOWN_ERROR); } } - package = new Package(header, pkg); - if (package == NULL) { - return (mError=NO_MEMORY); - } err = group->packages.add(package); if (err < NO_ERROR) { return (mError=err); From 34ed82706a56c94993efa2b679ff1520e5db5bb7 Mon Sep 17 00:00:00 2001 From: Andy McFadden Date: Tue, 7 Jul 2009 10:01:12 -0700 Subject: [PATCH 141/541] Fix sim build. Looks like older gcc (4.1.x) doesn't properly handle templated fanciness. Apparently that's what we have on the build server. --- include/utils/List.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/utils/List.h b/include/utils/List.h index 4041a8912..403cd7f1e 100644 --- a/include/utils/List.h +++ b/include/utils/List.h @@ -154,9 +154,9 @@ protected: inline _NodePtr getNode() const { return mpNode; } + _NodePtr mpNode; /* should be private, but older gcc fails */ private: friend class List; - _NodePtr mpNode; }; public: From 44dad3e006e0a966d93473dde3d74b2b43555519 Mon Sep 17 00:00:00 2001 From: Daisuke Miyakawa Date: Tue, 30 Jun 2009 20:40:42 +0900 Subject: [PATCH 142/541] Add useful functions to String8, which enables users to convert between UTF-8 and UTF-32 It will be used in SQL functions in external/sqlite/android. See https://android-git.corp.google.com/g/Gerrit#change,5511 for example. Related internal bug id: 1707173 --- include/utils/String8.h | 116 +++++++++++++++- libs/utils/String8.cpp | 298 ++++++++++++++++++++++++++++++++++++---- 2 files changed, 383 insertions(+), 31 deletions(-) diff --git a/include/utils/String8.h b/include/utils/String8.h index c49faf6fe..ecc577437 100644 --- a/include/utils/String8.h +++ b/include/utils/String8.h @@ -29,11 +29,107 @@ // --------------------------------------------------------------------------- +extern "C" { + +typedef uint32_t char32_t; + +size_t strlen32(const char32_t *); +size_t strnlen32(const char32_t *, size_t); + +/* + * Returns the length of "src" when "src" is valid UTF-8 string. + * Returns 0 if src is NULL, 0-length string or non UTF-8 string. + * This function should be used to determine whether "src" is valid UTF-8 + * characters with valid unicode codepoints. "src" must be null-terminated. + * + * If you are going to use other GetUtf... functions defined in this header + * with string which may not be valid UTF-8 with valid codepoint (form 0 to + * 0x10FFFF), you should use this function before calling others, since the + * other functions do not check whether the string is valid UTF-8 or not. + * + * If you do not care whether "src" is valid UTF-8 or not, you should use + * strlen() as usual, which should be much faster. + */ +size_t utf8_length(const char *src); + +/* + * Returns the UTF-32 length of "src". + */ +size_t utf32_length(const char *src, size_t src_len); + +/* + * Returns the UTF-8 length of "src". + */ +size_t utf8_length_from_utf32(const char32_t *src, size_t src_len); + +/* + * Returns the unicode value at "index". + * Returns -1 when the index is invalid (equals to or more than "src_len"). + * If returned value is positive, it is able to be converted to char32_t, which + * is unsigned. Then, if "next_index" is not NULL, the next index to be used is + * stored in "next_index". "next_index" can be NULL. + */ +int32_t utf32_at(const char *src, size_t src_len, + size_t index, size_t *next_index); + +/* + * Stores a UTF-32 string converted from "src" in "dst", if "dst_length" is not + * large enough to store the string, the part of the "src" string is stored + * into "dst". + * Returns the size actually used for storing the string. + * "dst" is not null-terminated when dst_len is fully used (like strncpy). + */ +size_t utf8_to_utf32(const char* src, size_t src_len, + char32_t* dst, size_t dst_len); + +/* + * Stores a UTF-8 string converted from "src" in "dst", if "dst_length" is not + * large enough to store the string, the part of the "src" string is stored + * into "dst" as much as possible. See the examples for more detail. + * Returns the size actually used for storing the string. + * dst" is not null-terminated when dst_len is fully used (like strncpy). + * + * Example 1 + * "src" == \u3042\u3044 (\xE3\x81\x82\xE3\x81\x84) + * "src_len" == 2 + * "dst_len" >= 7 + * -> + * Returned value == 6 + * "dst" becomes \xE3\x81\x82\xE3\x81\x84\0 + * (note that "dst" is null-terminated) + * + * Example 2 + * "src" == \u3042\u3044 (\xE3\x81\x82\xE3\x81\x84) + * "src_len" == 2 + * "dst_len" == 5 + * -> + * Returned value == 3 + * "dst" becomes \xE3\x81\x82\0 + * (note that "dst" is null-terminated, but \u3044 is not stored in "dst" + * since "dst" does not have enough size to store the character) + * + * Example 3 + * "src" == \u3042\u3044 (\xE3\x81\x82\xE3\x81\x84) + * "src_len" == 2 + * "dst_len" == 6 + * -> + * Returned value == 6 + * "dst" becomes \xE3\x81\x82\xE3\x81\x84 + * (note that "dst" is NOT null-terminated, like strncpy) + */ +size_t utf32_to_utf8(const char32_t* src, size_t src_len, + char* dst, size_t dst_len); + +} + +// --------------------------------------------------------------------------- + namespace android { class TextOutput; -//! This is a string holding UTF-8 characters. +//! This is a string holding UTF-8 characters. Does not allow the value more +// than 0x10FFFF, which is not valid unicode codepoint. class String8 { public: @@ -45,7 +141,8 @@ public: explicit String8(const String16& o); explicit String8(const char16_t* o); explicit String8(const char16_t* o, size_t numChars); - + explicit String8(const char32_t* o); + explicit String8(const char32_t* o, size_t numChars); ~String8(); inline const char* string() const; @@ -59,11 +156,20 @@ public: status_t setTo(const char* other); status_t setTo(const char* other, size_t numChars); status_t setTo(const char16_t* other, size_t numChars); - + status_t setTo(const char32_t* other, + size_t length); + status_t append(const String8& other); status_t append(const char* other); status_t append(const char* other, size_t numChars); + // Note that this function takes O(N) time to calculate the value. + // No cache value is stored. + size_t getUtf32Length() const; + int32_t getUtf32At(size_t index, + size_t *next_index) const; + size_t getUtf32(char32_t* dst, size_t dst_len) const; + inline String8& operator=(const String8& other); inline String8& operator=(const char* other); @@ -103,7 +209,7 @@ public: void toLower(size_t start, size_t numChars); void toUpper(); void toUpper(size_t start, size_t numChars); - + /* * These methods operate on the string as if it were a path name. */ @@ -346,7 +452,7 @@ inline String8::operator const char*() const return mString; } -}; // namespace android +} // namespace android // --------------------------------------------------------------------------- diff --git a/libs/utils/String8.cpp b/libs/utils/String8.cpp index c50d343a7..71bf3ce7e 100644 --- a/libs/utils/String8.cpp +++ b/libs/utils/String8.cpp @@ -25,25 +25,39 @@ #include -namespace android { +/* + * Functions outside android is below the namespace android, since they use + * functions and constants in android namespace. + */ // --------------------------------------------------------------------------- -static const uint32_t kByteMask = 0x000000BF; -static const uint32_t kByteMark = 0x00000080; +namespace android { + +static const char32_t kByteMask = 0x000000BF; +static const char32_t kByteMark = 0x00000080; // Surrogates aren't valid for UTF-32 characters, so define some // constants that will let us screen them out. -static const uint32_t kUnicodeSurrogateHighStart = 0x0000D800; -static const uint32_t kUnicodeSurrogateHighEnd = 0x0000DBFF; -static const uint32_t kUnicodeSurrogateLowStart = 0x0000DC00; -static const uint32_t kUnicodeSurrogateLowEnd = 0x0000DFFF; -static const uint32_t kUnicodeSurrogateStart = kUnicodeSurrogateHighStart; -static const uint32_t kUnicodeSurrogateEnd = kUnicodeSurrogateLowEnd; +static const char32_t kUnicodeSurrogateHighStart = 0x0000D800; +static const char32_t kUnicodeSurrogateHighEnd = 0x0000DBFF; +static const char32_t kUnicodeSurrogateLowStart = 0x0000DC00; +static const char32_t kUnicodeSurrogateLowEnd = 0x0000DFFF; +static const char32_t kUnicodeSurrogateStart = kUnicodeSurrogateHighStart; +static const char32_t kUnicodeSurrogateEnd = kUnicodeSurrogateLowEnd; +static const char32_t kUnicodeMaxCodepoint = 0x0010FFFF; // Mask used to set appropriate bits in first byte of UTF-8 sequence, // indexed by number of bytes in the sequence. -static const uint32_t kFirstByteMark[] = { +// 0xxxxxxx +// -> (00-7f) 7bit. Bit mask for the first byte is 0x00000000 +// 110yyyyx 10xxxxxx +// -> (c0-df)(80-bf) 11bit. Bit mask is 0x000000C0 +// 1110yyyy 10yxxxxx 10xxxxxx +// -> (e0-ef)(80-bf)(80-bf) 16bit. Bit mask is 0x000000E0 +// 11110yyy 10yyxxxx 10xxxxxx 10xxxxxx +// -> (f0-f7)(80-bf)(80-bf)(80-bf) 21bit. Bit mask is 0x000000F0 +static const char32_t kFirstByteMark[] = { 0x00000000, 0x00000000, 0x000000C0, 0x000000E0, 0x000000F0 }; @@ -52,7 +66,7 @@ static const uint32_t kFirstByteMark[] = { #define RES_PATH_SEPARATOR '/' // Return number of utf8 bytes required for the character. -static size_t utf32_to_utf8_bytes(uint32_t srcChar) +static size_t utf32_to_utf8_bytes(char32_t srcChar) { size_t bytesToWrite; @@ -79,7 +93,7 @@ static size_t utf32_to_utf8_bytes(uint32_t srcChar) } } // Max code point for Unicode is 0x0010FFFF. - else if (srcChar < 0x00110000) + else if (srcChar <= kUnicodeMaxCodepoint) { bytesToWrite = 4; } @@ -94,7 +108,7 @@ static size_t utf32_to_utf8_bytes(uint32_t srcChar) // Write out the source character to . -static void utf32_to_utf8(uint8_t* dstP, uint32_t srcChar, size_t bytes) +static void utf32_to_utf8(uint8_t* dstP, char32_t srcChar, size_t bytes) { dstP += bytes; switch (bytes) @@ -126,7 +140,7 @@ void initialize_string8() // Bite me, Darwin! gDarwinIsReallyAnnoying = gDarwinCantLoadAllObjects; #endif - + SharedBuffer* buf = SharedBuffer::alloc(1); char* str = (char*)buf->data(); *str = 0; @@ -160,20 +174,20 @@ static char* allocFromUTF8(const char* in, size_t len) return getEmptyString(); } -// Note: not dealing with expanding surrogate pairs. -static char* allocFromUTF16(const char16_t* in, size_t len) +template +static char* allocFromUTF16OrUTF32(const T* in, L len) { if (len == 0) return getEmptyString(); - + size_t bytes = 0; - const char16_t* end = in+len; - const char16_t* p = in; - + const T* end = in+len; + const T* p = in; + while (p < end) { bytes += utf32_to_utf8_bytes(*p); p++; } - + SharedBuffer* buf = SharedBuffer::alloc(bytes+1); LOG_ASSERT(buf, "Unable to allocate shared buffer"); if (buf) { @@ -181,19 +195,30 @@ static char* allocFromUTF16(const char16_t* in, size_t len) char* str = (char*)buf->data(); char* d = str; while (p < end) { - uint32_t c = *p++; + const T c = *p++; size_t len = utf32_to_utf8_bytes(c); utf32_to_utf8((uint8_t*)d, c, len); d += len; } *d = 0; - + return str; } - + return getEmptyString(); } +// Note: not dealing with expanding surrogate pairs. +static char* allocFromUTF16(const char16_t* in, size_t len) +{ + return allocFromUTF16OrUTF32(in, len); +} + +static char* allocFromUTF32(const char32_t* in, size_t len) +{ + return allocFromUTF16OrUTF32(in, len); +} + // --------------------------------------------------------------------------- String8::String8() @@ -238,6 +263,16 @@ String8::String8(const char16_t* o, size_t len) { } +String8::String8(const char32_t* o) + : mString(allocFromUTF32(o, strlen32(o))) +{ +} + +String8::String8(const char32_t* o, size_t len) + : mString(allocFromUTF32(o, len)) +{ +} + String8::~String8() { SharedBuffer::bufferFromData(mString)->release(); @@ -280,6 +315,16 @@ status_t String8::setTo(const char16_t* other, size_t len) return NO_MEMORY; } +status_t String8::setTo(const char32_t* other, size_t len) +{ + SharedBuffer::bufferFromData(mString)->release(); + mString = allocFromUTF32(other, len); + if (mString) return NO_ERROR; + + mString = getEmptyString(); + return NO_MEMORY; +} + status_t String8::append(const String8& other) { const size_t otherLen = other.bytes(); @@ -418,6 +463,21 @@ void String8::toUpper(size_t start, size_t length) unlockBuffer(len); } +size_t String8::getUtf32Length() const +{ + return utf32_length(mString, length()); +} + +int32_t String8::getUtf32At(size_t index, size_t *next_index) const +{ + return utf32_at(mString, length(), index, next_index); +} + +size_t String8::getUtf32(char32_t* dst, size_t dst_len) const +{ + return utf8_to_utf32(mString, length(), dst, dst_len); +} + TextOutput& operator<<(TextOutput& to, const String8& val) { to << val.string(); @@ -427,7 +487,6 @@ TextOutput& operator<<(TextOutput& to, const String8& val) // --------------------------------------------------------------------------- // Path functions - void String8::setPathName(const char* name) { setPathName(name, strlen(name)); @@ -600,5 +659,192 @@ String8& String8::convertToResPath() return *this; } - }; // namespace android + +// --------------------------------------------------------------------------- + +size_t strlen32(const char32_t *s) +{ + const char32_t *ss = s; + while ( *ss ) + ss++; + return ss-s; +} + +size_t strnlen32(const char32_t *s, size_t maxlen) +{ + const char32_t *ss = s; + while ((maxlen > 0) && *ss) { + ss++; + maxlen--; + } + return ss-s; +} + +size_t utf8_codepoint_count(const char *src) +{ + const char *cur = src; + size_t ret = 0; + while (*cur != '\0') { + const char first_char = *cur++; + if ((first_char & 0x80) == 0) { // ASCII + ret += 1; + continue; + } + // (UTF-8's character must not be like 10xxxxxx, + // but 110xxxxx, 1110xxxx, ... or 1111110x) + if ((first_char & 0x40) == 0) { + return 0; + } + + int32_t mask, to_ignore_mask; + size_t num_to_read = 0; + char32_t utf32 = 0; + for (num_to_read = 1, mask = 0x40, to_ignore_mask = 0x80; + num_to_read < 5 && (first_char & mask); + num_to_read++, to_ignore_mask |= mask, mask >>= 1) { + if ((*cur & 0xC0) != 0x80) { // must be 10xxxxxx + return 0; + } + // 0x3F == 00111111 + utf32 = (utf32 << 6) + (*cur++ & 0x3F); + } + // "first_char" must be (110xxxxx - 11110xxx) + if (num_to_read == 5) { + return 0; + } + to_ignore_mask |= mask; + utf32 |= ((~to_ignore_mask) & first_char) << (6 * (num_to_read - 1)); + if (utf32 > android::kUnicodeMaxCodepoint) { + return 0; + } + + ret += num_to_read; + } + return ret; +} + +size_t utf32_length(const char *src, size_t src_len) +{ + if (src == NULL || src_len == 0) { + return 0; + } + size_t ret = 0; + const char* cur; + const char* end; + size_t num_to_skip; + for (cur = src, end = src + src_len, num_to_skip = 1; + cur < end; + cur += num_to_skip, ret++) { + const char first_char = *cur; + num_to_skip = 1; + if ((first_char & 0x80) == 0) { // ASCII + continue; + } + int32_t mask; + + for (mask = 0x40; (first_char & mask); num_to_skip++, mask >>= 1) { + } + } + return ret; +} + +size_t utf8_length_from_utf32(const char32_t *src, size_t src_len) +{ + if (src == NULL || src_len == 0) { + return 0; + } + size_t ret = 0; + const char32_t *end = src + src_len; + while (src < end) { + ret += android::utf32_to_utf8_bytes(*src++); + } + return ret; +} + +static int32_t utf32_at_internal(const char* cur, size_t *num_read) +{ + const char first_char = *cur; + if ((first_char & 0x80) == 0) { // ASCII + *num_read = 1; + return *cur; + } + cur++; + char32_t mask, to_ignore_mask; + size_t num_to_read = 0; + char32_t utf32 = first_char; + for (num_to_read = 1, mask = 0x40, to_ignore_mask = 0xFFFFFF80; + (first_char & mask); + num_to_read++, to_ignore_mask |= mask, mask >>= 1) { + // 0x3F == 00111111 + utf32 = (utf32 << 6) + (*cur++ & 0x3F); + } + to_ignore_mask |= mask; + utf32 &= ~(to_ignore_mask << (6 * (num_to_read - 1))); + + *num_read = num_to_read; + return static_cast(utf32); +} + +int32_t utf32_at(const char *src, size_t src_len, + size_t index, size_t *next_index) +{ + if (index >= src_len) { + return -1; + } + size_t dummy_index; + if (next_index == NULL) { + next_index = &dummy_index; + } + size_t num_read; + int32_t ret = utf32_at_internal(src + index, &num_read); + if (ret >= 0) { + *next_index = index + num_read; + } + + return ret; +} + +size_t utf8_to_utf32(const char* src, size_t src_len, + char32_t* dst, size_t dst_len) +{ + if (src == NULL || src_len == 0 || dst == NULL || dst_len == 0) { + return 0; + } + + const char* cur = src; + const char* end = src + src_len; + char32_t* cur_utf32 = dst; + const char32_t* end_utf32 = dst + dst_len; + while (cur_utf32 < end_utf32 && cur < end) { + size_t num_read; + *cur_utf32++ = + static_cast(utf32_at_internal(cur, &num_read)); + cur += num_read; + } + if (cur_utf32 < end_utf32) { + *cur_utf32 = 0; + } + return static_cast(cur_utf32 - dst); +} + +size_t utf32_to_utf8(const char32_t* src, size_t src_len, + char* dst, size_t dst_len) +{ + if (src == NULL || src_len == 0 || dst == NULL || dst_len == 0) { + return 0; + } + const char32_t *cur_utf32 = src; + const char32_t *end_utf32 = src + src_len; + char *cur = dst; + const char *end = dst + dst_len; + while (cur_utf32 < end_utf32 && cur < end) { + size_t len = android::utf32_to_utf8_bytes(*cur_utf32); + android::utf32_to_utf8((uint8_t *)cur, *cur_utf32++, len); + cur += len; + } + if (cur < end) { + *cur = '\0'; + } + return cur - dst; +} From 56d6326a0ef3a98dbdac5de540aebf569c0bca34 Mon Sep 17 00:00:00 2001 From: Daisuke Miyakawa Date: Thu, 9 Jul 2009 13:05:24 +0900 Subject: [PATCH 143/541] quick fix; utf8_codepoint_count must be utf8_length --- libs/utils/String8.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/utils/String8.cpp b/libs/utils/String8.cpp index 71bf3ce7e..e908ec1e8 100644 --- a/libs/utils/String8.cpp +++ b/libs/utils/String8.cpp @@ -681,7 +681,7 @@ size_t strnlen32(const char32_t *s, size_t maxlen) return ss-s; } -size_t utf8_codepoint_count(const char *src) +size_t utf8_length(const char *src) { const char *cur = src; size_t ret = 0; From d8cc052fafdc7a5580a36d1b27f819551e6f06ac Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Thu, 9 Jul 2009 11:36:32 -0700 Subject: [PATCH 144/541] Fix resources not being set correctly. --- libs/utils/ResourceTypes.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp index 109f28d30..87edb0125 100644 --- a/libs/utils/ResourceTypes.cpp +++ b/libs/utils/ResourceTypes.cpp @@ -1573,7 +1573,6 @@ status_t ResTable::add(Asset* asset, void* cookie, bool copyData) status_t ResTable::add(ResTable* src) { mError = src->mError; - mParams = src->mParams; for (size_t i=0; imHeaders.size(); i++) { mHeaders.add(src->mHeaders[i]); From 1555436473afd296488235f0aa22bb17162a929f Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Sun, 12 Jul 2009 23:11:20 -0700 Subject: [PATCH 145/541] implement Mutex and Condition with pthread instead of calling futex directly. internally pthread uses futex. the implementation consists of simple inlines there are no implementation files anymore. --- include/utils/threads.h | 90 +++++++++++- libs/utils/Android.mk | 14 +- libs/utils/Threads.cpp | 290 +------------------------------------ libs/utils/futex_synchro.c | 176 ---------------------- 4 files changed, 92 insertions(+), 478 deletions(-) delete mode 100644 libs/utils/futex_synchro.c diff --git a/include/utils/threads.h b/include/utils/threads.h index e0cb66423..5c0396596 100644 --- a/include/utils/threads.h +++ b/include/utils/threads.h @@ -21,6 +21,10 @@ #include #include +#if defined(HAVE_PTHREADS) +# include +#endif + // ------------------------------------------------------------------ // C API @@ -176,6 +180,8 @@ inline thread_id_t getThreadId() { return androidGetThreadId(); } +/*****************************************************************************/ + /* * Simple mutex class. The implementation is system-dependent. * @@ -212,11 +218,38 @@ private: // A mutex cannot be copied Mutex(const Mutex&); Mutex& operator = (const Mutex&); - void _init(); +#if defined(HAVE_PTHREADS) + pthread_mutex_t mMutex; +#else + void _init(); void* mState; +#endif }; +#if defined(HAVE_PTHREADS) + +inline Mutex::Mutex() { + pthread_mutex_init(&mMutex, NULL); +} +inline Mutex::Mutex(const char* name) { + pthread_mutex_init(&mMutex, NULL); +} +inline Mutex::~Mutex() { + pthread_mutex_destroy(&mMutex); +} +inline status_t Mutex::lock() { + return -pthread_mutex_lock(&mMutex); +} +inline void Mutex::unlock() { + pthread_mutex_unlock(&mMutex); +} +inline status_t Mutex::tryLock() { + return -pthread_mutex_trylock(&mMutex); +} + +#endif // HAVE_PTHREADS + /* * Automatic mutex. Declare one of these at the top of a function. * When the function returns, it will go out of scope, and release the @@ -225,6 +258,7 @@ private: typedef Mutex::Autolock AutoMutex; +/*****************************************************************************/ /* * Condition variable class. The implementation is system-dependent. @@ -240,9 +274,6 @@ public: ~Condition(); // Wait on the condition variable. Lock the mutex before calling. status_t wait(Mutex& mutex); - // Wait on the condition variable until the given time. Lock the mutex - // before calling. - status_t wait(Mutex& mutex, nsecs_t abstime); // same with relative timeout status_t waitRelative(Mutex& mutex, nsecs_t reltime); // Signal the condition variable, allowing one thread to continue. @@ -251,9 +282,60 @@ public: void broadcast(); private: +#if defined(HAVE_PTHREADS) + pthread_cond_t mCond; +#else void* mState; +#endif }; +#if defined(HAVE_PTHREADS) + +inline Condition::Condition() { + pthread_cond_init(&mCond, NULL); +} +inline Condition::~Condition() { + pthread_cond_destroy(&mCond); +} +inline status_t Condition::wait(Mutex& mutex) { + return -pthread_cond_wait(&mCond, &mutex.mMutex); +} +inline status_t Condition::waitRelative(Mutex& mutex, nsecs_t reltime) { +#if defined(HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE) + struct timespec ts; + ts.tv_sec = reltime/1000000000; + ts.tv_nsec = reltime%1000000000; + return -pthread_cond_timedwait_relative_np(&mCond, &mutex.mMutex, &ts); +#else // HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE + struct timespec ts; +#if defined(HAVE_POSIX_CLOCKS) + clock_gettime(CLOCK_REALTIME, &ts); +#else // HAVE_POSIX_CLOCKS + // we don't support the clocks here. + struct timeval t; + gettimeofday(&t, NULL); + ts.tv_sec = t.tv_sec; + ts.tv_nsec= t.tv_usec*1000; +#endif // HAVE_POSIX_CLOCKS + ts.tv_sec += reltime/1000000000; + ts.tv_nsec+= reltime%1000000000; + if (ts.tv_nsec >= 1000000000) { + ts.tv_nsec -= 1000000000; + ts.tv_sec += 1; + } + return -pthread_cond_timedwait(&mCond, &mutex.mMutex, &ts); +#endif // HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE +} +inline void Condition::signal() { + pthread_cond_signal(&mCond); +} +inline void Condition::broadcast() { + pthread_cond_broadcast(&mCond); +} + +#endif // HAVE_PTHREADS + +/*****************************************************************************/ /* * This is our spiffy thread object! diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk index 3f5cb85ac..59409a2bf 100644 --- a/libs/utils/Android.mk +++ b/libs/utils/Android.mk @@ -51,13 +51,6 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES:= $(commonSources) -ifeq ($(HOST_OS),linux) -# Use the futex based mutex and condition variable -# implementation from android-arm because it's shared mem safe - LOCAL_SRC_FILES += \ - futex_synchro.c -endif - LOCAL_MODULE:= libutils LOCAL_CFLAGS += -DLIBUTILS_NATIVE=1 $(TOOL_CFLAGS) @@ -87,15 +80,13 @@ LOCAL_SRC_FILES:= \ BackupHelpers.cpp ifeq ($(TARGET_OS),linux) -# Use the futex based mutex and condition variable -# implementation from android-arm because it's shared mem safe -LOCAL_SRC_FILES += futex_synchro.c LOCAL_LDLIBS += -lrt -ldl endif LOCAL_C_INCLUDES += \ external/zlib \ external/icu4c/common + LOCAL_LDLIBS += -lpthread LOCAL_SHARED_LIBRARIES := \ @@ -106,8 +97,7 @@ LOCAL_SHARED_LIBRARIES := \ ifneq ($(TARGET_SIMULATOR),true) ifeq ($(TARGET_OS)-$(TARGET_ARCH),linux-x86) # This is needed on x86 to bring in dl_iterate_phdr for CallStack.cpp -LOCAL_SHARED_LIBRARIES += \ - libdl +LOCAL_SHARED_LIBRARIES += libdl endif # linux-x86 endif # sim diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp index 9287c0bba..4036c497a 100644 --- a/libs/utils/Threads.cpp +++ b/libs/utils/Threads.cpp @@ -38,10 +38,6 @@ # define HAVE_CREATETHREAD // Cygwin, vs. HAVE__BEGINTHREADEX for MinGW #endif -#if defined(HAVE_FUTEX) -#include -#endif - #if defined(HAVE_PRCTL) #include #endif @@ -56,10 +52,6 @@ using namespace android; // ---------------------------------------------------------------------------- #if defined(HAVE_PTHREADS) -#if 0 -#pragma mark - -#pragma mark PTHREAD -#endif // ---------------------------------------------------------------------------- /* @@ -163,10 +155,6 @@ android_thread_id_t androidGetThreadId() // ---------------------------------------------------------------------------- #elif defined(HAVE_WIN32_THREADS) -#if 0 -#pragma mark - -#pragma mark WIN32_THREADS -#endif // ---------------------------------------------------------------------------- /* @@ -252,11 +240,6 @@ android_thread_id_t androidGetThreadId() // ---------------------------------------------------------------------------- -#if 0 -#pragma mark - -#pragma mark Common Thread functions -#endif - int androidCreateThread(android_thread_func_t fn, void* arg) { return createThreadEtc(fn, arg); @@ -294,109 +277,9 @@ namespace android { * =========================================================================== */ -#if 0 -#pragma mark - -#pragma mark Mutex -#endif - -#if defined(HAVE_PTHREADS) && !defined(HAVE_FUTEX) -/* - * Simple pthread wrapper. - */ - -Mutex::Mutex() -{ - _init(); -} - -Mutex::Mutex(const char* name) -{ - // XXX: name not used for now - _init(); -} - -void Mutex::_init() -{ - pthread_mutex_t* pMutex = new pthread_mutex_t; - pthread_mutex_init(pMutex, NULL); - mState = pMutex; -} - -Mutex::~Mutex() -{ - delete (pthread_mutex_t*) mState; -} - -status_t Mutex::lock() -{ - int res; - while ((res=pthread_mutex_lock((pthread_mutex_t*) mState)) == EINTR) ; - return -res; -} - -void Mutex::unlock() -{ - pthread_mutex_unlock((pthread_mutex_t*) mState); -} - -status_t Mutex::tryLock() -{ - int res; - while ((res=pthread_mutex_trylock((pthread_mutex_t*) mState)) == EINTR) ; - return -res; -} - -#elif defined(HAVE_FUTEX) -#if 0 -#pragma mark - -#endif - -#define STATE ((futex_mutex_t*) (&mState)) - -Mutex::Mutex() -{ - _init(); -} - -Mutex::Mutex(const char* name) -{ - _init(); -} - -void -Mutex::_init() -{ - futex_mutex_init(STATE); -} - -Mutex::~Mutex() -{ -} - -status_t Mutex::lock() -{ - int res; - while ((res=futex_mutex_lock(STATE, FUTEX_WAIT_INFINITE)) == EINTR) ; - return -res; -} - -void Mutex::unlock() -{ - futex_mutex_unlock(STATE); -} - -status_t Mutex::tryLock() -{ - int res; - while ((res=futex_mutex_trylock(STATE)) == EINTR) ; - return -res; -} -#undef STATE - +#if defined(HAVE_PTHREADS) +// implemented as inlines in threads.h #elif defined(HAVE_WIN32_THREADS) -#if 0 -#pragma mark - -#endif Mutex::Mutex() { @@ -456,161 +339,9 @@ status_t Mutex::tryLock() * =========================================================================== */ -#if 0 -#pragma mark - -#pragma mark Condition -#endif - -#if defined(HAVE_PTHREADS) && !defined(HAVE_FUTEX) - -/* - * Constructor. This is a simple pthread wrapper. - */ -Condition::Condition() -{ - pthread_cond_t* pCond = new pthread_cond_t; - - pthread_cond_init(pCond, NULL); - mState = pCond; -} - -/* - * Destructor. - */ -Condition::~Condition() -{ - pthread_cond_destroy((pthread_cond_t*) mState); - delete (pthread_cond_t*) mState; -} - -/* - * Wait on a condition variable. Lock the mutex before calling. - */ - -status_t Condition::wait(Mutex& mutex) -{ - assert(mutex.mState != NULL); - - int cc; - while ((cc = pthread_cond_wait((pthread_cond_t*)mState, - (pthread_mutex_t*) mutex.mState)) == EINTR) ; - return -cc; -} - -status_t Condition::wait(Mutex& mutex, nsecs_t abstime) -{ - assert(mutex.mState != NULL); - - struct timespec ts; - ts.tv_sec = abstime/1000000000; - ts.tv_nsec = abstime-(ts.tv_sec*1000000000); - - int cc; - while ((cc = pthread_cond_timedwait((pthread_cond_t*)mState, - (pthread_mutex_t*) mutex.mState, &ts)) == EINTR) ; - return -cc; -} - -status_t Condition::waitRelative(Mutex& mutex, nsecs_t reltime) -{ - return wait(mutex, systemTime()+reltime); -} - -/* - * Signal the condition variable, allowing one thread to continue. - */ -void Condition::signal() -{ - pthread_cond_signal((pthread_cond_t*) mState); -} - -/* - * Signal the condition variable, allowing all threads to continue. - */ -void Condition::broadcast() -{ - pthread_cond_broadcast((pthread_cond_t*) mState); -} - -#elif defined(HAVE_FUTEX) -#if 0 -#pragma mark - -#endif - -#define STATE ((futex_cond_t*) (&mState)) - -/* - * Constructor. This is a simple pthread wrapper. - */ -Condition::Condition() -{ - futex_cond_init(STATE); -} - -/* - * Destructor. - */ -Condition::~Condition() -{ -} - -/* - * Wait on a condition variable. Lock the mutex before calling. - */ - -status_t Condition::wait(Mutex& mutex) -{ - assert(mutex.mState != NULL); - - int res; - while ((res = futex_cond_wait(STATE, - (futex_mutex_t*)(&mutex.mState), FUTEX_WAIT_INFINITE)) == -EINTR) ; - - return -res; -} - -status_t Condition::wait(Mutex& mutex, nsecs_t abstime) -{ - nsecs_t reltime = abstime - systemTime(); - if (reltime <= 0) return true; - return waitRelative(mutex, reltime); -} - -status_t Condition::waitRelative(Mutex& mutex, nsecs_t reltime) -{ - assert(mutex.mState != NULL); - int res; - unsigned msec = ns2ms(reltime); - if(msec == 0) - return true; - // This code will not time out at the correct time if interrupted by signals - while ((res = futex_cond_wait(STATE, - (futex_mutex_t*)(&mutex.mState), msec)) == -EINTR) ; - return res; -} - -/* - * Signal the condition variable, allowing one thread to continue. - */ -void Condition::signal() -{ - futex_cond_signal(STATE); -} - -/* - * Signal the condition variable, allowing all threads to continue. - */ -void Condition::broadcast() -{ - futex_cond_broadcast(STATE); -} - -#undef STATE - +#if defined(HAVE_PTHREADS) +// implemented as inlines in threads.h #elif defined(HAVE_WIN32_THREADS) -#if 0 -#pragma mark - -#endif /* * Windows doesn't have a condition variable solution. It's possible @@ -753,14 +484,6 @@ status_t Condition::wait(Mutex& mutex) return ((WinCondition*)mState)->wait(condState, hMutex, NULL); } -status_t Condition::wait(Mutex& mutex, nsecs_t abstime) -{ - WinCondition* condState = (WinCondition*) mState; - HANDLE hMutex = (HANDLE) mutex.mState; - - return ((WinCondition*)mState)->wait(condState, hMutex, &abstime); -} - status_t Condition::waitRelative(Mutex& mutex, nsecs_t reltime) { return wait(mutex, systemTime()+reltime); @@ -841,11 +564,6 @@ void Condition::broadcast() // ---------------------------------------------------------------------------- -#if 0 -#pragma mark - -#pragma mark Thread::Thread -#endif - /* * This is our thread object! */ diff --git a/libs/utils/futex_synchro.c b/libs/utils/futex_synchro.c deleted file mode 100644 index ab48c6921..000000000 --- a/libs/utils/futex_synchro.c +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include - -#include -#include - -#include - -#include - - -// This futex glue code is need on desktop linux, but is already part of bionic. -#if !defined(HAVE_FUTEX_WRAPPERS) - -#include -#include -typedef unsigned int u32; -#define asmlinkage -#define __user -#include -#include - - -int futex (int *uaddr, int op, int val, const struct timespec *timeout, int *uaddr2, int val3) -{ - int err = syscall(SYS_futex, uaddr, op, val, timeout, uaddr2, val3); - return err == 0 ? 0 : -errno; -} - -int __futex_wait(volatile void *ftx, int val, const struct timespec *timeout) -{ - return futex((int*)ftx, FUTEX_WAIT, val, timeout, NULL, 0); -} - -int __futex_wake(volatile void *ftx, int count) -{ - return futex((int*)ftx, FUTEX_WAKE, count, NULL, NULL, 0); -} - -int __atomic_cmpxchg(int old, int _new, volatile int *ptr) -{ - return android_atomic_cmpxchg(old, _new, ptr); -} - -int __atomic_swap(int _new, volatile int *ptr) -{ - return android_atomic_swap(_new, ptr); -} - -int __atomic_dec(volatile int *ptr) -{ - return android_atomic_dec(ptr); -} - -#else // !defined(__arm__) - -int __futex_wait(volatile void *ftx, int val, const struct timespec *timeout); -int __futex_wake(volatile void *ftx, int count); - -int __atomic_cmpxchg(int old, int _new, volatile int *ptr); -int __atomic_swap(int _new, volatile int *ptr); -int __atomic_dec(volatile int *ptr); - -#endif // !defined(HAVE_FUTEX_WRAPPERS) - - -// lock states -// -// 0: unlocked -// 1: locked, no waiters -// 2: locked, maybe waiters - -void futex_mutex_init(futex_mutex_t *m) -{ - m->value = 0; -} - -int futex_mutex_lock(futex_mutex_t *m, unsigned msec) -{ - if(__atomic_cmpxchg(0, 1, &m->value) == 0) { - return 0; - } - if(msec == FUTEX_WAIT_INFINITE) { - while(__atomic_swap(2, &m->value) != 0) { - __futex_wait(&m->value, 2, 0); - } - } else { - struct timespec ts; - ts.tv_sec = msec / 1000; - ts.tv_nsec = (msec % 1000) * 1000000; - while(__atomic_swap(2, &m->value) != 0) { - if(__futex_wait(&m->value, 2, &ts) == -ETIMEDOUT) { - return -1; - } - } - } - return 0; -} - -int futex_mutex_trylock(futex_mutex_t *m) -{ - if(__atomic_cmpxchg(0, 1, &m->value) == 0) { - return 0; - } - return -1; -} - -void futex_mutex_unlock(futex_mutex_t *m) -{ - if(__atomic_dec(&m->value) != 1) { - m->value = 0; - __futex_wake(&m->value, 1); - } -} - -/* XXX *technically* there is a race condition that could allow - * XXX a signal to be missed. If thread A is preempted in _wait() - * XXX after unlocking the mutex and before waiting, and if other - * XXX threads call signal or broadcast UINT_MAX times (exactly), - * XXX before thread A is scheduled again and calls futex_wait(), - * XXX then the signal will be lost. - */ - -void futex_cond_init(futex_cond_t *c) -{ - c->value = 0; -} - -int futex_cond_wait(futex_cond_t *c, futex_mutex_t *m, unsigned msec) -{ - if(msec == FUTEX_WAIT_INFINITE){ - int oldvalue = c->value; - futex_mutex_unlock(m); - __futex_wait(&c->value, oldvalue, 0); - futex_mutex_lock(m, FUTEX_WAIT_INFINITE); - return 0; - } else { - int oldvalue = c->value; - struct timespec ts; - ts.tv_sec = msec / 1000; - ts.tv_nsec = (msec % 1000) * 1000000; - futex_mutex_unlock(m); - const int err = __futex_wait(&c->value, oldvalue, &ts); - futex_mutex_lock(m, FUTEX_WAIT_INFINITE); - return err; - } -} - -void futex_cond_signal(futex_cond_t *c) -{ - __atomic_dec(&c->value); - __futex_wake(&c->value, 1); -} - -void futex_cond_broadcast(futex_cond_t *c) -{ - __atomic_dec(&c->value); - __futex_wake(&c->value, INT_MAX); -} - From de6926bc94a2e73fc2b5364840deebc6f2ff7f6e Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Mon, 13 Jul 2009 21:59:37 -0700 Subject: [PATCH 146/541] add a ctor to Mutex to specify the type, which can be shared. This is used by sf and af an soon will allow some optimization in the kernel for non shared mutexes --- include/utils/threads.h | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/include/utils/threads.h b/include/utils/threads.h index 5c0396596..e9b078889 100644 --- a/include/utils/threads.h +++ b/include/utils/threads.h @@ -190,8 +190,14 @@ inline thread_id_t getThreadId() { */ class Mutex { public: + enum { + NORMAL = 0, + SHARED = 1 + }; + Mutex(); Mutex(const char* name); + Mutex(int type, const char* name = NULL); ~Mutex(); // lock or unlock the mutex @@ -235,6 +241,17 @@ inline Mutex::Mutex() { inline Mutex::Mutex(const char* name) { pthread_mutex_init(&mMutex, NULL); } +inline Mutex::Mutex(int type, const char* name) { + if (type == SHARED) { + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED); + pthread_mutex_init(&mMutex, &attr); + pthread_mutexattr_destroy(&attr); + } else { + pthread_mutex_init(&mMutex, NULL); + } +} inline Mutex::~Mutex() { pthread_mutex_destroy(&mMutex); } From c18deadb84c36fce1e184dc4f02f189105bae5be Mon Sep 17 00:00:00 2001 From: Joe Onorato Date: Mon, 13 Jul 2009 14:44:07 -0700 Subject: [PATCH 147/541] Need to skip the padding after reading. m_dataEndPos points to the end of the data, not the beginning of the next entity. --- libs/utils/BackupData.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/libs/utils/BackupData.cpp b/libs/utils/BackupData.cpp index cce754a08..be0477752 100644 --- a/libs/utils/BackupData.cpp +++ b/libs/utils/BackupData.cpp @@ -298,10 +298,12 @@ BackupDataReader::SkipEntityData() } if (m_header.entity.dataSize > 0) { int pos = lseek(m_fd, m_dataEndPos, SEEK_SET); - return pos == -1 ? (int)errno : (int)NO_ERROR; - } else { - return NO_ERROR; + if (pos == -1) { + return errno; + } } + SKIP_PADDING(); + return NO_ERROR; } ssize_t From 2d8baa06b5b4987c203817b4adffebe5424fb017 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Thu, 16 Jul 2009 11:27:13 -0700 Subject: [PATCH 148/541] fix FILE* version of ZipUtils::inflateToBuffer This can't have ever worked; a successful fread() was returning 1 instead of getSize. --- libs/utils/ZipUtils.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libs/utils/ZipUtils.cpp b/libs/utils/ZipUtils.cpp index 5df94cbbd..9138878ff 100644 --- a/libs/utils/ZipUtils.cpp +++ b/libs/utils/ZipUtils.cpp @@ -210,7 +210,7 @@ bail: LOGV("+++ reading %ld bytes (%ld left)\n", getSize, compRemaining); - int cc = fread(readBuf, getSize, 1, fp); + int cc = fread(readBuf, 1, getSize, fp); if (cc != (int) getSize) { LOGD("inflate read failed (%d vs %ld)\n", cc, getSize); @@ -341,4 +341,3 @@ bail: return true; } - From 5fcc03f9b212d9011c6d9123d96a6462cd4ba91c Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Fri, 17 Jul 2009 11:13:48 -0700 Subject: [PATCH 149/541] Add "nodpi" density, and expose a bunch of density-related APIs. Also update the DpiTest app to use nodpi images, and try to have a mode where it turns off compatibility though it's not quite working. --- include/utils/ResourceTypes.h | 3 ++- libs/utils/ResourceTypes.cpp | 13 +++++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h index 93bca4aef..381933556 100644 --- a/include/utils/ResourceTypes.h +++ b/include/utils/ResourceTypes.h @@ -825,7 +825,8 @@ struct ResTable_config }; enum { - DENSITY_ANY = 0 + DENSITY_DEFAULT = 0, + DENSITY_NONE = 0xffff }; union { diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp index 87edb0125..98d450b76 100644 --- a/libs/utils/ResourceTypes.cpp +++ b/libs/utils/ResourceTypes.cpp @@ -4007,7 +4007,16 @@ void ResTable::print(bool inclValues) const printf(" NON-INTEGER ResTable_type ADDRESS: %p\n", type); continue; } - printf(" config %d lang=%c%c cnt=%c%c orien=%d touch=%d density=%d key=%d infl=%d nav=%d w=%d h=%d lyt=%d\n", + char density[16]; + uint16_t dval = dtohs(type->config.density); + if (dval == ResTable_config::DENSITY_DEFAULT) { + strcpy(density, "def"); + } else if (dval == ResTable_config::DENSITY_NONE) { + strcpy(density, "non"); + } else { + sprintf(density, "%d", (int)dval); + } + printf(" config %d lang=%c%c cnt=%c%c orien=%d touch=%d density=%s key=%d infl=%d nav=%d w=%d h=%d lyt=%d\n", (int)configIndex, type->config.language[0] ? type->config.language[0] : '-', type->config.language[1] ? type->config.language[1] : '-', @@ -4015,7 +4024,7 @@ void ResTable::print(bool inclValues) const type->config.country[1] ? type->config.country[1] : '-', type->config.orientation, type->config.touchscreen, - dtohs(type->config.density), + density, type->config.keyboard, type->config.inputFlags, type->config.navigation, From ae7a402db054f5c5aa78c3d9a210f91126982c4d Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Queru Date: Tue, 21 Jul 2009 11:16:54 -0700 Subject: [PATCH 150/541] donut snapshot --- libs/utils/file_backup_helper.cpp | 685 ------------------------------ 1 file changed, 685 deletions(-) delete mode 100644 libs/utils/file_backup_helper.cpp diff --git a/libs/utils/file_backup_helper.cpp b/libs/utils/file_backup_helper.cpp deleted file mode 100644 index 453084ace..000000000 --- a/libs/utils/file_backup_helper.cpp +++ /dev/null @@ -1,685 +0,0 @@ -#define LOG_TAG "file_backup_helper" - -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -using namespace android; - -#define MAGIC0 0x70616e53 // Snap -#define MAGIC1 0x656c6946 // File - -#define LOGP(x...) LOGD(x) -//#define LOGP(x...) printf(x) - -struct SnapshotHeader { - int magic0; - int fileCount; - int magic1; - int totalSize; -}; - -struct FileState { - int modTime_sec; - int modTime_nsec; - int size; - int crc32; - int nameLen; -}; - -const static int ROUND_UP[4] = { 0, 3, 2, 1 }; - -static inline int -round_up(int n) -{ - return n + ROUND_UP[n % 4]; -} - -static int -read_snapshot_file(int fd, KeyedVector* snapshot) -{ - int bytesRead = 0; - int amt; - SnapshotHeader header; - - amt = read(fd, &header, sizeof(header)); - if (amt != sizeof(header)) { - return errno; - } - bytesRead += amt; - - if (header.magic0 != MAGIC0 || header.magic1 != MAGIC1) { - LOGW("read_snapshot_file header.magic0=0x%08x magic1=0x%08x", header.magic0, header.magic1); - return 1; - } - - for (int i=0; iadd(String8(filename, file.nameLen), file); - } - bytesRead += amt; - if (filename != filenameBuf) { - free(filename); - } - if (amt != nameBufSize) { - LOGW("read_snapshot_file filename truncated/error with read at %d bytes\n", bytesRead); - return 1; - } - } - - if (header.totalSize != bytesRead) { - LOGW("read_snapshot_file length mismatch: header.totalSize=%d bytesRead=%d\n", - header.totalSize, bytesRead); - return 1; - } - - return 0; -} - -static int -write_snapshot_file(int fd, const KeyedVector& snapshot) -{ - int bytesWritten = sizeof(SnapshotHeader); - // preflight size - const int N = snapshot.size(); - for (int i=0; i oldSnapshot; - KeyedVector newSnapshot; - - if (oldSnapshotFD != -1) { - err = read_snapshot_file(oldSnapshotFD, &oldSnapshot); - if (err != 0) { - // On an error, treat this as a full backup. - oldSnapshot.clear(); - } - } - - for (int i=0; i 0) { - // file added - String8 realFilename(base); - realFilename.appendPath(q); - write_update_file(realFilename, q); - m++; - } - else if (cmp < 0) { - // file removed - write_delete_file(p); - n++; - } - else { - // both files exist, check them - String8 realFilename(base); - realFilename.appendPath(q); - const FileState& f = oldSnapshot.valueAt(n); - const FileState& g = newSnapshot.valueAt(m); - - LOGP("%s\n", q.string()); - LOGP(" new: modTime=%d,%d size=%-3d crc32=0x%08x\n", - f.modTime_sec, f.modTime_nsec, f.size, f.crc32); - LOGP(" old: modTime=%d,%d size=%-3d crc32=0x%08x\n", - g.modTime_sec, g.modTime_nsec, g.size, g.crc32); - if (f.modTime_sec != g.modTime_sec || f.modTime_nsec != g.modTime_nsec - || f.size != g.size || f.crc32 != g.crc32) { - write_update_file(realFilename, p); - } - n++; - m++; - } - } - - // these were deleted - while (n snapshot; - const char* filename = SCRATCH_DIR "backup_helper_test_empty.snap"; - - system("rm -r " SCRATCH_DIR); - mkdir(SCRATCH_DIR, 0777); - - // write - fd = creat(filename, 0666); - if (fd == -1) { - fprintf(stderr, "error creating %s\n", filename); - return 1; - } - - err = write_snapshot_file(fd, snapshot); - - close(fd); - - if (err != 0) { - fprintf(stderr, "write_snapshot_file reported error %d (%s)\n", err, strerror(err)); - return err; - } - - static const unsigned char correct_data[] = { - 0x53, 0x6e, 0x61, 0x70, 0x00, 0x00, 0x00, 0x00, - 0x46, 0x69, 0x6c, 0x65, 0x10, 0x00, 0x00, 0x00 - }; - - err = compare_file(filename, correct_data, sizeof(correct_data)); - if (err != 0) { - return err; - } - - // read - fd = open(filename, O_RDONLY); - if (fd == -1) { - fprintf(stderr, "error opening for read %s\n", filename); - return 1; - } - - KeyedVector readSnapshot; - err = read_snapshot_file(fd, &readSnapshot); - if (err != 0) { - fprintf(stderr, "read_snapshot_file failed %d\n", err); - return err; - } - - if (readSnapshot.size() != 0) { - fprintf(stderr, "readSnapshot should be length 0\n"); - return 1; - } - - return 0; -} - -int -backup_helper_test_four() -{ - int err; - int fd; - KeyedVector snapshot; - const char* filename = SCRATCH_DIR "backup_helper_test_four.snap"; - - system("rm -r " SCRATCH_DIR); - mkdir(SCRATCH_DIR, 0777); - - // write - fd = creat(filename, 0666); - if (fd == -1) { - fprintf(stderr, "error opening %s\n", filename); - return 1; - } - - String8 filenames[4]; - FileState states[4]; - - states[0].modTime_sec = 0xfedcba98; - states[0].modTime_nsec = 0xdeadbeef; - states[0].size = 0xababbcbc; - states[0].crc32 = 0x12345678; - states[0].nameLen = -12; - filenames[0] = String8("bytes_of_padding"); - snapshot.add(filenames[0], states[0]); - - states[1].modTime_sec = 0x93400031; - states[1].modTime_nsec = 0xdeadbeef; - states[1].size = 0x88557766; - states[1].crc32 = 0x22334422; - states[1].nameLen = -1; - filenames[1] = String8("bytes_of_padding3"); - snapshot.add(filenames[1], states[1]); - - states[2].modTime_sec = 0x33221144; - states[2].modTime_nsec = 0xdeadbeef; - states[2].size = 0x11223344; - states[2].crc32 = 0x01122334; - states[2].nameLen = 0; - filenames[2] = String8("bytes_of_padding_2"); - snapshot.add(filenames[2], states[2]); - - states[3].modTime_sec = 0x33221144; - states[3].modTime_nsec = 0xdeadbeef; - states[3].size = 0x11223344; - states[3].crc32 = 0x01122334; - states[3].nameLen = 0; - filenames[3] = String8("bytes_of_padding__1"); - snapshot.add(filenames[3], states[3]); - - err = write_snapshot_file(fd, snapshot); - - close(fd); - - if (err != 0) { - fprintf(stderr, "write_snapshot_file reported error %d (%s)\n", err, strerror(err)); - return err; - } - - static const unsigned char correct_data[] = { - // header - 0x53, 0x6e, 0x61, 0x70, 0x04, 0x00, 0x00, 0x00, - 0x46, 0x69, 0x6c, 0x65, 0xac, 0x00, 0x00, 0x00, - - // bytes_of_padding - 0x98, 0xba, 0xdc, 0xfe, 0xef, 0xbe, 0xad, 0xde, - 0xbc, 0xbc, 0xab, 0xab, 0x78, 0x56, 0x34, 0x12, - 0x10, 0x00, 0x00, 0x00, 0x62, 0x79, 0x74, 0x65, - 0x73, 0x5f, 0x6f, 0x66, 0x5f, 0x70, 0x61, 0x64, - 0x64, 0x69, 0x6e, 0x67, - - // bytes_of_padding3 - 0x31, 0x00, 0x40, 0x93, 0xef, 0xbe, 0xad, 0xde, - 0x66, 0x77, 0x55, 0x88, 0x22, 0x44, 0x33, 0x22, - 0x11, 0x00, 0x00, 0x00, 0x62, 0x79, 0x74, 0x65, - 0x73, 0x5f, 0x6f, 0x66, 0x5f, 0x70, 0x61, 0x64, - 0x64, 0x69, 0x6e, 0x67, 0x33, 0xab, 0xab, 0xab, - - // bytes of padding2 - 0x44, 0x11, 0x22, 0x33, 0xef, 0xbe, 0xad, 0xde, - 0x44, 0x33, 0x22, 0x11, 0x34, 0x23, 0x12, 0x01, - 0x12, 0x00, 0x00, 0x00, 0x62, 0x79, 0x74, 0x65, - 0x73, 0x5f, 0x6f, 0x66, 0x5f, 0x70, 0x61, 0x64, - 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x32, 0xab, 0xab, - - // bytes of padding3 - 0x44, 0x11, 0x22, 0x33, 0xef, 0xbe, 0xad, 0xde, - 0x44, 0x33, 0x22, 0x11, 0x34, 0x23, 0x12, 0x01, - 0x13, 0x00, 0x00, 0x00, 0x62, 0x79, 0x74, 0x65, - 0x73, 0x5f, 0x6f, 0x66, 0x5f, 0x70, 0x61, 0x64, - 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x5f, 0x31, 0xab - }; - - err = compare_file(filename, correct_data, sizeof(correct_data)); - if (err != 0) { - return err; - } - - // read - fd = open(filename, O_RDONLY); - if (fd == -1) { - fprintf(stderr, "error opening for read %s\n", filename); - return 1; - } - - - KeyedVector readSnapshot; - err = read_snapshot_file(fd, &readSnapshot); - if (err != 0) { - fprintf(stderr, "read_snapshot_file failed %d\n", err); - return err; - } - - if (readSnapshot.size() != 4) { - fprintf(stderr, "readSnapshot should be length 4 is %d\n", readSnapshot.size()); - return 1; - } - - bool matched = true; - for (size_t i=0; i Date: Tue, 21 Jul 2009 17:46:02 -0700 Subject: [PATCH 151/541] First pass at reworking screen density/size APIs. This changes the names of the directories in aapt, to what you see in the list of DpiTest resources. Also adds a new "long" configuration for wide screens, which the platform sets appropriate, and introduces a new kind of resizeability for not large but significantly larger than normal screens which may have compatibility issues. --- include/utils/ResourceTypes.h | 84 ++++++++++++++++++++++++----------- libs/utils/ResourceTypes.cpp | 7 +-- 2 files changed, 61 insertions(+), 30 deletions(-) diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h index 381933556..edd0cae63 100644 --- a/include/utils/ResourceTypes.h +++ b/include/utils/ResourceTypes.h @@ -826,6 +826,9 @@ struct ResTable_config enum { DENSITY_DEFAULT = 0, + DENSITY_LOW = 120, + DENSITY_MEDIUM = 160, + DENSITY_HIGH = 240, DENSITY_NONE = 0xffff }; @@ -855,7 +858,6 @@ struct ResTable_config enum { MASK_KEYSHIDDEN = 0x0003, - SHIFT_KEYSHIDDEN = 0, KEYSHIDDEN_ANY = 0x0000, KEYSHIDDEN_NO = 0x0001, KEYSHIDDEN_YES = 0x0002, @@ -907,10 +909,18 @@ struct ResTable_config }; enum { - SCREENLAYOUT_ANY = 0x0000, - SCREENLAYOUT_SMALL = 0x0001, - SCREENLAYOUT_NORMAL = 0x0002, - SCREENLAYOUT_LARGE = 0x0003, + // screenLayout bits for screen size class. + MASK_SCREENSIZE = 0x0f, + SCREENSIZE_ANY = 0x00, + SCREENSIZE_SMALL = 0x01, + SCREENSIZE_NORMAL = 0x02, + SCREENSIZE_LARGE = 0x03, + + // screenLayout bits for wide/long screen variation. + MASK_SCREENLONG = 0x30, + SCREENLONG_ANY = 0x00, + SCREENLONG_NO = 0x10, + SCREENLONG_YES = 0x20, }; union { @@ -1040,6 +1050,17 @@ struct ResTable_config } } + if (screenConfig || o.screenConfig) { + if (((screenLayout^o.screenLayout) & MASK_SCREENSIZE) != 0) { + if (!(screenLayout & MASK_SCREENSIZE)) return false; + if (!(o.screenLayout & MASK_SCREENSIZE)) return true; + } + if (((screenLayout^o.screenLayout) & MASK_SCREENLONG) != 0) { + if (!(screenLayout & MASK_SCREENLONG)) return false; + if (!(o.screenLayout & MASK_SCREENLONG)) return true; + } + } + if (screenType || o.screenType) { if (orientation != o.orientation) { if (!orientation) return false; @@ -1056,7 +1077,7 @@ struct ResTable_config } if (input || o.input) { - if (inputFlags != o.inputFlags) { + if (((inputFlags^o.inputFlags) & MASK_KEYSHIDDEN) != 0) { if (!(inputFlags & MASK_KEYSHIDDEN)) return false; if (!(o.inputFlags & MASK_KEYSHIDDEN)) return true; } @@ -1084,13 +1105,6 @@ struct ResTable_config } } - if (screenConfig || o.screenConfig) { - if (screenLayout != o.screenLayout) { - if (!screenLayout) return false; - if (!o.screenLayout) return true; - } - } - if (version || o.version) { if (sdkVersion != o.sdkVersion) { if (!sdkVersion) return false; @@ -1139,6 +1153,17 @@ struct ResTable_config } } + if (screenConfig || o.screenConfig) { + if (((screenLayout^o.screenLayout) & MASK_SCREENSIZE) != 0 + && (requested->screenLayout & MASK_SCREENSIZE)) { + return (screenLayout & MASK_SCREENSIZE); + } + if (((screenLayout^o.screenLayout) & MASK_SCREENLONG) != 0 + && (requested->screenLayout & MASK_SCREENLONG)) { + return (screenLayout & MASK_SCREENLONG); + } + } + if (screenType || o.screenType) { if ((orientation != o.orientation) && requested->orientation) { return (orientation); @@ -1220,12 +1245,6 @@ struct ResTable_config } } - if (screenConfig || o.screenConfig) { - if ((screenLayout != o.screenLayout) && requested->screenLayout) { - return (screenLayout); - } - } - if (version || o.version) { if ((sdkVersion != o.sdkVersion) && requested->sdkVersion) { return (sdkVersion); @@ -1273,6 +1292,21 @@ struct ResTable_config return false; } } + if (screenConfig != 0) { + const int screenSize = screenLayout&MASK_SCREENSIZE; + const int setScreenSize = settings.screenLayout&MASK_SCREENSIZE; + if (setScreenSize != 0 && screenSize != 0 + && screenSize != setScreenSize) { + return false; + } + + const int screenLong = screenLayout&MASK_SCREENLONG; + const int setScreenLong = settings.screenLayout&MASK_SCREENLONG; + if (setScreenLong != 0 && screenLong != 0 + && screenLong != setScreenLong) { + return false; + } + } if (screenType != 0) { if (settings.orientation != 0 && orientation != 0 && orientation != settings.orientation) { @@ -1317,12 +1351,6 @@ struct ResTable_config return false; } } - if (screenConfig != 0) { - if (settings.screenLayout != 0 && screenLayout != 0 - && screenLayout != settings.screenLayout) { - return false; - } - } if (version != 0) { if (settings.sdkVersion != 0 && sdkVersion != 0 && sdkVersion != settings.sdkVersion) { @@ -1352,12 +1380,14 @@ struct ResTable_config String8 toString() const { char buf[200]; sprintf(buf, "imsi=%d/%d lang=%c%c reg=%c%c orient=%d touch=%d dens=%d " - "kbd=%d nav=%d input=%d scrnW=%d scrnH=%d layout=%d vers=%d.%d", + "kbd=%d nav=%d input=%d scrnW=%d scrnH=%d sz=%d long=%d vers=%d.%d", mcc, mnc, language[0] ? language[0] : '-', language[1] ? language[1] : '-', country[0] ? country[0] : '-', country[1] ? country[1] : '-', orientation, touchscreen, density, keyboard, navigation, inputFlags, - screenWidth, screenHeight, screenLayout, sdkVersion, minorVersion); + screenWidth, screenHeight, + screenLayout&MASK_SCREENSIZE, screenLayout&MASK_SCREENLONG, + sdkVersion, minorVersion); return String8(buf); } }; diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp index 98d450b76..4dca8bd81 100644 --- a/libs/utils/ResourceTypes.cpp +++ b/libs/utils/ResourceTypes.cpp @@ -4012,11 +4012,11 @@ void ResTable::print(bool inclValues) const if (dval == ResTable_config::DENSITY_DEFAULT) { strcpy(density, "def"); } else if (dval == ResTable_config::DENSITY_NONE) { - strcpy(density, "non"); + strcpy(density, "no"); } else { sprintf(density, "%d", (int)dval); } - printf(" config %d lang=%c%c cnt=%c%c orien=%d touch=%d density=%s key=%d infl=%d nav=%d w=%d h=%d lyt=%d\n", + printf(" config %d lang=%c%c cnt=%c%c orien=%d touch=%d density=%s key=%d infl=%d nav=%d w=%d h=%d sz=%d lng=%d\n", (int)configIndex, type->config.language[0] ? type->config.language[0] : '-', type->config.language[1] ? type->config.language[1] : '-', @@ -4030,7 +4030,8 @@ void ResTable::print(bool inclValues) const type->config.navigation, dtohs(type->config.screenWidth), dtohs(type->config.screenHeight), - type->config.screenLayout); + type->config.screenLayout&ResTable_config::MASK_SCREENSIZE, + type->config.screenLayout&ResTable_config::MASK_SCREENLONG); size_t entryCount = dtohl(type->entryCount); uint32_t entriesStart = dtohl(type->entriesStart); if ((entriesStart&0x3) != 0) { From 045a17c6bfd1a49ad30d4b149c2d6250ee2896e4 Mon Sep 17 00:00:00 2001 From: Joe Onorato Date: Tue, 28 Jul 2009 18:23:05 -0700 Subject: [PATCH 152/541] Fix the IOException in wallpaper restore -- the padding isn't required at the end. --- libs/utils/BackupData.cpp | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/libs/utils/BackupData.cpp b/libs/utils/BackupData.cpp index be0477752..c51d989b4 100644 --- a/libs/utils/BackupData.cpp +++ b/libs/utils/BackupData.cpp @@ -196,6 +196,7 @@ BackupDataReader::Status() } else { \ m_status = errno; \ } \ + LOGD("CHECK_SIZE failed with at line %d m_status='%s'", __LINE__, strerror(m_status)); \ return m_status; \ } \ } while(0) @@ -203,6 +204,7 @@ BackupDataReader::Status() do { \ status_t err = skip_padding(); \ if (err != NO_ERROR) { \ + LOGD("SKIP_PADDING FAILED at line %d", __LINE__); \ m_status = err; \ return err; \ } \ @@ -218,10 +220,19 @@ BackupDataReader::ReadNextHeader(bool* done, int* type) int amt; - // No error checking here, in case we're at the end of the stream. Just let read() fail. - skip_padding(); + amt = skip_padding(); + if (amt == EIO) { + *done = true; + return NO_ERROR; + } + else if (amt != NO_ERROR) { + return amt; + } amt = read(m_fd, &m_header, sizeof(m_header)); *done = m_done = (amt == 0); + if (*done) { + return NO_ERROR; + } CHECK_SIZE(amt, sizeof(m_header)); m_pos += sizeof(m_header); if (type) { From 01025b2e804468f62b85887e8f2b80c2012f774b Mon Sep 17 00:00:00 2001 From: Joe Onorato Date: Tue, 28 Jul 2009 18:24:51 -0700 Subject: [PATCH 153/541] Only restore the bits for wallpapers that aren't built in. --- libs/utils/BackupData.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/libs/utils/BackupData.cpp b/libs/utils/BackupData.cpp index c51d989b4..0cef35aed 100644 --- a/libs/utils/BackupData.cpp +++ b/libs/utils/BackupData.cpp @@ -193,6 +193,7 @@ BackupDataReader::Status() if ((actual) != (expected)) { \ if ((actual) == 0) { \ m_status = EIO; \ + m_done = true; \ } else { \ m_status = errno; \ } \ @@ -222,7 +223,7 @@ BackupDataReader::ReadNextHeader(bool* done, int* type) amt = skip_padding(); if (amt == EIO) { - *done = true; + *done = m_done = true; return NO_ERROR; } else if (amt != NO_ERROR) { @@ -338,6 +339,10 @@ BackupDataReader::ReadEntityData(void* data, size_t size) m_status = errno; return -1; } + if (amt == 0) { + m_status = EIO; + m_done = true; + } m_pos += amt; return amt; } From ce87c24b3ab6f06a04c1e480814d7d66881aa007 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Wed, 29 Jul 2009 15:41:19 -0700 Subject: [PATCH 154/541] Fix #2018814: System cannot correctly render assets with "wrap_content" attribute in QVGA It turns out we were not returning the density for anything retrieved from a TypedArray... which basically means any bitmap references from a layout or style...!!! This is now fixed. Also fiddle with the density compatibility mode to turn on smoothing in certain situations, helping the look of things when they need to scale and we couldn't do the scaling at load time. --- include/utils/ResourceTypes.h | 6 ++++-- libs/utils/ResourceTypes.cpp | 11 +++++++---- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h index edd0cae63..e524e2a31 100644 --- a/include/utils/ResourceTypes.h +++ b/include/utils/ResourceTypes.h @@ -1655,7 +1655,8 @@ public: ssize_t resolveReference(Res_value* inOutValue, ssize_t blockIndex, uint32_t* outLastRef = NULL, - uint32_t* inoutTypeSpecFlags = NULL) const; + uint32_t* inoutTypeSpecFlags = NULL, + ResTable_config* outConfig = NULL) const; enum { TMP_BUFFER_SIZE = 16 @@ -1729,7 +1730,8 @@ public: */ ssize_t resolveAttributeReference(Res_value* inOutValue, ssize_t blockIndex, uint32_t* outLastRef = NULL, - uint32_t* inoutTypeSpecFlags = NULL) const; + uint32_t* inoutTypeSpecFlags = NULL, + ResTable_config* inoutConfig = NULL) const; void dumpToLog() const; diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp index 4dca8bd81..0831f4a0c 100644 --- a/libs/utils/ResourceTypes.cpp +++ b/libs/utils/ResourceTypes.cpp @@ -1486,7 +1486,7 @@ ssize_t ResTable::Theme::getAttribute(uint32_t resID, Res_value* outValue, ssize_t ResTable::Theme::resolveAttributeReference(Res_value* inOutValue, ssize_t blockIndex, uint32_t* outLastRef, - uint32_t* inoutTypeSpecFlags) const + uint32_t* inoutTypeSpecFlags, ResTable_config* inoutConfig) const { //printf("Resolving type=0x%x\n", inOutValue->dataType); if (inOutValue->dataType == Res_value::TYPE_ATTRIBUTE) { @@ -1498,7 +1498,8 @@ ssize_t ResTable::Theme::resolveAttributeReference(Res_value* inOutValue, return blockIndex; } } - return mTable.resolveReference(inOutValue, blockIndex, outLastRef); + return mTable.resolveReference(inOutValue, blockIndex, outLastRef, + inoutTypeSpecFlags, inoutConfig); } void ResTable::Theme::dumpToLog() const @@ -1891,7 +1892,8 @@ ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag } ssize_t ResTable::resolveReference(Res_value* value, ssize_t blockIndex, - uint32_t* outLastRef, uint32_t* inoutTypeSpecFlags) const + uint32_t* outLastRef, uint32_t* inoutTypeSpecFlags, + ResTable_config* outConfig) const { int count=0; while (blockIndex >= 0 && value->dataType == value->TYPE_REFERENCE @@ -1899,7 +1901,8 @@ ssize_t ResTable::resolveReference(Res_value* value, ssize_t blockIndex, if (outLastRef) *outLastRef = value->data; uint32_t lastRef = value->data; uint32_t newFlags = 0; - const ssize_t newIndex = getResource(value->data, value, true, &newFlags); + const ssize_t newIndex = getResource(value->data, value, true, &newFlags, + outConfig); //LOGI("Resolving reference d=%p: newIndex=%d, t=0x%02x, d=%p\n", // (void*)lastRef, (int)newIndex, (int)value->dataType, (void*)value->data); //printf("Getting reference 0x%08x: newIndex=%d\n", value->data, newIndex); From 9bafd12fa18b74e30995525a32b1be5d7804656a Mon Sep 17 00:00:00 2001 From: David 'Digit' Turner Date: Sat, 1 Aug 2009 00:20:17 +0200 Subject: [PATCH 155/541] Fix Win32 libutils to get a working SDK build. --- libs/utils/Threads.cpp | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp index 4036c497a..6be372c87 100644 --- a/libs/utils/Threads.cpp +++ b/libs/utils/Threads.cpp @@ -296,6 +296,19 @@ Mutex::Mutex(const char* name) // XXX: name not used for now HANDLE hMutex; + assert(sizeof(hMutex) == sizeof(mState)); + + hMutex = CreateMutex(NULL, FALSE, NULL); + mState = (void*) hMutex; +} + +Mutex::Mutex(int type, const char* name) +{ + // XXX: type and name not used for now + HANDLE hMutex; + + assert(sizeof(hMutex) == sizeof(mState)); + hMutex = CreateMutex(NULL, FALSE, NULL); mState = (void*) hMutex; } @@ -486,7 +499,11 @@ status_t Condition::wait(Mutex& mutex) status_t Condition::waitRelative(Mutex& mutex, nsecs_t reltime) { - return wait(mutex, systemTime()+reltime); + WinCondition* condState = (WinCondition*) mState; + HANDLE hMutex = (HANDLE) mutex.mState; + nsecs_t absTime = systemTime()+reltime; + + return ((WinCondition*)mState)->wait(condState, hMutex, &absTime); } /* From 0042a806ce64e9d6f3ae838c4d8ce87311f89354 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Tue, 11 Aug 2009 18:56:41 -0700 Subject: [PATCH 156/541] Fix issue #2048263: More debugging information We now hopefully do better about generating the anr reports, and include information about the malloc loaded assets in meminfo. --- include/utils/Asset.h | 12 +++++++++ libs/utils/Asset.cpp | 58 +++++++++++++++++++++++++++++++++++++++---- 2 files changed, 65 insertions(+), 5 deletions(-) diff --git a/include/utils/Asset.h b/include/utils/Asset.h index 453a2049a..5908bcced 100644 --- a/include/utils/Asset.h +++ b/include/utils/Asset.h @@ -45,6 +45,7 @@ public: virtual ~Asset(void); static int32_t getGlobalCount(); + static String8 getAssetAllocations(); /* used when opening an asset */ typedef enum AccessMode { @@ -109,6 +110,12 @@ public: */ virtual int openFileDescriptor(off_t* outStart, off_t* outLength) const = 0; + /* + * Return whether this asset's buffer is allocated in RAM (not mmapped). + * Note: not virtual so it is safe to call even when being destroyed. + */ + virtual bool isAllocated(void) const { return false; } + /* * Get a string identifying the asset's source. This might be a full * path, it might be a colon-separated list of identifiers. @@ -197,6 +204,9 @@ private: AccessMode mAccessMode; // how the asset was opened String8 mAssetSource; // debug string + + Asset* mNext; // linked list. + Asset* mPrev; }; @@ -239,6 +249,7 @@ public: virtual off_t getLength(void) const { return mLength; } virtual off_t getRemainingLength(void) const { return mLength-mOffset; } virtual int openFileDescriptor(off_t* outStart, off_t* outLength) const; + virtual bool isAllocated(void) const { return mBuf != NULL; } private: off_t mStart; // absolute file offset of start of chunk @@ -295,6 +306,7 @@ public: virtual off_t getLength(void) const { return mUncompressedLen; } virtual off_t getRemainingLength(void) const { return mUncompressedLen-mOffset; } virtual int openFileDescriptor(off_t* outStart, off_t* outLength) const { return -1; } + virtual bool isAllocated(void) const { return mBuf != NULL; } private: off_t mStart; // offset to start of compressed data diff --git a/libs/utils/Asset.cpp b/libs/utils/Asset.cpp index 23cb72d4e..42951237d 100644 --- a/libs/utils/Asset.cpp +++ b/libs/utils/Asset.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -40,24 +41,71 @@ using namespace android; # define O_BINARY 0 #endif -static volatile int32_t gCount = 0; +static Mutex gAssetLock; +static int32_t gCount = 0; +static Asset* gHead = NULL; +static Asset* gTail = NULL; int32_t Asset::getGlobalCount() { + AutoMutex _l(gAssetLock); return gCount; } +String8 Asset::getAssetAllocations() +{ + AutoMutex _l(gAssetLock); + String8 res; + Asset* cur = gHead; + while (cur != NULL) { + if (cur->isAllocated()) { + res.append(" "); + res.append(cur->getAssetSource()); + off_t size = (cur->getLength()+512)/1024; + char buf[64]; + sprintf(buf, ": %dK\n", (int)size); + res.append(buf); + } + cur = cur->mNext; + } + + return res; +} + Asset::Asset(void) : mAccessMode(ACCESS_UNKNOWN) { - int count = android_atomic_inc(&gCount)+1; - //LOGI("Creating Asset %p #%d\n", this, count); + AutoMutex _l(gAssetLock); + gCount++; + mNext = mPrev = NULL; + if (gTail == NULL) { + gHead = gTail = this; + } else { + mPrev = gTail; + gTail->mNext = this; + gTail = this; + } + //LOGI("Creating Asset %p #%d\n", this, gCount); } Asset::~Asset(void) { - int count = android_atomic_dec(&gCount); - //LOGI("Destroying Asset in %p #%d\n", this, count); + AutoMutex _l(gAssetLock); + gCount--; + if (gHead == this) { + gHead = mNext; + } + if (gTail == this) { + gTail = mPrev; + } + if (mNext != NULL) { + mNext->mPrev = mPrev; + } + if (mPrev != NULL) { + mPrev->mNext = mNext; + } + mNext = mPrev = NULL; + //LOGI("Destroying Asset in %p #%d\n", this, gCount); } /* From 023a9546adc3e2c506a76b27583e92ade0988f25 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Mon, 17 Aug 2009 13:33:27 -0700 Subject: [PATCH 157/541] Support for marshalling pointers / intptr_t in Parcel. Some refactoring to eliminate code duplication in Parcel implementation. --- include/utils/Debug.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/utils/Debug.h b/include/utils/Debug.h index 21d04bdce..d9ed32d7d 100644 --- a/include/utils/Debug.h +++ b/include/utils/Debug.h @@ -29,6 +29,8 @@ template<> struct CompileTimeAssert {}; #define COMPILE_TIME_ASSERT(_exp) \ template class CompileTimeAssert< (_exp) >; #endif +#define COMPILE_TIME_ASSERT_FUNCTION_SCOPE(_exp) \ + CompileTimeAssert<( _exp )>(); // --------------------------------------------------------------------------- From 7e3953f3434e2b8bb3cc0127c9f493b06045ecd7 Mon Sep 17 00:00:00 2001 From: Eric Fischer Date: Tue, 1 Sep 2009 15:20:30 -0700 Subject: [PATCH 158/541] Make it an error to use a bare apostrophe in aapt, and adjust warnings/errors. In practice, no one ever writes an apostrophe in an aapt string with the intent of using it to quote whitespace -- they always mean to include a literal apostrophe in the string and then are surprised when they find the apostrophe missing. Make this an error so that it is discovered right away instead of waiting until late in QA or after the strings have already been sent for translation. (And fix a recently-introduced string that has exactly this problem.) Silence the warning about an empty span in a string, since this seems to annoy people instead of finding any real problems. Make the error about having a translated string with no base string into a warning, since this is a big pain when making changes to an application that has already had some translations done, and the dead translations should be removed by a later translation import anyway. --- libs/utils/ResourceTypes.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp index 0831f4a0c..f80843d8d 100644 --- a/libs/utils/ResourceTypes.cpp +++ b/libs/utils/ResourceTypes.cpp @@ -3284,7 +3284,16 @@ bool ResTable::collectString(String16* outString, break; } if (c == '\'' && (quoted == 0 || quoted == '\'')) { - break; + /* + * In practice, when people write ' instead of \' + * in a string, they are doing it by accident + * instead of really meaning to use ' as a quoting + * character. Warn them so they don't lose it. + */ + if (outErrorMsg) { + *outErrorMsg = "Apostrophe not preceded by \\"; + } + return false; } } p++; From 641b630d2b08fa1cc48ee8e03b69b498f246c08e Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Mon, 7 Sep 2009 16:32:45 -0700 Subject: [PATCH 159/541] fix [2068105] implement queueBuffer/lockBuffer/dequeueBuffer properly Rewrote SurfaceFlinger's buffer management from the ground-up. The design now support an arbitrary number of buffers per surface, however the current implementation is limited to four. Currently only 2 buffers are used in practice. The main new feature is to be able to dequeue all buffers at once (very important when there are only two). A client can dequeue all buffers until there are none available, it can lock all buffers except the last one that is used for composition. The client will block then, until a new buffer is enqueued. The current implementation requires that buffers are locked in the same order they are dequeued and enqueued in the same order they are locked. Only one buffer can be locked at a time. eg. Allowed sequence: DQ, DQ, LOCK, Q, LOCK, Q eg. Forbidden sequence: DQ, DQ, LOCK, LOCK, Q, Q --- include/utils/Errors.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/utils/Errors.h b/include/utils/Errors.h index 1bf9e6f2b..81f818b75 100644 --- a/include/utils/Errors.h +++ b/include/utils/Errors.h @@ -63,7 +63,7 @@ enum { BAD_INDEX = -EOVERFLOW, NOT_ENOUGH_DATA = -ENODATA, WOULD_BLOCK = -EWOULDBLOCK, - TIMED_OUT = -ETIME, + TIMED_OUT = -ETIMEDOUT, UNKNOWN_TRANSACTION = -EBADMSG, #else BAD_INDEX = -E2BIG, From 51ce3ad7607a4b5ac533e6a3fd90709b30dba03f Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Wed, 9 Sep 2009 02:38:13 -0700 Subject: [PATCH 160/541] use broadcast() instead of signal() when signaling the condition-variable Thread::RequestExitAndWait() is waiting for we could have several thread waiting on the condition and they all need to wake-up. also added a debug "mTid" field in the class, which contains the tid of the thread (as opposed to pthread_t), this is useful when debugging under gdb for instance. --- include/utils/threads.h | 3 +++ libs/utils/Threads.cpp | 9 +++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/include/utils/threads.h b/include/utils/threads.h index e9b078889..0fc533f95 100644 --- a/include/utils/threads.h +++ b/include/utils/threads.h @@ -408,6 +408,9 @@ private: volatile bool mExitPending; volatile bool mRunning; sp mHoldSelf; +#if HAVE_ANDROID_OS + int mTid; +#endif }; diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp index 6be372c87..ec3db0980 100644 --- a/libs/utils/Threads.cpp +++ b/libs/utils/Threads.cpp @@ -655,6 +655,11 @@ int Thread::_threadLoop(void* user) wp weak(strong); self->mHoldSelf.clear(); +#if HAVE_ANDROID_OS + // this is very useful for debugging with gdb + self->mTid = gettid(); +#endif + bool first = true; do { @@ -685,7 +690,7 @@ int Thread::_threadLoop(void* user) self->mExitPending = true; self->mLock.lock(); self->mRunning = false; - self->mThreadExitedCondition.signal(); + self->mThreadExitedCondition.broadcast(); self->mLock.unlock(); break; } @@ -693,7 +698,7 @@ int Thread::_threadLoop(void* user) // Release our strong reference, to let a chance to the thread // to die a peaceful death. strong.clear(); - // And immediately, reacquire a strong reference for the next loop + // And immediately, re-acquire a strong reference for the next loop strong = weak.promote(); } while(strong != 0); From 7089ab8b38b626c79a55fdf216e07ec0c8088d01 Mon Sep 17 00:00:00 2001 From: San Mehat Date: Tue, 8 Sep 2009 20:28:35 -0700 Subject: [PATCH 161/541] Process: Add support for using scheduler policies instead of cgroups. Preference is given to cgroups if available. Signed-off-by: San Mehat --- include/utils/threads.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/utils/threads.h b/include/utils/threads.h index 0fc533f95..f5304f72b 100644 --- a/include/utils/threads.h +++ b/include/utils/threads.h @@ -90,6 +90,11 @@ enum { ANDROID_TGROUP_MAX = ANDROID_TGROUP_FG_BOOST, }; +typedef enum { + SP_BACKGROUND = 0, + SP_FOREGROUND = 1, +} SchedPolicy; + // Create and run a new thread. extern int androidCreateThread(android_thread_func_t, void *); From f4d57661befb874bff67d20f39ed737dbea62c67 Mon Sep 17 00:00:00 2001 From: San Mehat Date: Sat, 12 Sep 2009 10:10:37 -0700 Subject: [PATCH 162/541] process: Switch to common cutils sched_policy api Signed-off-by: San Mehat --- include/utils/threads.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/include/utils/threads.h b/include/utils/threads.h index f5304f72b..0fc533f95 100644 --- a/include/utils/threads.h +++ b/include/utils/threads.h @@ -90,11 +90,6 @@ enum { ANDROID_TGROUP_MAX = ANDROID_TGROUP_FG_BOOST, }; -typedef enum { - SP_BACKGROUND = 0, - SP_FOREGROUND = 1, -} SchedPolicy; - // Create and run a new thread. extern int androidCreateThread(android_thread_func_t, void *); From 7f4b32fd75ab441f478e86a8b755ba38a58473c0 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Tue, 15 Sep 2009 22:50:40 -0700 Subject: [PATCH 163/541] Implement issue #1780928: Need support hiding nav keys. This implements support for devices whose hardware can hide their navigation keys. It works much like the existing keyboardHidden configuration, and for compatibility uses the same configuration change bit. Also add FLAG_TURN_ON_SCREEN for windows, which has the system cause the screen to be turned on when the window is displayed. Great fun when used with FLAG_SHOW_WHEN_LOCKED! Change-Id: I0b867f19af85cfd8786a14cea194b34f7bdd9b7a --- include/utils/ResourceTypes.h | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h index e524e2a31..17ccad6f9 100644 --- a/include/utils/ResourceTypes.h +++ b/include/utils/ResourceTypes.h @@ -864,6 +864,13 @@ struct ResTable_config KEYSHIDDEN_SOFT = 0x0003, }; + enum { + MASK_NAVHIDDEN = 0x000c, + NAVHIDDEN_ANY = 0x0000, + NAVHIDDEN_NO = 0x0004, + NAVHIDDEN_YES = 0x0008, + }; + union { struct { uint8_t keyboard; @@ -1011,7 +1018,8 @@ struct ResTable_config if (orientation != o.orientation) diffs |= CONFIG_ORIENTATION; if (density != o.density) diffs |= CONFIG_DENSITY; if (touchscreen != o.touchscreen) diffs |= CONFIG_TOUCHSCREEN; - if (((inputFlags^o.inputFlags)&MASK_KEYSHIDDEN) != 0) diffs |= CONFIG_KEYBOARD_HIDDEN; + if (((inputFlags^o.inputFlags)&(MASK_KEYSHIDDEN|MASK_NAVHIDDEN)) != 0) + diffs |= CONFIG_KEYBOARD_HIDDEN; if (keyboard != o.keyboard) diffs |= CONFIG_KEYBOARD; if (navigation != o.navigation) diffs |= CONFIG_NAVIGATION; if (screenSize != o.screenSize) diffs |= CONFIG_SCREEN_SIZE; @@ -1082,6 +1090,11 @@ struct ResTable_config if (!(o.inputFlags & MASK_KEYSHIDDEN)) return true; } + if (((inputFlags^o.inputFlags) & MASK_NAVHIDDEN) != 0) { + if (!(inputFlags & MASK_NAVHIDDEN)) return false; + if (!(o.inputFlags & MASK_NAVHIDDEN)) return true; + } + if (keyboard != o.keyboard) { if (!keyboard) return false; if (!o.keyboard) return true; @@ -1225,6 +1238,18 @@ struct ResTable_config } } + const int navHidden = inputFlags & MASK_NAVHIDDEN; + const int oNavHidden = o.inputFlags & MASK_NAVHIDDEN; + if (navHidden != oNavHidden) { + const int reqNavHidden = + requested->inputFlags & MASK_NAVHIDDEN; + if (reqNavHidden) { + + if (!navHidden) return false; + if (!oNavHidden) return true; + } + } + if ((keyboard != o.keyboard) && requested->keyboard) { return (keyboard); } @@ -1332,6 +1357,12 @@ struct ResTable_config return false; } } + const int navHidden = inputFlags&MASK_NAVHIDDEN; + const int setNavHidden = settings.inputFlags&MASK_NAVHIDDEN; + if (setNavHidden != 0 && navHidden != 0 + && navHidden != setNavHidden) { + return false; + } if (settings.keyboard != 0 && keyboard != 0 && keyboard != settings.keyboard) { return false; From f660ea533bc4a9312de1f07cdefc2411038e22e5 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Sun, 20 Sep 2009 12:40:03 -0700 Subject: [PATCH 164/541] Fix issue #1862317: Browser does not appear to honor anchors (#es) in links Also a little improved debugging output of bad resource identifiers. Change-Id: I054064ef22855608ffd722e4ccf12ce57d1992b2 --- libs/utils/ResourceTypes.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp index f80843d8d..a5a8cc953 100644 --- a/libs/utils/ResourceTypes.cpp +++ b/libs/utils/ResourceTypes.cpp @@ -1740,7 +1740,11 @@ bool ResTable::getResourceName(uint32_t resID, resource_name* outName) const const int e = Res_GETENTRY(resID); if (p < 0) { - LOGW("No package identifier when getting name for resource number 0x%08x", resID); + if (Res_GETPACKAGE(resID)+1 == 0) { + LOGW("No package identifier when getting name for resource number 0x%08x", resID); + } else { + LOGW("Resources don't contain pacakge for resource number 0x%08x", resID); + } return false; } if (t < 0) { @@ -1786,7 +1790,11 @@ ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag const int e = Res_GETENTRY(resID); if (p < 0) { - LOGW("No package identifier when getting value for resource number 0x%08x", resID); + if (Res_GETPACKAGE(resID)+1 == 0) { + LOGW("No package identifier when getting name for resource number 0x%08x", resID); + } else { + LOGW("Resources don't contain pacakge for resource number 0x%08x", resID); + } return BAD_INDEX; } if (t < 0) { From df6d39adf3344a1c5505d3d83527715fa2f7ef48 Mon Sep 17 00:00:00 2001 From: Christopher Tate Date: Mon, 21 Sep 2009 19:36:51 -0700 Subject: [PATCH 165/541] Clear the device's data from the transport when backup is disabled Turning off backup in the Settings UI constitutes an opt-out of the whole mechanism. For privacy reasons we instruct the backend to wipe all of the data belonging to this device when the user does this. If the attempt fails it is rescheduled in the future based on the transport's requestBackupTime() suggestion. If network connectivity changes prompt the transport to indicate a backup pass is appropriate "now," any pending init operation is processed before the backup schedule is resumed. The broadcasts used internally to the backup manager are now fully protected; third party apps can neither send nor receive them. (Also a minor logging change; don't log 'appropriate' EOF encountered during parsing of a backup data stream.) --- libs/utils/BackupData.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libs/utils/BackupData.cpp b/libs/utils/BackupData.cpp index 0cef35aed..2535094a8 100644 --- a/libs/utils/BackupData.cpp +++ b/libs/utils/BackupData.cpp @@ -196,8 +196,9 @@ BackupDataReader::Status() m_done = true; \ } else { \ m_status = errno; \ + LOGD("CHECK_SIZE(a=%ld e=%ld) failed at line %d m_status='%s'", \ + long(actual), long(expected), __LINE__, strerror(m_status)); \ } \ - LOGD("CHECK_SIZE failed with at line %d m_status='%s'", __LINE__, strerror(m_status)); \ return m_status; \ } \ } while(0) From 56aa1ada4014aa0beb575fe5e918574ebbaf784e Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Sun, 27 Sep 2009 17:00:13 -0700 Subject: [PATCH 166/541] fix "pacakge" typo in error log message --- libs/utils/ResourceTypes.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp index a5a8cc953..872b2bc70 100644 --- a/libs/utils/ResourceTypes.cpp +++ b/libs/utils/ResourceTypes.cpp @@ -1743,7 +1743,7 @@ bool ResTable::getResourceName(uint32_t resID, resource_name* outName) const if (Res_GETPACKAGE(resID)+1 == 0) { LOGW("No package identifier when getting name for resource number 0x%08x", resID); } else { - LOGW("Resources don't contain pacakge for resource number 0x%08x", resID); + LOGW("Resources don't contain package for resource number 0x%08x", resID); } return false; } @@ -1793,7 +1793,7 @@ ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag if (Res_GETPACKAGE(resID)+1 == 0) { LOGW("No package identifier when getting name for resource number 0x%08x", resID); } else { - LOGW("Resources don't contain pacakge for resource number 0x%08x", resID); + LOGW("Resources don't contain package for resource number 0x%08x", resID); } return BAD_INDEX; } From 62b7796351a3f0ee3fe9985fbc11c8d73ab304ef Mon Sep 17 00:00:00 2001 From: Christopher Tate Date: Wed, 30 Sep 2009 16:17:37 -0700 Subject: [PATCH 167/541] Turn off most of the backup-related debug logging The core logging in BackupManagerService and in the Google backup transport are still enabled at this point. Change-Id: I10abfa565bbd1097dd3631051b6aca163e4af33a --- libs/utils/BackupData.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/utils/BackupData.cpp b/libs/utils/BackupData.cpp index 2535094a8..adb317499 100644 --- a/libs/utils/BackupData.cpp +++ b/libs/utils/BackupData.cpp @@ -107,7 +107,7 @@ BackupDataWriter::WriteEntityHeader(const String8& key, size_t dataSize) } else { k = key; } - if (true) { + if (false) { LOGD("Writing entity: prefix='%s' key='%s' dataSize=%d", m_keyPrefix.string(), key.string(), dataSize); } From 6b8297700b5f947cb959edb56d34751bd74d5e81 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Sun, 1 Nov 2009 21:16:59 -0800 Subject: [PATCH 168/541] Fix issue #2226370: Resource versions match with equality Also fixed turned-around increment of version number for resources. :( Change-Id: I604137272da984bcd69cee4f174e6b7f2c786e46 --- include/utils/ResourceTypes.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h index 17ccad6f9..49145e8ef 100644 --- a/include/utils/ResourceTypes.h +++ b/include/utils/ResourceTypes.h @@ -1272,7 +1272,7 @@ struct ResTable_config if (version || o.version) { if ((sdkVersion != o.sdkVersion) && requested->sdkVersion) { - return (sdkVersion); + return (sdkVersion > o.sdkVersion); } if ((minorVersion != o.minorVersion) && @@ -1384,7 +1384,7 @@ struct ResTable_config } if (version != 0) { if (settings.sdkVersion != 0 && sdkVersion != 0 - && sdkVersion != settings.sdkVersion) { + && sdkVersion > settings.sdkVersion) { return false; } if (settings.minorVersion != 0 && minorVersion != 0 From 198df9e70cfa72762712678cea243a4fd34c18d1 Mon Sep 17 00:00:00 2001 From: Dan Egnor Date: Thu, 12 Nov 2009 11:32:50 -0800 Subject: [PATCH 169/541] Rename [I]DropBox[Service] to [I]DropBoxManager[Service]. Un-hide the DropBoxManager interface, and update the public API accordingly. --- cleanspec.mk | 1 + 1 file changed, 1 insertion(+) create mode 100644 cleanspec.mk diff --git a/cleanspec.mk b/cleanspec.mk new file mode 100644 index 000000000..683e303ec --- /dev/null +++ b/cleanspec.mk @@ -0,0 +1 @@ +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/com/android/internal/os/IDropBoxService.java) From 9b9d67363b10e466e4627f67b9b6536a3409a1f9 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Fri, 20 Nov 2009 14:26:42 -0800 Subject: [PATCH 170/541] Debugging for issue #2228381 android.view.InflateException Binary XML file line #37: Error inflating class after adding a secondary account Now that I have these debug logs, I want to keep them since they will make debugging these kinds of issues a lot easier in the future. (Note in this case there was no problem in the framework.) Change-Id: If2b0bbeda4706b7c5dc1ba4a5db04b74f40e1543 --- libs/utils/ResourceTypes.cpp | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp index 872b2bc70..450af8df0 100644 --- a/libs/utils/ResourceTypes.cpp +++ b/libs/utils/ResourceTypes.cpp @@ -42,6 +42,7 @@ #define TABLE_GETENTRY(x) //x #define TABLE_SUPER_NOISY(x) //x #define LOAD_TABLE_NOISY(x) //x +#define TABLE_THEME(x) //x namespace android { @@ -1447,18 +1448,23 @@ ssize_t ResTable::Theme::getAttribute(uint32_t resID, Res_value* outValue, const uint32_t t = Res_GETTYPE(resID); const uint32_t e = Res_GETENTRY(resID); - TABLE_NOISY(LOGV("Looking up attr 0x%08x in theme %p", resID, this)); + TABLE_THEME(LOGI("Looking up attr 0x%08x in theme %p", resID, this)); if (p >= 0) { const package_info* const pi = mPackages[p]; + TABLE_THEME(LOGI("Found package: %p", pi)); if (pi != NULL) { + TABLE_THEME(LOGI("Desired type index is %ld in avail %d", t, pi->numTypes)); if (t < pi->numTypes) { const type_info& ti = pi->types[t]; + TABLE_THEME(LOGI("Desired entry index is %ld in avail %d", e, ti.numEntries)); if (e < ti.numEntries) { const theme_entry& te = ti.entries[e]; - if (outTypeSpecFlags != NULL) { - *outTypeSpecFlags |= te.typeSpecFlags; - } + if (outTypeSpecFlags != NULL) { + *outTypeSpecFlags |= te.typeSpecFlags; + } + TABLE_THEME(LOGI("Theme value: type=0x%x, data=0x%08x", + te.value.dataType, te.value.data)); const uint8_t type = te.value.dataType; if (type == Res_value::TYPE_ATTRIBUTE) { if (cnt > 0) { @@ -1492,6 +1498,8 @@ ssize_t ResTable::Theme::resolveAttributeReference(Res_value* inOutValue, if (inOutValue->dataType == Res_value::TYPE_ATTRIBUTE) { uint32_t newTypeSpecFlags; blockIndex = getAttribute(inOutValue->data, inOutValue, &newTypeSpecFlags); + TABLE_THEME(LOGI("Resolving attr reference: blockIndex=%d, type=0x%x, data=%p\n", + (int)blockIndex, (int)inOutValue->dataType, (void*)inOutValue->data)); if (inoutTypeSpecFlags != NULL) *inoutTypeSpecFlags |= newTypeSpecFlags; //printf("Retrieved attribute new type=0x%x\n", inOutValue->dataType); if (blockIndex < 0) { @@ -1911,8 +1919,8 @@ ssize_t ResTable::resolveReference(Res_value* value, ssize_t blockIndex, uint32_t newFlags = 0; const ssize_t newIndex = getResource(value->data, value, true, &newFlags, outConfig); - //LOGI("Resolving reference d=%p: newIndex=%d, t=0x%02x, d=%p\n", - // (void*)lastRef, (int)newIndex, (int)value->dataType, (void*)value->data); + TABLE_THEME(LOGI("Resolving reference %p: newIndex=%d, type=0x%x, data=%p\n", + (void*)lastRef, (int)newIndex, (int)value->dataType, (void*)value->data)); //printf("Getting reference 0x%08x: newIndex=%d\n", value->data, newIndex); if (inoutTypeSpecFlags != NULL) *inoutTypeSpecFlags |= newFlags; if (newIndex < 0) { From 09b41cbf9fe74a831c0ad883406e03dfe0568d78 Mon Sep 17 00:00:00 2001 From: Ken Shirriff Date: Tue, 1 Dec 2009 17:38:52 -0800 Subject: [PATCH 171/541] Generate a calendar static Java library. This contains the calendar contract and pim functions for use by the sync adapter. This library is a duplicate of the functions in the framework. --- calendar/Android.mk | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 calendar/Android.mk diff --git a/calendar/Android.mk b/calendar/Android.mk new file mode 100644 index 000000000..fd20dfaea --- /dev/null +++ b/calendar/Android.mk @@ -0,0 +1,18 @@ +# Copyright 2009 Google, Inc. + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := calendar +LOCAL_SRC_FILES := \ + ../core/java/android/provider/Calendar.java \ + ../core/java/android/pim/EventRecurrence.java \ + ../core/java/android/pim/ICalendar.java \ + ../core/java/android/pim/RecurrenceSet.java \ + ../core/java/android/pim/ContactsAsyncHelper.java \ + ../core/java/android/pim/DateException.java + +include $(BUILD_STATIC_JAVA_LIBRARY) + +# Include this library in the build server's output directory +$(call dist-for-goals, droid, $(LOCAL_BUILT_MODULE):calendar.jar) From 9a2d83e698b16ec86ad2751b6e7cf103ad645cce Mon Sep 17 00:00:00 2001 From: Kenny Root Date: Fri, 4 Dec 2009 09:38:48 -0800 Subject: [PATCH 172/541] Optional use of UTF-8 strings in resource bundles Allows the use of UTF-8 for packing resources instead of the default of UTF-16 for Java. When strings are extracted from the ResStringPool, they are converted to UTF-16 and the result is cached for subsequent calls. When using aapt to package, add in the "-8" switch to pack the resources using UTF-8. This will result in the value, key, and type strings as well as the compiled XML string values taking significantly less space in the final application package in most scenarios. Change-Id: I129483f8b3d3b1c5869dced05cb525e494a6c83a --- include/utils/ResourceTypes.h | 9 ++- include/utils/String16.h | 5 ++ include/utils/String8.h | 8 +++ libs/utils/ResourceTypes.cpp | 104 +++++++++++++++++++++++++++------- libs/utils/String16.cpp | 58 +++++++++++-------- libs/utils/String8.cpp | 67 +++++++++++++++++++++- 6 files changed, 203 insertions(+), 48 deletions(-) diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h index 49145e8ef..a845908f9 100644 --- a/include/utils/ResourceTypes.h +++ b/include/utils/ResourceTypes.h @@ -393,7 +393,10 @@ struct ResStringPool_header enum { // If set, the string index is sorted by the string values (based // on strcmp16()). - SORTED_FLAG = 1<<0 + SORTED_FLAG = 1<<0, + + // String pool is encoded in UTF-8 + UTF8_FLAG = 1<<8 }; uint32_t flags; @@ -456,9 +459,11 @@ private: void* mOwnedData; const ResStringPool_header* mHeader; size_t mSize; + mutable Mutex mDecodeLock; const uint32_t* mEntries; const uint32_t* mEntryStyles; - const char16_t* mStrings; + const void* mStrings; + char16_t** mCache; uint32_t mStringPoolSize; // number of uint16_t const uint32_t* mStyles; uint32_t mStylePoolSize; // number of uint32_t diff --git a/include/utils/String16.h b/include/utils/String16.h index a2d22eea9..07a0c1188 100644 --- a/include/utils/String16.h +++ b/include/utils/String16.h @@ -49,12 +49,17 @@ int strzcmp16(const char16_t *s1, size_t n1, const char16_t *s2, size_t n2); // Version of strzcmp16 for comparing strings in different endianness. int strzcmp16_h_n(const char16_t *s1H, size_t n1, const char16_t *s2N, size_t n2); +// Convert UTF-8 to UTF-16 including surrogate pairs +void utf8_to_utf16(const uint8_t *src, size_t srcLen, char16_t* dst, const size_t dstLen); + } // --------------------------------------------------------------------------- namespace android { +// --------------------------------------------------------------------------- + class String8; class TextOutput; diff --git a/include/utils/String8.h b/include/utils/String8.h index ecc577437..c4b18a4ca 100644 --- a/include/utils/String8.h +++ b/include/utils/String8.h @@ -57,6 +57,11 @@ size_t utf8_length(const char *src); */ size_t utf32_length(const char *src, size_t src_len); +/* + * Returns the UTF-8 length of "src". + */ +size_t utf8_length_from_utf16(const char16_t *src, size_t src_len); + /* * Returns the UTF-8 length of "src". */ @@ -120,6 +125,9 @@ size_t utf8_to_utf32(const char* src, size_t src_len, size_t utf32_to_utf8(const char32_t* src, size_t src_len, char* dst, size_t dst_len); +size_t utf16_to_utf8(const char16_t* src, size_t src_len, + char* dst, size_t dst_len); + } // --------------------------------------------------------------------------- diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp index 450af8df0..afca814fd 100644 --- a/libs/utils/ResourceTypes.cpp +++ b/libs/utils/ResourceTypes.cpp @@ -229,12 +229,12 @@ Res_png_9patch* Res_png_9patch::deserialize(const void* inData) // -------------------------------------------------------------------- ResStringPool::ResStringPool() - : mError(NO_INIT), mOwnedData(NULL) + : mError(NO_INIT), mOwnedData(NULL), mHeader(NULL), mCache(NULL) { } ResStringPool::ResStringPool(const void* data, size_t size, bool copyData) - : mError(NO_INIT), mOwnedData(NULL) + : mError(NO_INIT), mOwnedData(NULL), mHeader(NULL), mCache(NULL) { setTo(data, size, copyData); } @@ -296,7 +296,17 @@ status_t ResStringPool::setTo(const void* data, size_t size, bool copyData) (int)size); return (mError=BAD_TYPE); } - mStrings = (const char16_t*) + + size_t charSize; + if (mHeader->flags&ResStringPool_header::UTF8_FLAG) { + charSize = sizeof(uint8_t); + mCache = (char16_t**)malloc(sizeof(char16_t**)*mHeader->stringCount); + memset(mCache, 0, sizeof(char16_t**)*mHeader->stringCount); + } else { + charSize = sizeof(char16_t); + } + + mStrings = (const void*) (((const uint8_t*)data)+mHeader->stringsStart); if (mHeader->stringsStart >= (mHeader->header.size-sizeof(uint16_t))) { LOGW("Bad string block: string pool starts at %d, after total size %d\n", @@ -305,7 +315,7 @@ status_t ResStringPool::setTo(const void* data, size_t size, bool copyData) } if (mHeader->styleCount == 0) { mStringPoolSize = - (mHeader->header.size-mHeader->stringsStart)/sizeof(uint16_t); + (mHeader->header.size-mHeader->stringsStart)/charSize; } else { // check invariant: styles follow the strings if (mHeader->stylesStart <= mHeader->stringsStart) { @@ -314,7 +324,7 @@ status_t ResStringPool::setTo(const void* data, size_t size, bool copyData) return (mError=BAD_TYPE); } mStringPoolSize = - (mHeader->stylesStart-mHeader->stringsStart)/sizeof(uint16_t); + (mHeader->stylesStart-mHeader->stringsStart)/charSize; } // check invariant: stringCount > 0 requires a string pool to exist @@ -329,13 +339,19 @@ status_t ResStringPool::setTo(const void* data, size_t size, bool copyData) for (i=0; istringCount; i++) { e[i] = dtohl(mEntries[i]); } - char16_t* s = const_cast(mStrings); - for (i=0; iflags&ResStringPool_header::UTF8_FLAG)) { + const char16_t* strings = (const char16_t*)mStrings; + char16_t* s = const_cast(strings); + for (i=0; iflags&ResStringPool_header::UTF8_FLAG && + ((uint8_t*)mStrings)[mStringPoolSize-1] != 0) || + (!mHeader->flags&ResStringPool_header::UTF8_FLAG && + ((char16_t*)mStrings)[mStringPoolSize-1] != 0)) { LOGW("Bad string block: last string is not 0-terminated\n"); return (mError=BAD_TYPE); } @@ -410,24 +426,67 @@ void ResStringPool::uninit() free(mOwnedData); mOwnedData = NULL; } + if (mHeader != NULL && mCache != NULL) { + for (size_t x = 0; x < mHeader->stringCount; x++) { + if (mCache[x] != NULL) { + free(mCache[x]); + mCache[x] = NULL; + } + } + free(mCache); + mCache = NULL; + } } +#define DECODE_LENGTH(str, chrsz, len) \ + len = *(str); \ + if (*(str)&(1<<(chrsz*8-1))) { \ + (str)++; \ + len = (((len)&((1<<(chrsz*8-1))-1))<<(chrsz*8)) + *(str); \ + } \ + (str)++; + const uint16_t* ResStringPool::stringAt(size_t idx, size_t* outLen) const { if (mError == NO_ERROR && idx < mHeader->stringCount) { - const uint32_t off = (mEntries[idx]/sizeof(uint16_t)); + const bool isUTF8 = (mHeader->flags&ResStringPool_header::UTF8_FLAG) != 0; + const uint32_t off = mEntries[idx]/(isUTF8?sizeof(char):sizeof(char16_t)); if (off < (mStringPoolSize-1)) { - const char16_t* str = mStrings+off; - *outLen = *str; - if ((*str)&0x8000) { - str++; - *outLen = (((*outLen)&0x7fff)<<16) + *str; - } - if ((uint32_t)(str+1+*outLen-mStrings) < mStringPoolSize) { - return str+1; + if (!isUTF8) { + const char16_t* strings = (char16_t*)mStrings; + const char16_t* str = strings+off; + DECODE_LENGTH(str, sizeof(char16_t), *outLen) + if ((uint32_t)(str+*outLen-strings) < mStringPoolSize) { + return str; + } else { + LOGW("Bad string block: string #%d extends to %d, past end at %d\n", + (int)idx, (int)(str+*outLen-strings), (int)mStringPoolSize); + } } else { - LOGW("Bad string block: string #%d extends to %d, past end at %d\n", - (int)idx, (int)(str+1+*outLen-mStrings), (int)mStringPoolSize); + const uint8_t* strings = (uint8_t*)mStrings; + const uint8_t* str = strings+off; + DECODE_LENGTH(str, sizeof(uint8_t), *outLen) + size_t encLen; + DECODE_LENGTH(str, sizeof(uint8_t), encLen) + if ((uint32_t)(str+encLen-strings) < mStringPoolSize) { + AutoMutex lock(mDecodeLock); + if (mCache[idx] != NULL) { + return mCache[idx]; + } + char16_t *u16str = (char16_t *)calloc(*outLen+1, sizeof(char16_t)); + if (!u16str) { + LOGW("No memory when trying to allocate decode cache for string #%d\n", + (int)idx); + return NULL; + } + const unsigned char *u8src = reinterpret_cast(str); + utf8_to_utf16(u8src, encLen, u16str, *outLen); + mCache[idx] = u16str; + return u16str; + } else { + LOGW("Bad string block: string #%d extends to %d, past end at %d\n", + (int)idx, (int)(str+encLen-strings), (int)mStringPoolSize); + } } } else { LOGW("Bad string block: string #%d entry is at %d, past end at %d\n", @@ -466,6 +525,10 @@ ssize_t ResStringPool::indexOfString(const char16_t* str, size_t strLen) const size_t len; + // TODO optimize searching for UTF-8 strings taking into account + // the cache fill to determine when to convert the searched-for + // string key to UTF-8. + if (mHeader->flags&ResStringPool_header::SORTED_FLAG) { // Do a binary search for the string... ssize_t l = 0; @@ -1043,6 +1106,7 @@ status_t ResXMLTree::getError() const void ResXMLTree::uninit() { mError = NO_INIT; + mStrings.uninit(); if (mOwnedData) { free(mOwnedData); mOwnedData = NULL; diff --git a/libs/utils/String16.cpp b/libs/utils/String16.cpp index aef67f22a..eab7b2b92 100644 --- a/libs/utils/String16.cpp +++ b/libs/utils/String16.cpp @@ -172,10 +172,6 @@ int strzcmp16_h_n(const char16_t *s1H, size_t n1, const char16_t *s2N, size_t n2 : 0); } -// --------------------------------------------------------------------------- - -namespace android { - static inline size_t utf8_char_len(uint8_t ch) { @@ -215,8 +211,38 @@ utf8_to_utf32(const uint8_t *src, size_t length) //printf("Char at %p: len=%d, utf-16=%p\n", src, length, (void*)result); } +void +utf8_to_utf16(const uint8_t *src, size_t srcLen, + char16_t* dst, const size_t dstLen) +{ + const uint8_t* const end = src + srcLen; + const char16_t* const dstEnd = dst + dstLen; + while (src < end && dst < dstEnd) { + size_t len = utf8_char_len(*src); + uint32_t codepoint = utf8_to_utf32((const uint8_t*)src, len); + + // Convert the UTF32 codepoint to one or more UTF16 codepoints + if (codepoint <= 0xFFFF) { + // Single UTF16 character + *dst++ = (char16_t) codepoint; + } else { + // Multiple UTF16 characters with surrogates + codepoint = codepoint - 0x10000; + *dst++ = (char16_t) ((codepoint >> 10) + 0xD800); + *dst++ = (char16_t) ((codepoint & 0x3FF) + 0xDC00); + } + + src += len; + } + if (dst < dstEnd) { + *dst = 0; + } +} + // --------------------------------------------------------------------------- +namespace android { + static SharedBuffer* gEmptyStringBuf = NULL; static char16_t* gEmptyString = NULL; @@ -260,30 +286,14 @@ static char16_t* allocFromUTF8(const char* in, size_t len) p += utf8len; } - SharedBuffer* buf = SharedBuffer::alloc((chars+1)*sizeof(char16_t)); + size_t bufSize = (chars+1)*sizeof(char16_t); + SharedBuffer* buf = SharedBuffer::alloc(bufSize); if (buf) { p = in; char16_t* str = (char16_t*)buf->data(); - char16_t* d = str; - while (p < end) { - size_t len = utf8_char_len(*p); - uint32_t codepoint = utf8_to_utf32((const uint8_t*)p, len); - - // Convert the UTF32 codepoint to one or more UTF16 codepoints - if (codepoint <= 0xFFFF) { - // Single UTF16 character - *d++ = (char16_t) codepoint; - } else { - // Multiple UTF16 characters with surrogates - codepoint = codepoint - 0x10000; - *d++ = (char16_t) ((codepoint >> 10) + 0xD800); - *d++ = (char16_t) ((codepoint & 0x3FF) + 0xDC00); - } - - p += len; - } - *d = 0; + utf8_to_utf16((const uint8_t*)p, len, str, bufSize); + //printf("Created UTF-16 string from UTF-8 \"%s\":", in); //printHexData(1, str, buf->size(), 16, 1); //printf("\n"); diff --git a/libs/utils/String8.cpp b/libs/utils/String8.cpp index e908ec1e8..3a3483871 100644 --- a/libs/utils/String8.cpp +++ b/libs/utils/String8.cpp @@ -208,10 +208,23 @@ static char* allocFromUTF16OrUTF32(const T* in, L len) return getEmptyString(); } -// Note: not dealing with expanding surrogate pairs. static char* allocFromUTF16(const char16_t* in, size_t len) { - return allocFromUTF16OrUTF32(in, len); + if (len == 0) return getEmptyString(); + + const size_t bytes = utf8_length_from_utf16(in, len); + + SharedBuffer* buf = SharedBuffer::alloc(bytes+1); + LOG_ASSERT(buf, "Unable to allocate shared buffer"); + if (buf) { + char* str = (char*)buf->data(); + + utf16_to_utf8(in, len, str, bytes+1); + + return str; + } + + return getEmptyString(); } static char* allocFromUTF32(const char32_t* in, size_t len) @@ -762,6 +775,26 @@ size_t utf8_length_from_utf32(const char32_t *src, size_t src_len) return ret; } +size_t utf8_length_from_utf16(const char16_t *src, size_t src_len) +{ + if (src == NULL || src_len == 0) { + return 0; + } + size_t ret = 0; + const char16_t* const end = src + src_len; + while (src < end) { + if ((*src & 0xFC00) == 0xD800 && (src + 1) < end + && (*++src & 0xFC00) == 0xDC00) { + // surrogate pairs are always 4 bytes. + ret += 4; + src++; + } else { + ret += android::utf32_to_utf8_bytes((char32_t) *src++); + } + } + return ret; +} + static int32_t utf32_at_internal(const char* cur, size_t *num_read) { const char first_char = *cur; @@ -848,3 +881,33 @@ size_t utf32_to_utf8(const char32_t* src, size_t src_len, } return cur - dst; } + +size_t utf16_to_utf8(const char16_t* src, size_t src_len, + char* dst, size_t dst_len) +{ + if (src == NULL || src_len == 0 || dst == NULL || dst_len == 0) { + return 0; + } + const char16_t* cur_utf16 = src; + const char16_t* const end_utf16 = src + src_len; + char *cur = dst; + const char* const end = dst + dst_len; + while (cur_utf16 < end_utf16 && cur < end) { + char32_t utf32; + // surrogate pairs + if ((*cur_utf16 & 0xFC00) == 0xD800 && (cur_utf16 + 1) < end_utf16) { + utf32 = (*cur_utf16++ - 0xD800) << 10; + utf32 |= *cur_utf16++ - 0xDC00; + utf32 += 0x10000; + } else { + utf32 = (char32_t) *cur_utf16++; + } + size_t len = android::utf32_to_utf8_bytes(utf32); + android::utf32_to_utf8((uint8_t*)cur, utf32, len); + cur += len; + } + if (cur < end) { + *cur = '\0'; + } + return cur - dst; +} From 235af97debd4b75263dfdb9e3be78e50eff2a53a Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Mon, 7 Dec 2009 17:59:37 -0800 Subject: [PATCH 173/541] Propagate background scheduling class across processes. This is a very simply implementation: upon receiving an IPC, if the handling thread is at a background priority (the driver will have taken care of propagating this from the calling thread), then stick it in to the background scheduling group. Plus an API to turn this off for the process, which is used by the system process. This also pulls some of the code for managing scheduling classes out of the Process JNI wrappers and in to some convenience methods in thread.h. --- include/utils/threads.h | 18 +++++++++++++++ libs/utils/Threads.cpp | 49 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+) diff --git a/include/utils/threads.h b/include/utils/threads.h index 0fc533f95..130d83c0d 100644 --- a/include/utils/threads.h +++ b/include/utils/threads.h @@ -124,6 +124,24 @@ typedef int (*android_create_thread_fn)(android_thread_func_t entryFunction, extern void androidSetCreateThreadFunc(android_create_thread_fn func); +// ------------------------------------------------------------------ +// Extra functions working with raw pids. + +// Get pid for the current thread. +extern pid_t androidGetTid(); + +// Change the scheduling group of a particular thread. The group +// should be one of the ANDROID_TGROUP constants. Returns BAD_VALUE if +// grp is out of range, else another non-zero value with errno set if +// the operation failed. +extern int androidSetThreadSchedulingGroup(pid_t tid, int grp); + +// Change the priority AND scheduling group of a particular thread. The priority +// should be one of the ANDROID_PRIORITY constants. Returns INVALID_OPERATION +// if the priority set failed, else another value if just the group set failed; +// in either case errno is set. +extern int androidSetThreadPriority(pid_t tid, int prio); + #ifdef __cplusplus } #endif diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp index ec3db0980..6ca260374 100644 --- a/libs/utils/Threads.cpp +++ b/libs/utils/Threads.cpp @@ -20,6 +20,8 @@ #include #include +#include + #include #include #include @@ -269,6 +271,53 @@ void androidSetCreateThreadFunc(android_create_thread_fn func) gCreateThreadFn = func; } +pid_t androidGetTid() +{ +#ifdef HAVE_GETTID + return gettid(); +#else + return getpid(); +#endif +} + +int androidSetThreadSchedulingGroup(pid_t tid, int grp) +{ + if (grp > ANDROID_TGROUP_MAX || grp < 0) { + return BAD_VALUE; + } + + if (set_sched_policy(tid, (grp == ANDROID_TGROUP_BG_NONINTERACT) ? + SP_BACKGROUND : SP_FOREGROUND)) { + return PERMISSION_DENIED; + } + + return NO_ERROR; +} + +int androidSetThreadPriority(pid_t tid, int pri) +{ + int rc = 0; + int lasterr = 0; + + if (pri >= ANDROID_PRIORITY_BACKGROUND) { + rc = set_sched_policy(tid, SP_BACKGROUND); + } else if (getpriority(PRIO_PROCESS, tid) >= ANDROID_PRIORITY_BACKGROUND) { + rc = set_sched_policy(tid, SP_FOREGROUND); + } + + if (rc) { + lasterr = errno; + } + + if (setpriority(PRIO_PROCESS, tid, pri) < 0) { + rc = INVALID_OPERATION; + } else { + errno = lasterr; + } + + return rc; +} + namespace android { /* From 3432efa95e93776f0e45ff9729a0e4580b4c8eba Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Tue, 8 Dec 2009 16:38:01 -0800 Subject: [PATCH 174/541] Hopefully fix Windows build --- libs/utils/Threads.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp index 6ca260374..00f9943bf 100644 --- a/libs/utils/Threads.cpp +++ b/libs/utils/Threads.cpp @@ -309,11 +309,13 @@ int androidSetThreadPriority(pid_t tid, int pri) lasterr = errno; } +#if defined(HAVE_PTHREADS) if (setpriority(PRIO_PROCESS, tid, pri) < 0) { rc = INVALID_OPERATION; } else { errno = lasterr; } +#endif return rc; } From aaa7ef86c623541b85a36f208ca1c0c9f8c2c2c6 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Tue, 8 Dec 2009 19:45:59 -0800 Subject: [PATCH 175/541] Maybe really fix windows build. --- libs/utils/Threads.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp index 00f9943bf..2b1f49044 100644 --- a/libs/utils/Threads.cpp +++ b/libs/utils/Threads.cpp @@ -286,10 +286,12 @@ int androidSetThreadSchedulingGroup(pid_t tid, int grp) return BAD_VALUE; } +#if defined(HAVE_PTHREADS) if (set_sched_policy(tid, (grp == ANDROID_TGROUP_BG_NONINTERACT) ? SP_BACKGROUND : SP_FOREGROUND)) { return PERMISSION_DENIED; } +#endif return NO_ERROR; } @@ -297,6 +299,8 @@ int androidSetThreadSchedulingGroup(pid_t tid, int grp) int androidSetThreadPriority(pid_t tid, int pri) { int rc = 0; + +#if defined(HAVE_PTHREADS) int lasterr = 0; if (pri >= ANDROID_PRIORITY_BACKGROUND) { @@ -309,7 +313,6 @@ int androidSetThreadPriority(pid_t tid, int pri) lasterr = errno; } -#if defined(HAVE_PTHREADS) if (setpriority(PRIO_PROCESS, tid, pri) < 0) { rc = INVALID_OPERATION; } else { From 1443fdf9336da1fd51a11e04efb8f56d7d62cc60 Mon Sep 17 00:00:00 2001 From: Kenny Root Date: Thu, 10 Dec 2009 14:20:15 -0800 Subject: [PATCH 176/541] Add string resource type inspection Allows "aapt dump --values resource" to print out whether a string in a ResStringPool is in UTF-8 or UTF-16 encoding. Change-Id: I6478884a70a3b46fee862dece6cb33454fc34843 --- include/utils/ResourceTypes.h | 4 ++++ libs/utils/ResourceTypes.cpp | 10 +++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h index a845908f9..6090f6001 100644 --- a/include/utils/ResourceTypes.h +++ b/include/utils/ResourceTypes.h @@ -454,6 +454,10 @@ public: size_t size() const; +#ifndef HAVE_ANDROID_OS + bool isUTF8() const; +#endif + private: status_t mError; void* mOwnedData; diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp index afca814fd..e8bd5cf01 100644 --- a/libs/utils/ResourceTypes.cpp +++ b/libs/utils/ResourceTypes.cpp @@ -576,6 +576,13 @@ size_t ResStringPool::size() const return (mError == NO_ERROR) ? mHeader->stringCount : 0; } +#ifndef HAVE_ANDROID_OS +bool ResStringPool::isUTF8() const +{ + return (mHeader->flags&ResStringPool_header::UTF8_FLAG)!=0; +} +#endif + // -------------------------------------------------------------------- // -------------------------------------------------------------------- // -------------------------------------------------------------------- @@ -4016,7 +4023,8 @@ void ResTable::print_value(const Package* pkg, const Res_value& value) const if (str == NULL) { printf("(string) null\n"); } else { - printf("(string) \"%s\"\n", + printf("(string%d) \"%s\"\n", + pkg->header->values.isUTF8()?8:16, String8(str, len).string()); } } else if (value.dataType == Res_value::TYPE_FLOAT) { From 25d6d1576add4ce67778d96a74ff9a8ff8f1a985 Mon Sep 17 00:00:00 2001 From: Tom Taylor Date: Thu, 28 Jan 2010 15:54:15 -0800 Subject: [PATCH 177/541] Remove MMS from the framework The MMS code has been moved into the mms-common library. Move SqliteWrapper (and make it hidden) into the database directory because Telephony.java depends on it. Create a mmscommon library similar to androidcommon for a number of files used both by the telephony layer, by mms, and by myfaves. Change-Id: I2e23e87c4961b87c42a4c8a63f812fa9e0e44dec --- mms-common/Android.mk | 29 + .../com/android/common/CharacterSets.java | 172 ++ .../java/com/android/common/ContentType.java | 223 ++ .../android/common/EncodedStringValue.java | 284 +++ .../common/InvalidHeaderValueException.java | 41 + .../java/com/android/common/MmsException.java | 60 + .../java/com/android/common/PduHeaders.java | 719 +++++++ .../com/android/common/mms/ContentType.java | 223 ++ .../common/mms/pdu/AcknowledgeInd.java | 90 + .../com/android/common/mms/pdu/Base64.java | 167 ++ .../android/common/mms/pdu/DeliveryInd.java | 140 ++ .../android/common/mms/pdu/GenericPdu.java | 115 + .../common/mms/pdu/MultimediaMessagePdu.java | 152 ++ .../common/mms/pdu/NotificationInd.java | 287 +++ .../android/common/mms/pdu/NotifyRespInd.java | 115 + .../com/android/common/mms/pdu/PduBody.java | 191 ++ .../android/common/mms/pdu/PduComposer.java | 1184 +++++++++++ .../common/mms/pdu/PduContentTypes.java | 110 + .../com/android/common/mms/pdu/PduParser.java | 1873 +++++++++++++++++ .../com/android/common/mms/pdu/PduPart.java | 402 ++++ .../android/common/mms/pdu/PduPersister.java | 1266 +++++++++++ .../common/mms/pdu/QuotedPrintable.java | 68 + .../android/common/mms/pdu/ReadOrigInd.java | 155 ++ .../android/common/mms/pdu/ReadRecInd.java | 146 ++ .../android/common/mms/pdu/RetrieveConf.java | 302 +++ .../com/android/common/mms/pdu/SendConf.java | 119 ++ .../com/android/common/mms/pdu/SendReq.java | 347 +++ .../mms/telephony/TelephonyProvider.java | 1790 ++++++++++++++++ .../common/mms/util/AbstractCache.java | 113 + .../com/android/common/mms/util/PduCache.java | 243 +++ .../common/mms/util/PduCacheEntry.java | 44 + 31 files changed, 11170 insertions(+) create mode 100644 mms-common/Android.mk create mode 100644 mms-common/java/com/android/common/CharacterSets.java create mode 100644 mms-common/java/com/android/common/ContentType.java create mode 100644 mms-common/java/com/android/common/EncodedStringValue.java create mode 100644 mms-common/java/com/android/common/InvalidHeaderValueException.java create mode 100644 mms-common/java/com/android/common/MmsException.java create mode 100644 mms-common/java/com/android/common/PduHeaders.java create mode 100644 mms-common/java/com/android/common/mms/ContentType.java create mode 100644 mms-common/java/com/android/common/mms/pdu/AcknowledgeInd.java create mode 100644 mms-common/java/com/android/common/mms/pdu/Base64.java create mode 100644 mms-common/java/com/android/common/mms/pdu/DeliveryInd.java create mode 100644 mms-common/java/com/android/common/mms/pdu/GenericPdu.java create mode 100644 mms-common/java/com/android/common/mms/pdu/MultimediaMessagePdu.java create mode 100644 mms-common/java/com/android/common/mms/pdu/NotificationInd.java create mode 100644 mms-common/java/com/android/common/mms/pdu/NotifyRespInd.java create mode 100644 mms-common/java/com/android/common/mms/pdu/PduBody.java create mode 100644 mms-common/java/com/android/common/mms/pdu/PduComposer.java create mode 100644 mms-common/java/com/android/common/mms/pdu/PduContentTypes.java create mode 100644 mms-common/java/com/android/common/mms/pdu/PduParser.java create mode 100644 mms-common/java/com/android/common/mms/pdu/PduPart.java create mode 100644 mms-common/java/com/android/common/mms/pdu/PduPersister.java create mode 100644 mms-common/java/com/android/common/mms/pdu/QuotedPrintable.java create mode 100644 mms-common/java/com/android/common/mms/pdu/ReadOrigInd.java create mode 100644 mms-common/java/com/android/common/mms/pdu/ReadRecInd.java create mode 100644 mms-common/java/com/android/common/mms/pdu/RetrieveConf.java create mode 100644 mms-common/java/com/android/common/mms/pdu/SendConf.java create mode 100644 mms-common/java/com/android/common/mms/pdu/SendReq.java create mode 100644 mms-common/java/com/android/common/mms/telephony/TelephonyProvider.java create mode 100644 mms-common/java/com/android/common/mms/util/AbstractCache.java create mode 100644 mms-common/java/com/android/common/mms/util/PduCache.java create mode 100644 mms-common/java/com/android/common/mms/util/PduCacheEntry.java diff --git a/mms-common/Android.mk b/mms-common/Android.mk new file mode 100644 index 000000000..de994c0cd --- /dev/null +++ b/mms-common/Android.mk @@ -0,0 +1,29 @@ +# 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. + +LOCAL_PATH := $(call my-dir) + +# Note: the source code is in java/, not src/, because this code is also part of +# the framework library, and build/core/pathmap.mk expects a java/ subdirectory. + +include $(CLEAR_VARS) +LOCAL_MODULE := mms-common +LOCAL_SRC_FILES := $(call all-java-files-under, java) +include $(BUILD_STATIC_JAVA_LIBRARY) + +# Include this library in the build server's output directory +$(call dist-for-goals, droid, $(LOCAL_BUILT_MODULE):mms-common.jar) + +# Build the test package +include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/mms-common/java/com/android/common/CharacterSets.java b/mms-common/java/com/android/common/CharacterSets.java new file mode 100644 index 000000000..f19b07835 --- /dev/null +++ b/mms-common/java/com/android/common/CharacterSets.java @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2007 Esmertec AG. + * 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. + */ + +package com.android.mmscommon; + +import java.io.UnsupportedEncodingException; +import java.util.HashMap; + +public class CharacterSets { + /** + * IANA assigned MIB enum numbers. + * + * From wap-230-wsp-20010705-a.pdf + * Any-charset = + * Equivalent to the special RFC2616 charset value "*" + */ + public static final int ANY_CHARSET = 0x00; + public static final int US_ASCII = 0x03; + public static final int ISO_8859_1 = 0x04; + public static final int ISO_8859_2 = 0x05; + public static final int ISO_8859_3 = 0x06; + public static final int ISO_8859_4 = 0x07; + public static final int ISO_8859_5 = 0x08; + public static final int ISO_8859_6 = 0x09; + public static final int ISO_8859_7 = 0x0A; + public static final int ISO_8859_8 = 0x0B; + public static final int ISO_8859_9 = 0x0C; + public static final int SHIFT_JIS = 0x11; + public static final int UTF_8 = 0x6A; + public static final int BIG5 = 0x07EA; + public static final int UCS2 = 0x03E8; + public static final int UTF_16 = 0x03F7; + + /** + * If the encoding of given data is unsupported, use UTF_8 to decode it. + */ + public static final int DEFAULT_CHARSET = UTF_8; + + /** + * Array of MIB enum numbers. + */ + private static final int[] MIBENUM_NUMBERS = { + ANY_CHARSET, + US_ASCII, + ISO_8859_1, + ISO_8859_2, + ISO_8859_3, + ISO_8859_4, + ISO_8859_5, + ISO_8859_6, + ISO_8859_7, + ISO_8859_8, + ISO_8859_9, + SHIFT_JIS, + UTF_8, + BIG5, + UCS2, + UTF_16, + }; + + /** + * The Well-known-charset Mime name. + */ + public static final String MIMENAME_ANY_CHARSET = "*"; + public static final String MIMENAME_US_ASCII = "us-ascii"; + public static final String MIMENAME_ISO_8859_1 = "iso-8859-1"; + public static final String MIMENAME_ISO_8859_2 = "iso-8859-2"; + public static final String MIMENAME_ISO_8859_3 = "iso-8859-3"; + public static final String MIMENAME_ISO_8859_4 = "iso-8859-4"; + public static final String MIMENAME_ISO_8859_5 = "iso-8859-5"; + public static final String MIMENAME_ISO_8859_6 = "iso-8859-6"; + public static final String MIMENAME_ISO_8859_7 = "iso-8859-7"; + public static final String MIMENAME_ISO_8859_8 = "iso-8859-8"; + public static final String MIMENAME_ISO_8859_9 = "iso-8859-9"; + public static final String MIMENAME_SHIFT_JIS = "shift_JIS"; + public static final String MIMENAME_UTF_8 = "utf-8"; + public static final String MIMENAME_BIG5 = "big5"; + public static final String MIMENAME_UCS2 = "iso-10646-ucs-2"; + public static final String MIMENAME_UTF_16 = "utf-16"; + + public static final String DEFAULT_CHARSET_NAME = MIMENAME_UTF_8; + + /** + * Array of the names of character sets. + */ + private static final String[] MIME_NAMES = { + MIMENAME_ANY_CHARSET, + MIMENAME_US_ASCII, + MIMENAME_ISO_8859_1, + MIMENAME_ISO_8859_2, + MIMENAME_ISO_8859_3, + MIMENAME_ISO_8859_4, + MIMENAME_ISO_8859_5, + MIMENAME_ISO_8859_6, + MIMENAME_ISO_8859_7, + MIMENAME_ISO_8859_8, + MIMENAME_ISO_8859_9, + MIMENAME_SHIFT_JIS, + MIMENAME_UTF_8, + MIMENAME_BIG5, + MIMENAME_UCS2, + MIMENAME_UTF_16, + }; + + private static final HashMap MIBENUM_TO_NAME_MAP; + private static final HashMap NAME_TO_MIBENUM_MAP; + + static { + // Create the HashMaps. + MIBENUM_TO_NAME_MAP = new HashMap(); + NAME_TO_MIBENUM_MAP = new HashMap(); + assert(MIBENUM_NUMBERS.length == MIME_NAMES.length); + int count = MIBENUM_NUMBERS.length - 1; + for(int i = 0; i <= count; i++) { + MIBENUM_TO_NAME_MAP.put(MIBENUM_NUMBERS[i], MIME_NAMES[i]); + NAME_TO_MIBENUM_MAP.put(MIME_NAMES[i], MIBENUM_NUMBERS[i]); + } + } + + private CharacterSets() {} // Non-instantiatable + + /** + * Map an MIBEnum number to the name of the charset which this number + * is assigned to by IANA. + * + * @param mibEnumValue An IANA assigned MIBEnum number. + * @return The name string of the charset. + * @throws UnsupportedEncodingException + */ + public static String getMimeName(int mibEnumValue) + throws UnsupportedEncodingException { + String name = MIBENUM_TO_NAME_MAP.get(mibEnumValue); + if (name == null) { + throw new UnsupportedEncodingException(); + } + return name; + } + + /** + * Map a well-known charset name to its assigned MIBEnum number. + * + * @param mimeName The charset name. + * @return The MIBEnum number assigned by IANA for this charset. + * @throws UnsupportedEncodingException + */ + public static int getMibEnumValue(String mimeName) + throws UnsupportedEncodingException { + if(null == mimeName) { + return -1; + } + + Integer mibEnumValue = NAME_TO_MIBENUM_MAP.get(mimeName); + if (mibEnumValue == null) { + throw new UnsupportedEncodingException(); + } + return mibEnumValue; + } +} diff --git a/mms-common/java/com/android/common/ContentType.java b/mms-common/java/com/android/common/ContentType.java new file mode 100644 index 000000000..ca449fe2e --- /dev/null +++ b/mms-common/java/com/android/common/ContentType.java @@ -0,0 +1,223 @@ +/* + * Copyright (C) 2007-2008 Esmertec AG. + * Copyright (C) 2007-2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.mmscommon; + +import java.util.ArrayList; + +public class ContentType { + public static final String MMS_MESSAGE = "application/vnd.wap.mms-message"; + // The phony content type for generic PDUs (e.g. ReadOrig.ind, + // Notification.ind, Delivery.ind). + public static final String MMS_GENERIC = "application/vnd.wap.mms-generic"; + public static final String MULTIPART_MIXED = "application/vnd.wap.multipart.mixed"; + public static final String MULTIPART_RELATED = "application/vnd.wap.multipart.related"; + public static final String MULTIPART_ALTERNATIVE = "application/vnd.wap.multipart.alternative"; + + public static final String TEXT_PLAIN = "text/plain"; + public static final String TEXT_HTML = "text/html"; + public static final String TEXT_VCALENDAR = "text/x-vCalendar"; + public static final String TEXT_VCARD = "text/x-vCard"; + + public static final String IMAGE_UNSPECIFIED = "image/*"; + public static final String IMAGE_JPEG = "image/jpeg"; + public static final String IMAGE_JPG = "image/jpg"; + public static final String IMAGE_GIF = "image/gif"; + public static final String IMAGE_WBMP = "image/vnd.wap.wbmp"; + public static final String IMAGE_PNG = "image/png"; + + public static final String AUDIO_UNSPECIFIED = "audio/*"; + public static final String AUDIO_AAC = "audio/aac"; + public static final String AUDIO_AMR = "audio/amr"; + public static final String AUDIO_IMELODY = "audio/imelody"; + public static final String AUDIO_MID = "audio/mid"; + public static final String AUDIO_MIDI = "audio/midi"; + public static final String AUDIO_MP3 = "audio/mp3"; + public static final String AUDIO_MPEG3 = "audio/mpeg3"; + public static final String AUDIO_MPEG = "audio/mpeg"; + public static final String AUDIO_MPG = "audio/mpg"; + public static final String AUDIO_MP4 = "audio/mp4"; + public static final String AUDIO_X_MID = "audio/x-mid"; + public static final String AUDIO_X_MIDI = "audio/x-midi"; + public static final String AUDIO_X_MP3 = "audio/x-mp3"; + public static final String AUDIO_X_MPEG3 = "audio/x-mpeg3"; + public static final String AUDIO_X_MPEG = "audio/x-mpeg"; + public static final String AUDIO_X_MPG = "audio/x-mpg"; + public static final String AUDIO_3GPP = "audio/3gpp"; + public static final String AUDIO_OGG = "application/ogg"; + + public static final String VIDEO_UNSPECIFIED = "video/*"; + public static final String VIDEO_3GPP = "video/3gpp"; + public static final String VIDEO_3G2 = "video/3gpp2"; + public static final String VIDEO_H263 = "video/h263"; + public static final String VIDEO_MP4 = "video/mp4"; + + public static final String APP_SMIL = "application/smil"; + public static final String APP_WAP_XHTML = "application/vnd.wap.xhtml+xml"; + public static final String APP_XHTML = "application/xhtml+xml"; + + public static final String APP_DRM_CONTENT = "application/vnd.oma.drm.content"; + public static final String APP_DRM_MESSAGE = "application/vnd.oma.drm.message"; + + private static final ArrayList sSupportedContentTypes = new ArrayList(); + private static final ArrayList sSupportedImageTypes = new ArrayList(); + private static final ArrayList sSupportedAudioTypes = new ArrayList(); + private static final ArrayList sSupportedVideoTypes = new ArrayList(); + + static { + sSupportedContentTypes.add(TEXT_PLAIN); + sSupportedContentTypes.add(TEXT_HTML); + sSupportedContentTypes.add(TEXT_VCALENDAR); + sSupportedContentTypes.add(TEXT_VCARD); + + sSupportedContentTypes.add(IMAGE_JPEG); + sSupportedContentTypes.add(IMAGE_GIF); + sSupportedContentTypes.add(IMAGE_WBMP); + sSupportedContentTypes.add(IMAGE_PNG); + sSupportedContentTypes.add(IMAGE_JPG); + //supportedContentTypes.add(IMAGE_SVG); not yet supported. + + sSupportedContentTypes.add(AUDIO_AAC); + sSupportedContentTypes.add(AUDIO_AMR); + sSupportedContentTypes.add(AUDIO_IMELODY); + sSupportedContentTypes.add(AUDIO_MID); + sSupportedContentTypes.add(AUDIO_MIDI); + sSupportedContentTypes.add(AUDIO_MP3); + sSupportedContentTypes.add(AUDIO_MPEG3); + sSupportedContentTypes.add(AUDIO_MPEG); + sSupportedContentTypes.add(AUDIO_MPG); + sSupportedContentTypes.add(AUDIO_X_MID); + sSupportedContentTypes.add(AUDIO_X_MIDI); + sSupportedContentTypes.add(AUDIO_X_MP3); + sSupportedContentTypes.add(AUDIO_X_MPEG3); + sSupportedContentTypes.add(AUDIO_X_MPEG); + sSupportedContentTypes.add(AUDIO_X_MPG); + sSupportedContentTypes.add(AUDIO_3GPP); + sSupportedContentTypes.add(AUDIO_OGG); + + sSupportedContentTypes.add(VIDEO_3GPP); + sSupportedContentTypes.add(VIDEO_3G2); + sSupportedContentTypes.add(VIDEO_H263); + sSupportedContentTypes.add(VIDEO_MP4); + + sSupportedContentTypes.add(APP_SMIL); + sSupportedContentTypes.add(APP_WAP_XHTML); + sSupportedContentTypes.add(APP_XHTML); + + sSupportedContentTypes.add(APP_DRM_CONTENT); + sSupportedContentTypes.add(APP_DRM_MESSAGE); + + // add supported image types + sSupportedImageTypes.add(IMAGE_JPEG); + sSupportedImageTypes.add(IMAGE_GIF); + sSupportedImageTypes.add(IMAGE_WBMP); + sSupportedImageTypes.add(IMAGE_PNG); + sSupportedImageTypes.add(IMAGE_JPG); + + // add supported audio types + sSupportedAudioTypes.add(AUDIO_AAC); + sSupportedAudioTypes.add(AUDIO_AMR); + sSupportedAudioTypes.add(AUDIO_IMELODY); + sSupportedAudioTypes.add(AUDIO_MID); + sSupportedAudioTypes.add(AUDIO_MIDI); + sSupportedAudioTypes.add(AUDIO_MP3); + sSupportedAudioTypes.add(AUDIO_MPEG3); + sSupportedAudioTypes.add(AUDIO_MPEG); + sSupportedAudioTypes.add(AUDIO_MPG); + sSupportedAudioTypes.add(AUDIO_MP4); + sSupportedAudioTypes.add(AUDIO_X_MID); + sSupportedAudioTypes.add(AUDIO_X_MIDI); + sSupportedAudioTypes.add(AUDIO_X_MP3); + sSupportedAudioTypes.add(AUDIO_X_MPEG3); + sSupportedAudioTypes.add(AUDIO_X_MPEG); + sSupportedAudioTypes.add(AUDIO_X_MPG); + sSupportedAudioTypes.add(AUDIO_3GPP); + sSupportedAudioTypes.add(AUDIO_OGG); + + // add supported video types + sSupportedVideoTypes.add(VIDEO_3GPP); + sSupportedVideoTypes.add(VIDEO_3G2); + sSupportedVideoTypes.add(VIDEO_H263); + sSupportedVideoTypes.add(VIDEO_MP4); + } + + // This class should never be instantiated. + private ContentType() { + } + + public static boolean isSupportedType(String contentType) { + return (null != contentType) && sSupportedContentTypes.contains(contentType); + } + + public static boolean isSupportedImageType(String contentType) { + return isImageType(contentType) && isSupportedType(contentType); + } + + public static boolean isSupportedAudioType(String contentType) { + return isAudioType(contentType) && isSupportedType(contentType); + } + + public static boolean isSupportedVideoType(String contentType) { + return isVideoType(contentType) && isSupportedType(contentType); + } + + public static boolean isTextType(String contentType) { + return (null != contentType) && contentType.startsWith("text/"); + } + + public static boolean isImageType(String contentType) { + return (null != contentType) && contentType.startsWith("image/"); + } + + public static boolean isAudioType(String contentType) { + return (null != contentType) && contentType.startsWith("audio/"); + } + + public static boolean isVideoType(String contentType) { + return (null != contentType) && contentType.startsWith("video/"); + } + + public static boolean isDrmType(String contentType) { + return (null != contentType) + && (contentType.equals(APP_DRM_CONTENT) + || contentType.equals(APP_DRM_MESSAGE)); + } + + public static boolean isUnspecified(String contentType) { + return (null != contentType) && contentType.endsWith("*"); + } + + @SuppressWarnings("unchecked") + public static ArrayList getImageTypes() { + return (ArrayList) sSupportedImageTypes.clone(); + } + + @SuppressWarnings("unchecked") + public static ArrayList getAudioTypes() { + return (ArrayList) sSupportedAudioTypes.clone(); + } + + @SuppressWarnings("unchecked") + public static ArrayList getVideoTypes() { + return (ArrayList) sSupportedVideoTypes.clone(); + } + + @SuppressWarnings("unchecked") + public static ArrayList getSupportedTypes() { + return (ArrayList) sSupportedContentTypes.clone(); + } +} diff --git a/mms-common/java/com/android/common/EncodedStringValue.java b/mms-common/java/com/android/common/EncodedStringValue.java new file mode 100644 index 000000000..0a4424e28 --- /dev/null +++ b/mms-common/java/com/android/common/EncodedStringValue.java @@ -0,0 +1,284 @@ +/* + * Copyright (C) 2007-2008 Esmertec AG. + * Copyright (C) 2007-2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.mmscommon; + +import android.util.Config; +import android.util.Log; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.util.ArrayList; + +/** + * Encoded-string-value = Text-string | Value-length Char-set Text-string + */ +public class EncodedStringValue implements Cloneable { + private static final String TAG = "EncodedStringValue"; + private static final boolean DEBUG = false; + private static final boolean LOCAL_LOGV = DEBUG ? Config.LOGD : Config.LOGV; + + /** + * The Char-set value. + */ + private int mCharacterSet; + + /** + * The Text-string value. + */ + private byte[] mData; + + /** + * Constructor. + * + * @param charset the Char-set value + * @param data the Text-string value + * @throws NullPointerException if Text-string value is null. + */ + public EncodedStringValue(int charset, byte[] data) { + // TODO: CharSet needs to be validated against MIBEnum. + if(null == data) { + throw new NullPointerException("EncodedStringValue: Text-string is null."); + } + + mCharacterSet = charset; + mData = new byte[data.length]; + System.arraycopy(data, 0, mData, 0, data.length); + } + + /** + * Constructor. + * + * @param data the Text-string value + * @throws NullPointerException if Text-string value is null. + */ + public EncodedStringValue(byte[] data) { + this(CharacterSets.DEFAULT_CHARSET, data); + } + + public EncodedStringValue(String data) { + try { + mData = data.getBytes(CharacterSets.DEFAULT_CHARSET_NAME); + mCharacterSet = CharacterSets.DEFAULT_CHARSET; + } catch (UnsupportedEncodingException e) { + Log.e(TAG, "Default encoding must be supported.", e); + } + } + + /** + * Get Char-set value. + * + * @return the value + */ + public int getCharacterSet() { + return mCharacterSet; + } + + /** + * Set Char-set value. + * + * @param charset the Char-set value + */ + public void setCharacterSet(int charset) { + // TODO: CharSet needs to be validated against MIBEnum. + mCharacterSet = charset; + } + + /** + * Get Text-string value. + * + * @return the value + */ + public byte[] getTextString() { + byte[] byteArray = new byte[mData.length]; + + System.arraycopy(mData, 0, byteArray, 0, mData.length); + return byteArray; + } + + /** + * Set Text-string value. + * + * @param textString the Text-string value + * @throws NullPointerException if Text-string value is null. + */ + public void setTextString(byte[] textString) { + if(null == textString) { + throw new NullPointerException("EncodedStringValue: Text-string is null."); + } + + mData = new byte[textString.length]; + System.arraycopy(textString, 0, mData, 0, textString.length); + } + + /** + * Convert this object to a {@link java.lang.String}. If the encoding of + * the EncodedStringValue is null or unsupported, it will be + * treated as iso-8859-1 encoding. + * + * @return The decoded String. + */ + public String getString() { + if (CharacterSets.ANY_CHARSET == mCharacterSet) { + return new String(mData); // system default encoding. + } else { + try { + String name = CharacterSets.getMimeName(mCharacterSet); + return new String(mData, name); + } catch (UnsupportedEncodingException e) { + if (LOCAL_LOGV) { + Log.v(TAG, e.getMessage(), e); + } + try { + return new String(mData, CharacterSets.MIMENAME_ISO_8859_1); + } catch (UnsupportedEncodingException _) { + return new String(mData); // system default encoding. + } + } + } + } + + /** + * Append to Text-string. + * + * @param textString the textString to append + * @throws NullPointerException if the text String is null + * or an IOException occured. + */ + public void appendTextString(byte[] textString) { + if(null == textString) { + throw new NullPointerException("Text-string is null."); + } + + if(null == mData) { + mData = new byte[textString.length]; + System.arraycopy(textString, 0, mData, 0, textString.length); + } else { + ByteArrayOutputStream newTextString = new ByteArrayOutputStream(); + try { + newTextString.write(mData); + newTextString.write(textString); + } catch (IOException e) { + e.printStackTrace(); + throw new NullPointerException( + "appendTextString: failed when write a new Text-string"); + } + + mData = newTextString.toByteArray(); + } + } + + /* + * (non-Javadoc) + * @see java.lang.Object#clone() + */ + @Override + public Object clone() throws CloneNotSupportedException { + super.clone(); + int len = mData.length; + byte[] dstBytes = new byte[len]; + System.arraycopy(mData, 0, dstBytes, 0, len); + + try { + return new EncodedStringValue(mCharacterSet, dstBytes); + } catch (Exception e) { + Log.e(TAG, "failed to clone an EncodedStringValue: " + this); + e.printStackTrace(); + throw new CloneNotSupportedException(e.getMessage()); + } + } + + /** + * Split this encoded string around matches of the given pattern. + * + * @param pattern the delimiting pattern + * @return the array of encoded strings computed by splitting this encoded + * string around matches of the given pattern + */ + public EncodedStringValue[] split(String pattern) { + String[] temp = getString().split(pattern); + EncodedStringValue[] ret = new EncodedStringValue[temp.length]; + for (int i = 0; i < ret.length; ++i) { + try { + ret[i] = new EncodedStringValue(mCharacterSet, + temp[i].getBytes()); + } catch (NullPointerException _) { + // Can't arrive here + return null; + } + } + return ret; + } + + /** + * Extract an EncodedStringValue[] from a given String. + */ + public static EncodedStringValue[] extract(String src) { + String[] values = src.split(";"); + + ArrayList list = new ArrayList(); + for (int i = 0; i < values.length; i++) { + if (values[i].length() > 0) { + list.add(new EncodedStringValue(values[i])); + } + } + + int len = list.size(); + if (len > 0) { + return list.toArray(new EncodedStringValue[len]); + } else { + return null; + } + } + + /** + * Concatenate an EncodedStringValue[] into a single String. + */ + public static String concat(EncodedStringValue[] addr) { + StringBuilder sb = new StringBuilder(); + int maxIndex = addr.length - 1; + for (int i = 0; i <= maxIndex; i++) { + sb.append(addr[i].getString()); + if (i < maxIndex) { + sb.append(";"); + } + } + + return sb.toString(); + } + + public static EncodedStringValue copy(EncodedStringValue value) { + if (value == null) { + return null; + } + + return new EncodedStringValue(value.mCharacterSet, value.mData); + } + + public static EncodedStringValue[] encodeStrings(String[] array) { + int count = array.length; + if (count > 0) { + EncodedStringValue[] encodedArray = new EncodedStringValue[count]; + for (int i = 0; i < count; i++) { + encodedArray[i] = new EncodedStringValue(array[i]); + } + return encodedArray; + } + return null; + } +} diff --git a/mms-common/java/com/android/common/InvalidHeaderValueException.java b/mms-common/java/com/android/common/InvalidHeaderValueException.java new file mode 100644 index 000000000..34d5871ec --- /dev/null +++ b/mms-common/java/com/android/common/InvalidHeaderValueException.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2007 Esmertec AG. + * 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. + */ + +package com.android.mmscommon; + +/** + * Thrown when an invalid header value was set. + */ +public class InvalidHeaderValueException extends MmsException { + private static final long serialVersionUID = -2053384496042052262L; + + /** + * Constructs an InvalidHeaderValueException with no detailed message. + */ + public InvalidHeaderValueException() { + super(); + } + + /** + * Constructs an InvalidHeaderValueException with the specified detailed message. + * + * @param message the detailed message. + */ + public InvalidHeaderValueException(String message) { + super(message); + } +} diff --git a/mms-common/java/com/android/common/MmsException.java b/mms-common/java/com/android/common/MmsException.java new file mode 100644 index 000000000..296a2c346 --- /dev/null +++ b/mms-common/java/com/android/common/MmsException.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2007 Esmertec AG. + * 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. + */ + +package com.android.mmscommon; + +/** + * A generic exception that is thrown by the Mms client. + */ +public class MmsException extends Exception { + private static final long serialVersionUID = -7323249827281485390L; + + /** + * Creates a new MmsException. + */ + public MmsException() { + super(); + } + + /** + * Creates a new MmsException with the specified detail message. + * + * @param message the detail message. + */ + public MmsException(String message) { + super(message); + } + + /** + * Creates a new MmsException with the specified cause. + * + * @param cause the cause. + */ + public MmsException(Throwable cause) { + super(cause); + } + + /** + * Creates a new MmsException with the specified detail message and cause. + * + * @param message the detail message. + * @param cause the cause. + */ + public MmsException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/mms-common/java/com/android/common/PduHeaders.java b/mms-common/java/com/android/common/PduHeaders.java new file mode 100644 index 000000000..d8f12111b --- /dev/null +++ b/mms-common/java/com/android/common/PduHeaders.java @@ -0,0 +1,719 @@ +/* + * Copyright (C) 2007 Esmertec AG. + * 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. + */ + +package com.android.mmscommon; + +import java.util.ArrayList; +import java.util.HashMap; + +public class PduHeaders { + /** + * All pdu header fields. + */ + public static final int BCC = 0x81; + public static final int CC = 0x82; + public static final int CONTENT_LOCATION = 0x83; + public static final int CONTENT_TYPE = 0x84; + public static final int DATE = 0x85; + public static final int DELIVERY_REPORT = 0x86; + public static final int DELIVERY_TIME = 0x87; + public static final int EXPIRY = 0x88; + public static final int FROM = 0x89; + public static final int MESSAGE_CLASS = 0x8A; + public static final int MESSAGE_ID = 0x8B; + public static final int MESSAGE_TYPE = 0x8C; + public static final int MMS_VERSION = 0x8D; + public static final int MESSAGE_SIZE = 0x8E; + public static final int PRIORITY = 0x8F; + + public static final int READ_REPLY = 0x90; + public static final int READ_REPORT = 0x90; + public static final int REPORT_ALLOWED = 0x91; + public static final int RESPONSE_STATUS = 0x92; + public static final int RESPONSE_TEXT = 0x93; + public static final int SENDER_VISIBILITY = 0x94; + public static final int STATUS = 0x95; + public static final int SUBJECT = 0x96; + public static final int TO = 0x97; + public static final int TRANSACTION_ID = 0x98; + public static final int RETRIEVE_STATUS = 0x99; + public static final int RETRIEVE_TEXT = 0x9A; + public static final int READ_STATUS = 0x9B; + public static final int REPLY_CHARGING = 0x9C; + public static final int REPLY_CHARGING_DEADLINE = 0x9D; + public static final int REPLY_CHARGING_ID = 0x9E; + public static final int REPLY_CHARGING_SIZE = 0x9F; + + public static final int PREVIOUSLY_SENT_BY = 0xA0; + public static final int PREVIOUSLY_SENT_DATE = 0xA1; + public static final int STORE = 0xA2; + public static final int MM_STATE = 0xA3; + public static final int MM_FLAGS = 0xA4; + public static final int STORE_STATUS = 0xA5; + public static final int STORE_STATUS_TEXT = 0xA6; + public static final int STORED = 0xA7; + public static final int ATTRIBUTES = 0xA8; + public static final int TOTALS = 0xA9; + public static final int MBOX_TOTALS = 0xAA; + public static final int QUOTAS = 0xAB; + public static final int MBOX_QUOTAS = 0xAC; + public static final int MESSAGE_COUNT = 0xAD; + public static final int CONTENT = 0xAE; + public static final int START = 0xAF; + + public static final int ADDITIONAL_HEADERS = 0xB0; + public static final int DISTRIBUTION_INDICATOR = 0xB1; + public static final int ELEMENT_DESCRIPTOR = 0xB2; + public static final int LIMIT = 0xB3; + public static final int RECOMMENDED_RETRIEVAL_MODE = 0xB4; + public static final int RECOMMENDED_RETRIEVAL_MODE_TEXT = 0xB5; + public static final int STATUS_TEXT = 0xB6; + public static final int APPLIC_ID = 0xB7; + public static final int REPLY_APPLIC_ID = 0xB8; + public static final int AUX_APPLIC_ID = 0xB9; + public static final int CONTENT_CLASS = 0xBA; + public static final int DRM_CONTENT = 0xBB; + public static final int ADAPTATION_ALLOWED = 0xBC; + public static final int REPLACE_ID = 0xBD; + public static final int CANCEL_ID = 0xBE; + public static final int CANCEL_STATUS = 0xBF; + + /** + * X-Mms-Message-Type field types. + */ + public static final int MESSAGE_TYPE_SEND_REQ = 0x80; + public static final int MESSAGE_TYPE_SEND_CONF = 0x81; + public static final int MESSAGE_TYPE_NOTIFICATION_IND = 0x82; + public static final int MESSAGE_TYPE_NOTIFYRESP_IND = 0x83; + public static final int MESSAGE_TYPE_RETRIEVE_CONF = 0x84; + public static final int MESSAGE_TYPE_ACKNOWLEDGE_IND = 0x85; + public static final int MESSAGE_TYPE_DELIVERY_IND = 0x86; + public static final int MESSAGE_TYPE_READ_REC_IND = 0x87; + public static final int MESSAGE_TYPE_READ_ORIG_IND = 0x88; + public static final int MESSAGE_TYPE_FORWARD_REQ = 0x89; + public static final int MESSAGE_TYPE_FORWARD_CONF = 0x8A; + public static final int MESSAGE_TYPE_MBOX_STORE_REQ = 0x8B; + public static final int MESSAGE_TYPE_MBOX_STORE_CONF = 0x8C; + public static final int MESSAGE_TYPE_MBOX_VIEW_REQ = 0x8D; + public static final int MESSAGE_TYPE_MBOX_VIEW_CONF = 0x8E; + public static final int MESSAGE_TYPE_MBOX_UPLOAD_REQ = 0x8F; + public static final int MESSAGE_TYPE_MBOX_UPLOAD_CONF = 0x90; + public static final int MESSAGE_TYPE_MBOX_DELETE_REQ = 0x91; + public static final int MESSAGE_TYPE_MBOX_DELETE_CONF = 0x92; + public static final int MESSAGE_TYPE_MBOX_DESCR = 0x93; + public static final int MESSAGE_TYPE_DELETE_REQ = 0x94; + public static final int MESSAGE_TYPE_DELETE_CONF = 0x95; + public static final int MESSAGE_TYPE_CANCEL_REQ = 0x96; + public static final int MESSAGE_TYPE_CANCEL_CONF = 0x97; + + /** + * X-Mms-Delivery-Report | + * X-Mms-Read-Report | + * X-Mms-Report-Allowed | + * X-Mms-Sender-Visibility | + * X-Mms-Store | + * X-Mms-Stored | + * X-Mms-Totals | + * X-Mms-Quotas | + * X-Mms-Distribution-Indicator | + * X-Mms-DRM-Content | + * X-Mms-Adaptation-Allowed | + * field types. + */ + public static final int VALUE_YES = 0x80; + public static final int VALUE_NO = 0x81; + + /** + * Delivery-Time | + * Expiry and Reply-Charging-Deadline | + * field type components. + */ + public static final int VALUE_ABSOLUTE_TOKEN = 0x80; + public static final int VALUE_RELATIVE_TOKEN = 0x81; + + /** + * X-Mms-MMS-Version field types. + */ + public static final int MMS_VERSION_1_3 = ((1 << 4) | 3); + public static final int MMS_VERSION_1_2 = ((1 << 4) | 2); + public static final int MMS_VERSION_1_1 = ((1 << 4) | 1); + public static final int MMS_VERSION_1_0 = ((1 << 4) | 0); + + // Current version is 1.2. + public static final int CURRENT_MMS_VERSION = MMS_VERSION_1_2; + + /** + * From field type components. + */ + public static final int FROM_ADDRESS_PRESENT_TOKEN = 0x80; + public static final int FROM_INSERT_ADDRESS_TOKEN = 0x81; + + public static final String FROM_ADDRESS_PRESENT_TOKEN_STR = "address-present-token"; + public static final String FROM_INSERT_ADDRESS_TOKEN_STR = "insert-address-token"; + + /** + * X-Mms-Status Field. + */ + public static final int STATUS_EXPIRED = 0x80; + public static final int STATUS_RETRIEVED = 0x81; + public static final int STATUS_REJECTED = 0x82; + public static final int STATUS_DEFERRED = 0x83; + public static final int STATUS_UNRECOGNIZED = 0x84; + public static final int STATUS_INDETERMINATE = 0x85; + public static final int STATUS_FORWARDED = 0x86; + public static final int STATUS_UNREACHABLE = 0x87; + + /** + * MM-Flags field type components. + */ + public static final int MM_FLAGS_ADD_TOKEN = 0x80; + public static final int MM_FLAGS_REMOVE_TOKEN = 0x81; + public static final int MM_FLAGS_FILTER_TOKEN = 0x82; + + /** + * X-Mms-Message-Class field types. + */ + public static final int MESSAGE_CLASS_PERSONAL = 0x80; + public static final int MESSAGE_CLASS_ADVERTISEMENT = 0x81; + public static final int MESSAGE_CLASS_INFORMATIONAL = 0x82; + public static final int MESSAGE_CLASS_AUTO = 0x83; + + public static final String MESSAGE_CLASS_PERSONAL_STR = "personal"; + public static final String MESSAGE_CLASS_ADVERTISEMENT_STR = "advertisement"; + public static final String MESSAGE_CLASS_INFORMATIONAL_STR = "informational"; + public static final String MESSAGE_CLASS_AUTO_STR = "auto"; + + /** + * X-Mms-Priority field types. + */ + public static final int PRIORITY_LOW = 0x80; + public static final int PRIORITY_NORMAL = 0x81; + public static final int PRIORITY_HIGH = 0x82; + + /** + * X-Mms-Response-Status field types. + */ + public static final int RESPONSE_STATUS_OK = 0x80; + public static final int RESPONSE_STATUS_ERROR_UNSPECIFIED = 0x81; + public static final int RESPONSE_STATUS_ERROR_SERVICE_DENIED = 0x82; + + public static final int RESPONSE_STATUS_ERROR_MESSAGE_FORMAT_CORRUPT = 0x83; + public static final int RESPONSE_STATUS_ERROR_SENDING_ADDRESS_UNRESOLVED = 0x84; + + public static final int RESPONSE_STATUS_ERROR_MESSAGE_NOT_FOUND = 0x85; + public static final int RESPONSE_STATUS_ERROR_NETWORK_PROBLEM = 0x86; + public static final int RESPONSE_STATUS_ERROR_CONTENT_NOT_ACCEPTED = 0x87; + public static final int RESPONSE_STATUS_ERROR_UNSUPPORTED_MESSAGE = 0x88; + public static final int RESPONSE_STATUS_ERROR_TRANSIENT_FAILURE = 0xC0; + + public static final int RESPONSE_STATUS_ERROR_TRANSIENT_SENDNG_ADDRESS_UNRESOLVED = 0xC1; + public static final int RESPONSE_STATUS_ERROR_TRANSIENT_MESSAGE_NOT_FOUND = 0xC2; + public static final int RESPONSE_STATUS_ERROR_TRANSIENT_NETWORK_PROBLEM = 0xC3; + public static final int RESPONSE_STATUS_ERROR_TRANSIENT_PARTIAL_SUCCESS = 0xC4; + + public static final int RESPONSE_STATUS_ERROR_PERMANENT_FAILURE = 0xE0; + public static final int RESPONSE_STATUS_ERROR_PERMANENT_SERVICE_DENIED = 0xE1; + public static final int RESPONSE_STATUS_ERROR_PERMANENT_MESSAGE_FORMAT_CORRUPT = 0xE2; + public static final int RESPONSE_STATUS_ERROR_PERMANENT_SENDING_ADDRESS_UNRESOLVED = 0xE3; + public static final int RESPONSE_STATUS_ERROR_PERMANENT_MESSAGE_NOT_FOUND = 0xE4; + public static final int RESPONSE_STATUS_ERROR_PERMANENT_CONTENT_NOT_ACCEPTED = 0xE5; + public static final int RESPONSE_STATUS_ERROR_PERMANENT_REPLY_CHARGING_LIMITATIONS_NOT_MET = 0xE6; + public static final int RESPONSE_STATUS_ERROR_PERMANENT_REPLY_CHARGING_REQUEST_NOT_ACCEPTED = 0xE6; + public static final int RESPONSE_STATUS_ERROR_PERMANENT_REPLY_CHARGING_FORWARDING_DENIED = 0xE8; + public static final int RESPONSE_STATUS_ERROR_PERMANENT_REPLY_CHARGING_NOT_SUPPORTED = 0xE9; + public static final int RESPONSE_STATUS_ERROR_PERMANENT_ADDRESS_HIDING_NOT_SUPPORTED = 0xEA; + public static final int RESPONSE_STATUS_ERROR_PERMANENT_LACK_OF_PREPAID = 0xEB; + public static final int RESPONSE_STATUS_ERROR_PERMANENT_END = 0xFF; + + /** + * X-Mms-Retrieve-Status field types. + */ + public static final int RETRIEVE_STATUS_OK = 0x80; + public static final int RETRIEVE_STATUS_ERROR_TRANSIENT_FAILURE = 0xC0; + public static final int RETRIEVE_STATUS_ERROR_TRANSIENT_MESSAGE_NOT_FOUND = 0xC1; + public static final int RETRIEVE_STATUS_ERROR_TRANSIENT_NETWORK_PROBLEM = 0xC2; + public static final int RETRIEVE_STATUS_ERROR_PERMANENT_FAILURE = 0xE0; + public static final int RETRIEVE_STATUS_ERROR_PERMANENT_SERVICE_DENIED = 0xE1; + public static final int RETRIEVE_STATUS_ERROR_PERMANENT_MESSAGE_NOT_FOUND = 0xE2; + public static final int RETRIEVE_STATUS_ERROR_PERMANENT_CONTENT_UNSUPPORTED = 0xE3; + public static final int RETRIEVE_STATUS_ERROR_END = 0xFF; + + /** + * X-Mms-Sender-Visibility field types. + */ + public static final int SENDER_VISIBILITY_HIDE = 0x80; + public static final int SENDER_VISIBILITY_SHOW = 0x81; + + /** + * X-Mms-Read-Status field types. + */ + public static final int READ_STATUS_READ = 0x80; + public static final int READ_STATUS__DELETED_WITHOUT_BEING_READ = 0x81; + + /** + * X-Mms-Cancel-Status field types. + */ + public static final int CANCEL_STATUS_REQUEST_SUCCESSFULLY_RECEIVED = 0x80; + public static final int CANCEL_STATUS_REQUEST_CORRUPTED = 0x81; + + /** + * X-Mms-Reply-Charging field types. + */ + public static final int REPLY_CHARGING_REQUESTED = 0x80; + public static final int REPLY_CHARGING_REQUESTED_TEXT_ONLY = 0x81; + public static final int REPLY_CHARGING_ACCEPTED = 0x82; + public static final int REPLY_CHARGING_ACCEPTED_TEXT_ONLY = 0x83; + + /** + * X-Mms-MM-State field types. + */ + public static final int MM_STATE_DRAFT = 0x80; + public static final int MM_STATE_SENT = 0x81; + public static final int MM_STATE_NEW = 0x82; + public static final int MM_STATE_RETRIEVED = 0x83; + public static final int MM_STATE_FORWARDED = 0x84; + + /** + * X-Mms-Recommended-Retrieval-Mode field types. + */ + public static final int RECOMMENDED_RETRIEVAL_MODE_MANUAL = 0x80; + + /** + * X-Mms-Content-Class field types. + */ + public static final int CONTENT_CLASS_TEXT = 0x80; + public static final int CONTENT_CLASS_IMAGE_BASIC = 0x81; + public static final int CONTENT_CLASS_IMAGE_RICH = 0x82; + public static final int CONTENT_CLASS_VIDEO_BASIC = 0x83; + public static final int CONTENT_CLASS_VIDEO_RICH = 0x84; + public static final int CONTENT_CLASS_MEGAPIXEL = 0x85; + public static final int CONTENT_CLASS_CONTENT_BASIC = 0x86; + public static final int CONTENT_CLASS_CONTENT_RICH = 0x87; + + /** + * X-Mms-Store-Status field types. + */ + public static final int STORE_STATUS_SUCCESS = 0x80; + public static final int STORE_STATUS_ERROR_TRANSIENT_FAILURE = 0xC0; + public static final int STORE_STATUS_ERROR_TRANSIENT_NETWORK_PROBLEM = 0xC1; + public static final int STORE_STATUS_ERROR_PERMANENT_FAILURE = 0xE0; + public static final int STORE_STATUS_ERROR_PERMANENT_SERVICE_DENIED = 0xE1; + public static final int STORE_STATUS_ERROR_PERMANENT_MESSAGE_FORMAT_CORRUPT = 0xE2; + public static final int STORE_STATUS_ERROR_PERMANENT_MESSAGE_NOT_FOUND = 0xE3; + public static final int STORE_STATUS_ERROR_PERMANENT_MMBOX_FULL = 0xE4; + public static final int STORE_STATUS_ERROR_END = 0xFF; + + /** + * The map contains the value of all headers. + */ + private HashMap mHeaderMap = null; + + /** + * Constructor of PduHeaders. + */ + public PduHeaders() { + mHeaderMap = new HashMap(); + } + + /** + * Get octet value by header field. + * + * @param field the field + * @return the octet value of the pdu header + * with specified header field. Return 0 if + * the value is not set. + */ + public int getOctet(int field) { + Integer octet = (Integer) mHeaderMap.get(field); + if (null == octet) { + return 0; + } + + return octet; + } + + /** + * Set octet value to pdu header by header field. + * + * @param value the value + * @param field the field + * @throws InvalidHeaderValueException if the value is invalid. + */ + public void setOctet(int value, int field) + throws InvalidHeaderValueException{ + /** + * Check whether this field can be set for specific + * header and check validity of the field. + */ + switch (field) { + case REPORT_ALLOWED: + case ADAPTATION_ALLOWED: + case DELIVERY_REPORT: + case DRM_CONTENT: + case DISTRIBUTION_INDICATOR: + case QUOTAS: + case READ_REPORT: + case STORE: + case STORED: + case TOTALS: + case SENDER_VISIBILITY: + if ((VALUE_YES != value) && (VALUE_NO != value)) { + // Invalid value. + throw new InvalidHeaderValueException("Invalid Octet value!"); + } + break; + case READ_STATUS: + if ((READ_STATUS_READ != value) && + (READ_STATUS__DELETED_WITHOUT_BEING_READ != value)) { + // Invalid value. + throw new InvalidHeaderValueException("Invalid Octet value!"); + } + break; + case CANCEL_STATUS: + if ((CANCEL_STATUS_REQUEST_SUCCESSFULLY_RECEIVED != value) && + (CANCEL_STATUS_REQUEST_CORRUPTED != value)) { + // Invalid value. + throw new InvalidHeaderValueException("Invalid Octet value!"); + } + break; + case PRIORITY: + if ((value < PRIORITY_LOW) || (value > PRIORITY_HIGH)) { + // Invalid value. + throw new InvalidHeaderValueException("Invalid Octet value!"); + } + break; + case STATUS: + if ((value < STATUS_EXPIRED) || (value > STATUS_UNREACHABLE)) { + // Invalid value. + throw new InvalidHeaderValueException("Invalid Octet value!"); + } + break; + case REPLY_CHARGING: + if ((value < REPLY_CHARGING_REQUESTED) + || (value > REPLY_CHARGING_ACCEPTED_TEXT_ONLY)) { + // Invalid value. + throw new InvalidHeaderValueException("Invalid Octet value!"); + } + break; + case MM_STATE: + if ((value < MM_STATE_DRAFT) || (value > MM_STATE_FORWARDED)) { + // Invalid value. + throw new InvalidHeaderValueException("Invalid Octet value!"); + } + break; + case RECOMMENDED_RETRIEVAL_MODE: + if (RECOMMENDED_RETRIEVAL_MODE_MANUAL != value) { + // Invalid value. + throw new InvalidHeaderValueException("Invalid Octet value!"); + } + break; + case CONTENT_CLASS: + if ((value < CONTENT_CLASS_TEXT) + || (value > CONTENT_CLASS_CONTENT_RICH)) { + // Invalid value. + throw new InvalidHeaderValueException("Invalid Octet value!"); + } + break; + case RETRIEVE_STATUS: + // According to oma-ts-mms-enc-v1_3, section 7.3.50, we modify the invalid value. + if ((value > RETRIEVE_STATUS_ERROR_TRANSIENT_NETWORK_PROBLEM) && + (value < RETRIEVE_STATUS_ERROR_PERMANENT_FAILURE)) { + value = RETRIEVE_STATUS_ERROR_TRANSIENT_FAILURE; + } else if ((value > RETRIEVE_STATUS_ERROR_PERMANENT_CONTENT_UNSUPPORTED) && + (value <= RETRIEVE_STATUS_ERROR_END)) { + value = RETRIEVE_STATUS_ERROR_PERMANENT_FAILURE; + } else if ((value < RETRIEVE_STATUS_OK) || + ((value > RETRIEVE_STATUS_OK) && + (value < RETRIEVE_STATUS_ERROR_TRANSIENT_FAILURE)) || + (value > RETRIEVE_STATUS_ERROR_END)) { + value = RETRIEVE_STATUS_ERROR_PERMANENT_FAILURE; + } + break; + case STORE_STATUS: + // According to oma-ts-mms-enc-v1_3, section 7.3.58, we modify the invalid value. + if ((value > STORE_STATUS_ERROR_TRANSIENT_NETWORK_PROBLEM) && + (value < STORE_STATUS_ERROR_PERMANENT_FAILURE)) { + value = STORE_STATUS_ERROR_TRANSIENT_FAILURE; + } else if ((value > STORE_STATUS_ERROR_PERMANENT_MMBOX_FULL) && + (value <= STORE_STATUS_ERROR_END)) { + value = STORE_STATUS_ERROR_PERMANENT_FAILURE; + } else if ((value < STORE_STATUS_SUCCESS) || + ((value > STORE_STATUS_SUCCESS) && + (value < STORE_STATUS_ERROR_TRANSIENT_FAILURE)) || + (value > STORE_STATUS_ERROR_END)) { + value = STORE_STATUS_ERROR_PERMANENT_FAILURE; + } + break; + case RESPONSE_STATUS: + // According to oma-ts-mms-enc-v1_3, section 7.3.48, we modify the invalid value. + if ((value > RESPONSE_STATUS_ERROR_TRANSIENT_PARTIAL_SUCCESS) && + (value < RESPONSE_STATUS_ERROR_PERMANENT_FAILURE)) { + value = RESPONSE_STATUS_ERROR_TRANSIENT_FAILURE; + } else if (((value > RESPONSE_STATUS_ERROR_PERMANENT_LACK_OF_PREPAID) && + (value <= RESPONSE_STATUS_ERROR_PERMANENT_END)) || + (value < RESPONSE_STATUS_OK) || + ((value > RESPONSE_STATUS_ERROR_UNSUPPORTED_MESSAGE) && + (value < RESPONSE_STATUS_ERROR_TRANSIENT_FAILURE)) || + (value > RESPONSE_STATUS_ERROR_PERMANENT_END)) { + value = RESPONSE_STATUS_ERROR_PERMANENT_FAILURE; + } + break; + case MMS_VERSION: + if ((value < MMS_VERSION_1_0)|| (value > MMS_VERSION_1_3)) { + value = CURRENT_MMS_VERSION; // Current version is the default value. + } + break; + case MESSAGE_TYPE: + if ((value < MESSAGE_TYPE_SEND_REQ) || (value > MESSAGE_TYPE_CANCEL_CONF)) { + // Invalid value. + throw new InvalidHeaderValueException("Invalid Octet value!"); + } + break; + default: + // This header value should not be Octect. + throw new RuntimeException("Invalid header field!"); + } + mHeaderMap.put(field, value); + } + + /** + * Get TextString value by header field. + * + * @param field the field + * @return the TextString value of the pdu header + * with specified header field + */ + public byte[] getTextString(int field) { + return (byte[]) mHeaderMap.get(field); + } + + /** + * Set TextString value to pdu header by header field. + * + * @param value the value + * @param field the field + * @return the TextString value of the pdu header + * with specified header field + * @throws NullPointerException if the value is null. + */ + public void setTextString(byte[] value, int field) { + /** + * Check whether this field can be set for specific + * header and check validity of the field. + */ + if (null == value) { + throw new NullPointerException(); + } + + switch (field) { + case TRANSACTION_ID: + case REPLY_CHARGING_ID: + case AUX_APPLIC_ID: + case APPLIC_ID: + case REPLY_APPLIC_ID: + case MESSAGE_ID: + case REPLACE_ID: + case CANCEL_ID: + case CONTENT_LOCATION: + case MESSAGE_CLASS: + case CONTENT_TYPE: + break; + default: + // This header value should not be Text-String. + throw new RuntimeException("Invalid header field!"); + } + mHeaderMap.put(field, value); + } + + /** + * Get EncodedStringValue value by header field. + * + * @param field the field + * @return the EncodedStringValue value of the pdu header + * with specified header field + */ + public EncodedStringValue getEncodedStringValue(int field) { + return (EncodedStringValue) mHeaderMap.get(field); + } + + /** + * Get TO, CC or BCC header value. + * + * @param field the field + * @return the EncodeStringValue array of the pdu header + * with specified header field + */ + public EncodedStringValue[] getEncodedStringValues(int field) { + ArrayList list = + (ArrayList) mHeaderMap.get(field); + if (null == list) { + return null; + } + EncodedStringValue[] values = new EncodedStringValue[list.size()]; + return list.toArray(values); + } + + /** + * Set EncodedStringValue value to pdu header by header field. + * + * @param value the value + * @param field the field + * @return the EncodedStringValue value of the pdu header + * with specified header field + * @throws NullPointerException if the value is null. + */ + public void setEncodedStringValue(EncodedStringValue value, int field) { + /** + * Check whether this field can be set for specific + * header and check validity of the field. + */ + if (null == value) { + throw new NullPointerException(); + } + + switch (field) { + case SUBJECT: + case RECOMMENDED_RETRIEVAL_MODE_TEXT: + case RETRIEVE_TEXT: + case STATUS_TEXT: + case STORE_STATUS_TEXT: + case RESPONSE_TEXT: + case FROM: + case PREVIOUSLY_SENT_BY: + case MM_FLAGS: + break; + default: + // This header value should not be Encoded-String-Value. + throw new RuntimeException("Invalid header field!"); + } + + mHeaderMap.put(field, value); + } + + /** + * Set TO, CC or BCC header value. + * + * @param value the value + * @param field the field + * @return the EncodedStringValue value array of the pdu header + * with specified header field + * @throws NullPointerException if the value is null. + */ + public void setEncodedStringValues(EncodedStringValue[] value, int field) { + /** + * Check whether this field can be set for specific + * header and check validity of the field. + */ + if (null == value) { + throw new NullPointerException(); + } + + switch (field) { + case BCC: + case CC: + case TO: + break; + default: + // This header value should not be Encoded-String-Value. + throw new RuntimeException("Invalid header field!"); + } + + ArrayList list = new ArrayList(); + for (int i = 0; i < value.length; i++) { + list.add(value[i]); + } + mHeaderMap.put(field, list); + } + + /** + * Append one EncodedStringValue to another. + * + * @param value the EncodedStringValue to append + * @param field the field + * @throws NullPointerException if the value is null. + */ + public void appendEncodedStringValue(EncodedStringValue value, + int field) { + if (null == value) { + throw new NullPointerException(); + } + + switch (field) { + case BCC: + case CC: + case TO: + break; + default: + throw new RuntimeException("Invalid header field!"); + } + + ArrayList list = + (ArrayList) mHeaderMap.get(field); + if (null == list) { + list = new ArrayList(); + } + list.add(value); + mHeaderMap.put(field, list); + } + + /** + * Get LongInteger value by header field. + * + * @param field the field + * @return the LongInteger value of the pdu header + * with specified header field. if return -1, the + * field is not existed in pdu header. + */ + public long getLongInteger(int field) { + Long longInteger = (Long) mHeaderMap.get(field); + if (null == longInteger) { + return -1; + } + + return longInteger.longValue(); + } + + /** + * Set LongInteger value to pdu header by header field. + * + * @param value the value + * @param field the field + */ + public void setLongInteger(long value, int field) { + /** + * Check whether this field can be set for specific + * header and check validity of the field. + */ + switch (field) { + case DATE: + case REPLY_CHARGING_SIZE: + case MESSAGE_SIZE: + case MESSAGE_COUNT: + case START: + case LIMIT: + case DELIVERY_TIME: + case EXPIRY: + case REPLY_CHARGING_DEADLINE: + case PREVIOUSLY_SENT_DATE: + break; + default: + // This header value should not be LongInteger. + throw new RuntimeException("Invalid header field!"); + } + mHeaderMap.put(field, value); + } +} diff --git a/mms-common/java/com/android/common/mms/ContentType.java b/mms-common/java/com/android/common/mms/ContentType.java new file mode 100644 index 000000000..0fdb46c92 --- /dev/null +++ b/mms-common/java/com/android/common/mms/ContentType.java @@ -0,0 +1,223 @@ +/* + * Copyright (C) 2007-2008 Esmertec AG. + * Copyright (C) 2007-2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.mms.mms; + +import java.util.ArrayList; + +public class ContentType { + public static final String MMS_MESSAGE = "application/vnd.wap.mms-message"; + // The phony content type for generic PDUs (e.g. ReadOrig.ind, + // Notification.ind, Delivery.ind). + public static final String MMS_GENERIC = "application/vnd.wap.mms-generic"; + public static final String MULTIPART_MIXED = "application/vnd.wap.multipart.mixed"; + public static final String MULTIPART_RELATED = "application/vnd.wap.multipart.related"; + public static final String MULTIPART_ALTERNATIVE = "application/vnd.wap.multipart.alternative"; + + public static final String TEXT_PLAIN = "text/plain"; + public static final String TEXT_HTML = "text/html"; + public static final String TEXT_VCALENDAR = "text/x-vCalendar"; + public static final String TEXT_VCARD = "text/x-vCard"; + + public static final String IMAGE_UNSPECIFIED = "image/*"; + public static final String IMAGE_JPEG = "image/jpeg"; + public static final String IMAGE_JPG = "image/jpg"; + public static final String IMAGE_GIF = "image/gif"; + public static final String IMAGE_WBMP = "image/vnd.wap.wbmp"; + public static final String IMAGE_PNG = "image/png"; + + public static final String AUDIO_UNSPECIFIED = "audio/*"; + public static final String AUDIO_AAC = "audio/aac"; + public static final String AUDIO_AMR = "audio/amr"; + public static final String AUDIO_IMELODY = "audio/imelody"; + public static final String AUDIO_MID = "audio/mid"; + public static final String AUDIO_MIDI = "audio/midi"; + public static final String AUDIO_MP3 = "audio/mp3"; + public static final String AUDIO_MPEG3 = "audio/mpeg3"; + public static final String AUDIO_MPEG = "audio/mpeg"; + public static final String AUDIO_MPG = "audio/mpg"; + public static final String AUDIO_MP4 = "audio/mp4"; + public static final String AUDIO_X_MID = "audio/x-mid"; + public static final String AUDIO_X_MIDI = "audio/x-midi"; + public static final String AUDIO_X_MP3 = "audio/x-mp3"; + public static final String AUDIO_X_MPEG3 = "audio/x-mpeg3"; + public static final String AUDIO_X_MPEG = "audio/x-mpeg"; + public static final String AUDIO_X_MPG = "audio/x-mpg"; + public static final String AUDIO_3GPP = "audio/3gpp"; + public static final String AUDIO_OGG = "application/ogg"; + + public static final String VIDEO_UNSPECIFIED = "video/*"; + public static final String VIDEO_3GPP = "video/3gpp"; + public static final String VIDEO_3G2 = "video/3gpp2"; + public static final String VIDEO_H263 = "video/h263"; + public static final String VIDEO_MP4 = "video/mp4"; + + public static final String APP_SMIL = "application/smil"; + public static final String APP_WAP_XHTML = "application/vnd.wap.xhtml+xml"; + public static final String APP_XHTML = "application/xhtml+xml"; + + public static final String APP_DRM_CONTENT = "application/vnd.oma.drm.content"; + public static final String APP_DRM_MESSAGE = "application/vnd.oma.drm.message"; + + private static final ArrayList sSupportedContentTypes = new ArrayList(); + private static final ArrayList sSupportedImageTypes = new ArrayList(); + private static final ArrayList sSupportedAudioTypes = new ArrayList(); + private static final ArrayList sSupportedVideoTypes = new ArrayList(); + + static { + sSupportedContentTypes.add(TEXT_PLAIN); + sSupportedContentTypes.add(TEXT_HTML); + sSupportedContentTypes.add(TEXT_VCALENDAR); + sSupportedContentTypes.add(TEXT_VCARD); + + sSupportedContentTypes.add(IMAGE_JPEG); + sSupportedContentTypes.add(IMAGE_GIF); + sSupportedContentTypes.add(IMAGE_WBMP); + sSupportedContentTypes.add(IMAGE_PNG); + sSupportedContentTypes.add(IMAGE_JPG); + //supportedContentTypes.add(IMAGE_SVG); not yet supported. + + sSupportedContentTypes.add(AUDIO_AAC); + sSupportedContentTypes.add(AUDIO_AMR); + sSupportedContentTypes.add(AUDIO_IMELODY); + sSupportedContentTypes.add(AUDIO_MID); + sSupportedContentTypes.add(AUDIO_MIDI); + sSupportedContentTypes.add(AUDIO_MP3); + sSupportedContentTypes.add(AUDIO_MPEG3); + sSupportedContentTypes.add(AUDIO_MPEG); + sSupportedContentTypes.add(AUDIO_MPG); + sSupportedContentTypes.add(AUDIO_X_MID); + sSupportedContentTypes.add(AUDIO_X_MIDI); + sSupportedContentTypes.add(AUDIO_X_MP3); + sSupportedContentTypes.add(AUDIO_X_MPEG3); + sSupportedContentTypes.add(AUDIO_X_MPEG); + sSupportedContentTypes.add(AUDIO_X_MPG); + sSupportedContentTypes.add(AUDIO_3GPP); + sSupportedContentTypes.add(AUDIO_OGG); + + sSupportedContentTypes.add(VIDEO_3GPP); + sSupportedContentTypes.add(VIDEO_3G2); + sSupportedContentTypes.add(VIDEO_H263); + sSupportedContentTypes.add(VIDEO_MP4); + + sSupportedContentTypes.add(APP_SMIL); + sSupportedContentTypes.add(APP_WAP_XHTML); + sSupportedContentTypes.add(APP_XHTML); + + sSupportedContentTypes.add(APP_DRM_CONTENT); + sSupportedContentTypes.add(APP_DRM_MESSAGE); + + // add supported image types + sSupportedImageTypes.add(IMAGE_JPEG); + sSupportedImageTypes.add(IMAGE_GIF); + sSupportedImageTypes.add(IMAGE_WBMP); + sSupportedImageTypes.add(IMAGE_PNG); + sSupportedImageTypes.add(IMAGE_JPG); + + // add supported audio types + sSupportedAudioTypes.add(AUDIO_AAC); + sSupportedAudioTypes.add(AUDIO_AMR); + sSupportedAudioTypes.add(AUDIO_IMELODY); + sSupportedAudioTypes.add(AUDIO_MID); + sSupportedAudioTypes.add(AUDIO_MIDI); + sSupportedAudioTypes.add(AUDIO_MP3); + sSupportedAudioTypes.add(AUDIO_MPEG3); + sSupportedAudioTypes.add(AUDIO_MPEG); + sSupportedAudioTypes.add(AUDIO_MPG); + sSupportedAudioTypes.add(AUDIO_MP4); + sSupportedAudioTypes.add(AUDIO_X_MID); + sSupportedAudioTypes.add(AUDIO_X_MIDI); + sSupportedAudioTypes.add(AUDIO_X_MP3); + sSupportedAudioTypes.add(AUDIO_X_MPEG3); + sSupportedAudioTypes.add(AUDIO_X_MPEG); + sSupportedAudioTypes.add(AUDIO_X_MPG); + sSupportedAudioTypes.add(AUDIO_3GPP); + sSupportedAudioTypes.add(AUDIO_OGG); + + // add supported video types + sSupportedVideoTypes.add(VIDEO_3GPP); + sSupportedVideoTypes.add(VIDEO_3G2); + sSupportedVideoTypes.add(VIDEO_H263); + sSupportedVideoTypes.add(VIDEO_MP4); + } + + // This class should never be instantiated. + private ContentType() { + } + + public static boolean isSupportedType(String contentType) { + return (null != contentType) && sSupportedContentTypes.contains(contentType); + } + + public static boolean isSupportedImageType(String contentType) { + return isImageType(contentType) && isSupportedType(contentType); + } + + public static boolean isSupportedAudioType(String contentType) { + return isAudioType(contentType) && isSupportedType(contentType); + } + + public static boolean isSupportedVideoType(String contentType) { + return isVideoType(contentType) && isSupportedType(contentType); + } + + public static boolean isTextType(String contentType) { + return (null != contentType) && contentType.startsWith("text/"); + } + + public static boolean isImageType(String contentType) { + return (null != contentType) && contentType.startsWith("image/"); + } + + public static boolean isAudioType(String contentType) { + return (null != contentType) && contentType.startsWith("audio/"); + } + + public static boolean isVideoType(String contentType) { + return (null != contentType) && contentType.startsWith("video/"); + } + + public static boolean isDrmType(String contentType) { + return (null != contentType) + && (contentType.equals(APP_DRM_CONTENT) + || contentType.equals(APP_DRM_MESSAGE)); + } + + public static boolean isUnspecified(String contentType) { + return (null != contentType) && contentType.endsWith("*"); + } + + @SuppressWarnings("unchecked") + public static ArrayList getImageTypes() { + return (ArrayList) sSupportedImageTypes.clone(); + } + + @SuppressWarnings("unchecked") + public static ArrayList getAudioTypes() { + return (ArrayList) sSupportedAudioTypes.clone(); + } + + @SuppressWarnings("unchecked") + public static ArrayList getVideoTypes() { + return (ArrayList) sSupportedVideoTypes.clone(); + } + + @SuppressWarnings("unchecked") + public static ArrayList getSupportedTypes() { + return (ArrayList) sSupportedContentTypes.clone(); + } +} diff --git a/mms-common/java/com/android/common/mms/pdu/AcknowledgeInd.java b/mms-common/java/com/android/common/mms/pdu/AcknowledgeInd.java new file mode 100644 index 000000000..d1243b22b --- /dev/null +++ b/mms-common/java/com/android/common/mms/pdu/AcknowledgeInd.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2007 Esmertec AG. + * 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. + */ + +package com.android.mmscommon.mms.pdu; + +import com.android.mmscommon.InvalidHeaderValueException; +import com.android.mmscommon.PduHeaders; + +/** + * M-Acknowledge.ind PDU. + */ +public class AcknowledgeInd extends GenericPdu { + /** + * Constructor, used when composing a M-Acknowledge.ind pdu. + * + * @param mmsVersion current viersion of mms + * @param transactionId the transaction-id value + * @throws InvalidHeaderValueException if parameters are invalid. + * NullPointerException if transactionId is null. + */ + public AcknowledgeInd(int mmsVersion, byte[] transactionId) + throws InvalidHeaderValueException { + super(); + + setMessageType(PduHeaders.MESSAGE_TYPE_ACKNOWLEDGE_IND); + setMmsVersion(mmsVersion); + setTransactionId(transactionId); + } + + /** + * Constructor with given headers. + * + * @param headers Headers for this PDU. + */ + AcknowledgeInd(PduHeaders headers) { + super(headers); + } + + /** + * Get X-Mms-Report-Allowed field value. + * + * @return the X-Mms-Report-Allowed value + */ + public int getReportAllowed() { + return mPduHeaders.getOctet(PduHeaders.REPORT_ALLOWED); + } + + /** + * Set X-Mms-Report-Allowed field value. + * + * @param value the value + * @throws InvalidHeaderValueException if the value is invalid. + */ + public void setReportAllowed(int value) throws InvalidHeaderValueException { + mPduHeaders.setOctet(value, PduHeaders.REPORT_ALLOWED); + } + + /** + * Get X-Mms-Transaction-Id field value. + * + * @return the X-Mms-Report-Allowed value + */ + public byte[] getTransactionId() { + return mPduHeaders.getTextString(PduHeaders.TRANSACTION_ID); + } + + /** + * Set X-Mms-Transaction-Id field value. + * + * @param value the value + * @throws NullPointerException if the value is null. + */ + public void setTransactionId(byte[] value) { + mPduHeaders.setTextString(value, PduHeaders.TRANSACTION_ID); + } +} diff --git a/mms-common/java/com/android/common/mms/pdu/Base64.java b/mms-common/java/com/android/common/mms/pdu/Base64.java new file mode 100644 index 000000000..4c95dec72 --- /dev/null +++ b/mms-common/java/com/android/common/mms/pdu/Base64.java @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2007 Esmertec AG. + * 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. + */ + +package com.android.mmscommon.mms.pdu; + +public class Base64 { + /** + * Used to get the number of Quadruples. + */ + static final int FOURBYTE = 4; + + /** + * Byte used to pad output. + */ + static final byte PAD = (byte) '='; + + /** + * The base length. + */ + static final int BASELENGTH = 255; + + // Create arrays to hold the base64 characters + private static byte[] base64Alphabet = new byte[BASELENGTH]; + + // Populating the character arrays + static { + for (int i = 0; i < BASELENGTH; i++) { + base64Alphabet[i] = (byte) -1; + } + for (int i = 'Z'; i >= 'A'; i--) { + base64Alphabet[i] = (byte) (i - 'A'); + } + for (int i = 'z'; i >= 'a'; i--) { + base64Alphabet[i] = (byte) (i - 'a' + 26); + } + for (int i = '9'; i >= '0'; i--) { + base64Alphabet[i] = (byte) (i - '0' + 52); + } + + base64Alphabet['+'] = 62; + base64Alphabet['/'] = 63; + } + + /** + * Decodes Base64 data into octects + * + * @param base64Data Byte array containing Base64 data + * @return Array containing decoded data. + */ + public static byte[] decodeBase64(byte[] base64Data) { + // RFC 2045 requires that we discard ALL non-Base64 characters + base64Data = discardNonBase64(base64Data); + + // handle the edge case, so we don't have to worry about it later + if (base64Data.length == 0) { + return new byte[0]; + } + + int numberQuadruple = base64Data.length / FOURBYTE; + byte decodedData[] = null; + byte b1 = 0, b2 = 0, b3 = 0, b4 = 0, marker0 = 0, marker1 = 0; + + // Throw away anything not in base64Data + + int encodedIndex = 0; + int dataIndex = 0; + { + // this sizes the output array properly - rlw + int lastData = base64Data.length; + // ignore the '=' padding + while (base64Data[lastData - 1] == PAD) { + if (--lastData == 0) { + return new byte[0]; + } + } + decodedData = new byte[lastData - numberQuadruple]; + } + + for (int i = 0; i < numberQuadruple; i++) { + dataIndex = i * 4; + marker0 = base64Data[dataIndex + 2]; + marker1 = base64Data[dataIndex + 3]; + + b1 = base64Alphabet[base64Data[dataIndex]]; + b2 = base64Alphabet[base64Data[dataIndex + 1]]; + + if (marker0 != PAD && marker1 != PAD) { + //No PAD e.g 3cQl + b3 = base64Alphabet[marker0]; + b4 = base64Alphabet[marker1]; + + decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4); + decodedData[encodedIndex + 1] = + (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); + decodedData[encodedIndex + 2] = (byte) (b3 << 6 | b4); + } else if (marker0 == PAD) { + //Two PAD e.g. 3c[Pad][Pad] + decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4); + } else if (marker1 == PAD) { + //One PAD e.g. 3cQ[Pad] + b3 = base64Alphabet[marker0]; + + decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4); + decodedData[encodedIndex + 1] = + (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); + } + encodedIndex += 3; + } + return decodedData; + } + + /** + * Check octect wheter it is a base64 encoding. + * + * @param octect to be checked byte + * @return ture if it is base64 encoding, false otherwise. + */ + private static boolean isBase64(byte octect) { + if (octect == PAD) { + return true; + } else if (base64Alphabet[octect] == -1) { + return false; + } else { + return true; + } + } + + /** + * Discards any characters outside of the base64 alphabet, per + * the requirements on page 25 of RFC 2045 - "Any characters + * outside of the base64 alphabet are to be ignored in base64 + * encoded data." + * + * @param data The base-64 encoded data to groom + * @return The data, less non-base64 characters (see RFC 2045). + */ + static byte[] discardNonBase64(byte[] data) { + byte groomedData[] = new byte[data.length]; + int bytesCopied = 0; + + for (int i = 0; i < data.length; i++) { + if (isBase64(data[i])) { + groomedData[bytesCopied++] = data[i]; + } + } + + byte packedData[] = new byte[bytesCopied]; + + System.arraycopy(groomedData, 0, packedData, 0, bytesCopied); + + return packedData; + } +} diff --git a/mms-common/java/com/android/common/mms/pdu/DeliveryInd.java b/mms-common/java/com/android/common/mms/pdu/DeliveryInd.java new file mode 100644 index 000000000..e83729b8b --- /dev/null +++ b/mms-common/java/com/android/common/mms/pdu/DeliveryInd.java @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2007 Esmertec AG. + * 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. + */ + +package com.android.mmscommon.mms.pdu; + +import com.android.mmscommon.EncodedStringValue; +import com.android.mmscommon.InvalidHeaderValueException; +import com.android.mmscommon.PduHeaders; + +/** + * M-Delivery.Ind Pdu. + */ +public class DeliveryInd extends GenericPdu { + /** + * Empty constructor. + * Since the Pdu corresponding to this class is constructed + * by the Proxy-Relay server, this class is only instantiated + * by the Pdu Parser. + * + * @throws InvalidHeaderValueException if error occurs. + */ + public DeliveryInd() throws InvalidHeaderValueException { + super(); + setMessageType(PduHeaders.MESSAGE_TYPE_DELIVERY_IND); + } + + /** + * Constructor with given headers. + * + * @param headers Headers for this PDU. + */ + DeliveryInd(PduHeaders headers) { + super(headers); + } + + /** + * Get Date value. + * + * @return the value + */ + public long getDate() { + return mPduHeaders.getLongInteger(PduHeaders.DATE); + } + + /** + * Set Date value. + * + * @param value the value + */ + public void setDate(long value) { + mPduHeaders.setLongInteger(value, PduHeaders.DATE); + } + + /** + * Get Message-ID value. + * + * @return the value + */ + public byte[] getMessageId() { + return mPduHeaders.getTextString(PduHeaders.MESSAGE_ID); + } + + /** + * Set Message-ID value. + * + * @param value the value, should not be null + * @throws NullPointerException if the value is null. + */ + public void setMessageId(byte[] value) { + mPduHeaders.setTextString(value, PduHeaders.MESSAGE_ID); + } + + /** + * Get Status value. + * + * @return the value + */ + public int getStatus() { + return mPduHeaders.getOctet(PduHeaders.STATUS); + } + + /** + * Set Status value. + * + * @param value the value + * @throws InvalidHeaderValueException if the value is invalid. + */ + public void setStatus(int value) throws InvalidHeaderValueException { + mPduHeaders.setOctet(value, PduHeaders.STATUS); + } + + /** + * Get To value. + * + * @return the value + */ + public EncodedStringValue[] getTo() { + return mPduHeaders.getEncodedStringValues(PduHeaders.TO); + } + + /** + * set To value. + * + * @param value the value + * @throws NullPointerException if the value is null. + */ + public void setTo(EncodedStringValue[] value) { + mPduHeaders.setEncodedStringValues(value, PduHeaders.TO); + } + + /* + * Optional, not supported header fields: + * + * public byte[] getApplicId() {return null;} + * public void setApplicId(byte[] value) {} + * + * public byte[] getAuxApplicId() {return null;} + * public void getAuxApplicId(byte[] value) {} + * + * public byte[] getReplyApplicId() {return 0x00;} + * public void setReplyApplicId(byte[] value) {} + * + * public EncodedStringValue getStatusText() {return null;} + * public void setStatusText(EncodedStringValue value) {} + */ +} diff --git a/mms-common/java/com/android/common/mms/pdu/GenericPdu.java b/mms-common/java/com/android/common/mms/pdu/GenericPdu.java new file mode 100644 index 000000000..c38e50250 --- /dev/null +++ b/mms-common/java/com/android/common/mms/pdu/GenericPdu.java @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2007 Esmertec AG. + * 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. + */ + +package com.android.mmscommon.mms.pdu; + +import com.android.mmscommon.EncodedStringValue; +import com.android.mmscommon.InvalidHeaderValueException; +import com.android.mmscommon.PduHeaders; + +public class GenericPdu { + /** + * The headers of pdu. + */ + PduHeaders mPduHeaders = null; + + /** + * Constructor. + */ + public GenericPdu() { + mPduHeaders = new PduHeaders(); + } + + /** + * Constructor. + * + * @param headers Headers for this PDU. + */ + GenericPdu(PduHeaders headers) { + mPduHeaders = headers; + } + + /** + * Get the headers of this PDU. + * + * @return A PduHeaders of this PDU. + */ + PduHeaders getPduHeaders() { + return mPduHeaders; + } + + /** + * Get X-Mms-Message-Type field value. + * + * @return the X-Mms-Report-Allowed value + */ + public int getMessageType() { + return mPduHeaders.getOctet(PduHeaders.MESSAGE_TYPE); + } + + /** + * Set X-Mms-Message-Type field value. + * + * @param value the value + * @throws InvalidHeaderValueException if the value is invalid. + * RuntimeException if field's value is not Octet. + */ + public void setMessageType(int value) throws InvalidHeaderValueException { + mPduHeaders.setOctet(value, PduHeaders.MESSAGE_TYPE); + } + + /** + * Get X-Mms-MMS-Version field value. + * + * @return the X-Mms-MMS-Version value + */ + public int getMmsVersion() { + return mPduHeaders.getOctet(PduHeaders.MMS_VERSION); + } + + /** + * Set X-Mms-MMS-Version field value. + * + * @param value the value + * @throws InvalidHeaderValueException if the value is invalid. + * RuntimeException if field's value is not Octet. + */ + public void setMmsVersion(int value) throws InvalidHeaderValueException { + mPduHeaders.setOctet(value, PduHeaders.MMS_VERSION); + } + + /** + * Get From value. + * From-value = Value-length + * (Address-present-token Encoded-string-value | Insert-address-token) + * + * @return the value + */ + public EncodedStringValue getFrom() { + return mPduHeaders.getEncodedStringValue(PduHeaders.FROM); + } + + /** + * Set From value. + * + * @param value the value + * @throws NullPointerException if the value is null. + */ + public void setFrom(EncodedStringValue value) { + mPduHeaders.setEncodedStringValue(value, PduHeaders.FROM); + } +} diff --git a/mms-common/java/com/android/common/mms/pdu/MultimediaMessagePdu.java b/mms-common/java/com/android/common/mms/pdu/MultimediaMessagePdu.java new file mode 100644 index 000000000..04fde2d45 --- /dev/null +++ b/mms-common/java/com/android/common/mms/pdu/MultimediaMessagePdu.java @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2007 Esmertec AG. + * 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. + */ + +package com.android.mmscommon.mms.pdu; + +import com.android.mmscommon.EncodedStringValue; +import com.android.mmscommon.InvalidHeaderValueException; +import com.android.mmscommon.PduHeaders; + +/** + * Multimedia message PDU. + */ +public class MultimediaMessagePdu extends GenericPdu{ + /** + * The body. + */ + private PduBody mMessageBody; + + /** + * Constructor. + */ + public MultimediaMessagePdu() { + super(); + } + + /** + * Constructor. + * + * @param header the header of this PDU + * @param body the body of this PDU + */ + public MultimediaMessagePdu(PduHeaders header, PduBody body) { + super(header); + mMessageBody = body; + } + + /** + * Constructor with given headers. + * + * @param headers Headers for this PDU. + */ + MultimediaMessagePdu(PduHeaders headers) { + super(headers); + } + + /** + * Get body of the PDU. + * + * @return the body + */ + public PduBody getBody() { + return mMessageBody; + } + + /** + * Set body of the PDU. + * + * @param body the body + */ + public void setBody(PduBody body) { + mMessageBody = body; + } + + /** + * Get subject. + * + * @return the value + */ + public EncodedStringValue getSubject() { + return mPduHeaders.getEncodedStringValue(PduHeaders.SUBJECT); + } + + /** + * Set subject. + * + * @param value the value + * @throws NullPointerException if the value is null. + */ + public void setSubject(EncodedStringValue value) { + mPduHeaders.setEncodedStringValue(value, PduHeaders.SUBJECT); + } + + /** + * Get To value. + * + * @return the value + */ + public EncodedStringValue[] getTo() { + return mPduHeaders.getEncodedStringValues(PduHeaders.TO); + } + + /** + * Add a "To" value. + * + * @param value the value + * @throws NullPointerException if the value is null. + */ + public void addTo(EncodedStringValue value) { + mPduHeaders.appendEncodedStringValue(value, PduHeaders.TO); + } + + /** + * Get X-Mms-Priority value. + * + * @return the value + */ + public int getPriority() { + return mPduHeaders.getOctet(PduHeaders.PRIORITY); + } + + /** + * Set X-Mms-Priority value. + * + * @param value the value + * @throws InvalidHeaderValueException if the value is invalid. + */ + public void setPriority(int value) throws InvalidHeaderValueException { + mPduHeaders.setOctet(value, PduHeaders.PRIORITY); + } + + /** + * Get Date value. + * + * @return the value + */ + public long getDate() { + return mPduHeaders.getLongInteger(PduHeaders.DATE); + } + + /** + * Set Date value in seconds. + * + * @param value the value + */ + public void setDate(long value) { + mPduHeaders.setLongInteger(value, PduHeaders.DATE); + } +} diff --git a/mms-common/java/com/android/common/mms/pdu/NotificationInd.java b/mms-common/java/com/android/common/mms/pdu/NotificationInd.java new file mode 100644 index 000000000..24f17b09a --- /dev/null +++ b/mms-common/java/com/android/common/mms/pdu/NotificationInd.java @@ -0,0 +1,287 @@ +/* + * Copyright (C) 2007 Esmertec AG. + * 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. + */ + +package com.android.mmscommon.mms.pdu; + +import com.android.mmscommon.EncodedStringValue; +import com.android.mmscommon.InvalidHeaderValueException; +import com.android.mmscommon.PduHeaders; + +/** + * M-Notification.ind PDU. + */ +public class NotificationInd extends GenericPdu { + /** + * Empty constructor. + * Since the Pdu corresponding to this class is constructed + * by the Proxy-Relay server, this class is only instantiated + * by the Pdu Parser. + * + * @throws InvalidHeaderValueException if error occurs. + * RuntimeException if an undeclared error occurs. + */ + public NotificationInd() throws InvalidHeaderValueException { + super(); + setMessageType(PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND); + } + + /** + * Constructor with given headers. + * + * @param headers Headers for this PDU. + */ + NotificationInd(PduHeaders headers) { + super(headers); + } + + /** + * Get X-Mms-Content-Class Value. + * + * @return the value + */ + public int getContentClass() { + return mPduHeaders.getOctet(PduHeaders.CONTENT_CLASS); + } + + /** + * Set X-Mms-Content-Class Value. + * + * @param value the value + * @throws InvalidHeaderValueException if the value is invalid. + * RuntimeException if an undeclared error occurs. + */ + public void setContentClass(int value) throws InvalidHeaderValueException { + mPduHeaders.setOctet(value, PduHeaders.CONTENT_CLASS); + } + + /** + * Get X-Mms-Content-Location value. + * When used in a PDU other than M-Mbox-Delete.conf and M-Delete.conf: + * Content-location-value = Uri-value + * + * @return the value + */ + public byte[] getContentLocation() { + return mPduHeaders.getTextString(PduHeaders.CONTENT_LOCATION); + } + + /** + * Set X-Mms-Content-Location value. + * + * @param value the value + * @throws NullPointerException if the value is null. + * RuntimeException if an undeclared error occurs. + */ + public void setContentLocation(byte[] value) { + mPduHeaders.setTextString(value, PduHeaders.CONTENT_LOCATION); + } + + /** + * Get X-Mms-Expiry value. + * + * Expiry-value = Value-length + * (Absolute-token Date-value | Relative-token Delta-seconds-value) + * + * @return the value + */ + public long getExpiry() { + return mPduHeaders.getLongInteger(PduHeaders.EXPIRY); + } + + /** + * Set X-Mms-Expiry value. + * + * @param value the value + * @throws RuntimeException if an undeclared error occurs. + */ + public void setExpiry(long value) { + mPduHeaders.setLongInteger(value, PduHeaders.EXPIRY); + } + + /** + * Get From value. + * From-value = Value-length + * (Address-present-token Encoded-string-value | Insert-address-token) + * + * @return the value + */ + public EncodedStringValue getFrom() { + return mPduHeaders.getEncodedStringValue(PduHeaders.FROM); + } + + /** + * Set From value. + * + * @param value the value + * @throws NullPointerException if the value is null. + * RuntimeException if an undeclared error occurs. + */ + public void setFrom(EncodedStringValue value) { + mPduHeaders.setEncodedStringValue(value, PduHeaders.FROM); + } + + /** + * Get X-Mms-Message-Class value. + * Message-class-value = Class-identifier | Token-text + * Class-identifier = Personal | Advertisement | Informational | Auto + * + * @return the value + */ + public byte[] getMessageClass() { + return mPduHeaders.getTextString(PduHeaders.MESSAGE_CLASS); + } + + /** + * Set X-Mms-Message-Class value. + * + * @param value the value + * @throws NullPointerException if the value is null. + * RuntimeException if an undeclared error occurs. + */ + public void setMessageClass(byte[] value) { + mPduHeaders.setTextString(value, PduHeaders.MESSAGE_CLASS); + } + + /** + * Get X-Mms-Message-Size value. + * Message-size-value = Long-integer + * + * @return the value + */ + public long getMessageSize() { + return mPduHeaders.getLongInteger(PduHeaders.MESSAGE_SIZE); + } + + /** + * Set X-Mms-Message-Size value. + * + * @param value the value + * @throws RuntimeException if an undeclared error occurs. + */ + public void setMessageSize(long value) { + mPduHeaders.setLongInteger(value, PduHeaders.MESSAGE_SIZE); + } + + /** + * Get subject. + * + * @return the value + */ + public EncodedStringValue getSubject() { + return mPduHeaders.getEncodedStringValue(PduHeaders.SUBJECT); + } + + /** + * Set subject. + * + * @param value the value + * @throws NullPointerException if the value is null. + * RuntimeException if an undeclared error occurs. + */ + public void setSubject(EncodedStringValue value) { + mPduHeaders.setEncodedStringValue(value, PduHeaders.SUBJECT); + } + + /** + * Get X-Mms-Transaction-Id. + * + * @return the value + */ + public byte[] getTransactionId() { + return mPduHeaders.getTextString(PduHeaders.TRANSACTION_ID); + } + + /** + * Set X-Mms-Transaction-Id. + * + * @param value the value + * @throws NullPointerException if the value is null. + * RuntimeException if an undeclared error occurs. + */ + public void setTransactionId(byte[] value) { + mPduHeaders.setTextString(value, PduHeaders.TRANSACTION_ID); + } + + /** + * Get X-Mms-Delivery-Report Value. + * + * @return the value + */ + public int getDeliveryReport() { + return mPduHeaders.getOctet(PduHeaders.DELIVERY_REPORT); + } + + /** + * Set X-Mms-Delivery-Report Value. + * + * @param value the value + * @throws InvalidHeaderValueException if the value is invalid. + * RuntimeException if an undeclared error occurs. + */ + public void setDeliveryReport(int value) throws InvalidHeaderValueException { + mPduHeaders.setOctet(value, PduHeaders.DELIVERY_REPORT); + } + + /* + * Optional, not supported header fields: + * + * public byte[] getApplicId() {return null;} + * public void setApplicId(byte[] value) {} + * + * public byte[] getAuxApplicId() {return null;} + * public void getAuxApplicId(byte[] value) {} + * + * public byte getDrmContent() {return 0x00;} + * public void setDrmContent(byte value) {} + * + * public byte getDistributionIndicator() {return 0x00;} + * public void setDistributionIndicator(byte value) {} + * + * public ElementDescriptorValue getElementDescriptor() {return null;} + * public void getElementDescriptor(ElementDescriptorValue value) {} + * + * public byte getPriority() {return 0x00;} + * public void setPriority(byte value) {} + * + * public byte getRecommendedRetrievalMode() {return 0x00;} + * public void setRecommendedRetrievalMode(byte value) {} + * + * public byte getRecommendedRetrievalModeText() {return 0x00;} + * public void setRecommendedRetrievalModeText(byte value) {} + * + * public byte[] getReplaceId() {return 0x00;} + * public void setReplaceId(byte[] value) {} + * + * public byte[] getReplyApplicId() {return 0x00;} + * public void setReplyApplicId(byte[] value) {} + * + * public byte getReplyCharging() {return 0x00;} + * public void setReplyCharging(byte value) {} + * + * public byte getReplyChargingDeadline() {return 0x00;} + * public void setReplyChargingDeadline(byte value) {} + * + * public byte[] getReplyChargingId() {return 0x00;} + * public void setReplyChargingId(byte[] value) {} + * + * public long getReplyChargingSize() {return 0;} + * public void setReplyChargingSize(long value) {} + * + * public byte getStored() {return 0x00;} + * public void setStored(byte value) {} + */ +} diff --git a/mms-common/java/com/android/common/mms/pdu/NotifyRespInd.java b/mms-common/java/com/android/common/mms/pdu/NotifyRespInd.java new file mode 100644 index 000000000..c2e2b2653 --- /dev/null +++ b/mms-common/java/com/android/common/mms/pdu/NotifyRespInd.java @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2007 Esmertec AG. + * 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. + */ + +package com.android.mmscommon.mms.pdu; + +import com.android.mmscommon.InvalidHeaderValueException; +import com.android.mmscommon.PduHeaders; + +/** + * M-NofifyResp.ind PDU. + */ +public class NotifyRespInd extends GenericPdu { + /** + * Constructor, used when composing a M-NotifyResp.ind pdu. + * + * @param mmsVersion current version of mms + * @param transactionId the transaction-id value + * @param status the status value + * @throws InvalidHeaderValueException if parameters are invalid. + * NullPointerException if transactionId is null. + * RuntimeException if an undeclared error occurs. + */ + public NotifyRespInd(int mmsVersion, + byte[] transactionId, + int status) throws InvalidHeaderValueException { + super(); + setMessageType(PduHeaders.MESSAGE_TYPE_NOTIFYRESP_IND); + setMmsVersion(mmsVersion); + setTransactionId(transactionId); + setStatus(status); + } + + /** + * Constructor with given headers. + * + * @param headers Headers for this PDU. + */ + NotifyRespInd(PduHeaders headers) { + super(headers); + } + + /** + * Get X-Mms-Report-Allowed field value. + * + * @return the X-Mms-Report-Allowed value + */ + public int getReportAllowed() { + return mPduHeaders.getOctet(PduHeaders.REPORT_ALLOWED); + } + + /** + * Set X-Mms-Report-Allowed field value. + * + * @param value the value + * @throws InvalidHeaderValueException if the value is invalid. + * RuntimeException if an undeclared error occurs. + */ + public void setReportAllowed(int value) throws InvalidHeaderValueException { + mPduHeaders.setOctet(value, PduHeaders.REPORT_ALLOWED); + } + + /** + * Set X-Mms-Status field value. + * + * @param value the value + * @throws InvalidHeaderValueException if the value is invalid. + * RuntimeException if an undeclared error occurs. + */ + public void setStatus(int value) throws InvalidHeaderValueException { + mPduHeaders.setOctet(value, PduHeaders.STATUS); + } + + /** + * GetX-Mms-Status field value. + * + * @return the X-Mms-Status value + */ + public int getStatus() { + return mPduHeaders.getOctet(PduHeaders.STATUS); + } + + /** + * Get X-Mms-Transaction-Id field value. + * + * @return the X-Mms-Report-Allowed value + */ + public byte[] getTransactionId() { + return mPduHeaders.getTextString(PduHeaders.TRANSACTION_ID); + } + + /** + * Set X-Mms-Transaction-Id field value. + * + * @param value the value + * @throws NullPointerException if the value is null. + * RuntimeException if an undeclared error occurs. + */ + public void setTransactionId(byte[] value) { + mPduHeaders.setTextString(value, PduHeaders.TRANSACTION_ID); + } +} diff --git a/mms-common/java/com/android/common/mms/pdu/PduBody.java b/mms-common/java/com/android/common/mms/pdu/PduBody.java new file mode 100644 index 000000000..cc28d80eb --- /dev/null +++ b/mms-common/java/com/android/common/mms/pdu/PduBody.java @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2007 Esmertec AG. + * 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. + */ + +package com.android.mmscommon.mms.pdu; + +import java.util.HashMap; +import java.util.Map; +import java.util.Vector; + +public class PduBody { + private Vector mParts = null; + + private Map mPartMapByContentId = null; + private Map mPartMapByContentLocation = null; + private Map mPartMapByName = null; + private Map mPartMapByFileName = null; + + /** + * Constructor. + */ + public PduBody() { + mParts = new Vector(); + + mPartMapByContentId = new HashMap(); + mPartMapByContentLocation = new HashMap(); + mPartMapByName = new HashMap(); + mPartMapByFileName = new HashMap(); + } + + private void putPartToMaps(PduPart part) { + // Put part to mPartMapByContentId. + byte[] contentId = part.getContentId(); + if(null != contentId) { + mPartMapByContentId.put(new String(contentId), part); + } + + // Put part to mPartMapByContentLocation. + byte[] contentLocation = part.getContentLocation(); + if(null != contentLocation) { + String clc = new String(contentLocation); + mPartMapByContentLocation.put(clc, part); + } + + // Put part to mPartMapByName. + byte[] name = part.getName(); + if(null != name) { + String clc = new String(name); + mPartMapByName.put(clc, part); + } + + // Put part to mPartMapByFileName. + byte[] fileName = part.getFilename(); + if(null != fileName) { + String clc = new String(fileName); + mPartMapByFileName.put(clc, part); + } + } + + /** + * Appends the specified part to the end of this body. + * + * @param part part to be appended + * @return true when success, false when fail + * @throws NullPointerException when part is null + */ + public boolean addPart(PduPart part) { + if(null == part) { + throw new NullPointerException(); + } + + putPartToMaps(part); + return mParts.add(part); + } + + /** + * Inserts the specified part at the specified position. + * + * @param index index at which the specified part is to be inserted + * @param part part to be inserted + * @throws NullPointerException when part is null + */ + public void addPart(int index, PduPart part) { + if(null == part) { + throw new NullPointerException(); + } + + putPartToMaps(part); + mParts.add(index, part); + } + + /** + * Removes the part at the specified position. + * + * @param index index of the part to return + * @return part at the specified index + */ + public PduPart removePart(int index) { + return mParts.remove(index); + } + + /** + * Remove all of the parts. + */ + public void removeAll() { + mParts.clear(); + } + + /** + * Get the part at the specified position. + * + * @param index index of the part to return + * @return part at the specified index + */ + public PduPart getPart(int index) { + return mParts.get(index); + } + + /** + * Get the index of the specified part. + * + * @param part the part object + * @return index the index of the first occurrence of the part in this body + */ + public int getPartIndex(PduPart part) { + return mParts.indexOf(part); + } + + /** + * Get the number of parts. + * + * @return the number of parts + */ + public int getPartsNum() { + return mParts.size(); + } + + /** + * Get pdu part by content id. + * + * @param cid the value of content id. + * @return the pdu part. + */ + public PduPart getPartByContentId(String cid) { + return mPartMapByContentId.get(cid); + } + + /** + * Get pdu part by Content-Location. Content-Location of part is + * the same as filename and name(param of content-type). + * + * @param fileName the value of filename. + * @return the pdu part. + */ + public PduPart getPartByContentLocation(String contentLocation) { + return mPartMapByContentLocation.get(contentLocation); + } + + /** + * Get pdu part by name. + * + * @param fileName the value of filename. + * @return the pdu part. + */ + public PduPart getPartByName(String name) { + return mPartMapByName.get(name); + } + + /** + * Get pdu part by filename. + * + * @param fileName the value of filename. + * @return the pdu part. + */ + public PduPart getPartByFileName(String filename) { + return mPartMapByFileName.get(filename); + } +} diff --git a/mms-common/java/com/android/common/mms/pdu/PduComposer.java b/mms-common/java/com/android/common/mms/pdu/PduComposer.java new file mode 100644 index 000000000..bb3116d69 --- /dev/null +++ b/mms-common/java/com/android/common/mms/pdu/PduComposer.java @@ -0,0 +1,1184 @@ +/* + * Copyright (C) 2007-2008 Esmertec AG. + * Copyright (C) 2007-2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.mmscommon.mms.pdu; + +import com.android.mmscommon.EncodedStringValue; +import com.android.mmscommon.PduHeaders; + +import android.content.ContentResolver; +import android.content.Context; +import android.util.Log; +import android.text.TextUtils; + +import java.io.ByteArrayOutputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.util.Arrays; +import java.util.HashMap; + +public class PduComposer { + /** + * Address type. + */ + static private final int PDU_PHONE_NUMBER_ADDRESS_TYPE = 1; + static private final int PDU_EMAIL_ADDRESS_TYPE = 2; + static private final int PDU_IPV4_ADDRESS_TYPE = 3; + static private final int PDU_IPV6_ADDRESS_TYPE = 4; + static private final int PDU_UNKNOWN_ADDRESS_TYPE = 5; + + /** + * Address regular expression string. + */ + static final String REGEXP_PHONE_NUMBER_ADDRESS_TYPE = "\\+?[0-9|\\.|\\-]+"; + static final String REGEXP_EMAIL_ADDRESS_TYPE = "[a-zA-Z| ]*\\<{0,1}[a-zA-Z| ]+@{1}" + + "[a-zA-Z| ]+\\.{1}[a-zA-Z| ]+\\>{0,1}"; + static final String REGEXP_IPV6_ADDRESS_TYPE = + "[a-fA-F]{4}\\:{1}[a-fA-F0-9]{4}\\:{1}[a-fA-F0-9]{4}\\:{1}" + + "[a-fA-F0-9]{4}\\:{1}[a-fA-F0-9]{4}\\:{1}[a-fA-F0-9]{4}\\:{1}" + + "[a-fA-F0-9]{4}\\:{1}[a-fA-F0-9]{4}"; + static final String REGEXP_IPV4_ADDRESS_TYPE = "[0-9]{1,3}\\.{1}[0-9]{1,3}\\.{1}" + + "[0-9]{1,3}\\.{1}[0-9]{1,3}"; + + /** + * The postfix strings of address. + */ + static final String STRING_PHONE_NUMBER_ADDRESS_TYPE = "/TYPE=PLMN"; + static final String STRING_IPV4_ADDRESS_TYPE = "/TYPE=IPV4"; + static final String STRING_IPV6_ADDRESS_TYPE = "/TYPE=IPV6"; + + /** + * Error values. + */ + static private final int PDU_COMPOSE_SUCCESS = 0; + static private final int PDU_COMPOSE_CONTENT_ERROR = 1; + static private final int PDU_COMPOSE_FIELD_NOT_SET = 2; + static private final int PDU_COMPOSE_FIELD_NOT_SUPPORTED = 3; + + /** + * WAP values defined in WSP spec. + */ + static private final int QUOTED_STRING_FLAG = 34; + static private final int END_STRING_FLAG = 0; + static private final int LENGTH_QUOTE = 31; + static private final int TEXT_MAX = 127; + static private final int SHORT_INTEGER_MAX = 127; + static private final int LONG_INTEGER_LENGTH_MAX = 8; + + /** + * Block size when read data from InputStream. + */ + static private final int PDU_COMPOSER_BLOCK_SIZE = 1024; + + /** + * The output message. + */ + protected ByteArrayOutputStream mMessage = null; + + /** + * The PDU. + */ + private GenericPdu mPdu = null; + + /** + * Current visiting position of the mMessage. + */ + protected int mPosition = 0; + + /** + * Message compose buffer stack. + */ + private BufferStack mStack = null; + + /** + * Content resolver. + */ + private final ContentResolver mResolver; + + /** + * Header of this pdu. + */ + private PduHeaders mPduHeader = null; + + /** + * Map of all content type + */ + private static HashMap mContentTypeMap = null; + + static { + mContentTypeMap = new HashMap(); + + int i; + for (i = 0; i < PduContentTypes.contentTypes.length; i++) { + mContentTypeMap.put(PduContentTypes.contentTypes[i], i); + } + } + + /** + * Constructor. + * + * @param context the context + * @param pdu the pdu to be composed + */ + public PduComposer(Context context, GenericPdu pdu) { + mPdu = pdu; + mResolver = context.getContentResolver(); + mPduHeader = pdu.getPduHeaders(); + mStack = new BufferStack(); + mMessage = new ByteArrayOutputStream(); + mPosition = 0; + } + + /** + * Make the message. No need to check whether mandatory fields are set, + * because the constructors of outgoing pdus are taking care of this. + * + * @return OutputStream of maked message. Return null if + * the PDU is invalid. + */ + public byte[] make() { + // Get Message-type. + int type = mPdu.getMessageType(); + + /* make the message */ + switch (type) { + case PduHeaders.MESSAGE_TYPE_SEND_REQ: + if (makeSendReqPdu() != PDU_COMPOSE_SUCCESS) { + return null; + } + break; + case PduHeaders.MESSAGE_TYPE_NOTIFYRESP_IND: + if (makeNotifyResp() != PDU_COMPOSE_SUCCESS) { + return null; + } + break; + case PduHeaders.MESSAGE_TYPE_ACKNOWLEDGE_IND: + if (makeAckInd() != PDU_COMPOSE_SUCCESS) { + return null; + } + break; + case PduHeaders.MESSAGE_TYPE_READ_REC_IND: + if (makeReadRecInd() != PDU_COMPOSE_SUCCESS) { + return null; + } + break; + default: + return null; + } + + return mMessage.toByteArray(); + } + + /** + * Copy buf to mMessage. + */ + protected void arraycopy(byte[] buf, int pos, int length) { + mMessage.write(buf, pos, length); + mPosition = mPosition + length; + } + + /** + * Append a byte to mMessage. + */ + protected void append(int value) { + mMessage.write(value); + mPosition ++; + } + + /** + * Append short integer value to mMessage. + * This implementation doesn't check the validity of parameter, since it + * assumes that the values are validated in the GenericPdu setter methods. + */ + protected void appendShortInteger(int value) { + /* + * From WAP-230-WSP-20010705-a: + * Short-integer = OCTET + * ; Integers in range 0-127 shall be encoded as a one octet value + * ; with the most significant bit set to one (1xxx xxxx) and with + * ; the value in the remaining least significant bits. + * In our implementation, only low 7 bits are stored and otherwise + * bits are ignored. + */ + append((value | 0x80) & 0xff); + } + + /** + * Append an octet number between 128 and 255 into mMessage. + * NOTE: + * A value between 0 and 127 should be appended by using appendShortInteger. + * This implementation doesn't check the validity of parameter, since it + * assumes that the values are validated in the GenericPdu setter methods. + */ + protected void appendOctet(int number) { + append(number); + } + + /** + * Append a short length into mMessage. + * This implementation doesn't check the validity of parameter, since it + * assumes that the values are validated in the GenericPdu setter methods. + */ + protected void appendShortLength(int value) { + /* + * From WAP-230-WSP-20010705-a: + * Short-length = + */ + append(value); + } + + /** + * Append long integer into mMessage. it's used for really long integers. + * This implementation doesn't check the validity of parameter, since it + * assumes that the values are validated in the GenericPdu setter methods. + */ + protected void appendLongInteger(long longInt) { + /* + * From WAP-230-WSP-20010705-a: + * Long-integer = Short-length Multi-octet-integer + * ; The Short-length indicates the length of the Multi-octet-integer + * Multi-octet-integer = 1*30 OCTET + * ; The content octets shall be an unsigned integer value with the + * ; most significant octet encoded first (big-endian representation). + * ; The minimum number of octets must be used to encode the value. + */ + int size; + long temp = longInt; + + // Count the length of the long integer. + for(size = 0; (temp != 0) && (size < LONG_INTEGER_LENGTH_MAX); size++) { + temp = (temp >>> 8); + } + + // Set Length. + appendShortLength(size); + + // Count and set the long integer. + int i; + int shift = (size -1) * 8; + + for (i = 0; i < size; i++) { + append((int)((longInt >>> shift) & 0xff)); + shift = shift - 8; + } + } + + /** + * Append text string into mMessage. + * This implementation doesn't check the validity of parameter, since it + * assumes that the values are validated in the GenericPdu setter methods. + */ + protected void appendTextString(byte[] text) { + /* + * From WAP-230-WSP-20010705-a: + * Text-string = [Quote] *TEXT End-of-string + * ; If the first character in the TEXT is in the range of 128-255, + * ; a Quote character must precede it. Otherwise the Quote character + * ;must be omitted. The Quote is not part of the contents. + */ + if (((text[0])&0xff) > TEXT_MAX) { // No need to check for <= 255 + append(TEXT_MAX); + } + + arraycopy(text, 0, text.length); + append(0); + } + + /** + * Append text string into mMessage. + * This implementation doesn't check the validity of parameter, since it + * assumes that the values are validated in the GenericPdu setter methods. + */ + protected void appendTextString(String str) { + /* + * From WAP-230-WSP-20010705-a: + * Text-string = [Quote] *TEXT End-of-string + * ; If the first character in the TEXT is in the range of 128-255, + * ; a Quote character must precede it. Otherwise the Quote character + * ;must be omitted. The Quote is not part of the contents. + */ + appendTextString(str.getBytes()); + } + + /** + * Append encoded string value to mMessage. + * This implementation doesn't check the validity of parameter, since it + * assumes that the values are validated in the GenericPdu setter methods. + */ + protected void appendEncodedString(EncodedStringValue enStr) { + /* + * From OMA-TS-MMS-ENC-V1_3-20050927-C: + * Encoded-string-value = Text-string | Value-length Char-set Text-string + */ + assert(enStr != null); + + int charset = enStr.getCharacterSet(); + byte[] textString = enStr.getTextString(); + if (null == textString) { + return; + } + + /* + * In the implementation of EncodedStringValue, the charset field will + * never be 0. It will always be composed as + * Encoded-string-value = Value-length Char-set Text-string + */ + mStack.newbuf(); + PositionMarker start = mStack.mark(); + + appendShortInteger(charset); + appendTextString(textString); + + int len = start.getLength(); + mStack.pop(); + appendValueLength(len); + mStack.copy(); + } + + /** + * Append uintvar integer into mMessage. + * This implementation doesn't check the validity of parameter, since it + * assumes that the values are validated in the GenericPdu setter methods. + */ + protected void appendUintvarInteger(long value) { + /* + * From WAP-230-WSP-20010705-a: + * To encode a large unsigned integer, split it into 7-bit fragments + * and place them in the payloads of multiple octets. The most significant + * bits are placed in the first octets with the least significant bits + * ending up in the last octet. All octets MUST set the Continue bit to 1 + * except the last octet, which MUST set the Continue bit to 0. + */ + int i; + long max = SHORT_INTEGER_MAX; + + for (i = 0; i < 5; i++) { + if (value < max) { + break; + } + + max = (max << 7) | 0x7fl; + } + + while(i > 0) { + long temp = value >>> (i * 7); + temp = temp & 0x7f; + + append((int)((temp | 0x80) & 0xff)); + + i--; + } + + append((int)(value & 0x7f)); + } + + /** + * Append date value into mMessage. + * This implementation doesn't check the validity of parameter, since it + * assumes that the values are validated in the GenericPdu setter methods. + */ + protected void appendDateValue(long date) { + /* + * From OMA-TS-MMS-ENC-V1_3-20050927-C: + * Date-value = Long-integer + */ + appendLongInteger(date); + } + + /** + * Append value length to mMessage. + * This implementation doesn't check the validity of parameter, since it + * assumes that the values are validated in the GenericPdu setter methods. + */ + protected void appendValueLength(long value) { + /* + * From WAP-230-WSP-20010705-a: + * Value-length = Short-length | (Length-quote Length) + * ; Value length is used to indicate the length of the value to follow + * Short-length = + * Length-quote = + * Length = Uintvar-integer + */ + if (value < LENGTH_QUOTE) { + appendShortLength((int) value); + return; + } + + append(LENGTH_QUOTE); + appendUintvarInteger(value); + } + + /** + * Append quoted string to mMessage. + * This implementation doesn't check the validity of parameter, since it + * assumes that the values are validated in the GenericPdu setter methods. + */ + protected void appendQuotedString(byte[] text) { + /* + * From WAP-230-WSP-20010705-a: + * Quoted-string = *TEXT End-of-string + * ;The TEXT encodes an RFC2616 Quoted-string with the enclosing + * ;quotation-marks <"> removed. + */ + append(QUOTED_STRING_FLAG); + arraycopy(text, 0, text.length); + append(END_STRING_FLAG); + } + + /** + * Append quoted string to mMessage. + * This implementation doesn't check the validity of parameter, since it + * assumes that the values are validated in the GenericPdu setter methods. + */ + protected void appendQuotedString(String str) { + /* + * From WAP-230-WSP-20010705-a: + * Quoted-string = *TEXT End-of-string + * ;The TEXT encodes an RFC2616 Quoted-string with the enclosing + * ;quotation-marks <"> removed. + */ + appendQuotedString(str.getBytes()); + } + + private EncodedStringValue appendAddressType(EncodedStringValue address) { + EncodedStringValue temp = null; + + try { + int addressType = checkAddressType(address.getString()); + temp = EncodedStringValue.copy(address); + if (PDU_PHONE_NUMBER_ADDRESS_TYPE == addressType) { + // Phone number. + temp.appendTextString(STRING_PHONE_NUMBER_ADDRESS_TYPE.getBytes()); + } else if (PDU_IPV4_ADDRESS_TYPE == addressType) { + // Ipv4 address. + temp.appendTextString(STRING_IPV4_ADDRESS_TYPE.getBytes()); + } else if (PDU_IPV6_ADDRESS_TYPE == addressType) { + // Ipv6 address. + temp.appendTextString(STRING_IPV6_ADDRESS_TYPE.getBytes()); + } + } catch (NullPointerException e) { + return null; + } + + return temp; + } + + /** + * Append header to mMessage. + */ + private int appendHeader(int field) { + switch (field) { + case PduHeaders.MMS_VERSION: + appendOctet(field); + + int version = mPduHeader.getOctet(field); + if (0 == version) { + appendShortInteger(PduHeaders.CURRENT_MMS_VERSION); + } else { + appendShortInteger(version); + } + + break; + + case PduHeaders.MESSAGE_ID: + case PduHeaders.TRANSACTION_ID: + byte[] textString = mPduHeader.getTextString(field); + if (null == textString) { + return PDU_COMPOSE_FIELD_NOT_SET; + } + + appendOctet(field); + appendTextString(textString); + break; + + case PduHeaders.TO: + case PduHeaders.BCC: + case PduHeaders.CC: + EncodedStringValue[] addr = mPduHeader.getEncodedStringValues(field); + + if (null == addr) { + return PDU_COMPOSE_FIELD_NOT_SET; + } + + EncodedStringValue temp; + for (int i = 0; i < addr.length; i++) { + temp = appendAddressType(addr[i]); + if (temp == null) { + return PDU_COMPOSE_CONTENT_ERROR; + } + + appendOctet(field); + appendEncodedString(temp); + } + break; + + case PduHeaders.FROM: + // Value-length (Address-present-token Encoded-string-value | Insert-address-token) + appendOctet(field); + + EncodedStringValue from = mPduHeader.getEncodedStringValue(field); + if ((from == null) + || TextUtils.isEmpty(from.getString()) + || new String(from.getTextString()).equals( + PduHeaders.FROM_INSERT_ADDRESS_TOKEN_STR)) { + // Length of from = 1 + append(1); + // Insert-address-token = + append(PduHeaders.FROM_INSERT_ADDRESS_TOKEN); + } else { + mStack.newbuf(); + PositionMarker fstart = mStack.mark(); + + // Address-present-token = + append(PduHeaders.FROM_ADDRESS_PRESENT_TOKEN); + + temp = appendAddressType(from); + if (temp == null) { + return PDU_COMPOSE_CONTENT_ERROR; + } + + appendEncodedString(temp); + + int flen = fstart.getLength(); + mStack.pop(); + appendValueLength(flen); + mStack.copy(); + } + break; + + case PduHeaders.READ_STATUS: + case PduHeaders.STATUS: + case PduHeaders.REPORT_ALLOWED: + case PduHeaders.PRIORITY: + case PduHeaders.DELIVERY_REPORT: + case PduHeaders.READ_REPORT: + int octet = mPduHeader.getOctet(field); + if (0 == octet) { + return PDU_COMPOSE_FIELD_NOT_SET; + } + + appendOctet(field); + appendOctet(octet); + break; + + case PduHeaders.DATE: + long date = mPduHeader.getLongInteger(field); + if (-1 == date) { + return PDU_COMPOSE_FIELD_NOT_SET; + } + + appendOctet(field); + appendDateValue(date); + break; + + case PduHeaders.SUBJECT: + EncodedStringValue enString = + mPduHeader.getEncodedStringValue(field); + if (null == enString) { + return PDU_COMPOSE_FIELD_NOT_SET; + } + + appendOctet(field); + appendEncodedString(enString); + break; + + case PduHeaders.MESSAGE_CLASS: + byte[] messageClass = mPduHeader.getTextString(field); + if (null == messageClass) { + return PDU_COMPOSE_FIELD_NOT_SET; + } + + appendOctet(field); + if (Arrays.equals(messageClass, + PduHeaders.MESSAGE_CLASS_ADVERTISEMENT_STR.getBytes())) { + appendOctet(PduHeaders.MESSAGE_CLASS_ADVERTISEMENT); + } else if (Arrays.equals(messageClass, + PduHeaders.MESSAGE_CLASS_AUTO_STR.getBytes())) { + appendOctet(PduHeaders.MESSAGE_CLASS_AUTO); + } else if (Arrays.equals(messageClass, + PduHeaders.MESSAGE_CLASS_PERSONAL_STR.getBytes())) { + appendOctet(PduHeaders.MESSAGE_CLASS_PERSONAL); + } else if (Arrays.equals(messageClass, + PduHeaders.MESSAGE_CLASS_INFORMATIONAL_STR.getBytes())) { + appendOctet(PduHeaders.MESSAGE_CLASS_INFORMATIONAL); + } else { + appendTextString(messageClass); + } + break; + + case PduHeaders.EXPIRY: + long expiry = mPduHeader.getLongInteger(field); + if (-1 == expiry) { + return PDU_COMPOSE_FIELD_NOT_SET; + } + + appendOctet(field); + + mStack.newbuf(); + PositionMarker expiryStart = mStack.mark(); + + append(PduHeaders.VALUE_RELATIVE_TOKEN); + appendLongInteger(expiry); + + int expiryLength = expiryStart.getLength(); + mStack.pop(); + appendValueLength(expiryLength); + mStack.copy(); + break; + + default: + return PDU_COMPOSE_FIELD_NOT_SUPPORTED; + } + + return PDU_COMPOSE_SUCCESS; + } + + /** + * Make ReadRec.Ind. + */ + private int makeReadRecInd() { + if (mMessage == null) { + mMessage = new ByteArrayOutputStream(); + mPosition = 0; + } + + // X-Mms-Message-Type + appendOctet(PduHeaders.MESSAGE_TYPE); + appendOctet(PduHeaders.MESSAGE_TYPE_READ_REC_IND); + + // X-Mms-MMS-Version + if (appendHeader(PduHeaders.MMS_VERSION) != PDU_COMPOSE_SUCCESS) { + return PDU_COMPOSE_CONTENT_ERROR; + } + + // Message-ID + if (appendHeader(PduHeaders.MESSAGE_ID) != PDU_COMPOSE_SUCCESS) { + return PDU_COMPOSE_CONTENT_ERROR; + } + + // To + if (appendHeader(PduHeaders.TO) != PDU_COMPOSE_SUCCESS) { + return PDU_COMPOSE_CONTENT_ERROR; + } + + // From + if (appendHeader(PduHeaders.FROM) != PDU_COMPOSE_SUCCESS) { + return PDU_COMPOSE_CONTENT_ERROR; + } + + // Date Optional + appendHeader(PduHeaders.DATE); + + // X-Mms-Read-Status + if (appendHeader(PduHeaders.READ_STATUS) != PDU_COMPOSE_SUCCESS) { + return PDU_COMPOSE_CONTENT_ERROR; + } + + // X-Mms-Applic-ID Optional(not support) + // X-Mms-Reply-Applic-ID Optional(not support) + // X-Mms-Aux-Applic-Info Optional(not support) + + return PDU_COMPOSE_SUCCESS; + } + + /** + * Make NotifyResp.Ind. + */ + private int makeNotifyResp() { + if (mMessage == null) { + mMessage = new ByteArrayOutputStream(); + mPosition = 0; + } + + // X-Mms-Message-Type + appendOctet(PduHeaders.MESSAGE_TYPE); + appendOctet(PduHeaders.MESSAGE_TYPE_NOTIFYRESP_IND); + + // X-Mms-Transaction-ID + if (appendHeader(PduHeaders.TRANSACTION_ID) != PDU_COMPOSE_SUCCESS) { + return PDU_COMPOSE_CONTENT_ERROR; + } + + // X-Mms-MMS-Version + if (appendHeader(PduHeaders.MMS_VERSION) != PDU_COMPOSE_SUCCESS) { + return PDU_COMPOSE_CONTENT_ERROR; + } + + // X-Mms-Status + if (appendHeader(PduHeaders.STATUS) != PDU_COMPOSE_SUCCESS) { + return PDU_COMPOSE_CONTENT_ERROR; + } + + // X-Mms-Report-Allowed Optional (not support) + return PDU_COMPOSE_SUCCESS; + } + + /** + * Make Acknowledge.Ind. + */ + private int makeAckInd() { + if (mMessage == null) { + mMessage = new ByteArrayOutputStream(); + mPosition = 0; + } + + // X-Mms-Message-Type + appendOctet(PduHeaders.MESSAGE_TYPE); + appendOctet(PduHeaders.MESSAGE_TYPE_ACKNOWLEDGE_IND); + + // X-Mms-Transaction-ID + if (appendHeader(PduHeaders.TRANSACTION_ID) != PDU_COMPOSE_SUCCESS) { + return PDU_COMPOSE_CONTENT_ERROR; + } + + // X-Mms-MMS-Version + if (appendHeader(PduHeaders.MMS_VERSION) != PDU_COMPOSE_SUCCESS) { + return PDU_COMPOSE_CONTENT_ERROR; + } + + // X-Mms-Report-Allowed Optional + appendHeader(PduHeaders.REPORT_ALLOWED); + + return PDU_COMPOSE_SUCCESS; + } + + /** + * Make Send.req. + */ + private int makeSendReqPdu() { + if (mMessage == null) { + mMessage = new ByteArrayOutputStream(); + mPosition = 0; + } + + // X-Mms-Message-Type + appendOctet(PduHeaders.MESSAGE_TYPE); + appendOctet(PduHeaders.MESSAGE_TYPE_SEND_REQ); + + // X-Mms-Transaction-ID + appendOctet(PduHeaders.TRANSACTION_ID); + + byte[] trid = mPduHeader.getTextString(PduHeaders.TRANSACTION_ID); + if (trid == null) { + // Transaction-ID should be set(by Transaction) before make(). + throw new IllegalArgumentException("Transaction-ID is null."); + } + appendTextString(trid); + + // X-Mms-MMS-Version + if (appendHeader(PduHeaders.MMS_VERSION) != PDU_COMPOSE_SUCCESS) { + return PDU_COMPOSE_CONTENT_ERROR; + } + + // Date Date-value Optional. + appendHeader(PduHeaders.DATE); + + // From + if (appendHeader(PduHeaders.FROM) != PDU_COMPOSE_SUCCESS) { + return PDU_COMPOSE_CONTENT_ERROR; + } + + boolean recipient = false; + + // To + if (appendHeader(PduHeaders.TO) != PDU_COMPOSE_CONTENT_ERROR) { + recipient = true; + } + + // Cc + if (appendHeader(PduHeaders.CC) != PDU_COMPOSE_CONTENT_ERROR) { + recipient = true; + } + + // Bcc + if (appendHeader(PduHeaders.BCC) != PDU_COMPOSE_CONTENT_ERROR) { + recipient = true; + } + + // Need at least one of "cc", "bcc" and "to". + if (false == recipient) { + return PDU_COMPOSE_CONTENT_ERROR; + } + + // Subject Optional + appendHeader(PduHeaders.SUBJECT); + + // X-Mms-Message-Class Optional + // Message-class-value = Class-identifier | Token-text + appendHeader(PduHeaders.MESSAGE_CLASS); + + // X-Mms-Expiry Optional + appendHeader(PduHeaders.EXPIRY); + + // X-Mms-Priority Optional + appendHeader(PduHeaders.PRIORITY); + + // X-Mms-Delivery-Report Optional + appendHeader(PduHeaders.DELIVERY_REPORT); + + // X-Mms-Read-Report Optional + appendHeader(PduHeaders.READ_REPORT); + + // Content-Type + appendOctet(PduHeaders.CONTENT_TYPE); + + // Message body + makeMessageBody(); + + return PDU_COMPOSE_SUCCESS; // Composing the message is OK + } + + /** + * Make message body. + */ + private int makeMessageBody() { + // 1. add body informations + mStack.newbuf(); // Switching buffer because we need to + + PositionMarker ctStart = mStack.mark(); + + // This contentTypeIdentifier should be used for type of attachment... + String contentType = new String(mPduHeader.getTextString(PduHeaders.CONTENT_TYPE)); + Integer contentTypeIdentifier = mContentTypeMap.get(contentType); + if (contentTypeIdentifier == null) { + // content type is mandatory + return PDU_COMPOSE_CONTENT_ERROR; + } + + appendShortInteger(contentTypeIdentifier.intValue()); + + // content-type parameter: start + PduBody body = ((SendReq) mPdu).getBody(); + if (null == body || body.getPartsNum() == 0) { + // empty message + appendUintvarInteger(0); + mStack.pop(); + mStack.copy(); + return PDU_COMPOSE_SUCCESS; + } + + PduPart part; + try { + part = body.getPart(0); + + byte[] start = part.getContentId(); + if (start != null) { + appendOctet(PduPart.P_DEP_START); + if (('<' == start[0]) && ('>' == start[start.length - 1])) { + appendTextString(start); + } else { + appendTextString("<" + new String(start) + ">"); + } + } + + // content-type parameter: type + appendOctet(PduPart.P_CT_MR_TYPE); + appendTextString(part.getContentType()); + } + catch (ArrayIndexOutOfBoundsException e){ + e.printStackTrace(); + } + + int ctLength = ctStart.getLength(); + mStack.pop(); + appendValueLength(ctLength); + mStack.copy(); + + // 3. add content + int partNum = body.getPartsNum(); + appendUintvarInteger(partNum); + for (int i = 0; i < partNum; i++) { + part = body.getPart(i); + mStack.newbuf(); // Leaving space for header lengh and data length + PositionMarker attachment = mStack.mark(); + + mStack.newbuf(); // Leaving space for Content-Type length + PositionMarker contentTypeBegin = mStack.mark(); + + byte[] partContentType = part.getContentType(); + + if (partContentType == null) { + // content type is mandatory + return PDU_COMPOSE_CONTENT_ERROR; + } + + // content-type value + Integer partContentTypeIdentifier = + mContentTypeMap.get(new String(partContentType)); + if (partContentTypeIdentifier == null) { + appendTextString(partContentType); + } else { + appendShortInteger(partContentTypeIdentifier.intValue()); + } + + /* Content-type parameter : name. + * The value of name, filename, content-location is the same. + * Just one of them is enough for this PDU. + */ + byte[] name = part.getName(); + + if (null == name) { + name = part.getFilename(); + + if (null == name) { + name = part.getContentLocation(); + + if (null == name) { + /* at lease one of name, filename, Content-location + * should be available. + */ + return PDU_COMPOSE_CONTENT_ERROR; + } + } + } + appendOctet(PduPart.P_DEP_NAME); + appendTextString(name); + + // content-type parameter : charset + int charset = part.getCharset(); + if (charset != 0) { + appendOctet(PduPart.P_CHARSET); + appendShortInteger(charset); + } + + int contentTypeLength = contentTypeBegin.getLength(); + mStack.pop(); + appendValueLength(contentTypeLength); + mStack.copy(); + + // content id + byte[] contentId = part.getContentId(); + + if (null != contentId) { + appendOctet(PduPart.P_CONTENT_ID); + if (('<' == contentId[0]) && ('>' == contentId[contentId.length - 1])) { + appendQuotedString(contentId); + } else { + appendQuotedString("<" + new String(contentId) + ">"); + } + } + + // content-location + byte[] contentLocation = part.getContentLocation(); + if (null != contentLocation) { + appendOctet(PduPart.P_CONTENT_LOCATION); + appendTextString(contentLocation); + } + + // content + int headerLength = attachment.getLength(); + + int dataLength = 0; // Just for safety... + byte[] partData = part.getData(); + + if (partData != null) { + arraycopy(partData, 0, partData.length); + dataLength = partData.length; + } else { + InputStream cr; + try { + byte[] buffer = new byte[PDU_COMPOSER_BLOCK_SIZE]; + cr = mResolver.openInputStream(part.getDataUri()); + int len = 0; + while ((len = cr.read(buffer)) != -1) { + mMessage.write(buffer, 0, len); + mPosition += len; + dataLength += len; + } + } catch (FileNotFoundException e) { + return PDU_COMPOSE_CONTENT_ERROR; + } catch (IOException e) { + return PDU_COMPOSE_CONTENT_ERROR; + } catch (RuntimeException e) { + return PDU_COMPOSE_CONTENT_ERROR; + } + } + + if (dataLength != (attachment.getLength() - headerLength)) { + throw new RuntimeException("BUG: Length sanity check failed"); + } + + mStack.pop(); + appendUintvarInteger(headerLength); + appendUintvarInteger(dataLength); + mStack.copy(); + } + + return PDU_COMPOSE_SUCCESS; + } + + /** + * Record current message informations. + */ + static private class LengthRecordNode { + ByteArrayOutputStream currentMessage = null; + public int currentPosition = 0; + + public LengthRecordNode next = null; + } + + /** + * Mark current message position and stact size. + */ + private class PositionMarker { + private int c_pos; // Current position + private int currentStackSize; // Current stack size + + int getLength() { + // If these assert fails, likely that you are finding the + // size of buffer that is deep in BufferStack you can only + // find the length of the buffer that is on top + if (currentStackSize != mStack.stackSize) { + throw new RuntimeException("BUG: Invalid call to getLength()"); + } + + return mPosition - c_pos; + } + } + + /** + * This implementation can be OPTIMIZED to use only + * 2 buffers. This optimization involves changing BufferStack + * only... Its usage (interface) will not change. + */ + private class BufferStack { + private LengthRecordNode stack = null; + private LengthRecordNode toCopy = null; + + int stackSize = 0; + + /** + * Create a new message buffer and push it into the stack. + */ + void newbuf() { + // You can't create a new buff when toCopy != null + // That is after calling pop() and before calling copy() + // If you do, it is a bug + if (toCopy != null) { + throw new RuntimeException("BUG: Invalid newbuf() before copy()"); + } + + LengthRecordNode temp = new LengthRecordNode(); + + temp.currentMessage = mMessage; + temp.currentPosition = mPosition; + + temp.next = stack; + stack = temp; + + stackSize = stackSize + 1; + + mMessage = new ByteArrayOutputStream(); + mPosition = 0; + } + + /** + * Pop the message before and record current message in the stack. + */ + void pop() { + ByteArrayOutputStream currentMessage = mMessage; + int currentPosition = mPosition; + + mMessage = stack.currentMessage; + mPosition = stack.currentPosition; + + toCopy = stack; + // Re using the top element of the stack to avoid memory allocation + + stack = stack.next; + stackSize = stackSize - 1; + + toCopy.currentMessage = currentMessage; + toCopy.currentPosition = currentPosition; + } + + /** + * Append current message to the message before. + */ + void copy() { + arraycopy(toCopy.currentMessage.toByteArray(), 0, + toCopy.currentPosition); + + toCopy = null; + } + + /** + * Mark current message position + */ + PositionMarker mark() { + PositionMarker m = new PositionMarker(); + + m.c_pos = mPosition; + m.currentStackSize = stackSize; + + return m; + } + } + + /** + * Check address type. + * + * @param address address string without the postfix stinng type, + * such as "/TYPE=PLMN", "/TYPE=IPv6" and "/TYPE=IPv4" + * @return PDU_PHONE_NUMBER_ADDRESS_TYPE if it is phone number, + * PDU_EMAIL_ADDRESS_TYPE if it is email address, + * PDU_IPV4_ADDRESS_TYPE if it is ipv4 address, + * PDU_IPV6_ADDRESS_TYPE if it is ipv6 address, + * PDU_UNKNOWN_ADDRESS_TYPE if it is unknown. + */ + protected static int checkAddressType(String address) { + /** + * From OMA-TS-MMS-ENC-V1_3-20050927-C.pdf, section 8. + * address = ( e-mail / device-address / alphanum-shortcode / num-shortcode) + * e-mail = mailbox; to the definition of mailbox as described in + * section 3.4 of [RFC2822], but excluding the + * obsolete definitions as indicated by the "obs-" prefix. + * device-address = ( global-phone-number "/TYPE=PLMN" ) + * / ( ipv4 "/TYPE=IPv4" ) / ( ipv6 "/TYPE=IPv6" ) + * / ( escaped-value "/TYPE=" address-type ) + * + * global-phone-number = ["+"] 1*( DIGIT / written-sep ) + * written-sep =("-"/".") + * + * ipv4 = 1*3DIGIT 3( "." 1*3DIGIT ) ; IPv4 address value + * + * ipv6 = 4HEXDIG 7( ":" 4HEXDIG ) ; IPv6 address per RFC 2373 + */ + + if (null == address) { + return PDU_UNKNOWN_ADDRESS_TYPE; + } + + if (address.matches(REGEXP_IPV4_ADDRESS_TYPE)) { + // Ipv4 address. + return PDU_IPV4_ADDRESS_TYPE; + }else if (address.matches(REGEXP_PHONE_NUMBER_ADDRESS_TYPE)) { + // Phone number. + return PDU_PHONE_NUMBER_ADDRESS_TYPE; + } else if (address.matches(REGEXP_EMAIL_ADDRESS_TYPE)) { + // Email address. + return PDU_EMAIL_ADDRESS_TYPE; + } else if (address.matches(REGEXP_IPV6_ADDRESS_TYPE)) { + // Ipv6 address. + return PDU_IPV6_ADDRESS_TYPE; + } else { + // Unknown address. + return PDU_UNKNOWN_ADDRESS_TYPE; + } + } +} diff --git a/mms-common/java/com/android/common/mms/pdu/PduContentTypes.java b/mms-common/java/com/android/common/mms/pdu/PduContentTypes.java new file mode 100644 index 000000000..3f971fd98 --- /dev/null +++ b/mms-common/java/com/android/common/mms/pdu/PduContentTypes.java @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2007 Esmertec AG. + * 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. + */ + +package com.android.mmscommon.mms.pdu; + +public class PduContentTypes { + /** + * All content types. From: + * http://www.openmobilealliance.org/tech/omna/omna-wsp-content-type.htm + */ + static final String[] contentTypes = { + "*/*", /* 0x00 */ + "text/*", /* 0x01 */ + "text/html", /* 0x02 */ + "text/plain", /* 0x03 */ + "text/x-hdml", /* 0x04 */ + "text/x-ttml", /* 0x05 */ + "text/x-vCalendar", /* 0x06 */ + "text/x-vCard", /* 0x07 */ + "text/vnd.wap.wml", /* 0x08 */ + "text/vnd.wap.wmlscript", /* 0x09 */ + "text/vnd.wap.wta-event", /* 0x0A */ + "multipart/*", /* 0x0B */ + "multipart/mixed", /* 0x0C */ + "multipart/form-data", /* 0x0D */ + "multipart/byterantes", /* 0x0E */ + "multipart/alternative", /* 0x0F */ + "application/*", /* 0x10 */ + "application/java-vm", /* 0x11 */ + "application/x-www-form-urlencoded", /* 0x12 */ + "application/x-hdmlc", /* 0x13 */ + "application/vnd.wap.wmlc", /* 0x14 */ + "application/vnd.wap.wmlscriptc", /* 0x15 */ + "application/vnd.wap.wta-eventc", /* 0x16 */ + "application/vnd.wap.uaprof", /* 0x17 */ + "application/vnd.wap.wtls-ca-certificate", /* 0x18 */ + "application/vnd.wap.wtls-user-certificate", /* 0x19 */ + "application/x-x509-ca-cert", /* 0x1A */ + "application/x-x509-user-cert", /* 0x1B */ + "image/*", /* 0x1C */ + "image/gif", /* 0x1D */ + "image/jpeg", /* 0x1E */ + "image/tiff", /* 0x1F */ + "image/png", /* 0x20 */ + "image/vnd.wap.wbmp", /* 0x21 */ + "application/vnd.wap.multipart.*", /* 0x22 */ + "application/vnd.wap.multipart.mixed", /* 0x23 */ + "application/vnd.wap.multipart.form-data", /* 0x24 */ + "application/vnd.wap.multipart.byteranges", /* 0x25 */ + "application/vnd.wap.multipart.alternative", /* 0x26 */ + "application/xml", /* 0x27 */ + "text/xml", /* 0x28 */ + "application/vnd.wap.wbxml", /* 0x29 */ + "application/x-x968-cross-cert", /* 0x2A */ + "application/x-x968-ca-cert", /* 0x2B */ + "application/x-x968-user-cert", /* 0x2C */ + "text/vnd.wap.si", /* 0x2D */ + "application/vnd.wap.sic", /* 0x2E */ + "text/vnd.wap.sl", /* 0x2F */ + "application/vnd.wap.slc", /* 0x30 */ + "text/vnd.wap.co", /* 0x31 */ + "application/vnd.wap.coc", /* 0x32 */ + "application/vnd.wap.multipart.related", /* 0x33 */ + "application/vnd.wap.sia", /* 0x34 */ + "text/vnd.wap.connectivity-xml", /* 0x35 */ + "application/vnd.wap.connectivity-wbxml", /* 0x36 */ + "application/pkcs7-mime", /* 0x37 */ + "application/vnd.wap.hashed-certificate", /* 0x38 */ + "application/vnd.wap.signed-certificate", /* 0x39 */ + "application/vnd.wap.cert-response", /* 0x3A */ + "application/xhtml+xml", /* 0x3B */ + "application/wml+xml", /* 0x3C */ + "text/css", /* 0x3D */ + "application/vnd.wap.mms-message", /* 0x3E */ + "application/vnd.wap.rollover-certificate", /* 0x3F */ + "application/vnd.wap.locc+wbxml", /* 0x40 */ + "application/vnd.wap.loc+xml", /* 0x41 */ + "application/vnd.syncml.dm+wbxml", /* 0x42 */ + "application/vnd.syncml.dm+xml", /* 0x43 */ + "application/vnd.syncml.notification", /* 0x44 */ + "application/vnd.wap.xhtml+xml", /* 0x45 */ + "application/vnd.wv.csp.cir", /* 0x46 */ + "application/vnd.oma.dd+xml", /* 0x47 */ + "application/vnd.oma.drm.message", /* 0x48 */ + "application/vnd.oma.drm.content", /* 0x49 */ + "application/vnd.oma.drm.rights+xml", /* 0x4A */ + "application/vnd.oma.drm.rights+wbxml", /* 0x4B */ + "application/vnd.wv.csp+xml", /* 0x4C */ + "application/vnd.wv.csp+wbxml", /* 0x4D */ + "application/vnd.syncml.ds.notification", /* 0x4E */ + "audio/*", /* 0x4F */ + "video/*", /* 0x50 */ + "application/vnd.oma.dd2+xml", /* 0x51 */ + "application/mikey" /* 0x52 */ + }; +} diff --git a/mms-common/java/com/android/common/mms/pdu/PduParser.java b/mms-common/java/com/android/common/mms/pdu/PduParser.java new file mode 100644 index 000000000..9253f8373 --- /dev/null +++ b/mms-common/java/com/android/common/mms/pdu/PduParser.java @@ -0,0 +1,1873 @@ +/* + * Copyright (C) 2007-2008 Esmertec AG. + * Copyright (C) 2007-2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.mmscommon.mms.pdu; + +import com.android.mmscommon.ContentType; +import com.android.mmscommon.CharacterSets; +import com.android.mmscommon.EncodedStringValue; +import com.android.mmscommon.InvalidHeaderValueException; +import com.android.mmscommon.PduHeaders; + +import android.util.Config; +import android.util.Log; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.UnsupportedEncodingException; +import java.util.Arrays; +import java.util.HashMap; + +public class PduParser { + /** + * The next are WAP values defined in WSP specification. + */ + private static final int QUOTE = 127; + private static final int LENGTH_QUOTE = 31; + private static final int TEXT_MIN = 32; + private static final int TEXT_MAX = 127; + private static final int SHORT_INTEGER_MAX = 127; + private static final int SHORT_LENGTH_MAX = 30; + private static final int LONG_INTEGER_LENGTH_MAX = 8; + private static final int QUOTED_STRING_FLAG = 34; + private static final int END_STRING_FLAG = 0x00; + //The next two are used by the interface "parseWapString" to + //distinguish Text-String and Quoted-String. + private static final int TYPE_TEXT_STRING = 0; + private static final int TYPE_QUOTED_STRING = 1; + private static final int TYPE_TOKEN_STRING = 2; + + /** + * Specify the part position. + */ + private static final int THE_FIRST_PART = 0; + private static final int THE_LAST_PART = 1; + + /** + * The pdu data. + */ + private ByteArrayInputStream mPduDataStream = null; + + /** + * Store pdu headers + */ + private PduHeaders mHeaders = null; + + /** + * Store pdu parts. + */ + private PduBody mBody = null; + + /** + * Store the "type" parameter in "Content-Type" header field. + */ + private static byte[] mTypeParam = null; + + /** + * Store the "start" parameter in "Content-Type" header field. + */ + private static byte[] mStartParam = null; + + /** + * The log tag. + */ + private static final String LOG_TAG = "PduParser"; + private static final boolean DEBUG = false; + private static final boolean LOCAL_LOGV = DEBUG ? Config.LOGD : Config.LOGV; + + /** + * Constructor. + * + * @param pduDataStream pdu data to be parsed + */ + public PduParser(byte[] pduDataStream) { + mPduDataStream = new ByteArrayInputStream(pduDataStream); + } + + /** + * Parse the pdu. + * + * @return the pdu structure if parsing successfully. + * null if parsing error happened or mandatory fields are not set. + */ + public GenericPdu parse(){ + if (mPduDataStream == null) { + return null; + } + + /* parse headers */ + mHeaders = parseHeaders(mPduDataStream); + if (null == mHeaders) { + // Parse headers failed. + return null; + } + + /* get the message type */ + int messageType = mHeaders.getOctet(PduHeaders.MESSAGE_TYPE); + + /* check mandatory header fields */ + if (false == checkMandatoryHeader(mHeaders)) { + log("check mandatory headers failed!"); + return null; + } + + if ((PduHeaders.MESSAGE_TYPE_SEND_REQ == messageType) || + (PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF == messageType)) { + /* need to parse the parts */ + mBody = parseParts(mPduDataStream); + if (null == mBody) { + // Parse parts failed. + return null; + } + } + + switch (messageType) { + case PduHeaders.MESSAGE_TYPE_SEND_REQ: + SendReq sendReq = new SendReq(mHeaders, mBody); + return sendReq; + case PduHeaders.MESSAGE_TYPE_SEND_CONF: + SendConf sendConf = new SendConf(mHeaders); + return sendConf; + case PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND: + NotificationInd notificationInd = + new NotificationInd(mHeaders); + return notificationInd; + case PduHeaders.MESSAGE_TYPE_NOTIFYRESP_IND: + NotifyRespInd notifyRespInd = + new NotifyRespInd(mHeaders); + return notifyRespInd; + case PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF: + RetrieveConf retrieveConf = + new RetrieveConf(mHeaders, mBody); + + byte[] contentType = retrieveConf.getContentType(); + if (null == contentType) { + return null; + } + String ctTypeStr = new String(contentType); + if (ctTypeStr.equals(ContentType.MULTIPART_MIXED) + || ctTypeStr.equals(ContentType.MULTIPART_RELATED) + || ctTypeStr.equals(ContentType.MULTIPART_ALTERNATIVE)) { + // The MMS content type must be "application/vnd.wap.multipart.mixed" + // or "application/vnd.wap.multipart.related" + // or "application/vnd.wap.multipart.alternative" + return retrieveConf; + } + return null; + case PduHeaders.MESSAGE_TYPE_DELIVERY_IND: + DeliveryInd deliveryInd = + new DeliveryInd(mHeaders); + return deliveryInd; + case PduHeaders.MESSAGE_TYPE_ACKNOWLEDGE_IND: + AcknowledgeInd acknowledgeInd = + new AcknowledgeInd(mHeaders); + return acknowledgeInd; + case PduHeaders.MESSAGE_TYPE_READ_ORIG_IND: + ReadOrigInd readOrigInd = + new ReadOrigInd(mHeaders); + return readOrigInd; + case PduHeaders.MESSAGE_TYPE_READ_REC_IND: + ReadRecInd readRecInd = + new ReadRecInd(mHeaders); + return readRecInd; + default: + log("Parser doesn't support this message type in this version!"); + return null; + } + } + + /** + * Parse pdu headers. + * + * @param pduDataStream pdu data input stream + * @return headers in PduHeaders structure, null when parse fail + */ + protected PduHeaders parseHeaders(ByteArrayInputStream pduDataStream){ + if (pduDataStream == null) { + return null; + } + + boolean keepParsing = true; + PduHeaders headers = new PduHeaders(); + + while (keepParsing && (pduDataStream.available() > 0)) { + int headerField = extractByteValue(pduDataStream); + switch (headerField) { + case PduHeaders.MESSAGE_TYPE: + { + int messageType = extractByteValue(pduDataStream); + switch (messageType) { + // We don't support these kind of messages now. + case PduHeaders.MESSAGE_TYPE_FORWARD_REQ: + case PduHeaders.MESSAGE_TYPE_FORWARD_CONF: + case PduHeaders.MESSAGE_TYPE_MBOX_STORE_REQ: + case PduHeaders.MESSAGE_TYPE_MBOX_STORE_CONF: + case PduHeaders.MESSAGE_TYPE_MBOX_VIEW_REQ: + case PduHeaders.MESSAGE_TYPE_MBOX_VIEW_CONF: + case PduHeaders.MESSAGE_TYPE_MBOX_UPLOAD_REQ: + case PduHeaders.MESSAGE_TYPE_MBOX_UPLOAD_CONF: + case PduHeaders.MESSAGE_TYPE_MBOX_DELETE_REQ: + case PduHeaders.MESSAGE_TYPE_MBOX_DELETE_CONF: + case PduHeaders.MESSAGE_TYPE_MBOX_DESCR: + case PduHeaders.MESSAGE_TYPE_DELETE_REQ: + case PduHeaders.MESSAGE_TYPE_DELETE_CONF: + case PduHeaders.MESSAGE_TYPE_CANCEL_REQ: + case PduHeaders.MESSAGE_TYPE_CANCEL_CONF: + return null; + } + try { + headers.setOctet(messageType, headerField); + } catch(InvalidHeaderValueException e) { + log("Set invalid Octet value: " + messageType + + " into the header filed: " + headerField); + return null; + } catch(RuntimeException e) { + log(headerField + "is not Octet header field!"); + return null; + } + break; + } + /* Octect value */ + case PduHeaders.REPORT_ALLOWED: + case PduHeaders.ADAPTATION_ALLOWED: + case PduHeaders.DELIVERY_REPORT: + case PduHeaders.DRM_CONTENT: + case PduHeaders.DISTRIBUTION_INDICATOR: + case PduHeaders.QUOTAS: + case PduHeaders.READ_REPORT: + case PduHeaders.STORE: + case PduHeaders.STORED: + case PduHeaders.TOTALS: + case PduHeaders.SENDER_VISIBILITY: + case PduHeaders.READ_STATUS: + case PduHeaders.CANCEL_STATUS: + case PduHeaders.PRIORITY: + case PduHeaders.STATUS: + case PduHeaders.REPLY_CHARGING: + case PduHeaders.MM_STATE: + case PduHeaders.RECOMMENDED_RETRIEVAL_MODE: + case PduHeaders.CONTENT_CLASS: + case PduHeaders.RETRIEVE_STATUS: + case PduHeaders.STORE_STATUS: + /** + * The following field has a different value when + * used in the M-Mbox-Delete.conf and M-Delete.conf PDU. + * For now we ignore this fact, since we do not support these PDUs + */ + case PduHeaders.RESPONSE_STATUS: + { + int value = extractByteValue(pduDataStream); + + try { + headers.setOctet(value, headerField); + } catch(InvalidHeaderValueException e) { + log("Set invalid Octet value: " + value + + " into the header filed: " + headerField); + return null; + } catch(RuntimeException e) { + log(headerField + "is not Octet header field!"); + return null; + } + break; + } + + /* Long-Integer */ + case PduHeaders.DATE: + case PduHeaders.REPLY_CHARGING_SIZE: + case PduHeaders.MESSAGE_SIZE: + { + try { + long value = parseLongInteger(pduDataStream); + headers.setLongInteger(value, headerField); + } catch(RuntimeException e) { + log(headerField + "is not Long-Integer header field!"); + return null; + } + break; + } + + /* Integer-Value */ + case PduHeaders.MESSAGE_COUNT: + case PduHeaders.START: + case PduHeaders.LIMIT: + { + try { + long value = parseIntegerValue(pduDataStream); + headers.setLongInteger(value, headerField); + } catch(RuntimeException e) { + log(headerField + "is not Long-Integer header field!"); + return null; + } + break; + } + + /* Text-String */ + case PduHeaders.TRANSACTION_ID: + case PduHeaders.REPLY_CHARGING_ID: + case PduHeaders.AUX_APPLIC_ID: + case PduHeaders.APPLIC_ID: + case PduHeaders.REPLY_APPLIC_ID: + /** + * The next three header fields are email addresses + * as defined in RFC2822, + * not including the characters "<" and ">" + */ + case PduHeaders.MESSAGE_ID: + case PduHeaders.REPLACE_ID: + case PduHeaders.CANCEL_ID: + /** + * The following field has a different value when + * used in the M-Mbox-Delete.conf and M-Delete.conf PDU. + * For now we ignore this fact, since we do not support these PDUs + */ + case PduHeaders.CONTENT_LOCATION: + { + byte[] value = parseWapString(pduDataStream, TYPE_TEXT_STRING); + if (null != value) { + try { + headers.setTextString(value, headerField); + } catch(NullPointerException e) { + log("null pointer error!"); + } catch(RuntimeException e) { + log(headerField + "is not Text-String header field!"); + return null; + } + } + break; + } + + /* Encoded-string-value */ + case PduHeaders.SUBJECT: + case PduHeaders.RECOMMENDED_RETRIEVAL_MODE_TEXT: + case PduHeaders.RETRIEVE_TEXT: + case PduHeaders.STATUS_TEXT: + case PduHeaders.STORE_STATUS_TEXT: + /* the next one is not support + * M-Mbox-Delete.conf and M-Delete.conf now */ + case PduHeaders.RESPONSE_TEXT: + { + EncodedStringValue value = + parseEncodedStringValue(pduDataStream); + if (null != value) { + try { + headers.setEncodedStringValue(value, headerField); + } catch(NullPointerException e) { + log("null pointer error!"); + } catch (RuntimeException e) { + log(headerField + "is not Encoded-String-Value header field!"); + return null; + } + } + break; + } + + /* Addressing model */ + case PduHeaders.BCC: + case PduHeaders.CC: + case PduHeaders.TO: + { + EncodedStringValue value = + parseEncodedStringValue(pduDataStream); + if (null != value) { + byte[] address = value.getTextString(); + if (null != address) { + String str = new String(address); + int endIndex = str.indexOf("/"); + if (endIndex > 0) { + str = str.substring(0, endIndex); + } + try { + value.setTextString(str.getBytes()); + } catch(NullPointerException e) { + log("null pointer error!"); + return null; + } + } + + try { + headers.appendEncodedStringValue(value, headerField); + } catch(NullPointerException e) { + log("null pointer error!"); + } catch(RuntimeException e) { + log(headerField + "is not Encoded-String-Value header field!"); + return null; + } + } + break; + } + + /* Value-length + * (Absolute-token Date-value | Relative-token Delta-seconds-value) */ + case PduHeaders.DELIVERY_TIME: + case PduHeaders.EXPIRY: + case PduHeaders.REPLY_CHARGING_DEADLINE: + { + /* parse Value-length */ + parseValueLength(pduDataStream); + + /* Absolute-token or Relative-token */ + int token = extractByteValue(pduDataStream); + + /* Date-value or Delta-seconds-value */ + long timeValue; + try { + timeValue = parseLongInteger(pduDataStream); + } catch(RuntimeException e) { + log(headerField + "is not Long-Integer header field!"); + return null; + } + if (PduHeaders.VALUE_RELATIVE_TOKEN == token) { + /* need to convert the Delta-seconds-value + * into Date-value */ + timeValue = System.currentTimeMillis()/1000 + timeValue; + } + + try { + headers.setLongInteger(timeValue, headerField); + } catch(RuntimeException e) { + log(headerField + "is not Long-Integer header field!"); + return null; + } + break; + } + + case PduHeaders.FROM: { + /* From-value = + * Value-length + * (Address-present-token Encoded-string-value | Insert-address-token) + */ + EncodedStringValue from = null; + parseValueLength(pduDataStream); /* parse value-length */ + + /* Address-present-token or Insert-address-token */ + int fromToken = extractByteValue(pduDataStream); + + /* Address-present-token or Insert-address-token */ + if (PduHeaders.FROM_ADDRESS_PRESENT_TOKEN == fromToken) { + /* Encoded-string-value */ + from = parseEncodedStringValue(pduDataStream); + if (null != from) { + byte[] address = from.getTextString(); + if (null != address) { + String str = new String(address); + int endIndex = str.indexOf("/"); + if (endIndex > 0) { + str = str.substring(0, endIndex); + } + try { + from.setTextString(str.getBytes()); + } catch(NullPointerException e) { + log("null pointer error!"); + return null; + } + } + } + } else { + try { + from = new EncodedStringValue( + PduHeaders.FROM_INSERT_ADDRESS_TOKEN_STR.getBytes()); + } catch(NullPointerException e) { + log(headerField + "is not Encoded-String-Value header field!"); + return null; + } + } + + try { + headers.setEncodedStringValue(from, PduHeaders.FROM); + } catch(NullPointerException e) { + log("null pointer error!"); + } catch(RuntimeException e) { + log(headerField + "is not Encoded-String-Value header field!"); + return null; + } + break; + } + + case PduHeaders.MESSAGE_CLASS: { + /* Message-class-value = Class-identifier | Token-text */ + pduDataStream.mark(1); + int messageClass = extractByteValue(pduDataStream); + + if (messageClass >= PduHeaders.MESSAGE_CLASS_PERSONAL) { + /* Class-identifier */ + try { + if (PduHeaders.MESSAGE_CLASS_PERSONAL == messageClass) { + headers.setTextString( + PduHeaders.MESSAGE_CLASS_PERSONAL_STR.getBytes(), + PduHeaders.MESSAGE_CLASS); + } else if (PduHeaders.MESSAGE_CLASS_ADVERTISEMENT == messageClass) { + headers.setTextString( + PduHeaders.MESSAGE_CLASS_ADVERTISEMENT_STR.getBytes(), + PduHeaders.MESSAGE_CLASS); + } else if (PduHeaders.MESSAGE_CLASS_INFORMATIONAL == messageClass) { + headers.setTextString( + PduHeaders.MESSAGE_CLASS_INFORMATIONAL_STR.getBytes(), + PduHeaders.MESSAGE_CLASS); + } else if (PduHeaders.MESSAGE_CLASS_AUTO == messageClass) { + headers.setTextString( + PduHeaders.MESSAGE_CLASS_AUTO_STR.getBytes(), + PduHeaders.MESSAGE_CLASS); + } + } catch(NullPointerException e) { + log("null pointer error!"); + } catch(RuntimeException e) { + log(headerField + "is not Text-String header field!"); + return null; + } + } else { + /* Token-text */ + pduDataStream.reset(); + byte[] messageClassString = parseWapString(pduDataStream, TYPE_TEXT_STRING); + if (null != messageClassString) { + try { + headers.setTextString(messageClassString, PduHeaders.MESSAGE_CLASS); + } catch(NullPointerException e) { + log("null pointer error!"); + } catch(RuntimeException e) { + log(headerField + "is not Text-String header field!"); + return null; + } + } + } + break; + } + + case PduHeaders.MMS_VERSION: { + int version = parseShortInteger(pduDataStream); + + try { + headers.setOctet(version, PduHeaders.MMS_VERSION); + } catch(InvalidHeaderValueException e) { + log("Set invalid Octet value: " + version + + " into the header filed: " + headerField); + return null; + } catch(RuntimeException e) { + log(headerField + "is not Octet header field!"); + return null; + } + break; + } + + case PduHeaders.PREVIOUSLY_SENT_BY: { + /* Previously-sent-by-value = + * Value-length Forwarded-count-value Encoded-string-value */ + /* parse value-length */ + parseValueLength(pduDataStream); + + /* parse Forwarded-count-value */ + try { + parseIntegerValue(pduDataStream); + } catch(RuntimeException e) { + log(headerField + " is not Integer-Value"); + return null; + } + + /* parse Encoded-string-value */ + EncodedStringValue previouslySentBy = + parseEncodedStringValue(pduDataStream); + if (null != previouslySentBy) { + try { + headers.setEncodedStringValue(previouslySentBy, + PduHeaders.PREVIOUSLY_SENT_BY); + } catch(NullPointerException e) { + log("null pointer error!"); + } catch(RuntimeException e) { + log(headerField + "is not Encoded-String-Value header field!"); + return null; + } + } + break; + } + + case PduHeaders.PREVIOUSLY_SENT_DATE: { + /* Previously-sent-date-value = + * Value-length Forwarded-count-value Date-value */ + /* parse value-length */ + parseValueLength(pduDataStream); + + /* parse Forwarded-count-value */ + try { + parseIntegerValue(pduDataStream); + } catch(RuntimeException e) { + log(headerField + " is not Integer-Value"); + return null; + } + + /* Date-value */ + try { + long perviouslySentDate = parseLongInteger(pduDataStream); + headers.setLongInteger(perviouslySentDate, + PduHeaders.PREVIOUSLY_SENT_DATE); + } catch(RuntimeException e) { + log(headerField + "is not Long-Integer header field!"); + return null; + } + break; + } + + case PduHeaders.MM_FLAGS: { + /* MM-flags-value = + * Value-length + * ( Add-token | Remove-token | Filter-token ) + * Encoded-string-value + */ + + /* parse Value-length */ + parseValueLength(pduDataStream); + + /* Add-token | Remove-token | Filter-token */ + extractByteValue(pduDataStream); + + /* Encoded-string-value */ + parseEncodedStringValue(pduDataStream); + + /* not store this header filed in "headers", + * because now PduHeaders doesn't support it */ + break; + } + + /* Value-length + * (Message-total-token | Size-total-token) Integer-Value */ + case PduHeaders.MBOX_TOTALS: + case PduHeaders.MBOX_QUOTAS: + { + /* Value-length */ + parseValueLength(pduDataStream); + + /* Message-total-token | Size-total-token */ + extractByteValue(pduDataStream); + + /*Integer-Value*/ + try { + parseIntegerValue(pduDataStream); + } catch(RuntimeException e) { + log(headerField + " is not Integer-Value"); + return null; + } + + /* not store these headers filed in "headers", + because now PduHeaders doesn't support them */ + break; + } + + case PduHeaders.ELEMENT_DESCRIPTOR: { + parseContentType(pduDataStream, null); + + /* not store this header filed in "headers", + because now PduHeaders doesn't support it */ + break; + } + + case PduHeaders.CONTENT_TYPE: { + HashMap map = + new HashMap(); + byte[] contentType = + parseContentType(pduDataStream, map); + + if (null != contentType) { + try { + headers.setTextString(contentType, PduHeaders.CONTENT_TYPE); + } catch(NullPointerException e) { + log("null pointer error!"); + } catch(RuntimeException e) { + log(headerField + "is not Text-String header field!"); + return null; + } + } + + /* get start parameter */ + mStartParam = (byte[]) map.get(PduPart.P_START); + + /* get charset parameter */ + mTypeParam= (byte[]) map.get(PduPart.P_TYPE); + + keepParsing = false; + break; + } + + case PduHeaders.CONTENT: + case PduHeaders.ADDITIONAL_HEADERS: + case PduHeaders.ATTRIBUTES: + default: { + log("Unknown header"); + } + } + } + + return headers; + } + + /** + * Parse pdu parts. + * + * @param pduDataStream pdu data input stream + * @return parts in PduBody structure + */ + protected static PduBody parseParts(ByteArrayInputStream pduDataStream) { + if (pduDataStream == null) { + return null; + } + + int count = parseUnsignedInt(pduDataStream); // get the number of parts + PduBody body = new PduBody(); + + for (int i = 0 ; i < count ; i++) { + int headerLength = parseUnsignedInt(pduDataStream); + int dataLength = parseUnsignedInt(pduDataStream); + PduPart part = new PduPart(); + int startPos = pduDataStream.available(); + if (startPos <= 0) { + // Invalid part. + return null; + } + + /* parse part's content-type */ + HashMap map = new HashMap(); + byte[] contentType = parseContentType(pduDataStream, map); + if (null != contentType) { + part.setContentType(contentType); + } else { + part.setContentType((PduContentTypes.contentTypes[0]).getBytes()); //"*/*" + } + + /* get name parameter */ + byte[] name = (byte[]) map.get(PduPart.P_NAME); + if (null != name) { + part.setName(name); + } + + /* get charset parameter */ + Integer charset = (Integer) map.get(PduPart.P_CHARSET); + if (null != charset) { + part.setCharset(charset); + } + + /* parse part's headers */ + int endPos = pduDataStream.available(); + int partHeaderLen = headerLength - (startPos - endPos); + if (partHeaderLen > 0) { + if (false == parsePartHeaders(pduDataStream, part, partHeaderLen)) { + // Parse part header faild. + return null; + } + } else if (partHeaderLen < 0) { + // Invalid length of content-type. + return null; + } + + /* FIXME: check content-id, name, filename and content location, + * if not set anyone of them, generate a default content-location + */ + if ((null == part.getContentLocation()) + && (null == part.getName()) + && (null == part.getFilename()) + && (null == part.getContentId())) { + part.setContentLocation(Long.toOctalString( + System.currentTimeMillis()).getBytes()); + } + + /* get part's data */ + if (dataLength > 0) { + byte[] partData = new byte[dataLength]; + pduDataStream.read(partData, 0, dataLength); + // Check Content-Transfer-Encoding. + byte[] partDataEncoding = part.getContentTransferEncoding(); + if (null != partDataEncoding) { + String encoding = new String(partDataEncoding); + if (encoding.equalsIgnoreCase(PduPart.P_BASE64)) { + // Decode "base64" into "binary". + partData = Base64.decodeBase64(partData); + } else if (encoding.equalsIgnoreCase(PduPart.P_QUOTED_PRINTABLE)) { + // Decode "quoted-printable" into "binary". + partData = QuotedPrintable.decodeQuotedPrintable(partData); + } else { + // "binary" is the default encoding. + } + } + if (null == partData) { + log("Decode part data error!"); + return null; + } + part.setData(partData); + } + + /* add this part to body */ + if (THE_FIRST_PART == checkPartPosition(part)) { + /* this is the first part */ + body.addPart(0, part); + } else { + /* add the part to the end */ + body.addPart(part); + } + } + + return body; + } + + /** + * Log status. + * + * @param text log information + */ + private static void log(String text) { + if (LOCAL_LOGV) { + Log.v(LOG_TAG, text); + } + } + + /** + * Parse unsigned integer. + * + * @param pduDataStream pdu data input stream + * @return the integer, -1 when failed + */ + protected static int parseUnsignedInt(ByteArrayInputStream pduDataStream) { + /** + * From wap-230-wsp-20010705-a.pdf + * The maximum size of a uintvar is 32 bits. + * So it will be encoded in no more than 5 octets. + */ + assert(null != pduDataStream); + int result = 0; + int temp = pduDataStream.read(); + if (temp == -1) { + return temp; + } + + while((temp & 0x80) != 0) { + result = result << 7; + result |= temp & 0x7F; + temp = pduDataStream.read(); + if (temp == -1) { + return temp; + } + } + + result = result << 7; + result |= temp & 0x7F; + + return result; + } + + /** + * Parse value length. + * + * @param pduDataStream pdu data input stream + * @return the integer + */ + protected static int parseValueLength(ByteArrayInputStream pduDataStream) { + /** + * From wap-230-wsp-20010705-a.pdf + * Value-length = Short-length | (Length-quote Length) + * Short-length = + * Length-quote = + * Length = Uintvar-integer + * Uintvar-integer = 1*5 OCTET + */ + assert(null != pduDataStream); + int temp = pduDataStream.read(); + assert(-1 != temp); + int first = temp & 0xFF; + + if (first <= SHORT_LENGTH_MAX) { + return first; + } else if (first == LENGTH_QUOTE) { + return parseUnsignedInt(pduDataStream); + } + + throw new RuntimeException ("Value length > LENGTH_QUOTE!"); + } + + /** + * Parse encoded string value. + * + * @param pduDataStream pdu data input stream + * @return the EncodedStringValue + */ + protected static EncodedStringValue parseEncodedStringValue(ByteArrayInputStream pduDataStream){ + /** + * From OMA-TS-MMS-ENC-V1_3-20050927-C.pdf + * Encoded-string-value = Text-string | Value-length Char-set Text-string + */ + assert(null != pduDataStream); + pduDataStream.mark(1); + EncodedStringValue returnValue = null; + int charset = 0; + int temp = pduDataStream.read(); + assert(-1 != temp); + int first = temp & 0xFF; + + pduDataStream.reset(); + if (first < TEXT_MIN) { + parseValueLength(pduDataStream); + + charset = parseShortInteger(pduDataStream); //get the "Charset" + } + + byte[] textString = parseWapString(pduDataStream, TYPE_TEXT_STRING); + + try { + if (0 != charset) { + returnValue = new EncodedStringValue(charset, textString); + } else { + returnValue = new EncodedStringValue(textString); + } + } catch(Exception e) { + return null; + } + + return returnValue; + } + + /** + * Parse Text-String or Quoted-String. + * + * @param pduDataStream pdu data input stream + * @param stringType TYPE_TEXT_STRING or TYPE_QUOTED_STRING + * @return the string without End-of-string in byte array + */ + protected static byte[] parseWapString(ByteArrayInputStream pduDataStream, + int stringType) { + assert(null != pduDataStream); + /** + * From wap-230-wsp-20010705-a.pdf + * Text-string = [Quote] *TEXT End-of-string + * If the first character in the TEXT is in the range of 128-255, + * a Quote character must precede it. + * Otherwise the Quote character must be omitted. + * The Quote is not part of the contents. + * Quote = + * End-of-string = + * + * Quoted-string = *TEXT End-of-string + * + * Token-text = Token End-of-string + */ + + // Mark supposed beginning of Text-string + // We will have to mark again if first char is QUOTE or QUOTED_STRING_FLAG + pduDataStream.mark(1); + + // Check first char + int temp = pduDataStream.read(); + assert(-1 != temp); + if ((TYPE_QUOTED_STRING == stringType) && + (QUOTED_STRING_FLAG == temp)) { + // Mark again if QUOTED_STRING_FLAG and ignore it + pduDataStream.mark(1); + } else if ((TYPE_TEXT_STRING == stringType) && + (QUOTE == temp)) { + // Mark again if QUOTE and ignore it + pduDataStream.mark(1); + } else { + // Otherwise go back to origin + pduDataStream.reset(); + } + + // We are now definitely at the beginning of string + /** + * Return *TOKEN or *TEXT (Text-String without QUOTE, + * Quoted-String without QUOTED_STRING_FLAG and without End-of-string) + */ + return getWapString(pduDataStream, stringType); + } + + /** + * Check TOKEN data defined in RFC2616. + * @param ch checking data + * @return true when ch is TOKEN, false when ch is not TOKEN + */ + protected static boolean isTokenCharacter(int ch) { + /** + * Token = 1* + * separators = "("(40) | ")"(41) | "<"(60) | ">"(62) | "@"(64) + * | ","(44) | ";"(59) | ":"(58) | "\"(92) | <">(34) + * | "/"(47) | "["(91) | "]"(93) | "?"(63) | "="(61) + * | "{"(123) | "}"(125) | SP(32) | HT(9) + * CHAR = + * CTL = + * SP = + * HT = + */ + if((ch < 33) || (ch > 126)) { + return false; + } + + switch(ch) { + case '"': /* '"' */ + case '(': /* '(' */ + case ')': /* ')' */ + case ',': /* ',' */ + case '/': /* '/' */ + case ':': /* ':' */ + case ';': /* ';' */ + case '<': /* '<' */ + case '=': /* '=' */ + case '>': /* '>' */ + case '?': /* '?' */ + case '@': /* '@' */ + case '[': /* '[' */ + case '\\': /* '\' */ + case ']': /* ']' */ + case '{': /* '{' */ + case '}': /* '}' */ + return false; + } + + return true; + } + + /** + * Check TEXT data defined in RFC2616. + * @param ch checking data + * @return true when ch is TEXT, false when ch is not TEXT + */ + protected static boolean isText(int ch) { + /** + * TEXT = + * CTL = + * LWS = [CRLF] 1*( SP | HT ) + * CRLF = CR LF + * CR = + * LF = + */ + if(((ch >= 32) && (ch <= 126)) || ((ch >= 128) && (ch <= 255))) { + return true; + } + + switch(ch) { + case '\t': /* '\t' */ + case '\n': /* '\n' */ + case '\r': /* '\r' */ + return true; + } + + return false; + } + + protected static byte[] getWapString(ByteArrayInputStream pduDataStream, + int stringType) { + assert(null != pduDataStream); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + int temp = pduDataStream.read(); + assert(-1 != temp); + while((-1 != temp) && ('\0' != temp)) { + // check each of the character + if (stringType == TYPE_TOKEN_STRING) { + if (isTokenCharacter(temp)) { + out.write(temp); + } + } else { + if (isText(temp)) { + out.write(temp); + } + } + + temp = pduDataStream.read(); + assert(-1 != temp); + } + + if (out.size() > 0) { + return out.toByteArray(); + } + + return null; + } + + /** + * Extract a byte value from the input stream. + * + * @param pduDataStream pdu data input stream + * @return the byte + */ + protected static int extractByteValue(ByteArrayInputStream pduDataStream) { + assert(null != pduDataStream); + int temp = pduDataStream.read(); + assert(-1 != temp); + return temp & 0xFF; + } + + /** + * Parse Short-Integer. + * + * @param pduDataStream pdu data input stream + * @return the byte + */ + protected static int parseShortInteger(ByteArrayInputStream pduDataStream) { + /** + * From wap-230-wsp-20010705-a.pdf + * Short-integer = OCTET + * Integers in range 0-127 shall be encoded as a one + * octet value with the most significant bit set to one (1xxx xxxx) + * and with the value in the remaining least significant bits. + */ + assert(null != pduDataStream); + int temp = pduDataStream.read(); + assert(-1 != temp); + return temp & 0x7F; + } + + /** + * Parse Long-Integer. + * + * @param pduDataStream pdu data input stream + * @return long integer + */ + protected static long parseLongInteger(ByteArrayInputStream pduDataStream) { + /** + * From wap-230-wsp-20010705-a.pdf + * Long-integer = Short-length Multi-octet-integer + * The Short-length indicates the length of the Multi-octet-integer + * Multi-octet-integer = 1*30 OCTET + * The content octets shall be an unsigned integer value + * with the most significant octet encoded first (big-endian representation). + * The minimum number of octets must be used to encode the value. + * Short-length = + */ + assert(null != pduDataStream); + int temp = pduDataStream.read(); + assert(-1 != temp); + int count = temp & 0xFF; + + if (count > LONG_INTEGER_LENGTH_MAX) { + throw new RuntimeException("Octet count greater than 8 and I can't represent that!"); + } + + long result = 0; + + for (int i = 0 ; i < count ; i++) { + temp = pduDataStream.read(); + assert(-1 != temp); + result <<= 8; + result += (temp & 0xFF); + } + + return result; + } + + /** + * Parse Integer-Value. + * + * @param pduDataStream pdu data input stream + * @return long integer + */ + protected static long parseIntegerValue(ByteArrayInputStream pduDataStream) { + /** + * From wap-230-wsp-20010705-a.pdf + * Integer-Value = Short-integer | Long-integer + */ + assert(null != pduDataStream); + pduDataStream.mark(1); + int temp = pduDataStream.read(); + assert(-1 != temp); + pduDataStream.reset(); + if (temp > SHORT_INTEGER_MAX) { + return parseShortInteger(pduDataStream); + } else { + return parseLongInteger(pduDataStream); + } + } + + /** + * To skip length of the wap value. + * + * @param pduDataStream pdu data input stream + * @param length area size + * @return the values in this area + */ + protected static int skipWapValue(ByteArrayInputStream pduDataStream, int length) { + assert(null != pduDataStream); + byte[] area = new byte[length]; + int readLen = pduDataStream.read(area, 0, length); + if (readLen < length) { //The actually read length is lower than the length + return -1; + } else { + return readLen; + } + } + + /** + * Parse content type parameters. For now we just support + * four parameters used in mms: "type", "start", "name", "charset". + * + * @param pduDataStream pdu data input stream + * @param map to store parameters of Content-Type field + * @param length length of all the parameters + */ + protected static void parseContentTypeParams(ByteArrayInputStream pduDataStream, + HashMap map, Integer length) { + /** + * From wap-230-wsp-20010705-a.pdf + * Parameter = Typed-parameter | Untyped-parameter + * Typed-parameter = Well-known-parameter-token Typed-value + * the actual expected type of the value is implied by the well-known parameter + * Well-known-parameter-token = Integer-value + * the code values used for parameters are specified in the Assigned Numbers appendix + * Typed-value = Compact-value | Text-value + * In addition to the expected type, there may be no value. + * If the value cannot be encoded using the expected type, it shall be encoded as text. + * Compact-value = Integer-value | + * Date-value | Delta-seconds-value | Q-value | Version-value | + * Uri-value + * Untyped-parameter = Token-text Untyped-value + * the type of the value is unknown, but it shall be encoded as an integer, + * if that is possible. + * Untyped-value = Integer-value | Text-value + */ + assert(null != pduDataStream); + assert(length > 0); + + int startPos = pduDataStream.available(); + int tempPos = 0; + int lastLen = length; + while(0 < lastLen) { + int param = pduDataStream.read(); + assert(-1 != param); + lastLen--; + + switch (param) { + /** + * From rfc2387, chapter 3.1 + * The type parameter must be specified and its value is the MIME media + * type of the "root" body part. It permits a MIME user agent to + * determine the content-type without reference to the enclosed body + * part. If the value of the type parameter and the root body part's + * content-type differ then the User Agent's behavior is undefined. + * + * From wap-230-wsp-20010705-a.pdf + * type = Constrained-encoding + * Constrained-encoding = Extension-Media | Short-integer + * Extension-media = *TEXT End-of-string + */ + case PduPart.P_TYPE: + case PduPart.P_CT_MR_TYPE: + pduDataStream.mark(1); + int first = extractByteValue(pduDataStream); + pduDataStream.reset(); + if (first > TEXT_MAX) { + // Short-integer (well-known type) + int index = parseShortInteger(pduDataStream); + + if (index < PduContentTypes.contentTypes.length) { + byte[] type = (PduContentTypes.contentTypes[index]).getBytes(); + map.put(PduPart.P_TYPE, type); + } else { + //not support this type, ignore it. + } + } else { + // Text-String (extension-media) + byte[] type = parseWapString(pduDataStream, TYPE_TEXT_STRING); + if ((null != type) && (null != map)) { + map.put(PduPart.P_TYPE, type); + } + } + + tempPos = pduDataStream.available(); + lastLen = length - (startPos - tempPos); + break; + + /** + * From oma-ts-mms-conf-v1_3.pdf, chapter 10.2.3. + * Start Parameter Referring to Presentation + * + * From rfc2387, chapter 3.2 + * The start parameter, if given, is the content-ID of the compound + * object's "root". If not present the "root" is the first body part in + * the Multipart/Related entity. The "root" is the element the + * applications processes first. + * + * From wap-230-wsp-20010705-a.pdf + * start = Text-String + */ + case PduPart.P_START: + case PduPart.P_DEP_START: + byte[] start = parseWapString(pduDataStream, TYPE_TEXT_STRING); + if ((null != start) && (null != map)) { + map.put(PduPart.P_START, start); + } + + tempPos = pduDataStream.available(); + lastLen = length - (startPos - tempPos); + break; + + /** + * From oma-ts-mms-conf-v1_3.pdf + * In creation, the character set SHALL be either us-ascii + * (IANA MIBenum 3) or utf-8 (IANA MIBenum 106)[Unicode]. + * In retrieval, both us-ascii and utf-8 SHALL be supported. + * + * From wap-230-wsp-20010705-a.pdf + * charset = Well-known-charset|Text-String + * Well-known-charset = Any-charset | Integer-value + * Both are encoded using values from Character Set + * Assignments table in Assigned Numbers + * Any-charset = + * Equivalent to the special RFC2616 charset value "*" + */ + case PduPart.P_CHARSET: + pduDataStream.mark(1); + int firstValue = extractByteValue(pduDataStream); + pduDataStream.reset(); + //Check first char + if (((firstValue > TEXT_MIN) && (firstValue < TEXT_MAX)) || + (END_STRING_FLAG == firstValue)) { + //Text-String (extension-charset) + byte[] charsetStr = parseWapString(pduDataStream, TYPE_TEXT_STRING); + try { + int charsetInt = CharacterSets.getMibEnumValue( + new String(charsetStr)); + map.put(PduPart.P_CHARSET, charsetInt); + } catch (UnsupportedEncodingException e) { + // Not a well-known charset, use "*". + Log.e(LOG_TAG, Arrays.toString(charsetStr), e); + map.put(PduPart.P_CHARSET, CharacterSets.ANY_CHARSET); + } + } else { + //Well-known-charset + int charset = (int) parseIntegerValue(pduDataStream); + if (map != null) { + map.put(PduPart.P_CHARSET, charset); + } + } + + tempPos = pduDataStream.available(); + lastLen = length - (startPos - tempPos); + break; + + /** + * From oma-ts-mms-conf-v1_3.pdf + * A name for multipart object SHALL be encoded using name-parameter + * for Content-Type header in WSP multipart headers. + * + * From wap-230-wsp-20010705-a.pdf + * name = Text-String + */ + case PduPart.P_DEP_NAME: + case PduPart.P_NAME: + byte[] name = parseWapString(pduDataStream, TYPE_TEXT_STRING); + if ((null != name) && (null != map)) { + map.put(PduPart.P_NAME, name); + } + + tempPos = pduDataStream.available(); + lastLen = length - (startPos - tempPos); + break; + default: + if (LOCAL_LOGV) { + Log.v(LOG_TAG, "Not supported Content-Type parameter"); + } + if (-1 == skipWapValue(pduDataStream, lastLen)) { + Log.e(LOG_TAG, "Corrupt Content-Type"); + } else { + lastLen = 0; + } + break; + } + } + + if (0 != lastLen) { + Log.e(LOG_TAG, "Corrupt Content-Type"); + } + } + + /** + * Parse content type. + * + * @param pduDataStream pdu data input stream + * @param map to store parameters in Content-Type header field + * @return Content-Type value + */ + protected static byte[] parseContentType(ByteArrayInputStream pduDataStream, + HashMap map) { + /** + * From wap-230-wsp-20010705-a.pdf + * Content-type-value = Constrained-media | Content-general-form + * Content-general-form = Value-length Media-type + * Media-type = (Well-known-media | Extension-Media) *(Parameter) + */ + assert(null != pduDataStream); + + byte[] contentType = null; + pduDataStream.mark(1); + int temp = pduDataStream.read(); + assert(-1 != temp); + pduDataStream.reset(); + + int cur = (temp & 0xFF); + + if (cur < TEXT_MIN) { + int length = parseValueLength(pduDataStream); + int startPos = pduDataStream.available(); + pduDataStream.mark(1); + temp = pduDataStream.read(); + assert(-1 != temp); + pduDataStream.reset(); + int first = (temp & 0xFF); + + if ((first >= TEXT_MIN) && (first <= TEXT_MAX)) { + contentType = parseWapString(pduDataStream, TYPE_TEXT_STRING); + } else if (first > TEXT_MAX) { + int index = parseShortInteger(pduDataStream); + + if (index < PduContentTypes.contentTypes.length) { //well-known type + contentType = (PduContentTypes.contentTypes[index]).getBytes(); + } else { + pduDataStream.reset(); + contentType = parseWapString(pduDataStream, TYPE_TEXT_STRING); + } + } else { + Log.e(LOG_TAG, "Corrupt content-type"); + return (PduContentTypes.contentTypes[0]).getBytes(); //"*/*" + } + + int endPos = pduDataStream.available(); + int parameterLen = length - (startPos - endPos); + if (parameterLen > 0) {//have parameters + parseContentTypeParams(pduDataStream, map, parameterLen); + } + + if (parameterLen < 0) { + Log.e(LOG_TAG, "Corrupt MMS message"); + return (PduContentTypes.contentTypes[0]).getBytes(); //"*/*" + } + } else if (cur <= TEXT_MAX) { + contentType = parseWapString(pduDataStream, TYPE_TEXT_STRING); + } else { + contentType = + (PduContentTypes.contentTypes[parseShortInteger(pduDataStream)]).getBytes(); + } + + return contentType; + } + + /** + * Parse part's headers. + * + * @param pduDataStream pdu data input stream + * @param part to store the header informations of the part + * @param length length of the headers + * @return true if parse successfully, false otherwise + */ + protected static boolean parsePartHeaders(ByteArrayInputStream pduDataStream, + PduPart part, int length) { + assert(null != pduDataStream); + assert(null != part); + assert(length > 0); + + /** + * From oma-ts-mms-conf-v1_3.pdf, chapter 10.2. + * A name for multipart object SHALL be encoded using name-parameter + * for Content-Type header in WSP multipart headers. + * In decoding, name-parameter of Content-Type SHALL be used if available. + * If name-parameter of Content-Type is not available, + * filename parameter of Content-Disposition header SHALL be used if available. + * If neither name-parameter of Content-Type header nor filename parameter + * of Content-Disposition header is available, + * Content-Location header SHALL be used if available. + * + * Within SMIL part the reference to the media object parts SHALL use + * either Content-ID or Content-Location mechanism [RFC2557] + * and the corresponding WSP part headers in media object parts + * contain the corresponding definitions. + */ + int startPos = pduDataStream.available(); + int tempPos = 0; + int lastLen = length; + while(0 < lastLen) { + int header = pduDataStream.read(); + assert(-1 != header); + lastLen--; + + if (header > TEXT_MAX) { + // Number assigned headers. + switch (header) { + case PduPart.P_CONTENT_LOCATION: + /** + * From wap-230-wsp-20010705-a.pdf, chapter 8.4.2.21 + * Content-location-value = Uri-value + */ + byte[] contentLocation = parseWapString(pduDataStream, TYPE_TEXT_STRING); + if (null != contentLocation) { + part.setContentLocation(contentLocation); + } + + tempPos = pduDataStream.available(); + lastLen = length - (startPos - tempPos); + break; + case PduPart.P_CONTENT_ID: + /** + * From wap-230-wsp-20010705-a.pdf, chapter 8.4.2.21 + * Content-ID-value = Quoted-string + */ + byte[] contentId = parseWapString(pduDataStream, TYPE_QUOTED_STRING); + if (null != contentId) { + part.setContentId(contentId); + } + + tempPos = pduDataStream.available(); + lastLen = length - (startPos - tempPos); + break; + case PduPart.P_DEP_CONTENT_DISPOSITION: + case PduPart.P_CONTENT_DISPOSITION: + /** + * From wap-230-wsp-20010705-a.pdf, chapter 8.4.2.21 + * Content-disposition-value = Value-length Disposition *(Parameter) + * Disposition = Form-data | Attachment | Inline | Token-text + * Form-data = + * Attachment = + * Inline = + */ + int len = parseValueLength(pduDataStream); + pduDataStream.mark(1); + int thisStartPos = pduDataStream.available(); + int thisEndPos = 0; + int value = pduDataStream.read(); + + if (value == PduPart.P_DISPOSITION_FROM_DATA ) { + part.setContentDisposition(PduPart.DISPOSITION_FROM_DATA); + } else if (value == PduPart.P_DISPOSITION_ATTACHMENT) { + part.setContentDisposition(PduPart.DISPOSITION_ATTACHMENT); + } else if (value == PduPart.P_DISPOSITION_INLINE) { + part.setContentDisposition(PduPart.DISPOSITION_INLINE); + } else { + pduDataStream.reset(); + /* Token-text */ + part.setContentDisposition(parseWapString(pduDataStream, TYPE_TEXT_STRING)); + } + + /* get filename parameter and skip other parameters */ + thisEndPos = pduDataStream.available(); + if (thisStartPos - thisEndPos < len) { + value = pduDataStream.read(); + if (value == PduPart.P_FILENAME) { //filename is text-string + part.setFilename(parseWapString(pduDataStream, TYPE_TEXT_STRING)); + } + + /* skip other parameters */ + thisEndPos = pduDataStream.available(); + if (thisStartPos - thisEndPos < len) { + int last = len - (thisStartPos - thisEndPos); + byte[] temp = new byte[last]; + pduDataStream.read(temp, 0, last); + } + } + + tempPos = pduDataStream.available(); + lastLen = length - (startPos - tempPos); + break; + default: + if (LOCAL_LOGV) { + Log.v(LOG_TAG, "Not supported Part headers: " + header); + } + if (-1 == skipWapValue(pduDataStream, lastLen)) { + Log.e(LOG_TAG, "Corrupt Part headers"); + return false; + } + lastLen = 0; + break; + } + } else if ((header >= TEXT_MIN) && (header <= TEXT_MAX)) { + // Not assigned header. + byte[] tempHeader = parseWapString(pduDataStream, TYPE_TEXT_STRING); + byte[] tempValue = parseWapString(pduDataStream, TYPE_TEXT_STRING); + + // Check the header whether it is "Content-Transfer-Encoding". + if (true == + PduPart.CONTENT_TRANSFER_ENCODING.equalsIgnoreCase(new String(tempHeader))) { + part.setContentTransferEncoding(tempValue); + } + + tempPos = pduDataStream.available(); + lastLen = length - (startPos - tempPos); + } else { + if (LOCAL_LOGV) { + Log.v(LOG_TAG, "Not supported Part headers: " + header); + } + // Skip all headers of this part. + if (-1 == skipWapValue(pduDataStream, lastLen)) { + Log.e(LOG_TAG, "Corrupt Part headers"); + return false; + } + lastLen = 0; + } + } + + if (0 != lastLen) { + Log.e(LOG_TAG, "Corrupt Part headers"); + return false; + } + + return true; + } + + /** + * Check the position of a specified part. + * + * @param part the part to be checked + * @return part position, THE_FIRST_PART when it's the + * first one, THE_LAST_PART when it's the last one. + */ + private static int checkPartPosition(PduPart part) { + assert(null != part); + if ((null == mTypeParam) && + (null == mStartParam)) { + return THE_LAST_PART; + } + + /* check part's content-id */ + if (null != mStartParam) { + byte[] contentId = part.getContentId(); + if (null != contentId) { + if (true == Arrays.equals(mStartParam, contentId)) { + return THE_FIRST_PART; + } + } + } + + /* check part's content-type */ + if (null != mTypeParam) { + byte[] contentType = part.getContentType(); + if (null != contentType) { + if (true == Arrays.equals(mTypeParam, contentType)) { + return THE_FIRST_PART; + } + } + } + + return THE_LAST_PART; + } + + /** + * Check mandatory headers of a pdu. + * + * @param headers pdu headers + * @return true if the pdu has all of the mandatory headers, false otherwise. + */ + protected static boolean checkMandatoryHeader(PduHeaders headers) { + if (null == headers) { + return false; + } + + /* get message type */ + int messageType = headers.getOctet(PduHeaders.MESSAGE_TYPE); + + /* check Mms-Version field */ + int mmsVersion = headers.getOctet(PduHeaders.MMS_VERSION); + if (0 == mmsVersion) { + // Every message should have Mms-Version field. + return false; + } + + /* check mandatory header fields */ + switch (messageType) { + case PduHeaders.MESSAGE_TYPE_SEND_REQ: + // Content-Type field. + byte[] srContentType = headers.getTextString(PduHeaders.CONTENT_TYPE); + if (null == srContentType) { + return false; + } + + // From field. + EncodedStringValue srFrom = headers.getEncodedStringValue(PduHeaders.FROM); + if (null == srFrom) { + return false; + } + + // Transaction-Id field. + byte[] srTransactionId = headers.getTextString(PduHeaders.TRANSACTION_ID); + if (null == srTransactionId) { + return false; + } + + break; + case PduHeaders.MESSAGE_TYPE_SEND_CONF: + // Response-Status field. + int scResponseStatus = headers.getOctet(PduHeaders.RESPONSE_STATUS); + if (0 == scResponseStatus) { + return false; + } + + // Transaction-Id field. + byte[] scTransactionId = headers.getTextString(PduHeaders.TRANSACTION_ID); + if (null == scTransactionId) { + return false; + } + + break; + case PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND: + // Content-Location field. + byte[] niContentLocation = headers.getTextString(PduHeaders.CONTENT_LOCATION); + if (null == niContentLocation) { + return false; + } + + // Expiry field. + long niExpiry = headers.getLongInteger(PduHeaders.EXPIRY); + if (-1 == niExpiry) { + return false; + } + + // Message-Class field. + byte[] niMessageClass = headers.getTextString(PduHeaders.MESSAGE_CLASS); + if (null == niMessageClass) { + return false; + } + + // Message-Size field. + long niMessageSize = headers.getLongInteger(PduHeaders.MESSAGE_SIZE); + if (-1 == niMessageSize) { + return false; + } + + // Transaction-Id field. + byte[] niTransactionId = headers.getTextString(PduHeaders.TRANSACTION_ID); + if (null == niTransactionId) { + return false; + } + + break; + case PduHeaders.MESSAGE_TYPE_NOTIFYRESP_IND: + // Status field. + int nriStatus = headers.getOctet(PduHeaders.STATUS); + if (0 == nriStatus) { + return false; + } + + // Transaction-Id field. + byte[] nriTransactionId = headers.getTextString(PduHeaders.TRANSACTION_ID); + if (null == nriTransactionId) { + return false; + } + + break; + case PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF: + // Content-Type field. + byte[] rcContentType = headers.getTextString(PduHeaders.CONTENT_TYPE); + if (null == rcContentType) { + return false; + } + + // Date field. + long rcDate = headers.getLongInteger(PduHeaders.DATE); + if (-1 == rcDate) { + return false; + } + + break; + case PduHeaders.MESSAGE_TYPE_DELIVERY_IND: + // Date field. + long diDate = headers.getLongInteger(PduHeaders.DATE); + if (-1 == diDate) { + return false; + } + + // Message-Id field. + byte[] diMessageId = headers.getTextString(PduHeaders.MESSAGE_ID); + if (null == diMessageId) { + return false; + } + + // Status field. + int diStatus = headers.getOctet(PduHeaders.STATUS); + if (0 == diStatus) { + return false; + } + + // To field. + EncodedStringValue[] diTo = headers.getEncodedStringValues(PduHeaders.TO); + if (null == diTo) { + return false; + } + + break; + case PduHeaders.MESSAGE_TYPE_ACKNOWLEDGE_IND: + // Transaction-Id field. + byte[] aiTransactionId = headers.getTextString(PduHeaders.TRANSACTION_ID); + if (null == aiTransactionId) { + return false; + } + + break; + case PduHeaders.MESSAGE_TYPE_READ_ORIG_IND: + // Date field. + long roDate = headers.getLongInteger(PduHeaders.DATE); + if (-1 == roDate) { + return false; + } + + // From field. + EncodedStringValue roFrom = headers.getEncodedStringValue(PduHeaders.FROM); + if (null == roFrom) { + return false; + } + + // Message-Id field. + byte[] roMessageId = headers.getTextString(PduHeaders.MESSAGE_ID); + if (null == roMessageId) { + return false; + } + + // Read-Status field. + int roReadStatus = headers.getOctet(PduHeaders.READ_STATUS); + if (0 == roReadStatus) { + return false; + } + + // To field. + EncodedStringValue[] roTo = headers.getEncodedStringValues(PduHeaders.TO); + if (null == roTo) { + return false; + } + + break; + case PduHeaders.MESSAGE_TYPE_READ_REC_IND: + // From field. + EncodedStringValue rrFrom = headers.getEncodedStringValue(PduHeaders.FROM); + if (null == rrFrom) { + return false; + } + + // Message-Id field. + byte[] rrMessageId = headers.getTextString(PduHeaders.MESSAGE_ID); + if (null == rrMessageId) { + return false; + } + + // Read-Status field. + int rrReadStatus = headers.getOctet(PduHeaders.READ_STATUS); + if (0 == rrReadStatus) { + return false; + } + + // To field. + EncodedStringValue[] rrTo = headers.getEncodedStringValues(PduHeaders.TO); + if (null == rrTo) { + return false; + } + + break; + default: + // Parser doesn't support this message type in this version. + return false; + } + + return true; + } +} diff --git a/mms-common/java/com/android/common/mms/pdu/PduPart.java b/mms-common/java/com/android/common/mms/pdu/PduPart.java new file mode 100644 index 000000000..7d51b86e9 --- /dev/null +++ b/mms-common/java/com/android/common/mms/pdu/PduPart.java @@ -0,0 +1,402 @@ +/* + * Copyright (C) 2007-2008 Esmertec AG. + * Copyright (C) 2007-2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.mmscommon.mms.pdu; + +import android.net.Uri; + +import java.util.HashMap; +import java.util.Map; + +/** + * The pdu part. + */ +public class PduPart { + /** + * Well-Known Parameters. + */ + public static final int P_Q = 0x80; + public static final int P_CHARSET = 0x81; + public static final int P_LEVEL = 0x82; + public static final int P_TYPE = 0x83; + public static final int P_DEP_NAME = 0x85; + public static final int P_DEP_FILENAME = 0x86; + public static final int P_DIFFERENCES = 0x87; + public static final int P_PADDING = 0x88; + // This value of "TYPE" s used with Content-Type: multipart/related + public static final int P_CT_MR_TYPE = 0x89; + public static final int P_DEP_START = 0x8A; + public static final int P_DEP_START_INFO = 0x8B; + public static final int P_DEP_COMMENT = 0x8C; + public static final int P_DEP_DOMAIN = 0x8D; + public static final int P_MAX_AGE = 0x8E; + public static final int P_DEP_PATH = 0x8F; + public static final int P_SECURE = 0x90; + public static final int P_SEC = 0x91; + public static final int P_MAC = 0x92; + public static final int P_CREATION_DATE = 0x93; + public static final int P_MODIFICATION_DATE = 0x94; + public static final int P_READ_DATE = 0x95; + public static final int P_SIZE = 0x96; + public static final int P_NAME = 0x97; + public static final int P_FILENAME = 0x98; + public static final int P_START = 0x99; + public static final int P_START_INFO = 0x9A; + public static final int P_COMMENT = 0x9B; + public static final int P_DOMAIN = 0x9C; + public static final int P_PATH = 0x9D; + + /** + * Header field names. + */ + public static final int P_CONTENT_TYPE = 0x91; + public static final int P_CONTENT_LOCATION = 0x8E; + public static final int P_CONTENT_ID = 0xC0; + public static final int P_DEP_CONTENT_DISPOSITION = 0xAE; + public static final int P_CONTENT_DISPOSITION = 0xC5; + // The next header is unassigned header, use reserved header(0x48) value. + public static final int P_CONTENT_TRANSFER_ENCODING = 0xC8; + + /** + * Content=Transfer-Encoding string. + */ + public static final String CONTENT_TRANSFER_ENCODING = + "Content-Transfer-Encoding"; + + /** + * Value of Content-Transfer-Encoding. + */ + public static final String P_BINARY = "binary"; + public static final String P_7BIT = "7bit"; + public static final String P_8BIT = "8bit"; + public static final String P_BASE64 = "base64"; + public static final String P_QUOTED_PRINTABLE = "quoted-printable"; + + /** + * Value of disposition can be set to PduPart when the value is octet in + * the PDU. + * "from-data" instead of Form-data. + * "attachment" instead of Attachment. + * "inline" instead of Inline. + */ + static final byte[] DISPOSITION_FROM_DATA = "from-data".getBytes(); + static final byte[] DISPOSITION_ATTACHMENT = "attachment".getBytes(); + static final byte[] DISPOSITION_INLINE = "inline".getBytes(); + + /** + * Content-Disposition value. + */ + public static final int P_DISPOSITION_FROM_DATA = 0x80; + public static final int P_DISPOSITION_ATTACHMENT = 0x81; + public static final int P_DISPOSITION_INLINE = 0x82; + + /** + * Header of part. + */ + private Map mPartHeader = null; + + /** + * Data uri. + */ + private Uri mUri = null; + + /** + * Part data. + */ + private byte[] mPartData = null; + + private static final String TAG = "PduPart"; + + /** + * Empty Constructor. + */ + public PduPart() { + mPartHeader = new HashMap(); + } + + /** + * Set part data. The data are stored as byte array. + * + * @param data the data + */ + public void setData(byte[] data) { + if(data == null) { + return; + } + + mPartData = new byte[data.length]; + System.arraycopy(data, 0, mPartData, 0, data.length); + } + + /** + * @return A copy of the part data or null if the data wasn't set or + * the data is stored as Uri. + * @see #getDataUri + */ + public byte[] getData() { + if(mPartData == null) { + return null; + } + + byte[] byteArray = new byte[mPartData.length]; + System.arraycopy(mPartData, 0, byteArray, 0, mPartData.length); + return byteArray; + } + + /** + * Set data uri. The data are stored as Uri. + * + * @param uri the uri + */ + public void setDataUri(Uri uri) { + mUri = uri; + } + + /** + * @return The Uri of the part data or null if the data wasn't set or + * the data is stored as byte array. + * @see #getData + */ + public Uri getDataUri() { + return mUri; + } + + /** + * Set Content-id value + * + * @param contentId the content-id value + * @throws NullPointerException if the value is null. + */ + public void setContentId(byte[] contentId) { + if((contentId == null) || (contentId.length == 0)) { + throw new IllegalArgumentException( + "Content-Id may not be null or empty."); + } + + if ((contentId.length > 1) + && ((char) contentId[0] == '<') + && ((char) contentId[contentId.length - 1] == '>')) { + mPartHeader.put(P_CONTENT_ID, contentId); + return; + } + + // Insert beginning '<' and trailing '>' for Content-Id. + byte[] buffer = new byte[contentId.length + 2]; + buffer[0] = (byte) (0xff & '<'); + buffer[buffer.length - 1] = (byte) (0xff & '>'); + System.arraycopy(contentId, 0, buffer, 1, contentId.length); + mPartHeader.put(P_CONTENT_ID, buffer); + } + + /** + * Get Content-id value. + * + * @return the value + */ + public byte[] getContentId() { + return (byte[]) mPartHeader.get(P_CONTENT_ID); + } + + /** + * Set Char-set value. + * + * @param charset the value + */ + public void setCharset(int charset) { + mPartHeader.put(P_CHARSET, charset); + } + + /** + * Get Char-set value + * + * @return the charset value. Return 0 if charset was not set. + */ + public int getCharset() { + Integer charset = (Integer) mPartHeader.get(P_CHARSET); + if(charset == null) { + return 0; + } else { + return charset.intValue(); + } + } + + /** + * Set Content-Location value. + * + * @param contentLocation the value + * @throws NullPointerException if the value is null. + */ + public void setContentLocation(byte[] contentLocation) { + if(contentLocation == null) { + throw new NullPointerException("null content-location"); + } + + mPartHeader.put(P_CONTENT_LOCATION, contentLocation); + } + + /** + * Get Content-Location value. + * + * @return the value + * return PduPart.disposition[0] instead of (Form-data). + * return PduPart.disposition[1] instead of (Attachment). + * return PduPart.disposition[2] instead of (Inline). + */ + public byte[] getContentLocation() { + return (byte[]) mPartHeader.get(P_CONTENT_LOCATION); + } + + /** + * Set Content-Disposition value. + * Use PduPart.disposition[0] instead of (Form-data). + * Use PduPart.disposition[1] instead of (Attachment). + * Use PduPart.disposition[2] instead of (Inline). + * + * @param contentDisposition the value + * @throws NullPointerException if the value is null. + */ + public void setContentDisposition(byte[] contentDisposition) { + if(contentDisposition == null) { + throw new NullPointerException("null content-disposition"); + } + + mPartHeader.put(P_CONTENT_DISPOSITION, contentDisposition); + } + + /** + * Get Content-Disposition value. + * + * @return the value + */ + public byte[] getContentDisposition() { + return (byte[]) mPartHeader.get(P_CONTENT_DISPOSITION); + } + + /** + * Set Content-Type value. + * + * @param value the value + * @throws NullPointerException if the value is null. + */ + public void setContentType(byte[] contentType) { + if(contentType == null) { + throw new NullPointerException("null content-type"); + } + + mPartHeader.put(P_CONTENT_TYPE, contentType); + } + + /** + * Get Content-Type value of part. + * + * @return the value + */ + public byte[] getContentType() { + return (byte[]) mPartHeader.get(P_CONTENT_TYPE); + } + + /** + * Set Content-Transfer-Encoding value + * + * @param contentId the content-id value + * @throws NullPointerException if the value is null. + */ + public void setContentTransferEncoding(byte[] contentTransferEncoding) { + if(contentTransferEncoding == null) { + throw new NullPointerException("null content-transfer-encoding"); + } + + mPartHeader.put(P_CONTENT_TRANSFER_ENCODING, contentTransferEncoding); + } + + /** + * Get Content-Transfer-Encoding value. + * + * @return the value + */ + public byte[] getContentTransferEncoding() { + return (byte[]) mPartHeader.get(P_CONTENT_TRANSFER_ENCODING); + } + + /** + * Set Content-type parameter: name. + * + * @param name the name value + * @throws NullPointerException if the value is null. + */ + public void setName(byte[] name) { + if(null == name) { + throw new NullPointerException("null content-id"); + } + + mPartHeader.put(P_NAME, name); + } + + /** + * Get content-type parameter: name. + * + * @return the name + */ + public byte[] getName() { + return (byte[]) mPartHeader.get(P_NAME); + } + + /** + * Get Content-disposition parameter: filename + * + * @param fileName the filename value + * @throws NullPointerException if the value is null. + */ + public void setFilename(byte[] fileName) { + if(null == fileName) { + throw new NullPointerException("null content-id"); + } + + mPartHeader.put(P_FILENAME, fileName); + } + + /** + * Set Content-disposition parameter: filename + * + * @return the filename + */ + public byte[] getFilename() { + return (byte[]) mPartHeader.get(P_FILENAME); + } + + public String generateLocation() { + // Assumption: At least one of the content-location / name / filename + // or content-id should be set. This is guaranteed by the PduParser + // for incoming messages and by MM composer for outgoing messages. + byte[] location = (byte[]) mPartHeader.get(P_NAME); + if(null == location) { + location = (byte[]) mPartHeader.get(P_FILENAME); + + if (null == location) { + location = (byte[]) mPartHeader.get(P_CONTENT_LOCATION); + } + } + + if (null == location) { + byte[] contentId = (byte[]) mPartHeader.get(P_CONTENT_ID); + return "cid:" + new String(contentId); + } else { + return new String(location); + } + } +} + diff --git a/mms-common/java/com/android/common/mms/pdu/PduPersister.java b/mms-common/java/com/android/common/mms/pdu/PduPersister.java new file mode 100644 index 000000000..46f28c7f0 --- /dev/null +++ b/mms-common/java/com/android/common/mms/pdu/PduPersister.java @@ -0,0 +1,1266 @@ +/* + * Copyright (C) 2007-2008 Esmertec AG. + * Copyright (C) 2007-2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.mmscommon.mms.pdu; + +import com.android.mmscommon.mms.pdu.PduPersister; + +import com.android.mmscommon.ContentType; +import com.android.mmscommon.CharacterSets; +import com.android.mmscommon.EncodedStringValue; +import com.android.mmscommon.InvalidHeaderValueException; +import com.android.mmscommon.MmsException; +import com.android.mmscommon.PduHeaders; +import com.android.mmscommon.mms.util.PduCache; +import com.android.mmscommon.mms.util.PduCacheEntry; +import android.database.sqlite.SqliteWrapper; + +import android.content.ContentResolver; +import android.content.ContentUris; +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import android.database.DatabaseUtils; +import android.net.Uri; +import com.android.mmscommon.telephony.TelephonyProvider; +import com.android.mmscommon.telephony.TelephonyProvider.Mms; +import com.android.mmscommon.telephony.TelephonyProvider.MmsSms; +import com.android.mmscommon.telephony.TelephonyProvider.Threads; +import com.android.mmscommon.telephony.TelephonyProvider.Mms.Addr; +import com.android.mmscommon.telephony.TelephonyProvider.Mms.Part; +import com.android.mmscommon.telephony.TelephonyProvider.MmsSms.PendingMessages; +import android.text.TextUtils; +import android.util.Config; +import android.util.Log; + +import java.io.ByteArrayOutputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.Map.Entry; + + +/** + * This class is the high-level manager of PDU storage. + */ +public class PduPersister { + private static final String TAG = "PduPersister"; + private static final boolean DEBUG = false; + private static final boolean LOCAL_LOGV = DEBUG ? Config.LOGD : Config.LOGV; + + private static final long DUMMY_THREAD_ID = Long.MAX_VALUE; + + /** + * The uri of temporary drm objects. + */ + public static final String TEMPORARY_DRM_OBJECT_URI = + "content://mms/" + Long.MAX_VALUE + "/part"; + /** + * Indicate that we transiently failed to process a MM. + */ + public static final int PROC_STATUS_TRANSIENT_FAILURE = 1; + /** + * Indicate that we permanently failed to process a MM. + */ + public static final int PROC_STATUS_PERMANENTLY_FAILURE = 2; + /** + * Indicate that we have successfully processed a MM. + */ + public static final int PROC_STATUS_COMPLETED = 3; + + private static PduPersister sPersister; + private static final PduCache PDU_CACHE_INSTANCE; + + private static final int[] ADDRESS_FIELDS = new int[] { + PduHeaders.BCC, + PduHeaders.CC, + PduHeaders.FROM, + PduHeaders.TO + }; + + private static final String[] PDU_PROJECTION = new String[] { + Mms._ID, + Mms.MESSAGE_BOX, + Mms.THREAD_ID, + Mms.RETRIEVE_TEXT, + Mms.SUBJECT, + Mms.CONTENT_LOCATION, + Mms.CONTENT_TYPE, + Mms.MESSAGE_CLASS, + Mms.MESSAGE_ID, + Mms.RESPONSE_TEXT, + Mms.TRANSACTION_ID, + Mms.CONTENT_CLASS, + Mms.DELIVERY_REPORT, + Mms.MESSAGE_TYPE, + Mms.MMS_VERSION, + Mms.PRIORITY, + Mms.READ_REPORT, + Mms.READ_STATUS, + Mms.REPORT_ALLOWED, + Mms.RETRIEVE_STATUS, + Mms.STATUS, + Mms.DATE, + Mms.DELIVERY_TIME, + Mms.EXPIRY, + Mms.MESSAGE_SIZE, + Mms.SUBJECT_CHARSET, + Mms.RETRIEVE_TEXT_CHARSET, + }; + + private static final int PDU_COLUMN_ID = 0; + private static final int PDU_COLUMN_MESSAGE_BOX = 1; + private static final int PDU_COLUMN_THREAD_ID = 2; + private static final int PDU_COLUMN_RETRIEVE_TEXT = 3; + private static final int PDU_COLUMN_SUBJECT = 4; + private static final int PDU_COLUMN_CONTENT_LOCATION = 5; + private static final int PDU_COLUMN_CONTENT_TYPE = 6; + private static final int PDU_COLUMN_MESSAGE_CLASS = 7; + private static final int PDU_COLUMN_MESSAGE_ID = 8; + private static final int PDU_COLUMN_RESPONSE_TEXT = 9; + private static final int PDU_COLUMN_TRANSACTION_ID = 10; + private static final int PDU_COLUMN_CONTENT_CLASS = 11; + private static final int PDU_COLUMN_DELIVERY_REPORT = 12; + private static final int PDU_COLUMN_MESSAGE_TYPE = 13; + private static final int PDU_COLUMN_MMS_VERSION = 14; + private static final int PDU_COLUMN_PRIORITY = 15; + private static final int PDU_COLUMN_READ_REPORT = 16; + private static final int PDU_COLUMN_READ_STATUS = 17; + private static final int PDU_COLUMN_REPORT_ALLOWED = 18; + private static final int PDU_COLUMN_RETRIEVE_STATUS = 19; + private static final int PDU_COLUMN_STATUS = 20; + private static final int PDU_COLUMN_DATE = 21; + private static final int PDU_COLUMN_DELIVERY_TIME = 22; + private static final int PDU_COLUMN_EXPIRY = 23; + private static final int PDU_COLUMN_MESSAGE_SIZE = 24; + private static final int PDU_COLUMN_SUBJECT_CHARSET = 25; + private static final int PDU_COLUMN_RETRIEVE_TEXT_CHARSET = 26; + + private static final String[] PART_PROJECTION = new String[] { + Part._ID, + Part.CHARSET, + Part.CONTENT_DISPOSITION, + Part.CONTENT_ID, + Part.CONTENT_LOCATION, + Part.CONTENT_TYPE, + Part.FILENAME, + Part.NAME, + Part.TEXT + }; + + private static final int PART_COLUMN_ID = 0; + private static final int PART_COLUMN_CHARSET = 1; + private static final int PART_COLUMN_CONTENT_DISPOSITION = 2; + private static final int PART_COLUMN_CONTENT_ID = 3; + private static final int PART_COLUMN_CONTENT_LOCATION = 4; + private static final int PART_COLUMN_CONTENT_TYPE = 5; + private static final int PART_COLUMN_FILENAME = 6; + private static final int PART_COLUMN_NAME = 7; + private static final int PART_COLUMN_TEXT = 8; + + private static final HashMap MESSAGE_BOX_MAP; + // These map are used for convenience in persist() and load(). + private static final HashMap CHARSET_COLUMN_INDEX_MAP; + private static final HashMap ENCODED_STRING_COLUMN_INDEX_MAP; + private static final HashMap TEXT_STRING_COLUMN_INDEX_MAP; + private static final HashMap OCTET_COLUMN_INDEX_MAP; + private static final HashMap LONG_COLUMN_INDEX_MAP; + private static final HashMap CHARSET_COLUMN_NAME_MAP; + private static final HashMap ENCODED_STRING_COLUMN_NAME_MAP; + private static final HashMap TEXT_STRING_COLUMN_NAME_MAP; + private static final HashMap OCTET_COLUMN_NAME_MAP; + private static final HashMap LONG_COLUMN_NAME_MAP; + + static { + MESSAGE_BOX_MAP = new HashMap(); + MESSAGE_BOX_MAP.put(Mms.Inbox.CONTENT_URI, Mms.MESSAGE_BOX_INBOX); + MESSAGE_BOX_MAP.put(Mms.Sent.CONTENT_URI, Mms.MESSAGE_BOX_SENT); + MESSAGE_BOX_MAP.put(Mms.Draft.CONTENT_URI, Mms.MESSAGE_BOX_DRAFTS); + MESSAGE_BOX_MAP.put(Mms.Outbox.CONTENT_URI, Mms.MESSAGE_BOX_OUTBOX); + + CHARSET_COLUMN_INDEX_MAP = new HashMap(); + CHARSET_COLUMN_INDEX_MAP.put(PduHeaders.SUBJECT, PDU_COLUMN_SUBJECT_CHARSET); + CHARSET_COLUMN_INDEX_MAP.put(PduHeaders.RETRIEVE_TEXT, PDU_COLUMN_RETRIEVE_TEXT_CHARSET); + + CHARSET_COLUMN_NAME_MAP = new HashMap(); + CHARSET_COLUMN_NAME_MAP.put(PduHeaders.SUBJECT, Mms.SUBJECT_CHARSET); + CHARSET_COLUMN_NAME_MAP.put(PduHeaders.RETRIEVE_TEXT, Mms.RETRIEVE_TEXT_CHARSET); + + // Encoded string field code -> column index/name map. + ENCODED_STRING_COLUMN_INDEX_MAP = new HashMap(); + ENCODED_STRING_COLUMN_INDEX_MAP.put(PduHeaders.RETRIEVE_TEXT, PDU_COLUMN_RETRIEVE_TEXT); + ENCODED_STRING_COLUMN_INDEX_MAP.put(PduHeaders.SUBJECT, PDU_COLUMN_SUBJECT); + + ENCODED_STRING_COLUMN_NAME_MAP = new HashMap(); + ENCODED_STRING_COLUMN_NAME_MAP.put(PduHeaders.RETRIEVE_TEXT, Mms.RETRIEVE_TEXT); + ENCODED_STRING_COLUMN_NAME_MAP.put(PduHeaders.SUBJECT, Mms.SUBJECT); + + // Text string field code -> column index/name map. + TEXT_STRING_COLUMN_INDEX_MAP = new HashMap(); + TEXT_STRING_COLUMN_INDEX_MAP.put(PduHeaders.CONTENT_LOCATION, PDU_COLUMN_CONTENT_LOCATION); + TEXT_STRING_COLUMN_INDEX_MAP.put(PduHeaders.CONTENT_TYPE, PDU_COLUMN_CONTENT_TYPE); + TEXT_STRING_COLUMN_INDEX_MAP.put(PduHeaders.MESSAGE_CLASS, PDU_COLUMN_MESSAGE_CLASS); + TEXT_STRING_COLUMN_INDEX_MAP.put(PduHeaders.MESSAGE_ID, PDU_COLUMN_MESSAGE_ID); + TEXT_STRING_COLUMN_INDEX_MAP.put(PduHeaders.RESPONSE_TEXT, PDU_COLUMN_RESPONSE_TEXT); + TEXT_STRING_COLUMN_INDEX_MAP.put(PduHeaders.TRANSACTION_ID, PDU_COLUMN_TRANSACTION_ID); + + TEXT_STRING_COLUMN_NAME_MAP = new HashMap(); + TEXT_STRING_COLUMN_NAME_MAP.put(PduHeaders.CONTENT_LOCATION, Mms.CONTENT_LOCATION); + TEXT_STRING_COLUMN_NAME_MAP.put(PduHeaders.CONTENT_TYPE, Mms.CONTENT_TYPE); + TEXT_STRING_COLUMN_NAME_MAP.put(PduHeaders.MESSAGE_CLASS, Mms.MESSAGE_CLASS); + TEXT_STRING_COLUMN_NAME_MAP.put(PduHeaders.MESSAGE_ID, Mms.MESSAGE_ID); + TEXT_STRING_COLUMN_NAME_MAP.put(PduHeaders.RESPONSE_TEXT, Mms.RESPONSE_TEXT); + TEXT_STRING_COLUMN_NAME_MAP.put(PduHeaders.TRANSACTION_ID, Mms.TRANSACTION_ID); + + // Octet field code -> column index/name map. + OCTET_COLUMN_INDEX_MAP = new HashMap(); + OCTET_COLUMN_INDEX_MAP.put(PduHeaders.CONTENT_CLASS, PDU_COLUMN_CONTENT_CLASS); + OCTET_COLUMN_INDEX_MAP.put(PduHeaders.DELIVERY_REPORT, PDU_COLUMN_DELIVERY_REPORT); + OCTET_COLUMN_INDEX_MAP.put(PduHeaders.MESSAGE_TYPE, PDU_COLUMN_MESSAGE_TYPE); + OCTET_COLUMN_INDEX_MAP.put(PduHeaders.MMS_VERSION, PDU_COLUMN_MMS_VERSION); + OCTET_COLUMN_INDEX_MAP.put(PduHeaders.PRIORITY, PDU_COLUMN_PRIORITY); + OCTET_COLUMN_INDEX_MAP.put(PduHeaders.READ_REPORT, PDU_COLUMN_READ_REPORT); + OCTET_COLUMN_INDEX_MAP.put(PduHeaders.READ_STATUS, PDU_COLUMN_READ_STATUS); + OCTET_COLUMN_INDEX_MAP.put(PduHeaders.REPORT_ALLOWED, PDU_COLUMN_REPORT_ALLOWED); + OCTET_COLUMN_INDEX_MAP.put(PduHeaders.RETRIEVE_STATUS, PDU_COLUMN_RETRIEVE_STATUS); + OCTET_COLUMN_INDEX_MAP.put(PduHeaders.STATUS, PDU_COLUMN_STATUS); + + OCTET_COLUMN_NAME_MAP = new HashMap(); + OCTET_COLUMN_NAME_MAP.put(PduHeaders.CONTENT_CLASS, Mms.CONTENT_CLASS); + OCTET_COLUMN_NAME_MAP.put(PduHeaders.DELIVERY_REPORT, Mms.DELIVERY_REPORT); + OCTET_COLUMN_NAME_MAP.put(PduHeaders.MESSAGE_TYPE, Mms.MESSAGE_TYPE); + OCTET_COLUMN_NAME_MAP.put(PduHeaders.MMS_VERSION, Mms.MMS_VERSION); + OCTET_COLUMN_NAME_MAP.put(PduHeaders.PRIORITY, Mms.PRIORITY); + OCTET_COLUMN_NAME_MAP.put(PduHeaders.READ_REPORT, Mms.READ_REPORT); + OCTET_COLUMN_NAME_MAP.put(PduHeaders.READ_STATUS, Mms.READ_STATUS); + OCTET_COLUMN_NAME_MAP.put(PduHeaders.REPORT_ALLOWED, Mms.REPORT_ALLOWED); + OCTET_COLUMN_NAME_MAP.put(PduHeaders.RETRIEVE_STATUS, Mms.RETRIEVE_STATUS); + OCTET_COLUMN_NAME_MAP.put(PduHeaders.STATUS, Mms.STATUS); + + // Long field code -> column index/name map. + LONG_COLUMN_INDEX_MAP = new HashMap(); + LONG_COLUMN_INDEX_MAP.put(PduHeaders.DATE, PDU_COLUMN_DATE); + LONG_COLUMN_INDEX_MAP.put(PduHeaders.DELIVERY_TIME, PDU_COLUMN_DELIVERY_TIME); + LONG_COLUMN_INDEX_MAP.put(PduHeaders.EXPIRY, PDU_COLUMN_EXPIRY); + LONG_COLUMN_INDEX_MAP.put(PduHeaders.MESSAGE_SIZE, PDU_COLUMN_MESSAGE_SIZE); + + LONG_COLUMN_NAME_MAP = new HashMap(); + LONG_COLUMN_NAME_MAP.put(PduHeaders.DATE, Mms.DATE); + LONG_COLUMN_NAME_MAP.put(PduHeaders.DELIVERY_TIME, Mms.DELIVERY_TIME); + LONG_COLUMN_NAME_MAP.put(PduHeaders.EXPIRY, Mms.EXPIRY); + LONG_COLUMN_NAME_MAP.put(PduHeaders.MESSAGE_SIZE, Mms.MESSAGE_SIZE); + + PDU_CACHE_INSTANCE = PduCache.getInstance(); + } + + private final Context mContext; + private final ContentResolver mContentResolver; + + private PduPersister(Context context) { + mContext = context; + mContentResolver = context.getContentResolver(); + } + + /** Get(or create if not exist) an instance of PduPersister */ + public static PduPersister getPduPersister(Context context) { + if ((sPersister == null) || !context.equals(sPersister.mContext)) { + sPersister = new PduPersister(context); + } + + return sPersister; + } + + private void setEncodedStringValueToHeaders( + Cursor c, int columnIndex, + PduHeaders headers, int mapColumn) { + String s = c.getString(columnIndex); + if ((s != null) && (s.length() > 0)) { + int charsetColumnIndex = CHARSET_COLUMN_INDEX_MAP.get(mapColumn); + int charset = c.getInt(charsetColumnIndex); + EncodedStringValue value = new EncodedStringValue( + charset, getBytes(s)); + headers.setEncodedStringValue(value, mapColumn); + } + } + + private void setTextStringToHeaders( + Cursor c, int columnIndex, + PduHeaders headers, int mapColumn) { + String s = c.getString(columnIndex); + if (s != null) { + headers.setTextString(getBytes(s), mapColumn); + } + } + + private void setOctetToHeaders( + Cursor c, int columnIndex, + PduHeaders headers, int mapColumn) throws InvalidHeaderValueException { + if (!c.isNull(columnIndex)) { + int b = c.getInt(columnIndex); + headers.setOctet(b, mapColumn); + } + } + + private void setLongToHeaders( + Cursor c, int columnIndex, + PduHeaders headers, int mapColumn) { + if (!c.isNull(columnIndex)) { + long l = c.getLong(columnIndex); + headers.setLongInteger(l, mapColumn); + } + } + + private Integer getIntegerFromPartColumn(Cursor c, int columnIndex) { + if (!c.isNull(columnIndex)) { + return c.getInt(columnIndex); + } + return null; + } + + private byte[] getByteArrayFromPartColumn(Cursor c, int columnIndex) { + if (!c.isNull(columnIndex)) { + return getBytes(c.getString(columnIndex)); + } + return null; + } + + private PduPart[] loadParts(long msgId) throws MmsException { + Cursor c = SqliteWrapper.query(mContext, mContentResolver, + Uri.parse("content://mms/" + msgId + "/part"), + PART_PROJECTION, null, null, null); + + PduPart[] parts = null; + + try { + if ((c == null) || (c.getCount() == 0)) { + if (LOCAL_LOGV) { + Log.v(TAG, "loadParts(" + msgId + "): no part to load."); + } + return null; + } + + int partCount = c.getCount(); + int partIdx = 0; + parts = new PduPart[partCount]; + while (c.moveToNext()) { + PduPart part = new PduPart(); + Integer charset = getIntegerFromPartColumn( + c, PART_COLUMN_CHARSET); + if (charset != null) { + part.setCharset(charset); + } + + byte[] contentDisposition = getByteArrayFromPartColumn( + c, PART_COLUMN_CONTENT_DISPOSITION); + if (contentDisposition != null) { + part.setContentDisposition(contentDisposition); + } + + byte[] contentId = getByteArrayFromPartColumn( + c, PART_COLUMN_CONTENT_ID); + if (contentId != null) { + part.setContentId(contentId); + } + + byte[] contentLocation = getByteArrayFromPartColumn( + c, PART_COLUMN_CONTENT_LOCATION); + if (contentLocation != null) { + part.setContentLocation(contentLocation); + } + + byte[] contentType = getByteArrayFromPartColumn( + c, PART_COLUMN_CONTENT_TYPE); + if (contentType != null) { + part.setContentType(contentType); + } else { + throw new MmsException("Content-Type must be set."); + } + + byte[] fileName = getByteArrayFromPartColumn( + c, PART_COLUMN_FILENAME); + if (fileName != null) { + part.setFilename(fileName); + } + + byte[] name = getByteArrayFromPartColumn( + c, PART_COLUMN_NAME); + if (name != null) { + part.setName(name); + } + + // Construct a Uri for this part. + long partId = c.getLong(PART_COLUMN_ID); + Uri partURI = Uri.parse("content://mms/part/" + partId); + part.setDataUri(partURI); + + // For images/audio/video, we won't keep their data in Part + // because their renderer accept Uri as source. + String type = toIsoString(contentType); + if (!ContentType.isImageType(type) + && !ContentType.isAudioType(type) + && !ContentType.isVideoType(type)) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + InputStream is = null; + + // Store simple string values directly in the database instead of an + // external file. This makes the text searchable and retrieval slightly + // faster. + if (ContentType.TEXT_PLAIN.equals(type) || ContentType.APP_SMIL.equals(type) + || ContentType.TEXT_HTML.equals(type)) { + String text = c.getString(PART_COLUMN_TEXT); + if (text == null) { + text = ""; + } + byte [] blob = new EncodedStringValue(text).getTextString(); + baos.write(blob, 0, blob.length); + } else { + + try { + is = mContentResolver.openInputStream(partURI); + + byte[] buffer = new byte[256]; + int len = is.read(buffer); + while (len >= 0) { + baos.write(buffer, 0, len); + len = is.read(buffer); + } + } catch (IOException e) { + Log.e(TAG, "Failed to load part data", e); + c.close(); + throw new MmsException(e); + } finally { + if (is != null) { + try { + is.close(); + } catch (IOException e) { + Log.e(TAG, "Failed to close stream", e); + } // Ignore + } + } + } + part.setData(baos.toByteArray()); + } + parts[partIdx++] = part; + } + } finally { + if (c != null) { + c.close(); + } + } + + return parts; + } + + private void loadAddress(long msgId, PduHeaders headers) { + Cursor c = SqliteWrapper.query(mContext, mContentResolver, + Uri.parse("content://mms/" + msgId + "/addr"), + new String[] { Addr.ADDRESS, Addr.CHARSET, Addr.TYPE }, + null, null, null); + + if (c != null) { + try { + while (c.moveToNext()) { + String addr = c.getString(0); + if (!TextUtils.isEmpty(addr)) { + int addrType = c.getInt(2); + switch (addrType) { + case PduHeaders.FROM: + headers.setEncodedStringValue( + new EncodedStringValue(c.getInt(1), getBytes(addr)), + addrType); + break; + case PduHeaders.TO: + case PduHeaders.CC: + case PduHeaders.BCC: + headers.appendEncodedStringValue( + new EncodedStringValue(c.getInt(1), getBytes(addr)), + addrType); + break; + default: + Log.e(TAG, "Unknown address type: " + addrType); + break; + } + } + } + } finally { + c.close(); + } + } + } + + /** + * Load a PDU from storage by given Uri. + * + * @param uri The Uri of the PDU to be loaded. + * @return A generic PDU object, it may be cast to dedicated PDU. + * @throws MmsException Failed to load some fields of a PDU. + */ + public GenericPdu load(Uri uri) throws MmsException { + PduCacheEntry cacheEntry = PDU_CACHE_INSTANCE.get(uri); + if (cacheEntry != null) { + return cacheEntry.getPdu(); + } + + Cursor c = SqliteWrapper.query(mContext, mContentResolver, uri, + PDU_PROJECTION, null, null, null); + PduHeaders headers = new PduHeaders(); + Set> set; + long msgId = ContentUris.parseId(uri); + int msgBox; + long threadId; + + try { + if ((c == null) || (c.getCount() != 1) || !c.moveToFirst()) { + throw new MmsException("Bad uri: " + uri); + } + + msgBox = c.getInt(PDU_COLUMN_MESSAGE_BOX); + threadId = c.getLong(PDU_COLUMN_THREAD_ID); + + set = ENCODED_STRING_COLUMN_INDEX_MAP.entrySet(); + for (Entry e : set) { + setEncodedStringValueToHeaders( + c, e.getValue(), headers, e.getKey()); + } + + set = TEXT_STRING_COLUMN_INDEX_MAP.entrySet(); + for (Entry e : set) { + setTextStringToHeaders( + c, e.getValue(), headers, e.getKey()); + } + + set = OCTET_COLUMN_INDEX_MAP.entrySet(); + for (Entry e : set) { + setOctetToHeaders( + c, e.getValue(), headers, e.getKey()); + } + + set = LONG_COLUMN_INDEX_MAP.entrySet(); + for (Entry e : set) { + setLongToHeaders( + c, e.getValue(), headers, e.getKey()); + } + } finally { + if (c != null) { + c.close(); + } + } + + // Check whether 'msgId' has been assigned a valid value. + if (msgId == -1L) { + throw new MmsException("Error! ID of the message: -1."); + } + + // Load address information of the MM. + loadAddress(msgId, headers); + + int msgType = headers.getOctet(PduHeaders.MESSAGE_TYPE); + PduBody body = new PduBody(); + + // For PDU which type is M_retrieve.conf or Send.req, we should + // load multiparts and put them into the body of the PDU. + if ((msgType == PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF) + || (msgType == PduHeaders.MESSAGE_TYPE_SEND_REQ)) { + PduPart[] parts = loadParts(msgId); + if (parts != null) { + int partsNum = parts.length; + for (int i = 0; i < partsNum; i++) { + body.addPart(parts[i]); + } + } + } + + GenericPdu pdu = null; + switch (msgType) { + case PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND: + pdu = new NotificationInd(headers); + break; + case PduHeaders.MESSAGE_TYPE_DELIVERY_IND: + pdu = new DeliveryInd(headers); + break; + case PduHeaders.MESSAGE_TYPE_READ_ORIG_IND: + pdu = new ReadOrigInd(headers); + break; + case PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF: + pdu = new RetrieveConf(headers, body); + break; + case PduHeaders.MESSAGE_TYPE_SEND_REQ: + pdu = new SendReq(headers, body); + break; + case PduHeaders.MESSAGE_TYPE_ACKNOWLEDGE_IND: + pdu = new AcknowledgeInd(headers); + break; + case PduHeaders.MESSAGE_TYPE_NOTIFYRESP_IND: + pdu = new NotifyRespInd(headers); + break; + case PduHeaders.MESSAGE_TYPE_READ_REC_IND: + pdu = new ReadRecInd(headers); + break; + case PduHeaders.MESSAGE_TYPE_SEND_CONF: + case PduHeaders.MESSAGE_TYPE_FORWARD_REQ: + case PduHeaders.MESSAGE_TYPE_FORWARD_CONF: + case PduHeaders.MESSAGE_TYPE_MBOX_STORE_REQ: + case PduHeaders.MESSAGE_TYPE_MBOX_STORE_CONF: + case PduHeaders.MESSAGE_TYPE_MBOX_VIEW_REQ: + case PduHeaders.MESSAGE_TYPE_MBOX_VIEW_CONF: + case PduHeaders.MESSAGE_TYPE_MBOX_UPLOAD_REQ: + case PduHeaders.MESSAGE_TYPE_MBOX_UPLOAD_CONF: + case PduHeaders.MESSAGE_TYPE_MBOX_DELETE_REQ: + case PduHeaders.MESSAGE_TYPE_MBOX_DELETE_CONF: + case PduHeaders.MESSAGE_TYPE_MBOX_DESCR: + case PduHeaders.MESSAGE_TYPE_DELETE_REQ: + case PduHeaders.MESSAGE_TYPE_DELETE_CONF: + case PduHeaders.MESSAGE_TYPE_CANCEL_REQ: + case PduHeaders.MESSAGE_TYPE_CANCEL_CONF: + throw new MmsException( + "Unsupported PDU type: " + Integer.toHexString(msgType)); + + default: + throw new MmsException( + "Unrecognized PDU type: " + Integer.toHexString(msgType)); + } + + cacheEntry = new PduCacheEntry(pdu, msgBox, threadId); + PDU_CACHE_INSTANCE.put(uri, cacheEntry); + return pdu; + } + + private void persistAddress( + long msgId, int type, EncodedStringValue[] array) { + ContentValues values = new ContentValues(3); + + for (EncodedStringValue addr : array) { + values.clear(); // Clear all values first. + values.put(Addr.ADDRESS, toIsoString(addr.getTextString())); + values.put(Addr.CHARSET, addr.getCharacterSet()); + values.put(Addr.TYPE, type); + + Uri uri = Uri.parse("content://mms/" + msgId + "/addr"); + SqliteWrapper.insert(mContext, mContentResolver, uri, values); + } + } + + public Uri persistPart(PduPart part, long msgId) + throws MmsException { + Uri uri = Uri.parse("content://mms/" + msgId + "/part"); + ContentValues values = new ContentValues(8); + + int charset = part.getCharset(); + if (charset != 0 ) { + values.put(Part.CHARSET, charset); + } + + String contentType = null; + if (part.getContentType() != null) { + contentType = toIsoString(part.getContentType()); + values.put(Part.CONTENT_TYPE, contentType); + // To ensure the SMIL part is always the first part. + if (ContentType.APP_SMIL.equals(contentType)) { + values.put(Part.SEQ, -1); + } + } else { + throw new MmsException("MIME type of the part must be set."); + } + + if (part.getFilename() != null) { + String fileName = new String(part.getFilename()); + values.put(Part.FILENAME, fileName); + } + + if (part.getName() != null) { + String name = new String(part.getName()); + values.put(Part.NAME, name); + } + + Object value = null; + if (part.getContentDisposition() != null) { + value = toIsoString(part.getContentDisposition()); + values.put(Part.CONTENT_DISPOSITION, (String) value); + } + + if (part.getContentId() != null) { + value = toIsoString(part.getContentId()); + values.put(Part.CONTENT_ID, (String) value); + } + + if (part.getContentLocation() != null) { + value = toIsoString(part.getContentLocation()); + values.put(Part.CONTENT_LOCATION, (String) value); + } + + Uri res = SqliteWrapper.insert(mContext, mContentResolver, uri, values); + if (res == null) { + throw new MmsException("Failed to persist part, return null."); + } + + persistData(part, res, contentType); + // After successfully store the data, we should update + // the dataUri of the part. + part.setDataUri(res); + + return res; + } + + /** + * Save data of the part into storage. The source data may be given + * by a byte[] or a Uri. If it's a byte[], directly save it + * into storage, otherwise load source data from the dataUri and then + * save it. If the data is an image, we may scale down it according + * to user preference. + * + * @param part The PDU part which contains data to be saved. + * @param uri The URI of the part. + * @param contentType The MIME type of the part. + * @throws MmsException Cannot find source data or error occurred + * while saving the data. + */ + private void persistData(PduPart part, Uri uri, + String contentType) + throws MmsException { + OutputStream os = null; + InputStream is = null; + + try { + byte[] data = part.getData(); + if (ContentType.TEXT_PLAIN.equals(contentType) + || ContentType.APP_SMIL.equals(contentType) + || ContentType.TEXT_HTML.equals(contentType)) { + ContentValues cv = new ContentValues(); + cv.put(TelephonyProvider.Mms.Part.TEXT, new EncodedStringValue(data).getString()); + if (mContentResolver.update(uri, cv, null, null) != 1) { + throw new MmsException("unable to update " + uri.toString()); + } + } else { + os = mContentResolver.openOutputStream(uri); + if (data == null) { + Uri dataUri = part.getDataUri(); + if ((dataUri == null) || (dataUri == uri)) { + Log.w(TAG, "Can't find data for this part."); + return; + } + is = mContentResolver.openInputStream(dataUri); + + if (LOCAL_LOGV) { + Log.v(TAG, "Saving data to: " + uri); + } + + byte[] buffer = new byte[256]; + for (int len = 0; (len = is.read(buffer)) != -1; ) { + os.write(buffer, 0, len); + } + } else { + if (LOCAL_LOGV) { + Log.v(TAG, "Saving data to: " + uri); + } + os.write(data); + } + } + } catch (FileNotFoundException e) { + Log.e(TAG, "Failed to open Input/Output stream.", e); + throw new MmsException(e); + } catch (IOException e) { + Log.e(TAG, "Failed to read/write data.", e); + throw new MmsException(e); + } finally { + if (os != null) { + try { + os.close(); + } catch (IOException e) { + Log.e(TAG, "IOException while closing: " + os, e); + } // Ignore + } + if (is != null) { + try { + is.close(); + } catch (IOException e) { + Log.e(TAG, "IOException while closing: " + is, e); + } // Ignore + } + } + } + + private void updateAddress( + long msgId, int type, EncodedStringValue[] array) { + // Delete old address information and then insert new ones. + SqliteWrapper.delete(mContext, mContentResolver, + Uri.parse("content://mms/" + msgId + "/addr"), + Addr.TYPE + "=" + type, null); + + persistAddress(msgId, type, array); + } + + /** + * Update headers of a SendReq. + * + * @param uri The PDU which need to be updated. + * @param pdu New headers. + * @throws MmsException Bad URI or updating failed. + */ + public void updateHeaders(Uri uri, SendReq sendReq) { + PDU_CACHE_INSTANCE.purge(uri); + + ContentValues values = new ContentValues(10); + byte[] contentType = sendReq.getContentType(); + if (contentType != null) { + values.put(Mms.CONTENT_TYPE, toIsoString(contentType)); + } + + long date = sendReq.getDate(); + if (date != -1) { + values.put(Mms.DATE, date); + } + + int deliveryReport = sendReq.getDeliveryReport(); + if (deliveryReport != 0) { + values.put(Mms.DELIVERY_REPORT, deliveryReport); + } + + long expiry = sendReq.getExpiry(); + if (expiry != -1) { + values.put(Mms.EXPIRY, expiry); + } + + byte[] msgClass = sendReq.getMessageClass(); + if (msgClass != null) { + values.put(Mms.MESSAGE_CLASS, toIsoString(msgClass)); + } + + int priority = sendReq.getPriority(); + if (priority != 0) { + values.put(Mms.PRIORITY, priority); + } + + int readReport = sendReq.getReadReport(); + if (readReport != 0) { + values.put(Mms.READ_REPORT, readReport); + } + + byte[] transId = sendReq.getTransactionId(); + if (transId != null) { + values.put(Mms.TRANSACTION_ID, toIsoString(transId)); + } + + EncodedStringValue subject = sendReq.getSubject(); + if (subject != null) { + values.put(Mms.SUBJECT, toIsoString(subject.getTextString())); + values.put(Mms.SUBJECT_CHARSET, subject.getCharacterSet()); + } else { + values.put(Mms.SUBJECT, ""); + } + + long messageSize = sendReq.getMessageSize(); + if (messageSize > 0) { + values.put(Mms.MESSAGE_SIZE, messageSize); + } + + PduHeaders headers = sendReq.getPduHeaders(); + HashSet recipients = new HashSet(); + for (int addrType : ADDRESS_FIELDS) { + EncodedStringValue[] array = null; + if (addrType == PduHeaders.FROM) { + EncodedStringValue v = headers.getEncodedStringValue(addrType); + if (v != null) { + array = new EncodedStringValue[1]; + array[0] = v; + } + } else { + array = headers.getEncodedStringValues(addrType); + } + + if (array != null) { + long msgId = ContentUris.parseId(uri); + updateAddress(msgId, addrType, array); + if (addrType == PduHeaders.TO) { + for (EncodedStringValue v : array) { + if (v != null) { + recipients.add(v.getString()); + } + } + } + } + } + + long threadId = Threads.getOrCreateThreadId(mContext, recipients); + values.put(Mms.THREAD_ID, threadId); + + SqliteWrapper.update(mContext, mContentResolver, uri, values, null, null); + } + + private void updatePart(Uri uri, PduPart part) throws MmsException { + ContentValues values = new ContentValues(7); + + int charset = part.getCharset(); + if (charset != 0 ) { + values.put(Part.CHARSET, charset); + } + + String contentType = null; + if (part.getContentType() != null) { + contentType = toIsoString(part.getContentType()); + values.put(Part.CONTENT_TYPE, contentType); + } else { + throw new MmsException("MIME type of the part must be set."); + } + + if (part.getFilename() != null) { + String fileName = new String(part.getFilename()); + values.put(Part.FILENAME, fileName); + } + + if (part.getName() != null) { + String name = new String(part.getName()); + values.put(Part.NAME, name); + } + + Object value = null; + if (part.getContentDisposition() != null) { + value = toIsoString(part.getContentDisposition()); + values.put(Part.CONTENT_DISPOSITION, (String) value); + } + + if (part.getContentId() != null) { + value = toIsoString(part.getContentId()); + values.put(Part.CONTENT_ID, (String) value); + } + + if (part.getContentLocation() != null) { + value = toIsoString(part.getContentLocation()); + values.put(Part.CONTENT_LOCATION, (String) value); + } + + SqliteWrapper.update(mContext, mContentResolver, uri, values, null, null); + + // Only update the data when: + // 1. New binary data supplied or + // 2. The Uri of the part is different from the current one. + if ((part.getData() != null) + || (uri != part.getDataUri())) { + persistData(part, uri, contentType); + } + } + + /** + * Update all parts of a PDU. + * + * @param uri The PDU which need to be updated. + * @param body New message body of the PDU. + * @throws MmsException Bad URI or updating failed. + */ + public void updateParts(Uri uri, PduBody body) + throws MmsException { + PduCacheEntry cacheEntry = PDU_CACHE_INSTANCE.get(uri); + if (cacheEntry != null) { + ((MultimediaMessagePdu) cacheEntry.getPdu()).setBody(body); + } + + ArrayList toBeCreated = new ArrayList(); + HashMap toBeUpdated = new HashMap(); + + int partsNum = body.getPartsNum(); + StringBuilder filter = new StringBuilder().append('('); + for (int i = 0; i < partsNum; i++) { + PduPart part = body.getPart(i); + Uri partUri = part.getDataUri(); + if ((partUri == null) || !partUri.getAuthority().startsWith("mms")) { + toBeCreated.add(part); + } else { + toBeUpdated.put(partUri, part); + + // Don't use 'i > 0' to determine whether we should append + // 'AND' since 'i = 0' may be skipped in another branch. + if (filter.length() > 1) { + filter.append(" AND "); + } + + filter.append(Part._ID); + filter.append("!="); + DatabaseUtils.appendEscapedSQLString(filter, partUri.getLastPathSegment()); + } + } + filter.append(')'); + + long msgId = ContentUris.parseId(uri); + + // Remove the parts which doesn't exist anymore. + SqliteWrapper.delete(mContext, mContentResolver, + Uri.parse(Mms.CONTENT_URI + "/" + msgId + "/part"), + filter.length() > 2 ? filter.toString() : null, null); + + // Create new parts which didn't exist before. + for (PduPart part : toBeCreated) { + persistPart(part, msgId); + } + + // Update the modified parts. + for (Map.Entry e : toBeUpdated.entrySet()) { + updatePart(e.getKey(), e.getValue()); + } + } + + /** + * Persist a PDU object to specific location in the storage. + * + * @param pdu The PDU object to be stored. + * @param uri Where to store the given PDU object. + * @return A Uri which can be used to access the stored PDU. + */ + public Uri persist(GenericPdu pdu, Uri uri) throws MmsException { + if (uri == null) { + throw new MmsException("Uri may not be null."); + } + + Integer msgBox = MESSAGE_BOX_MAP.get(uri); + if (msgBox == null) { + throw new MmsException( + "Bad destination, must be one of " + + "content://mms/inbox, content://mms/sent, " + + "content://mms/drafts, content://mms/outbox, " + + "content://mms/temp."); + } + PDU_CACHE_INSTANCE.purge(uri); + + PduHeaders header = pdu.getPduHeaders(); + PduBody body = null; + ContentValues values = new ContentValues(); + Set> set; + + set = ENCODED_STRING_COLUMN_NAME_MAP.entrySet(); + for (Entry e : set) { + int field = e.getKey(); + EncodedStringValue encodedString = header.getEncodedStringValue(field); + if (encodedString != null) { + String charsetColumn = CHARSET_COLUMN_NAME_MAP.get(field); + values.put(e.getValue(), toIsoString(encodedString.getTextString())); + values.put(charsetColumn, encodedString.getCharacterSet()); + } + } + + set = TEXT_STRING_COLUMN_NAME_MAP.entrySet(); + for (Entry e : set){ + byte[] text = header.getTextString(e.getKey()); + if (text != null) { + values.put(e.getValue(), toIsoString(text)); + } + } + + set = OCTET_COLUMN_NAME_MAP.entrySet(); + for (Entry e : set){ + int b = header.getOctet(e.getKey()); + if (b != 0) { + values.put(e.getValue(), b); + } + } + + set = LONG_COLUMN_NAME_MAP.entrySet(); + for (Entry e : set){ + long l = header.getLongInteger(e.getKey()); + if (l != -1L) { + values.put(e.getValue(), l); + } + } + + HashMap addressMap = + new HashMap(ADDRESS_FIELDS.length); + // Save address information. + for (int addrType : ADDRESS_FIELDS) { + EncodedStringValue[] array = null; + if (addrType == PduHeaders.FROM) { + EncodedStringValue v = header.getEncodedStringValue(addrType); + if (v != null) { + array = new EncodedStringValue[1]; + array[0] = v; + } + } else { + array = header.getEncodedStringValues(addrType); + } + addressMap.put(addrType, array); + } + + HashSet recipients = new HashSet(); + long threadId = DUMMY_THREAD_ID; + int msgType = pdu.getMessageType(); + // Here we only allocate thread ID for M-Notification.ind, + // M-Retrieve.conf and M-Send.req. + // Some of other PDU types may be allocated a thread ID outside + // this scope. + if ((msgType == PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND) + || (msgType == PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF) + || (msgType == PduHeaders.MESSAGE_TYPE_SEND_REQ)) { + EncodedStringValue[] array = null; + switch (msgType) { + case PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND: + case PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF: + array = addressMap.get(PduHeaders.FROM); + break; + case PduHeaders.MESSAGE_TYPE_SEND_REQ: + array = addressMap.get(PduHeaders.TO); + break; + } + + if (array != null) { + for (EncodedStringValue v : array) { + if (v != null) { + recipients.add(v.getString()); + } + } + } + threadId = Threads.getOrCreateThreadId(mContext, recipients); + } + values.put(Mms.THREAD_ID, threadId); + + // Save parts first to avoid inconsistent message is loaded + // while saving the parts. + long dummyId = System.currentTimeMillis(); // Dummy ID of the msg. + // Get body if the PDU is a RetrieveConf or SendReq. + if (pdu instanceof MultimediaMessagePdu) { + body = ((MultimediaMessagePdu) pdu).getBody(); + // Start saving parts if necessary. + if (body != null) { + int partsNum = body.getPartsNum(); + for (int i = 0; i < partsNum; i++) { + PduPart part = body.getPart(i); + persistPart(part, dummyId); + } + } + } + + Uri res = SqliteWrapper.insert(mContext, mContentResolver, uri, values); + if (res == null) { + throw new MmsException("persist() failed: return null."); + } + + // Get the real ID of the PDU and update all parts which were + // saved with the dummy ID. + long msgId = ContentUris.parseId(res); + values = new ContentValues(1); + values.put(Part.MSG_ID, msgId); + SqliteWrapper.update(mContext, mContentResolver, + Uri.parse("content://mms/" + dummyId + "/part"), + values, null, null); + // We should return the longest URI of the persisted PDU, for + // example, if input URI is "content://mms/inbox" and the _ID of + // persisted PDU is '8', we should return "content://mms/inbox/8" + // instead of "content://mms/8". + // FIXME: Should the MmsProvider be responsible for this??? + res = Uri.parse(uri + "/" + msgId); + + // Save address information. + for (int addrType : ADDRESS_FIELDS) { + EncodedStringValue[] array = addressMap.get(addrType); + if (array != null) { + persistAddress(msgId, addrType, array); + } + } + + return res; + } + + /** + * Move a PDU object from one location to another. + * + * @param from Specify the PDU object to be moved. + * @param to The destination location, should be one of the following: + * "content://mms/inbox", "content://mms/sent", + * "content://mms/drafts", "content://mms/outbox", + * "content://mms/trash". + * @return New Uri of the moved PDU. + * @throws MmsException Error occurred while moving the message. + */ + public Uri move(Uri from, Uri to) throws MmsException { + // Check whether the 'msgId' has been assigned a valid value. + long msgId = ContentUris.parseId(from); + if (msgId == -1L) { + throw new MmsException("Error! ID of the message: -1."); + } + + // Get corresponding int value of destination box. + Integer msgBox = MESSAGE_BOX_MAP.get(to); + if (msgBox == null) { + throw new MmsException( + "Bad destination, must be one of " + + "content://mms/inbox, content://mms/sent, " + + "content://mms/drafts, content://mms/outbox, " + + "content://mms/temp."); + } + + ContentValues values = new ContentValues(1); + values.put(Mms.MESSAGE_BOX, msgBox); + SqliteWrapper.update(mContext, mContentResolver, from, values, null, null); + return ContentUris.withAppendedId(to, msgId); + } + + /** + * Wrap a byte[] into a String. + */ + public static String toIsoString(byte[] bytes) { + try { + return new String(bytes, CharacterSets.MIMENAME_ISO_8859_1); + } catch (UnsupportedEncodingException e) { + // Impossible to reach here! + Log.e(TAG, "ISO_8859_1 must be supported!", e); + return ""; + } + } + + /** + * Unpack a given String into a byte[]. + */ + public static byte[] getBytes(String data) { + try { + return data.getBytes(CharacterSets.MIMENAME_ISO_8859_1); + } catch (UnsupportedEncodingException e) { + // Impossible to reach here! + Log.e(TAG, "ISO_8859_1 must be supported!", e); + return new byte[0]; + } + } + + /** + * Remove all objects in the temporary path. + */ + public void release() { + Uri uri = Uri.parse(TEMPORARY_DRM_OBJECT_URI); + SqliteWrapper.delete(mContext, mContentResolver, uri, null, null); + } + + /** + * Find all messages to be sent or downloaded before certain time. + */ + public Cursor getPendingMessages(long dueTime) { + Uri.Builder uriBuilder = PendingMessages.CONTENT_URI.buildUpon(); + uriBuilder.appendQueryParameter("protocol", "mms"); + + String selection = PendingMessages.ERROR_TYPE + " < ?" + + " AND " + PendingMessages.DUE_TIME + " <= ?"; + + String[] selectionArgs = new String[] { + String.valueOf(MmsSms.ERR_TYPE_GENERIC_PERMANENT), + String.valueOf(dueTime) + }; + + return SqliteWrapper.query(mContext, mContentResolver, + uriBuilder.build(), null, selection, selectionArgs, + PendingMessages.DUE_TIME); + } +} diff --git a/mms-common/java/com/android/common/mms/pdu/QuotedPrintable.java b/mms-common/java/com/android/common/mms/pdu/QuotedPrintable.java new file mode 100644 index 000000000..e9da7df1f --- /dev/null +++ b/mms-common/java/com/android/common/mms/pdu/QuotedPrintable.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2007 Esmertec AG. + * 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. + */ + +package com.android.mmscommon.mms.pdu; + +import java.io.ByteArrayOutputStream; + +public class QuotedPrintable { + private static byte ESCAPE_CHAR = '='; + + /** + * Decodes an array quoted-printable characters into an array of original bytes. + * Escaped characters are converted back to their original representation. + * + *

+ * This function implements a subset of + * quoted-printable encoding specification (rule #1 and rule #2) + * as defined in RFC 1521. + *

+ * + * @param bytes array of quoted-printable characters + * @return array of original bytes, + * null if quoted-printable decoding is unsuccessful. + */ + public static final byte[] decodeQuotedPrintable(byte[] bytes) { + if (bytes == null) { + return null; + } + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + for (int i = 0; i < bytes.length; i++) { + int b = bytes[i]; + if (b == ESCAPE_CHAR) { + try { + if('\r' == (char)bytes[i + 1] && + '\n' == (char)bytes[i + 2]) { + i += 2; + continue; + } + int u = Character.digit((char) bytes[++i], 16); + int l = Character.digit((char) bytes[++i], 16); + if (u == -1 || l == -1) { + return null; + } + buffer.write((char) ((u << 4) + l)); + } catch (ArrayIndexOutOfBoundsException e) { + return null; + } + } else { + buffer.write(b); + } + } + return buffer.toByteArray(); + } +} diff --git a/mms-common/java/com/android/common/mms/pdu/ReadOrigInd.java b/mms-common/java/com/android/common/mms/pdu/ReadOrigInd.java new file mode 100644 index 000000000..967878486 --- /dev/null +++ b/mms-common/java/com/android/common/mms/pdu/ReadOrigInd.java @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2007 Esmertec AG. + * 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. + */ + +package com.android.mmscommon.mms.pdu; + +import com.android.mmscommon.EncodedStringValue; +import com.android.mmscommon.InvalidHeaderValueException; +import com.android.mmscommon.PduHeaders; + +public class ReadOrigInd extends GenericPdu { + /** + * Empty constructor. + * Since the Pdu corresponding to this class is constructed + * by the Proxy-Relay server, this class is only instantiated + * by the Pdu Parser. + * + * @throws InvalidHeaderValueException if error occurs. + */ + public ReadOrigInd() throws InvalidHeaderValueException { + super(); + setMessageType(PduHeaders.MESSAGE_TYPE_READ_ORIG_IND); + } + + /** + * Constructor with given headers. + * + * @param headers Headers for this PDU. + */ + ReadOrigInd(PduHeaders headers) { + super(headers); + } + + /** + * Get Date value. + * + * @return the value + */ + public long getDate() { + return mPduHeaders.getLongInteger(PduHeaders.DATE); + } + + /** + * Set Date value. + * + * @param value the value + */ + public void setDate(long value) { + mPduHeaders.setLongInteger(value, PduHeaders.DATE); + } + + /** + * Get From value. + * From-value = Value-length + * (Address-present-token Encoded-string-value | Insert-address-token) + * + * @return the value + */ + public EncodedStringValue getFrom() { + return mPduHeaders.getEncodedStringValue(PduHeaders.FROM); + } + + /** + * Set From value. + * + * @param value the value + * @throws NullPointerException if the value is null. + */ + public void setFrom(EncodedStringValue value) { + mPduHeaders.setEncodedStringValue(value, PduHeaders.FROM); + } + + /** + * Get Message-ID value. + * + * @return the value + */ + public byte[] getMessageId() { + return mPduHeaders.getTextString(PduHeaders.MESSAGE_ID); + } + + /** + * Set Message-ID value. + * + * @param value the value + * @throws NullPointerException if the value is null. + */ + public void setMessageId(byte[] value) { + mPduHeaders.setTextString(value, PduHeaders.MESSAGE_ID); + } + + /** + * Get X-MMS-Read-status value. + * + * @return the value + */ + public int getReadStatus() { + return mPduHeaders.getOctet(PduHeaders.READ_STATUS); + } + + /** + * Set X-MMS-Read-status value. + * + * @param value the value + * @throws InvalidHeaderValueException if the value is invalid. + */ + public void setReadStatus(int value) throws InvalidHeaderValueException { + mPduHeaders.setOctet(value, PduHeaders.READ_STATUS); + } + + /** + * Get To value. + * + * @return the value + */ + public EncodedStringValue[] getTo() { + return mPduHeaders.getEncodedStringValues(PduHeaders.TO); + } + + /** + * Set To value. + * + * @param value the value + * @throws NullPointerException if the value is null. + */ + public void setTo(EncodedStringValue[] value) { + mPduHeaders.setEncodedStringValues(value, PduHeaders.TO); + } + + /* + * Optional, not supported header fields: + * + * public byte[] getApplicId() {return null;} + * public void setApplicId(byte[] value) {} + * + * public byte[] getAuxApplicId() {return null;} + * public void getAuxApplicId(byte[] value) {} + * + * public byte[] getReplyApplicId() {return 0x00;} + * public void setReplyApplicId(byte[] value) {} + */ +} diff --git a/mms-common/java/com/android/common/mms/pdu/ReadRecInd.java b/mms-common/java/com/android/common/mms/pdu/ReadRecInd.java new file mode 100644 index 000000000..c1efbbc37 --- /dev/null +++ b/mms-common/java/com/android/common/mms/pdu/ReadRecInd.java @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2007 Esmertec AG. + * 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. + */ + +package com.android.mmscommon.mms.pdu; + +import com.android.mmscommon.EncodedStringValue; +import com.android.mmscommon.InvalidHeaderValueException; +import com.android.mmscommon.PduHeaders; + +public class ReadRecInd extends GenericPdu { + /** + * Constructor, used when composing a M-ReadRec.ind pdu. + * + * @param from the from value + * @param messageId the message ID value + * @param mmsVersion current viersion of mms + * @param readStatus the read status value + * @param to the to value + * @throws InvalidHeaderValueException if parameters are invalid. + * NullPointerException if messageId or to is null. + */ + public ReadRecInd(EncodedStringValue from, + byte[] messageId, + int mmsVersion, + int readStatus, + EncodedStringValue[] to) throws InvalidHeaderValueException { + super(); + setMessageType(PduHeaders.MESSAGE_TYPE_READ_REC_IND); + setFrom(from); + setMessageId(messageId); + setMmsVersion(mmsVersion); + setTo(to); + setReadStatus(readStatus); + } + + /** + * Constructor with given headers. + * + * @param headers Headers for this PDU. + */ + ReadRecInd(PduHeaders headers) { + super(headers); + } + + /** + * Get Date value. + * + * @return the value + */ + public long getDate() { + return mPduHeaders.getLongInteger(PduHeaders.DATE); + } + + /** + * Set Date value. + * + * @param value the value + */ + public void setDate(long value) { + mPduHeaders.setLongInteger(value, PduHeaders.DATE); + } + + /** + * Get Message-ID value. + * + * @return the value + */ + public byte[] getMessageId() { + return mPduHeaders.getTextString(PduHeaders.MESSAGE_ID); + } + + /** + * Set Message-ID value. + * + * @param value the value + * @throws NullPointerException if the value is null. + */ + public void setMessageId(byte[] value) { + mPduHeaders.setTextString(value, PduHeaders.MESSAGE_ID); + } + + /** + * Get To value. + * + * @return the value + */ + public EncodedStringValue[] getTo() { + return mPduHeaders.getEncodedStringValues(PduHeaders.TO); + } + + /** + * Set To value. + * + * @param value the value + * @throws NullPointerException if the value is null. + */ + public void setTo(EncodedStringValue[] value) { + mPduHeaders.setEncodedStringValues(value, PduHeaders.TO); + } + + /** + * Get X-MMS-Read-status value. + * + * @return the value + */ + public int getReadStatus() { + return mPduHeaders.getOctet(PduHeaders.READ_STATUS); + } + + /** + * Set X-MMS-Read-status value. + * + * @param value the value + * @throws InvalidHeaderValueException if the value is invalid. + */ + public void setReadStatus(int value) throws InvalidHeaderValueException { + mPduHeaders.setOctet(value, PduHeaders.READ_STATUS); + } + + /* + * Optional, not supported header fields: + * + * public byte[] getApplicId() {return null;} + * public void setApplicId(byte[] value) {} + * + * public byte[] getAuxApplicId() {return null;} + * public void getAuxApplicId(byte[] value) {} + * + * public byte[] getReplyApplicId() {return 0x00;} + * public void setReplyApplicId(byte[] value) {} + */ +} diff --git a/mms-common/java/com/android/common/mms/pdu/RetrieveConf.java b/mms-common/java/com/android/common/mms/pdu/RetrieveConf.java new file mode 100644 index 000000000..442949e95 --- /dev/null +++ b/mms-common/java/com/android/common/mms/pdu/RetrieveConf.java @@ -0,0 +1,302 @@ +/* + * Copyright (C) 2007 Esmertec AG. + * 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. + */ + +package com.android.mmscommon.mms.pdu; + +import com.android.mmscommon.EncodedStringValue; +import com.android.mmscommon.InvalidHeaderValueException; +import com.android.mmscommon.PduHeaders; + +/** + * M-Retrive.conf Pdu. + */ +public class RetrieveConf extends MultimediaMessagePdu { + /** + * Empty constructor. + * Since the Pdu corresponding to this class is constructed + * by the Proxy-Relay server, this class is only instantiated + * by the Pdu Parser. + * + * @throws InvalidHeaderValueException if error occurs. + */ + public RetrieveConf() throws InvalidHeaderValueException { + super(); + setMessageType(PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF); + } + + /** + * Constructor with given headers. + * + * @param headers Headers for this PDU. + */ + RetrieveConf(PduHeaders headers) { + super(headers); + } + + /** + * Constructor with given headers and body + * + * @param headers Headers for this PDU. + * @param body Body of this PDu. + */ + RetrieveConf(PduHeaders headers, PduBody body) { + super(headers, body); + } + + /** + * Get CC value. + * + * @return the value + */ + public EncodedStringValue[] getCc() { + return mPduHeaders.getEncodedStringValues(PduHeaders.CC); + } + + /** + * Add a "CC" value. + * + * @param value the value + * @throws NullPointerException if the value is null. + */ + public void addCc(EncodedStringValue value) { + mPduHeaders.appendEncodedStringValue(value, PduHeaders.CC); + } + + /** + * Get Content-type value. + * + * @return the value + */ + public byte[] getContentType() { + return mPduHeaders.getTextString(PduHeaders.CONTENT_TYPE); + } + + /** + * Set Content-type value. + * + * @param value the value + * @throws NullPointerException if the value is null. + */ + public void setContentType(byte[] value) { + mPduHeaders.setTextString(value, PduHeaders.CONTENT_TYPE); + } + + /** + * Get X-Mms-Delivery-Report value. + * + * @return the value + */ + public int getDeliveryReport() { + return mPduHeaders.getOctet(PduHeaders.DELIVERY_REPORT); + } + + /** + * Set X-Mms-Delivery-Report value. + * + * @param value the value + * @throws InvalidHeaderValueException if the value is invalid. + */ + public void setDeliveryReport(int value) throws InvalidHeaderValueException { + mPduHeaders.setOctet(value, PduHeaders.DELIVERY_REPORT); + } + + /** + * Get From value. + * From-value = Value-length + * (Address-present-token Encoded-string-value | Insert-address-token) + * + * @return the value + */ + public EncodedStringValue getFrom() { + return mPduHeaders.getEncodedStringValue(PduHeaders.FROM); + } + + /** + * Set From value. + * + * @param value the value + * @throws NullPointerException if the value is null. + */ + public void setFrom(EncodedStringValue value) { + mPduHeaders.setEncodedStringValue(value, PduHeaders.FROM); + } + + /** + * Get X-Mms-Message-Class value. + * Message-class-value = Class-identifier | Token-text + * Class-identifier = Personal | Advertisement | Informational | Auto + * + * @return the value + */ + public byte[] getMessageClass() { + return mPduHeaders.getTextString(PduHeaders.MESSAGE_CLASS); + } + + /** + * Set X-Mms-Message-Class value. + * + * @param value the value + * @throws NullPointerException if the value is null. + */ + public void setMessageClass(byte[] value) { + mPduHeaders.setTextString(value, PduHeaders.MESSAGE_CLASS); + } + + /** + * Get Message-ID value. + * + * @return the value + */ + public byte[] getMessageId() { + return mPduHeaders.getTextString(PduHeaders.MESSAGE_ID); + } + + /** + * Set Message-ID value. + * + * @param value the value + * @throws NullPointerException if the value is null. + */ + public void setMessageId(byte[] value) { + mPduHeaders.setTextString(value, PduHeaders.MESSAGE_ID); + } + + /** + * Get X-Mms-Read-Report value. + * + * @return the value + */ + public int getReadReport() { + return mPduHeaders.getOctet(PduHeaders.READ_REPORT); + } + + /** + * Set X-Mms-Read-Report value. + * + * @param value the value + * @throws InvalidHeaderValueException if the value is invalid. + */ + public void setReadReport(int value) throws InvalidHeaderValueException { + mPduHeaders.setOctet(value, PduHeaders.READ_REPORT); + } + + /** + * Get X-Mms-Retrieve-Status value. + * + * @return the value + */ + public int getRetrieveStatus() { + return mPduHeaders.getOctet(PduHeaders.RETRIEVE_STATUS); + } + + /** + * Set X-Mms-Retrieve-Status value. + * + * @param value the value + * @throws InvalidHeaderValueException if the value is invalid. + */ + public void setRetrieveStatus(int value) throws InvalidHeaderValueException { + mPduHeaders.setOctet(value, PduHeaders.RETRIEVE_STATUS); + } + + /** + * Get X-Mms-Retrieve-Text value. + * + * @return the value + */ + public EncodedStringValue getRetrieveText() { + return mPduHeaders.getEncodedStringValue(PduHeaders.RETRIEVE_TEXT); + } + + /** + * Set X-Mms-Retrieve-Text value. + * + * @param value the value + * @throws NullPointerException if the value is null. + */ + public void setRetrieveText(EncodedStringValue value) { + mPduHeaders.setEncodedStringValue(value, PduHeaders.RETRIEVE_TEXT); + } + + /** + * Get X-Mms-Transaction-Id. + * + * @return the value + */ + public byte[] getTransactionId() { + return mPduHeaders.getTextString(PduHeaders.TRANSACTION_ID); + } + + /** + * Set X-Mms-Transaction-Id. + * + * @param value the value + * @throws NullPointerException if the value is null. + */ + public void setTransactionId(byte[] value) { + mPduHeaders.setTextString(value, PduHeaders.TRANSACTION_ID); + } + + /* + * Optional, not supported header fields: + * + * public byte[] getApplicId() {return null;} + * public void setApplicId(byte[] value) {} + * + * public byte[] getAuxApplicId() {return null;} + * public void getAuxApplicId(byte[] value) {} + * + * public byte getContentClass() {return 0x00;} + * public void setApplicId(byte value) {} + * + * public byte getDrmContent() {return 0x00;} + * public void setDrmContent(byte value) {} + * + * public byte getDistributionIndicator() {return 0x00;} + * public void setDistributionIndicator(byte value) {} + * + * public PreviouslySentByValue getPreviouslySentBy() {return null;} + * public void setPreviouslySentBy(PreviouslySentByValue value) {} + * + * public PreviouslySentDateValue getPreviouslySentDate() {} + * public void setPreviouslySentDate(PreviouslySentDateValue value) {} + * + * public MmFlagsValue getMmFlags() {return null;} + * public void setMmFlags(MmFlagsValue value) {} + * + * public MmStateValue getMmState() {return null;} + * public void getMmState(MmStateValue value) {} + * + * public byte[] getReplaceId() {return 0x00;} + * public void setReplaceId(byte[] value) {} + * + * public byte[] getReplyApplicId() {return 0x00;} + * public void setReplyApplicId(byte[] value) {} + * + * public byte getReplyCharging() {return 0x00;} + * public void setReplyCharging(byte value) {} + * + * public byte getReplyChargingDeadline() {return 0x00;} + * public void setReplyChargingDeadline(byte value) {} + * + * public byte[] getReplyChargingId() {return 0x00;} + * public void setReplyChargingId(byte[] value) {} + * + * public long getReplyChargingSize() {return 0;} + * public void setReplyChargingSize(long value) {} + */ +} diff --git a/mms-common/java/com/android/common/mms/pdu/SendConf.java b/mms-common/java/com/android/common/mms/pdu/SendConf.java new file mode 100644 index 000000000..0a57b6b2d --- /dev/null +++ b/mms-common/java/com/android/common/mms/pdu/SendConf.java @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2007 Esmertec AG. + * 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. + */ + +package com.android.mmscommon.mms.pdu; + +import com.android.mmscommon.EncodedStringValue; +import com.android.mmscommon.InvalidHeaderValueException; +import com.android.mmscommon.PduHeaders; + +public class SendConf extends GenericPdu { + /** + * Empty constructor. + * Since the Pdu corresponding to this class is constructed + * by the Proxy-Relay server, this class is only instantiated + * by the Pdu Parser. + * + * @throws InvalidHeaderValueException if error occurs. + */ + public SendConf() throws InvalidHeaderValueException { + super(); + setMessageType(PduHeaders.MESSAGE_TYPE_SEND_CONF); + } + + /** + * Constructor with given headers. + * + * @param headers Headers for this PDU. + */ + SendConf(PduHeaders headers) { + super(headers); + } + + /** + * Get Message-ID value. + * + * @return the value + */ + public byte[] getMessageId() { + return mPduHeaders.getTextString(PduHeaders.MESSAGE_ID); + } + + /** + * Set Message-ID value. + * + * @param value the value + * @throws NullPointerException if the value is null. + */ + public void setMessageId(byte[] value) { + mPduHeaders.setTextString(value, PduHeaders.MESSAGE_ID); + } + + /** + * Get X-Mms-Response-Status. + * + * @return the value + */ + public int getResponseStatus() { + return mPduHeaders.getOctet(PduHeaders.RESPONSE_STATUS); + } + + /** + * Set X-Mms-Response-Status. + * + * @param value the values + * @throws InvalidHeaderValueException if the value is invalid. + */ + public void setResponseStatus(int value) throws InvalidHeaderValueException { + mPduHeaders.setOctet(value, PduHeaders.RESPONSE_STATUS); + } + + /** + * Get X-Mms-Transaction-Id field value. + * + * @return the X-Mms-Report-Allowed value + */ + public byte[] getTransactionId() { + return mPduHeaders.getTextString(PduHeaders.TRANSACTION_ID); + } + + /** + * Set X-Mms-Transaction-Id field value. + * + * @param value the value + * @throws NullPointerException if the value is null. + */ + public void setTransactionId(byte[] value) { + mPduHeaders.setTextString(value, PduHeaders.TRANSACTION_ID); + } + + /* + * Optional, not supported header fields: + * + * public byte[] getContentLocation() {return null;} + * public void setContentLocation(byte[] value) {} + * + * public EncodedStringValue getResponseText() {return null;} + * public void setResponseText(EncodedStringValue value) {} + * + * public byte getStoreStatus() {return 0x00;} + * public void setStoreStatus(byte value) {} + * + * public byte[] getStoreStatusText() {return null;} + * public void setStoreStatusText(byte[] value) {} + */ +} diff --git a/mms-common/java/com/android/common/mms/pdu/SendReq.java b/mms-common/java/com/android/common/mms/pdu/SendReq.java new file mode 100644 index 000000000..5da4719ae --- /dev/null +++ b/mms-common/java/com/android/common/mms/pdu/SendReq.java @@ -0,0 +1,347 @@ +/* + * Copyright (C) 2007-2008 Esmertec AG. + * Copyright (C) 2007-2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.mmscommon.mms.pdu; + +import android.util.Log; + +import com.android.mmscommon.EncodedStringValue; +import com.android.mmscommon.InvalidHeaderValueException; +import com.android.mmscommon.PduHeaders; + +public class SendReq extends MultimediaMessagePdu { + private static final String TAG = "SendReq"; + + public SendReq() { + super(); + + try { + setMessageType(PduHeaders.MESSAGE_TYPE_SEND_REQ); + setMmsVersion(PduHeaders.CURRENT_MMS_VERSION); + // FIXME: Content-type must be decided according to whether + // SMIL part present. + setContentType("application/vnd.wap.multipart.related".getBytes()); + setFrom(new EncodedStringValue(PduHeaders.FROM_INSERT_ADDRESS_TOKEN_STR.getBytes())); + setTransactionId(generateTransactionId()); + } catch (InvalidHeaderValueException e) { + // Impossible to reach here since all headers we set above are valid. + Log.e(TAG, "Unexpected InvalidHeaderValueException.", e); + throw new RuntimeException(e); + } + } + + private byte[] generateTransactionId() { + String transactionId = "T" + Long.toHexString(System.currentTimeMillis()); + return transactionId.getBytes(); + } + + /** + * Constructor, used when composing a M-Send.req pdu. + * + * @param contentType the content type value + * @param from the from value + * @param mmsVersion current viersion of mms + * @param transactionId the transaction-id value + * @throws InvalidHeaderValueException if parameters are invalid. + * NullPointerException if contentType, form or transactionId is null. + */ + public SendReq(byte[] contentType, + EncodedStringValue from, + int mmsVersion, + byte[] transactionId) throws InvalidHeaderValueException { + super(); + setMessageType(PduHeaders.MESSAGE_TYPE_SEND_REQ); + setContentType(contentType); + setFrom(from); + setMmsVersion(mmsVersion); + setTransactionId(transactionId); + } + + /** + * Constructor with given headers. + * + * @param headers Headers for this PDU. + */ + SendReq(PduHeaders headers) { + super(headers); + } + + /** + * Constructor with given headers and body + * + * @param headers Headers for this PDU. + * @param body Body of this PDu. + */ + SendReq(PduHeaders headers, PduBody body) { + super(headers, body); + } + + /** + * Get Bcc value. + * + * @return the value + */ + public EncodedStringValue[] getBcc() { + return mPduHeaders.getEncodedStringValues(PduHeaders.BCC); + } + + /** + * Add a "BCC" value. + * + * @param value the value + * @throws NullPointerException if the value is null. + */ + public void addBcc(EncodedStringValue value) { + mPduHeaders.appendEncodedStringValue(value, PduHeaders.BCC); + } + + /** + * Set "BCC" value. + * + * @param value the value + * @throws NullPointerException if the value is null. + */ + public void setBcc(EncodedStringValue[] value) { + mPduHeaders.setEncodedStringValues(value, PduHeaders.BCC); + } + + /** + * Get CC value. + * + * @return the value + */ + public EncodedStringValue[] getCc() { + return mPduHeaders.getEncodedStringValues(PduHeaders.CC); + } + + /** + * Add a "CC" value. + * + * @param value the value + * @throws NullPointerException if the value is null. + */ + public void addCc(EncodedStringValue value) { + mPduHeaders.appendEncodedStringValue(value, PduHeaders.CC); + } + + /** + * Set "CC" value. + * + * @param value the value + * @throws NullPointerException if the value is null. + */ + public void setCc(EncodedStringValue[] value) { + mPduHeaders.setEncodedStringValues(value, PduHeaders.CC); + } + + /** + * Get Content-type value. + * + * @return the value + */ + public byte[] getContentType() { + return mPduHeaders.getTextString(PduHeaders.CONTENT_TYPE); + } + + /** + * Set Content-type value. + * + * @param value the value + * @throws NullPointerException if the value is null. + */ + public void setContentType(byte[] value) { + mPduHeaders.setTextString(value, PduHeaders.CONTENT_TYPE); + } + + /** + * Get X-Mms-Delivery-Report value. + * + * @return the value + */ + public int getDeliveryReport() { + return mPduHeaders.getOctet(PduHeaders.DELIVERY_REPORT); + } + + /** + * Set X-Mms-Delivery-Report value. + * + * @param value the value + * @throws InvalidHeaderValueException if the value is invalid. + */ + public void setDeliveryReport(int value) throws InvalidHeaderValueException { + mPduHeaders.setOctet(value, PduHeaders.DELIVERY_REPORT); + } + + /** + * Get X-Mms-Expiry value. + * + * Expiry-value = Value-length + * (Absolute-token Date-value | Relative-token Delta-seconds-value) + * + * @return the value + */ + public long getExpiry() { + return mPduHeaders.getLongInteger(PduHeaders.EXPIRY); + } + + /** + * Set X-Mms-Expiry value. + * + * @param value the value + */ + public void setExpiry(long value) { + mPduHeaders.setLongInteger(value, PduHeaders.EXPIRY); + } + + /** + * Get X-Mms-MessageSize value. + * + * Expiry-value = size of message + * + * @return the value + */ + public long getMessageSize() { + return mPduHeaders.getLongInteger(PduHeaders.MESSAGE_SIZE); + } + + /** + * Set X-Mms-MessageSize value. + * + * @param value the value + */ + public void setMessageSize(long value) { + mPduHeaders.setLongInteger(value, PduHeaders.MESSAGE_SIZE); + } + + /** + * Get X-Mms-Message-Class value. + * Message-class-value = Class-identifier | Token-text + * Class-identifier = Personal | Advertisement | Informational | Auto + * + * @return the value + */ + public byte[] getMessageClass() { + return mPduHeaders.getTextString(PduHeaders.MESSAGE_CLASS); + } + + /** + * Set X-Mms-Message-Class value. + * + * @param value the value + * @throws NullPointerException if the value is null. + */ + public void setMessageClass(byte[] value) { + mPduHeaders.setTextString(value, PduHeaders.MESSAGE_CLASS); + } + + /** + * Get X-Mms-Read-Report value. + * + * @return the value + */ + public int getReadReport() { + return mPduHeaders.getOctet(PduHeaders.READ_REPORT); + } + + /** + * Set X-Mms-Read-Report value. + * + * @param value the value + * @throws InvalidHeaderValueException if the value is invalid. + */ + public void setReadReport(int value) throws InvalidHeaderValueException { + mPduHeaders.setOctet(value, PduHeaders.READ_REPORT); + } + + /** + * Set "To" value. + * + * @param value the value + * @throws NullPointerException if the value is null. + */ + public void setTo(EncodedStringValue[] value) { + mPduHeaders.setEncodedStringValues(value, PduHeaders.TO); + } + + /** + * Get X-Mms-Transaction-Id field value. + * + * @return the X-Mms-Report-Allowed value + */ + public byte[] getTransactionId() { + return mPduHeaders.getTextString(PduHeaders.TRANSACTION_ID); + } + + /** + * Set X-Mms-Transaction-Id field value. + * + * @param value the value + * @throws NullPointerException if the value is null. + */ + public void setTransactionId(byte[] value) { + mPduHeaders.setTextString(value, PduHeaders.TRANSACTION_ID); + } + + /* + * Optional, not supported header fields: + * + * public byte getAdaptationAllowed() {return 0}; + * public void setAdaptationAllowed(btye value) {}; + * + * public byte[] getApplicId() {return null;} + * public void setApplicId(byte[] value) {} + * + * public byte[] getAuxApplicId() {return null;} + * public void getAuxApplicId(byte[] value) {} + * + * public byte getContentClass() {return 0x00;} + * public void setApplicId(byte value) {} + * + * public long getDeliveryTime() {return 0}; + * public void setDeliveryTime(long value) {}; + * + * public byte getDrmContent() {return 0x00;} + * public void setDrmContent(byte value) {} + * + * public MmFlagsValue getMmFlags() {return null;} + * public void setMmFlags(MmFlagsValue value) {} + * + * public MmStateValue getMmState() {return null;} + * public void getMmState(MmStateValue value) {} + * + * public byte[] getReplyApplicId() {return 0x00;} + * public void setReplyApplicId(byte[] value) {} + * + * public byte getReplyCharging() {return 0x00;} + * public void setReplyCharging(byte value) {} + * + * public byte getReplyChargingDeadline() {return 0x00;} + * public void setReplyChargingDeadline(byte value) {} + * + * public byte[] getReplyChargingId() {return 0x00;} + * public void setReplyChargingId(byte[] value) {} + * + * public long getReplyChargingSize() {return 0;} + * public void setReplyChargingSize(long value) {} + * + * public byte[] getReplyApplicId() {return 0x00;} + * public void setReplyApplicId(byte[] value) {} + * + * public byte getStore() {return 0x00;} + * public void setStore(byte value) {} + */ +} diff --git a/mms-common/java/com/android/common/mms/telephony/TelephonyProvider.java b/mms-common/java/com/android/common/mms/telephony/TelephonyProvider.java new file mode 100644 index 000000000..0237bc2eb --- /dev/null +++ b/mms-common/java/com/android/common/mms/telephony/TelephonyProvider.java @@ -0,0 +1,1790 @@ +/* + * Copyright (C) 2006 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.mmscommon.telephony; + +import java.util.HashSet; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import android.content.ContentResolver; +import android.content.ContentValues; +import android.content.Context; +import android.content.Intent; +import android.database.Cursor; +import android.net.Uri; +import android.provider.BaseColumns; +import android.telephony.SmsMessage; +import android.text.TextUtils; +import android.util.Config; +import android.util.Log; + +import com.android.common.Patterns; +import android.database.sqlite.SqliteWrapper; + +/** + * The Telephony provider contains data related to phone operation. + * + * @hide + */ + +// This is a copy of the private TelephoneProvider.java file found in: +// com.android.providers.telephony +// TODO: keep these files in sync. + +public final class TelephonyProvider { + private static final String TAG = "Telephony"; + private static final boolean DEBUG = true; + private static final boolean LOCAL_LOGV = DEBUG ? Config.LOGD : Config.LOGV; + +// public static final Pattern EMAIL_ADDRESS +// = Pattern.compile( +// "[a-zA-Z0-9\\+\\.\\_\\%\\-]{1,256}" + +// "\\@" + +// "[a-zA-Z0-9][a-zA-Z0-9\\-]{0,64}" + +// "(" + +// "\\." + +// "[a-zA-Z0-9][a-zA-Z0-9\\-]{0,25}" + +// ")+" +// ); +// +// /** +// * This pattern is intended for searching for things that look like they +// * might be phone numbers in arbitrary text, not for validating whether +// * something is in fact a phone number. It will miss many things that +// * are legitimate phone numbers. +// * +// *

The pattern matches the following: +// *

    +// *
  • Optionally, a + sign followed immediately by one or more digits. Spaces, dots, or dashes +// * may follow. +// *
  • Optionally, sets of digits in parentheses, separated by spaces, dots, or dashes. +// *
  • A string starting and ending with a digit, containing digits, spaces, dots, and/or dashes. +// *
+// */ +// public static final Pattern PHONE +// = Pattern.compile( // sdd = space, dot, or dash +// "(\\+[0-9]+[\\- \\.]*)?" // +* +// + "(\\([0-9]+\\)[\\- \\.]*)?" // ()* +// + "([0-9][0-9\\- \\.][0-9\\- \\.]+[0-9])"); // + + + // Constructor + public TelephonyProvider() { + } + + /** + * Base columns for tables that contain text based SMSs. + */ + public interface TextBasedSmsColumns { + /** + * The type of the message + *

Type: INTEGER

+ */ + public static final String TYPE = "type"; + + public static final int MESSAGE_TYPE_ALL = 0; + public static final int MESSAGE_TYPE_INBOX = 1; + public static final int MESSAGE_TYPE_SENT = 2; + public static final int MESSAGE_TYPE_DRAFT = 3; + public static final int MESSAGE_TYPE_OUTBOX = 4; + public static final int MESSAGE_TYPE_FAILED = 5; // for failed outgoing messages + public static final int MESSAGE_TYPE_QUEUED = 6; // for messages to send later + + + /** + * The thread ID of the message + *

Type: INTEGER

+ */ + public static final String THREAD_ID = "thread_id"; + + /** + * The address of the other party + *

Type: TEXT

+ */ + public static final String ADDRESS = "address"; + + /** + * The person ID of the sender + *

Type: INTEGER (long)

+ */ + public static final String PERSON_ID = "person"; + + /** + * The date the message was sent + *

Type: INTEGER (long)

+ */ + public static final String DATE = "date"; + + /** + * Has the message been read + *

Type: INTEGER (boolean)

+ */ + public static final String READ = "read"; + + /** + * The TP-Status value for the message, or -1 if no status has + * been received + */ + public static final String STATUS = "status"; + + public static final int STATUS_NONE = -1; + public static final int STATUS_COMPLETE = 0; + public static final int STATUS_PENDING = 64; + public static final int STATUS_FAILED = 128; + + /** + * The subject of the message, if present + *

Type: TEXT

+ */ + public static final String SUBJECT = "subject"; + + /** + * The body of the message + *

Type: TEXT

+ */ + public static final String BODY = "body"; + + /** + * The id of the sender of the conversation, if present + *

Type: INTEGER (reference to item in content://contacts/people)

+ */ + public static final String PERSON = "person"; + + /** + * The protocol identifier code + *

Type: INTEGER

+ */ + public static final String PROTOCOL = "protocol"; + + /** + * Whether the TP-Reply-Path bit was set on this message + *

Type: BOOLEAN

+ */ + public static final String REPLY_PATH_PRESENT = "reply_path_present"; + + /** + * The service center (SC) through which to send the message, if present + *

Type: TEXT

+ */ + public static final String SERVICE_CENTER = "service_center"; + + /** + * Has the message been locked? + *

Type: INTEGER (boolean)

+ */ + public static final String LOCKED = "locked"; + + /** + * Error code associated with sending or receiving this message + *

Type: INTEGER

+ */ + public static final String ERROR_CODE = "error_code"; +} + + /** + * Contains all text based SMS messages. + */ + public static final class Sms implements BaseColumns, TextBasedSmsColumns { + public static final Cursor query(ContentResolver cr, String[] projection) { + return cr.query(CONTENT_URI, projection, null, null, DEFAULT_SORT_ORDER); + } + + public static final Cursor query(ContentResolver cr, String[] projection, + String where, String orderBy) { + return cr.query(CONTENT_URI, projection, where, + null, orderBy == null ? DEFAULT_SORT_ORDER : orderBy); + } + + /** + * The content:// style URL for this table + */ + public static final Uri CONTENT_URI = + Uri.parse("content://sms"); + + /** + * The default sort order for this table + */ + public static final String DEFAULT_SORT_ORDER = "date DESC"; + + /** + * Add an SMS to the given URI. + * + * @param resolver the content resolver to use + * @param uri the URI to add the message to + * @param address the address of the sender + * @param body the body of the message + * @param subject the psuedo-subject of the message + * @param date the timestamp for the message + * @param read true if the message has been read, false if not + * @param deliveryReport true if a delivery report was requested, false if not + * @return the URI for the new message + */ + public static Uri addMessageToUri(ContentResolver resolver, + Uri uri, String address, String body, String subject, + Long date, boolean read, boolean deliveryReport) { + return addMessageToUri(resolver, uri, address, body, subject, + date, read, deliveryReport, -1L); + } + + /** + * Add an SMS to the given URI with thread_id specified. + * + * @param resolver the content resolver to use + * @param uri the URI to add the message to + * @param address the address of the sender + * @param body the body of the message + * @param subject the psuedo-subject of the message + * @param date the timestamp for the message + * @param read true if the message has been read, false if not + * @param deliveryReport true if a delivery report was requested, false if not + * @param threadId the thread_id of the message + * @return the URI for the new message + */ + public static Uri addMessageToUri(ContentResolver resolver, + Uri uri, String address, String body, String subject, + Long date, boolean read, boolean deliveryReport, long threadId) { + ContentValues values = new ContentValues(7); + + values.put(ADDRESS, address); + if (date != null) { + values.put(DATE, date); + } + values.put(READ, read ? Integer.valueOf(1) : Integer.valueOf(0)); + values.put(SUBJECT, subject); + values.put(BODY, body); + if (deliveryReport) { + values.put(STATUS, STATUS_PENDING); + } + if (threadId != -1L) { + values.put(THREAD_ID, threadId); + } + return resolver.insert(uri, values); + } + + /** + * Move a message to the given folder. + * + * @param context the context to use + * @param uri the message to move + * @param folder the folder to move to + * @return true if the operation succeeded + */ + public static boolean moveMessageToFolder(Context context, + Uri uri, int folder, int error) { + if (uri == null) { + return false; + } + + boolean markAsUnread = false; + boolean markAsRead = false; + switch(folder) { + case MESSAGE_TYPE_INBOX: + case MESSAGE_TYPE_DRAFT: + break; + case MESSAGE_TYPE_OUTBOX: + case MESSAGE_TYPE_SENT: + markAsRead = true; + break; + case MESSAGE_TYPE_FAILED: + case MESSAGE_TYPE_QUEUED: + markAsUnread = true; + break; + default: + return false; + } + + ContentValues values = new ContentValues(3); + + values.put(TYPE, folder); + if (markAsUnread) { + values.put(READ, Integer.valueOf(0)); + } else if (markAsRead) { + values.put(READ, Integer.valueOf(1)); + } + values.put(ERROR_CODE, error); + + return 1 == SqliteWrapper.update(context, context.getContentResolver(), + uri, values, null, null); + } + + /** + * Returns true iff the folder (message type) identifies an + * outgoing message. + */ + public static boolean isOutgoingFolder(int messageType) { + return (messageType == MESSAGE_TYPE_FAILED) + || (messageType == MESSAGE_TYPE_OUTBOX) + || (messageType == MESSAGE_TYPE_SENT) + || (messageType == MESSAGE_TYPE_QUEUED); + } + + /** + * Contains all text based SMS messages in the SMS app's inbox. + */ + public static final class Inbox implements BaseColumns, TextBasedSmsColumns { + /** + * The content:// style URL for this table + */ + public static final Uri CONTENT_URI = + Uri.parse("content://sms/inbox"); + + /** + * The default sort order for this table + */ + public static final String DEFAULT_SORT_ORDER = "date DESC"; + + /** + * Add an SMS to the Draft box. + * + * @param resolver the content resolver to use + * @param address the address of the sender + * @param body the body of the message + * @param subject the psuedo-subject of the message + * @param date the timestamp for the message + * @param read true if the message has been read, false if not + * @return the URI for the new message + */ + public static Uri addMessage(ContentResolver resolver, + String address, String body, String subject, Long date, + boolean read) { + return addMessageToUri(resolver, CONTENT_URI, address, body, + subject, date, read, false); + } + } + + /** + * Contains all sent text based SMS messages in the SMS app's. + */ + public static final class Sent implements BaseColumns, TextBasedSmsColumns { + /** + * The content:// style URL for this table + */ + public static final Uri CONTENT_URI = + Uri.parse("content://sms/sent"); + + /** + * The default sort order for this table + */ + public static final String DEFAULT_SORT_ORDER = "date DESC"; + + /** + * Add an SMS to the Draft box. + * + * @param resolver the content resolver to use + * @param address the address of the sender + * @param body the body of the message + * @param subject the psuedo-subject of the message + * @param date the timestamp for the message + * @return the URI for the new message + */ + public static Uri addMessage(ContentResolver resolver, + String address, String body, String subject, Long date) { + return addMessageToUri(resolver, CONTENT_URI, address, body, + subject, date, true, false); + } + } + + /** + * Contains all sent text based SMS messages in the SMS app's. + */ + public static final class Draft implements BaseColumns, TextBasedSmsColumns { + /** + * The content:// style URL for this table + */ + public static final Uri CONTENT_URI = + Uri.parse("content://sms/draft"); + + /** + * The default sort order for this table + */ + public static final String DEFAULT_SORT_ORDER = "date DESC"; + + /** + * Add an SMS to the Draft box. + * + * @param resolver the content resolver to use + * @param address the address of the sender + * @param body the body of the message + * @param subject the psuedo-subject of the message + * @param date the timestamp for the message + * @return the URI for the new message + */ + public static Uri addMessage(ContentResolver resolver, + String address, String body, String subject, Long date) { + return addMessageToUri(resolver, CONTENT_URI, address, body, + subject, date, true, false); + } + + /** + * Save over an existing draft message. + * + * @param resolver the content resolver to use + * @param uri of existing message + * @param body the new body for the draft message + * @return true is successful, false otherwise + */ + public static boolean saveMessage(ContentResolver resolver, + Uri uri, String body) { + ContentValues values = new ContentValues(2); + values.put(BODY, body); + values.put(DATE, System.currentTimeMillis()); + return resolver.update(uri, values, null, null) == 1; + } + } + + /** + * Contains all pending outgoing text based SMS messages. + */ + public static final class Outbox implements BaseColumns, TextBasedSmsColumns { + /** + * The content:// style URL for this table + */ + public static final Uri CONTENT_URI = + Uri.parse("content://sms/outbox"); + + /** + * The default sort order for this table + */ + public static final String DEFAULT_SORT_ORDER = "date DESC"; + + /** + * Add an SMS to the Out box. + * + * @param resolver the content resolver to use + * @param address the address of the sender + * @param body the body of the message + * @param subject the psuedo-subject of the message + * @param date the timestamp for the message + * @param deliveryReport whether a delivery report was requested for the message + * @return the URI for the new message + */ + public static Uri addMessage(ContentResolver resolver, + String address, String body, String subject, Long date, + boolean deliveryReport, long threadId) { + return addMessageToUri(resolver, CONTENT_URI, address, body, + subject, date, true, deliveryReport, threadId); + } + } + + /** + * Contains all sent text-based SMS messages in the SMS app's. + */ + public static final class Conversations + implements BaseColumns, TextBasedSmsColumns { + /** + * The content:// style URL for this table + */ + public static final Uri CONTENT_URI = + Uri.parse("content://sms/conversations"); + + /** + * The default sort order for this table + */ + public static final String DEFAULT_SORT_ORDER = "date DESC"; + + /** + * The first 45 characters of the body of the message + *

Type: TEXT

+ */ + public static final String SNIPPET = "snippet"; + + /** + * The number of messages in the conversation + *

Type: INTEGER

+ */ + public static final String MESSAGE_COUNT = "msg_count"; + } + + /** + * Contains info about SMS related Intents that are broadcast. + */ + public static final class Intents { + /** + * Set by BroadcastReceiver. Indicates the message was handled + * successfully. + */ + public static final int RESULT_SMS_HANDLED = 1; + + /** + * Set by BroadcastReceiver. Indicates a generic error while + * processing the message. + */ + public static final int RESULT_SMS_GENERIC_ERROR = 2; + + /** + * Set by BroadcastReceiver. Indicates insufficient memory to store + * the message. + */ + public static final int RESULT_SMS_OUT_OF_MEMORY = 3; + + /** + * Set by BroadcastReceiver. Indicates the message, while + * possibly valid, is of a format or encoding that is not + * supported. + */ + public static final int RESULT_SMS_UNSUPPORTED = 4; + + /** + * Broadcast Action: A new text based SMS message has been received + * by the device. The intent will have the following extra + * values:

+ * + *
    + *
  • pdus - An Object[] od byte[]s containing the PDUs + * that make up the message.
  • + *
+ * + *

The extra values can be extracted using + * {@link #getMessagesFromIntent(Intent)}.

+ * + *

If a BroadcastReceiver encounters an error while processing + * this intent it should set the result code appropriately.

+ */ + public static final String SMS_RECEIVED_ACTION = + "android.provider.Telephony.SMS_RECEIVED"; + + /** + * Broadcast Action: A new data based SMS message has been received + * by the device. The intent will have the following extra + * values:

+ * + *
    + *
  • pdus - An Object[] od byte[]s containing the PDUs + * that make up the message.
  • + *
+ * + *

The extra values can be extracted using + * {@link #getMessagesFromIntent(Intent)}.

+ * + *

If a BroadcastReceiver encounters an error while processing + * this intent it should set the result code appropriately.

+ */ + public static final String DATA_SMS_RECEIVED_ACTION = + "android.intent.action.DATA_SMS_RECEIVED"; + + /** + * Broadcast Action: A new WAP PUSH message has been received by the + * device. The intent will have the following extra + * values:

+ * + *
    + *
  • transactionId (Integer) - The WAP transaction + * ID
  • + *
  • pduType (Integer) - The WAP PDU type
  • + *
  • header (byte[]) - The header of the message
  • + *
  • data (byte[]) - The data payload of the message
  • + *
+ * + *

If a BroadcastReceiver encounters an error while processing + * this intent it should set the result code appropriately.

+ */ + public static final String WAP_PUSH_RECEIVED_ACTION = + "android.provider.Telephony.WAP_PUSH_RECEIVED"; + + /** + * Broadcast Action: The SIM storage for SMS messages is full. If + * space is not freed, messages targeted for the SIM (class 2) may + * not be saved. + */ + public static final String SIM_FULL_ACTION = + "android.provider.Telephony.SIM_FULL"; + + /** + * Broadcast Action: An incoming SMS has been rejected by the + * telephony framework. This intent is sent in lieu of any + * of the RECEIVED_ACTION intents. The intent will have the + * following extra value:

+ * + *
    + *
  • result - An int result code, eg, + * {@link #RESULT_SMS_OUT_OF_MEMORY}, + * indicating the error returned to the network.
  • + *
+ + */ + public static final String SMS_REJECTED_ACTION = + "android.provider.Telephony.SMS_REJECTED"; + + /** + * Broadcast Action: The phone service state has changed. The intent will have the following + * extra values:

+ *
    + *
  • state - An int with one of the following values: + * {@link android.telephony.ServiceState#STATE_IN_SERVICE}, + * {@link android.telephony.ServiceState#STATE_OUT_OF_SERVICE}, + * {@link android.telephony.ServiceState#STATE_EMERGENCY_ONLY} + * or {@link android.telephony.ServiceState#STATE_POWER_OFF} + *
  • roaming - A boolean value indicating whether the phone is roaming.
  • + *
  • operator-alpha-long - The carrier name as a string.
  • + *
  • operator-alpha-short - A potentially shortened version of the carrier name, + * as a string.
  • + *
  • operator-numeric - A number representing the carrier, as a string. This is + * a five or six digit number consisting of the MCC (Mobile Country Code, 3 digits) + * and MNC (Mobile Network code, 2-3 digits).
  • + *
  • manual - A boolean, where true indicates that the user has chosen to select + * the network manually, and false indicates that network selection is handled by the + * phone.
  • + *
+ * + *

+ * Requires the READ_PHONE_STATE permission. + * + *

This is a protected intent that can only be sent + * by the system. + */ + public static final String ACTION_SERVICE_STATE_CHANGED = + "android.intent.action.SERVICE_STATE"; + + /** + * Read the PDUs out of an {@link #SMS_RECEIVED_ACTION} or a + * {@link #DATA_SMS_RECEIVED_ACTION} intent. + * + * @param intent the intent to read from + * @return an array of SmsMessages for the PDUs + */ + public static final SmsMessage[] getMessagesFromIntent( + Intent intent) { + Object[] messages = (Object[]) intent.getSerializableExtra("pdus"); + byte[][] pduObjs = new byte[messages.length][]; + + for (int i = 0; i < messages.length; i++) { + pduObjs[i] = (byte[]) messages[i]; + } + byte[][] pdus = new byte[pduObjs.length][]; + int pduCount = pdus.length; + SmsMessage[] msgs = new SmsMessage[pduCount]; + for (int i = 0; i < pduCount; i++) { + pdus[i] = pduObjs[i]; + msgs[i] = SmsMessage.createFromPdu(pdus[i]); + } + return msgs; + } + } + } + + /** + * Base columns for tables that contain MMSs. + */ + public interface BaseMmsColumns extends BaseColumns { + + public static final int MESSAGE_BOX_ALL = 0; + public static final int MESSAGE_BOX_INBOX = 1; + public static final int MESSAGE_BOX_SENT = 2; + public static final int MESSAGE_BOX_DRAFTS = 3; + public static final int MESSAGE_BOX_OUTBOX = 4; + + /** + * The date the message was sent. + *

Type: INTEGER (long)

+ */ + public static final String DATE = "date"; + + /** + * The box which the message belong to, for example, MESSAGE_BOX_INBOX. + *

Type: INTEGER

+ */ + public static final String MESSAGE_BOX = "msg_box"; + + /** + * Has the message been read. + *

Type: INTEGER (boolean)

+ */ + public static final String READ = "read"; + + /** + * The Message-ID of the message. + *

Type: TEXT

+ */ + public static final String MESSAGE_ID = "m_id"; + + /** + * The subject of the message, if present. + *

Type: TEXT

+ */ + public static final String SUBJECT = "sub"; + + /** + * The character set of the subject, if present. + *

Type: INTEGER

+ */ + public static final String SUBJECT_CHARSET = "sub_cs"; + + /** + * The Content-Type of the message. + *

Type: TEXT

+ */ + public static final String CONTENT_TYPE = "ct_t"; + + /** + * The Content-Location of the message. + *

Type: TEXT

+ */ + public static final String CONTENT_LOCATION = "ct_l"; + + /** + * The address of the sender. + *

Type: TEXT

+ */ + public static final String FROM = "from"; + + /** + * The address of the recipients. + *

Type: TEXT

+ */ + public static final String TO = "to"; + + /** + * The address of the cc. recipients. + *

Type: TEXT

+ */ + public static final String CC = "cc"; + + /** + * The address of the bcc. recipients. + *

Type: TEXT

+ */ + public static final String BCC = "bcc"; + + /** + * The expiry time of the message. + *

Type: INTEGER

+ */ + public static final String EXPIRY = "exp"; + + /** + * The class of the message. + *

Type: TEXT

+ */ + public static final String MESSAGE_CLASS = "m_cls"; + + /** + * The type of the message defined by MMS spec. + *

Type: INTEGER

+ */ + public static final String MESSAGE_TYPE = "m_type"; + + /** + * The version of specification that this message conform. + *

Type: INTEGER

+ */ + public static final String MMS_VERSION = "v"; + + /** + * The size of the message. + *

Type: INTEGER

+ */ + public static final String MESSAGE_SIZE = "m_size"; + + /** + * The priority of the message. + *

Type: TEXT

+ */ + public static final String PRIORITY = "pri"; + + /** + * The read-report of the message. + *

Type: TEXT

+ */ + public static final String READ_REPORT = "rr"; + + /** + * Whether the report is allowed. + *

Type: TEXT

+ */ + public static final String REPORT_ALLOWED = "rpt_a"; + + /** + * The response-status of the message. + *

Type: INTEGER

+ */ + public static final String RESPONSE_STATUS = "resp_st"; + + /** + * The status of the message. + *

Type: INTEGER

+ */ + public static final String STATUS = "st"; + + /** + * The transaction-id of the message. + *

Type: TEXT

+ */ + public static final String TRANSACTION_ID = "tr_id"; + + /** + * The retrieve-status of the message. + *

Type: INTEGER

+ */ + public static final String RETRIEVE_STATUS = "retr_st"; + + /** + * The retrieve-text of the message. + *

Type: TEXT

+ */ + public static final String RETRIEVE_TEXT = "retr_txt"; + + /** + * The character set of the retrieve-text. + *

Type: TEXT

+ */ + public static final String RETRIEVE_TEXT_CHARSET = "retr_txt_cs"; + + /** + * The read-status of the message. + *

Type: INTEGER

+ */ + public static final String READ_STATUS = "read_status"; + + /** + * The content-class of the message. + *

Type: INTEGER

+ */ + public static final String CONTENT_CLASS = "ct_cls"; + + /** + * The delivery-report of the message. + *

Type: INTEGER

+ */ + public static final String DELIVERY_REPORT = "d_rpt"; + + /** + * The delivery-time-token of the message. + *

Type: INTEGER

+ */ + public static final String DELIVERY_TIME_TOKEN = "d_tm_tok"; + + /** + * The delivery-time of the message. + *

Type: INTEGER

+ */ + public static final String DELIVERY_TIME = "d_tm"; + + /** + * The response-text of the message. + *

Type: TEXT

+ */ + public static final String RESPONSE_TEXT = "resp_txt"; + + /** + * The sender-visibility of the message. + *

Type: TEXT

+ */ + public static final String SENDER_VISIBILITY = "s_vis"; + + /** + * The reply-charging of the message. + *

Type: INTEGER

+ */ + public static final String REPLY_CHARGING = "r_chg"; + + /** + * The reply-charging-deadline-token of the message. + *

Type: INTEGER

+ */ + public static final String REPLY_CHARGING_DEADLINE_TOKEN = "r_chg_dl_tok"; + + /** + * The reply-charging-deadline of the message. + *

Type: INTEGER

+ */ + public static final String REPLY_CHARGING_DEADLINE = "r_chg_dl"; + + /** + * The reply-charging-id of the message. + *

Type: TEXT

+ */ + public static final String REPLY_CHARGING_ID = "r_chg_id"; + + /** + * The reply-charging-size of the message. + *

Type: INTEGER

+ */ + public static final String REPLY_CHARGING_SIZE = "r_chg_sz"; + + /** + * The previously-sent-by of the message. + *

Type: TEXT

+ */ + public static final String PREVIOUSLY_SENT_BY = "p_s_by"; + + /** + * The previously-sent-date of the message. + *

Type: INTEGER

+ */ + public static final String PREVIOUSLY_SENT_DATE = "p_s_d"; + + /** + * The store of the message. + *

Type: TEXT

+ */ + public static final String STORE = "store"; + + /** + * The mm-state of the message. + *

Type: INTEGER

+ */ + public static final String MM_STATE = "mm_st"; + + /** + * The mm-flags-token of the message. + *

Type: INTEGER

+ */ + public static final String MM_FLAGS_TOKEN = "mm_flg_tok"; + + /** + * The mm-flags of the message. + *

Type: TEXT

+ */ + public static final String MM_FLAGS = "mm_flg"; + + /** + * The store-status of the message. + *

Type: TEXT

+ */ + public static final String STORE_STATUS = "store_st"; + + /** + * The store-status-text of the message. + *

Type: TEXT

+ */ + public static final String STORE_STATUS_TEXT = "store_st_txt"; + + /** + * The stored of the message. + *

Type: TEXT

+ */ + public static final String STORED = "stored"; + + /** + * The totals of the message. + *

Type: TEXT

+ */ + public static final String TOTALS = "totals"; + + /** + * The mbox-totals of the message. + *

Type: TEXT

+ */ + public static final String MBOX_TOTALS = "mb_t"; + + /** + * The mbox-totals-token of the message. + *

Type: INTEGER

+ */ + public static final String MBOX_TOTALS_TOKEN = "mb_t_tok"; + + /** + * The quotas of the message. + *

Type: TEXT

+ */ + public static final String QUOTAS = "qt"; + + /** + * The mbox-quotas of the message. + *

Type: TEXT

+ */ + public static final String MBOX_QUOTAS = "mb_qt"; + + /** + * The mbox-quotas-token of the message. + *

Type: INTEGER

+ */ + public static final String MBOX_QUOTAS_TOKEN = "mb_qt_tok"; + + /** + * The message-count of the message. + *

Type: INTEGER

+ */ + public static final String MESSAGE_COUNT = "m_cnt"; + + /** + * The start of the message. + *

Type: INTEGER

+ */ + public static final String START = "start"; + + /** + * The distribution-indicator of the message. + *

Type: TEXT

+ */ + public static final String DISTRIBUTION_INDICATOR = "d_ind"; + + /** + * The element-descriptor of the message. + *

Type: TEXT

+ */ + public static final String ELEMENT_DESCRIPTOR = "e_des"; + + /** + * The limit of the message. + *

Type: INTEGER

+ */ + public static final String LIMIT = "limit"; + + /** + * The recommended-retrieval-mode of the message. + *

Type: INTEGER

+ */ + public static final String RECOMMENDED_RETRIEVAL_MODE = "r_r_mod"; + + /** + * The recommended-retrieval-mode-text of the message. + *

Type: TEXT

+ */ + public static final String RECOMMENDED_RETRIEVAL_MODE_TEXT = "r_r_mod_txt"; + + /** + * The status-text of the message. + *

Type: TEXT

+ */ + public static final String STATUS_TEXT = "st_txt"; + + /** + * The applic-id of the message. + *

Type: TEXT

+ */ + public static final String APPLIC_ID = "apl_id"; + + /** + * The reply-applic-id of the message. + *

Type: TEXT

+ */ + public static final String REPLY_APPLIC_ID = "r_apl_id"; + + /** + * The aux-applic-id of the message. + *

Type: TEXT

+ */ + public static final String AUX_APPLIC_ID = "aux_apl_id"; + + /** + * The drm-content of the message. + *

Type: TEXT

+ */ + public static final String DRM_CONTENT = "drm_c"; + + /** + * The adaptation-allowed of the message. + *

Type: TEXT

+ */ + public static final String ADAPTATION_ALLOWED = "adp_a"; + + /** + * The replace-id of the message. + *

Type: TEXT

+ */ + public static final String REPLACE_ID = "repl_id"; + + /** + * The cancel-id of the message. + *

Type: TEXT

+ */ + public static final String CANCEL_ID = "cl_id"; + + /** + * The cancel-status of the message. + *

Type: INTEGER

+ */ + public static final String CANCEL_STATUS = "cl_st"; + + /** + * The thread ID of the message + *

Type: INTEGER

+ */ + public static final String THREAD_ID = "thread_id"; + + /** + * Has the message been locked? + *

Type: INTEGER (boolean)

+ */ + public static final String LOCKED = "locked"; + } + + /** + * Columns for the "canonical_addresses" table used by MMS and + * SMS." + */ + public interface CanonicalAddressesColumns extends BaseColumns { + /** + * An address used in MMS or SMS. Email addresses are + * converted to lower case and are compared by string + * equality. Other addresses are compared using + * PHONE_NUMBERS_EQUAL. + *

Type: TEXT

+ */ + public static final String ADDRESS = "address"; + } + + /** + * Columns for the "threads" table used by MMS and SMS. + */ + public interface ThreadsColumns extends BaseColumns { + /** + * The date at which the thread was created. + * + *

Type: INTEGER (long)

+ */ + public static final String DATE = "date"; + + /** + * A string encoding of the recipient IDs of the recipients of + * the message, in numerical order and separated by spaces. + *

Type: TEXT

+ */ + public static final String RECIPIENT_IDS = "recipient_ids"; + + /** + * The message count of the thread. + *

Type: INTEGER

+ */ + public static final String MESSAGE_COUNT = "message_count"; + /** + * Indicates whether all messages of the thread have been read. + *

Type: INTEGER

+ */ + public static final String READ = "read"; + /** + * The snippet of the latest message in the thread. + *

Type: TEXT

+ */ + public static final String SNIPPET = "snippet"; + /** + * The charset of the snippet. + *

Type: INTEGER

+ */ + public static final String SNIPPET_CHARSET = "snippet_cs"; + /** + * Type of the thread, either Threads.COMMON_THREAD or + * Threads.BROADCAST_THREAD. + *

Type: INTEGER

+ */ + public static final String TYPE = "type"; + /** + * Indicates whether there is a transmission error in the thread. + *

Type: INTEGER

+ */ + public static final String ERROR = "error"; + /** + * Indicates whether this thread contains any attachments. + *

Type: INTEGER

+ */ + public static final String HAS_ATTACHMENT = "has_attachment"; + } + + /** + * Helper functions for the "threads" table used by MMS and SMS. + */ + public static final class Threads implements ThreadsColumns { + private static final String[] ID_PROJECTION = { BaseColumns._ID }; + private static final String STANDARD_ENCODING = "UTF-8"; + private static final Uri THREAD_ID_CONTENT_URI = Uri.parse( + "content://mms-sms/threadID"); + public static final Uri CONTENT_URI = Uri.withAppendedPath( + MmsSms.CONTENT_URI, "conversations"); + public static final Uri OBSOLETE_THREADS_URI = Uri.withAppendedPath( + CONTENT_URI, "obsolete"); + + public static final int COMMON_THREAD = 0; + public static final int BROADCAST_THREAD = 1; + + // No one should construct an instance of this class. + private Threads() { + } + + /** + * This is a single-recipient version of + * getOrCreateThreadId. It's convenient for use with SMS + * messages. + */ + public static long getOrCreateThreadId(Context context, String recipient) { + Set recipients = new HashSet(); + + recipients.add(recipient); + return getOrCreateThreadId(context, recipients); + } + + /** + * Given the recipients list and subject of an unsaved message, + * return its thread ID. If the message starts a new thread, + * allocate a new thread ID. Otherwise, use the appropriate + * existing thread ID. + * + * Find the thread ID of the same set of recipients (in + * any order, without any additions). If one + * is found, return it. Otherwise, return a unique thread ID. + */ + public static long getOrCreateThreadId( + Context context, Set recipients) { + Uri.Builder uriBuilder = THREAD_ID_CONTENT_URI.buildUpon(); + + for (String recipient : recipients) { + if (Mms.isEmailAddress(recipient)) { + recipient = Mms.extractAddrSpec(recipient); + } + + uriBuilder.appendQueryParameter("recipient", recipient); + } + + Uri uri = uriBuilder.build(); + if (DEBUG) { + Log.v(TAG, "getOrCreateThreadId uri: " + uri); + } + Cursor cursor = SqliteWrapper.query(context, context.getContentResolver(), + uri, ID_PROJECTION, null, null, null); + if (DEBUG) { + Log.v(TAG, "getOrCreateThreadId cursor cnt: " + cursor.getCount()); + } + if (cursor != null) { + try { + if (cursor.moveToFirst()) { + return cursor.getLong(0); + } else { + Log.e(TAG, "getOrCreateThreadId returned no rows!"); + } + } finally { + cursor.close(); + } + } + + Log.e(TAG, "getOrCreateThreadId failed with uri " + uri.toString()); + throw new IllegalArgumentException("Unable to find or allocate a thread ID."); + } + } + + /** + * Contains all MMS messages. + */ + public static final class Mms implements BaseMmsColumns { + /** + * The content:// style URL for this table + */ + public static final Uri CONTENT_URI = Uri.parse("content://mms"); + + public static final Uri REPORT_REQUEST_URI = Uri.withAppendedPath( + CONTENT_URI, "report-request"); + + public static final Uri REPORT_STATUS_URI = Uri.withAppendedPath( + CONTENT_URI, "report-status"); + + /** + * The default sort order for this table + */ + public static final String DEFAULT_SORT_ORDER = "date DESC"; + + /** + * mailbox = name-addr + * name-addr = [display-name] angle-addr + * angle-addr = [CFWS] "<" addr-spec ">" [CFWS] + */ + public static final Pattern NAME_ADDR_EMAIL_PATTERN = + Pattern.compile("\\s*(\"[^\"]*\"|[^<>\"]+)\\s*<([^<>]+)>\\s*"); + + /** + * quoted-string = [CFWS] + * DQUOTE *([FWS] qcontent) [FWS] DQUOTE + * [CFWS] + */ + public static final Pattern QUOTED_STRING_PATTERN = + Pattern.compile("\\s*\"([^\"]*)\"\\s*"); + + public static final Cursor query( + ContentResolver cr, String[] projection) { + return cr.query(CONTENT_URI, projection, null, null, DEFAULT_SORT_ORDER); + } + + public static final Cursor query( + ContentResolver cr, String[] projection, + String where, String orderBy) { + return cr.query(CONTENT_URI, projection, + where, null, orderBy == null ? DEFAULT_SORT_ORDER : orderBy); + } + + public static final String getMessageBoxName(int msgBox) { + switch (msgBox) { + case MESSAGE_BOX_ALL: + return "all"; + case MESSAGE_BOX_INBOX: + return "inbox"; + case MESSAGE_BOX_SENT: + return "sent"; + case MESSAGE_BOX_DRAFTS: + return "drafts"; + case MESSAGE_BOX_OUTBOX: + return "outbox"; + default: + throw new IllegalArgumentException("Invalid message box: " + msgBox); + } + } + + public static String extractAddrSpec(String address) { + Matcher match = NAME_ADDR_EMAIL_PATTERN.matcher(address); + + if (match.matches()) { + return match.group(2); + } + return address; + } + + /** + * Returns true if the address is an email address + * + * @param address the input address to be tested + * @return true if address is an email address + */ + public static boolean isEmailAddress(String address) { + if (TextUtils.isEmpty(address)) { + return false; + } + + String s = extractAddrSpec(address); + Matcher match = Patterns.EMAIL_ADDRESS.matcher(s); + return match.matches(); + } + + /** + * Returns true if the number is a Phone number + * + * @param number the input number to be tested + * @return true if number is a Phone number + */ + public static boolean isPhoneNumber(String number) { + if (TextUtils.isEmpty(number)) { + return false; + } + + Matcher match = Patterns.PHONE.matcher(number); + return match.matches(); + } + + /** + * Contains all MMS messages in the MMS app's inbox. + */ + public static final class Inbox implements BaseMmsColumns { + /** + * The content:// style URL for this table + */ + public static final Uri + CONTENT_URI = Uri.parse("content://mms/inbox"); + + /** + * The default sort order for this table + */ + public static final String DEFAULT_SORT_ORDER = "date DESC"; + } + + /** + * Contains all MMS messages in the MMS app's sent box. + */ + public static final class Sent implements BaseMmsColumns { + /** + * The content:// style URL for this table + */ + public static final Uri + CONTENT_URI = Uri.parse("content://mms/sent"); + + /** + * The default sort order for this table + */ + public static final String DEFAULT_SORT_ORDER = "date DESC"; + } + + /** + * Contains all MMS messages in the MMS app's drafts box. + */ + public static final class Draft implements BaseMmsColumns { + /** + * The content:// style URL for this table + */ + public static final Uri + CONTENT_URI = Uri.parse("content://mms/drafts"); + + /** + * The default sort order for this table + */ + public static final String DEFAULT_SORT_ORDER = "date DESC"; + } + + /** + * Contains all MMS messages in the MMS app's outbox. + */ + public static final class Outbox implements BaseMmsColumns { + /** + * The content:// style URL for this table + */ + public static final Uri + CONTENT_URI = Uri.parse("content://mms/outbox"); + + /** + * The default sort order for this table + */ + public static final String DEFAULT_SORT_ORDER = "date DESC"; + } + + public static final class Addr implements BaseColumns { + /** + * The ID of MM which this address entry belongs to. + */ + public static final String MSG_ID = "msg_id"; + + /** + * The ID of contact entry in Phone Book. + */ + public static final String CONTACT_ID = "contact_id"; + + /** + * The address text. + */ + public static final String ADDRESS = "address"; + + /** + * Type of address, must be one of PduHeaders.BCC, + * PduHeaders.CC, PduHeaders.FROM, PduHeaders.TO. + */ + public static final String TYPE = "type"; + + /** + * Character set of this entry. + */ + public static final String CHARSET = "charset"; + } + + public static final class Part implements BaseColumns { + /** + * The identifier of the message which this part belongs to. + *

Type: INTEGER

+ */ + public static final String MSG_ID = "mid"; + + /** + * The order of the part. + *

Type: INTEGER

+ */ + public static final String SEQ = "seq"; + + /** + * The content type of the part. + *

Type: TEXT

+ */ + public static final String CONTENT_TYPE = "ct"; + + /** + * The name of the part. + *

Type: TEXT

+ */ + public static final String NAME = "name"; + + /** + * The charset of the part. + *

Type: TEXT

+ */ + public static final String CHARSET = "chset"; + + /** + * The file name of the part. + *

Type: TEXT

+ */ + public static final String FILENAME = "fn"; + + /** + * The content disposition of the part. + *

Type: TEXT

+ */ + public static final String CONTENT_DISPOSITION = "cd"; + + /** + * The content ID of the part. + *

Type: INTEGER

+ */ + public static final String CONTENT_ID = "cid"; + + /** + * The content location of the part. + *

Type: INTEGER

+ */ + public static final String CONTENT_LOCATION = "cl"; + + /** + * The start of content-type of the message. + *

Type: INTEGER

+ */ + public static final String CT_START = "ctt_s"; + + /** + * The type of content-type of the message. + *

Type: TEXT

+ */ + public static final String CT_TYPE = "ctt_t"; + + /** + * The location(on filesystem) of the binary data of the part. + *

Type: INTEGER

+ */ + public static final String _DATA = "_data"; + + public static final String TEXT = "text"; + + } + + public static final class Rate { + public static final Uri CONTENT_URI = Uri.withAppendedPath( + Mms.CONTENT_URI, "rate"); + /** + * When a message was successfully sent. + *

Type: INTEGER

+ */ + public static final String SENT_TIME = "sent_time"; + } + + public static final class ScrapSpace { + /** + * The content:// style URL for this table + */ + public static final Uri CONTENT_URI = Uri.parse("content://mms/scrapSpace"); + + /** + * This is the scrap file we use to store the media attachment when the user + * chooses to capture a photo to be attached . We pass {#link@Uri} to the Camera app, + * which streams the captured image to the uri. Internally we write the media content + * to this file. It's named '.temp.jpg' so Gallery won't pick it up. + */ + public static final String SCRAP_FILE_PATH = "/sdcard/mms/scrapSpace/.temp.jpg"; + } + + public static final class Intents { + private Intents() { + // Non-instantiatable. + } + + /** + * The extra field to store the contents of the Intent, + * which should be an array of Uri. + */ + public static final String EXTRA_CONTENTS = "contents"; + /** + * The extra field to store the type of the contents, + * which should be an array of String. + */ + public static final String EXTRA_TYPES = "types"; + /** + * The extra field to store the 'Cc' addresses. + */ + public static final String EXTRA_CC = "cc"; + /** + * The extra field to store the 'Bcc' addresses; + */ + public static final String EXTRA_BCC = "bcc"; + /** + * The extra field to store the 'Subject'. + */ + public static final String EXTRA_SUBJECT = "subject"; + /** + * Indicates that the contents of specified URIs were changed. + * The application which is showing or caching these contents + * should be updated. + */ + public static final String + CONTENT_CHANGED_ACTION = "android.intent.action.CONTENT_CHANGED"; + /** + * An extra field which stores the URI of deleted contents. + */ + public static final String DELETED_CONTENTS = "deleted_contents"; + } + } + + /** + * Contains all MMS and SMS messages. + */ + public static final class MmsSms implements BaseColumns { + /** + * The column to distinguish SMS & MMS messages in query results. + */ + public static final String TYPE_DISCRIMINATOR_COLUMN = + "transport_type"; + + public static final Uri CONTENT_URI = Uri.parse("content://mms-sms/"); + + public static final Uri CONTENT_CONVERSATIONS_URI = Uri.parse( + "content://mms-sms/conversations"); + + public static final Uri CONTENT_FILTER_BYPHONE_URI = Uri.parse( + "content://mms-sms/messages/byphone"); + + public static final Uri CONTENT_UNDELIVERED_URI = Uri.parse( + "content://mms-sms/undelivered"); + + public static final Uri CONTENT_DRAFT_URI = Uri.parse( + "content://mms-sms/draft"); + + public static final Uri CONTENT_LOCKED_URI = Uri.parse( + "content://mms-sms/locked"); + + /*** + * Pass in a query parameter called "pattern" which is the text + * to search for. + * The sort order is fixed to be thread_id ASC,date DESC. + */ + public static final Uri SEARCH_URI = Uri.parse( + "content://mms-sms/search"); + + // Constants for message protocol types. + public static final int SMS_PROTO = 0; + public static final int MMS_PROTO = 1; + + // Constants for error types of pending messages. + public static final int NO_ERROR = 0; + public static final int ERR_TYPE_GENERIC = 1; + public static final int ERR_TYPE_SMS_PROTO_TRANSIENT = 2; + public static final int ERR_TYPE_MMS_PROTO_TRANSIENT = 3; + public static final int ERR_TYPE_TRANSPORT_FAILURE = 4; + public static final int ERR_TYPE_GENERIC_PERMANENT = 10; + public static final int ERR_TYPE_SMS_PROTO_PERMANENT = 11; + public static final int ERR_TYPE_MMS_PROTO_PERMANENT = 12; + + public static final class PendingMessages implements BaseColumns { + public static final Uri CONTENT_URI = Uri.withAppendedPath( + MmsSms.CONTENT_URI, "pending"); + /** + * The type of transport protocol(MMS or SMS). + *

Type: INTEGER

+ */ + public static final String PROTO_TYPE = "proto_type"; + /** + * The ID of the message to be sent or downloaded. + *

Type: INTEGER

+ */ + public static final String MSG_ID = "msg_id"; + /** + * The type of the message to be sent or downloaded. + * This field is only valid for MM. For SM, its value is always + * set to 0. + */ + public static final String MSG_TYPE = "msg_type"; + /** + * The type of the error code. + *

Type: INTEGER

+ */ + public static final String ERROR_TYPE = "err_type"; + /** + * The error code of sending/retrieving process. + *

Type: INTEGER

+ */ + public static final String ERROR_CODE = "err_code"; + /** + * How many times we tried to send or download the message. + *

Type: INTEGER

+ */ + public static final String RETRY_INDEX = "retry_index"; + /** + * The time to do next retry. + */ + public static final String DUE_TIME = "due_time"; + /** + * The time we last tried to send or download the message. + */ + public static final String LAST_TRY = "last_try"; + } + } + + public static final class Carriers implements BaseColumns { + /** + * The content:// style URL for this table + */ + public static final Uri CONTENT_URI = + Uri.parse("content://telephony/carriers"); + + /** + * The default sort order for this table + */ + public static final String DEFAULT_SORT_ORDER = "name ASC"; + + public static final String NAME = "name"; + + public static final String APN = "apn"; + + public static final String PROXY = "proxy"; + + public static final String PORT = "port"; + + public static final String MMSPROXY = "mmsproxy"; + + public static final String MMSPORT = "mmsport"; + + public static final String SERVER = "server"; + + public static final String USER = "user"; + + public static final String PASSWORD = "password"; + + public static final String MMSC = "mmsc"; + + public static final String MCC = "mcc"; + + public static final String MNC = "mnc"; + + public static final String NUMERIC = "numeric"; + + public static final String AUTH_TYPE = "authtype"; + + public static final String TYPE = "type"; + + public static final String CURRENT = "current"; + } + + public static final class Intents { + private Intents() { + // Not instantiable + } + + /** + * Broadcast Action: A "secret code" has been entered in the dialer. Secret codes are + * of the form *#*##*#*. The intent will have the data URI:

+ * + *

android_secret_code://<code>

+ */ + public static final String SECRET_CODE_ACTION = + "android.provider.Telephony.SECRET_CODE"; + + /** + * Broadcast Action: The Service Provider string(s) have been updated. Activities or + * services that use these strings should update their display. + * The intent will have the following extra values:

+ *
    + *
  • showPlmn - Boolean that indicates whether the PLMN should be shown.
  • + *
  • plmn - The operator name of the registered network, as a string.
  • + *
  • showSpn - Boolean that indicates whether the SPN should be shown.
  • + *
  • spn - The service provider name, as a string.
  • + *
+ * Note that showPlmn may indicate that plmn should be displayed, even + * though the value for plmn is null. This can happen, for example, if the phone + * has not registered to a network yet. In this case the receiver may substitute an + * appropriate placeholder string (eg, "No service"). + * + * It is recommended to display plmn before / above spn if + * both are displayed. + * + *

Note this is a protected intent that can only be sent + * by the system. + */ + public static final String SPN_STRINGS_UPDATED_ACTION = + "android.provider.Telephony.SPN_STRINGS_UPDATED"; + + public static final String EXTRA_SHOW_PLMN = "showPlmn"; + public static final String EXTRA_PLMN = "plmn"; + public static final String EXTRA_SHOW_SPN = "showSpn"; + public static final String EXTRA_SPN = "spn"; + } +} diff --git a/mms-common/java/com/android/common/mms/util/AbstractCache.java b/mms-common/java/com/android/common/mms/util/AbstractCache.java new file mode 100644 index 000000000..10a6fcedc --- /dev/null +++ b/mms-common/java/com/android/common/mms/util/AbstractCache.java @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2008 Esmertec AG. + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.mmscommon.mms.util; + +import android.util.Config; +import android.util.Log; + +import java.util.HashMap; + +public abstract class AbstractCache { + private static final String TAG = "AbstractCache"; + private static final boolean DEBUG = false; + private static final boolean LOCAL_LOGV = DEBUG ? Config.LOGD : Config.LOGV; + + private static final int MAX_CACHED_ITEMS = 500; + + private final HashMap> mCacheMap; + + protected AbstractCache() { + mCacheMap = new HashMap>(); + } + + public boolean put(K key, V value) { + if (LOCAL_LOGV) { + Log.v(TAG, "Trying to put " + key + " into cache."); + } + + if (mCacheMap.size() >= MAX_CACHED_ITEMS) { + // TODO Should remove the oldest or least hit cached entry + // and then cache the new one. + if (LOCAL_LOGV) { + Log.v(TAG, "Failed! size limitation reached."); + } + return false; + } + + if (key != null) { + CacheEntry cacheEntry = new CacheEntry(); + cacheEntry.value = value; + mCacheMap.put(key, cacheEntry); + + if (LOCAL_LOGV) { + Log.v(TAG, key + " cached, " + mCacheMap.size() + " items total."); + } + return true; + } + return false; + } + + public V get(K key) { + if (LOCAL_LOGV) { + Log.v(TAG, "Trying to get " + key + " from cache."); + } + + if (key != null) { + CacheEntry cacheEntry = mCacheMap.get(key); + if (cacheEntry != null) { + cacheEntry.hit++; + if (LOCAL_LOGV) { + Log.v(TAG, key + " hit " + cacheEntry.hit + " times."); + } + return cacheEntry.value; + } + } + return null; + } + + public V purge(K key) { + if (LOCAL_LOGV) { + Log.v(TAG, "Trying to purge " + key); + } + + CacheEntry v = mCacheMap.remove(key); + + if (LOCAL_LOGV) { + Log.v(TAG, mCacheMap.size() + " items cached."); + } + + return v != null ? v.value : null; + } + + public void purgeAll() { + if (LOCAL_LOGV) { + Log.v(TAG, "Purging cache, " + mCacheMap.size() + + " items dropped."); + } + mCacheMap.clear(); + } + + public int size() { + return mCacheMap.size(); + } + + private static class CacheEntry { + int hit; + V value; + } +} diff --git a/mms-common/java/com/android/common/mms/util/PduCache.java b/mms-common/java/com/android/common/mms/util/PduCache.java new file mode 100644 index 000000000..ca5432f07 --- /dev/null +++ b/mms-common/java/com/android/common/mms/util/PduCache.java @@ -0,0 +1,243 @@ +/* + * Copyright (C) 2008 Esmertec AG. + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.mmscommon.mms.util; + +import android.content.ContentUris; +import android.content.UriMatcher; +import android.net.Uri; +import com.android.mmscommon.telephony.TelephonyProvider.Mms; +import android.util.Config; +import android.util.Log; + +import java.util.HashMap; +import java.util.HashSet; + +public final class PduCache extends AbstractCache { + private static final String TAG = "PduCache"; + private static final boolean DEBUG = false; + private static final boolean LOCAL_LOGV = DEBUG ? Config.LOGD : Config.LOGV; + + private static final int MMS_ALL = 0; + private static final int MMS_ALL_ID = 1; + private static final int MMS_INBOX = 2; + private static final int MMS_INBOX_ID = 3; + private static final int MMS_SENT = 4; + private static final int MMS_SENT_ID = 5; + private static final int MMS_DRAFTS = 6; + private static final int MMS_DRAFTS_ID = 7; + private static final int MMS_OUTBOX = 8; + private static final int MMS_OUTBOX_ID = 9; + private static final int MMS_CONVERSATION = 10; + private static final int MMS_CONVERSATION_ID = 11; + + private static final UriMatcher URI_MATCHER; + private static final HashMap MATCH_TO_MSGBOX_ID_MAP; + + private static PduCache sInstance; + + static { + URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH); + URI_MATCHER.addURI("mms", null, MMS_ALL); + URI_MATCHER.addURI("mms", "#", MMS_ALL_ID); + URI_MATCHER.addURI("mms", "inbox", MMS_INBOX); + URI_MATCHER.addURI("mms", "inbox/#", MMS_INBOX_ID); + URI_MATCHER.addURI("mms", "sent", MMS_SENT); + URI_MATCHER.addURI("mms", "sent/#", MMS_SENT_ID); + URI_MATCHER.addURI("mms", "drafts", MMS_DRAFTS); + URI_MATCHER.addURI("mms", "drafts/#", MMS_DRAFTS_ID); + URI_MATCHER.addURI("mms", "outbox", MMS_OUTBOX); + URI_MATCHER.addURI("mms", "outbox/#", MMS_OUTBOX_ID); + URI_MATCHER.addURI("mms-sms", "conversations", MMS_CONVERSATION); + URI_MATCHER.addURI("mms-sms", "conversations/#", MMS_CONVERSATION_ID); + + MATCH_TO_MSGBOX_ID_MAP = new HashMap(); + MATCH_TO_MSGBOX_ID_MAP.put(MMS_INBOX, Mms.MESSAGE_BOX_INBOX); + MATCH_TO_MSGBOX_ID_MAP.put(MMS_SENT, Mms.MESSAGE_BOX_SENT); + MATCH_TO_MSGBOX_ID_MAP.put(MMS_DRAFTS, Mms.MESSAGE_BOX_DRAFTS); + MATCH_TO_MSGBOX_ID_MAP.put(MMS_OUTBOX, Mms.MESSAGE_BOX_OUTBOX); + } + + private final HashMap> mMessageBoxes; + private final HashMap> mThreads; + + private PduCache() { + mMessageBoxes = new HashMap>(); + mThreads = new HashMap>(); + } + + synchronized public static final PduCache getInstance() { + if (sInstance == null) { + if (LOCAL_LOGV) { + Log.v(TAG, "Constructing new PduCache instance."); + } + sInstance = new PduCache(); + } + return sInstance; + } + + @Override + synchronized public boolean put(Uri uri, PduCacheEntry entry) { + int msgBoxId = entry.getMessageBox(); + HashSet msgBox = mMessageBoxes.get(msgBoxId); + if (msgBox == null) { + msgBox = new HashSet(); + mMessageBoxes.put(msgBoxId, msgBox); + } + + long threadId = entry.getThreadId(); + HashSet thread = mThreads.get(threadId); + if (thread == null) { + thread = new HashSet(); + mThreads.put(threadId, thread); + } + + Uri finalKey = normalizeKey(uri); + boolean result = super.put(finalKey, entry); + if (result) { + msgBox.add(finalKey); + thread.add(finalKey); + } + return result; + } + + @Override + synchronized public PduCacheEntry purge(Uri uri) { + int match = URI_MATCHER.match(uri); + switch (match) { + case MMS_ALL_ID: + return purgeSingleEntry(uri); + case MMS_INBOX_ID: + case MMS_SENT_ID: + case MMS_DRAFTS_ID: + case MMS_OUTBOX_ID: + String msgId = uri.getLastPathSegment(); + return purgeSingleEntry(Uri.withAppendedPath(Mms.CONTENT_URI, msgId)); + // Implicit batch of purge, return null. + case MMS_ALL: + case MMS_CONVERSATION: + purgeAll(); + return null; + case MMS_INBOX: + case MMS_SENT: + case MMS_DRAFTS: + case MMS_OUTBOX: + purgeByMessageBox(MATCH_TO_MSGBOX_ID_MAP.get(match)); + return null; + case MMS_CONVERSATION_ID: + purgeByThreadId(ContentUris.parseId(uri)); + return null; + default: + return null; + } + } + + private PduCacheEntry purgeSingleEntry(Uri key) { + PduCacheEntry entry = super.purge(key); + if (entry != null) { + removeFromThreads(key, entry); + removeFromMessageBoxes(key, entry); + return entry; + } + return null; + } + + @Override + synchronized public void purgeAll() { + super.purgeAll(); + + mMessageBoxes.clear(); + mThreads.clear(); + } + + /** + * @param uri The Uri to be normalized. + * @return Uri The normalized key of cached entry. + */ + private Uri normalizeKey(Uri uri) { + int match = URI_MATCHER.match(uri); + Uri normalizedKey = null; + + switch (match) { + case MMS_ALL_ID: + normalizedKey = uri; + break; + case MMS_INBOX_ID: + case MMS_SENT_ID: + case MMS_DRAFTS_ID: + case MMS_OUTBOX_ID: + String msgId = uri.getLastPathSegment(); + normalizedKey = Uri.withAppendedPath(Mms.CONTENT_URI, msgId); + break; + default: + return null; + } + + if (LOCAL_LOGV) { + Log.v(TAG, uri + " -> " + normalizedKey); + } + return normalizedKey; + } + + private void purgeByMessageBox(Integer msgBoxId) { + if (LOCAL_LOGV) { + Log.v(TAG, "Purge cache in message box: " + msgBoxId); + } + + if (msgBoxId != null) { + HashSet msgBox = mMessageBoxes.remove(msgBoxId); + if (msgBox != null) { + for (Uri key : msgBox) { + PduCacheEntry entry = super.purge(key); + if (entry != null) { + removeFromThreads(key, entry); + } + } + } + } + } + + private void removeFromThreads(Uri key, PduCacheEntry entry) { + HashSet thread = mThreads.get(entry.getThreadId()); + if (thread != null) { + thread.remove(key); + } + } + + private void purgeByThreadId(long threadId) { + if (LOCAL_LOGV) { + Log.v(TAG, "Purge cache in thread: " + threadId); + } + + HashSet thread = mThreads.remove(threadId); + if (thread != null) { + for (Uri key : thread) { + PduCacheEntry entry = super.purge(key); + if (entry != null) { + removeFromMessageBoxes(key, entry); + } + } + } + } + + private void removeFromMessageBoxes(Uri key, PduCacheEntry entry) { + HashSet msgBox = mThreads.get(entry.getMessageBox()); + if (msgBox != null) { + msgBox.remove(key); + } + } +} diff --git a/mms-common/java/com/android/common/mms/util/PduCacheEntry.java b/mms-common/java/com/android/common/mms/util/PduCacheEntry.java new file mode 100644 index 000000000..aed741d2d --- /dev/null +++ b/mms-common/java/com/android/common/mms/util/PduCacheEntry.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2008 Esmertec AG. + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.mmscommon.mms.util; + +import com.android.mmscommon.mms.pdu.GenericPdu; + +public final class PduCacheEntry { + private final GenericPdu mPdu; + private final int mMessageBox; + private final long mThreadId; + + public PduCacheEntry(GenericPdu pdu, int msgBox, long threadId) { + mPdu = pdu; + mMessageBox = msgBox; + mThreadId = threadId; + } + + public GenericPdu getPdu() { + return mPdu; + } + + public int getMessageBox() { + return mMessageBox; + } + + public long getThreadId() { + return mThreadId; + } +} From 7e3befe696d8b218e5a98163e18a7f17c212e104 Mon Sep 17 00:00:00 2001 From: Ficus Kirkpatrick Date: Tue, 2 Feb 2010 15:22:35 -0800 Subject: [PATCH 178/541] Fix mms-common's pathnames to match package spec. Change-Id: I4ae65227ea29dff8a79bd1d9373dec46c4b53598 --- .../java/com/android/{common => mmscommon}/CharacterSets.java | 0 .../java/com/android/{common => mmscommon}/ContentType.java | 0 .../com/android/{common => mmscommon}/EncodedStringValue.java | 0 .../{common => mmscommon}/InvalidHeaderValueException.java | 0 .../java/com/android/{common => mmscommon}/MmsException.java | 0 .../java/com/android/{common => mmscommon}/PduHeaders.java | 0 .../java/com/android/{common => mmscommon}/mms/ContentType.java | 2 +- .../android/{common => mmscommon}/mms/pdu/AcknowledgeInd.java | 0 .../java/com/android/{common => mmscommon}/mms/pdu/Base64.java | 0 .../com/android/{common => mmscommon}/mms/pdu/DeliveryInd.java | 0 .../com/android/{common => mmscommon}/mms/pdu/GenericPdu.java | 0 .../{common => mmscommon}/mms/pdu/MultimediaMessagePdu.java | 0 .../android/{common => mmscommon}/mms/pdu/NotificationInd.java | 0 .../android/{common => mmscommon}/mms/pdu/NotifyRespInd.java | 0 .../java/com/android/{common => mmscommon}/mms/pdu/PduBody.java | 0 .../com/android/{common => mmscommon}/mms/pdu/PduComposer.java | 0 .../android/{common => mmscommon}/mms/pdu/PduContentTypes.java | 0 .../com/android/{common => mmscommon}/mms/pdu/PduParser.java | 0 .../java/com/android/{common => mmscommon}/mms/pdu/PduPart.java | 0 .../com/android/{common => mmscommon}/mms/pdu/PduPersister.java | 0 .../android/{common => mmscommon}/mms/pdu/QuotedPrintable.java | 0 .../com/android/{common => mmscommon}/mms/pdu/ReadOrigInd.java | 0 .../com/android/{common => mmscommon}/mms/pdu/ReadRecInd.java | 0 .../com/android/{common => mmscommon}/mms/pdu/RetrieveConf.java | 0 .../com/android/{common => mmscommon}/mms/pdu/SendConf.java | 0 .../java/com/android/{common => mmscommon}/mms/pdu/SendReq.java | 0 .../android/{common => mmscommon}/mms/util/AbstractCache.java | 0 .../com/android/{common => mmscommon}/mms/util/PduCache.java | 0 .../android/{common => mmscommon}/mms/util/PduCacheEntry.java | 0 .../{common/mms => mmscommon}/telephony/TelephonyProvider.java | 0 30 files changed, 1 insertion(+), 1 deletion(-) rename mms-common/java/com/android/{common => mmscommon}/CharacterSets.java (100%) rename mms-common/java/com/android/{common => mmscommon}/ContentType.java (100%) rename mms-common/java/com/android/{common => mmscommon}/EncodedStringValue.java (100%) rename mms-common/java/com/android/{common => mmscommon}/InvalidHeaderValueException.java (100%) rename mms-common/java/com/android/{common => mmscommon}/MmsException.java (100%) rename mms-common/java/com/android/{common => mmscommon}/PduHeaders.java (100%) rename mms-common/java/com/android/{common => mmscommon}/mms/ContentType.java (99%) rename mms-common/java/com/android/{common => mmscommon}/mms/pdu/AcknowledgeInd.java (100%) rename mms-common/java/com/android/{common => mmscommon}/mms/pdu/Base64.java (100%) rename mms-common/java/com/android/{common => mmscommon}/mms/pdu/DeliveryInd.java (100%) rename mms-common/java/com/android/{common => mmscommon}/mms/pdu/GenericPdu.java (100%) rename mms-common/java/com/android/{common => mmscommon}/mms/pdu/MultimediaMessagePdu.java (100%) rename mms-common/java/com/android/{common => mmscommon}/mms/pdu/NotificationInd.java (100%) rename mms-common/java/com/android/{common => mmscommon}/mms/pdu/NotifyRespInd.java (100%) rename mms-common/java/com/android/{common => mmscommon}/mms/pdu/PduBody.java (100%) rename mms-common/java/com/android/{common => mmscommon}/mms/pdu/PduComposer.java (100%) rename mms-common/java/com/android/{common => mmscommon}/mms/pdu/PduContentTypes.java (100%) rename mms-common/java/com/android/{common => mmscommon}/mms/pdu/PduParser.java (100%) rename mms-common/java/com/android/{common => mmscommon}/mms/pdu/PduPart.java (100%) rename mms-common/java/com/android/{common => mmscommon}/mms/pdu/PduPersister.java (100%) rename mms-common/java/com/android/{common => mmscommon}/mms/pdu/QuotedPrintable.java (100%) rename mms-common/java/com/android/{common => mmscommon}/mms/pdu/ReadOrigInd.java (100%) rename mms-common/java/com/android/{common => mmscommon}/mms/pdu/ReadRecInd.java (100%) rename mms-common/java/com/android/{common => mmscommon}/mms/pdu/RetrieveConf.java (100%) rename mms-common/java/com/android/{common => mmscommon}/mms/pdu/SendConf.java (100%) rename mms-common/java/com/android/{common => mmscommon}/mms/pdu/SendReq.java (100%) rename mms-common/java/com/android/{common => mmscommon}/mms/util/AbstractCache.java (100%) rename mms-common/java/com/android/{common => mmscommon}/mms/util/PduCache.java (100%) rename mms-common/java/com/android/{common => mmscommon}/mms/util/PduCacheEntry.java (100%) rename mms-common/java/com/android/{common/mms => mmscommon}/telephony/TelephonyProvider.java (100%) diff --git a/mms-common/java/com/android/common/CharacterSets.java b/mms-common/java/com/android/mmscommon/CharacterSets.java similarity index 100% rename from mms-common/java/com/android/common/CharacterSets.java rename to mms-common/java/com/android/mmscommon/CharacterSets.java diff --git a/mms-common/java/com/android/common/ContentType.java b/mms-common/java/com/android/mmscommon/ContentType.java similarity index 100% rename from mms-common/java/com/android/common/ContentType.java rename to mms-common/java/com/android/mmscommon/ContentType.java diff --git a/mms-common/java/com/android/common/EncodedStringValue.java b/mms-common/java/com/android/mmscommon/EncodedStringValue.java similarity index 100% rename from mms-common/java/com/android/common/EncodedStringValue.java rename to mms-common/java/com/android/mmscommon/EncodedStringValue.java diff --git a/mms-common/java/com/android/common/InvalidHeaderValueException.java b/mms-common/java/com/android/mmscommon/InvalidHeaderValueException.java similarity index 100% rename from mms-common/java/com/android/common/InvalidHeaderValueException.java rename to mms-common/java/com/android/mmscommon/InvalidHeaderValueException.java diff --git a/mms-common/java/com/android/common/MmsException.java b/mms-common/java/com/android/mmscommon/MmsException.java similarity index 100% rename from mms-common/java/com/android/common/MmsException.java rename to mms-common/java/com/android/mmscommon/MmsException.java diff --git a/mms-common/java/com/android/common/PduHeaders.java b/mms-common/java/com/android/mmscommon/PduHeaders.java similarity index 100% rename from mms-common/java/com/android/common/PduHeaders.java rename to mms-common/java/com/android/mmscommon/PduHeaders.java diff --git a/mms-common/java/com/android/common/mms/ContentType.java b/mms-common/java/com/android/mmscommon/mms/ContentType.java similarity index 99% rename from mms-common/java/com/android/common/mms/ContentType.java rename to mms-common/java/com/android/mmscommon/mms/ContentType.java index 0fdb46c92..f21eba8b8 100644 --- a/mms-common/java/com/android/common/mms/ContentType.java +++ b/mms-common/java/com/android/mmscommon/mms/ContentType.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package com.android.mms.mms; +package com.android.mmscommon.mms; import java.util.ArrayList; diff --git a/mms-common/java/com/android/common/mms/pdu/AcknowledgeInd.java b/mms-common/java/com/android/mmscommon/mms/pdu/AcknowledgeInd.java similarity index 100% rename from mms-common/java/com/android/common/mms/pdu/AcknowledgeInd.java rename to mms-common/java/com/android/mmscommon/mms/pdu/AcknowledgeInd.java diff --git a/mms-common/java/com/android/common/mms/pdu/Base64.java b/mms-common/java/com/android/mmscommon/mms/pdu/Base64.java similarity index 100% rename from mms-common/java/com/android/common/mms/pdu/Base64.java rename to mms-common/java/com/android/mmscommon/mms/pdu/Base64.java diff --git a/mms-common/java/com/android/common/mms/pdu/DeliveryInd.java b/mms-common/java/com/android/mmscommon/mms/pdu/DeliveryInd.java similarity index 100% rename from mms-common/java/com/android/common/mms/pdu/DeliveryInd.java rename to mms-common/java/com/android/mmscommon/mms/pdu/DeliveryInd.java diff --git a/mms-common/java/com/android/common/mms/pdu/GenericPdu.java b/mms-common/java/com/android/mmscommon/mms/pdu/GenericPdu.java similarity index 100% rename from mms-common/java/com/android/common/mms/pdu/GenericPdu.java rename to mms-common/java/com/android/mmscommon/mms/pdu/GenericPdu.java diff --git a/mms-common/java/com/android/common/mms/pdu/MultimediaMessagePdu.java b/mms-common/java/com/android/mmscommon/mms/pdu/MultimediaMessagePdu.java similarity index 100% rename from mms-common/java/com/android/common/mms/pdu/MultimediaMessagePdu.java rename to mms-common/java/com/android/mmscommon/mms/pdu/MultimediaMessagePdu.java diff --git a/mms-common/java/com/android/common/mms/pdu/NotificationInd.java b/mms-common/java/com/android/mmscommon/mms/pdu/NotificationInd.java similarity index 100% rename from mms-common/java/com/android/common/mms/pdu/NotificationInd.java rename to mms-common/java/com/android/mmscommon/mms/pdu/NotificationInd.java diff --git a/mms-common/java/com/android/common/mms/pdu/NotifyRespInd.java b/mms-common/java/com/android/mmscommon/mms/pdu/NotifyRespInd.java similarity index 100% rename from mms-common/java/com/android/common/mms/pdu/NotifyRespInd.java rename to mms-common/java/com/android/mmscommon/mms/pdu/NotifyRespInd.java diff --git a/mms-common/java/com/android/common/mms/pdu/PduBody.java b/mms-common/java/com/android/mmscommon/mms/pdu/PduBody.java similarity index 100% rename from mms-common/java/com/android/common/mms/pdu/PduBody.java rename to mms-common/java/com/android/mmscommon/mms/pdu/PduBody.java diff --git a/mms-common/java/com/android/common/mms/pdu/PduComposer.java b/mms-common/java/com/android/mmscommon/mms/pdu/PduComposer.java similarity index 100% rename from mms-common/java/com/android/common/mms/pdu/PduComposer.java rename to mms-common/java/com/android/mmscommon/mms/pdu/PduComposer.java diff --git a/mms-common/java/com/android/common/mms/pdu/PduContentTypes.java b/mms-common/java/com/android/mmscommon/mms/pdu/PduContentTypes.java similarity index 100% rename from mms-common/java/com/android/common/mms/pdu/PduContentTypes.java rename to mms-common/java/com/android/mmscommon/mms/pdu/PduContentTypes.java diff --git a/mms-common/java/com/android/common/mms/pdu/PduParser.java b/mms-common/java/com/android/mmscommon/mms/pdu/PduParser.java similarity index 100% rename from mms-common/java/com/android/common/mms/pdu/PduParser.java rename to mms-common/java/com/android/mmscommon/mms/pdu/PduParser.java diff --git a/mms-common/java/com/android/common/mms/pdu/PduPart.java b/mms-common/java/com/android/mmscommon/mms/pdu/PduPart.java similarity index 100% rename from mms-common/java/com/android/common/mms/pdu/PduPart.java rename to mms-common/java/com/android/mmscommon/mms/pdu/PduPart.java diff --git a/mms-common/java/com/android/common/mms/pdu/PduPersister.java b/mms-common/java/com/android/mmscommon/mms/pdu/PduPersister.java similarity index 100% rename from mms-common/java/com/android/common/mms/pdu/PduPersister.java rename to mms-common/java/com/android/mmscommon/mms/pdu/PduPersister.java diff --git a/mms-common/java/com/android/common/mms/pdu/QuotedPrintable.java b/mms-common/java/com/android/mmscommon/mms/pdu/QuotedPrintable.java similarity index 100% rename from mms-common/java/com/android/common/mms/pdu/QuotedPrintable.java rename to mms-common/java/com/android/mmscommon/mms/pdu/QuotedPrintable.java diff --git a/mms-common/java/com/android/common/mms/pdu/ReadOrigInd.java b/mms-common/java/com/android/mmscommon/mms/pdu/ReadOrigInd.java similarity index 100% rename from mms-common/java/com/android/common/mms/pdu/ReadOrigInd.java rename to mms-common/java/com/android/mmscommon/mms/pdu/ReadOrigInd.java diff --git a/mms-common/java/com/android/common/mms/pdu/ReadRecInd.java b/mms-common/java/com/android/mmscommon/mms/pdu/ReadRecInd.java similarity index 100% rename from mms-common/java/com/android/common/mms/pdu/ReadRecInd.java rename to mms-common/java/com/android/mmscommon/mms/pdu/ReadRecInd.java diff --git a/mms-common/java/com/android/common/mms/pdu/RetrieveConf.java b/mms-common/java/com/android/mmscommon/mms/pdu/RetrieveConf.java similarity index 100% rename from mms-common/java/com/android/common/mms/pdu/RetrieveConf.java rename to mms-common/java/com/android/mmscommon/mms/pdu/RetrieveConf.java diff --git a/mms-common/java/com/android/common/mms/pdu/SendConf.java b/mms-common/java/com/android/mmscommon/mms/pdu/SendConf.java similarity index 100% rename from mms-common/java/com/android/common/mms/pdu/SendConf.java rename to mms-common/java/com/android/mmscommon/mms/pdu/SendConf.java diff --git a/mms-common/java/com/android/common/mms/pdu/SendReq.java b/mms-common/java/com/android/mmscommon/mms/pdu/SendReq.java similarity index 100% rename from mms-common/java/com/android/common/mms/pdu/SendReq.java rename to mms-common/java/com/android/mmscommon/mms/pdu/SendReq.java diff --git a/mms-common/java/com/android/common/mms/util/AbstractCache.java b/mms-common/java/com/android/mmscommon/mms/util/AbstractCache.java similarity index 100% rename from mms-common/java/com/android/common/mms/util/AbstractCache.java rename to mms-common/java/com/android/mmscommon/mms/util/AbstractCache.java diff --git a/mms-common/java/com/android/common/mms/util/PduCache.java b/mms-common/java/com/android/mmscommon/mms/util/PduCache.java similarity index 100% rename from mms-common/java/com/android/common/mms/util/PduCache.java rename to mms-common/java/com/android/mmscommon/mms/util/PduCache.java diff --git a/mms-common/java/com/android/common/mms/util/PduCacheEntry.java b/mms-common/java/com/android/mmscommon/mms/util/PduCacheEntry.java similarity index 100% rename from mms-common/java/com/android/common/mms/util/PduCacheEntry.java rename to mms-common/java/com/android/mmscommon/mms/util/PduCacheEntry.java diff --git a/mms-common/java/com/android/common/mms/telephony/TelephonyProvider.java b/mms-common/java/com/android/mmscommon/telephony/TelephonyProvider.java similarity index 100% rename from mms-common/java/com/android/common/mms/telephony/TelephonyProvider.java rename to mms-common/java/com/android/mmscommon/telephony/TelephonyProvider.java From 4ef8aea3b501e8122559080d4ff2e1b0f1f9dbcb Mon Sep 17 00:00:00 2001 From: Tobias Haamel Date: Tue, 9 Feb 2010 23:09:17 +0100 Subject: [PATCH 179/541] Introduce special UI modes for night and car usage. The device mode is now called ui mode. Furthermore is the order of precedence for the resources now in such a way that the ui mode needs to be specified after the orientation and before the density. The ui mode can be set, like it is done for the locale, as follows: IActivityManager am = ActivityManagerNative.getDefault(); Configuration config = am.getConfiguration(); config.uiMode = Configuration.UI_MODE_TYPE_CAR | Configuration.UI_MODE_NIGHT_ANY; am.updateConfiguration(config); To allow users to disable the car mode and set the night mode the IUiModeManager interface is used. The automatic night mode switching will be added in a separate change. --- include/utils/ResourceTypes.h | 89 ++++++++++++++++++++++++++++------- 1 file changed, 71 insertions(+), 18 deletions(-) diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h index 6090f6001..13ea27e4d 100644 --- a/include/utils/ResourceTypes.h +++ b/include/utils/ResourceTypes.h @@ -939,10 +939,23 @@ struct ResTable_config SCREENLONG_YES = 0x20, }; + enum { + // uiMode bits for the mode type. + MASK_UI_MODE_TYPE = 0x0f, + UI_MODE_TYPE_NORMAL = 0x00, + UI_MODE_TYPE_CAR = 0x01, + + // uiMode bits for the night switch. + MASK_UI_MODE_NIGHT = 0x30, + UI_MODE_NIGHT_ANY = 0x00, + UI_MODE_NIGHT_NO = 0x10, + UI_MODE_NIGHT_YES = 0x20, + }; + union { struct { uint8_t screenLayout; - uint8_t screenConfigPad0; + uint8_t uiMode; uint8_t screenConfigPad1; uint8_t screenConfigPad2; }; @@ -996,6 +1009,8 @@ struct ResTable_config diff = (int32_t)(version - o.version); if (diff != 0) return diff; diff = (int32_t)(screenLayout - o.screenLayout); + if (diff != 0) return diff; + diff = (int32_t)(uiMode - o.uiMode); return (int)diff; } @@ -1014,7 +1029,8 @@ struct ResTable_config CONFIG_DENSITY = 0x0100, CONFIG_SCREEN_SIZE = 0x0200, CONFIG_VERSION = 0x0400, - CONFIG_SCREEN_LAYOUT = 0x0800 + CONFIG_SCREEN_LAYOUT = 0x0800, + CONFIG_UI_MODE = 0x1000 }; // Compare two configuration, returning CONFIG_* flags set for each value @@ -1034,6 +1050,7 @@ struct ResTable_config if (screenSize != o.screenSize) diffs |= CONFIG_SCREEN_SIZE; if (version != o.version) diffs |= CONFIG_VERSION; if (screenLayout != o.screenLayout) diffs |= CONFIG_SCREEN_LAYOUT; + if (uiMode != o.uiMode) diffs |= CONFIG_UI_MODE; return diffs; } @@ -1078,19 +1095,28 @@ struct ResTable_config } } - if (screenType || o.screenType) { - if (orientation != o.orientation) { - if (!orientation) return false; - if (!o.orientation) return true; - } + if (orientation != o.orientation) { + if (!orientation) return false; + if (!o.orientation) return true; + } - // density is never 'more specific' - // as the default just equals 160 - - if (touchscreen != o.touchscreen) { - if (!touchscreen) return false; - if (!o.touchscreen) return true; + if (screenConfig || o.screenConfig) { + if (((uiMode^o.uiMode) & MASK_UI_MODE_TYPE) != 0) { + if (!(uiMode & MASK_UI_MODE_TYPE)) return false; + if (!(o.uiMode & MASK_UI_MODE_TYPE)) return true; } + if (((uiMode^o.uiMode) & MASK_UI_MODE_NIGHT) != 0) { + if (!(uiMode & MASK_UI_MODE_NIGHT)) return false; + if (!(o.uiMode & MASK_UI_MODE_NIGHT)) return true; + } + } + + // density is never 'more specific' + // as the default just equals 160 + + if (touchscreen != o.touchscreen) { + if (!touchscreen) return false; + if (!o.touchscreen) return true; } if (input || o.input) { @@ -1186,11 +1212,22 @@ struct ResTable_config } } - if (screenType || o.screenType) { - if ((orientation != o.orientation) && requested->orientation) { - return (orientation); - } + if ((orientation != o.orientation) && requested->orientation) { + return (orientation); + } + if (screenConfig || o.screenConfig) { + if (((uiMode^o.uiMode) & MASK_UI_MODE_TYPE) != 0 + && (requested->uiMode & MASK_UI_MODE_TYPE)) { + return (uiMode & MASK_UI_MODE_TYPE); + } + if (((uiMode^o.uiMode) & MASK_UI_MODE_NIGHT) != 0 + && (requested->uiMode & MASK_UI_MODE_NIGHT)) { + return (uiMode & MASK_UI_MODE_NIGHT); + } + } + + if (screenType || o.screenType) { if (density != o.density) { // density is tough. Any density is potentially useful // because the system will scale it. Scaling down @@ -1340,6 +1377,20 @@ struct ResTable_config && screenLong != setScreenLong) { return false; } + + const int uiModeType = uiMode&MASK_UI_MODE_TYPE; + const int setUiModeType = settings.uiMode&MASK_UI_MODE_TYPE; + if (setUiModeType != 0 && uiModeType != 0 + && uiModeType != setUiModeType) { + return false; + } + + const int uiModeNight = uiMode&MASK_UI_MODE_NIGHT; + const int setUiModeNight = settings.uiMode&MASK_UI_MODE_NIGHT; + if (setUiModeNight != 0 && uiModeNight != 0 + && uiModeNight != setUiModeNight) { + return false; + } } if (screenType != 0) { if (settings.orientation != 0 && orientation != 0 @@ -1420,13 +1471,15 @@ struct ResTable_config String8 toString() const { char buf[200]; sprintf(buf, "imsi=%d/%d lang=%c%c reg=%c%c orient=%d touch=%d dens=%d " - "kbd=%d nav=%d input=%d scrnW=%d scrnH=%d sz=%d long=%d vers=%d.%d", + "kbd=%d nav=%d input=%d scrnW=%d scrnH=%d sz=%d long=%d " + "ui=%d night=%d vers=%d.%d", mcc, mnc, language[0] ? language[0] : '-', language[1] ? language[1] : '-', country[0] ? country[0] : '-', country[1] ? country[1] : '-', orientation, touchscreen, density, keyboard, navigation, inputFlags, screenWidth, screenHeight, screenLayout&MASK_SCREENSIZE, screenLayout&MASK_SCREENLONG, + uiMode&MASK_UI_MODE_TYPE, uiMode&MASK_UI_MODE_NIGHT, sdkVersion, minorVersion); return String8(buf); } From 861e14ff9f8180e1bb0e6d1a16756689a0197c82 Mon Sep 17 00:00:00 2001 From: Kenny Root Date: Fri, 12 Feb 2010 13:01:16 -0800 Subject: [PATCH 180/541] Excise code from Unicode.cpp that was dead Remove some utility functions for discovering character data that ICU probably took over a while ago. Change-Id: I97abe4de2f51eb2bf48679941258bc501184c3dc --- include/utils/AndroidUnicode.h | 127 --------------------------------- libs/utils/CharacterData.h | 41 ----------- libs/utils/Unicode.cpp | 61 ---------------- 3 files changed, 229 deletions(-) diff --git a/include/utils/AndroidUnicode.h b/include/utils/AndroidUnicode.h index 563fcd019..2b185d311 100644 --- a/include/utils/AndroidUnicode.h +++ b/include/utils/AndroidUnicode.h @@ -78,69 +78,6 @@ namespace android { DIRECTIONALITY_POP_DIRECTIONAL_FORMAT }; - /** - * Character types as specified in the Unicode standard. These map directly to - * java.lang.Character. - */ - enum CharType { - CHARTYPE_UNASSIGNED = 0, - CHARTYPE_UPPERCASE_LETTER, - CHARTYPE_LOWERCASE_LETTER, - CHARTYPE_TITLECASE_LETTER, - CHARTYPE_MODIFIER_LETTER, - CHARTYPE_OTHER_LETTER, - CHARTYPE_NON_SPACING_MARK, - CHARTYPE_ENCLOSING_MARK, - CHARTYPE_COMBINING_SPACING_MARK, - CHARTYPE_DECIMAL_DIGIT_NUMBER, - CHARTYPE_LETTER_NUMBER, - CHARTYPE_OTHER_NUMBER, - CHARTYPE_SPACE_SEPARATOR, - CHARTYPE_LINE_SEPARATOR, - CHARTYPE_PARAGRAPH_SEPARATOR, - CHARTYPE_CONTROL, - CHARTYPE_FORMAT, - CHARTYPE_MISSING_VALUE_FOR_JAVA, /* This is the mysterious missing 17 value from the java constants */ - CHARTYPE_PRIVATE_USE, - CHARTYPE_SURROGATE, - CHARTYPE_DASH_PUNCTUATION, - CHARTYPE_START_PUNCTUATION, - CHARTYPE_END_PUNCTUATION, - CHARTYPE_CONNECTOR_PUNCTUATION, - CHARTYPE_OTHER_PUNCTUATION, - CHARTYPE_MATH_SYMBOL, - CHARTYPE_CURRENCY_SYMBOL, - CHARTYPE_MODIFIER_SYMBOL, - CHARTYPE_OTHER_SYMBOL, - CHARTYPE_INITIAL_QUOTE_PUNCTUATION, - CHARTYPE_FINAL_QUOTE_PUNCTUATION - }; - - /** - * Decomposition types as described by the unicode standard. These values map to - * the same values in uchar.h in ICU. - */ - enum DecompositionType { - DECOMPOSITION_NONE = 0, - DECOMPOSITION_CANONICAL, - DECOMPOSITION_COMPAT, - DECOMPOSITION_CIRCLE, - DECOMPOSITION_FINAL, - DECOMPOSITION_FONT, - DECOMPOSITION_FRACTION, - DECOMPOSITION_INITIAL, - DECOMPOSITION_ISOLATED, - DECOMPOSITION_MEDIAL, - DECOMPOSITION_NARROW, - DECOMPOSITION_NOBREAK, - DECOMPOSITION_SMALL, - DECOMPOSITION_SQUARE, - DECOMPOSITION_SUB, - DECOMPOSITION_SUPER, - DECOMPOSITION_VERTICAL, - DECOMPOSITION_WIDE - }; - /** * Returns the packed data for java calls * @param c The unicode character. @@ -161,61 +98,6 @@ namespace android { */ static uint32_t getPackedData(UChar32 c); - /** - * Get the Character type. - * @param c The unicode character. - * @return The character's type or CHARTYPE_UNASSIGNED if the character is invalid - * or has an unassigned class. - */ - static CharType getType(UChar32 c); - - /** - * Get the Character's decomposition type. - * @param c The unicode character. - * @return The character's decomposition type or DECOMPOSITION_NONE is there - * is no decomposition. - */ - static DecompositionType getDecompositionType(UChar32 c); - - /** - * Returns the digit value of a character or -1 if the character - * is not within the specified radix. - * - * The digit value is computed for integer characters and letters - * within the given radix. This function does not handle Roman Numerals, - * fractions, or any other characters that may represent numbers. - * - * @param c The unicode character - * @param radix The intended radix. - * @return The digit value or -1 if there is no digit value or if the value is outside the radix. - */ - static int getDigitValue(UChar32 c, int radix = 10); - - /** - * Return the numeric value of a character - * - * @param c The unicode character. - * @return The numeric value of the character. -1 if the character has no numeric value, - * -2 if the character has a numeric value that is not representable by an integer. - */ - static int getNumericValue(UChar32 c); - - /** - * Convert the character to lowercase - * @param c The unicode character. - * @return The lowercase character equivalent of c. If c does not have a lowercase equivalent, - * the original character is returned. - */ - static UChar32 toLower(UChar32 c); - - /** - * Convert the character to uppercase - * @param c The unicode character. - * @return The uppercase character equivalent of c. If c does not have an uppercase equivalent, - * the original character is returned. - */ - static UChar32 toUpper(UChar32 c); - /** * Get the directionality of the character. * @param c The unicode character. @@ -239,15 +121,6 @@ namespace android { * @see isMirrored */ static UChar32 toMirror(UChar32 c); - - /** - * Convert the character to title case. - * @param c The unicode character. - * @return The titlecase equivalent of c. If c does not have a titlecase equivalent, - * the original character is returned. - */ - static UChar32 toTitle(UChar32 c); - }; } diff --git a/libs/utils/CharacterData.h b/libs/utils/CharacterData.h index e931d995e..d9b7c5631 100644 --- a/libs/utils/CharacterData.h +++ b/libs/utils/CharacterData.h @@ -615,32 +615,6 @@ namespace CharacterData { {0, 0} }; - // Array of uppercase differences - static const short UCDIFF[] = { - 0, -32, 743, 121, -1, -232, -300, 97, - 163, 130, 56, -2, -79, -210, -206, -205, - -202, -203, -207, -209, -211, -213, -214, -218, - -217, -219, -83, 84, -38, -37, -31, -64, - -63, -62, -57, -47, -54, -86, -80, 7, - -96, -48, -59, 8, 74, 86, 100, 128, - 112, 126, 9, -7205, -16, -26, -7264, -40 - }; - - // Array of lowercase differences - static const short LCDIFF[] = { - 0, 32, 1, -199, -121, 210, 206, 205, - 79, 202, 203, 207, 211, 209, 213, 214, - 218, 217, 219, 2, -97, -56, -130, -163, - 83, 38, 37, 64, 63, -60, -7, 80, - 48, 7264, -8, -74, -9, -86, -100, -112, - -128, -126, -7517, -8383, -8262, 16, 26, 40 - }; - - // Array of titlecase differences - static const short TCDIFF[] = { - 3, 1, 0, -1 - }; - // Array of mirrored character differences static const short MIRROR_DIFF[] = { 0, 1, -1, 2, -2, 16, -16, 3, @@ -649,21 +623,6 @@ namespace CharacterData { -2108 }; - // Array of all possible numeric values - static const int NUMERICS[] = { - -1, 0, 1, 2, 3, 4, 5, 6, - 7, 8, 9, 10, 11, 12, 13, 14, - 15, 16, 17, 18, 19, 20, 21, 22, - 23, 24, 25, 26, 27, 28, 29, 30, - 31, 32, 33, 34, 35, -2, 100, 1000, - 40, 50, 60, 70, 80, 90, 10000, 500, - 5000, 36, 37, 38, 39, 41, 42, 43, - 44, 45, 46, 47, 48, 49, 200, 300, - 400, 600, 700, 800, 900, 2000, 3000, 4000, - 6000, 7000, 8000, 9000, 20000, 30000, 40000, 50000, - 60000, 70000, 80000, 90000 - }; - // All possible packed data values, no duplicates static const uint32_t PACKED_DATA[] = { 0x00000000, 0x0000012F, 0x0000016F, 0x0000014F, 0x0000018F, 0x0000018C, 0x000001B8, 0x000000B8, diff --git a/libs/utils/Unicode.cpp b/libs/utils/Unicode.cpp index f92703e7f..65dbb4a3e 100644 --- a/libs/utils/Unicode.cpp +++ b/libs/utils/Unicode.cpp @@ -103,55 +103,6 @@ uint32_t android::Unicode::getPackedData(UChar32 c) return CharacterData::PACKED_DATA[findCharacterValue(c) & 0x7FF]; } -android::Unicode::CharType android::Unicode::getType(UChar32 c) -{ - if (c < 0 || c >= 0x10FFFF) - return CHARTYPE_UNASSIGNED; - return (CharType)((getPackedData(c) >> TYPE_SHIFT) & TYPE_MASK); -} - -android::Unicode::DecompositionType android::Unicode::getDecompositionType(UChar32 c) -{ - // findCharacterValue returns a 16-bit value with the top 5 bits containing a decomposition type - // and the remaining bits containing an index. - return (DecompositionType)((findCharacterValue(c) >> DECOMPOSITION_SHIFT) & DECOMPOSITION_MASK); -} - -int android::Unicode::getDigitValue(UChar32 c, int radix) -{ - if (radix < MIN_RADIX || radix > MAX_RADIX) - return -1; - - int tempValue = radix; - - if (c >= '0' && c <= '9') - tempValue = c - '0'; - else if (c >= 'a' && c <= 'z') - tempValue = c - 'a' + 10; - else if (c >= 'A' && c <= 'Z') - tempValue = c - 'A' + 10; - - return tempValue < radix ? tempValue : -1; -} - -int android::Unicode::getNumericValue(UChar32 c) -{ - if (isMirrored(c)) - return -1; - - return (int) CharacterData::NUMERICS[((getPackedData(c) >> NUMERIC_SHIFT) & NUMERIC_MASK)]; -} - -UChar32 android::Unicode::toLower(UChar32 c) -{ - return c + CharacterData::LCDIFF[(getPackedData(c) >> TOLOWER_SHIFT) & TOLOWER_MASK]; -} - -UChar32 android::Unicode::toUpper(UChar32 c) -{ - return c + CharacterData::UCDIFF[(getPackedData(c) >> TOUPPER_SHIFT) & TOUPPER_MASK]; -} - android::Unicode::Direction android::Unicode::getDirectionality(UChar32 c) { uint32_t data = getPackedData(c); @@ -179,15 +130,3 @@ UChar32 android::Unicode::toMirror(UChar32 c) return c + CharacterData::MIRROR_DIFF[(getPackedData(c) >> MIRROR_SHIFT) & MIRROR_MASK]; } - -UChar32 android::Unicode::toTitle(UChar32 c) -{ - int32_t diff = CharacterData::TCDIFF[(getPackedData(c) >> TOTITLE_SHIFT) & TOTITLE_MASK]; - - if (TOTITLE_MASK == diff) - return toUpper(c); - - return c + diff; -} - - From e81635474cbf376c94282f34084fe9cffc4334c2 Mon Sep 17 00:00:00 2001 From: Kenny Root Date: Fri, 12 Feb 2010 14:09:24 -0800 Subject: [PATCH 181/541] Totally remove Unicode.cpp and rely on ICU Unicode.cpp used a packed data table for character data that essentially duplicated ICU's functionality. Change-Id: Ia68fe4ac94e89dc68d9a3f45f33f6e648a5500b7 --- include/utils/AndroidUnicode.h | 128 ------ libs/utils/Android.mk | 1 - libs/utils/CharacterData.h | 689 --------------------------------- libs/utils/Unicode.cpp | 132 ------- 4 files changed, 950 deletions(-) delete mode 100644 include/utils/AndroidUnicode.h delete mode 100644 libs/utils/CharacterData.h delete mode 100644 libs/utils/Unicode.cpp diff --git a/include/utils/AndroidUnicode.h b/include/utils/AndroidUnicode.h deleted file mode 100644 index 2b185d311..000000000 --- a/include/utils/AndroidUnicode.h +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright (C) 2006 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_UNICODE_H -#define ANDROID_UNICODE_H - -#include -#include - -#define REPLACEMENT_CHAR (0xFFFD) - -// this part of code is copied from umachine.h under ICU -/** - * Define UChar32 as a type for single Unicode code points. - * UChar32 is a signed 32-bit integer (same as int32_t). - * - * The Unicode code point range is 0..0x10ffff. - * All other values (negative or >=0x110000) are illegal as Unicode code points. - * They may be used as sentinel values to indicate "done", "error" - * or similar non-code point conditions. - * - * @stable ICU 2.4 - */ -typedef int32_t UChar32; - -namespace android { - - class Encoding; - /** - * \class Unicode - * - * Helper class for getting properties of Unicode characters. Characters - * can have one of the types listed in CharType and each character can have the - * directionality of Direction. - */ - class Unicode - { - public: - /** - * Directions specified in the Unicode standard. These directions map directly - * to java.lang.Character. - */ - enum Direction { - DIRECTIONALITY_UNDEFINED = -1, - DIRECTIONALITY_LEFT_TO_RIGHT, - DIRECTIONALITY_RIGHT_TO_LEFT, - DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC, - DIRECTIONALITY_EUROPEAN_NUMBER, - DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR, - DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR, - DIRECTIONALITY_ARABIC_NUMBER, - DIRECTIONALITY_COMMON_NUMBER_SEPARATOR, - DIRECTIONALITY_NONSPACING_MARK, - DIRECTIONALITY_BOUNDARY_NEUTRAL, - DIRECTIONALITY_PARAGRAPH_SEPARATOR, - DIRECTIONALITY_SEGMENT_SEPARATOR, - DIRECTIONALITY_WHITESPACE, - DIRECTIONALITY_OTHER_NEUTRALS, - DIRECTIONALITY_LEFT_TO_RIGHT_EMBEDDING, - DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE, - DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING, - DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE, - DIRECTIONALITY_POP_DIRECTIONAL_FORMAT - }; - - /** - * Returns the packed data for java calls - * @param c The unicode character. - * @return The packed data for the character. - * - * Copied from java.lang.Character implementation: - * 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 - * F E D C B A 9 8 7 6 5 4 3 2 1 0 F E D C B A 9 8 7 6 5 4 3 2 1 0 - * - * 31 types --------- - * 18 directionalities --------- - * 2 mirroreds - - * ----------- 56 toupper diffs - * ----------- 48 tolower diffs - * --- 4 totitlecase diffs - * ------------- 84 numeric values - * --------- 24 mirror char diffs - */ - static uint32_t getPackedData(UChar32 c); - - /** - * Get the directionality of the character. - * @param c The unicode character. - * @return The direction of the character or DIRECTIONALITY_UNDEFINED. - */ - static Direction getDirectionality(UChar32 c); - - /** - * Check if the character is a mirrored character. This means that the character - * has an equivalent character that is the mirror image of itself. - * @param c The unicode character. - * @return True iff c has a mirror equivalent. - */ - static bool isMirrored(UChar32 c); - - /** - * Return the mirror of the given character. - * @param c The unicode character. - * @return The mirror equivalent of c. If c does not have a mirror equivalent, - * the original character is returned. - * @see isMirrored - */ - static UChar32 toMirror(UChar32 c); - }; - -} - -#endif diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk index 59409a2bf..d2cfd3b5c 100644 --- a/libs/utils/Android.mk +++ b/libs/utils/Android.mk @@ -75,7 +75,6 @@ include $(CLEAR_VARS) # we have the common sources, plus some device-specific stuff LOCAL_SRC_FILES:= \ $(commonSources) \ - Unicode.cpp \ BackupData.cpp \ BackupHelpers.cpp diff --git a/libs/utils/CharacterData.h b/libs/utils/CharacterData.h deleted file mode 100644 index d9b7c5631..000000000 --- a/libs/utils/CharacterData.h +++ /dev/null @@ -1,689 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// Automatically generated on 07-11-2006 by make-CharacterDataC -// DO NOT EDIT DIRECTLY -namespace CharacterData { - - // Structure containing an array of ranges - struct Range { - int length; - const uint32_t* array; - }; - - // For Latin1 characters just index into this array to get the index and decomposition - static const uint16_t LATIN1_DATA[] = { - 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, - 0x0001, 0x0002, 0x0003, 0x0002, 0x0004, 0x0003, 0x0001, 0x0001, - 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, - 0x0001, 0x0001, 0x0001, 0x0001, 0x0003, 0x0003, 0x0003, 0x0002, - 0x0005, 0x0006, 0x0006, 0x0007, 0x0008, 0x0007, 0x0006, 0x0006, - 0x0009, 0x000A, 0x0006, 0x000B, 0x000C, 0x000D, 0x000C, 0x000C, - 0x000E, 0x000F, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, - 0x0016, 0x0017, 0x000C, 0x0006, 0x0018, 0x0019, 0x001A, 0x0006, - 0x0006, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, 0x0020, 0x0021, - 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, - 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, 0x0030, 0x0031, - 0x0032, 0x0033, 0x0034, 0x0035, 0x0006, 0x0036, 0x0037, 0x0038, - 0x0037, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, - 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, - 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, - 0x0050, 0x0051, 0x0052, 0x0035, 0x0019, 0x0036, 0x0019, 0x0001, - 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0003, 0x0001, 0x0001, - 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, - 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, - 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, - 0x5853, 0x0006, 0x0008, 0x0008, 0x0008, 0x0008, 0x0054, 0x0054, - 0x1037, 0x0054, 0x7855, 0x0056, 0x0019, 0x0057, 0x0054, 0x1037, - 0x0058, 0x0059, 0x785A, 0x785B, 0x1037, 0x105C, 0x0054, 0x0006, - 0x1037, 0x785D, 0x7855, 0x005E, 0x305F, 0x305F, 0x305F, 0x0006, - 0x0860, 0x0860, 0x0860, 0x0860, 0x0860, 0x0860, 0x0060, 0x0860, - 0x0860, 0x0860, 0x0860, 0x0860, 0x0860, 0x0860, 0x0860, 0x0860, - 0x0060, 0x0860, 0x0860, 0x0860, 0x0860, 0x0860, 0x0860, 0x0019, - 0x0060, 0x0860, 0x0860, 0x0860, 0x0860, 0x0860, 0x0060, 0x0055, - 0x0861, 0x0861, 0x0861, 0x0861, 0x0861, 0x0861, 0x0061, 0x0861, - 0x0861, 0x0861, 0x0861, 0x0861, 0x0861, 0x0861, 0x0861, 0x0861, - 0x0061, 0x0861, 0x0861, 0x0861, 0x0861, 0x0861, 0x0861, 0x0019, - 0x0061, 0x0861, 0x0861, 0x0861, 0x0861, 0x0861, 0x0061, 0x0862 - }; - - // Each of these arrays is stripped into ranges. In order to build the arrays, each - // codepoint was bit-shifted so that even and odd characters were separated into different - // arrays. The identifier of each array is the top byte after bit-shifting. - // The numbers stored in the array are the bit-shifted codepoint, the decomposition, and an - // index into another array of all possible packed data values. The top 16 bits are the - // codepoint and the bottom 16 are the decomposition and index. The top 5 bits for the decomposition - // and the rest for the index. - static const uint32_t a0[] = { - 0x00800863, 0x00880063, 0x00890863, 0x00930063, 0x00940863, 0x00980864, 0x00991063, 0x009A0863, - 0x009C0055, 0x009D0865, 0x00A01065, 0x00A10065, 0x00A20865, 0x00A50063, 0x00A60863, 0x00A90063, - 0x00AA0863, 0x00B30063, 0x00B40863, 0x00BC0866, 0x00BD0865, 0x00C00055, 0x00C10063, 0x00C30067, - 0x00C40065, 0x00C50068, 0x00C60065, 0x00C70069, 0x00C8006A, 0x00C90065, 0x00CA006B, 0x00CB006C, - 0x00CC0063, 0x00CD006D, 0x00CE006C, 0x00CF006E, 0x00D00863, 0x00D10063, 0x00D3006F, 0x00D40065, - 0x00D50055, 0x00D60063, 0x00D7006F, 0x00D80865, 0x00D90070, 0x00DA0065, 0x00DC0063, 0x00DD0055, - 0x00DE0063, 0x00DF0055, 0x00E00071, 0x00E21072, 0x00E31073, 0x00E41074, 0x00E51072, 0x00E61073, - 0x00E70865, 0x00EF0863, 0x00F20063, 0x00F30863, 0x00F80855, 0x00F91074, 0x00FA0863, 0x00FB0075, - 0x00FC0863, 0x010E0063, 0x010F0863, 0x01100076, 0x01110063, 0x01130863, 0x011A0055, 0x011D0077, - 0x011E0065, 0x011F0077, 0x01200055, 0x01210078, 0x01280055, 0x012A0079, 0x012B007A, 0x012C0055, - 0x0130007A, 0x01310055, 0x0134007B, 0x01350055, 0x0139007C, 0x013A0055, 0x0140007D, 0x01410055, - 0x0144007D, 0x0145007E, 0x01460055, 0x0149007F, 0x014A0080, 0x014B0055, 0x01587881, 0x015D0082, - 0x015E0081, 0x01610037, 0x01630082, 0x01680081, 0x01690037, 0x016C1037, 0x016F0037, 0x01707881, - 0x01730037, 0x01770081, 0x01780037, 0x01800083, 0x01A00883, 0x01A10083, 0x01A20883, 0x01A30083, - 0x01B80078, 0x01BA0837, 0x01BB0078, 0x01BD1081, 0x01BE0078, 0x01BF0806, 0x01C00078, 0x01C21037, - 0x01C30884, 0x01C40885, 0x01C60886, 0x01C70887, 0x01C80855, 0x01C90060, 0x01D10078, 0x01D20060, - 0x01D50860, 0x01D60888, 0x01D70889, 0x01D80855, 0x01D90061, 0x01E1008A, 0x01E20061, 0x01E50861, - 0x01E6088B, 0x01E7088C, 0x01E8108D, 0x01E91077, 0x01EA0877, 0x01EB108E, 0x01EC0063, 0x01F8108F, - 0x01F91090, 0x01FA1091, 0x01FB0019, 0x01FC0065, 0x01FD0063, 0x01FE0055, 0x01FF0077, 0x02000892, - 0x02010092, 0x02060892, 0x02080060, 0x02180061, 0x02280893, 0x02290093, 0x022E0893, 0x02300063, - 0x023B0863, 0x023C0063, 0x02410094, 0x02420083, 0x02440095, 0x02450063, 0x02600077, 0x02610865, - 0x02620065, 0x02680863, 0x026A0063, 0x026B0863, 0x026C0063, 0x026D0863, 0x02700063, 0x02710863, - 0x02740063, 0x02750863, 0x027B0063, 0x027C0863, 0x027D0078, 0x02800063, 0x02880078, 0x02990096, - 0x02AC0078, 0x02AD0097, 0x02B00078, 0x02B10098, 0x02C40078, 0x02C50099, 0x02C60078, 0x02C90083, - 0x02DD0078, 0x02DE0083, 0x02DF009A, 0x02E10083, 0x02E3009A, 0x02E40078, 0x02E8009B, 0x02F60078, - 0x02F8009B, 0x02FA009A, 0x02FB0078, 0x0300009C, 0x03020078, 0x0306000C, 0x03070054, 0x03080083, - 0x030B0078, 0x030F009D, 0x03100078, 0x0311089E, 0x0314009E, 0x031E0078, 0x0320009F, 0x0321009E, - 0x03260083, 0x033000A0, 0x033100A1, 0x033200A2, 0x033300A3, 0x033400A4, 0x03350007, 0x033600A5, - 0x0337009E, 0x03380083, 0x0339009E, 0x033B109E, 0x033D009E, 0x0360089E, 0x0362009E, 0x036A009D, - 0x036B0083, 0x036F0095, 0x03700083, 0x0373009F, 0x03740083, 0x0377009E, 0x0378000E, 0x03790010, - 0x037A0012, 0x037B0014, 0x037C0016, 0x037D009E, 0x037F00A6, 0x0380009D, 0x03870078, 0x0388009E, - 0x03980083, 0x03A60078, 0x03A7009E, 0x03B70078, 0x03C0009E, 0x03D30083, 0x03D90078, 0x04810083, - 0x04820071, 0x049A0871, 0x049B0071, 0x049D0078, 0x049E0083, 0x049F00A7, 0x04A10083, 0x04A500A7, - 0x04A70078, 0x04A80071, 0x04A90083, 0x04AB0078, 0x04AC0871, 0x04B00071, 0x04B10083, 0x04B20097, - 0x04B300A8, 0x04B400A9, 0x04B500AA, 0x04B600AB, 0x04B700AC, 0x04B80097, 0x04B90078, 0x04C100A7, - 0x04C20078, 0x04C30071, 0x04C70078, 0x04C80071, 0x04C90078, 0x04CA0071, 0x04DA0078, 0x04DB0071, - 0x04DD0078, 0x04DE0083, 0x04DF00A7, 0x04E10083, 0x04E30078, 0x04E400A7, 0x04E50078, 0x04E608A7, - 0x04E70071, 0x04E80078, 0x04EE0871, 0x04EF0078, 0x04F00071, 0x04F10083, 0x04F20078, 0x04F300A8, - 0x04F400A9, 0x04F500AA, 0x04F600AB, 0x04F700AC, 0x04F80071, 0x04F90008, 0x04FA00AD, 0x04FB00AE, - 0x04FC00AF, 0x04FD0094, 0x04FE0078, 0x05010083, 0x05020078, 0x05030071, 0x05060078, 0x05080071, - 0x05090078, 0x050A0071, 0x051A0078, 0x051B0871, 0x051C0071, 0x051D0078, 0x051E0083, 0x051F00A7, - 0x05210083, 0x05220078, 0x05240083, 0x05250078, 0x05260083, 0x05270078, 0x052D0871, 0x052E0071, - 0x052F0871, 0x05300078, 0x053300A8, 0x053400A9, 0x053500AA, 0x053600AB, 0x053700AC, 0x05380083, - 0x05390071, 0x053B0078, 0x05410083, 0x05420078, 0x05430071, 0x05470078, 0x05480071, 0x05490078, - 0x054A0071, 0x055A0078, 0x055B0071, 0x055D0078, 0x055E0083, 0x055F00A7, 0x05610083, 0x05630078, - 0x05640083, 0x05650078, 0x056600A7, 0x05670078, 0x05680071, 0x05690078, 0x05700071, 0x05710083, - 0x05720078, 0x057300A8, 0x057400A9, 0x057500AA, 0x057600AB, 0x057700AC, 0x05780078, 0x058100A7, - 0x05820078, 0x05830071, 0x05870078, 0x05880071, 0x05890078, 0x058A0071, 0x059A0078, 0x059B0071, - 0x059D0078, 0x059E0083, 0x059F00A7, 0x05A10083, 0x05A20078, 0x05A408A7, 0x05A50078, 0x05A608A7, - 0x05A70078, 0x05AB0083, 0x05AC0078, 0x05AE0871, 0x05AF0078, 0x05B00071, 0x05B10078, 0x05B300A8, - 0x05B400A9, 0x05B500AA, 0x05B600AB, 0x05B700AC, 0x05B80094, 0x05B90078, 0x05C10083, 0x05C20078, - 0x05C30071, 0x05C60078, 0x05C70071, 0x05CA0871, 0x05CB0078, 0x05CD0071, 0x05D00078, 0x05D20071, - 0x05D30078, 0x05D40071, 0x05D60078, 0x05D70071, 0x05DD0078, 0x05DF00A7, 0x05E00083, 0x05E100A7, - 0x05E20078, 0x05E300A7, 0x05E508A7, 0x05E70078, 0x05F300A8, 0x05F400A9, 0x05F500AA, 0x05F600AB, - 0x05F700AC, 0x05F800B0, 0x05F900B1, 0x05FA0054, 0x05FE0078, 0x060100A7, 0x06020078, 0x06030071, - 0x061A0078, 0x061B0071, 0x061D0078, 0x061F0083, 0x062100A7, 0x06230083, 0x06240883, 0x06250083, - 0x06270078, 0x062B0083, 0x062C0078, 0x06300071, 0x06310078, 0x063300A8, 0x063400A9, 0x063500AA, - 0x063600AB, 0x063700AC, 0x06380078, 0x064100A7, 0x06420078, 0x06430071, 0x065A0078, 0x065B0071, - 0x065D0078, 0x065E0083, 0x065F00A7, 0x066008A7, 0x066100A7, 0x066300B2, 0x066408A7, 0x06660083, - 0x06670078, 0x066B00A7, 0x066C0078, 0x066F0071, 0x06710078, 0x067300A8, 0x067400A9, 0x067500AA, - 0x067600AB, 0x067700AC, 0x06780078, 0x068100A7, 0x06820078, 0x06830071, 0x069D0078, 0x069F00A7, - 0x06A10083, 0x06A20078, 0x06A300A7, 0x06A508A7, 0x06A70078, 0x06B00071, 0x06B10078, 0x06B300A8, - 0x06B400A9, 0x06B500AA, 0x06B600AB, 0x06B700AC, 0x06B80078, 0x06C100A7, 0x06C20078, 0x06C30071, - 0x06CC0078, 0x06CD0071, 0x06D90078, 0x06DA0071, 0x06DE0078, 0x06E00071, 0x06E40078, 0x06E50083, - 0x06E60078, 0x06E800A7, 0x06E90083, 0x06EC00A7, 0x06ED08A7, 0x06F00078, 0x06F900A7, 0x06FA0097, - 0x06FB0078, 0x07010071, 0x071A0083, 0x071E0078, 0x07200071, 0x07230081, 0x07240083, 0x072800A8, - 0x072900A9, 0x072A00AA, 0x072B00AB, 0x072C00AC, 0x072D0097, 0x072E0078, 0x07410071, 0x07430078, - 0x07440071, 0x07460078, 0x074A0071, 0x074C0078, 0x074D0071, 0x07500078, 0x07510071, 0x07520078, - 0x07550071, 0x07560078, 0x07570071, 0x075A0083, 0x075D0078, 0x075E0083, 0x075F0078, 0x07600071, - 0x07630081, 0x07640083, 0x07670078, 0x076800A8, 0x076900A9, 0x076A00AA, 0x076B00AB, 0x076C00AC, - 0x076D0078, 0x076E1071, 0x076F0078, 0x07800071, 0x07810094, 0x07820097, 0x07865897, 0x07870097, - 0x078A0094, 0x078C0083, 0x078D0094, 0x079000A8, 0x079100A9, 0x079200AA, 0x079300AB, 0x079400AC, - 0x079500B3, 0x079A0094, 0x079D00B4, 0x079F00A7, 0x07A00071, 0x07A40078, 0x07A50071, 0x07A90871, - 0x07AA0071, 0x07AE0871, 0x07AF0071, 0x07B60078, 0x07B90083, 0x07BB0883, 0x07BD0083, 0x07C40071, - 0x07C60078, 0x07C80083, 0x07CC0078, 0x07CD0083, 0x07D10883, 0x07D20083, 0x07D60883, 0x07D70083, - 0x07DF0094, 0x07E30083, 0x07E40094, 0x07E70078, 0x07E80097, 0x07E90078, 0x08000071, 0x08110078, - 0x08120071, 0x08130871, 0x08140078, 0x08150071, 0x081600A7, 0x08170083, 0x081A0078, 0x081B0083, - 0x081C00A7, 0x081D0078, 0x082000A8, 0x082100A9, 0x082200AA, 0x082300AB, 0x082400AC, 0x08250097, - 0x08280071, 0x082B00A7, 0x082C0083, 0x082D0078, 0x085000B5, 0x08630078, 0x08680071, 0x087E7881, - 0x087F0078, 0x08800071, 0x08AD0078, 0x08B00071, 0x08D20078, 0x08D40071, 0x08FD0078, 0x09000071, - 0x09270078, 0x09280071, 0x092F0078, 0x09300071, 0x09470078, 0x09480071, 0x095B0078, 0x095C0071, - 0x09630078, 0x09640071, 0x098B0078, 0x098C0071, 0x09AE0078, 0x09B00094, 0x09B10097, 0x09B500B6, - 0x09B600B7, 0x09B700B8, 0x09B800B9, 0x09B900B0, 0x09BA00BA, 0x09BB00BB, 0x09BC00BC, 0x09BD00BD, - 0x09BE00BE, 0x09BF0078, 0x09C00071, 0x09C80054, 0x09CD0078, 0x09D00071, 0x09FB0078, 0x0A010071, - 0x0B370097, 0x0B380071, 0x0B3C0078, 0x0B400005, 0x0B410071, 0x0B4E00BF, 0x0B4F0078, 0x0B500071, - 0x0B760097, 0x0B7700C0, 0x0B7800C1, 0x0B790078, 0x0B800071, 0x0B890083, 0x0B8B0078, 0x0B900071, - 0x0B990083, 0x0B9B0097, 0x0B9C0078, 0x0BA00071, 0x0BA90083, 0x0BAA0078, 0x0BB00071, 0x0BB90083, - 0x0BBA0078, 0x0BC00071, 0x0BDA00C2, 0x0BDB00A7, 0x0BDC0083, 0x0BDF00A7, 0x0BE30083, 0x0BE400A7, - 0x0BE50083, 0x0BEA0097, 0x0BEE0071, 0x0BEF0078, 0x0BF000A8, 0x0BF100A9, 0x0BF200AA, 0x0BF300AB, - 0x0BF400AC, 0x0BF50078, 0x0BF800C3, 0x0BF900C4, 0x0BFA00C5, 0x0BFB00C6, 0x0BFC00C7, 0x0BFD0078, - 0x0C000006, 0x0C030099, 0x0C040006, 0x0C060083, 0x0C070005, 0x0C0800A8, 0x0C0900A9, 0x0C0A00AA, - 0x0C0B00AB, 0x0C0C00AC, 0x0C0D0078, 0x0C100071, 0x0C3C0078, 0x0C400071, 0x0C550078, 0x0C800071, - 0x0C8F0078, 0x0C900083, 0x0C9200A7, 0x0C940083, 0x0C9500C8, 0x0C960078, 0x0C9800A7, 0x0C990083, - 0x0C9A00A7, 0x0C9D0083, 0x0C9E0078, 0x0CA00054, 0x0CA10078, 0x0CA20006, 0x0CA300A8, 0x0CA400A9, - 0x0CA500AA, 0x0CA600AB, 0x0CA700AC, 0x0CA80071, 0x0CB70078, 0x0CB80071, 0x0CBB0078, 0x0CC00071, - 0x0CD50078, 0x0CD800A7, 0x0CE10071, 0x0CE400A7, 0x0CE50078, 0x0CE800A8, 0x0CE900A9, 0x0CEA00AA, - 0x0CEB00AB, 0x0CEC00AC, 0x0CED0078, 0x0CEF0006, 0x0CF00054, 0x0D000071, 0x0D0C0083, 0x0D0D00A7, - 0x0D0E0078, 0x0D0F0097, 0x0D100078, 0x0E800055, 0x0E967881, 0x0EA70081, 0x0EA87881, 0x0EB17055, - 0x0EB60055, 0x0EBC7881, 0x0EBD0055, 0x0ECE7881, 0x0EE00083, 0x0EE20078, 0x0F000863, 0x0F4B0855, - 0x0F4D1055, 0x0F4E0078, 0x0F500863, 0x0F7D0078, 0x0F8008C9, 0x0F8408CA, 0x0F8808C9, 0x0F8B0078, - 0x0F8C08CA, 0x0F8F0078, 0x0F9008C9, 0x0F9408CA, 0x0F9808C9, 0x0F9C08CA, 0x0FA008C9, 0x0FA30078, - 0x0FA408CA, 0x0FA70078, 0x0FA80855, 0x0FAC0078, 0x0FB008C9, 0x0FB408CA, 0x0FB808CB, 0x0FB908CC, - 0x0FBB08CD, 0x0FBC08CE, 0x0FBD08CF, 0x0FBE08D0, 0x0FBF0078, 0x0FC008C9, 0x0FC408D1, 0x0FC808C9, - 0x0FCC08D1, 0x0FD008C9, 0x0FD408D1, 0x0FD808C9, 0x0FD90855, 0x0FDC08CA, 0x0FDD08D2, 0x0FDE08D3, - 0x0FDF08D4, 0x0FE01037, 0x0FE10855, 0x0FE408D5, 0x0FE608D3, 0x0FE70837, 0x0FE808C9, 0x0FE90855, - 0x0FEA0078, 0x0FEB0855, 0x0FEC08CA, 0x0FED08D6, 0x0FEE0078, 0x0FEF0837, 0x0FF008C9, 0x0FF10855, - 0x0FF408CA, 0x0FF508D7, 0x0FF608D8, 0x0FF70837, 0x0FF80078, 0x0FF90855, 0x0FFC08D9, 0x0FFD08DA, - 0x0FFE08D3, 0x0FFF1037, 0x10000805, 0x10011005, 0x10060057, 0x100700C2, 0x10080099, 0x100B0006, - 0x100C00DB, 0x100D00B4, 0x100E00DB, 0x100F00B4, 0x10100006, 0x10121006, 0x101400DC, 0x101500DD, - 0x101600DE, 0x101700DF, 0x10180007, 0x101A1007, 0x101B1006, 0x101C0006, 0x101D00E0, 0x101E1006, - 0x10200038, 0x10210006, 0x102200E1, 0x1023000A, 0x10241006, 0x10250006, 0x10290019, 0x102A0038, - 0x102B0006, 0x10300057, 0x10320078, 0x10350057, 0x103878E2, 0x10390078, 0x103A78E3, 0x103B78E4, - 0x103C78E5, 0x103D780B, 0x103E7819, 0x103F780A, 0x104070E2, 0x1041705A, 0x104270E3, 0x104370E4, - 0x104470E5, 0x1045700B, 0x10467019, 0x1047700A, 0x10487081, 0x104B0078, 0x10500008, 0x10541008, - 0x10550008, 0x105B0078, 0x10680083, 0x106F0095, 0x10730083, 0x10760078, 0x10801054, 0x10812877, - 0x10820054, 0x10831054, 0x10840054, 0x10852855, 0x10862877, 0x10872855, 0x10882877, 0x108A0054, - 0x108B1054, 0x108C0054, 0x108D2877, 0x108F0054, 0x10907854, 0x10922877, 0x109308E6, 0x10942877, - 0x109508E7, 0x10962877, 0x10970058, 0x10982877, 0x10990054, 0x109A2855, 0x109B1071, 0x109D0054, - 0x109E2855, 0x109F2877, 0x10A028E8, 0x10A10019, 0x10A32855, 0x10A50054, 0x10A70078, 0x10AA305F, - 0x10B010E9, 0x10B110EA, 0x10B210EB, 0x10B310EC, 0x10B410ED, 0x10B510EE, 0x10B610EF, 0x10B710F0, - 0x10B810F1, 0x10B910F2, 0x10BA10F3, 0x10BB10F4, 0x10BC10F5, 0x10BD10F6, 0x10BE10F7, 0x10BF10F8, - 0x10C000F9, 0x10C100FA, 0x10C20078, 0x10C80019, 0x10CB0054, 0x10CD0819, 0x10CE0054, 0x10D00019, - 0x10D10054, 0x10D30019, 0x10D40054, 0x10D70819, 0x10D80054, 0x10E70819, 0x10E80054, 0x10E90019, - 0x10EB0054, 0x10FA0019, 0x110100E8, 0x110208E8, 0x11030019, 0x110400FB, 0x110608FC, 0x11070019, - 0x1109000B, 0x110A0019, 0x110B00E8, 0x110C0019, 0x110D00E8, 0x110F0019, 0x111000E8, 0x111208E8, - 0x11140019, 0x111610E8, 0x111700E8, 0x111810E8, 0x111900E8, 0x111A0019, 0x111E00FD, 0x111F00E8, - 0x112208E8, 0x112300E8, 0x11270019, 0x112900FD, 0x112B0019, 0x113008E8, 0x113200FD, 0x11360019, - 0x113708FD, 0x113900FD, 0x113A08FD, 0x113B00FD, 0x113C08FD, 0x113D00FD, 0x114008FD, 0x114100FD, - 0x114208FD, 0x114300FD, 0x114408FD, 0x114500FD, 0x114600E8, 0x11470019, 0x114800FE, 0x114A0019, - 0x114C00FF, 0x114D0019, 0x115100FD, 0x11520019, 0x11530100, 0x11540101, 0x115500E8, 0x115608E8, - 0x115800FD, 0x115C00E8, 0x115D0019, 0x115F00E8, 0x11600019, 0x116500FE, 0x11670019, 0x116800FD, - 0x11690019, 0x116B00FD, 0x117008FD, 0x117200FD, 0x117508FD, 0x11770019, 0x117800FD, 0x11790102, - 0x117B0103, 0x117C00E8, 0x117D0104, 0x117F0105, 0x11800054, 0x118400FD, 0x11860054, 0x119000E8, - 0x11910054, 0x1195080A, 0x11960054, 0x119B0094, 0x11BE0019, 0x11BF0054, 0x11CE0019, 0x11DA00B4, - 0x11DB0006, 0x11DC0054, 0x11EE0078, 0x12000054, 0x12140078, 0x12200054, 0x12260078, 0x12301906, - 0x12311907, 0x12321908, 0x12331909, 0x1234190A, 0x1235190B, 0x1236190C, 0x1237190D, 0x1238190E, - 0x1239190F, 0x123A1106, 0x123B1107, 0x123C1108, 0x123D1109, 0x123E110A, 0x123F110B, 0x1240110C, - 0x1241110D, 0x1242110E, 0x1243110F, 0x1244105D, 0x1245105B, 0x12461110, 0x12471111, 0x12481112, - 0x12491113, 0x124A1114, 0x124B1115, 0x124C1116, 0x124D1117, 0x124E1094, 0x125B1918, 0x12681919, - 0x127518C3, 0x1276011A, 0x1277011B, 0x1278011C, 0x1279011D, 0x127A011E, 0x127B00C4, 0x127C00C5, - 0x127D00C6, 0x127E00C7, 0x127F011F, 0x12800054, 0x12FC0019, 0x13000054, 0x134F0078, 0x13500054, - 0x13560094, 0x13570054, 0x13590078, 0x13810054, 0x13850078, 0x13860054, 0x13940078, 0x13950054, - 0x13A60078, 0x13A80054, 0x13AA0078, 0x13AB0054, 0x13B00078, 0x13B10054, 0x13B40009, 0x13BB0106, - 0x13BC0107, 0x13BD0108, 0x13BE0109, 0x13BF010A, 0x13C00106, 0x13C10107, 0x13C20108, 0x13C30109, - 0x13C4010A, 0x13C50106, 0x13C60107, 0x13C70108, 0x13C80109, 0x13C9010A, 0x13CA0054, 0x13CB0078, - 0x13CC0054, 0x13D80078, 0x13D90054, 0x13E000E8, 0x13E10019, 0x13E200FE, 0x13E3000A, 0x13E40078, - 0x13E80019, 0x13EA00E8, 0x13EB00FE, 0x13EC0019, 0x13EE00E8, 0x13EF00FE, 0x13F00019, 0x13F100FD, - 0x13F30009, 0x13F60078, 0x13F80019, 0x14000094, 0x14800019, 0x14C2000A, 0x14C70120, 0x14C80121, - 0x14C9000A, 0x14CD0019, 0x14CE00E8, 0x14D80019, 0x14DC0122, 0x14DD0019, 0x14E000FD, 0x14E100E8, - 0x14E200FD, 0x14E30019, 0x14E700E8, 0x14E800FE, 0x14EA00FD, 0x14EB0019, 0x14EC0009, 0x14EE00E8, - 0x14EF0019, 0x14F200E8, 0x14F30019, 0x14F400E8, 0x14F50019, 0x14FA00E8, 0x14FC00FD, 0x14FD0019, - 0x14FE0009, 0x14FF0019, 0x150500E8, 0x150610E8, 0x150700E8, 0x15110019, 0x151200E8, 0x15140019, - 0x151600FE, 0x15180019, 0x151A00FD, 0x151B0019, 0x151E00FD, 0x151F00E8, 0x15200019, 0x152C00E8, - 0x152D0019, 0x153200FD, 0x15330019, 0x153500E8, 0x15370019, 0x153800E8, 0x15390019, 0x153A10E8, - 0x153B1019, 0x153C0019, 0x153D00FE, 0x153E00E8, 0x153F00FE, 0x154300E8, 0x154600FE, 0x154700E8, - 0x154900FE, 0x154F00E8, 0x155100FE, 0x15520019, 0x155300FD, 0x15570019, 0x155800FE, 0x155900E8, - 0x155A00FE, 0x155B00E8, 0x155E00FE, 0x156400E8, 0x156700FE, 0x156C0019, 0x156E08E8, 0x156F0123, - 0x15700019, 0x157100E8, 0x15720124, 0x157300E8, 0x15740019, 0x157600FD, 0x157700E8, 0x15780019, - 0x157C00FE, 0x157E0019, 0x15800054, 0x158A0078, 0x16000096, 0x16180098, 0x16300078, 0x16400063, - 0x16720055, 0x16730054, 0x16760078, 0x167D0006, 0x16800125, 0x16930078, 0x16980071, 0x16B30078, - 0x16C00071, 0x16CC0078, 0x16D00071, 0x16F00078, 0x17000006, 0x17010126, 0x17030006, 0x170500E0, - 0x17060126, 0x17070006, 0x170C0078, 0x170E0126, 0x170F0078, 0x17400054, 0x174D0078, 0x174E0054, - 0x177A0078, 0x17801054, 0x17EB0078, 0x17F80054, 0x17FE0078, 0x18008805, 0x18010006, 0x18020054, - 0x18030071, 0x18040009, 0x18090054, 0x180A0009, 0x180E0099, 0x180F00BF, 0x18100054, 0x18110127, - 0x18120128, 0x18130129, 0x1814012A, 0x18150083, 0x18180099, 0x18190081, 0x181B1054, 0x181C112B, - 0x181D112C, 0x181E0071, 0x181F0054, 0x18200078, 0x18210071, 0x18260871, 0x18320071, 0x18380871, - 0x18390071, 0x183A0871, 0x183C0071, 0x183D0871, 0x183F0071, 0x184A0871, 0x184B0071, 0x184C0078, - 0x184D0083, 0x184E1037, 0x184F0881, 0x18500099, 0x18510071, 0x18560871, 0x18620071, 0x18680871, - 0x18690071, 0x186A0871, 0x186C0071, 0x186D0871, 0x186F0071, 0x187A0871, 0x187B0071, 0x187C0871, - 0x187E0081, 0x187F0881, 0x18800078, 0x18830071, 0x18970078, 0x18991071, 0x18C80094, 0x18C978AD, - 0x18CA78AE, 0x18CB7894, 0x18D00071, 0x18DC0078, 0x18E00054, 0x18E80078, 0x18F80071, 0x19001094, - 0x190F1054, 0x191010AD, 0x191110AE, 0x1912112D, 0x1913112E, 0x1914112F, 0x19151094, 0x19220078, - 0x19286854, 0x19291930, 0x192A1931, 0x192B1932, 0x192C1933, 0x192D1934, 0x192E1935, 0x192F1936, - 0x19301894, 0x193E1854, 0x194018AD, 0x194118AE, 0x1942192D, 0x1943192E, 0x1944192F, 0x19451894, - 0x19591937, 0x195A1938, 0x195B1939, 0x195C193A, 0x195D193B, 0x195E193C, 0x195F193D, 0x19601094, - 0x19666854, 0x19681894, 0x19806894, 0x19AC1094, 0x19B96894, 0x19BC6854, 0x19BE6894, 0x19EF6854, - 0x19F01094, 0x1A000071, 0x26DB0078, 0x26E00054, 0x27000071, 0x4FDE0078, 0x50000071, 0x52470078, - 0x52480054, 0x52640078, 0x53800037, 0x538C0078, 0x54000071, 0x540100C8, 0x54020071, 0x54030083, - 0x54040071, 0x541200A7, 0x54130083, 0x54140054, 0x54160078, 0x56000071, 0x6BD20078, 0x6C00013E, - 0x7000013F, 0x7C800871, 0x7D070071, 0x7D080871, 0x7D0A0071, 0x7D0B0871, 0x7D120071, 0x7D130871, - 0x7D140071, 0x7D150871, 0x7D170078, 0x7D180871, 0x7D360078, 0x7D380871, 0x7D6D0078, 0x7D801055, - 0x7D840078, 0x7D8A1055, 0x7D8C0078, 0x7D8F0083, 0x7D90289B, 0x7D95089B, 0x7DA10078, 0x7DA2089B, - 0x7DA8409E, 0x7DAA389E, 0x7DAB409E, 0x7DAC389E, 0x7DAD409E, 0x7DAE389E, 0x7DAF409E, 0x7DB0389E, - 0x7DB1409E, 0x7DB2389E, 0x7DB3409E, 0x7DB4389E, 0x7DB5409E, 0x7DB6389E, 0x7DB7409E, 0x7DB8389E, - 0x7DB9409E, 0x7DBA389E, 0x7DBB409E, 0x7DBC389E, 0x7DBD409E, 0x7DBE389E, 0x7DBF409E, 0x7DC0389E, - 0x7DC1409E, 0x7DC8389E, 0x7DC9409E, 0x7DCA389E, 0x7DCB409E, 0x7DCC389E, 0x7DCD409E, 0x7DCE389E, - 0x7DCF409E, 0x7DD1389E, 0x7DD2409E, 0x7DD4389E, 0x7DD5409E, 0x7DD6389E, 0x7DD7409E, 0x7DD90078, - 0x7DEA209E, 0x7DEB489E, 0x7DEC209E, 0x7DEF409E, 0x7DF3389E, 0x7DF5409E, 0x7DFC389E, 0x7DFD209E, - 0x7DFE409E, 0x7DFF389E, 0x7E00409E, 0x7E32209E, 0x7E4C389E, 0x7E70489E, 0x7E7B409E, 0x7E89209E, - 0x7E97389E, 0x7E9A489E, 0x7E9E209E, 0x7E9F00B4, 0x7EA00078, 0x7EA8389E, 0x7EAC209E, 0x7EAE389E, - 0x7EAF209E, 0x7EB0389E, 0x7EB1209E, 0x7EB4389E, 0x7EB5209E, 0x7EB8389E, 0x7EBA209E, 0x7EC3389E, - 0x7EC80078, 0x7EC9389E, 0x7ECB209E, 0x7ECC389E, 0x7ECD209E, 0x7EDA389E, 0x7EDB209E, 0x7EDC389E, - 0x7EDE209E, 0x7EE2389E, 0x7EE3209E, 0x7EE40078, 0x7EF8409E, 0x7EFE4140, 0x7EFF0078, 0x7F000083, - 0x7F088006, 0x7F0C80BF, 0x7F0D0078, 0x7F100083, 0x7F120078, 0x7F188006, 0x7F198099, 0x7F1A8038, - 0x7F1B80BF, 0x7F230006, 0x7F2480BF, 0x7F251006, 0x7F271038, 0x7F28600C, 0x7F2A6006, 0x7F2C6099, - 0x7F2D60BF, 0x7F306006, 0x7F31600B, 0x7F326019, 0x7F346006, 0x7F356007, 0x7F360078, 0x7F38409E, - 0x7F41209E, 0x7F46489E, 0x7F47209E, 0x7F49489E, 0x7F4A209E, 0x7F4C489E, 0x7F4D209E, 0x7F4E489E, - 0x7F4F209E, 0x7F50489E, 0x7F51209E, 0x7F52489E, 0x7F53209E, 0x7F54489E, 0x7F55209E, 0x7F5A489E, - 0x7F5B209E, 0x7F5C489E, 0x7F5D209E, 0x7F5E489E, 0x7F5F209E, 0x7F60489E, 0x7F61209E, 0x7F62489E, - 0x7F63209E, 0x7F64489E, 0x7F65209E, 0x7F66489E, 0x7F67209E, 0x7F68489E, 0x7F69209E, 0x7F6A489E, - 0x7F6B209E, 0x7F6C489E, 0x7F6D209E, 0x7F6E489E, 0x7F6F209E, 0x7F70489E, 0x7F71209E, 0x7F72489E, - 0x7F73209E, 0x7F74489E, 0x7F75209E, 0x7F76489E, 0x7F77209E, 0x7F7A489E, 0x7F7B209E, 0x7F7F0078, - 0x7F818806, 0x7F828808, 0x7F838806, 0x7F848809, 0x7F858806, 0x7F86880C, 0x7F88880E, 0x7F898810, - 0x7F8A8812, 0x7F8B8814, 0x7F8C8816, 0x7F8D880C, 0x7F8E8818, 0x7F8F881A, 0x7F908806, 0x7F918860, - 0x7F9E8806, 0x7F9F8837, 0x7FA18861, 0x7FAE8819, 0x7FB0880A, 0x7FB15009, 0x7FB25006, 0x7FB35071, - 0x7FB85081, 0x7FB95071, 0x7FCF5081, 0x7FD05071, 0x7FE00078, 0x7FE15071, 0x7FE40078, 0x7FE55071, - 0x7FE80078, 0x7FE95071, 0x7FEC0078, 0x7FED5071, 0x7FEF0078, 0x7FF08808, 0x7FF18819, 0x7FF28854, - 0x7FF38808, 0x7FF45054, 0x7FF55019, 0x7FF75054, 0x7FF80078, 0x7FFD0141, 0x7FFE0054, 0x7FFF0078, - 0x80000071, 0x80060078, 0x80070071, 0x801F0078, 0x80200071, 0x80270078, 0x80280071, 0x802F0078, - 0x80400071, 0x807E0078, 0x80800097, 0x80810094, 0x80820078, 0x808400B6, 0x808500B7, 0x808600B8, - 0x808700B9, 0x808800B0, 0x808900BA, 0x808A00BB, 0x808B00BC, 0x808C00BD, 0x808D0142, 0x808E0143, - 0x808F0144, 0x80900145, 0x809100B1, 0x80920146, 0x80930147, 0x80940148, 0x80950149, 0x8096014A, - 0x8097014B, 0x8098014C, 0x8099014D, 0x809A0078, 0x809C0094, 0x80A0014E, 0x80A1014F, 0x80A20150, - 0x80A30151, 0x80A40152, 0x80A50150, 0x80A60153, 0x80A70151, 0x80A80154, 0x80A90155, 0x80AA0156, - 0x80AB0157, 0x80AC014F, 0x80AE0158, 0x80B00154, 0x80B30150, 0x80B50155, 0x80B60153, 0x80B90151, - 0x80BA0150, 0x80BB005F, 0x80BD0054, 0x80C500C3, 0x80C60078, 0x81800071, 0x819000AD, 0x819100B0, - 0x81920078, 0x81980071, 0x81A50159, 0x81A60078, 0x81C00071, 0x81CF0078, 0x81D00071, 0x81E20078, - 0x81E40071, 0x81E80094, 0x81E90158, 0x81EA015A, 0x81EB0078, 0x8200015B, 0x8214015C, 0x82280071, - 0x824F0078, 0x825000A8, 0x825100A9, 0x825200AA, 0x825300AB, 0x825400AC, 0x82550078, 0x8400009B, - 0x84030078, 0x8404009B, 0x841B0078, 0x841C009B, 0x841D0078, 0x841E009B, 0x841F0078, 0x8500009B, - 0x85010083, 0x85020078, 0x85030083, 0x85040078, 0x85060083, 0x8508009B, 0x850A0078, 0x850B009B, - 0x850C0078, 0x850D009B, 0x851A0078, 0x851C0083, 0x851E0078, 0x8520015D, 0x8521015E, 0x8522015F, - 0x85230160, 0x85240078, 0x8528009A, 0x852D0078, 0xE8000094, 0xE87B0078, 0xE8800094, 0xE8940078, - 0xE8950094, 0xE8AF0894, 0xE8B300A7, 0xE8B40083, 0xE8B50094, 0xE8B700A7, 0xE8BA0057, 0xE8BE0083, - 0xE8C20094, 0xE8C30083, 0xE8C60094, 0xE8D50083, 0xE8D70094, 0xE8DE0894, 0xE8E10094, 0xE8EF0078, - 0xE9000054, 0xE9210083, 0xE9230078, 0xE9800054, 0xE9AC0078, 0xEA002877, 0xEA0D2855, 0xEA1A2877, - 0xEA272855, 0xEA342877, 0xEA412855, 0xEA4E2877, 0xEA500078, 0xEA512877, 0xEA520078, 0xEA532877, - 0xEA540078, 0xEA552877, 0xEA5B2855, 0xEA5D0078, 0xEA5F2855, 0xEA620078, 0xEA632855, 0xEA682877, - 0xEA752855, 0xEA822877, 0xEA830078, 0xEA842877, 0xEA860078, 0xEA872877, 0xEA8F2855, 0xEA9C2877, - 0xEA9D0078, 0xEA9E2877, 0xEAA40078, 0xEAA52877, 0xEAA92855, 0xEAB62877, 0xEAC32855, 0xEAD02877, - 0xEADD2855, 0xEAEA2877, 0xEAF72855, 0xEB042877, 0xEB112855, 0xEB1E2877, 0xEB2B2855, 0xEB382877, - 0xEB452855, 0xEB530078, 0xEB542877, 0xEB612855, 0xEB712877, 0xEB7E2855, 0xEB8E2877, 0xEB9B2855, - 0xEBAB2877, 0xEBB82855, 0xEBC82877, 0xEBD52855, 0xEBE50078, 0xEBE7280E, 0xEBE82810, 0xEBE92812, - 0xEBEA2814, 0xEBEB2816, 0xEBEC280E, 0xEBED2810, 0xEBEE2812, 0xEBEF2814, 0xEBF02816, 0xEBF1280E, - 0xEBF22810, 0xEBF32812, 0xEBF42814, 0xEBF52816, 0xEBF6280E, 0xEBF72810, 0xEBF82812, 0xEBF92814, - 0xEBFA2816, 0xEBFB280E, 0xEBFC2810, 0xEBFD2812, 0xEBFE2814, 0xEBFF2816, 0xEC000078 - }; - - static const uint32_t a1[] = { - 0x00000071, 0x536C0078, 0x7C000871, 0x7D0F0078 - }; - - static const uint32_t a7[] = { - 0x00100057, 0x00400078, 0x00800083, 0x00F80078, 0x8000013F, 0xFFFF0078 - }; - - static const uint32_t a8[] = { - 0x0000013F, 0x7FFF0078 - }; - - static const uint32_t a16[] = { - 0x00800865, 0x00880065, 0x00890865, 0x00930065, 0x00940865, 0x00980161, 0x00991065, 0x009A0865, - 0x009C0863, 0x009F1063, 0x00A00063, 0x00A10863, 0x00A41055, 0x00A50065, 0x00A60865, 0x00A90065, - 0x00AA0865, 0x00B30065, 0x00B40865, 0x00BC0863, 0x00BF1162, 0x00C00163, 0x00C10065, 0x00C30063, - 0x00C40068, 0x00C50063, 0x00C60055, 0x00C70164, 0x00C80063, 0x00C90068, 0x00CA0165, 0x00CB0166, - 0x00CC0065, 0x00CD0055, 0x00CE0167, 0x00CF0168, 0x00D00865, 0x00D10065, 0x00D30063, 0x00D4006F, - 0x00D50055, 0x00D60065, 0x00D70863, 0x00D80070, 0x00D90063, 0x00DB0169, 0x00DC0065, 0x00DD0071, - 0x00DE0065, 0x00DF016A, 0x00E00071, 0x00E21074, 0x00E31072, 0x00E41073, 0x00E51074, 0x00E60863, - 0x00EE016B, 0x00EF0865, 0x00F20065, 0x00F30865, 0x00F81072, 0x00F91073, 0x00FA0865, 0x00FB016C, - 0x00FC0865, 0x010E0065, 0x010F0865, 0x01100055, 0x01110065, 0x01130865, 0x011A0055, 0x011D0063, - 0x011E016D, 0x011F0055, 0x0120016E, 0x01210078, 0x01280055, 0x0129016F, 0x012A0055, 0x012B007A, - 0x012C0170, 0x012D0171, 0x012E0055, 0x01310172, 0x01320055, 0x01340173, 0x01350055, 0x01370173, - 0x01380055, 0x013A0174, 0x013B0055, 0x0141007D, 0x01420055, 0x0145007E, 0x01460055, 0x01587881, - 0x015C0082, 0x015D0081, 0x01610037, 0x01630082, 0x01680081, 0x01690037, 0x016C1037, 0x016F0037, - 0x01707881, 0x01720037, 0x01800083, 0x01A00883, 0x01A20175, 0x01A30083, 0x01B80078, 0x01BA0037, - 0x01BB0078, 0x01C20837, 0x01C30806, 0x01C40885, 0x01C50078, 0x01C70887, 0x01C80060, 0x01D50860, - 0x01D60889, 0x01D80061, 0x01E50861, 0x01E6088C, 0x01E70078, 0x01E81176, 0x01E90877, 0x01EA1177, - 0x01EB0055, 0x01EC0065, 0x01F81093, 0x01F90055, 0x01FA1178, 0x01FB0063, 0x01FC10D8, 0x01FD0065, - 0x01FE0077, 0x02000892, 0x02020092, 0x02030892, 0x02040092, 0x02060892, 0x02070092, 0x02080060, - 0x020C0860, 0x020D0060, 0x02180061, 0x021C0861, 0x021D0061, 0x02280893, 0x022A0093, 0x022B0893, - 0x022C0093, 0x022E0893, 0x022F0093, 0x02300065, 0x023B0865, 0x023C0065, 0x02410083, 0x02430078, - 0x02440095, 0x02450065, 0x02600863, 0x02610063, 0x02670078, 0x02680865, 0x026A0065, 0x026B0865, - 0x026C0065, 0x026D0865, 0x02700065, 0x02710865, 0x02740065, 0x02750865, 0x027B0065, 0x027C0865, - 0x027D0078, 0x02800065, 0x02880078, 0x02980096, 0x02AB0078, 0x02AC0081, 0x02AD0097, 0x02B00098, - 0x02C31055, 0x02C40097, 0x02C50078, 0x02C80083, 0x02E1009A, 0x02E20083, 0x02E40078, 0x02E8009B, - 0x02F50078, 0x02F8009B, 0x02F9009A, 0x02FA0078, 0x0300009C, 0x03020078, 0x03050140, 0x0306009D, - 0x03070054, 0x03080083, 0x030B0078, 0x030D009D, 0x030E0078, 0x030F009D, 0x0310009E, 0x0311089E, - 0x0313009E, 0x031D0078, 0x0320009E, 0x03250083, 0x032F0078, 0x03300179, 0x0331017A, 0x0332017B, - 0x0333017C, 0x0334017D, 0x033500A5, 0x0336009D, 0x0337009E, 0x033A109E, 0x033C009E, 0x0369089E, - 0x036A009E, 0x036B0083, 0x036E009C, 0x036F0083, 0x0372009F, 0x03730083, 0x03740054, 0x03750083, - 0x0377009E, 0x0378000F, 0x03790011, 0x037A0013, 0x037B0015, 0x037C0017, 0x037D009E, 0x037E00A6, - 0x037F009E, 0x0380009D, 0x03870057, 0x03880083, 0x0389009E, 0x03980083, 0x03A50078, 0x03A6009E, - 0x03B70078, 0x03C0009E, 0x03D30083, 0x03D8009E, 0x03D90078, 0x04800083, 0x048100A7, 0x04820071, - 0x04940871, 0x04950071, 0x04980871, 0x04990071, 0x049D0078, 0x049E0071, 0x049F00A7, 0x04A00083, - 0x04A400A7, 0x04A60083, 0x04A70078, 0x04A80083, 0x04AA0078, 0x04AC0871, 0x04B00071, 0x04B10083, - 0x04B20097, 0x04B3017E, 0x04B4017F, 0x04B50180, 0x04B60181, 0x04B70182, 0x04B80078, 0x04BE0071, - 0x04BF0078, 0x04C00083, 0x04C100A7, 0x04C20071, 0x04C60078, 0x04C70071, 0x04C80078, 0x04C90071, - 0x04D40078, 0x04D50071, 0x04D80078, 0x04DB0071, 0x04DD0078, 0x04DE0071, 0x04DF00A7, 0x04E00083, - 0x04E20078, 0x04E300A7, 0x04E40078, 0x04E508A7, 0x04E60083, 0x04E70078, 0x04EB00A7, 0x04EC0078, - 0x04EE0871, 0x04F00071, 0x04F10083, 0x04F20078, 0x04F3017E, 0x04F4017F, 0x04F50180, 0x04F60181, - 0x04F70182, 0x04F80071, 0x04F90008, 0x04FA00B6, 0x04FB00B7, 0x04FC0183, 0x04FD0078, 0x05000083, - 0x050100A7, 0x05020071, 0x05050078, 0x05070071, 0x05080078, 0x05090071, 0x05140078, 0x05150071, - 0x05180078, 0x05190871, 0x051A0071, 0x051B0078, 0x051C0071, 0x051D0078, 0x051F00A7, 0x05200083, - 0x05210078, 0x05230083, 0x05240078, 0x05250083, 0x05270078, 0x052C0871, 0x052E0078, 0x0533017E, - 0x0534017F, 0x05350180, 0x05360181, 0x05370182, 0x05380083, 0x05390071, 0x053A0078, 0x05400083, - 0x054100A7, 0x05420071, 0x05540078, 0x05550071, 0x05580078, 0x05590071, 0x055D0078, 0x055E0071, - 0x055F00A7, 0x05600083, 0x056400A7, 0x05660083, 0x05670078, 0x05700071, 0x05710083, 0x05720078, - 0x0573017E, 0x0574017F, 0x05750180, 0x05760181, 0x05770182, 0x05780008, 0x05790078, 0x05800083, - 0x058100A7, 0x05820071, 0x05860078, 0x05870071, 0x05880078, 0x05890071, 0x05940078, 0x05950071, - 0x05980078, 0x05990071, 0x059D0078, 0x059E0071, 0x059F0083, 0x05A20078, 0x05A300A7, 0x05A40078, - 0x05A508A7, 0x05A60083, 0x05A70078, 0x05AB00A7, 0x05AC0078, 0x05AE0871, 0x05AF0071, 0x05B10078, - 0x05B3017E, 0x05B4017F, 0x05B50180, 0x05B60181, 0x05B70182, 0x05B80071, 0x05B90078, 0x05C10071, - 0x05C50078, 0x05C70071, 0x05C80078, 0x05C90071, 0x05CB0078, 0x05CC0071, 0x05CD0078, 0x05CF0071, - 0x05D00078, 0x05D10071, 0x05D20078, 0x05D40071, 0x05D50078, 0x05D70071, 0x05DD0078, 0x05DF00A7, - 0x05E10078, 0x05E300A7, 0x05E40078, 0x05E508A7, 0x05E60083, 0x05E70078, 0x05EB00A7, 0x05EC0078, - 0x05F3017E, 0x05F4017F, 0x05F50180, 0x05F60181, 0x05F70182, 0x05F80184, 0x05F90054, 0x05FC0008, - 0x05FD0078, 0x060000A7, 0x06020071, 0x06060078, 0x06070071, 0x06080078, 0x06090071, 0x06140078, - 0x06150071, 0x061D0078, 0x061F0083, 0x062000A7, 0x06220078, 0x06230083, 0x06240078, 0x06250083, - 0x06270078, 0x062A0083, 0x062B0078, 0x06300071, 0x06310078, 0x0633017E, 0x0634017F, 0x06350180, - 0x06360181, 0x06370182, 0x06380078, 0x064100A7, 0x06420071, 0x06460078, 0x06470071, 0x06480078, - 0x06490071, 0x06540078, 0x06550071, 0x065D0078, 0x065E0071, 0x065F00B2, 0x066000A7, 0x06620078, - 0x066308A7, 0x06640078, 0x066508A7, 0x06660083, 0x06670078, 0x066A00A7, 0x066B0078, 0x06700071, - 0x06710078, 0x0673017E, 0x0674017F, 0x06750180, 0x06760181, 0x06770182, 0x06780078, 0x068100A7, - 0x06820071, 0x06860078, 0x06870071, 0x06880078, 0x06890071, 0x06940078, 0x06950071, 0x069D0078, - 0x069F00A7, 0x06A00083, 0x06A20078, 0x06A300A7, 0x06A40078, 0x06A508A7, 0x06A60083, 0x06A70078, - 0x06AB00A7, 0x06AC0078, 0x06B00071, 0x06B10078, 0x06B3017E, 0x06B4017F, 0x06B50180, 0x06B60181, - 0x06B70182, 0x06B80078, 0x06C100A7, 0x06C20071, 0x06CB0078, 0x06CD0071, 0x06DF0078, 0x06E00071, - 0x06E30078, 0x06E700A7, 0x06E90083, 0x06EA0078, 0x06EC00A7, 0x06EE08A7, 0x06EF00A7, 0x06F00078, - 0x06F900A7, 0x06FA0078, 0x07000071, 0x07180083, 0x07191071, 0x071A0083, 0x071D0078, 0x071F0008, - 0x07200071, 0x07230083, 0x07270097, 0x0728017E, 0x0729017F, 0x072A0180, 0x072B0181, 0x072C0182, - 0x072D0097, 0x072E0078, 0x07400071, 0x07410078, 0x07430071, 0x07440078, 0x07460071, 0x07470078, - 0x074A0071, 0x07540078, 0x07550071, 0x07580083, 0x07591071, 0x075A0083, 0x075E0071, 0x075F0078, - 0x07600071, 0x07620078, 0x07640083, 0x07670078, 0x0768017E, 0x0769017F, 0x076A0180, 0x076B0181, - 0x076C0182, 0x076D0078, 0x076E1071, 0x076F0078, 0x07800094, 0x07820097, 0x07890094, 0x078C0083, - 0x078D0094, 0x0790017E, 0x0791017F, 0x07920180, 0x07930181, 0x07940182, 0x079500B3, 0x079A0083, - 0x079D00BF, 0x079F00A7, 0x07A00071, 0x07A10871, 0x07A20071, 0x07A60871, 0x07A70071, 0x07AB0871, - 0x07AC0071, 0x07B40871, 0x07B50078, 0x07B80083, 0x07B90883, 0x07BB1083, 0x07BD0083, 0x07BF00A7, - 0x07C00883, 0x07C10083, 0x07C20097, 0x07C30083, 0x07C40071, 0x07C60078, 0x07C80083, 0x07C90883, - 0x07CA0083, 0x07CE0883, 0x07CF0083, 0x07D30883, 0x07D40083, 0x07DC0883, 0x07DD0083, 0x07DE0078, - 0x07DF0094, 0x07E60078, 0x07E70094, 0x07E80097, 0x07E90078, 0x08000071, 0x08150078, 0x08160083, - 0x081800A7, 0x08190078, 0x081B0083, 0x081D0078, 0x0820017E, 0x0821017F, 0x08220180, 0x08230181, - 0x08240182, 0x08250097, 0x08280071, 0x082B00A7, 0x082C0083, 0x082D0078, 0x085000B5, 0x08630078, - 0x08680071, 0x087D0097, 0x087E0078, 0x08800071, 0x08AD0078, 0x08AF0071, 0x08D10078, 0x08D40071, - 0x08FD0078, 0x09000071, 0x09240078, 0x09250071, 0x09270078, 0x09280071, 0x092B0078, 0x092D0071, - 0x092F0078, 0x09300071, 0x09440078, 0x09450071, 0x09470078, 0x09480071, 0x09580078, 0x09590071, - 0x095B0078, 0x095C0071, 0x095F0078, 0x09610071, 0x09630078, 0x09640071, 0x096B0078, 0x096C0071, - 0x09880078, 0x09890071, 0x098B0078, 0x098C0071, 0x09AD0078, 0x09AF0083, 0x09B00097, 0x09B400AD, - 0x09B500AE, 0x09B6012D, 0x09B7012E, 0x09B8012F, 0x09B90185, 0x09BA0186, 0x09BB0187, 0x09BC0188, - 0x09BD0184, 0x09BE0078, 0x09C00071, 0x09C80054, 0x09CD0078, 0x09D00071, 0x09FA0078, 0x0A000071, - 0x0B360097, 0x0B370071, 0x0B3B0078, 0x0B400071, 0x0B4D00B4, 0x0B4E0078, 0x0B500071, 0x0B750097, - 0x0B770189, 0x0B780078, 0x0B800071, 0x0B860078, 0x0B870071, 0x0B890083, 0x0B8A0078, 0x0B900071, - 0x0B990083, 0x0B9A0097, 0x0B9B0078, 0x0BA00071, 0x0BA90083, 0x0BAA0078, 0x0BB00071, 0x0BB60078, - 0x0BB70071, 0x0BB80078, 0x0BB90083, 0x0BBA0078, 0x0BC00071, 0x0BDA00C2, 0x0BDB0083, 0x0BDF00A7, - 0x0BE40083, 0x0BEA0097, 0x0BEB0081, 0x0BEC0097, 0x0BED0008, 0x0BEE0083, 0x0BEF0078, 0x0BF0017E, - 0x0BF1017F, 0x0BF20180, 0x0BF30181, 0x0BF40182, 0x0BF50078, 0x0BF80106, 0x0BF90107, 0x0BFA0108, - 0x0BFB0109, 0x0BFC010A, 0x0BFD0078, 0x0C000006, 0x0C050083, 0x0C070078, 0x0C08017E, 0x0C09017F, - 0x0C0A0180, 0x0C0B0181, 0x0C0C0182, 0x0C0D0078, 0x0C100071, 0x0C210081, 0x0C220071, 0x0C3C0078, - 0x0C400071, 0x0C540083, 0x0C550078, 0x0C800071, 0x0C8E0078, 0x0C900083, 0x0C9100A7, 0x0C930083, - 0x0C9400C8, 0x0C960078, 0x0C9800A7, 0x0C9C0083, 0x0C9E0078, 0x0CA20006, 0x0CA3017E, 0x0CA4017F, - 0x0CA50180, 0x0CA60181, 0x0CA70182, 0x0CA80071, 0x0CB70078, 0x0CB80071, 0x0CBA0078, 0x0CC00071, - 0x0CD50078, 0x0CD800A7, 0x0CE00071, 0x0CE400A7, 0x0CE50078, 0x0CE8017E, 0x0CE9017F, 0x0CEA0180, - 0x0CEB0181, 0x0CEC0182, 0x0CED0078, 0x0CEF0006, 0x0CF00054, 0x0D000071, 0x0D0B0083, 0x0D0C00A7, - 0x0D0E0078, 0x0D0F0097, 0x0D100078, 0x0E800055, 0x0E967881, 0x0E970081, 0x0E987881, 0x0E9D0081, - 0x0E9E7881, 0x0EB17055, 0x0EB50055, 0x0ECD7881, 0x0EE00083, 0x0EE20078, 0x0F000865, 0x0F4B0855, - 0x0F4D098A, 0x0F4E0078, 0x0F500865, 0x0F7D0078, 0x0F8008C9, 0x0F8408CA, 0x0F8808C9, 0x0F8B0078, - 0x0F8C08CA, 0x0F8F0078, 0x0F9008C9, 0x0F9408CA, 0x0F9808C9, 0x0F9C08CA, 0x0FA008C9, 0x0FA30078, - 0x0FA408CA, 0x0FA70078, 0x0FA808C9, 0x0FAC08CA, 0x0FB008C9, 0x0FB408CA, 0x0FB808CB, 0x0FB908CC, - 0x0FBB08CD, 0x0FBC08CE, 0x0FBD08CF, 0x0FBE08D0, 0x0FBF0078, 0x0FC008C9, 0x0FC408D1, 0x0FC808C9, - 0x0FCC08D1, 0x0FD008C9, 0x0FD408D1, 0x0FD808C9, 0x0FD9098B, 0x0FDA0078, 0x0FDB0855, 0x0FDC08CA, - 0x0FDD08D2, 0x0FDE1037, 0x0FE00837, 0x0FE1098B, 0x0FE20078, 0x0FE30855, 0x0FE408D5, 0x0FE60837, - 0x0FE808C9, 0x0FE90855, 0x0FEA0078, 0x0FEB0855, 0x0FEC08CA, 0x0FED08D6, 0x0FEE0837, 0x0FF008C9, - 0x0FF10855, 0x0FF20890, 0x0FF30855, 0x0FF408CA, 0x0FF508D7, 0x0FF60837, 0x0FF80078, 0x0FF9098B, - 0x0FFA0078, 0x0FFB0855, 0x0FFC08D9, 0x0FFD08DA, 0x0FFE0837, 0x0FFF0078, 0x10000805, 0x10011005, - 0x10035805, 0x10041005, 0x10050057, 0x1007018C, 0x10085899, 0x10090099, 0x100B1006, 0x100C018D, - 0x100D00DB, 0x100E018D, 0x100F00DB, 0x10100006, 0x10121006, 0x10130006, 0x1014018E, 0x1015018F, - 0x10160190, 0x10175853, 0x10180007, 0x10191007, 0x101A0006, 0x101B1006, 0x101C0126, 0x101D0006, - 0x101F0038, 0x10200006, 0x10220009, 0x10231006, 0x10250006, 0x102B1006, 0x102C0006, 0x102F1005, - 0x10300057, 0x10320078, 0x10350057, 0x10387855, 0x10390078, 0x103A7910, 0x103B7911, 0x103C7912, - 0x103D780B, 0x103E7809, 0x103F7855, 0x1040705D, 0x1041705B, 0x10427110, 0x10437111, 0x10447112, - 0x1045700B, 0x10467009, 0x10470078, 0x10487081, 0x104A0078, 0x10500008, 0x105B0078, 0x10680083, - 0x106E0095, 0x10700083, 0x10710095, 0x10720083, 0x10760078, 0x10801054, 0x10831077, 0x10841054, - 0x10852877, 0x10872855, 0x10882877, 0x10892855, 0x108A2877, 0x108B0054, 0x108C2877, 0x108F0054, - 0x10901054, 0x10910054, 0x10950991, 0x10962877, 0x10972855, 0x10982877, 0x109A1071, 0x109C2855, - 0x109D1054, 0x109E2855, 0x109F2877, 0x10A00019, 0x10A22877, 0x10A32855, 0x10A50019, 0x10A60078, - 0x10A9305F, 0x10AF3106, 0x10B01192, 0x10B11193, 0x10B21194, 0x10B31195, 0x10B41196, 0x10B51197, - 0x10B61198, 0x10B71199, 0x10B8119A, 0x10B9119B, 0x10BA119C, 0x10BB119D, 0x10BC119E, 0x10BD119F, - 0x10BE11A0, 0x10BF11A1, 0x10C001A2, 0x10C101A3, 0x10C20078, 0x10C80019, 0x10CA0054, 0x10CD0819, - 0x10CE0054, 0x10D10019, 0x10D20054, 0x10E60854, 0x10E70819, 0x10E80054, 0x10FA0019, 0x110000E8, - 0x11020019, 0x110408FB, 0x110500FC, 0x11070019, 0x110800E8, 0x11090059, 0x110A01A4, 0x110B0019, - 0x110D00E8, 0x11110019, 0x111500E8, 0x111610E8, 0x111800E8, 0x111A0019, 0x111C00E8, 0x111E00FE, - 0x111F00E8, 0x112008E8, 0x112101A5, 0x112200E8, 0x112308E8, 0x112500E8, 0x11260019, 0x112900FE, - 0x112B0019, 0x112F00E8, 0x11300019, 0x113200FE, 0x11360819, 0x113708FE, 0x113900FE, 0x113A08FE, - 0x113B00FE, 0x113C08FE, 0x113D00FE, 0x114008FE, 0x114100FE, 0x114208FE, 0x114300FE, 0x114408FE, - 0x114500FE, 0x11460019, 0x114700FD, 0x11490019, 0x115100FE, 0x11520019, 0x115300E8, 0x115401A6, - 0x115608E8, 0x115800FE, 0x115C0019, 0x115F00E8, 0x11600019, 0x116400FD, 0x116601A7, 0x11670019, - 0x116800FE, 0x11690019, 0x116B00FE, 0x117008FE, 0x117200FE, 0x117508FE, 0x11770019, 0x117800FE, - 0x11790102, 0x117A00E8, 0x117B0103, 0x117C00E8, 0x117D0104, 0x117E0105, 0x117F00E8, 0x11800054, - 0x118400FE, 0x11860054, 0x119000E8, 0x11910054, 0x11940809, 0x11950054, 0x119B0094, 0x11BD0054, - 0x11CA0094, 0x11CB0054, 0x11CD0019, 0x11DA00BF, 0x11DB0054, 0x11EE0078, 0x12000054, 0x12130078, - 0x12200054, 0x12250078, 0x123018C4, 0x123118C5, 0x123218C6, 0x123318C7, 0x1234191F, 0x1235191A, - 0x1236191B, 0x1237191C, 0x1238191D, 0x1239191E, 0x123A10C4, 0x123B10C5, 0x123C10C6, 0x123D10C7, - 0x123E111F, 0x123F111A, 0x1240111B, 0x1241111C, 0x1242111D, 0x1243111E, 0x1244105A, 0x124510E3, - 0x124610E4, 0x124710E5, 0x124811A8, 0x124911A9, 0x124A11AA, 0x124B11AB, 0x124C11AC, 0x124D11AD, - 0x124E1094, 0x125B1918, 0x12681919, 0x1275010B, 0x1276010C, 0x1277010D, 0x1278010E, 0x1279010F, - 0x127A0106, 0x127B0107, 0x127C0108, 0x127D0109, 0x127E010A, 0x127F00C3, 0x12800054, 0x12DB0019, - 0x12DC0054, 0x12E00019, 0x12E10054, 0x12FC0019, 0x13000054, 0x13370019, 0x13380054, 0x134E0078, - 0x13500054, 0x13590078, 0x13800054, 0x13820078, 0x13830054, 0x13850078, 0x13860054, 0x13A90078, - 0x13AC0054, 0x13AF0078, 0x13B00054, 0x13B4000A, 0x13BB00C4, 0x13BC00C5, 0x13BD00C6, 0x13BE00C7, - 0x13BF011F, 0x13C000C4, 0x13C100C5, 0x13C200C6, 0x13C300C7, 0x13C4011F, 0x13C500C4, 0x13C600C5, - 0x13C700C6, 0x13C800C7, 0x13C9011F, 0x13CA0078, 0x13CC0054, 0x13DF0078, 0x13E00019, 0x13E100FD, - 0x13E20009, 0x13E30078, 0x13E80019, 0x13E900E8, 0x13EA00FD, 0x13EB0019, 0x13EE00FD, 0x13EF0019, - 0x13F100FE, 0x13F3000A, 0x13F60078, 0x13F80019, 0x14000094, 0x14800019, 0x14C10009, 0x14C601AE, - 0x14C701AF, 0x14C80009, 0x14CC0019, 0x14CD00E8, 0x14D80019, 0x14E000FE, 0x14E100E8, 0x14E200FE, - 0x14E30019, 0x14E400E8, 0x14E50019, 0x14E700FD, 0x14E90019, 0x14EA00FE, 0x14EB0019, 0x14EC000A, - 0x14EE0019, 0x14F000E8, 0x14F30019, 0x14F400E8, 0x14F50019, 0x14FA01B0, 0x14FB00E8, 0x14FC00FE, - 0x14FD0019, 0x14FE000A, 0x14FF0019, 0x150500E8, 0x150E0019, 0x150F00E8, 0x15110019, 0x151400E8, - 0x151500FD, 0x15170019, 0x151A00FE, 0x151B0019, 0x151E00FE, 0x151F0019, 0x152B00E8, 0x152C0019, - 0x153200FE, 0x15330019, 0x153500E8, 0x15380019, 0x153900E8, 0x153A1019, 0x153B0019, 0x153C00FD, - 0x153D00E8, 0x153E00FD, 0x154200E8, 0x154500FD, 0x154600E8, 0x154800FD, 0x154E00E8, 0x155000FD, - 0x155100E8, 0x15520019, 0x155300FE, 0x155700FD, 0x155800E8, 0x155900FD, 0x155A00E8, 0x155D00FD, - 0x156300E8, 0x156600FD, 0x156B0019, 0x157101B1, 0x15730019, 0x157600FE, 0x15770019, 0x157900E8, - 0x157A0019, 0x157B00FD, 0x157D00E8, 0x157F0019, 0x15800054, 0x158A0078, 0x16000096, 0x16170078, - 0x16180098, 0x162F0078, 0x16400065, 0x16720054, 0x16750078, 0x167C0006, 0x167E005F, 0x167F0006, - 0x16800125, 0x16930078, 0x16980071, 0x16B30078, 0x16B77881, 0x16B80078, 0x16C00071, 0x16CB0078, - 0x16D00071, 0x16D30078, 0x16D40071, 0x16D70078, 0x16D80071, 0x16DB0078, 0x16DC0071, 0x16DF0078, - 0x16E00071, 0x16E30078, 0x16E40071, 0x16E70078, 0x16E80071, 0x16EB0078, 0x16EC0071, 0x16EF0078, - 0x17000006, 0x170100E0, 0x17030006, 0x17040126, 0x17050006, 0x170600E0, 0x17070006, 0x170B0099, - 0x170C0078, 0x170E00E0, 0x170F0078, 0x17400054, 0x174F1054, 0x17500054, 0x17791054, 0x177A0078, - 0x17801054, 0x17EB0078, 0x17F80054, 0x17FE0078, 0x18000006, 0x18020081, 0x180301B2, 0x1804000A, - 0x18090054, 0x180A000A, 0x180E00B4, 0x180F00BF, 0x181001B3, 0x181101B4, 0x181201B5, 0x181301B6, - 0x181401B7, 0x18150083, 0x18180081, 0x181B0054, 0x181C11B8, 0x181D0081, 0x181E0006, 0x181F0054, - 0x18200071, 0x18320871, 0x18350071, 0x18380871, 0x183A0071, 0x183B0871, 0x183D0071, 0x183E0871, - 0x183F0071, 0x184B0078, 0x184C0083, 0x184D1037, 0x184E0081, 0x184F8071, 0x18500071, 0x18620871, - 0x18650071, 0x18680871, 0x186A0071, 0x186B0871, 0x186D0071, 0x186E0871, 0x186F0071, 0x187B0871, - 0x187D0006, 0x187E0081, 0x187F8071, 0x18800078, 0x18820071, 0x18960078, 0x18981071, 0x18C70078, - 0x18C80094, 0x18C978B6, 0x18CA78B7, 0x18CB7894, 0x18D00071, 0x18DC0078, 0x18E00054, 0x18E80078, - 0x18F80071, 0x19001094, 0x190E1054, 0x190F0078, 0x191010B6, 0x191110B7, 0x191210B8, 0x191310B9, - 0x191410B0, 0x19151094, 0x19220078, 0x192819B9, 0x192919BA, 0x192A19BB, 0x192B19BC, 0x192C19BD, - 0x192D19BE, 0x192E19BF, 0x192F19C0, 0x19301894, 0x193E1854, 0x193F0094, 0x194018B6, 0x194118B7, - 0x194218B8, 0x194318B9, 0x194418B0, 0x19451894, 0x195819C1, 0x195919C2, 0x195A19C3, 0x195B19C4, - 0x195C19C5, 0x195D19C6, 0x195E19C7, 0x195F19C8, 0x19601094, 0x19666854, 0x19681894, 0x197F0078, - 0x19806894, 0x19AC1094, 0x19B86894, 0x19BB6854, 0x19BD6894, 0x19EF6854, 0x19F01094, 0x19FF6854, - 0x1A000071, 0x26DB0078, 0x26E00054, 0x27000071, 0x4FDE0078, 0x50000071, 0x500A0081, 0x500B0071, - 0x52460078, 0x52480054, 0x52630078, 0x53800037, 0x538B0078, 0x54000071, 0x54050083, 0x54060071, - 0x541100A7, 0x54120083, 0x541300A7, 0x54140054, 0x54160078, 0x56000071, 0x6BD20078, 0x6C00013E, - 0x7000013F, 0x7C800871, 0x7D070071, 0x7D0A0871, 0x7D0F0071, 0x7D120871, 0x7D130071, 0x7D150871, - 0x7D170078, 0x7D180871, 0x7D350078, 0x7D380871, 0x7D6D0078, 0x7D801055, 0x7D830078, 0x7D891055, - 0x7D8C0078, 0x7D8E089B, 0x7D90289B, 0x7D94280B, 0x7D95089B, 0x7D9B0078, 0x7D9C089B, 0x7D9E0078, - 0x7DA0089B, 0x7DA20078, 0x7DA3089B, 0x7DA7109B, 0x7DA8209E, 0x7DAA489E, 0x7DAB209E, 0x7DAC489E, - 0x7DAD209E, 0x7DAE489E, 0x7DAF209E, 0x7DB0489E, 0x7DB1209E, 0x7DB2489E, 0x7DB3209E, 0x7DB4489E, - 0x7DB5209E, 0x7DB6489E, 0x7DB7209E, 0x7DB8489E, 0x7DB9209E, 0x7DBA489E, 0x7DBB209E, 0x7DBC489E, - 0x7DBD209E, 0x7DBE489E, 0x7DBF209E, 0x7DC0489E, 0x7DC1209E, 0x7DC8489E, 0x7DC9209E, 0x7DCA489E, - 0x7DCB209E, 0x7DCC489E, 0x7DCD209E, 0x7DCE489E, 0x7DCF209E, 0x7DD1489E, 0x7DD2209E, 0x7DD4489E, - 0x7DD5209E, 0x7DD6489E, 0x7DD7209E, 0x7DD90078, 0x7DE9409E, 0x7DEA389E, 0x7DEB409E, 0x7DEF209E, - 0x7DF3489E, 0x7DF5209E, 0x7DFC409E, 0x7DFD389E, 0x7DFE209E, 0x7DFF489E, 0x7E00409E, 0x7E32209E, - 0x7E4B389E, 0x7E6F489E, 0x7E7A409E, 0x7E88209E, 0x7E96389E, 0x7E9A489E, 0x7E9E409E, 0x7E9F00BF, - 0x7EA00078, 0x7EA8209E, 0x7EA9389E, 0x7EAD209E, 0x7EAE389E, 0x7EAF209E, 0x7EB0389E, 0x7EB3209E, - 0x7EB5389E, 0x7EB7209E, 0x7EB9389E, 0x7EBA209E, 0x7EBB389E, 0x7EBC209E, 0x7EBE389E, 0x7EBF209E, - 0x7EC1389E, 0x7EC2209E, 0x7EC4389E, 0x7EC5209E, 0x7EC6389E, 0x7EC80078, 0x7EC9389E, 0x7ECB209E, - 0x7ECE389E, 0x7ECF209E, 0x7EDA389E, 0x7EDB209E, 0x7EE1389E, 0x7EE3209E, 0x7EE40078, 0x7EF8409E, - 0x7EFE0054, 0x7EFF0078, 0x7F000083, 0x7F088006, 0x7F0B80B4, 0x7F0C8006, 0x7F0D0078, 0x7F100083, - 0x7F120078, 0x7F188099, 0x7F198038, 0x7F1A80B4, 0x7F220006, 0x7F2380B4, 0x7F241006, 0x7F261038, - 0x7F286006, 0x7F290078, 0x7F2A600C, 0x7F2B6006, 0x7F2C60B4, 0x7F2F6007, 0x7F306006, 0x7F31600D, - 0x7F326019, 0x7F330078, 0x7F346008, 0x7F356006, 0x7F360078, 0x7F38489E, 0x7F39009E, 0x7F3A0078, - 0x7F3B489E, 0x7F40409E, 0x7F45389E, 0x7F46409E, 0x7F48389E, 0x7F49409E, 0x7F4B389E, 0x7F4C409E, - 0x7F4D389E, 0x7F4E409E, 0x7F4F389E, 0x7F50409E, 0x7F51389E, 0x7F52409E, 0x7F53389E, 0x7F54409E, - 0x7F59389E, 0x7F5A409E, 0x7F5B389E, 0x7F5C409E, 0x7F5D389E, 0x7F5E409E, 0x7F5F389E, 0x7F60409E, - 0x7F61389E, 0x7F62409E, 0x7F63389E, 0x7F64409E, 0x7F65389E, 0x7F66409E, 0x7F67389E, 0x7F68409E, - 0x7F69389E, 0x7F6A409E, 0x7F6B389E, 0x7F6C409E, 0x7F6D389E, 0x7F6E409E, 0x7F6F389E, 0x7F70409E, - 0x7F71389E, 0x7F72409E, 0x7F73389E, 0x7F74409E, 0x7F75389E, 0x7F76409E, 0x7F79389E, 0x7F7A409E, - 0x7F7E0078, 0x7F7F0057, 0x7F808806, 0x7F818807, 0x7F838806, 0x7F84880A, 0x7F85880B, 0x7F86880D, - 0x7F87880C, 0x7F88880F, 0x7F898811, 0x7F8A8813, 0x7F8B8815, 0x7F8C8817, 0x7F8D8806, 0x7F8E8819, - 0x7F8F8806, 0x7F908860, 0x7F9D8835, 0x7F9E8836, 0x7F9F8838, 0x7FA08861, 0x7FAD8835, 0x7FAE8836, - 0x7FAF8809, 0x7FB05006, 0x7FB1500A, 0x7FB25006, 0x7FB35071, 0x7FCF5081, 0x7FD05071, 0x7FDF0078, - 0x7FE15071, 0x7FE40078, 0x7FE55071, 0x7FE80078, 0x7FE95071, 0x7FEC0078, 0x7FED5071, 0x7FEE0078, - 0x7FF08808, 0x7FF18837, 0x7FF28808, 0x7FF30078, 0x7FF45019, 0x7FF65054, 0x7FF70078, 0x7FFC0141, - 0x7FFE0054, 0x7FFF0078, 0x80000071, 0x80130078, 0x80140071, 0x801D0078, 0x801E0071, 0x80270078, - 0x80280071, 0x802F0078, 0x80400071, 0x807D0078, 0x80800006, 0x80810078, 0x808300AD, 0x808400AE, - 0x8085012D, 0x8086012E, 0x8087012F, 0x80880185, 0x80890186, 0x808A0187, 0x808B0188, 0x808C0184, - 0x808D01C9, 0x808E01CA, 0x808F01CB, 0x809001CC, 0x809101CD, 0x809201CE, 0x809301CF, 0x809401D0, - 0x809500BE, 0x809601D1, 0x809701D2, 0x809801D3, 0x809901D4, 0x809A0078, 0x809B0094, 0x80A0014E, - 0x80A10152, 0x80A20153, 0x80A30157, 0x80A40154, 0x80A50155, 0x80A60156, 0x80A70152, 0x80A80150, - 0x80A90153, 0x80AA01D5, 0x80AB0154, 0x80AC014F, 0x80AD0158, 0x80AF0152, 0x80B00154, 0x80B201D6, - 0x80B30150, 0x80B501D7, 0x80B60153, 0x80B80156, 0x80B90152, 0x80BA005F, 0x80BC0054, 0x80C50078, - 0x81800071, 0x818F0078, 0x8190012D, 0x819100BB, 0x81920078, 0x81980071, 0x81A50078, 0x81C00071, - 0x81CF0097, 0x81D00071, 0x81E20078, 0x81E40071, 0x81E8014F, 0x81E90154, 0x81EA0155, 0x81EB0078, - 0x8200015B, 0x8214015C, 0x82280071, 0x824F0078, 0x8250017E, 0x8251017F, 0x82520180, 0x82530181, - 0x82540182, 0x82550078, 0x8400009B, 0x84030078, 0x8405009B, 0x841C0078, 0x841F009B, 0x84200078, - 0x85000083, 0x85030078, 0x85060083, 0x8508009B, 0x851A0078, 0x851C0083, 0x851D0078, 0x851F0083, - 0x852001D8, 0x852101D9, 0x852201DA, 0x852301DB, 0x85240078, 0x8528009A, 0x852C0078, 0xE8000094, - 0xE87B0078, 0xE8800094, 0xE8930078, 0xE8950094, 0xE8AF0894, 0xE8B200A7, 0xE8B30083, 0xE8B50094, - 0xE8B600A7, 0xE8B90057, 0xE8BD0083, 0xE8C10094, 0xE8C20083, 0xE8C60094, 0xE8D50083, 0xE8D70094, - 0xE8DD0894, 0xE8E00094, 0xE8EF0078, 0xE9000054, 0xE9210083, 0xE9220054, 0xE9230078, 0xE9800054, - 0xE9AB0078, 0xEA002877, 0xEA0D2855, 0xEA1A2877, 0xEA272855, 0xEA2A0078, 0xEA2B2855, 0xEA342877, - 0xEA412855, 0xEA4E0078, 0xEA4F2877, 0xEA500078, 0xEA522877, 0xEA530078, 0xEA542877, 0xEA560078, - 0xEA572877, 0xEA5B2855, 0xEA682877, 0xEA752855, 0xEA822877, 0xEA850078, 0xEA862877, 0xEA8A0078, - 0xEA8B2877, 0xEA8E0078, 0xEA8F2855, 0xEA9C2877, 0xEA9F0078, 0xEAA02877, 0xEAA20078, 0xEAA52877, - 0xEAA80078, 0xEAA92855, 0xEAB62877, 0xEAC32855, 0xEAD02877, 0xEADD2855, 0xEAEA2877, 0xEAF72855, - 0xEB042877, 0xEB112855, 0xEB1E2877, 0xEB2B2855, 0xEB382877, 0xEB452855, 0xEB530078, 0xEB542877, - 0xEB6029DC, 0xEB612855, 0xEB6D29DC, 0xEB6E2855, 0xEB712877, 0xEB7D29DC, 0xEB7E2855, 0xEB8A29DC, - 0xEB8B2855, 0xEB8E2877, 0xEB9A29DC, 0xEB9B2855, 0xEBA729DC, 0xEBA82855, 0xEBAB2877, 0xEBB729DC, - 0xEBB82855, 0xEBC429DC, 0xEBC52855, 0xEBC82877, 0xEBD429DC, 0xEBD52855, 0xEBE129DC, 0xEBE22855, - 0xEBE50078, 0xEBE7280F, 0xEBE82811, 0xEBE92813, 0xEBEA2815, 0xEBEB2817, 0xEBEC280F, 0xEBED2811, - 0xEBEE2813, 0xEBEF2815, 0xEBF02817, 0xEBF1280F, 0xEBF22811, 0xEBF32813, 0xEBF42815, 0xEBF52817, - 0xEBF6280F, 0xEBF72811, 0xEBF82813, 0xEBF92815, 0xEBFA2817, 0xEBFB280F, 0xEBFC2811, 0xEBFD2813, - 0xEBFE2815, 0xEBFF2817, 0xEC000078 - }; - - static const uint32_t a17[] = { - 0x00000071, 0x536B0078, 0x7C000871, 0x7D0F0078 - }; - - static const uint32_t a23[] = { - 0x00000057, 0x00010078, 0x00100057, 0x00400078, 0x00800083, 0x00F80078, 0x8000013F, 0xFFFF0078 - }; - - static const uint32_t a24[] = { - 0x0000013F, 0x7FFF0078 - }; - - - // The full set of all arrays to be searched. - static const Range FULL_DATA[] = { - {sizeof(a0)/sizeof(uint32_t), a0}, - {sizeof(a1)/sizeof(uint32_t), a1}, - {0, 0}, - {0, 0}, - {0, 0}, - {0, 0}, - {0, 0}, - {sizeof(a7)/sizeof(uint32_t), a7}, - {sizeof(a8)/sizeof(uint32_t), a8}, - {0, 0}, - {0, 0}, - {0, 0}, - {0, 0}, - {0, 0}, - {0, 0}, - {0, 0}, - {sizeof(a16)/sizeof(uint32_t), a16}, - {sizeof(a17)/sizeof(uint32_t), a17}, - {0, 0}, - {0, 0}, - {0, 0}, - {0, 0}, - {0, 0}, - {sizeof(a23)/sizeof(uint32_t), a23}, - {sizeof(a24)/sizeof(uint32_t), a24}, - {0, 0}, - {0, 0}, - {0, 0}, - {0, 0}, - {0, 0}, - {0, 0}, - {0, 0} - }; - - // Array of mirrored character differences - static const short MIRROR_DIFF[] = { - 0, 1, -1, 2, -2, 16, -16, 3, - -3, 2016, 138, 1824, 2104, 2108, 2106, -138, - 8, 7, -8, -7, -1824, -2016, -2104, -2106, - -2108 - }; - - // All possible packed data values, no duplicates - static const uint32_t PACKED_DATA[] = { - 0x00000000, 0x0000012F, 0x0000016F, 0x0000014F, 0x0000018F, 0x0000018C, 0x000001B8, 0x000000B8, - 0x000000BA, 0x020005B5, 0x040005B6, 0x00000099, 0x000000F8, 0x00000094, 0x02000069, 0x04000069, - 0x06000069, 0x08000069, 0x0A000069, 0x0C000069, 0x0E000069, 0x10000069, 0x12000069, 0x14000069, - 0x060005B9, 0x000001B9, 0x080005B9, 0x16020001, 0x18020001, 0x1A020001, 0x1C020001, 0x1E020001, - 0x20020001, 0x22020001, 0x24020001, 0x26020001, 0x28020001, 0x2A020001, 0x2C020001, 0x2E020001, - 0x30020001, 0x32020001, 0x34020001, 0x36020001, 0x38020001, 0x3A020001, 0x3C020001, 0x3E020001, - 0x40020001, 0x42020001, 0x44020001, 0x46020001, 0x48020001, 0x060005B5, 0x080005B6, 0x000001BB, - 0x000001B7, 0x16000802, 0x18000802, 0x1A000802, 0x1C000802, 0x1E000802, 0x20000802, 0x22000802, - 0x24000802, 0x26000802, 0x28000802, 0x2A000802, 0x2C000802, 0x2E000802, 0x30000802, 0x32000802, - 0x34000802, 0x36000802, 0x38000802, 0x3A000802, 0x3C000802, 0x3E000802, 0x40000802, 0x42000802, - 0x44000802, 0x46000802, 0x48000802, 0x000000EC, 0x000001BC, 0x00000002, 0x0A0005BD, 0x00000130, - 0x000000BC, 0x000000B9, 0x0600006B, 0x0800006B, 0x00001002, 0x0400006B, 0x0C0005BE, 0x4A0001AB, - 0x00020001, 0x00000802, 0x00001802, 0x00040001, 0x00060001, 0x00002002, 0x00080001, 0x000C0001, - 0x000E0001, 0x00100001, 0x00140001, 0x00160001, 0x00180001, 0x00004002, 0x00004802, 0x00200001, - 0x00220001, 0x00000005, 0x00A60001, 0x01805802, 0x01042003, 0x00280001, 0x002C0001, 0x00000001, - 0x00000000, 0x00007002, 0x00007802, 0x00009802, 0x0000A802, 0x0000B802, 0x0000C002, 0x0000C802, - 0x0000D002, 0x00000004, 0x000001A4, 0x00000106, 0x00320001, 0x00340001, 0x00360001, 0x00380001, - 0x0000E002, 0x0000E802, 0x0000F002, 0x0000F802, 0x00010002, 0x00010802, 0x00012002, 0x00012802, - 0x00013802, 0x003A0001, 0x003E0001, 0x00013002, 0x0000001C, 0x00000107, 0x00400001, 0x00000018, - 0x00014802, 0x000001B4, 0x00000038, 0x00000025, 0x00000050, 0x00000058, 0x00000045, 0x00000044, - 0x020000C9, 0x060000C9, 0x0A0000C9, 0x0E0000C9, 0x120000C9, 0x000000D8, 0x0000005C, 0x00000008, - 0x02000009, 0x06000009, 0x0A000009, 0x0E000009, 0x12000009, 0x0400000B, 0x0800000B, 0x0000000B, - 0x1600000B, 0x4E00000B, 0x00000006, 0x4A00000B, 0x000001B5, 0x00420001, 0x0600000B, 0x0A00000B, - 0x0E00000B, 0x1200000B, 0x3E00000B, 0x5200000B, 0x5600000B, 0x5A00000B, 0x5C00000B, 0x000001B6, - 0x2400000A, 0x2800000A, 0x00000010, 0x020001AB, 0x060001AB, 0x0A0001AB, 0x0E0001AB, 0x120001AB, - 0x00000108, 0x00015802, 0x00440001, 0x00016002, 0x00016802, 0x00017002, 0x00017802, 0x00018002, - 0x00018802, 0x00440003, 0x00460001, 0x00480003, 0x00019802, 0x004A0001, 0x004C0001, 0x004E0001, - 0x003C0001, 0x00500001, 0x00520001, 0x000001BD, 0x0000018D, 0x000001D0, 0x00000250, 0x00000230, - 0x040005BE, 0x000000F9, 0x0200006B, 0x0A00006B, 0x0E00006B, 0x1200006B, 0x00540001, 0x00560001, - 0x000005B9, 0x045A000A, 0x085A000A, 0x0C5A000A, 0x105A000A, 0x145A000A, 0x185A000A, 0x525A000A, - 0x5E5A000A, 0x0401A00A, 0x0801A00A, 0x0C01A00A, 0x1001A00A, 0x1401A00A, 0x1801A00A, 0x5201A00A, - 0x5E01A00A, 0x4E00000A, 0x5C00000A, 0x0E0005B9, 0x100005B9, 0x020005B9, 0x040005B9, 0x160005B9, - 0x180005B9, 0x1A0005B9, 0x200005B9, 0x220005B9, 0x240005B9, 0x260005B9, 0x040001AB, 0x080001AB, - 0x0C0001AB, 0x100001AB, 0x140001AB, 0x180001AB, 0x1C0001AB, 0x200001AB, 0x240001AB, 0x280001AB, - 0x0C00006B, 0x1000006B, 0x1400006B, 0x1800006B, 0x1C00006B, 0x2000006B, 0x2400006B, 0x2800006B, - 0x005C001C, 0x0001A81C, 0x1A0001AB, 0x1E0001AB, 0x220001AB, 0x260001AB, 0x2A0001AB, 0x160001AB, - 0x020005B6, 0x100005B6, 0x280005B9, 0x2C0005B9, 0x300005B9, 0x0001B002, 0x020005BD, 0x0600000A, - 0x0A00000A, 0x0E00000A, 0x1200000A, 0x1600000A, 0x3E00000A, 0x0C00000B, 0x1000000B, 0x1400000B, - 0x2E0001AB, 0x320001AB, 0x360001AB, 0x3A0001AB, 0x3E0001AB, 0x420001AB, 0x460001AB, 0x640001AB, - 0x680001AB, 0x6A0001AB, 0x6E0001AB, 0x720001AB, 0x760001AB, 0x7A0001AB, 0x00000013, 0x00000012, - 0x0000005A, 0x000001B0, 0x7C00000B, 0x8000000B, 0x8200000B, 0x8600000B, 0x8C00000B, 0x6000000B, - 0x9200000B, 0x9600000B, 0x9800000B, 0x9C00000B, 0xA000000B, 0xA400000B, 0x4A0001AA, 0x040001AA, - 0x520001AA, 0x600001AA, 0x0C0001AA, 0x5E0001AA, 0x160001AA, 0x4C0001AA, 0x4E0001AA, 0x9E0001AA, - 0x060001AA, 0x8800000A, 0x2A0001AA, 0x005E0001, 0x0001B802, 0x0400002B, 0x0800002B, 0x1600002B, - 0x4C00002B, 0x00002802, 0x00003002, 0x000A0001, 0x00120001, 0x00003802, 0x001A0001, 0x001C0001, - 0x001E0001, 0x00240001, 0x00005002, 0x00006002, 0x002A0001, 0x002E0001, 0x00300001, 0x00006802, - 0x00008002, 0x00008802, 0x00009002, 0x0000A002, 0x0000B002, 0x0000D906, 0x00011002, 0x00011802, - 0x00014002, 0x040000C9, 0x080000C9, 0x0C0000C9, 0x100000C9, 0x140000C9, 0x04000009, 0x08000009, - 0x0C000009, 0x10000009, 0x14000009, 0x2200000B, 0x4C00000B, 0x2A00000B, 0x5000000B, 0x5400000B, - 0x5800000B, 0x2600000A, 0x00015002, 0x00019002, 0x00000030, 0x000001BE, 0x0000014E, 0x00000210, - 0x000001F0, 0x00580001, 0x065A000A, 0x0A5A000A, 0x0E5A000A, 0x125A000A, 0x165A000A, 0x1A5A000A, - 0x4C5A000A, 0x4E5A000A, 0x0601A00A, 0x0A01A00A, 0x0E01A00A, 0x1201A00A, 0x1601A00A, 0x1A01A00A, - 0x4C01A00A, 0x4E01A00A, 0x6000000A, 0x0000000A, 0x120005B9, 0x140005B9, 0x1C0005B9, 0x1E0005B9, - 0x1600006B, 0x1A00006B, 0x1E00006B, 0x2200006B, 0x2600006B, 0x2A00006B, 0x0E0005B5, 0x040005B5, - 0x2A0005B9, 0x2E0005B9, 0x0200000A, 0x0400000A, 0x0800000A, 0x0C00000A, 0x1000000A, 0x1400000A, - 0x2A00000A, 0x2C0001AB, 0x300001AB, 0x340001AB, 0x380001AB, 0x3C0001AB, 0x400001AB, 0x440001AB, - 0x480001AB, 0x620001AB, 0x660001AB, 0x500001AB, 0x6C0001AB, 0x700001AB, 0x740001AB, 0x780001AB, - 0x520001AB, 0x7E00000B, 0x5E00000B, 0x8400000B, 0x8800000B, 0x8A00000B, 0x8E00000B, 0x9000000B, - 0x9400000B, 0x9A00000B, 0x9E00000B, 0xA200000B, 0xA600000B, 0x5C0001AA, 0x3E0001AA, 0x7E0001AA, - 0x0600002B, 0x0A00002B, 0x2A00002B, 0x4E00002B, 0x00000019 - }; -} diff --git a/libs/utils/Unicode.cpp b/libs/utils/Unicode.cpp deleted file mode 100644 index 65dbb4a3e..000000000 --- a/libs/utils/Unicode.cpp +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include "CharacterData.h" - -#define LOG_TAG "Unicode" -#include - -// ICU headers for using macros -#include - -#define MIN_RADIX 2 -#define MAX_RADIX 36 - -#define TYPE_SHIFT 0 -#define TYPE_MASK ((1<<5)-1) - -#define DIRECTION_SHIFT (TYPE_SHIFT+5) -#define DIRECTION_MASK ((1<<5)-1) - -#define MIRRORED_SHIFT (DIRECTION_SHIFT+5) -#define MIRRORED_MASK ((1<<1)-1) - -#define TOUPPER_SHIFT (MIRRORED_SHIFT+1) -#define TOUPPER_MASK ((1<<6)-1) - -#define TOLOWER_SHIFT (TOUPPER_SHIFT+6) -#define TOLOWER_MASK ((1<<6)-1) - -#define TOTITLE_SHIFT (TOLOWER_SHIFT+6) -#define TOTITLE_MASK ((1<<2)-1) - -#define MIRROR_SHIFT (TOTITLE_SHIFT+2) -#define MIRROR_MASK ((1<<5)-1) - -#define NUMERIC_SHIFT (TOTITLE_SHIFT+2) -#define NUMERIC_MASK ((1<<7)-1) - -#define DECOMPOSITION_SHIFT (11) -#define DECOMPOSITION_MASK ((1<<5)-1) - -/* - * Returns the value stored in the CharacterData tables that contains - * an index into the packed data table and the decomposition type. - */ -static uint16_t findCharacterValue(UChar32 c) -{ - LOG_ASSERT(c >= 0 && c <= 0x10FFFF, "findCharacterValue received an invalid codepoint"); - if (c < 256) - return CharacterData::LATIN1_DATA[c]; - - // Rotate the bits because the tables are separated into even and odd codepoints - c = (c >> 1) | ((c & 1) << 20); - - CharacterData::Range search = CharacterData::FULL_DATA[c >> 16]; - const uint32_t* array = search.array; - - // This trick is so that that compare in the while loop does not - // need to shift the array entry down by 16 - c <<= 16; - c |= 0xFFFF; - - int high = (int)search.length - 1; - int low = 0; - - if (high < 0) - return 0; - - while (low < high - 1) - { - int probe = (high + low) >> 1; - - // The entries contain the codepoint in the high 16 bits and the index - // into PACKED_DATA in the low 16. - if (array[probe] > (unsigned)c) - high = probe; - else - low = probe; - } - - LOG_ASSERT((array[low] <= (unsigned)c), "A suitable range was not found"); - return array[low] & 0xFFFF; -} - -uint32_t android::Unicode::getPackedData(UChar32 c) -{ - // findCharacterValue returns a 16-bit value with the top 5 bits containing a decomposition type - // and the remaining bits containing an index. - return CharacterData::PACKED_DATA[findCharacterValue(c) & 0x7FF]; -} - -android::Unicode::Direction android::Unicode::getDirectionality(UChar32 c) -{ - uint32_t data = getPackedData(c); - - if (0 == data) - return DIRECTIONALITY_UNDEFINED; - - Direction d = (Direction) ((data >> DIRECTION_SHIFT) & DIRECTION_MASK); - - if (DIRECTION_MASK == d) - return DIRECTIONALITY_UNDEFINED; - - return d; -} - -bool android::Unicode::isMirrored(UChar32 c) -{ - return ((getPackedData(c) >> MIRRORED_SHIFT) & MIRRORED_MASK) != 0; -} - -UChar32 android::Unicode::toMirror(UChar32 c) -{ - if (!isMirrored(c)) - return c; - - return c + CharacterData::MIRROR_DIFF[(getPackedData(c) >> MIRROR_SHIFT) & MIRROR_MASK]; -} From 8f0251fd36eac0348f9321712436f972a6179715 Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Wed, 10 Feb 2010 14:59:26 -0800 Subject: [PATCH 182/541] use framework base64 implementation Change-Id: Ib1dc595e53d64901f81d06350c6ab9b138beff7e --- .../com/android/mmscommon/mms/pdu/Base64.java | 167 ------------------ .../android/mmscommon/mms/pdu/PduParser.java | 5 +- 2 files changed, 3 insertions(+), 169 deletions(-) delete mode 100644 mms-common/java/com/android/mmscommon/mms/pdu/Base64.java diff --git a/mms-common/java/com/android/mmscommon/mms/pdu/Base64.java b/mms-common/java/com/android/mmscommon/mms/pdu/Base64.java deleted file mode 100644 index 4c95dec72..000000000 --- a/mms-common/java/com/android/mmscommon/mms/pdu/Base64.java +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Copyright (C) 2007 Esmertec AG. - * 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. - */ - -package com.android.mmscommon.mms.pdu; - -public class Base64 { - /** - * Used to get the number of Quadruples. - */ - static final int FOURBYTE = 4; - - /** - * Byte used to pad output. - */ - static final byte PAD = (byte) '='; - - /** - * The base length. - */ - static final int BASELENGTH = 255; - - // Create arrays to hold the base64 characters - private static byte[] base64Alphabet = new byte[BASELENGTH]; - - // Populating the character arrays - static { - for (int i = 0; i < BASELENGTH; i++) { - base64Alphabet[i] = (byte) -1; - } - for (int i = 'Z'; i >= 'A'; i--) { - base64Alphabet[i] = (byte) (i - 'A'); - } - for (int i = 'z'; i >= 'a'; i--) { - base64Alphabet[i] = (byte) (i - 'a' + 26); - } - for (int i = '9'; i >= '0'; i--) { - base64Alphabet[i] = (byte) (i - '0' + 52); - } - - base64Alphabet['+'] = 62; - base64Alphabet['/'] = 63; - } - - /** - * Decodes Base64 data into octects - * - * @param base64Data Byte array containing Base64 data - * @return Array containing decoded data. - */ - public static byte[] decodeBase64(byte[] base64Data) { - // RFC 2045 requires that we discard ALL non-Base64 characters - base64Data = discardNonBase64(base64Data); - - // handle the edge case, so we don't have to worry about it later - if (base64Data.length == 0) { - return new byte[0]; - } - - int numberQuadruple = base64Data.length / FOURBYTE; - byte decodedData[] = null; - byte b1 = 0, b2 = 0, b3 = 0, b4 = 0, marker0 = 0, marker1 = 0; - - // Throw away anything not in base64Data - - int encodedIndex = 0; - int dataIndex = 0; - { - // this sizes the output array properly - rlw - int lastData = base64Data.length; - // ignore the '=' padding - while (base64Data[lastData - 1] == PAD) { - if (--lastData == 0) { - return new byte[0]; - } - } - decodedData = new byte[lastData - numberQuadruple]; - } - - for (int i = 0; i < numberQuadruple; i++) { - dataIndex = i * 4; - marker0 = base64Data[dataIndex + 2]; - marker1 = base64Data[dataIndex + 3]; - - b1 = base64Alphabet[base64Data[dataIndex]]; - b2 = base64Alphabet[base64Data[dataIndex + 1]]; - - if (marker0 != PAD && marker1 != PAD) { - //No PAD e.g 3cQl - b3 = base64Alphabet[marker0]; - b4 = base64Alphabet[marker1]; - - decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4); - decodedData[encodedIndex + 1] = - (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); - decodedData[encodedIndex + 2] = (byte) (b3 << 6 | b4); - } else if (marker0 == PAD) { - //Two PAD e.g. 3c[Pad][Pad] - decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4); - } else if (marker1 == PAD) { - //One PAD e.g. 3cQ[Pad] - b3 = base64Alphabet[marker0]; - - decodedData[encodedIndex] = (byte) (b1 << 2 | b2 >> 4); - decodedData[encodedIndex + 1] = - (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); - } - encodedIndex += 3; - } - return decodedData; - } - - /** - * Check octect wheter it is a base64 encoding. - * - * @param octect to be checked byte - * @return ture if it is base64 encoding, false otherwise. - */ - private static boolean isBase64(byte octect) { - if (octect == PAD) { - return true; - } else if (base64Alphabet[octect] == -1) { - return false; - } else { - return true; - } - } - - /** - * Discards any characters outside of the base64 alphabet, per - * the requirements on page 25 of RFC 2045 - "Any characters - * outside of the base64 alphabet are to be ignored in base64 - * encoded data." - * - * @param data The base-64 encoded data to groom - * @return The data, less non-base64 characters (see RFC 2045). - */ - static byte[] discardNonBase64(byte[] data) { - byte groomedData[] = new byte[data.length]; - int bytesCopied = 0; - - for (int i = 0; i < data.length; i++) { - if (isBase64(data[i])) { - groomedData[bytesCopied++] = data[i]; - } - } - - byte packedData[] = new byte[bytesCopied]; - - System.arraycopy(groomedData, 0, packedData, 0, bytesCopied); - - return packedData; - } -} diff --git a/mms-common/java/com/android/mmscommon/mms/pdu/PduParser.java b/mms-common/java/com/android/mmscommon/mms/pdu/PduParser.java index 9253f8373..6a58ba691 100644 --- a/mms-common/java/com/android/mmscommon/mms/pdu/PduParser.java +++ b/mms-common/java/com/android/mmscommon/mms/pdu/PduParser.java @@ -17,14 +17,15 @@ package com.android.mmscommon.mms.pdu; -import com.android.mmscommon.ContentType; import com.android.mmscommon.CharacterSets; +import com.android.mmscommon.ContentType; import com.android.mmscommon.EncodedStringValue; import com.android.mmscommon.InvalidHeaderValueException; import com.android.mmscommon.PduHeaders; import android.util.Config; import android.util.Log; +import android.util.base64.Base64; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -790,7 +791,7 @@ public class PduParser { String encoding = new String(partDataEncoding); if (encoding.equalsIgnoreCase(PduPart.P_BASE64)) { // Decode "base64" into "binary". - partData = Base64.decodeBase64(partData); + partData = Base64.decode(partData, Base64.DEFAULT); } else if (encoding.equalsIgnoreCase(PduPart.P_QUOTED_PRINTABLE)) { // Decode "quoted-printable" into "binary". partData = QuotedPrintable.decodeQuotedPrintable(partData); From a580e68cc3bea688167eb5e55122bec8e83ab939 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Thu, 11 Feb 2010 17:30:52 -0800 Subject: [PATCH 183/541] remove a dependency of GraphicBuffer (libui) on Parcel (libbinder). Add a Flattenable interface to libutils which can be used to flatten an object into bytestream + filedescriptor stream. Parcel is modified to handle Flattenable. And GraphicBuffer implements Flattenable. Except for the overlay classes libui is now independent of libbinder. --- include/utils/Flattenable.h | 62 +++++++++++++++++++++++++++++++++++++ libs/utils/Android.mk | 1 + libs/utils/Flattenable.cpp | 24 ++++++++++++++ 3 files changed, 87 insertions(+) create mode 100644 include/utils/Flattenable.h create mode 100644 libs/utils/Flattenable.cpp diff --git a/include/utils/Flattenable.h b/include/utils/Flattenable.h new file mode 100644 index 000000000..852be3b6a --- /dev/null +++ b/include/utils/Flattenable.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2010 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_UTILS_FLATTENABLE_H +#define ANDROID_UTILS_FLATTENABLE_H + + +#include +#include +#include + +namespace android { + +class Flattenable +{ +public: + // size in bytes of the flattened object + virtual size_t getFlattenedSize() const = 0; + + // number of file descriptors to flatten + virtual size_t getFdCount() const = 0; + + // flattens the object into buffer. + // size should be at least of getFlattenedSize() + // file descriptors are written in the fds[] array but ownership is + // not transfered (ie: they must be dupped by the caller of + // flatten() if needed). + virtual status_t flatten(void* buffer, size_t size, + int fds[], size_t count) const = 0; + + // unflattens the object from buffer. + // size should be equal to the value of getFlattenedSize() when the + // object was flattened. + // unflattened file descriptors are found in the fds[] array and + // don't need to be dupped(). ie: the caller of unflatten doesn't + // keep ownership. If a fd is not retained by unflatten() it must be + // explicitly closed. + virtual status_t unflatten(void const* buffer, size_t size, + int fds[], size_t count) = 0; + +protected: + virtual ~Flattenable() = 0; + +}; + +}; // namespace android + + +#endif /* ANDROID_UTILS_FLATTENABLE_H */ diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk index d2cfd3b5c..d0eedb43a 100644 --- a/libs/utils/Android.mk +++ b/libs/utils/Android.mk @@ -25,6 +25,7 @@ commonSources:= \ CallStack.cpp \ Debug.cpp \ FileMap.cpp \ + Flattenable.cpp \ RefBase.cpp \ ResourceTypes.cpp \ SharedBuffer.cpp \ diff --git a/libs/utils/Flattenable.cpp b/libs/utils/Flattenable.cpp new file mode 100644 index 000000000..1f2ffaa28 --- /dev/null +++ b/libs/utils/Flattenable.cpp @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2006 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 + +namespace android { + +Flattenable::~Flattenable() { +} + +}; // namespace android From 60ed8d122a19e97ed3a168ccb0e4d3cba94c3825 Mon Sep 17 00:00:00 2001 From: Kenny Root Date: Mon, 22 Feb 2010 22:36:26 -0800 Subject: [PATCH 184/541] Use UTF-8 strings to avoid duplicate caching, part 1 StringBlock instances containing UTF-8 strings use a cache to convert into UTF-16, but using that cache and then using a JNI call to NewString causes the UTF-8 string as well as two copies of the UTF-16 string to be held in memory. Getting the UTF-8 string directly from the StringPool eliminates one copy of the UTF-16 string being held in memory. This is part 1. Part 2 will include ResXMLParser optimizations. Change-Id: Ibd4509a485db746d59cd4b9501f544877139276c --- include/utils/ResourceTypes.h | 2 ++ libs/utils/ResourceTypes.cpp | 45 ++++++++++++++++++++++++++++++----- 2 files changed, 41 insertions(+), 6 deletions(-) diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h index 13ea27e4d..cd657e831 100644 --- a/include/utils/ResourceTypes.h +++ b/include/utils/ResourceTypes.h @@ -447,6 +447,8 @@ public: } const char16_t* stringAt(size_t idx, size_t* outLen) const; + const char* string8At(size_t idx, size_t* outLen) const; + const ResStringPool_span* styleAt(const ResStringPool_ref& ref) const; const ResStringPool_span* styleAt(size_t idx) const; diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp index e8bd5cf01..38600b9ae 100644 --- a/libs/utils/ResourceTypes.cpp +++ b/libs/utils/ResourceTypes.cpp @@ -497,6 +497,34 @@ const uint16_t* ResStringPool::stringAt(size_t idx, size_t* outLen) const return NULL; } +const char* ResStringPool::string8At(size_t idx, size_t* outLen) const +{ + if (mError == NO_ERROR && idx < mHeader->stringCount) { + const bool isUTF8 = (mHeader->flags&ResStringPool_header::UTF8_FLAG) != 0; + const uint32_t off = mEntries[idx]/(isUTF8?sizeof(char):sizeof(char16_t)); + if (off < (mStringPoolSize-1)) { + if (isUTF8) { + const uint8_t* strings = (uint8_t*)mStrings; + const uint8_t* str = strings+off; + DECODE_LENGTH(str, sizeof(uint8_t), *outLen) + size_t encLen; + DECODE_LENGTH(str, sizeof(uint8_t), encLen) + if ((uint32_t)(str+encLen-strings) < mStringPoolSize) { + return (const char*)str; + } else { + LOGW("Bad string block: string #%d extends to %d, past end at %d\n", + (int)idx, (int)(str+encLen-strings), (int)mStringPoolSize); + } + } + } else { + LOGW("Bad string block: string #%d entry is at %d, past end at %d\n", + (int)idx, (int)(off*sizeof(uint16_t)), + (int)(mStringPoolSize*sizeof(uint16_t))); + } + } + return NULL; +} + const ResStringPool_span* ResStringPool::styleAt(const ResStringPool_ref& ref) const { return styleAt(ref.index); @@ -4018,14 +4046,19 @@ void ResTable::print_value(const Package* pkg, const Res_value& value) const 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( + const char* str8 = pkg->header->values.string8At( value.data, &len); - if (str == NULL) { - printf("(string) null\n"); + if (str8 != NULL) { + printf("(string8) \"%s\"\n", str8); } else { - printf("(string%d) \"%s\"\n", - pkg->header->values.isUTF8()?8:16, - String8(str, len).string()); + const char16_t* str16 = pkg->header->values.stringAt( + value.data, &len); + if (str16 != NULL) { + printf("(string16) \"%s\"\n", + String8(str16, len).string()); + } else { + printf("(string) null\n"); + } } } else if (value.dataType == Res_value::TYPE_FLOAT) { printf("(float) %g\n", *(const float*)&value.data); From d882653c22d6277b46d01aeadccbae3f7870cf62 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Wed, 24 Feb 2010 19:54:22 -0800 Subject: [PATCH 185/541] Re-arrange android-common so framework no longer links with it. This is the framework part, moving classes around so the framework no longer needs to link to android-common. Makes some APIs public, others that didn't need to be public are private in the framework, some small things are copied. --- mms-common/Android.mk | 1 + .../java/com/android/mmscommon/telephony/TelephonyProvider.java | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/mms-common/Android.mk b/mms-common/Android.mk index de994c0cd..57f1ccca0 100644 --- a/mms-common/Android.mk +++ b/mms-common/Android.mk @@ -20,6 +20,7 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := mms-common LOCAL_SRC_FILES := $(call all-java-files-under, java) +LOCAL_STATIC_JAVA_LIBRARIES += android-common include $(BUILD_STATIC_JAVA_LIBRARY) # Include this library in the build server's output directory diff --git a/mms-common/java/com/android/mmscommon/telephony/TelephonyProvider.java b/mms-common/java/com/android/mmscommon/telephony/TelephonyProvider.java index 0237bc2eb..87e47583e 100644 --- a/mms-common/java/com/android/mmscommon/telephony/TelephonyProvider.java +++ b/mms-common/java/com/android/mmscommon/telephony/TelephonyProvider.java @@ -32,8 +32,8 @@ import android.telephony.SmsMessage; import android.text.TextUtils; import android.util.Config; import android.util.Log; +import android.util.Patterns; -import com.android.common.Patterns; import android.database.sqlite.SqliteWrapper; /** From 47c6fcb973d27d7cfe7cddc31cb4f6ad37b92607 Mon Sep 17 00:00:00 2001 From: Wei Huang Date: Thu, 25 Feb 2010 17:24:15 -0800 Subject: [PATCH 186/541] define the "seen" column for sms and pdu tables. --- .../mmscommon/mms/pdu/PduPersister.java | 4 ++ .../telephony/TelephonyProvider.java | 51 ++++++++----------- 2 files changed, 24 insertions(+), 31 deletions(-) diff --git a/mms-common/java/com/android/mmscommon/mms/pdu/PduPersister.java b/mms-common/java/com/android/mmscommon/mms/pdu/PduPersister.java index 46f28c7f0..d92f0e1d7 100644 --- a/mms-common/java/com/android/mmscommon/mms/pdu/PduPersister.java +++ b/mms-common/java/com/android/mmscommon/mms/pdu/PduPersister.java @@ -1145,6 +1145,10 @@ public class PduPersister { } } + // mark "read" and "seen" + values.put(Mms.READ, 0); + values.put(Mms.SEEN, 0); + Uri res = SqliteWrapper.insert(mContext, mContentResolver, uri, values); if (res == null) { throw new MmsException("persist() failed: return null."); diff --git a/mms-common/java/com/android/mmscommon/telephony/TelephonyProvider.java b/mms-common/java/com/android/mmscommon/telephony/TelephonyProvider.java index 87e47583e..cfc9231f0 100644 --- a/mms-common/java/com/android/mmscommon/telephony/TelephonyProvider.java +++ b/mms-common/java/com/android/mmscommon/telephony/TelephonyProvider.java @@ -51,37 +51,6 @@ public final class TelephonyProvider { private static final boolean DEBUG = true; private static final boolean LOCAL_LOGV = DEBUG ? Config.LOGD : Config.LOGV; -// public static final Pattern EMAIL_ADDRESS -// = Pattern.compile( -// "[a-zA-Z0-9\\+\\.\\_\\%\\-]{1,256}" + -// "\\@" + -// "[a-zA-Z0-9][a-zA-Z0-9\\-]{0,64}" + -// "(" + -// "\\." + -// "[a-zA-Z0-9][a-zA-Z0-9\\-]{0,25}" + -// ")+" -// ); -// -// /** -// * This pattern is intended for searching for things that look like they -// * might be phone numbers in arbitrary text, not for validating whether -// * something is in fact a phone number. It will miss many things that -// * are legitimate phone numbers. -// * -// *

The pattern matches the following: -// *

    -// *
  • Optionally, a + sign followed immediately by one or more digits. Spaces, dots, or dashes -// * may follow. -// *
  • Optionally, sets of digits in parentheses, separated by spaces, dots, or dashes. -// *
  • A string starting and ending with a digit, containing digits, spaces, dots, and/or dashes. -// *
-// */ -// public static final Pattern PHONE -// = Pattern.compile( // sdd = space, dot, or dash -// "(\\+[0-9]+[\\- \\.]*)?" // +* -// + "(\\([0-9]+\\)[\\- \\.]*)?" // ()* -// + "([0-9][0-9\\- \\.][0-9\\- \\.]+[0-9])"); // + - // Constructor public TelephonyProvider() { } @@ -135,6 +104,12 @@ public final class TelephonyProvider { */ public static final String READ = "read"; + /** + * Indicates whether this message has been seen by the user. The "seen" flag will be + * used to figure out whether we need to throw up a statusbar notification or not. + */ + public static final String SEEN = "seen"; + /** * The TP-Status value for the message, or -1 if no status has * been received @@ -705,6 +680,12 @@ public final class TelephonyProvider { */ public static final String READ = "read"; + /** + * Indicates whether this message has been seen by the user. The "seen" flag will be + * used to figure out whether we need to throw up a statusbar notification or not. + */ + public static final String SEEN = "seen"; + /** * The Message-ID of the message. *

Type: TEXT

@@ -1157,6 +1138,7 @@ public final class TelephonyProvider { *

Type: INTEGER

*/ public static final String READ = "read"; + /** * The snippet of the latest message in the thread. *

Type: TEXT

@@ -1697,6 +1679,13 @@ public final class TelephonyProvider { */ public static final String LAST_TRY = "last_try"; } + + public static final class WordsTable { + public static final String ID = "_id"; + public static final String SOURCE_ROW_ID = "source_id"; + public static final String TABLE_ID = "table_to_use"; + public static final String INDEXED_TEXT = "index_text"; + } } public static final class Carriers implements BaseColumns { From 806246a598d56c3069866d95bbba6098c369c320 Mon Sep 17 00:00:00 2001 From: Ken Shirriff Date: Mon, 1 Mar 2010 14:19:26 -0800 Subject: [PATCH 187/541] Remove DateException and obsolete DateUtils functions. These functions are deprecated and no longer used. --- calendar/Android.mk | 1 - 1 file changed, 1 deletion(-) diff --git a/calendar/Android.mk b/calendar/Android.mk index fd20dfaea..7f9ee0ac4 100644 --- a/calendar/Android.mk +++ b/calendar/Android.mk @@ -10,7 +10,6 @@ LOCAL_SRC_FILES := \ ../core/java/android/pim/ICalendar.java \ ../core/java/android/pim/RecurrenceSet.java \ ../core/java/android/pim/ContactsAsyncHelper.java \ - ../core/java/android/pim/DateException.java include $(BUILD_STATIC_JAVA_LIBRARY) From 7925ea7735a70929655d319c46edbe962dcd3943 Mon Sep 17 00:00:00 2001 From: Ficus Kirkpatrick Date: Mon, 1 Mar 2010 16:33:53 -0800 Subject: [PATCH 188/541] Demote the famous ResourceTypes warning to LOGV. It is spamming the log bigtime and can be promoted back to LOGW or worse by whoever decides to actually investigate the bug. Change-Id: I72d950155378f641ebdfbacabae774f5736a52bc --- libs/utils/ResourceTypes.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp index 38600b9ae..5f89788cd 100644 --- a/libs/utils/ResourceTypes.cpp +++ b/libs/utils/ResourceTypes.cpp @@ -1850,7 +1850,7 @@ bool ResTable::getResourceName(uint32_t resID, resource_name* outName) const if (Res_GETPACKAGE(resID)+1 == 0) { LOGW("No package identifier when getting name for resource number 0x%08x", resID); } else { - LOGW("Resources don't contain package for resource number 0x%08x", resID); + LOGV("Resources don't contain package for resource number 0x%08x", resID); } return false; } @@ -1900,7 +1900,7 @@ ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag if (Res_GETPACKAGE(resID)+1 == 0) { LOGW("No package identifier when getting name for resource number 0x%08x", resID); } else { - LOGW("Resources don't contain package for resource number 0x%08x", resID); + LOGV("Resources don't contain package for resource number 0x%08x", resID); } return BAD_INDEX; } From aeb43bd6dfc146c86105fb85e59e452da422a7c1 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Mon, 1 Mar 2010 17:43:39 -0800 Subject: [PATCH 189/541] Fix issue #2448075: aapt doesn't fix up activity-alias android:targetActivity links And related: - The aapt tool now sets a resource configurations sdk level to match any configs that have been set (for example if you specify density your sdk level will be at least 4). - New option to modify the targetPackage attribute of instrumentation. - Clean up of aapt options help. - Fix of UI type values to leave 0 for "unspecified". - Make the UI mode config APIs public. --- include/utils/ResourceTypes.h | 13 +-- libs/utils/ResourceTypes.cpp | 175 ++++++++++++++++++++++++++++++---- 2 files changed, 166 insertions(+), 22 deletions(-) diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h index cd657e831..cbcef4e1a 100644 --- a/include/utils/ResourceTypes.h +++ b/include/utils/ResourceTypes.h @@ -944,8 +944,9 @@ struct ResTable_config enum { // uiMode bits for the mode type. MASK_UI_MODE_TYPE = 0x0f, - UI_MODE_TYPE_NORMAL = 0x00, - UI_MODE_TYPE_CAR = 0x01, + UI_MODE_TYPE_ANY = 0x00, + UI_MODE_TYPE_NORMAL = 0x01, + UI_MODE_TYPE_CAR = 0x02, // uiMode bits for the night switch. MASK_UI_MODE_NIGHT = 0x30, @@ -1086,7 +1087,7 @@ struct ResTable_config } } - if (screenConfig || o.screenConfig) { + if (screenLayout || o.screenLayout) { if (((screenLayout^o.screenLayout) & MASK_SCREENSIZE) != 0) { if (!(screenLayout & MASK_SCREENSIZE)) return false; if (!(o.screenLayout & MASK_SCREENSIZE)) return true; @@ -1102,7 +1103,7 @@ struct ResTable_config if (!o.orientation) return true; } - if (screenConfig || o.screenConfig) { + if (uiMode || o.uiMode) { if (((uiMode^o.uiMode) & MASK_UI_MODE_TYPE) != 0) { if (!(uiMode & MASK_UI_MODE_TYPE)) return false; if (!(o.uiMode & MASK_UI_MODE_TYPE)) return true; @@ -1203,7 +1204,7 @@ struct ResTable_config } } - if (screenConfig || o.screenConfig) { + if (screenLayout || o.screenLayout) { if (((screenLayout^o.screenLayout) & MASK_SCREENSIZE) != 0 && (requested->screenLayout & MASK_SCREENSIZE)) { return (screenLayout & MASK_SCREENSIZE); @@ -1218,7 +1219,7 @@ struct ResTable_config return (orientation); } - if (screenConfig || o.screenConfig) { + if (uiMode || o.uiMode) { if (((uiMode^o.uiMode) & MASK_UI_MODE_TYPE) != 0 && (requested->uiMode & MASK_UI_MODE_TYPE)) { return (uiMode & MASK_UI_MODE_TYPE); diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp index 5f89788cd..6da11b5cd 100644 --- a/libs/utils/ResourceTypes.cpp +++ b/libs/utils/ResourceTypes.cpp @@ -4149,22 +4149,165 @@ void ResTable::print(bool inclValues) const } else { sprintf(density, "%d", (int)dval); } - printf(" config %d lang=%c%c cnt=%c%c orien=%d touch=%d density=%s key=%d infl=%d nav=%d w=%d h=%d sz=%d lng=%d\n", - (int)configIndex, - type->config.language[0] ? type->config.language[0] : '-', - type->config.language[1] ? type->config.language[1] : '-', - type->config.country[0] ? type->config.country[0] : '-', - type->config.country[1] ? type->config.country[1] : '-', - type->config.orientation, - type->config.touchscreen, - density, - type->config.keyboard, - type->config.inputFlags, - type->config.navigation, - dtohs(type->config.screenWidth), - dtohs(type->config.screenHeight), - type->config.screenLayout&ResTable_config::MASK_SCREENSIZE, - type->config.screenLayout&ResTable_config::MASK_SCREENLONG); + printf(" config %d", (int)configIndex); + if (type->config.mcc != 0) { + printf(" mcc=%d", dtohs(type->config.mcc)); + } + if (type->config.mnc != 0) { + printf(" mnc=%d", dtohs(type->config.mnc)); + } + if (type->config.locale != 0) { + printf(" lang=%c%c cnt=%c%c", + type->config.language[0] ? type->config.language[0] : '-', + type->config.language[1] ? type->config.language[1] : '-', + type->config.country[0] ? type->config.country[0] : '-', + type->config.country[1] ? type->config.country[1] : '-'); + } + if (type->config.screenLayout != 0) { + printf(" sz=%d", + type->config.screenLayout&ResTable_config::MASK_SCREENSIZE); + switch (type->config.screenLayout&ResTable_config::MASK_SCREENSIZE) { + case ResTable_config::SCREENSIZE_SMALL: + printf(" (small)"); + break; + case ResTable_config::SCREENSIZE_NORMAL: + printf(" (normal)"); + break; + case ResTable_config::SCREENSIZE_LARGE: + printf(" (large)"); + break; + } + printf(" lng=%d", + type->config.screenLayout&ResTable_config::MASK_SCREENLONG); + switch (type->config.screenLayout&ResTable_config::MASK_SCREENLONG) { + case ResTable_config::SCREENLONG_NO: + printf(" (notlong)"); + break; + case ResTable_config::SCREENLONG_YES: + printf(" (long)"); + break; + } + } + if (type->config.orientation != 0) { + printf(" orient=%d", type->config.orientation); + switch (type->config.orientation) { + case ResTable_config::ORIENTATION_PORT: + printf(" (port)"); + break; + case ResTable_config::ORIENTATION_LAND: + printf(" (land)"); + break; + case ResTable_config::ORIENTATION_SQUARE: + printf(" (square)"); + break; + } + } + if (type->config.uiMode != 0) { + printf(" type=%d", + type->config.uiMode&ResTable_config::MASK_UI_MODE_TYPE); + switch (type->config.uiMode&ResTable_config::MASK_UI_MODE_TYPE) { + case ResTable_config::UI_MODE_TYPE_NORMAL: + printf(" (normal)"); + break; + case ResTable_config::UI_MODE_TYPE_CAR: + printf(" (car)"); + break; + } + printf(" night=%d", + type->config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT); + switch (type->config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT) { + case ResTable_config::UI_MODE_NIGHT_NO: + printf(" (no)"); + break; + case ResTable_config::UI_MODE_NIGHT_YES: + printf(" (yes)"); + break; + } + } + if (dval != 0) { + printf(" density=%s", density); + } + if (type->config.touchscreen != 0) { + printf(" touch=%d", type->config.touchscreen); + switch (type->config.touchscreen) { + case ResTable_config::TOUCHSCREEN_NOTOUCH: + printf(" (notouch)"); + break; + case ResTable_config::TOUCHSCREEN_STYLUS: + printf(" (stylus)"); + break; + case ResTable_config::TOUCHSCREEN_FINGER: + printf(" (finger)"); + break; + } + } + if (type->config.inputFlags != 0) { + printf(" keyhid=%d", type->config.inputFlags&ResTable_config::MASK_KEYSHIDDEN); + switch (type->config.inputFlags&ResTable_config::MASK_KEYSHIDDEN) { + case ResTable_config::KEYSHIDDEN_NO: + printf(" (no)"); + break; + case ResTable_config::KEYSHIDDEN_YES: + printf(" (yes)"); + break; + case ResTable_config::KEYSHIDDEN_SOFT: + printf(" (soft)"); + break; + } + printf(" navhid=%d", type->config.inputFlags&ResTable_config::MASK_NAVHIDDEN); + switch (type->config.inputFlags&ResTable_config::MASK_NAVHIDDEN) { + case ResTable_config::NAVHIDDEN_NO: + printf(" (no)"); + break; + case ResTable_config::NAVHIDDEN_YES: + printf(" (yes)"); + break; + } + } + if (type->config.keyboard != 0) { + printf(" kbd=%d", type->config.keyboard); + switch (type->config.keyboard) { + case ResTable_config::KEYBOARD_NOKEYS: + printf(" (nokeys)"); + break; + case ResTable_config::KEYBOARD_QWERTY: + printf(" (qwerty)"); + break; + case ResTable_config::KEYBOARD_12KEY: + printf(" (12key)"); + break; + } + } + if (type->config.navigation != 0) { + printf(" nav=%d", type->config.navigation); + switch (type->config.navigation) { + case ResTable_config::NAVIGATION_NONAV: + printf(" (nonav)"); + break; + case ResTable_config::NAVIGATION_DPAD: + printf(" (dpad)"); + break; + case ResTable_config::NAVIGATION_TRACKBALL: + printf(" (trackball)"); + break; + case ResTable_config::NAVIGATION_WHEEL: + printf(" (wheel)"); + break; + } + } + if (type->config.screenWidth != 0) { + printf(" w=%d", dtohs(type->config.screenWidth)); + } + if (type->config.screenHeight != 0) { + printf(" h=%d", dtohs(type->config.screenHeight)); + } + if (type->config.sdkVersion != 0) { + printf(" sdk=%d", dtohs(type->config.sdkVersion)); + } + if (type->config.minorVersion != 0) { + printf(" mver=%d", dtohs(type->config.minorVersion)); + } + printf("\n"); size_t entryCount = dtohl(type->entryCount); uint32_t entriesStart = dtohl(type->entriesStart); if ((entriesStart&0x3) != 0) { From 64d40b5e5d987f69240fa1e5269b606fc3ed54b6 Mon Sep 17 00:00:00 2001 From: Ken Shirriff Date: Tue, 2 Mar 2010 12:50:03 -0800 Subject: [PATCH 190/541] Remove calendar.jar. This jar file failed to meet its goal of unbundling calendar. bug 2468654 --- calendar/Android.mk | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 calendar/Android.mk diff --git a/calendar/Android.mk b/calendar/Android.mk deleted file mode 100644 index 7f9ee0ac4..000000000 --- a/calendar/Android.mk +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright 2009 Google, Inc. - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) -LOCAL_MODULE := calendar -LOCAL_SRC_FILES := \ - ../core/java/android/provider/Calendar.java \ - ../core/java/android/pim/EventRecurrence.java \ - ../core/java/android/pim/ICalendar.java \ - ../core/java/android/pim/RecurrenceSet.java \ - ../core/java/android/pim/ContactsAsyncHelper.java \ - -include $(BUILD_STATIC_JAVA_LIBRARY) - -# Include this library in the build server's output directory -$(call dist-for-goals, droid, $(LOCAL_BUILT_MODULE):calendar.jar) From 8b51a1a88b8f2193d787c3f1285c48da8281ff8f Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Thu, 4 Mar 2010 00:58:29 -0800 Subject: [PATCH 191/541] Fix some bugs. Bug #2376231: Apps lose window focus (and back key causes ANR) if the lock screen is dismissed while the phone is in landscape mode This is another case where we weren't recomputing the focused window after changing the visibility policy. bug #2479958: Investigate source of "Resources don't contain package for resource number 0x7f0a0000" Um, okay, so it turns out there were bugs all over the place where we would load an XML resource from a another application, but not use the Resources for that application to retrieve its resources...! I think the only reason any of this stuff was working at all was because it typically only cared about retrieving the resource identifiers of the items (it would look up the values later). Bug #2401082: Passion ERE26 monkey crash - InputMethodManagerService Add some null checks. --- libs/utils/ResourceTypes.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp index 6da11b5cd..38d841295 100644 --- a/libs/utils/ResourceTypes.cpp +++ b/libs/utils/ResourceTypes.cpp @@ -1850,7 +1850,7 @@ bool ResTable::getResourceName(uint32_t resID, resource_name* outName) const if (Res_GETPACKAGE(resID)+1 == 0) { LOGW("No package identifier when getting name for resource number 0x%08x", resID); } else { - LOGV("Resources don't contain package for resource number 0x%08x", resID); + LOGW("No known package when getting name for resource number 0x%08x", resID); } return false; } @@ -1898,9 +1898,9 @@ ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag if (p < 0) { if (Res_GETPACKAGE(resID)+1 == 0) { - LOGW("No package identifier when getting name for resource number 0x%08x", resID); + LOGW("No package identifier when getting value for resource number 0x%08x", resID); } else { - LOGV("Resources don't contain package for resource number 0x%08x", resID); + LOGW("No known package when getting value for resource number 0x%08x", resID); } return BAD_INDEX; } @@ -1921,7 +1921,7 @@ ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag const PackageGroup* const grp = mPackageGroups[p]; if (grp == NULL) { LOGW("Bad identifier when getting value for resource number 0x%08x", resID); - return false; + return BAD_INDEX; } size_t ip = grp->packages.size(); while (ip > 0) { @@ -2003,7 +2003,7 @@ ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag return bestPackage->header->index; } - return BAD_INDEX; + return BAD_VALUE; } ssize_t ResTable::resolveReference(Res_value* value, ssize_t blockIndex, @@ -2018,6 +2018,9 @@ ssize_t ResTable::resolveReference(Res_value* value, ssize_t blockIndex, uint32_t newFlags = 0; const ssize_t newIndex = getResource(value->data, value, true, &newFlags, outConfig); + if (newIndex == BAD_INDEX) { + return BAD_INDEX; + } TABLE_THEME(LOGI("Resolving reference %p: newIndex=%d, type=0x%x, data=%p\n", (void*)lastRef, (int)newIndex, (int)value->dataType, (void*)value->data)); //printf("Getting reference 0x%08x: newIndex=%d\n", value->data, newIndex); From 70592f2c4734681521fd49fd9654967907901127 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Thu, 4 Mar 2010 18:41:49 -0800 Subject: [PATCH 192/541] Refactor car mode. Extract all UI behavior from dock observer and ACTION_DOCK_EVENT. Also introduce a desk type to go along with the car type all through the resource system, since we now need to have corresponding high-level broadcasts for desk dock mode. As part of that I also reworked some of the logic for switching modes to all funnel through a single update() call that looks all of the current state to decide what to do next, and fixed various locking issues. In addition I found there were bugs in the configuration change handling causing us to only switch into the car mode config and then never get out of it. Unfortunately now that we are actually changing the configuration for each mode change, the transitions between them are really crummy as we restart all kinds of activities. :( --- include/utils/ResourceTypes.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h index cbcef4e1a..0e796dc56 100644 --- a/include/utils/ResourceTypes.h +++ b/include/utils/ResourceTypes.h @@ -946,7 +946,8 @@ struct ResTable_config MASK_UI_MODE_TYPE = 0x0f, UI_MODE_TYPE_ANY = 0x00, UI_MODE_TYPE_NORMAL = 0x01, - UI_MODE_TYPE_CAR = 0x02, + UI_MODE_TYPE_DESK = 0x02, + UI_MODE_TYPE_CAR = 0x03, // uiMode bits for the night switch. MASK_UI_MODE_NIGHT = 0x30, From a77468038b47b70bba9386762da0ee1c699e4dea Mon Sep 17 00:00:00 2001 From: Christopher Tate Date: Fri, 5 Mar 2010 15:46:30 -0800 Subject: [PATCH 193/541] Refactor android.backup => android.app.backup Change-Id: I0b21316ff890d7f3c7d4b82837bb60670724c2e8 --- cleanspec.mk | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cleanspec.mk b/cleanspec.mk index 683e303ec..9d9aa7e55 100644 --- a/cleanspec.mk +++ b/cleanspec.mk @@ -1 +1,3 @@ $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/com/android/internal/os/IDropBoxService.java) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/backup) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/com/android/internal/backup) From 73308880cf1ca5ae9385c2ab26e03a857bfd77c3 Mon Sep 17 00:00:00 2001 From: Christopher Tate Date: Fri, 5 Mar 2010 18:03:22 -0800 Subject: [PATCH 194/541] Fix up missed refactoring in JNI reg and preloaded-classes Change-Id: I079bdf4edfb9083eba3e15d8e4dbf3b2bad9190c --- cleanspec.mk | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cleanspec.mk b/cleanspec.mk index 9d9aa7e55..e0843079c 100644 --- a/cleanspec.mk +++ b/cleanspec.mk @@ -1,3 +1,5 @@ $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/com/android/internal/os/IDropBoxService.java) $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/backup) $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/com/android/internal/backup) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/backup) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/com/android/internal/backup) From 44ca383c5f564f1b5f6bd88026950be1c5fc68ad Mon Sep 17 00:00:00 2001 From: Christopher Tate Date: Fri, 5 Mar 2010 18:48:59 -0800 Subject: [PATCH 195/541] Rename cleanspec => CleanSpec to match the build rule Change-Id: I9cc9027ea82a90af5f369976823f0feb0a1eeda0 --- cleanspec.mk | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 cleanspec.mk diff --git a/cleanspec.mk b/cleanspec.mk deleted file mode 100644 index e0843079c..000000000 --- a/cleanspec.mk +++ /dev/null @@ -1,5 +0,0 @@ -$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/com/android/internal/os/IDropBoxService.java) -$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/backup) -$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/com/android/internal/backup) -$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/backup) -$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/com/android/internal/backup) From df6e753fa7a9e51daa5940e4180ba507de342313 Mon Sep 17 00:00:00 2001 From: Tom Taylor Date: Mon, 8 Mar 2010 16:34:53 -0800 Subject: [PATCH 196/541] Remove mms-common library Put the Mms files back in the framework where they've been since 1.0. Change-Id: I3c449468053ddd82d35c45a06d71957de660bf99 --- mms-common/Android.mk | 30 - .../com/android/mmscommon/CharacterSets.java | 172 -- .../com/android/mmscommon/ContentType.java | 223 -- .../android/mmscommon/EncodedStringValue.java | 284 --- .../InvalidHeaderValueException.java | 41 - .../com/android/mmscommon/MmsException.java | 60 - .../com/android/mmscommon/PduHeaders.java | 719 ------- .../android/mmscommon/mms/ContentType.java | 223 -- .../mmscommon/mms/pdu/AcknowledgeInd.java | 90 - .../mmscommon/mms/pdu/DeliveryInd.java | 140 -- .../android/mmscommon/mms/pdu/GenericPdu.java | 115 - .../mms/pdu/MultimediaMessagePdu.java | 152 -- .../mmscommon/mms/pdu/NotificationInd.java | 287 --- .../mmscommon/mms/pdu/NotifyRespInd.java | 115 - .../android/mmscommon/mms/pdu/PduBody.java | 191 -- .../mmscommon/mms/pdu/PduComposer.java | 1184 ----------- .../mmscommon/mms/pdu/PduContentTypes.java | 110 - .../android/mmscommon/mms/pdu/PduParser.java | 1874 ----------------- .../android/mmscommon/mms/pdu/PduPart.java | 402 ---- .../mmscommon/mms/pdu/PduPersister.java | 1270 ----------- .../mmscommon/mms/pdu/QuotedPrintable.java | 68 - .../mmscommon/mms/pdu/ReadOrigInd.java | 155 -- .../android/mmscommon/mms/pdu/ReadRecInd.java | 146 -- .../mmscommon/mms/pdu/RetrieveConf.java | 302 --- .../android/mmscommon/mms/pdu/SendConf.java | 119 -- .../android/mmscommon/mms/pdu/SendReq.java | 347 --- .../mmscommon/mms/util/AbstractCache.java | 113 - .../android/mmscommon/mms/util/PduCache.java | 243 --- .../mmscommon/mms/util/PduCacheEntry.java | 44 - .../telephony/TelephonyProvider.java | 1779 ---------------- 30 files changed, 10998 deletions(-) delete mode 100644 mms-common/Android.mk delete mode 100644 mms-common/java/com/android/mmscommon/CharacterSets.java delete mode 100644 mms-common/java/com/android/mmscommon/ContentType.java delete mode 100644 mms-common/java/com/android/mmscommon/EncodedStringValue.java delete mode 100644 mms-common/java/com/android/mmscommon/InvalidHeaderValueException.java delete mode 100644 mms-common/java/com/android/mmscommon/MmsException.java delete mode 100644 mms-common/java/com/android/mmscommon/PduHeaders.java delete mode 100644 mms-common/java/com/android/mmscommon/mms/ContentType.java delete mode 100644 mms-common/java/com/android/mmscommon/mms/pdu/AcknowledgeInd.java delete mode 100644 mms-common/java/com/android/mmscommon/mms/pdu/DeliveryInd.java delete mode 100644 mms-common/java/com/android/mmscommon/mms/pdu/GenericPdu.java delete mode 100644 mms-common/java/com/android/mmscommon/mms/pdu/MultimediaMessagePdu.java delete mode 100644 mms-common/java/com/android/mmscommon/mms/pdu/NotificationInd.java delete mode 100644 mms-common/java/com/android/mmscommon/mms/pdu/NotifyRespInd.java delete mode 100644 mms-common/java/com/android/mmscommon/mms/pdu/PduBody.java delete mode 100644 mms-common/java/com/android/mmscommon/mms/pdu/PduComposer.java delete mode 100644 mms-common/java/com/android/mmscommon/mms/pdu/PduContentTypes.java delete mode 100644 mms-common/java/com/android/mmscommon/mms/pdu/PduParser.java delete mode 100644 mms-common/java/com/android/mmscommon/mms/pdu/PduPart.java delete mode 100644 mms-common/java/com/android/mmscommon/mms/pdu/PduPersister.java delete mode 100644 mms-common/java/com/android/mmscommon/mms/pdu/QuotedPrintable.java delete mode 100644 mms-common/java/com/android/mmscommon/mms/pdu/ReadOrigInd.java delete mode 100644 mms-common/java/com/android/mmscommon/mms/pdu/ReadRecInd.java delete mode 100644 mms-common/java/com/android/mmscommon/mms/pdu/RetrieveConf.java delete mode 100644 mms-common/java/com/android/mmscommon/mms/pdu/SendConf.java delete mode 100644 mms-common/java/com/android/mmscommon/mms/pdu/SendReq.java delete mode 100644 mms-common/java/com/android/mmscommon/mms/util/AbstractCache.java delete mode 100644 mms-common/java/com/android/mmscommon/mms/util/PduCache.java delete mode 100644 mms-common/java/com/android/mmscommon/mms/util/PduCacheEntry.java delete mode 100644 mms-common/java/com/android/mmscommon/telephony/TelephonyProvider.java diff --git a/mms-common/Android.mk b/mms-common/Android.mk deleted file mode 100644 index 57f1ccca0..000000000 --- a/mms-common/Android.mk +++ /dev/null @@ -1,30 +0,0 @@ -# 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. - -LOCAL_PATH := $(call my-dir) - -# Note: the source code is in java/, not src/, because this code is also part of -# the framework library, and build/core/pathmap.mk expects a java/ subdirectory. - -include $(CLEAR_VARS) -LOCAL_MODULE := mms-common -LOCAL_SRC_FILES := $(call all-java-files-under, java) -LOCAL_STATIC_JAVA_LIBRARIES += android-common -include $(BUILD_STATIC_JAVA_LIBRARY) - -# Include this library in the build server's output directory -$(call dist-for-goals, droid, $(LOCAL_BUILT_MODULE):mms-common.jar) - -# Build the test package -include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/mms-common/java/com/android/mmscommon/CharacterSets.java b/mms-common/java/com/android/mmscommon/CharacterSets.java deleted file mode 100644 index f19b07835..000000000 --- a/mms-common/java/com/android/mmscommon/CharacterSets.java +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Copyright (C) 2007 Esmertec AG. - * 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. - */ - -package com.android.mmscommon; - -import java.io.UnsupportedEncodingException; -import java.util.HashMap; - -public class CharacterSets { - /** - * IANA assigned MIB enum numbers. - * - * From wap-230-wsp-20010705-a.pdf - * Any-charset = - * Equivalent to the special RFC2616 charset value "*" - */ - public static final int ANY_CHARSET = 0x00; - public static final int US_ASCII = 0x03; - public static final int ISO_8859_1 = 0x04; - public static final int ISO_8859_2 = 0x05; - public static final int ISO_8859_3 = 0x06; - public static final int ISO_8859_4 = 0x07; - public static final int ISO_8859_5 = 0x08; - public static final int ISO_8859_6 = 0x09; - public static final int ISO_8859_7 = 0x0A; - public static final int ISO_8859_8 = 0x0B; - public static final int ISO_8859_9 = 0x0C; - public static final int SHIFT_JIS = 0x11; - public static final int UTF_8 = 0x6A; - public static final int BIG5 = 0x07EA; - public static final int UCS2 = 0x03E8; - public static final int UTF_16 = 0x03F7; - - /** - * If the encoding of given data is unsupported, use UTF_8 to decode it. - */ - public static final int DEFAULT_CHARSET = UTF_8; - - /** - * Array of MIB enum numbers. - */ - private static final int[] MIBENUM_NUMBERS = { - ANY_CHARSET, - US_ASCII, - ISO_8859_1, - ISO_8859_2, - ISO_8859_3, - ISO_8859_4, - ISO_8859_5, - ISO_8859_6, - ISO_8859_7, - ISO_8859_8, - ISO_8859_9, - SHIFT_JIS, - UTF_8, - BIG5, - UCS2, - UTF_16, - }; - - /** - * The Well-known-charset Mime name. - */ - public static final String MIMENAME_ANY_CHARSET = "*"; - public static final String MIMENAME_US_ASCII = "us-ascii"; - public static final String MIMENAME_ISO_8859_1 = "iso-8859-1"; - public static final String MIMENAME_ISO_8859_2 = "iso-8859-2"; - public static final String MIMENAME_ISO_8859_3 = "iso-8859-3"; - public static final String MIMENAME_ISO_8859_4 = "iso-8859-4"; - public static final String MIMENAME_ISO_8859_5 = "iso-8859-5"; - public static final String MIMENAME_ISO_8859_6 = "iso-8859-6"; - public static final String MIMENAME_ISO_8859_7 = "iso-8859-7"; - public static final String MIMENAME_ISO_8859_8 = "iso-8859-8"; - public static final String MIMENAME_ISO_8859_9 = "iso-8859-9"; - public static final String MIMENAME_SHIFT_JIS = "shift_JIS"; - public static final String MIMENAME_UTF_8 = "utf-8"; - public static final String MIMENAME_BIG5 = "big5"; - public static final String MIMENAME_UCS2 = "iso-10646-ucs-2"; - public static final String MIMENAME_UTF_16 = "utf-16"; - - public static final String DEFAULT_CHARSET_NAME = MIMENAME_UTF_8; - - /** - * Array of the names of character sets. - */ - private static final String[] MIME_NAMES = { - MIMENAME_ANY_CHARSET, - MIMENAME_US_ASCII, - MIMENAME_ISO_8859_1, - MIMENAME_ISO_8859_2, - MIMENAME_ISO_8859_3, - MIMENAME_ISO_8859_4, - MIMENAME_ISO_8859_5, - MIMENAME_ISO_8859_6, - MIMENAME_ISO_8859_7, - MIMENAME_ISO_8859_8, - MIMENAME_ISO_8859_9, - MIMENAME_SHIFT_JIS, - MIMENAME_UTF_8, - MIMENAME_BIG5, - MIMENAME_UCS2, - MIMENAME_UTF_16, - }; - - private static final HashMap MIBENUM_TO_NAME_MAP; - private static final HashMap NAME_TO_MIBENUM_MAP; - - static { - // Create the HashMaps. - MIBENUM_TO_NAME_MAP = new HashMap(); - NAME_TO_MIBENUM_MAP = new HashMap(); - assert(MIBENUM_NUMBERS.length == MIME_NAMES.length); - int count = MIBENUM_NUMBERS.length - 1; - for(int i = 0; i <= count; i++) { - MIBENUM_TO_NAME_MAP.put(MIBENUM_NUMBERS[i], MIME_NAMES[i]); - NAME_TO_MIBENUM_MAP.put(MIME_NAMES[i], MIBENUM_NUMBERS[i]); - } - } - - private CharacterSets() {} // Non-instantiatable - - /** - * Map an MIBEnum number to the name of the charset which this number - * is assigned to by IANA. - * - * @param mibEnumValue An IANA assigned MIBEnum number. - * @return The name string of the charset. - * @throws UnsupportedEncodingException - */ - public static String getMimeName(int mibEnumValue) - throws UnsupportedEncodingException { - String name = MIBENUM_TO_NAME_MAP.get(mibEnumValue); - if (name == null) { - throw new UnsupportedEncodingException(); - } - return name; - } - - /** - * Map a well-known charset name to its assigned MIBEnum number. - * - * @param mimeName The charset name. - * @return The MIBEnum number assigned by IANA for this charset. - * @throws UnsupportedEncodingException - */ - public static int getMibEnumValue(String mimeName) - throws UnsupportedEncodingException { - if(null == mimeName) { - return -1; - } - - Integer mibEnumValue = NAME_TO_MIBENUM_MAP.get(mimeName); - if (mibEnumValue == null) { - throw new UnsupportedEncodingException(); - } - return mibEnumValue; - } -} diff --git a/mms-common/java/com/android/mmscommon/ContentType.java b/mms-common/java/com/android/mmscommon/ContentType.java deleted file mode 100644 index ca449fe2e..000000000 --- a/mms-common/java/com/android/mmscommon/ContentType.java +++ /dev/null @@ -1,223 +0,0 @@ -/* - * Copyright (C) 2007-2008 Esmertec AG. - * Copyright (C) 2007-2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.mmscommon; - -import java.util.ArrayList; - -public class ContentType { - public static final String MMS_MESSAGE = "application/vnd.wap.mms-message"; - // The phony content type for generic PDUs (e.g. ReadOrig.ind, - // Notification.ind, Delivery.ind). - public static final String MMS_GENERIC = "application/vnd.wap.mms-generic"; - public static final String MULTIPART_MIXED = "application/vnd.wap.multipart.mixed"; - public static final String MULTIPART_RELATED = "application/vnd.wap.multipart.related"; - public static final String MULTIPART_ALTERNATIVE = "application/vnd.wap.multipart.alternative"; - - public static final String TEXT_PLAIN = "text/plain"; - public static final String TEXT_HTML = "text/html"; - public static final String TEXT_VCALENDAR = "text/x-vCalendar"; - public static final String TEXT_VCARD = "text/x-vCard"; - - public static final String IMAGE_UNSPECIFIED = "image/*"; - public static final String IMAGE_JPEG = "image/jpeg"; - public static final String IMAGE_JPG = "image/jpg"; - public static final String IMAGE_GIF = "image/gif"; - public static final String IMAGE_WBMP = "image/vnd.wap.wbmp"; - public static final String IMAGE_PNG = "image/png"; - - public static final String AUDIO_UNSPECIFIED = "audio/*"; - public static final String AUDIO_AAC = "audio/aac"; - public static final String AUDIO_AMR = "audio/amr"; - public static final String AUDIO_IMELODY = "audio/imelody"; - public static final String AUDIO_MID = "audio/mid"; - public static final String AUDIO_MIDI = "audio/midi"; - public static final String AUDIO_MP3 = "audio/mp3"; - public static final String AUDIO_MPEG3 = "audio/mpeg3"; - public static final String AUDIO_MPEG = "audio/mpeg"; - public static final String AUDIO_MPG = "audio/mpg"; - public static final String AUDIO_MP4 = "audio/mp4"; - public static final String AUDIO_X_MID = "audio/x-mid"; - public static final String AUDIO_X_MIDI = "audio/x-midi"; - public static final String AUDIO_X_MP3 = "audio/x-mp3"; - public static final String AUDIO_X_MPEG3 = "audio/x-mpeg3"; - public static final String AUDIO_X_MPEG = "audio/x-mpeg"; - public static final String AUDIO_X_MPG = "audio/x-mpg"; - public static final String AUDIO_3GPP = "audio/3gpp"; - public static final String AUDIO_OGG = "application/ogg"; - - public static final String VIDEO_UNSPECIFIED = "video/*"; - public static final String VIDEO_3GPP = "video/3gpp"; - public static final String VIDEO_3G2 = "video/3gpp2"; - public static final String VIDEO_H263 = "video/h263"; - public static final String VIDEO_MP4 = "video/mp4"; - - public static final String APP_SMIL = "application/smil"; - public static final String APP_WAP_XHTML = "application/vnd.wap.xhtml+xml"; - public static final String APP_XHTML = "application/xhtml+xml"; - - public static final String APP_DRM_CONTENT = "application/vnd.oma.drm.content"; - public static final String APP_DRM_MESSAGE = "application/vnd.oma.drm.message"; - - private static final ArrayList sSupportedContentTypes = new ArrayList(); - private static final ArrayList sSupportedImageTypes = new ArrayList(); - private static final ArrayList sSupportedAudioTypes = new ArrayList(); - private static final ArrayList sSupportedVideoTypes = new ArrayList(); - - static { - sSupportedContentTypes.add(TEXT_PLAIN); - sSupportedContentTypes.add(TEXT_HTML); - sSupportedContentTypes.add(TEXT_VCALENDAR); - sSupportedContentTypes.add(TEXT_VCARD); - - sSupportedContentTypes.add(IMAGE_JPEG); - sSupportedContentTypes.add(IMAGE_GIF); - sSupportedContentTypes.add(IMAGE_WBMP); - sSupportedContentTypes.add(IMAGE_PNG); - sSupportedContentTypes.add(IMAGE_JPG); - //supportedContentTypes.add(IMAGE_SVG); not yet supported. - - sSupportedContentTypes.add(AUDIO_AAC); - sSupportedContentTypes.add(AUDIO_AMR); - sSupportedContentTypes.add(AUDIO_IMELODY); - sSupportedContentTypes.add(AUDIO_MID); - sSupportedContentTypes.add(AUDIO_MIDI); - sSupportedContentTypes.add(AUDIO_MP3); - sSupportedContentTypes.add(AUDIO_MPEG3); - sSupportedContentTypes.add(AUDIO_MPEG); - sSupportedContentTypes.add(AUDIO_MPG); - sSupportedContentTypes.add(AUDIO_X_MID); - sSupportedContentTypes.add(AUDIO_X_MIDI); - sSupportedContentTypes.add(AUDIO_X_MP3); - sSupportedContentTypes.add(AUDIO_X_MPEG3); - sSupportedContentTypes.add(AUDIO_X_MPEG); - sSupportedContentTypes.add(AUDIO_X_MPG); - sSupportedContentTypes.add(AUDIO_3GPP); - sSupportedContentTypes.add(AUDIO_OGG); - - sSupportedContentTypes.add(VIDEO_3GPP); - sSupportedContentTypes.add(VIDEO_3G2); - sSupportedContentTypes.add(VIDEO_H263); - sSupportedContentTypes.add(VIDEO_MP4); - - sSupportedContentTypes.add(APP_SMIL); - sSupportedContentTypes.add(APP_WAP_XHTML); - sSupportedContentTypes.add(APP_XHTML); - - sSupportedContentTypes.add(APP_DRM_CONTENT); - sSupportedContentTypes.add(APP_DRM_MESSAGE); - - // add supported image types - sSupportedImageTypes.add(IMAGE_JPEG); - sSupportedImageTypes.add(IMAGE_GIF); - sSupportedImageTypes.add(IMAGE_WBMP); - sSupportedImageTypes.add(IMAGE_PNG); - sSupportedImageTypes.add(IMAGE_JPG); - - // add supported audio types - sSupportedAudioTypes.add(AUDIO_AAC); - sSupportedAudioTypes.add(AUDIO_AMR); - sSupportedAudioTypes.add(AUDIO_IMELODY); - sSupportedAudioTypes.add(AUDIO_MID); - sSupportedAudioTypes.add(AUDIO_MIDI); - sSupportedAudioTypes.add(AUDIO_MP3); - sSupportedAudioTypes.add(AUDIO_MPEG3); - sSupportedAudioTypes.add(AUDIO_MPEG); - sSupportedAudioTypes.add(AUDIO_MPG); - sSupportedAudioTypes.add(AUDIO_MP4); - sSupportedAudioTypes.add(AUDIO_X_MID); - sSupportedAudioTypes.add(AUDIO_X_MIDI); - sSupportedAudioTypes.add(AUDIO_X_MP3); - sSupportedAudioTypes.add(AUDIO_X_MPEG3); - sSupportedAudioTypes.add(AUDIO_X_MPEG); - sSupportedAudioTypes.add(AUDIO_X_MPG); - sSupportedAudioTypes.add(AUDIO_3GPP); - sSupportedAudioTypes.add(AUDIO_OGG); - - // add supported video types - sSupportedVideoTypes.add(VIDEO_3GPP); - sSupportedVideoTypes.add(VIDEO_3G2); - sSupportedVideoTypes.add(VIDEO_H263); - sSupportedVideoTypes.add(VIDEO_MP4); - } - - // This class should never be instantiated. - private ContentType() { - } - - public static boolean isSupportedType(String contentType) { - return (null != contentType) && sSupportedContentTypes.contains(contentType); - } - - public static boolean isSupportedImageType(String contentType) { - return isImageType(contentType) && isSupportedType(contentType); - } - - public static boolean isSupportedAudioType(String contentType) { - return isAudioType(contentType) && isSupportedType(contentType); - } - - public static boolean isSupportedVideoType(String contentType) { - return isVideoType(contentType) && isSupportedType(contentType); - } - - public static boolean isTextType(String contentType) { - return (null != contentType) && contentType.startsWith("text/"); - } - - public static boolean isImageType(String contentType) { - return (null != contentType) && contentType.startsWith("image/"); - } - - public static boolean isAudioType(String contentType) { - return (null != contentType) && contentType.startsWith("audio/"); - } - - public static boolean isVideoType(String contentType) { - return (null != contentType) && contentType.startsWith("video/"); - } - - public static boolean isDrmType(String contentType) { - return (null != contentType) - && (contentType.equals(APP_DRM_CONTENT) - || contentType.equals(APP_DRM_MESSAGE)); - } - - public static boolean isUnspecified(String contentType) { - return (null != contentType) && contentType.endsWith("*"); - } - - @SuppressWarnings("unchecked") - public static ArrayList getImageTypes() { - return (ArrayList) sSupportedImageTypes.clone(); - } - - @SuppressWarnings("unchecked") - public static ArrayList getAudioTypes() { - return (ArrayList) sSupportedAudioTypes.clone(); - } - - @SuppressWarnings("unchecked") - public static ArrayList getVideoTypes() { - return (ArrayList) sSupportedVideoTypes.clone(); - } - - @SuppressWarnings("unchecked") - public static ArrayList getSupportedTypes() { - return (ArrayList) sSupportedContentTypes.clone(); - } -} diff --git a/mms-common/java/com/android/mmscommon/EncodedStringValue.java b/mms-common/java/com/android/mmscommon/EncodedStringValue.java deleted file mode 100644 index 0a4424e28..000000000 --- a/mms-common/java/com/android/mmscommon/EncodedStringValue.java +++ /dev/null @@ -1,284 +0,0 @@ -/* - * Copyright (C) 2007-2008 Esmertec AG. - * Copyright (C) 2007-2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.mmscommon; - -import android.util.Config; -import android.util.Log; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.util.ArrayList; - -/** - * Encoded-string-value = Text-string | Value-length Char-set Text-string - */ -public class EncodedStringValue implements Cloneable { - private static final String TAG = "EncodedStringValue"; - private static final boolean DEBUG = false; - private static final boolean LOCAL_LOGV = DEBUG ? Config.LOGD : Config.LOGV; - - /** - * The Char-set value. - */ - private int mCharacterSet; - - /** - * The Text-string value. - */ - private byte[] mData; - - /** - * Constructor. - * - * @param charset the Char-set value - * @param data the Text-string value - * @throws NullPointerException if Text-string value is null. - */ - public EncodedStringValue(int charset, byte[] data) { - // TODO: CharSet needs to be validated against MIBEnum. - if(null == data) { - throw new NullPointerException("EncodedStringValue: Text-string is null."); - } - - mCharacterSet = charset; - mData = new byte[data.length]; - System.arraycopy(data, 0, mData, 0, data.length); - } - - /** - * Constructor. - * - * @param data the Text-string value - * @throws NullPointerException if Text-string value is null. - */ - public EncodedStringValue(byte[] data) { - this(CharacterSets.DEFAULT_CHARSET, data); - } - - public EncodedStringValue(String data) { - try { - mData = data.getBytes(CharacterSets.DEFAULT_CHARSET_NAME); - mCharacterSet = CharacterSets.DEFAULT_CHARSET; - } catch (UnsupportedEncodingException e) { - Log.e(TAG, "Default encoding must be supported.", e); - } - } - - /** - * Get Char-set value. - * - * @return the value - */ - public int getCharacterSet() { - return mCharacterSet; - } - - /** - * Set Char-set value. - * - * @param charset the Char-set value - */ - public void setCharacterSet(int charset) { - // TODO: CharSet needs to be validated against MIBEnum. - mCharacterSet = charset; - } - - /** - * Get Text-string value. - * - * @return the value - */ - public byte[] getTextString() { - byte[] byteArray = new byte[mData.length]; - - System.arraycopy(mData, 0, byteArray, 0, mData.length); - return byteArray; - } - - /** - * Set Text-string value. - * - * @param textString the Text-string value - * @throws NullPointerException if Text-string value is null. - */ - public void setTextString(byte[] textString) { - if(null == textString) { - throw new NullPointerException("EncodedStringValue: Text-string is null."); - } - - mData = new byte[textString.length]; - System.arraycopy(textString, 0, mData, 0, textString.length); - } - - /** - * Convert this object to a {@link java.lang.String}. If the encoding of - * the EncodedStringValue is null or unsupported, it will be - * treated as iso-8859-1 encoding. - * - * @return The decoded String. - */ - public String getString() { - if (CharacterSets.ANY_CHARSET == mCharacterSet) { - return new String(mData); // system default encoding. - } else { - try { - String name = CharacterSets.getMimeName(mCharacterSet); - return new String(mData, name); - } catch (UnsupportedEncodingException e) { - if (LOCAL_LOGV) { - Log.v(TAG, e.getMessage(), e); - } - try { - return new String(mData, CharacterSets.MIMENAME_ISO_8859_1); - } catch (UnsupportedEncodingException _) { - return new String(mData); // system default encoding. - } - } - } - } - - /** - * Append to Text-string. - * - * @param textString the textString to append - * @throws NullPointerException if the text String is null - * or an IOException occured. - */ - public void appendTextString(byte[] textString) { - if(null == textString) { - throw new NullPointerException("Text-string is null."); - } - - if(null == mData) { - mData = new byte[textString.length]; - System.arraycopy(textString, 0, mData, 0, textString.length); - } else { - ByteArrayOutputStream newTextString = new ByteArrayOutputStream(); - try { - newTextString.write(mData); - newTextString.write(textString); - } catch (IOException e) { - e.printStackTrace(); - throw new NullPointerException( - "appendTextString: failed when write a new Text-string"); - } - - mData = newTextString.toByteArray(); - } - } - - /* - * (non-Javadoc) - * @see java.lang.Object#clone() - */ - @Override - public Object clone() throws CloneNotSupportedException { - super.clone(); - int len = mData.length; - byte[] dstBytes = new byte[len]; - System.arraycopy(mData, 0, dstBytes, 0, len); - - try { - return new EncodedStringValue(mCharacterSet, dstBytes); - } catch (Exception e) { - Log.e(TAG, "failed to clone an EncodedStringValue: " + this); - e.printStackTrace(); - throw new CloneNotSupportedException(e.getMessage()); - } - } - - /** - * Split this encoded string around matches of the given pattern. - * - * @param pattern the delimiting pattern - * @return the array of encoded strings computed by splitting this encoded - * string around matches of the given pattern - */ - public EncodedStringValue[] split(String pattern) { - String[] temp = getString().split(pattern); - EncodedStringValue[] ret = new EncodedStringValue[temp.length]; - for (int i = 0; i < ret.length; ++i) { - try { - ret[i] = new EncodedStringValue(mCharacterSet, - temp[i].getBytes()); - } catch (NullPointerException _) { - // Can't arrive here - return null; - } - } - return ret; - } - - /** - * Extract an EncodedStringValue[] from a given String. - */ - public static EncodedStringValue[] extract(String src) { - String[] values = src.split(";"); - - ArrayList list = new ArrayList(); - for (int i = 0; i < values.length; i++) { - if (values[i].length() > 0) { - list.add(new EncodedStringValue(values[i])); - } - } - - int len = list.size(); - if (len > 0) { - return list.toArray(new EncodedStringValue[len]); - } else { - return null; - } - } - - /** - * Concatenate an EncodedStringValue[] into a single String. - */ - public static String concat(EncodedStringValue[] addr) { - StringBuilder sb = new StringBuilder(); - int maxIndex = addr.length - 1; - for (int i = 0; i <= maxIndex; i++) { - sb.append(addr[i].getString()); - if (i < maxIndex) { - sb.append(";"); - } - } - - return sb.toString(); - } - - public static EncodedStringValue copy(EncodedStringValue value) { - if (value == null) { - return null; - } - - return new EncodedStringValue(value.mCharacterSet, value.mData); - } - - public static EncodedStringValue[] encodeStrings(String[] array) { - int count = array.length; - if (count > 0) { - EncodedStringValue[] encodedArray = new EncodedStringValue[count]; - for (int i = 0; i < count; i++) { - encodedArray[i] = new EncodedStringValue(array[i]); - } - return encodedArray; - } - return null; - } -} diff --git a/mms-common/java/com/android/mmscommon/InvalidHeaderValueException.java b/mms-common/java/com/android/mmscommon/InvalidHeaderValueException.java deleted file mode 100644 index 34d5871ec..000000000 --- a/mms-common/java/com/android/mmscommon/InvalidHeaderValueException.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2007 Esmertec AG. - * 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. - */ - -package com.android.mmscommon; - -/** - * Thrown when an invalid header value was set. - */ -public class InvalidHeaderValueException extends MmsException { - private static final long serialVersionUID = -2053384496042052262L; - - /** - * Constructs an InvalidHeaderValueException with no detailed message. - */ - public InvalidHeaderValueException() { - super(); - } - - /** - * Constructs an InvalidHeaderValueException with the specified detailed message. - * - * @param message the detailed message. - */ - public InvalidHeaderValueException(String message) { - super(message); - } -} diff --git a/mms-common/java/com/android/mmscommon/MmsException.java b/mms-common/java/com/android/mmscommon/MmsException.java deleted file mode 100644 index 296a2c346..000000000 --- a/mms-common/java/com/android/mmscommon/MmsException.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (C) 2007 Esmertec AG. - * 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. - */ - -package com.android.mmscommon; - -/** - * A generic exception that is thrown by the Mms client. - */ -public class MmsException extends Exception { - private static final long serialVersionUID = -7323249827281485390L; - - /** - * Creates a new MmsException. - */ - public MmsException() { - super(); - } - - /** - * Creates a new MmsException with the specified detail message. - * - * @param message the detail message. - */ - public MmsException(String message) { - super(message); - } - - /** - * Creates a new MmsException with the specified cause. - * - * @param cause the cause. - */ - public MmsException(Throwable cause) { - super(cause); - } - - /** - * Creates a new MmsException with the specified detail message and cause. - * - * @param message the detail message. - * @param cause the cause. - */ - public MmsException(String message, Throwable cause) { - super(message, cause); - } -} diff --git a/mms-common/java/com/android/mmscommon/PduHeaders.java b/mms-common/java/com/android/mmscommon/PduHeaders.java deleted file mode 100644 index d8f12111b..000000000 --- a/mms-common/java/com/android/mmscommon/PduHeaders.java +++ /dev/null @@ -1,719 +0,0 @@ -/* - * Copyright (C) 2007 Esmertec AG. - * 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. - */ - -package com.android.mmscommon; - -import java.util.ArrayList; -import java.util.HashMap; - -public class PduHeaders { - /** - * All pdu header fields. - */ - public static final int BCC = 0x81; - public static final int CC = 0x82; - public static final int CONTENT_LOCATION = 0x83; - public static final int CONTENT_TYPE = 0x84; - public static final int DATE = 0x85; - public static final int DELIVERY_REPORT = 0x86; - public static final int DELIVERY_TIME = 0x87; - public static final int EXPIRY = 0x88; - public static final int FROM = 0x89; - public static final int MESSAGE_CLASS = 0x8A; - public static final int MESSAGE_ID = 0x8B; - public static final int MESSAGE_TYPE = 0x8C; - public static final int MMS_VERSION = 0x8D; - public static final int MESSAGE_SIZE = 0x8E; - public static final int PRIORITY = 0x8F; - - public static final int READ_REPLY = 0x90; - public static final int READ_REPORT = 0x90; - public static final int REPORT_ALLOWED = 0x91; - public static final int RESPONSE_STATUS = 0x92; - public static final int RESPONSE_TEXT = 0x93; - public static final int SENDER_VISIBILITY = 0x94; - public static final int STATUS = 0x95; - public static final int SUBJECT = 0x96; - public static final int TO = 0x97; - public static final int TRANSACTION_ID = 0x98; - public static final int RETRIEVE_STATUS = 0x99; - public static final int RETRIEVE_TEXT = 0x9A; - public static final int READ_STATUS = 0x9B; - public static final int REPLY_CHARGING = 0x9C; - public static final int REPLY_CHARGING_DEADLINE = 0x9D; - public static final int REPLY_CHARGING_ID = 0x9E; - public static final int REPLY_CHARGING_SIZE = 0x9F; - - public static final int PREVIOUSLY_SENT_BY = 0xA0; - public static final int PREVIOUSLY_SENT_DATE = 0xA1; - public static final int STORE = 0xA2; - public static final int MM_STATE = 0xA3; - public static final int MM_FLAGS = 0xA4; - public static final int STORE_STATUS = 0xA5; - public static final int STORE_STATUS_TEXT = 0xA6; - public static final int STORED = 0xA7; - public static final int ATTRIBUTES = 0xA8; - public static final int TOTALS = 0xA9; - public static final int MBOX_TOTALS = 0xAA; - public static final int QUOTAS = 0xAB; - public static final int MBOX_QUOTAS = 0xAC; - public static final int MESSAGE_COUNT = 0xAD; - public static final int CONTENT = 0xAE; - public static final int START = 0xAF; - - public static final int ADDITIONAL_HEADERS = 0xB0; - public static final int DISTRIBUTION_INDICATOR = 0xB1; - public static final int ELEMENT_DESCRIPTOR = 0xB2; - public static final int LIMIT = 0xB3; - public static final int RECOMMENDED_RETRIEVAL_MODE = 0xB4; - public static final int RECOMMENDED_RETRIEVAL_MODE_TEXT = 0xB5; - public static final int STATUS_TEXT = 0xB6; - public static final int APPLIC_ID = 0xB7; - public static final int REPLY_APPLIC_ID = 0xB8; - public static final int AUX_APPLIC_ID = 0xB9; - public static final int CONTENT_CLASS = 0xBA; - public static final int DRM_CONTENT = 0xBB; - public static final int ADAPTATION_ALLOWED = 0xBC; - public static final int REPLACE_ID = 0xBD; - public static final int CANCEL_ID = 0xBE; - public static final int CANCEL_STATUS = 0xBF; - - /** - * X-Mms-Message-Type field types. - */ - public static final int MESSAGE_TYPE_SEND_REQ = 0x80; - public static final int MESSAGE_TYPE_SEND_CONF = 0x81; - public static final int MESSAGE_TYPE_NOTIFICATION_IND = 0x82; - public static final int MESSAGE_TYPE_NOTIFYRESP_IND = 0x83; - public static final int MESSAGE_TYPE_RETRIEVE_CONF = 0x84; - public static final int MESSAGE_TYPE_ACKNOWLEDGE_IND = 0x85; - public static final int MESSAGE_TYPE_DELIVERY_IND = 0x86; - public static final int MESSAGE_TYPE_READ_REC_IND = 0x87; - public static final int MESSAGE_TYPE_READ_ORIG_IND = 0x88; - public static final int MESSAGE_TYPE_FORWARD_REQ = 0x89; - public static final int MESSAGE_TYPE_FORWARD_CONF = 0x8A; - public static final int MESSAGE_TYPE_MBOX_STORE_REQ = 0x8B; - public static final int MESSAGE_TYPE_MBOX_STORE_CONF = 0x8C; - public static final int MESSAGE_TYPE_MBOX_VIEW_REQ = 0x8D; - public static final int MESSAGE_TYPE_MBOX_VIEW_CONF = 0x8E; - public static final int MESSAGE_TYPE_MBOX_UPLOAD_REQ = 0x8F; - public static final int MESSAGE_TYPE_MBOX_UPLOAD_CONF = 0x90; - public static final int MESSAGE_TYPE_MBOX_DELETE_REQ = 0x91; - public static final int MESSAGE_TYPE_MBOX_DELETE_CONF = 0x92; - public static final int MESSAGE_TYPE_MBOX_DESCR = 0x93; - public static final int MESSAGE_TYPE_DELETE_REQ = 0x94; - public static final int MESSAGE_TYPE_DELETE_CONF = 0x95; - public static final int MESSAGE_TYPE_CANCEL_REQ = 0x96; - public static final int MESSAGE_TYPE_CANCEL_CONF = 0x97; - - /** - * X-Mms-Delivery-Report | - * X-Mms-Read-Report | - * X-Mms-Report-Allowed | - * X-Mms-Sender-Visibility | - * X-Mms-Store | - * X-Mms-Stored | - * X-Mms-Totals | - * X-Mms-Quotas | - * X-Mms-Distribution-Indicator | - * X-Mms-DRM-Content | - * X-Mms-Adaptation-Allowed | - * field types. - */ - public static final int VALUE_YES = 0x80; - public static final int VALUE_NO = 0x81; - - /** - * Delivery-Time | - * Expiry and Reply-Charging-Deadline | - * field type components. - */ - public static final int VALUE_ABSOLUTE_TOKEN = 0x80; - public static final int VALUE_RELATIVE_TOKEN = 0x81; - - /** - * X-Mms-MMS-Version field types. - */ - public static final int MMS_VERSION_1_3 = ((1 << 4) | 3); - public static final int MMS_VERSION_1_2 = ((1 << 4) | 2); - public static final int MMS_VERSION_1_1 = ((1 << 4) | 1); - public static final int MMS_VERSION_1_0 = ((1 << 4) | 0); - - // Current version is 1.2. - public static final int CURRENT_MMS_VERSION = MMS_VERSION_1_2; - - /** - * From field type components. - */ - public static final int FROM_ADDRESS_PRESENT_TOKEN = 0x80; - public static final int FROM_INSERT_ADDRESS_TOKEN = 0x81; - - public static final String FROM_ADDRESS_PRESENT_TOKEN_STR = "address-present-token"; - public static final String FROM_INSERT_ADDRESS_TOKEN_STR = "insert-address-token"; - - /** - * X-Mms-Status Field. - */ - public static final int STATUS_EXPIRED = 0x80; - public static final int STATUS_RETRIEVED = 0x81; - public static final int STATUS_REJECTED = 0x82; - public static final int STATUS_DEFERRED = 0x83; - public static final int STATUS_UNRECOGNIZED = 0x84; - public static final int STATUS_INDETERMINATE = 0x85; - public static final int STATUS_FORWARDED = 0x86; - public static final int STATUS_UNREACHABLE = 0x87; - - /** - * MM-Flags field type components. - */ - public static final int MM_FLAGS_ADD_TOKEN = 0x80; - public static final int MM_FLAGS_REMOVE_TOKEN = 0x81; - public static final int MM_FLAGS_FILTER_TOKEN = 0x82; - - /** - * X-Mms-Message-Class field types. - */ - public static final int MESSAGE_CLASS_PERSONAL = 0x80; - public static final int MESSAGE_CLASS_ADVERTISEMENT = 0x81; - public static final int MESSAGE_CLASS_INFORMATIONAL = 0x82; - public static final int MESSAGE_CLASS_AUTO = 0x83; - - public static final String MESSAGE_CLASS_PERSONAL_STR = "personal"; - public static final String MESSAGE_CLASS_ADVERTISEMENT_STR = "advertisement"; - public static final String MESSAGE_CLASS_INFORMATIONAL_STR = "informational"; - public static final String MESSAGE_CLASS_AUTO_STR = "auto"; - - /** - * X-Mms-Priority field types. - */ - public static final int PRIORITY_LOW = 0x80; - public static final int PRIORITY_NORMAL = 0x81; - public static final int PRIORITY_HIGH = 0x82; - - /** - * X-Mms-Response-Status field types. - */ - public static final int RESPONSE_STATUS_OK = 0x80; - public static final int RESPONSE_STATUS_ERROR_UNSPECIFIED = 0x81; - public static final int RESPONSE_STATUS_ERROR_SERVICE_DENIED = 0x82; - - public static final int RESPONSE_STATUS_ERROR_MESSAGE_FORMAT_CORRUPT = 0x83; - public static final int RESPONSE_STATUS_ERROR_SENDING_ADDRESS_UNRESOLVED = 0x84; - - public static final int RESPONSE_STATUS_ERROR_MESSAGE_NOT_FOUND = 0x85; - public static final int RESPONSE_STATUS_ERROR_NETWORK_PROBLEM = 0x86; - public static final int RESPONSE_STATUS_ERROR_CONTENT_NOT_ACCEPTED = 0x87; - public static final int RESPONSE_STATUS_ERROR_UNSUPPORTED_MESSAGE = 0x88; - public static final int RESPONSE_STATUS_ERROR_TRANSIENT_FAILURE = 0xC0; - - public static final int RESPONSE_STATUS_ERROR_TRANSIENT_SENDNG_ADDRESS_UNRESOLVED = 0xC1; - public static final int RESPONSE_STATUS_ERROR_TRANSIENT_MESSAGE_NOT_FOUND = 0xC2; - public static final int RESPONSE_STATUS_ERROR_TRANSIENT_NETWORK_PROBLEM = 0xC3; - public static final int RESPONSE_STATUS_ERROR_TRANSIENT_PARTIAL_SUCCESS = 0xC4; - - public static final int RESPONSE_STATUS_ERROR_PERMANENT_FAILURE = 0xE0; - public static final int RESPONSE_STATUS_ERROR_PERMANENT_SERVICE_DENIED = 0xE1; - public static final int RESPONSE_STATUS_ERROR_PERMANENT_MESSAGE_FORMAT_CORRUPT = 0xE2; - public static final int RESPONSE_STATUS_ERROR_PERMANENT_SENDING_ADDRESS_UNRESOLVED = 0xE3; - public static final int RESPONSE_STATUS_ERROR_PERMANENT_MESSAGE_NOT_FOUND = 0xE4; - public static final int RESPONSE_STATUS_ERROR_PERMANENT_CONTENT_NOT_ACCEPTED = 0xE5; - public static final int RESPONSE_STATUS_ERROR_PERMANENT_REPLY_CHARGING_LIMITATIONS_NOT_MET = 0xE6; - public static final int RESPONSE_STATUS_ERROR_PERMANENT_REPLY_CHARGING_REQUEST_NOT_ACCEPTED = 0xE6; - public static final int RESPONSE_STATUS_ERROR_PERMANENT_REPLY_CHARGING_FORWARDING_DENIED = 0xE8; - public static final int RESPONSE_STATUS_ERROR_PERMANENT_REPLY_CHARGING_NOT_SUPPORTED = 0xE9; - public static final int RESPONSE_STATUS_ERROR_PERMANENT_ADDRESS_HIDING_NOT_SUPPORTED = 0xEA; - public static final int RESPONSE_STATUS_ERROR_PERMANENT_LACK_OF_PREPAID = 0xEB; - public static final int RESPONSE_STATUS_ERROR_PERMANENT_END = 0xFF; - - /** - * X-Mms-Retrieve-Status field types. - */ - public static final int RETRIEVE_STATUS_OK = 0x80; - public static final int RETRIEVE_STATUS_ERROR_TRANSIENT_FAILURE = 0xC0; - public static final int RETRIEVE_STATUS_ERROR_TRANSIENT_MESSAGE_NOT_FOUND = 0xC1; - public static final int RETRIEVE_STATUS_ERROR_TRANSIENT_NETWORK_PROBLEM = 0xC2; - public static final int RETRIEVE_STATUS_ERROR_PERMANENT_FAILURE = 0xE0; - public static final int RETRIEVE_STATUS_ERROR_PERMANENT_SERVICE_DENIED = 0xE1; - public static final int RETRIEVE_STATUS_ERROR_PERMANENT_MESSAGE_NOT_FOUND = 0xE2; - public static final int RETRIEVE_STATUS_ERROR_PERMANENT_CONTENT_UNSUPPORTED = 0xE3; - public static final int RETRIEVE_STATUS_ERROR_END = 0xFF; - - /** - * X-Mms-Sender-Visibility field types. - */ - public static final int SENDER_VISIBILITY_HIDE = 0x80; - public static final int SENDER_VISIBILITY_SHOW = 0x81; - - /** - * X-Mms-Read-Status field types. - */ - public static final int READ_STATUS_READ = 0x80; - public static final int READ_STATUS__DELETED_WITHOUT_BEING_READ = 0x81; - - /** - * X-Mms-Cancel-Status field types. - */ - public static final int CANCEL_STATUS_REQUEST_SUCCESSFULLY_RECEIVED = 0x80; - public static final int CANCEL_STATUS_REQUEST_CORRUPTED = 0x81; - - /** - * X-Mms-Reply-Charging field types. - */ - public static final int REPLY_CHARGING_REQUESTED = 0x80; - public static final int REPLY_CHARGING_REQUESTED_TEXT_ONLY = 0x81; - public static final int REPLY_CHARGING_ACCEPTED = 0x82; - public static final int REPLY_CHARGING_ACCEPTED_TEXT_ONLY = 0x83; - - /** - * X-Mms-MM-State field types. - */ - public static final int MM_STATE_DRAFT = 0x80; - public static final int MM_STATE_SENT = 0x81; - public static final int MM_STATE_NEW = 0x82; - public static final int MM_STATE_RETRIEVED = 0x83; - public static final int MM_STATE_FORWARDED = 0x84; - - /** - * X-Mms-Recommended-Retrieval-Mode field types. - */ - public static final int RECOMMENDED_RETRIEVAL_MODE_MANUAL = 0x80; - - /** - * X-Mms-Content-Class field types. - */ - public static final int CONTENT_CLASS_TEXT = 0x80; - public static final int CONTENT_CLASS_IMAGE_BASIC = 0x81; - public static final int CONTENT_CLASS_IMAGE_RICH = 0x82; - public static final int CONTENT_CLASS_VIDEO_BASIC = 0x83; - public static final int CONTENT_CLASS_VIDEO_RICH = 0x84; - public static final int CONTENT_CLASS_MEGAPIXEL = 0x85; - public static final int CONTENT_CLASS_CONTENT_BASIC = 0x86; - public static final int CONTENT_CLASS_CONTENT_RICH = 0x87; - - /** - * X-Mms-Store-Status field types. - */ - public static final int STORE_STATUS_SUCCESS = 0x80; - public static final int STORE_STATUS_ERROR_TRANSIENT_FAILURE = 0xC0; - public static final int STORE_STATUS_ERROR_TRANSIENT_NETWORK_PROBLEM = 0xC1; - public static final int STORE_STATUS_ERROR_PERMANENT_FAILURE = 0xE0; - public static final int STORE_STATUS_ERROR_PERMANENT_SERVICE_DENIED = 0xE1; - public static final int STORE_STATUS_ERROR_PERMANENT_MESSAGE_FORMAT_CORRUPT = 0xE2; - public static final int STORE_STATUS_ERROR_PERMANENT_MESSAGE_NOT_FOUND = 0xE3; - public static final int STORE_STATUS_ERROR_PERMANENT_MMBOX_FULL = 0xE4; - public static final int STORE_STATUS_ERROR_END = 0xFF; - - /** - * The map contains the value of all headers. - */ - private HashMap mHeaderMap = null; - - /** - * Constructor of PduHeaders. - */ - public PduHeaders() { - mHeaderMap = new HashMap(); - } - - /** - * Get octet value by header field. - * - * @param field the field - * @return the octet value of the pdu header - * with specified header field. Return 0 if - * the value is not set. - */ - public int getOctet(int field) { - Integer octet = (Integer) mHeaderMap.get(field); - if (null == octet) { - return 0; - } - - return octet; - } - - /** - * Set octet value to pdu header by header field. - * - * @param value the value - * @param field the field - * @throws InvalidHeaderValueException if the value is invalid. - */ - public void setOctet(int value, int field) - throws InvalidHeaderValueException{ - /** - * Check whether this field can be set for specific - * header and check validity of the field. - */ - switch (field) { - case REPORT_ALLOWED: - case ADAPTATION_ALLOWED: - case DELIVERY_REPORT: - case DRM_CONTENT: - case DISTRIBUTION_INDICATOR: - case QUOTAS: - case READ_REPORT: - case STORE: - case STORED: - case TOTALS: - case SENDER_VISIBILITY: - if ((VALUE_YES != value) && (VALUE_NO != value)) { - // Invalid value. - throw new InvalidHeaderValueException("Invalid Octet value!"); - } - break; - case READ_STATUS: - if ((READ_STATUS_READ != value) && - (READ_STATUS__DELETED_WITHOUT_BEING_READ != value)) { - // Invalid value. - throw new InvalidHeaderValueException("Invalid Octet value!"); - } - break; - case CANCEL_STATUS: - if ((CANCEL_STATUS_REQUEST_SUCCESSFULLY_RECEIVED != value) && - (CANCEL_STATUS_REQUEST_CORRUPTED != value)) { - // Invalid value. - throw new InvalidHeaderValueException("Invalid Octet value!"); - } - break; - case PRIORITY: - if ((value < PRIORITY_LOW) || (value > PRIORITY_HIGH)) { - // Invalid value. - throw new InvalidHeaderValueException("Invalid Octet value!"); - } - break; - case STATUS: - if ((value < STATUS_EXPIRED) || (value > STATUS_UNREACHABLE)) { - // Invalid value. - throw new InvalidHeaderValueException("Invalid Octet value!"); - } - break; - case REPLY_CHARGING: - if ((value < REPLY_CHARGING_REQUESTED) - || (value > REPLY_CHARGING_ACCEPTED_TEXT_ONLY)) { - // Invalid value. - throw new InvalidHeaderValueException("Invalid Octet value!"); - } - break; - case MM_STATE: - if ((value < MM_STATE_DRAFT) || (value > MM_STATE_FORWARDED)) { - // Invalid value. - throw new InvalidHeaderValueException("Invalid Octet value!"); - } - break; - case RECOMMENDED_RETRIEVAL_MODE: - if (RECOMMENDED_RETRIEVAL_MODE_MANUAL != value) { - // Invalid value. - throw new InvalidHeaderValueException("Invalid Octet value!"); - } - break; - case CONTENT_CLASS: - if ((value < CONTENT_CLASS_TEXT) - || (value > CONTENT_CLASS_CONTENT_RICH)) { - // Invalid value. - throw new InvalidHeaderValueException("Invalid Octet value!"); - } - break; - case RETRIEVE_STATUS: - // According to oma-ts-mms-enc-v1_3, section 7.3.50, we modify the invalid value. - if ((value > RETRIEVE_STATUS_ERROR_TRANSIENT_NETWORK_PROBLEM) && - (value < RETRIEVE_STATUS_ERROR_PERMANENT_FAILURE)) { - value = RETRIEVE_STATUS_ERROR_TRANSIENT_FAILURE; - } else if ((value > RETRIEVE_STATUS_ERROR_PERMANENT_CONTENT_UNSUPPORTED) && - (value <= RETRIEVE_STATUS_ERROR_END)) { - value = RETRIEVE_STATUS_ERROR_PERMANENT_FAILURE; - } else if ((value < RETRIEVE_STATUS_OK) || - ((value > RETRIEVE_STATUS_OK) && - (value < RETRIEVE_STATUS_ERROR_TRANSIENT_FAILURE)) || - (value > RETRIEVE_STATUS_ERROR_END)) { - value = RETRIEVE_STATUS_ERROR_PERMANENT_FAILURE; - } - break; - case STORE_STATUS: - // According to oma-ts-mms-enc-v1_3, section 7.3.58, we modify the invalid value. - if ((value > STORE_STATUS_ERROR_TRANSIENT_NETWORK_PROBLEM) && - (value < STORE_STATUS_ERROR_PERMANENT_FAILURE)) { - value = STORE_STATUS_ERROR_TRANSIENT_FAILURE; - } else if ((value > STORE_STATUS_ERROR_PERMANENT_MMBOX_FULL) && - (value <= STORE_STATUS_ERROR_END)) { - value = STORE_STATUS_ERROR_PERMANENT_FAILURE; - } else if ((value < STORE_STATUS_SUCCESS) || - ((value > STORE_STATUS_SUCCESS) && - (value < STORE_STATUS_ERROR_TRANSIENT_FAILURE)) || - (value > STORE_STATUS_ERROR_END)) { - value = STORE_STATUS_ERROR_PERMANENT_FAILURE; - } - break; - case RESPONSE_STATUS: - // According to oma-ts-mms-enc-v1_3, section 7.3.48, we modify the invalid value. - if ((value > RESPONSE_STATUS_ERROR_TRANSIENT_PARTIAL_SUCCESS) && - (value < RESPONSE_STATUS_ERROR_PERMANENT_FAILURE)) { - value = RESPONSE_STATUS_ERROR_TRANSIENT_FAILURE; - } else if (((value > RESPONSE_STATUS_ERROR_PERMANENT_LACK_OF_PREPAID) && - (value <= RESPONSE_STATUS_ERROR_PERMANENT_END)) || - (value < RESPONSE_STATUS_OK) || - ((value > RESPONSE_STATUS_ERROR_UNSUPPORTED_MESSAGE) && - (value < RESPONSE_STATUS_ERROR_TRANSIENT_FAILURE)) || - (value > RESPONSE_STATUS_ERROR_PERMANENT_END)) { - value = RESPONSE_STATUS_ERROR_PERMANENT_FAILURE; - } - break; - case MMS_VERSION: - if ((value < MMS_VERSION_1_0)|| (value > MMS_VERSION_1_3)) { - value = CURRENT_MMS_VERSION; // Current version is the default value. - } - break; - case MESSAGE_TYPE: - if ((value < MESSAGE_TYPE_SEND_REQ) || (value > MESSAGE_TYPE_CANCEL_CONF)) { - // Invalid value. - throw new InvalidHeaderValueException("Invalid Octet value!"); - } - break; - default: - // This header value should not be Octect. - throw new RuntimeException("Invalid header field!"); - } - mHeaderMap.put(field, value); - } - - /** - * Get TextString value by header field. - * - * @param field the field - * @return the TextString value of the pdu header - * with specified header field - */ - public byte[] getTextString(int field) { - return (byte[]) mHeaderMap.get(field); - } - - /** - * Set TextString value to pdu header by header field. - * - * @param value the value - * @param field the field - * @return the TextString value of the pdu header - * with specified header field - * @throws NullPointerException if the value is null. - */ - public void setTextString(byte[] value, int field) { - /** - * Check whether this field can be set for specific - * header and check validity of the field. - */ - if (null == value) { - throw new NullPointerException(); - } - - switch (field) { - case TRANSACTION_ID: - case REPLY_CHARGING_ID: - case AUX_APPLIC_ID: - case APPLIC_ID: - case REPLY_APPLIC_ID: - case MESSAGE_ID: - case REPLACE_ID: - case CANCEL_ID: - case CONTENT_LOCATION: - case MESSAGE_CLASS: - case CONTENT_TYPE: - break; - default: - // This header value should not be Text-String. - throw new RuntimeException("Invalid header field!"); - } - mHeaderMap.put(field, value); - } - - /** - * Get EncodedStringValue value by header field. - * - * @param field the field - * @return the EncodedStringValue value of the pdu header - * with specified header field - */ - public EncodedStringValue getEncodedStringValue(int field) { - return (EncodedStringValue) mHeaderMap.get(field); - } - - /** - * Get TO, CC or BCC header value. - * - * @param field the field - * @return the EncodeStringValue array of the pdu header - * with specified header field - */ - public EncodedStringValue[] getEncodedStringValues(int field) { - ArrayList list = - (ArrayList) mHeaderMap.get(field); - if (null == list) { - return null; - } - EncodedStringValue[] values = new EncodedStringValue[list.size()]; - return list.toArray(values); - } - - /** - * Set EncodedStringValue value to pdu header by header field. - * - * @param value the value - * @param field the field - * @return the EncodedStringValue value of the pdu header - * with specified header field - * @throws NullPointerException if the value is null. - */ - public void setEncodedStringValue(EncodedStringValue value, int field) { - /** - * Check whether this field can be set for specific - * header and check validity of the field. - */ - if (null == value) { - throw new NullPointerException(); - } - - switch (field) { - case SUBJECT: - case RECOMMENDED_RETRIEVAL_MODE_TEXT: - case RETRIEVE_TEXT: - case STATUS_TEXT: - case STORE_STATUS_TEXT: - case RESPONSE_TEXT: - case FROM: - case PREVIOUSLY_SENT_BY: - case MM_FLAGS: - break; - default: - // This header value should not be Encoded-String-Value. - throw new RuntimeException("Invalid header field!"); - } - - mHeaderMap.put(field, value); - } - - /** - * Set TO, CC or BCC header value. - * - * @param value the value - * @param field the field - * @return the EncodedStringValue value array of the pdu header - * with specified header field - * @throws NullPointerException if the value is null. - */ - public void setEncodedStringValues(EncodedStringValue[] value, int field) { - /** - * Check whether this field can be set for specific - * header and check validity of the field. - */ - if (null == value) { - throw new NullPointerException(); - } - - switch (field) { - case BCC: - case CC: - case TO: - break; - default: - // This header value should not be Encoded-String-Value. - throw new RuntimeException("Invalid header field!"); - } - - ArrayList list = new ArrayList(); - for (int i = 0; i < value.length; i++) { - list.add(value[i]); - } - mHeaderMap.put(field, list); - } - - /** - * Append one EncodedStringValue to another. - * - * @param value the EncodedStringValue to append - * @param field the field - * @throws NullPointerException if the value is null. - */ - public void appendEncodedStringValue(EncodedStringValue value, - int field) { - if (null == value) { - throw new NullPointerException(); - } - - switch (field) { - case BCC: - case CC: - case TO: - break; - default: - throw new RuntimeException("Invalid header field!"); - } - - ArrayList list = - (ArrayList) mHeaderMap.get(field); - if (null == list) { - list = new ArrayList(); - } - list.add(value); - mHeaderMap.put(field, list); - } - - /** - * Get LongInteger value by header field. - * - * @param field the field - * @return the LongInteger value of the pdu header - * with specified header field. if return -1, the - * field is not existed in pdu header. - */ - public long getLongInteger(int field) { - Long longInteger = (Long) mHeaderMap.get(field); - if (null == longInteger) { - return -1; - } - - return longInteger.longValue(); - } - - /** - * Set LongInteger value to pdu header by header field. - * - * @param value the value - * @param field the field - */ - public void setLongInteger(long value, int field) { - /** - * Check whether this field can be set for specific - * header and check validity of the field. - */ - switch (field) { - case DATE: - case REPLY_CHARGING_SIZE: - case MESSAGE_SIZE: - case MESSAGE_COUNT: - case START: - case LIMIT: - case DELIVERY_TIME: - case EXPIRY: - case REPLY_CHARGING_DEADLINE: - case PREVIOUSLY_SENT_DATE: - break; - default: - // This header value should not be LongInteger. - throw new RuntimeException("Invalid header field!"); - } - mHeaderMap.put(field, value); - } -} diff --git a/mms-common/java/com/android/mmscommon/mms/ContentType.java b/mms-common/java/com/android/mmscommon/mms/ContentType.java deleted file mode 100644 index f21eba8b8..000000000 --- a/mms-common/java/com/android/mmscommon/mms/ContentType.java +++ /dev/null @@ -1,223 +0,0 @@ -/* - * Copyright (C) 2007-2008 Esmertec AG. - * Copyright (C) 2007-2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.mmscommon.mms; - -import java.util.ArrayList; - -public class ContentType { - public static final String MMS_MESSAGE = "application/vnd.wap.mms-message"; - // The phony content type for generic PDUs (e.g. ReadOrig.ind, - // Notification.ind, Delivery.ind). - public static final String MMS_GENERIC = "application/vnd.wap.mms-generic"; - public static final String MULTIPART_MIXED = "application/vnd.wap.multipart.mixed"; - public static final String MULTIPART_RELATED = "application/vnd.wap.multipart.related"; - public static final String MULTIPART_ALTERNATIVE = "application/vnd.wap.multipart.alternative"; - - public static final String TEXT_PLAIN = "text/plain"; - public static final String TEXT_HTML = "text/html"; - public static final String TEXT_VCALENDAR = "text/x-vCalendar"; - public static final String TEXT_VCARD = "text/x-vCard"; - - public static final String IMAGE_UNSPECIFIED = "image/*"; - public static final String IMAGE_JPEG = "image/jpeg"; - public static final String IMAGE_JPG = "image/jpg"; - public static final String IMAGE_GIF = "image/gif"; - public static final String IMAGE_WBMP = "image/vnd.wap.wbmp"; - public static final String IMAGE_PNG = "image/png"; - - public static final String AUDIO_UNSPECIFIED = "audio/*"; - public static final String AUDIO_AAC = "audio/aac"; - public static final String AUDIO_AMR = "audio/amr"; - public static final String AUDIO_IMELODY = "audio/imelody"; - public static final String AUDIO_MID = "audio/mid"; - public static final String AUDIO_MIDI = "audio/midi"; - public static final String AUDIO_MP3 = "audio/mp3"; - public static final String AUDIO_MPEG3 = "audio/mpeg3"; - public static final String AUDIO_MPEG = "audio/mpeg"; - public static final String AUDIO_MPG = "audio/mpg"; - public static final String AUDIO_MP4 = "audio/mp4"; - public static final String AUDIO_X_MID = "audio/x-mid"; - public static final String AUDIO_X_MIDI = "audio/x-midi"; - public static final String AUDIO_X_MP3 = "audio/x-mp3"; - public static final String AUDIO_X_MPEG3 = "audio/x-mpeg3"; - public static final String AUDIO_X_MPEG = "audio/x-mpeg"; - public static final String AUDIO_X_MPG = "audio/x-mpg"; - public static final String AUDIO_3GPP = "audio/3gpp"; - public static final String AUDIO_OGG = "application/ogg"; - - public static final String VIDEO_UNSPECIFIED = "video/*"; - public static final String VIDEO_3GPP = "video/3gpp"; - public static final String VIDEO_3G2 = "video/3gpp2"; - public static final String VIDEO_H263 = "video/h263"; - public static final String VIDEO_MP4 = "video/mp4"; - - public static final String APP_SMIL = "application/smil"; - public static final String APP_WAP_XHTML = "application/vnd.wap.xhtml+xml"; - public static final String APP_XHTML = "application/xhtml+xml"; - - public static final String APP_DRM_CONTENT = "application/vnd.oma.drm.content"; - public static final String APP_DRM_MESSAGE = "application/vnd.oma.drm.message"; - - private static final ArrayList sSupportedContentTypes = new ArrayList(); - private static final ArrayList sSupportedImageTypes = new ArrayList(); - private static final ArrayList sSupportedAudioTypes = new ArrayList(); - private static final ArrayList sSupportedVideoTypes = new ArrayList(); - - static { - sSupportedContentTypes.add(TEXT_PLAIN); - sSupportedContentTypes.add(TEXT_HTML); - sSupportedContentTypes.add(TEXT_VCALENDAR); - sSupportedContentTypes.add(TEXT_VCARD); - - sSupportedContentTypes.add(IMAGE_JPEG); - sSupportedContentTypes.add(IMAGE_GIF); - sSupportedContentTypes.add(IMAGE_WBMP); - sSupportedContentTypes.add(IMAGE_PNG); - sSupportedContentTypes.add(IMAGE_JPG); - //supportedContentTypes.add(IMAGE_SVG); not yet supported. - - sSupportedContentTypes.add(AUDIO_AAC); - sSupportedContentTypes.add(AUDIO_AMR); - sSupportedContentTypes.add(AUDIO_IMELODY); - sSupportedContentTypes.add(AUDIO_MID); - sSupportedContentTypes.add(AUDIO_MIDI); - sSupportedContentTypes.add(AUDIO_MP3); - sSupportedContentTypes.add(AUDIO_MPEG3); - sSupportedContentTypes.add(AUDIO_MPEG); - sSupportedContentTypes.add(AUDIO_MPG); - sSupportedContentTypes.add(AUDIO_X_MID); - sSupportedContentTypes.add(AUDIO_X_MIDI); - sSupportedContentTypes.add(AUDIO_X_MP3); - sSupportedContentTypes.add(AUDIO_X_MPEG3); - sSupportedContentTypes.add(AUDIO_X_MPEG); - sSupportedContentTypes.add(AUDIO_X_MPG); - sSupportedContentTypes.add(AUDIO_3GPP); - sSupportedContentTypes.add(AUDIO_OGG); - - sSupportedContentTypes.add(VIDEO_3GPP); - sSupportedContentTypes.add(VIDEO_3G2); - sSupportedContentTypes.add(VIDEO_H263); - sSupportedContentTypes.add(VIDEO_MP4); - - sSupportedContentTypes.add(APP_SMIL); - sSupportedContentTypes.add(APP_WAP_XHTML); - sSupportedContentTypes.add(APP_XHTML); - - sSupportedContentTypes.add(APP_DRM_CONTENT); - sSupportedContentTypes.add(APP_DRM_MESSAGE); - - // add supported image types - sSupportedImageTypes.add(IMAGE_JPEG); - sSupportedImageTypes.add(IMAGE_GIF); - sSupportedImageTypes.add(IMAGE_WBMP); - sSupportedImageTypes.add(IMAGE_PNG); - sSupportedImageTypes.add(IMAGE_JPG); - - // add supported audio types - sSupportedAudioTypes.add(AUDIO_AAC); - sSupportedAudioTypes.add(AUDIO_AMR); - sSupportedAudioTypes.add(AUDIO_IMELODY); - sSupportedAudioTypes.add(AUDIO_MID); - sSupportedAudioTypes.add(AUDIO_MIDI); - sSupportedAudioTypes.add(AUDIO_MP3); - sSupportedAudioTypes.add(AUDIO_MPEG3); - sSupportedAudioTypes.add(AUDIO_MPEG); - sSupportedAudioTypes.add(AUDIO_MPG); - sSupportedAudioTypes.add(AUDIO_MP4); - sSupportedAudioTypes.add(AUDIO_X_MID); - sSupportedAudioTypes.add(AUDIO_X_MIDI); - sSupportedAudioTypes.add(AUDIO_X_MP3); - sSupportedAudioTypes.add(AUDIO_X_MPEG3); - sSupportedAudioTypes.add(AUDIO_X_MPEG); - sSupportedAudioTypes.add(AUDIO_X_MPG); - sSupportedAudioTypes.add(AUDIO_3GPP); - sSupportedAudioTypes.add(AUDIO_OGG); - - // add supported video types - sSupportedVideoTypes.add(VIDEO_3GPP); - sSupportedVideoTypes.add(VIDEO_3G2); - sSupportedVideoTypes.add(VIDEO_H263); - sSupportedVideoTypes.add(VIDEO_MP4); - } - - // This class should never be instantiated. - private ContentType() { - } - - public static boolean isSupportedType(String contentType) { - return (null != contentType) && sSupportedContentTypes.contains(contentType); - } - - public static boolean isSupportedImageType(String contentType) { - return isImageType(contentType) && isSupportedType(contentType); - } - - public static boolean isSupportedAudioType(String contentType) { - return isAudioType(contentType) && isSupportedType(contentType); - } - - public static boolean isSupportedVideoType(String contentType) { - return isVideoType(contentType) && isSupportedType(contentType); - } - - public static boolean isTextType(String contentType) { - return (null != contentType) && contentType.startsWith("text/"); - } - - public static boolean isImageType(String contentType) { - return (null != contentType) && contentType.startsWith("image/"); - } - - public static boolean isAudioType(String contentType) { - return (null != contentType) && contentType.startsWith("audio/"); - } - - public static boolean isVideoType(String contentType) { - return (null != contentType) && contentType.startsWith("video/"); - } - - public static boolean isDrmType(String contentType) { - return (null != contentType) - && (contentType.equals(APP_DRM_CONTENT) - || contentType.equals(APP_DRM_MESSAGE)); - } - - public static boolean isUnspecified(String contentType) { - return (null != contentType) && contentType.endsWith("*"); - } - - @SuppressWarnings("unchecked") - public static ArrayList getImageTypes() { - return (ArrayList) sSupportedImageTypes.clone(); - } - - @SuppressWarnings("unchecked") - public static ArrayList getAudioTypes() { - return (ArrayList) sSupportedAudioTypes.clone(); - } - - @SuppressWarnings("unchecked") - public static ArrayList getVideoTypes() { - return (ArrayList) sSupportedVideoTypes.clone(); - } - - @SuppressWarnings("unchecked") - public static ArrayList getSupportedTypes() { - return (ArrayList) sSupportedContentTypes.clone(); - } -} diff --git a/mms-common/java/com/android/mmscommon/mms/pdu/AcknowledgeInd.java b/mms-common/java/com/android/mmscommon/mms/pdu/AcknowledgeInd.java deleted file mode 100644 index d1243b22b..000000000 --- a/mms-common/java/com/android/mmscommon/mms/pdu/AcknowledgeInd.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (C) 2007 Esmertec AG. - * 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. - */ - -package com.android.mmscommon.mms.pdu; - -import com.android.mmscommon.InvalidHeaderValueException; -import com.android.mmscommon.PduHeaders; - -/** - * M-Acknowledge.ind PDU. - */ -public class AcknowledgeInd extends GenericPdu { - /** - * Constructor, used when composing a M-Acknowledge.ind pdu. - * - * @param mmsVersion current viersion of mms - * @param transactionId the transaction-id value - * @throws InvalidHeaderValueException if parameters are invalid. - * NullPointerException if transactionId is null. - */ - public AcknowledgeInd(int mmsVersion, byte[] transactionId) - throws InvalidHeaderValueException { - super(); - - setMessageType(PduHeaders.MESSAGE_TYPE_ACKNOWLEDGE_IND); - setMmsVersion(mmsVersion); - setTransactionId(transactionId); - } - - /** - * Constructor with given headers. - * - * @param headers Headers for this PDU. - */ - AcknowledgeInd(PduHeaders headers) { - super(headers); - } - - /** - * Get X-Mms-Report-Allowed field value. - * - * @return the X-Mms-Report-Allowed value - */ - public int getReportAllowed() { - return mPduHeaders.getOctet(PduHeaders.REPORT_ALLOWED); - } - - /** - * Set X-Mms-Report-Allowed field value. - * - * @param value the value - * @throws InvalidHeaderValueException if the value is invalid. - */ - public void setReportAllowed(int value) throws InvalidHeaderValueException { - mPduHeaders.setOctet(value, PduHeaders.REPORT_ALLOWED); - } - - /** - * Get X-Mms-Transaction-Id field value. - * - * @return the X-Mms-Report-Allowed value - */ - public byte[] getTransactionId() { - return mPduHeaders.getTextString(PduHeaders.TRANSACTION_ID); - } - - /** - * Set X-Mms-Transaction-Id field value. - * - * @param value the value - * @throws NullPointerException if the value is null. - */ - public void setTransactionId(byte[] value) { - mPduHeaders.setTextString(value, PduHeaders.TRANSACTION_ID); - } -} diff --git a/mms-common/java/com/android/mmscommon/mms/pdu/DeliveryInd.java b/mms-common/java/com/android/mmscommon/mms/pdu/DeliveryInd.java deleted file mode 100644 index e83729b8b..000000000 --- a/mms-common/java/com/android/mmscommon/mms/pdu/DeliveryInd.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright (C) 2007 Esmertec AG. - * 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. - */ - -package com.android.mmscommon.mms.pdu; - -import com.android.mmscommon.EncodedStringValue; -import com.android.mmscommon.InvalidHeaderValueException; -import com.android.mmscommon.PduHeaders; - -/** - * M-Delivery.Ind Pdu. - */ -public class DeliveryInd extends GenericPdu { - /** - * Empty constructor. - * Since the Pdu corresponding to this class is constructed - * by the Proxy-Relay server, this class is only instantiated - * by the Pdu Parser. - * - * @throws InvalidHeaderValueException if error occurs. - */ - public DeliveryInd() throws InvalidHeaderValueException { - super(); - setMessageType(PduHeaders.MESSAGE_TYPE_DELIVERY_IND); - } - - /** - * Constructor with given headers. - * - * @param headers Headers for this PDU. - */ - DeliveryInd(PduHeaders headers) { - super(headers); - } - - /** - * Get Date value. - * - * @return the value - */ - public long getDate() { - return mPduHeaders.getLongInteger(PduHeaders.DATE); - } - - /** - * Set Date value. - * - * @param value the value - */ - public void setDate(long value) { - mPduHeaders.setLongInteger(value, PduHeaders.DATE); - } - - /** - * Get Message-ID value. - * - * @return the value - */ - public byte[] getMessageId() { - return mPduHeaders.getTextString(PduHeaders.MESSAGE_ID); - } - - /** - * Set Message-ID value. - * - * @param value the value, should not be null - * @throws NullPointerException if the value is null. - */ - public void setMessageId(byte[] value) { - mPduHeaders.setTextString(value, PduHeaders.MESSAGE_ID); - } - - /** - * Get Status value. - * - * @return the value - */ - public int getStatus() { - return mPduHeaders.getOctet(PduHeaders.STATUS); - } - - /** - * Set Status value. - * - * @param value the value - * @throws InvalidHeaderValueException if the value is invalid. - */ - public void setStatus(int value) throws InvalidHeaderValueException { - mPduHeaders.setOctet(value, PduHeaders.STATUS); - } - - /** - * Get To value. - * - * @return the value - */ - public EncodedStringValue[] getTo() { - return mPduHeaders.getEncodedStringValues(PduHeaders.TO); - } - - /** - * set To value. - * - * @param value the value - * @throws NullPointerException if the value is null. - */ - public void setTo(EncodedStringValue[] value) { - mPduHeaders.setEncodedStringValues(value, PduHeaders.TO); - } - - /* - * Optional, not supported header fields: - * - * public byte[] getApplicId() {return null;} - * public void setApplicId(byte[] value) {} - * - * public byte[] getAuxApplicId() {return null;} - * public void getAuxApplicId(byte[] value) {} - * - * public byte[] getReplyApplicId() {return 0x00;} - * public void setReplyApplicId(byte[] value) {} - * - * public EncodedStringValue getStatusText() {return null;} - * public void setStatusText(EncodedStringValue value) {} - */ -} diff --git a/mms-common/java/com/android/mmscommon/mms/pdu/GenericPdu.java b/mms-common/java/com/android/mmscommon/mms/pdu/GenericPdu.java deleted file mode 100644 index c38e50250..000000000 --- a/mms-common/java/com/android/mmscommon/mms/pdu/GenericPdu.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (C) 2007 Esmertec AG. - * 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. - */ - -package com.android.mmscommon.mms.pdu; - -import com.android.mmscommon.EncodedStringValue; -import com.android.mmscommon.InvalidHeaderValueException; -import com.android.mmscommon.PduHeaders; - -public class GenericPdu { - /** - * The headers of pdu. - */ - PduHeaders mPduHeaders = null; - - /** - * Constructor. - */ - public GenericPdu() { - mPduHeaders = new PduHeaders(); - } - - /** - * Constructor. - * - * @param headers Headers for this PDU. - */ - GenericPdu(PduHeaders headers) { - mPduHeaders = headers; - } - - /** - * Get the headers of this PDU. - * - * @return A PduHeaders of this PDU. - */ - PduHeaders getPduHeaders() { - return mPduHeaders; - } - - /** - * Get X-Mms-Message-Type field value. - * - * @return the X-Mms-Report-Allowed value - */ - public int getMessageType() { - return mPduHeaders.getOctet(PduHeaders.MESSAGE_TYPE); - } - - /** - * Set X-Mms-Message-Type field value. - * - * @param value the value - * @throws InvalidHeaderValueException if the value is invalid. - * RuntimeException if field's value is not Octet. - */ - public void setMessageType(int value) throws InvalidHeaderValueException { - mPduHeaders.setOctet(value, PduHeaders.MESSAGE_TYPE); - } - - /** - * Get X-Mms-MMS-Version field value. - * - * @return the X-Mms-MMS-Version value - */ - public int getMmsVersion() { - return mPduHeaders.getOctet(PduHeaders.MMS_VERSION); - } - - /** - * Set X-Mms-MMS-Version field value. - * - * @param value the value - * @throws InvalidHeaderValueException if the value is invalid. - * RuntimeException if field's value is not Octet. - */ - public void setMmsVersion(int value) throws InvalidHeaderValueException { - mPduHeaders.setOctet(value, PduHeaders.MMS_VERSION); - } - - /** - * Get From value. - * From-value = Value-length - * (Address-present-token Encoded-string-value | Insert-address-token) - * - * @return the value - */ - public EncodedStringValue getFrom() { - return mPduHeaders.getEncodedStringValue(PduHeaders.FROM); - } - - /** - * Set From value. - * - * @param value the value - * @throws NullPointerException if the value is null. - */ - public void setFrom(EncodedStringValue value) { - mPduHeaders.setEncodedStringValue(value, PduHeaders.FROM); - } -} diff --git a/mms-common/java/com/android/mmscommon/mms/pdu/MultimediaMessagePdu.java b/mms-common/java/com/android/mmscommon/mms/pdu/MultimediaMessagePdu.java deleted file mode 100644 index 04fde2d45..000000000 --- a/mms-common/java/com/android/mmscommon/mms/pdu/MultimediaMessagePdu.java +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright (C) 2007 Esmertec AG. - * 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. - */ - -package com.android.mmscommon.mms.pdu; - -import com.android.mmscommon.EncodedStringValue; -import com.android.mmscommon.InvalidHeaderValueException; -import com.android.mmscommon.PduHeaders; - -/** - * Multimedia message PDU. - */ -public class MultimediaMessagePdu extends GenericPdu{ - /** - * The body. - */ - private PduBody mMessageBody; - - /** - * Constructor. - */ - public MultimediaMessagePdu() { - super(); - } - - /** - * Constructor. - * - * @param header the header of this PDU - * @param body the body of this PDU - */ - public MultimediaMessagePdu(PduHeaders header, PduBody body) { - super(header); - mMessageBody = body; - } - - /** - * Constructor with given headers. - * - * @param headers Headers for this PDU. - */ - MultimediaMessagePdu(PduHeaders headers) { - super(headers); - } - - /** - * Get body of the PDU. - * - * @return the body - */ - public PduBody getBody() { - return mMessageBody; - } - - /** - * Set body of the PDU. - * - * @param body the body - */ - public void setBody(PduBody body) { - mMessageBody = body; - } - - /** - * Get subject. - * - * @return the value - */ - public EncodedStringValue getSubject() { - return mPduHeaders.getEncodedStringValue(PduHeaders.SUBJECT); - } - - /** - * Set subject. - * - * @param value the value - * @throws NullPointerException if the value is null. - */ - public void setSubject(EncodedStringValue value) { - mPduHeaders.setEncodedStringValue(value, PduHeaders.SUBJECT); - } - - /** - * Get To value. - * - * @return the value - */ - public EncodedStringValue[] getTo() { - return mPduHeaders.getEncodedStringValues(PduHeaders.TO); - } - - /** - * Add a "To" value. - * - * @param value the value - * @throws NullPointerException if the value is null. - */ - public void addTo(EncodedStringValue value) { - mPduHeaders.appendEncodedStringValue(value, PduHeaders.TO); - } - - /** - * Get X-Mms-Priority value. - * - * @return the value - */ - public int getPriority() { - return mPduHeaders.getOctet(PduHeaders.PRIORITY); - } - - /** - * Set X-Mms-Priority value. - * - * @param value the value - * @throws InvalidHeaderValueException if the value is invalid. - */ - public void setPriority(int value) throws InvalidHeaderValueException { - mPduHeaders.setOctet(value, PduHeaders.PRIORITY); - } - - /** - * Get Date value. - * - * @return the value - */ - public long getDate() { - return mPduHeaders.getLongInteger(PduHeaders.DATE); - } - - /** - * Set Date value in seconds. - * - * @param value the value - */ - public void setDate(long value) { - mPduHeaders.setLongInteger(value, PduHeaders.DATE); - } -} diff --git a/mms-common/java/com/android/mmscommon/mms/pdu/NotificationInd.java b/mms-common/java/com/android/mmscommon/mms/pdu/NotificationInd.java deleted file mode 100644 index 24f17b09a..000000000 --- a/mms-common/java/com/android/mmscommon/mms/pdu/NotificationInd.java +++ /dev/null @@ -1,287 +0,0 @@ -/* - * Copyright (C) 2007 Esmertec AG. - * 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. - */ - -package com.android.mmscommon.mms.pdu; - -import com.android.mmscommon.EncodedStringValue; -import com.android.mmscommon.InvalidHeaderValueException; -import com.android.mmscommon.PduHeaders; - -/** - * M-Notification.ind PDU. - */ -public class NotificationInd extends GenericPdu { - /** - * Empty constructor. - * Since the Pdu corresponding to this class is constructed - * by the Proxy-Relay server, this class is only instantiated - * by the Pdu Parser. - * - * @throws InvalidHeaderValueException if error occurs. - * RuntimeException if an undeclared error occurs. - */ - public NotificationInd() throws InvalidHeaderValueException { - super(); - setMessageType(PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND); - } - - /** - * Constructor with given headers. - * - * @param headers Headers for this PDU. - */ - NotificationInd(PduHeaders headers) { - super(headers); - } - - /** - * Get X-Mms-Content-Class Value. - * - * @return the value - */ - public int getContentClass() { - return mPduHeaders.getOctet(PduHeaders.CONTENT_CLASS); - } - - /** - * Set X-Mms-Content-Class Value. - * - * @param value the value - * @throws InvalidHeaderValueException if the value is invalid. - * RuntimeException if an undeclared error occurs. - */ - public void setContentClass(int value) throws InvalidHeaderValueException { - mPduHeaders.setOctet(value, PduHeaders.CONTENT_CLASS); - } - - /** - * Get X-Mms-Content-Location value. - * When used in a PDU other than M-Mbox-Delete.conf and M-Delete.conf: - * Content-location-value = Uri-value - * - * @return the value - */ - public byte[] getContentLocation() { - return mPduHeaders.getTextString(PduHeaders.CONTENT_LOCATION); - } - - /** - * Set X-Mms-Content-Location value. - * - * @param value the value - * @throws NullPointerException if the value is null. - * RuntimeException if an undeclared error occurs. - */ - public void setContentLocation(byte[] value) { - mPduHeaders.setTextString(value, PduHeaders.CONTENT_LOCATION); - } - - /** - * Get X-Mms-Expiry value. - * - * Expiry-value = Value-length - * (Absolute-token Date-value | Relative-token Delta-seconds-value) - * - * @return the value - */ - public long getExpiry() { - return mPduHeaders.getLongInteger(PduHeaders.EXPIRY); - } - - /** - * Set X-Mms-Expiry value. - * - * @param value the value - * @throws RuntimeException if an undeclared error occurs. - */ - public void setExpiry(long value) { - mPduHeaders.setLongInteger(value, PduHeaders.EXPIRY); - } - - /** - * Get From value. - * From-value = Value-length - * (Address-present-token Encoded-string-value | Insert-address-token) - * - * @return the value - */ - public EncodedStringValue getFrom() { - return mPduHeaders.getEncodedStringValue(PduHeaders.FROM); - } - - /** - * Set From value. - * - * @param value the value - * @throws NullPointerException if the value is null. - * RuntimeException if an undeclared error occurs. - */ - public void setFrom(EncodedStringValue value) { - mPduHeaders.setEncodedStringValue(value, PduHeaders.FROM); - } - - /** - * Get X-Mms-Message-Class value. - * Message-class-value = Class-identifier | Token-text - * Class-identifier = Personal | Advertisement | Informational | Auto - * - * @return the value - */ - public byte[] getMessageClass() { - return mPduHeaders.getTextString(PduHeaders.MESSAGE_CLASS); - } - - /** - * Set X-Mms-Message-Class value. - * - * @param value the value - * @throws NullPointerException if the value is null. - * RuntimeException if an undeclared error occurs. - */ - public void setMessageClass(byte[] value) { - mPduHeaders.setTextString(value, PduHeaders.MESSAGE_CLASS); - } - - /** - * Get X-Mms-Message-Size value. - * Message-size-value = Long-integer - * - * @return the value - */ - public long getMessageSize() { - return mPduHeaders.getLongInteger(PduHeaders.MESSAGE_SIZE); - } - - /** - * Set X-Mms-Message-Size value. - * - * @param value the value - * @throws RuntimeException if an undeclared error occurs. - */ - public void setMessageSize(long value) { - mPduHeaders.setLongInteger(value, PduHeaders.MESSAGE_SIZE); - } - - /** - * Get subject. - * - * @return the value - */ - public EncodedStringValue getSubject() { - return mPduHeaders.getEncodedStringValue(PduHeaders.SUBJECT); - } - - /** - * Set subject. - * - * @param value the value - * @throws NullPointerException if the value is null. - * RuntimeException if an undeclared error occurs. - */ - public void setSubject(EncodedStringValue value) { - mPduHeaders.setEncodedStringValue(value, PduHeaders.SUBJECT); - } - - /** - * Get X-Mms-Transaction-Id. - * - * @return the value - */ - public byte[] getTransactionId() { - return mPduHeaders.getTextString(PduHeaders.TRANSACTION_ID); - } - - /** - * Set X-Mms-Transaction-Id. - * - * @param value the value - * @throws NullPointerException if the value is null. - * RuntimeException if an undeclared error occurs. - */ - public void setTransactionId(byte[] value) { - mPduHeaders.setTextString(value, PduHeaders.TRANSACTION_ID); - } - - /** - * Get X-Mms-Delivery-Report Value. - * - * @return the value - */ - public int getDeliveryReport() { - return mPduHeaders.getOctet(PduHeaders.DELIVERY_REPORT); - } - - /** - * Set X-Mms-Delivery-Report Value. - * - * @param value the value - * @throws InvalidHeaderValueException if the value is invalid. - * RuntimeException if an undeclared error occurs. - */ - public void setDeliveryReport(int value) throws InvalidHeaderValueException { - mPduHeaders.setOctet(value, PduHeaders.DELIVERY_REPORT); - } - - /* - * Optional, not supported header fields: - * - * public byte[] getApplicId() {return null;} - * public void setApplicId(byte[] value) {} - * - * public byte[] getAuxApplicId() {return null;} - * public void getAuxApplicId(byte[] value) {} - * - * public byte getDrmContent() {return 0x00;} - * public void setDrmContent(byte value) {} - * - * public byte getDistributionIndicator() {return 0x00;} - * public void setDistributionIndicator(byte value) {} - * - * public ElementDescriptorValue getElementDescriptor() {return null;} - * public void getElementDescriptor(ElementDescriptorValue value) {} - * - * public byte getPriority() {return 0x00;} - * public void setPriority(byte value) {} - * - * public byte getRecommendedRetrievalMode() {return 0x00;} - * public void setRecommendedRetrievalMode(byte value) {} - * - * public byte getRecommendedRetrievalModeText() {return 0x00;} - * public void setRecommendedRetrievalModeText(byte value) {} - * - * public byte[] getReplaceId() {return 0x00;} - * public void setReplaceId(byte[] value) {} - * - * public byte[] getReplyApplicId() {return 0x00;} - * public void setReplyApplicId(byte[] value) {} - * - * public byte getReplyCharging() {return 0x00;} - * public void setReplyCharging(byte value) {} - * - * public byte getReplyChargingDeadline() {return 0x00;} - * public void setReplyChargingDeadline(byte value) {} - * - * public byte[] getReplyChargingId() {return 0x00;} - * public void setReplyChargingId(byte[] value) {} - * - * public long getReplyChargingSize() {return 0;} - * public void setReplyChargingSize(long value) {} - * - * public byte getStored() {return 0x00;} - * public void setStored(byte value) {} - */ -} diff --git a/mms-common/java/com/android/mmscommon/mms/pdu/NotifyRespInd.java b/mms-common/java/com/android/mmscommon/mms/pdu/NotifyRespInd.java deleted file mode 100644 index c2e2b2653..000000000 --- a/mms-common/java/com/android/mmscommon/mms/pdu/NotifyRespInd.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (C) 2007 Esmertec AG. - * 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. - */ - -package com.android.mmscommon.mms.pdu; - -import com.android.mmscommon.InvalidHeaderValueException; -import com.android.mmscommon.PduHeaders; - -/** - * M-NofifyResp.ind PDU. - */ -public class NotifyRespInd extends GenericPdu { - /** - * Constructor, used when composing a M-NotifyResp.ind pdu. - * - * @param mmsVersion current version of mms - * @param transactionId the transaction-id value - * @param status the status value - * @throws InvalidHeaderValueException if parameters are invalid. - * NullPointerException if transactionId is null. - * RuntimeException if an undeclared error occurs. - */ - public NotifyRespInd(int mmsVersion, - byte[] transactionId, - int status) throws InvalidHeaderValueException { - super(); - setMessageType(PduHeaders.MESSAGE_TYPE_NOTIFYRESP_IND); - setMmsVersion(mmsVersion); - setTransactionId(transactionId); - setStatus(status); - } - - /** - * Constructor with given headers. - * - * @param headers Headers for this PDU. - */ - NotifyRespInd(PduHeaders headers) { - super(headers); - } - - /** - * Get X-Mms-Report-Allowed field value. - * - * @return the X-Mms-Report-Allowed value - */ - public int getReportAllowed() { - return mPduHeaders.getOctet(PduHeaders.REPORT_ALLOWED); - } - - /** - * Set X-Mms-Report-Allowed field value. - * - * @param value the value - * @throws InvalidHeaderValueException if the value is invalid. - * RuntimeException if an undeclared error occurs. - */ - public void setReportAllowed(int value) throws InvalidHeaderValueException { - mPduHeaders.setOctet(value, PduHeaders.REPORT_ALLOWED); - } - - /** - * Set X-Mms-Status field value. - * - * @param value the value - * @throws InvalidHeaderValueException if the value is invalid. - * RuntimeException if an undeclared error occurs. - */ - public void setStatus(int value) throws InvalidHeaderValueException { - mPduHeaders.setOctet(value, PduHeaders.STATUS); - } - - /** - * GetX-Mms-Status field value. - * - * @return the X-Mms-Status value - */ - public int getStatus() { - return mPduHeaders.getOctet(PduHeaders.STATUS); - } - - /** - * Get X-Mms-Transaction-Id field value. - * - * @return the X-Mms-Report-Allowed value - */ - public byte[] getTransactionId() { - return mPduHeaders.getTextString(PduHeaders.TRANSACTION_ID); - } - - /** - * Set X-Mms-Transaction-Id field value. - * - * @param value the value - * @throws NullPointerException if the value is null. - * RuntimeException if an undeclared error occurs. - */ - public void setTransactionId(byte[] value) { - mPduHeaders.setTextString(value, PduHeaders.TRANSACTION_ID); - } -} diff --git a/mms-common/java/com/android/mmscommon/mms/pdu/PduBody.java b/mms-common/java/com/android/mmscommon/mms/pdu/PduBody.java deleted file mode 100644 index cc28d80eb..000000000 --- a/mms-common/java/com/android/mmscommon/mms/pdu/PduBody.java +++ /dev/null @@ -1,191 +0,0 @@ -/* - * Copyright (C) 2007 Esmertec AG. - * 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. - */ - -package com.android.mmscommon.mms.pdu; - -import java.util.HashMap; -import java.util.Map; -import java.util.Vector; - -public class PduBody { - private Vector mParts = null; - - private Map mPartMapByContentId = null; - private Map mPartMapByContentLocation = null; - private Map mPartMapByName = null; - private Map mPartMapByFileName = null; - - /** - * Constructor. - */ - public PduBody() { - mParts = new Vector(); - - mPartMapByContentId = new HashMap(); - mPartMapByContentLocation = new HashMap(); - mPartMapByName = new HashMap(); - mPartMapByFileName = new HashMap(); - } - - private void putPartToMaps(PduPart part) { - // Put part to mPartMapByContentId. - byte[] contentId = part.getContentId(); - if(null != contentId) { - mPartMapByContentId.put(new String(contentId), part); - } - - // Put part to mPartMapByContentLocation. - byte[] contentLocation = part.getContentLocation(); - if(null != contentLocation) { - String clc = new String(contentLocation); - mPartMapByContentLocation.put(clc, part); - } - - // Put part to mPartMapByName. - byte[] name = part.getName(); - if(null != name) { - String clc = new String(name); - mPartMapByName.put(clc, part); - } - - // Put part to mPartMapByFileName. - byte[] fileName = part.getFilename(); - if(null != fileName) { - String clc = new String(fileName); - mPartMapByFileName.put(clc, part); - } - } - - /** - * Appends the specified part to the end of this body. - * - * @param part part to be appended - * @return true when success, false when fail - * @throws NullPointerException when part is null - */ - public boolean addPart(PduPart part) { - if(null == part) { - throw new NullPointerException(); - } - - putPartToMaps(part); - return mParts.add(part); - } - - /** - * Inserts the specified part at the specified position. - * - * @param index index at which the specified part is to be inserted - * @param part part to be inserted - * @throws NullPointerException when part is null - */ - public void addPart(int index, PduPart part) { - if(null == part) { - throw new NullPointerException(); - } - - putPartToMaps(part); - mParts.add(index, part); - } - - /** - * Removes the part at the specified position. - * - * @param index index of the part to return - * @return part at the specified index - */ - public PduPart removePart(int index) { - return mParts.remove(index); - } - - /** - * Remove all of the parts. - */ - public void removeAll() { - mParts.clear(); - } - - /** - * Get the part at the specified position. - * - * @param index index of the part to return - * @return part at the specified index - */ - public PduPart getPart(int index) { - return mParts.get(index); - } - - /** - * Get the index of the specified part. - * - * @param part the part object - * @return index the index of the first occurrence of the part in this body - */ - public int getPartIndex(PduPart part) { - return mParts.indexOf(part); - } - - /** - * Get the number of parts. - * - * @return the number of parts - */ - public int getPartsNum() { - return mParts.size(); - } - - /** - * Get pdu part by content id. - * - * @param cid the value of content id. - * @return the pdu part. - */ - public PduPart getPartByContentId(String cid) { - return mPartMapByContentId.get(cid); - } - - /** - * Get pdu part by Content-Location. Content-Location of part is - * the same as filename and name(param of content-type). - * - * @param fileName the value of filename. - * @return the pdu part. - */ - public PduPart getPartByContentLocation(String contentLocation) { - return mPartMapByContentLocation.get(contentLocation); - } - - /** - * Get pdu part by name. - * - * @param fileName the value of filename. - * @return the pdu part. - */ - public PduPart getPartByName(String name) { - return mPartMapByName.get(name); - } - - /** - * Get pdu part by filename. - * - * @param fileName the value of filename. - * @return the pdu part. - */ - public PduPart getPartByFileName(String filename) { - return mPartMapByFileName.get(filename); - } -} diff --git a/mms-common/java/com/android/mmscommon/mms/pdu/PduComposer.java b/mms-common/java/com/android/mmscommon/mms/pdu/PduComposer.java deleted file mode 100644 index bb3116d69..000000000 --- a/mms-common/java/com/android/mmscommon/mms/pdu/PduComposer.java +++ /dev/null @@ -1,1184 +0,0 @@ -/* - * Copyright (C) 2007-2008 Esmertec AG. - * Copyright (C) 2007-2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.mmscommon.mms.pdu; - -import com.android.mmscommon.EncodedStringValue; -import com.android.mmscommon.PduHeaders; - -import android.content.ContentResolver; -import android.content.Context; -import android.util.Log; -import android.text.TextUtils; - -import java.io.ByteArrayOutputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.util.Arrays; -import java.util.HashMap; - -public class PduComposer { - /** - * Address type. - */ - static private final int PDU_PHONE_NUMBER_ADDRESS_TYPE = 1; - static private final int PDU_EMAIL_ADDRESS_TYPE = 2; - static private final int PDU_IPV4_ADDRESS_TYPE = 3; - static private final int PDU_IPV6_ADDRESS_TYPE = 4; - static private final int PDU_UNKNOWN_ADDRESS_TYPE = 5; - - /** - * Address regular expression string. - */ - static final String REGEXP_PHONE_NUMBER_ADDRESS_TYPE = "\\+?[0-9|\\.|\\-]+"; - static final String REGEXP_EMAIL_ADDRESS_TYPE = "[a-zA-Z| ]*\\<{0,1}[a-zA-Z| ]+@{1}" + - "[a-zA-Z| ]+\\.{1}[a-zA-Z| ]+\\>{0,1}"; - static final String REGEXP_IPV6_ADDRESS_TYPE = - "[a-fA-F]{4}\\:{1}[a-fA-F0-9]{4}\\:{1}[a-fA-F0-9]{4}\\:{1}" + - "[a-fA-F0-9]{4}\\:{1}[a-fA-F0-9]{4}\\:{1}[a-fA-F0-9]{4}\\:{1}" + - "[a-fA-F0-9]{4}\\:{1}[a-fA-F0-9]{4}"; - static final String REGEXP_IPV4_ADDRESS_TYPE = "[0-9]{1,3}\\.{1}[0-9]{1,3}\\.{1}" + - "[0-9]{1,3}\\.{1}[0-9]{1,3}"; - - /** - * The postfix strings of address. - */ - static final String STRING_PHONE_NUMBER_ADDRESS_TYPE = "/TYPE=PLMN"; - static final String STRING_IPV4_ADDRESS_TYPE = "/TYPE=IPV4"; - static final String STRING_IPV6_ADDRESS_TYPE = "/TYPE=IPV6"; - - /** - * Error values. - */ - static private final int PDU_COMPOSE_SUCCESS = 0; - static private final int PDU_COMPOSE_CONTENT_ERROR = 1; - static private final int PDU_COMPOSE_FIELD_NOT_SET = 2; - static private final int PDU_COMPOSE_FIELD_NOT_SUPPORTED = 3; - - /** - * WAP values defined in WSP spec. - */ - static private final int QUOTED_STRING_FLAG = 34; - static private final int END_STRING_FLAG = 0; - static private final int LENGTH_QUOTE = 31; - static private final int TEXT_MAX = 127; - static private final int SHORT_INTEGER_MAX = 127; - static private final int LONG_INTEGER_LENGTH_MAX = 8; - - /** - * Block size when read data from InputStream. - */ - static private final int PDU_COMPOSER_BLOCK_SIZE = 1024; - - /** - * The output message. - */ - protected ByteArrayOutputStream mMessage = null; - - /** - * The PDU. - */ - private GenericPdu mPdu = null; - - /** - * Current visiting position of the mMessage. - */ - protected int mPosition = 0; - - /** - * Message compose buffer stack. - */ - private BufferStack mStack = null; - - /** - * Content resolver. - */ - private final ContentResolver mResolver; - - /** - * Header of this pdu. - */ - private PduHeaders mPduHeader = null; - - /** - * Map of all content type - */ - private static HashMap mContentTypeMap = null; - - static { - mContentTypeMap = new HashMap(); - - int i; - for (i = 0; i < PduContentTypes.contentTypes.length; i++) { - mContentTypeMap.put(PduContentTypes.contentTypes[i], i); - } - } - - /** - * Constructor. - * - * @param context the context - * @param pdu the pdu to be composed - */ - public PduComposer(Context context, GenericPdu pdu) { - mPdu = pdu; - mResolver = context.getContentResolver(); - mPduHeader = pdu.getPduHeaders(); - mStack = new BufferStack(); - mMessage = new ByteArrayOutputStream(); - mPosition = 0; - } - - /** - * Make the message. No need to check whether mandatory fields are set, - * because the constructors of outgoing pdus are taking care of this. - * - * @return OutputStream of maked message. Return null if - * the PDU is invalid. - */ - public byte[] make() { - // Get Message-type. - int type = mPdu.getMessageType(); - - /* make the message */ - switch (type) { - case PduHeaders.MESSAGE_TYPE_SEND_REQ: - if (makeSendReqPdu() != PDU_COMPOSE_SUCCESS) { - return null; - } - break; - case PduHeaders.MESSAGE_TYPE_NOTIFYRESP_IND: - if (makeNotifyResp() != PDU_COMPOSE_SUCCESS) { - return null; - } - break; - case PduHeaders.MESSAGE_TYPE_ACKNOWLEDGE_IND: - if (makeAckInd() != PDU_COMPOSE_SUCCESS) { - return null; - } - break; - case PduHeaders.MESSAGE_TYPE_READ_REC_IND: - if (makeReadRecInd() != PDU_COMPOSE_SUCCESS) { - return null; - } - break; - default: - return null; - } - - return mMessage.toByteArray(); - } - - /** - * Copy buf to mMessage. - */ - protected void arraycopy(byte[] buf, int pos, int length) { - mMessage.write(buf, pos, length); - mPosition = mPosition + length; - } - - /** - * Append a byte to mMessage. - */ - protected void append(int value) { - mMessage.write(value); - mPosition ++; - } - - /** - * Append short integer value to mMessage. - * This implementation doesn't check the validity of parameter, since it - * assumes that the values are validated in the GenericPdu setter methods. - */ - protected void appendShortInteger(int value) { - /* - * From WAP-230-WSP-20010705-a: - * Short-integer = OCTET - * ; Integers in range 0-127 shall be encoded as a one octet value - * ; with the most significant bit set to one (1xxx xxxx) and with - * ; the value in the remaining least significant bits. - * In our implementation, only low 7 bits are stored and otherwise - * bits are ignored. - */ - append((value | 0x80) & 0xff); - } - - /** - * Append an octet number between 128 and 255 into mMessage. - * NOTE: - * A value between 0 and 127 should be appended by using appendShortInteger. - * This implementation doesn't check the validity of parameter, since it - * assumes that the values are validated in the GenericPdu setter methods. - */ - protected void appendOctet(int number) { - append(number); - } - - /** - * Append a short length into mMessage. - * This implementation doesn't check the validity of parameter, since it - * assumes that the values are validated in the GenericPdu setter methods. - */ - protected void appendShortLength(int value) { - /* - * From WAP-230-WSP-20010705-a: - * Short-length = - */ - append(value); - } - - /** - * Append long integer into mMessage. it's used for really long integers. - * This implementation doesn't check the validity of parameter, since it - * assumes that the values are validated in the GenericPdu setter methods. - */ - protected void appendLongInteger(long longInt) { - /* - * From WAP-230-WSP-20010705-a: - * Long-integer = Short-length Multi-octet-integer - * ; The Short-length indicates the length of the Multi-octet-integer - * Multi-octet-integer = 1*30 OCTET - * ; The content octets shall be an unsigned integer value with the - * ; most significant octet encoded first (big-endian representation). - * ; The minimum number of octets must be used to encode the value. - */ - int size; - long temp = longInt; - - // Count the length of the long integer. - for(size = 0; (temp != 0) && (size < LONG_INTEGER_LENGTH_MAX); size++) { - temp = (temp >>> 8); - } - - // Set Length. - appendShortLength(size); - - // Count and set the long integer. - int i; - int shift = (size -1) * 8; - - for (i = 0; i < size; i++) { - append((int)((longInt >>> shift) & 0xff)); - shift = shift - 8; - } - } - - /** - * Append text string into mMessage. - * This implementation doesn't check the validity of parameter, since it - * assumes that the values are validated in the GenericPdu setter methods. - */ - protected void appendTextString(byte[] text) { - /* - * From WAP-230-WSP-20010705-a: - * Text-string = [Quote] *TEXT End-of-string - * ; If the first character in the TEXT is in the range of 128-255, - * ; a Quote character must precede it. Otherwise the Quote character - * ;must be omitted. The Quote is not part of the contents. - */ - if (((text[0])&0xff) > TEXT_MAX) { // No need to check for <= 255 - append(TEXT_MAX); - } - - arraycopy(text, 0, text.length); - append(0); - } - - /** - * Append text string into mMessage. - * This implementation doesn't check the validity of parameter, since it - * assumes that the values are validated in the GenericPdu setter methods. - */ - protected void appendTextString(String str) { - /* - * From WAP-230-WSP-20010705-a: - * Text-string = [Quote] *TEXT End-of-string - * ; If the first character in the TEXT is in the range of 128-255, - * ; a Quote character must precede it. Otherwise the Quote character - * ;must be omitted. The Quote is not part of the contents. - */ - appendTextString(str.getBytes()); - } - - /** - * Append encoded string value to mMessage. - * This implementation doesn't check the validity of parameter, since it - * assumes that the values are validated in the GenericPdu setter methods. - */ - protected void appendEncodedString(EncodedStringValue enStr) { - /* - * From OMA-TS-MMS-ENC-V1_3-20050927-C: - * Encoded-string-value = Text-string | Value-length Char-set Text-string - */ - assert(enStr != null); - - int charset = enStr.getCharacterSet(); - byte[] textString = enStr.getTextString(); - if (null == textString) { - return; - } - - /* - * In the implementation of EncodedStringValue, the charset field will - * never be 0. It will always be composed as - * Encoded-string-value = Value-length Char-set Text-string - */ - mStack.newbuf(); - PositionMarker start = mStack.mark(); - - appendShortInteger(charset); - appendTextString(textString); - - int len = start.getLength(); - mStack.pop(); - appendValueLength(len); - mStack.copy(); - } - - /** - * Append uintvar integer into mMessage. - * This implementation doesn't check the validity of parameter, since it - * assumes that the values are validated in the GenericPdu setter methods. - */ - protected void appendUintvarInteger(long value) { - /* - * From WAP-230-WSP-20010705-a: - * To encode a large unsigned integer, split it into 7-bit fragments - * and place them in the payloads of multiple octets. The most significant - * bits are placed in the first octets with the least significant bits - * ending up in the last octet. All octets MUST set the Continue bit to 1 - * except the last octet, which MUST set the Continue bit to 0. - */ - int i; - long max = SHORT_INTEGER_MAX; - - for (i = 0; i < 5; i++) { - if (value < max) { - break; - } - - max = (max << 7) | 0x7fl; - } - - while(i > 0) { - long temp = value >>> (i * 7); - temp = temp & 0x7f; - - append((int)((temp | 0x80) & 0xff)); - - i--; - } - - append((int)(value & 0x7f)); - } - - /** - * Append date value into mMessage. - * This implementation doesn't check the validity of parameter, since it - * assumes that the values are validated in the GenericPdu setter methods. - */ - protected void appendDateValue(long date) { - /* - * From OMA-TS-MMS-ENC-V1_3-20050927-C: - * Date-value = Long-integer - */ - appendLongInteger(date); - } - - /** - * Append value length to mMessage. - * This implementation doesn't check the validity of parameter, since it - * assumes that the values are validated in the GenericPdu setter methods. - */ - protected void appendValueLength(long value) { - /* - * From WAP-230-WSP-20010705-a: - * Value-length = Short-length | (Length-quote Length) - * ; Value length is used to indicate the length of the value to follow - * Short-length = - * Length-quote = - * Length = Uintvar-integer - */ - if (value < LENGTH_QUOTE) { - appendShortLength((int) value); - return; - } - - append(LENGTH_QUOTE); - appendUintvarInteger(value); - } - - /** - * Append quoted string to mMessage. - * This implementation doesn't check the validity of parameter, since it - * assumes that the values are validated in the GenericPdu setter methods. - */ - protected void appendQuotedString(byte[] text) { - /* - * From WAP-230-WSP-20010705-a: - * Quoted-string = *TEXT End-of-string - * ;The TEXT encodes an RFC2616 Quoted-string with the enclosing - * ;quotation-marks <"> removed. - */ - append(QUOTED_STRING_FLAG); - arraycopy(text, 0, text.length); - append(END_STRING_FLAG); - } - - /** - * Append quoted string to mMessage. - * This implementation doesn't check the validity of parameter, since it - * assumes that the values are validated in the GenericPdu setter methods. - */ - protected void appendQuotedString(String str) { - /* - * From WAP-230-WSP-20010705-a: - * Quoted-string = *TEXT End-of-string - * ;The TEXT encodes an RFC2616 Quoted-string with the enclosing - * ;quotation-marks <"> removed. - */ - appendQuotedString(str.getBytes()); - } - - private EncodedStringValue appendAddressType(EncodedStringValue address) { - EncodedStringValue temp = null; - - try { - int addressType = checkAddressType(address.getString()); - temp = EncodedStringValue.copy(address); - if (PDU_PHONE_NUMBER_ADDRESS_TYPE == addressType) { - // Phone number. - temp.appendTextString(STRING_PHONE_NUMBER_ADDRESS_TYPE.getBytes()); - } else if (PDU_IPV4_ADDRESS_TYPE == addressType) { - // Ipv4 address. - temp.appendTextString(STRING_IPV4_ADDRESS_TYPE.getBytes()); - } else if (PDU_IPV6_ADDRESS_TYPE == addressType) { - // Ipv6 address. - temp.appendTextString(STRING_IPV6_ADDRESS_TYPE.getBytes()); - } - } catch (NullPointerException e) { - return null; - } - - return temp; - } - - /** - * Append header to mMessage. - */ - private int appendHeader(int field) { - switch (field) { - case PduHeaders.MMS_VERSION: - appendOctet(field); - - int version = mPduHeader.getOctet(field); - if (0 == version) { - appendShortInteger(PduHeaders.CURRENT_MMS_VERSION); - } else { - appendShortInteger(version); - } - - break; - - case PduHeaders.MESSAGE_ID: - case PduHeaders.TRANSACTION_ID: - byte[] textString = mPduHeader.getTextString(field); - if (null == textString) { - return PDU_COMPOSE_FIELD_NOT_SET; - } - - appendOctet(field); - appendTextString(textString); - break; - - case PduHeaders.TO: - case PduHeaders.BCC: - case PduHeaders.CC: - EncodedStringValue[] addr = mPduHeader.getEncodedStringValues(field); - - if (null == addr) { - return PDU_COMPOSE_FIELD_NOT_SET; - } - - EncodedStringValue temp; - for (int i = 0; i < addr.length; i++) { - temp = appendAddressType(addr[i]); - if (temp == null) { - return PDU_COMPOSE_CONTENT_ERROR; - } - - appendOctet(field); - appendEncodedString(temp); - } - break; - - case PduHeaders.FROM: - // Value-length (Address-present-token Encoded-string-value | Insert-address-token) - appendOctet(field); - - EncodedStringValue from = mPduHeader.getEncodedStringValue(field); - if ((from == null) - || TextUtils.isEmpty(from.getString()) - || new String(from.getTextString()).equals( - PduHeaders.FROM_INSERT_ADDRESS_TOKEN_STR)) { - // Length of from = 1 - append(1); - // Insert-address-token = - append(PduHeaders.FROM_INSERT_ADDRESS_TOKEN); - } else { - mStack.newbuf(); - PositionMarker fstart = mStack.mark(); - - // Address-present-token = - append(PduHeaders.FROM_ADDRESS_PRESENT_TOKEN); - - temp = appendAddressType(from); - if (temp == null) { - return PDU_COMPOSE_CONTENT_ERROR; - } - - appendEncodedString(temp); - - int flen = fstart.getLength(); - mStack.pop(); - appendValueLength(flen); - mStack.copy(); - } - break; - - case PduHeaders.READ_STATUS: - case PduHeaders.STATUS: - case PduHeaders.REPORT_ALLOWED: - case PduHeaders.PRIORITY: - case PduHeaders.DELIVERY_REPORT: - case PduHeaders.READ_REPORT: - int octet = mPduHeader.getOctet(field); - if (0 == octet) { - return PDU_COMPOSE_FIELD_NOT_SET; - } - - appendOctet(field); - appendOctet(octet); - break; - - case PduHeaders.DATE: - long date = mPduHeader.getLongInteger(field); - if (-1 == date) { - return PDU_COMPOSE_FIELD_NOT_SET; - } - - appendOctet(field); - appendDateValue(date); - break; - - case PduHeaders.SUBJECT: - EncodedStringValue enString = - mPduHeader.getEncodedStringValue(field); - if (null == enString) { - return PDU_COMPOSE_FIELD_NOT_SET; - } - - appendOctet(field); - appendEncodedString(enString); - break; - - case PduHeaders.MESSAGE_CLASS: - byte[] messageClass = mPduHeader.getTextString(field); - if (null == messageClass) { - return PDU_COMPOSE_FIELD_NOT_SET; - } - - appendOctet(field); - if (Arrays.equals(messageClass, - PduHeaders.MESSAGE_CLASS_ADVERTISEMENT_STR.getBytes())) { - appendOctet(PduHeaders.MESSAGE_CLASS_ADVERTISEMENT); - } else if (Arrays.equals(messageClass, - PduHeaders.MESSAGE_CLASS_AUTO_STR.getBytes())) { - appendOctet(PduHeaders.MESSAGE_CLASS_AUTO); - } else if (Arrays.equals(messageClass, - PduHeaders.MESSAGE_CLASS_PERSONAL_STR.getBytes())) { - appendOctet(PduHeaders.MESSAGE_CLASS_PERSONAL); - } else if (Arrays.equals(messageClass, - PduHeaders.MESSAGE_CLASS_INFORMATIONAL_STR.getBytes())) { - appendOctet(PduHeaders.MESSAGE_CLASS_INFORMATIONAL); - } else { - appendTextString(messageClass); - } - break; - - case PduHeaders.EXPIRY: - long expiry = mPduHeader.getLongInteger(field); - if (-1 == expiry) { - return PDU_COMPOSE_FIELD_NOT_SET; - } - - appendOctet(field); - - mStack.newbuf(); - PositionMarker expiryStart = mStack.mark(); - - append(PduHeaders.VALUE_RELATIVE_TOKEN); - appendLongInteger(expiry); - - int expiryLength = expiryStart.getLength(); - mStack.pop(); - appendValueLength(expiryLength); - mStack.copy(); - break; - - default: - return PDU_COMPOSE_FIELD_NOT_SUPPORTED; - } - - return PDU_COMPOSE_SUCCESS; - } - - /** - * Make ReadRec.Ind. - */ - private int makeReadRecInd() { - if (mMessage == null) { - mMessage = new ByteArrayOutputStream(); - mPosition = 0; - } - - // X-Mms-Message-Type - appendOctet(PduHeaders.MESSAGE_TYPE); - appendOctet(PduHeaders.MESSAGE_TYPE_READ_REC_IND); - - // X-Mms-MMS-Version - if (appendHeader(PduHeaders.MMS_VERSION) != PDU_COMPOSE_SUCCESS) { - return PDU_COMPOSE_CONTENT_ERROR; - } - - // Message-ID - if (appendHeader(PduHeaders.MESSAGE_ID) != PDU_COMPOSE_SUCCESS) { - return PDU_COMPOSE_CONTENT_ERROR; - } - - // To - if (appendHeader(PduHeaders.TO) != PDU_COMPOSE_SUCCESS) { - return PDU_COMPOSE_CONTENT_ERROR; - } - - // From - if (appendHeader(PduHeaders.FROM) != PDU_COMPOSE_SUCCESS) { - return PDU_COMPOSE_CONTENT_ERROR; - } - - // Date Optional - appendHeader(PduHeaders.DATE); - - // X-Mms-Read-Status - if (appendHeader(PduHeaders.READ_STATUS) != PDU_COMPOSE_SUCCESS) { - return PDU_COMPOSE_CONTENT_ERROR; - } - - // X-Mms-Applic-ID Optional(not support) - // X-Mms-Reply-Applic-ID Optional(not support) - // X-Mms-Aux-Applic-Info Optional(not support) - - return PDU_COMPOSE_SUCCESS; - } - - /** - * Make NotifyResp.Ind. - */ - private int makeNotifyResp() { - if (mMessage == null) { - mMessage = new ByteArrayOutputStream(); - mPosition = 0; - } - - // X-Mms-Message-Type - appendOctet(PduHeaders.MESSAGE_TYPE); - appendOctet(PduHeaders.MESSAGE_TYPE_NOTIFYRESP_IND); - - // X-Mms-Transaction-ID - if (appendHeader(PduHeaders.TRANSACTION_ID) != PDU_COMPOSE_SUCCESS) { - return PDU_COMPOSE_CONTENT_ERROR; - } - - // X-Mms-MMS-Version - if (appendHeader(PduHeaders.MMS_VERSION) != PDU_COMPOSE_SUCCESS) { - return PDU_COMPOSE_CONTENT_ERROR; - } - - // X-Mms-Status - if (appendHeader(PduHeaders.STATUS) != PDU_COMPOSE_SUCCESS) { - return PDU_COMPOSE_CONTENT_ERROR; - } - - // X-Mms-Report-Allowed Optional (not support) - return PDU_COMPOSE_SUCCESS; - } - - /** - * Make Acknowledge.Ind. - */ - private int makeAckInd() { - if (mMessage == null) { - mMessage = new ByteArrayOutputStream(); - mPosition = 0; - } - - // X-Mms-Message-Type - appendOctet(PduHeaders.MESSAGE_TYPE); - appendOctet(PduHeaders.MESSAGE_TYPE_ACKNOWLEDGE_IND); - - // X-Mms-Transaction-ID - if (appendHeader(PduHeaders.TRANSACTION_ID) != PDU_COMPOSE_SUCCESS) { - return PDU_COMPOSE_CONTENT_ERROR; - } - - // X-Mms-MMS-Version - if (appendHeader(PduHeaders.MMS_VERSION) != PDU_COMPOSE_SUCCESS) { - return PDU_COMPOSE_CONTENT_ERROR; - } - - // X-Mms-Report-Allowed Optional - appendHeader(PduHeaders.REPORT_ALLOWED); - - return PDU_COMPOSE_SUCCESS; - } - - /** - * Make Send.req. - */ - private int makeSendReqPdu() { - if (mMessage == null) { - mMessage = new ByteArrayOutputStream(); - mPosition = 0; - } - - // X-Mms-Message-Type - appendOctet(PduHeaders.MESSAGE_TYPE); - appendOctet(PduHeaders.MESSAGE_TYPE_SEND_REQ); - - // X-Mms-Transaction-ID - appendOctet(PduHeaders.TRANSACTION_ID); - - byte[] trid = mPduHeader.getTextString(PduHeaders.TRANSACTION_ID); - if (trid == null) { - // Transaction-ID should be set(by Transaction) before make(). - throw new IllegalArgumentException("Transaction-ID is null."); - } - appendTextString(trid); - - // X-Mms-MMS-Version - if (appendHeader(PduHeaders.MMS_VERSION) != PDU_COMPOSE_SUCCESS) { - return PDU_COMPOSE_CONTENT_ERROR; - } - - // Date Date-value Optional. - appendHeader(PduHeaders.DATE); - - // From - if (appendHeader(PduHeaders.FROM) != PDU_COMPOSE_SUCCESS) { - return PDU_COMPOSE_CONTENT_ERROR; - } - - boolean recipient = false; - - // To - if (appendHeader(PduHeaders.TO) != PDU_COMPOSE_CONTENT_ERROR) { - recipient = true; - } - - // Cc - if (appendHeader(PduHeaders.CC) != PDU_COMPOSE_CONTENT_ERROR) { - recipient = true; - } - - // Bcc - if (appendHeader(PduHeaders.BCC) != PDU_COMPOSE_CONTENT_ERROR) { - recipient = true; - } - - // Need at least one of "cc", "bcc" and "to". - if (false == recipient) { - return PDU_COMPOSE_CONTENT_ERROR; - } - - // Subject Optional - appendHeader(PduHeaders.SUBJECT); - - // X-Mms-Message-Class Optional - // Message-class-value = Class-identifier | Token-text - appendHeader(PduHeaders.MESSAGE_CLASS); - - // X-Mms-Expiry Optional - appendHeader(PduHeaders.EXPIRY); - - // X-Mms-Priority Optional - appendHeader(PduHeaders.PRIORITY); - - // X-Mms-Delivery-Report Optional - appendHeader(PduHeaders.DELIVERY_REPORT); - - // X-Mms-Read-Report Optional - appendHeader(PduHeaders.READ_REPORT); - - // Content-Type - appendOctet(PduHeaders.CONTENT_TYPE); - - // Message body - makeMessageBody(); - - return PDU_COMPOSE_SUCCESS; // Composing the message is OK - } - - /** - * Make message body. - */ - private int makeMessageBody() { - // 1. add body informations - mStack.newbuf(); // Switching buffer because we need to - - PositionMarker ctStart = mStack.mark(); - - // This contentTypeIdentifier should be used for type of attachment... - String contentType = new String(mPduHeader.getTextString(PduHeaders.CONTENT_TYPE)); - Integer contentTypeIdentifier = mContentTypeMap.get(contentType); - if (contentTypeIdentifier == null) { - // content type is mandatory - return PDU_COMPOSE_CONTENT_ERROR; - } - - appendShortInteger(contentTypeIdentifier.intValue()); - - // content-type parameter: start - PduBody body = ((SendReq) mPdu).getBody(); - if (null == body || body.getPartsNum() == 0) { - // empty message - appendUintvarInteger(0); - mStack.pop(); - mStack.copy(); - return PDU_COMPOSE_SUCCESS; - } - - PduPart part; - try { - part = body.getPart(0); - - byte[] start = part.getContentId(); - if (start != null) { - appendOctet(PduPart.P_DEP_START); - if (('<' == start[0]) && ('>' == start[start.length - 1])) { - appendTextString(start); - } else { - appendTextString("<" + new String(start) + ">"); - } - } - - // content-type parameter: type - appendOctet(PduPart.P_CT_MR_TYPE); - appendTextString(part.getContentType()); - } - catch (ArrayIndexOutOfBoundsException e){ - e.printStackTrace(); - } - - int ctLength = ctStart.getLength(); - mStack.pop(); - appendValueLength(ctLength); - mStack.copy(); - - // 3. add content - int partNum = body.getPartsNum(); - appendUintvarInteger(partNum); - for (int i = 0; i < partNum; i++) { - part = body.getPart(i); - mStack.newbuf(); // Leaving space for header lengh and data length - PositionMarker attachment = mStack.mark(); - - mStack.newbuf(); // Leaving space for Content-Type length - PositionMarker contentTypeBegin = mStack.mark(); - - byte[] partContentType = part.getContentType(); - - if (partContentType == null) { - // content type is mandatory - return PDU_COMPOSE_CONTENT_ERROR; - } - - // content-type value - Integer partContentTypeIdentifier = - mContentTypeMap.get(new String(partContentType)); - if (partContentTypeIdentifier == null) { - appendTextString(partContentType); - } else { - appendShortInteger(partContentTypeIdentifier.intValue()); - } - - /* Content-type parameter : name. - * The value of name, filename, content-location is the same. - * Just one of them is enough for this PDU. - */ - byte[] name = part.getName(); - - if (null == name) { - name = part.getFilename(); - - if (null == name) { - name = part.getContentLocation(); - - if (null == name) { - /* at lease one of name, filename, Content-location - * should be available. - */ - return PDU_COMPOSE_CONTENT_ERROR; - } - } - } - appendOctet(PduPart.P_DEP_NAME); - appendTextString(name); - - // content-type parameter : charset - int charset = part.getCharset(); - if (charset != 0) { - appendOctet(PduPart.P_CHARSET); - appendShortInteger(charset); - } - - int contentTypeLength = contentTypeBegin.getLength(); - mStack.pop(); - appendValueLength(contentTypeLength); - mStack.copy(); - - // content id - byte[] contentId = part.getContentId(); - - if (null != contentId) { - appendOctet(PduPart.P_CONTENT_ID); - if (('<' == contentId[0]) && ('>' == contentId[contentId.length - 1])) { - appendQuotedString(contentId); - } else { - appendQuotedString("<" + new String(contentId) + ">"); - } - } - - // content-location - byte[] contentLocation = part.getContentLocation(); - if (null != contentLocation) { - appendOctet(PduPart.P_CONTENT_LOCATION); - appendTextString(contentLocation); - } - - // content - int headerLength = attachment.getLength(); - - int dataLength = 0; // Just for safety... - byte[] partData = part.getData(); - - if (partData != null) { - arraycopy(partData, 0, partData.length); - dataLength = partData.length; - } else { - InputStream cr; - try { - byte[] buffer = new byte[PDU_COMPOSER_BLOCK_SIZE]; - cr = mResolver.openInputStream(part.getDataUri()); - int len = 0; - while ((len = cr.read(buffer)) != -1) { - mMessage.write(buffer, 0, len); - mPosition += len; - dataLength += len; - } - } catch (FileNotFoundException e) { - return PDU_COMPOSE_CONTENT_ERROR; - } catch (IOException e) { - return PDU_COMPOSE_CONTENT_ERROR; - } catch (RuntimeException e) { - return PDU_COMPOSE_CONTENT_ERROR; - } - } - - if (dataLength != (attachment.getLength() - headerLength)) { - throw new RuntimeException("BUG: Length sanity check failed"); - } - - mStack.pop(); - appendUintvarInteger(headerLength); - appendUintvarInteger(dataLength); - mStack.copy(); - } - - return PDU_COMPOSE_SUCCESS; - } - - /** - * Record current message informations. - */ - static private class LengthRecordNode { - ByteArrayOutputStream currentMessage = null; - public int currentPosition = 0; - - public LengthRecordNode next = null; - } - - /** - * Mark current message position and stact size. - */ - private class PositionMarker { - private int c_pos; // Current position - private int currentStackSize; // Current stack size - - int getLength() { - // If these assert fails, likely that you are finding the - // size of buffer that is deep in BufferStack you can only - // find the length of the buffer that is on top - if (currentStackSize != mStack.stackSize) { - throw new RuntimeException("BUG: Invalid call to getLength()"); - } - - return mPosition - c_pos; - } - } - - /** - * This implementation can be OPTIMIZED to use only - * 2 buffers. This optimization involves changing BufferStack - * only... Its usage (interface) will not change. - */ - private class BufferStack { - private LengthRecordNode stack = null; - private LengthRecordNode toCopy = null; - - int stackSize = 0; - - /** - * Create a new message buffer and push it into the stack. - */ - void newbuf() { - // You can't create a new buff when toCopy != null - // That is after calling pop() and before calling copy() - // If you do, it is a bug - if (toCopy != null) { - throw new RuntimeException("BUG: Invalid newbuf() before copy()"); - } - - LengthRecordNode temp = new LengthRecordNode(); - - temp.currentMessage = mMessage; - temp.currentPosition = mPosition; - - temp.next = stack; - stack = temp; - - stackSize = stackSize + 1; - - mMessage = new ByteArrayOutputStream(); - mPosition = 0; - } - - /** - * Pop the message before and record current message in the stack. - */ - void pop() { - ByteArrayOutputStream currentMessage = mMessage; - int currentPosition = mPosition; - - mMessage = stack.currentMessage; - mPosition = stack.currentPosition; - - toCopy = stack; - // Re using the top element of the stack to avoid memory allocation - - stack = stack.next; - stackSize = stackSize - 1; - - toCopy.currentMessage = currentMessage; - toCopy.currentPosition = currentPosition; - } - - /** - * Append current message to the message before. - */ - void copy() { - arraycopy(toCopy.currentMessage.toByteArray(), 0, - toCopy.currentPosition); - - toCopy = null; - } - - /** - * Mark current message position - */ - PositionMarker mark() { - PositionMarker m = new PositionMarker(); - - m.c_pos = mPosition; - m.currentStackSize = stackSize; - - return m; - } - } - - /** - * Check address type. - * - * @param address address string without the postfix stinng type, - * such as "/TYPE=PLMN", "/TYPE=IPv6" and "/TYPE=IPv4" - * @return PDU_PHONE_NUMBER_ADDRESS_TYPE if it is phone number, - * PDU_EMAIL_ADDRESS_TYPE if it is email address, - * PDU_IPV4_ADDRESS_TYPE if it is ipv4 address, - * PDU_IPV6_ADDRESS_TYPE if it is ipv6 address, - * PDU_UNKNOWN_ADDRESS_TYPE if it is unknown. - */ - protected static int checkAddressType(String address) { - /** - * From OMA-TS-MMS-ENC-V1_3-20050927-C.pdf, section 8. - * address = ( e-mail / device-address / alphanum-shortcode / num-shortcode) - * e-mail = mailbox; to the definition of mailbox as described in - * section 3.4 of [RFC2822], but excluding the - * obsolete definitions as indicated by the "obs-" prefix. - * device-address = ( global-phone-number "/TYPE=PLMN" ) - * / ( ipv4 "/TYPE=IPv4" ) / ( ipv6 "/TYPE=IPv6" ) - * / ( escaped-value "/TYPE=" address-type ) - * - * global-phone-number = ["+"] 1*( DIGIT / written-sep ) - * written-sep =("-"/".") - * - * ipv4 = 1*3DIGIT 3( "." 1*3DIGIT ) ; IPv4 address value - * - * ipv6 = 4HEXDIG 7( ":" 4HEXDIG ) ; IPv6 address per RFC 2373 - */ - - if (null == address) { - return PDU_UNKNOWN_ADDRESS_TYPE; - } - - if (address.matches(REGEXP_IPV4_ADDRESS_TYPE)) { - // Ipv4 address. - return PDU_IPV4_ADDRESS_TYPE; - }else if (address.matches(REGEXP_PHONE_NUMBER_ADDRESS_TYPE)) { - // Phone number. - return PDU_PHONE_NUMBER_ADDRESS_TYPE; - } else if (address.matches(REGEXP_EMAIL_ADDRESS_TYPE)) { - // Email address. - return PDU_EMAIL_ADDRESS_TYPE; - } else if (address.matches(REGEXP_IPV6_ADDRESS_TYPE)) { - // Ipv6 address. - return PDU_IPV6_ADDRESS_TYPE; - } else { - // Unknown address. - return PDU_UNKNOWN_ADDRESS_TYPE; - } - } -} diff --git a/mms-common/java/com/android/mmscommon/mms/pdu/PduContentTypes.java b/mms-common/java/com/android/mmscommon/mms/pdu/PduContentTypes.java deleted file mode 100644 index 3f971fd98..000000000 --- a/mms-common/java/com/android/mmscommon/mms/pdu/PduContentTypes.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (C) 2007 Esmertec AG. - * 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. - */ - -package com.android.mmscommon.mms.pdu; - -public class PduContentTypes { - /** - * All content types. From: - * http://www.openmobilealliance.org/tech/omna/omna-wsp-content-type.htm - */ - static final String[] contentTypes = { - "*/*", /* 0x00 */ - "text/*", /* 0x01 */ - "text/html", /* 0x02 */ - "text/plain", /* 0x03 */ - "text/x-hdml", /* 0x04 */ - "text/x-ttml", /* 0x05 */ - "text/x-vCalendar", /* 0x06 */ - "text/x-vCard", /* 0x07 */ - "text/vnd.wap.wml", /* 0x08 */ - "text/vnd.wap.wmlscript", /* 0x09 */ - "text/vnd.wap.wta-event", /* 0x0A */ - "multipart/*", /* 0x0B */ - "multipart/mixed", /* 0x0C */ - "multipart/form-data", /* 0x0D */ - "multipart/byterantes", /* 0x0E */ - "multipart/alternative", /* 0x0F */ - "application/*", /* 0x10 */ - "application/java-vm", /* 0x11 */ - "application/x-www-form-urlencoded", /* 0x12 */ - "application/x-hdmlc", /* 0x13 */ - "application/vnd.wap.wmlc", /* 0x14 */ - "application/vnd.wap.wmlscriptc", /* 0x15 */ - "application/vnd.wap.wta-eventc", /* 0x16 */ - "application/vnd.wap.uaprof", /* 0x17 */ - "application/vnd.wap.wtls-ca-certificate", /* 0x18 */ - "application/vnd.wap.wtls-user-certificate", /* 0x19 */ - "application/x-x509-ca-cert", /* 0x1A */ - "application/x-x509-user-cert", /* 0x1B */ - "image/*", /* 0x1C */ - "image/gif", /* 0x1D */ - "image/jpeg", /* 0x1E */ - "image/tiff", /* 0x1F */ - "image/png", /* 0x20 */ - "image/vnd.wap.wbmp", /* 0x21 */ - "application/vnd.wap.multipart.*", /* 0x22 */ - "application/vnd.wap.multipart.mixed", /* 0x23 */ - "application/vnd.wap.multipart.form-data", /* 0x24 */ - "application/vnd.wap.multipart.byteranges", /* 0x25 */ - "application/vnd.wap.multipart.alternative", /* 0x26 */ - "application/xml", /* 0x27 */ - "text/xml", /* 0x28 */ - "application/vnd.wap.wbxml", /* 0x29 */ - "application/x-x968-cross-cert", /* 0x2A */ - "application/x-x968-ca-cert", /* 0x2B */ - "application/x-x968-user-cert", /* 0x2C */ - "text/vnd.wap.si", /* 0x2D */ - "application/vnd.wap.sic", /* 0x2E */ - "text/vnd.wap.sl", /* 0x2F */ - "application/vnd.wap.slc", /* 0x30 */ - "text/vnd.wap.co", /* 0x31 */ - "application/vnd.wap.coc", /* 0x32 */ - "application/vnd.wap.multipart.related", /* 0x33 */ - "application/vnd.wap.sia", /* 0x34 */ - "text/vnd.wap.connectivity-xml", /* 0x35 */ - "application/vnd.wap.connectivity-wbxml", /* 0x36 */ - "application/pkcs7-mime", /* 0x37 */ - "application/vnd.wap.hashed-certificate", /* 0x38 */ - "application/vnd.wap.signed-certificate", /* 0x39 */ - "application/vnd.wap.cert-response", /* 0x3A */ - "application/xhtml+xml", /* 0x3B */ - "application/wml+xml", /* 0x3C */ - "text/css", /* 0x3D */ - "application/vnd.wap.mms-message", /* 0x3E */ - "application/vnd.wap.rollover-certificate", /* 0x3F */ - "application/vnd.wap.locc+wbxml", /* 0x40 */ - "application/vnd.wap.loc+xml", /* 0x41 */ - "application/vnd.syncml.dm+wbxml", /* 0x42 */ - "application/vnd.syncml.dm+xml", /* 0x43 */ - "application/vnd.syncml.notification", /* 0x44 */ - "application/vnd.wap.xhtml+xml", /* 0x45 */ - "application/vnd.wv.csp.cir", /* 0x46 */ - "application/vnd.oma.dd+xml", /* 0x47 */ - "application/vnd.oma.drm.message", /* 0x48 */ - "application/vnd.oma.drm.content", /* 0x49 */ - "application/vnd.oma.drm.rights+xml", /* 0x4A */ - "application/vnd.oma.drm.rights+wbxml", /* 0x4B */ - "application/vnd.wv.csp+xml", /* 0x4C */ - "application/vnd.wv.csp+wbxml", /* 0x4D */ - "application/vnd.syncml.ds.notification", /* 0x4E */ - "audio/*", /* 0x4F */ - "video/*", /* 0x50 */ - "application/vnd.oma.dd2+xml", /* 0x51 */ - "application/mikey" /* 0x52 */ - }; -} diff --git a/mms-common/java/com/android/mmscommon/mms/pdu/PduParser.java b/mms-common/java/com/android/mmscommon/mms/pdu/PduParser.java deleted file mode 100644 index 6a58ba691..000000000 --- a/mms-common/java/com/android/mmscommon/mms/pdu/PduParser.java +++ /dev/null @@ -1,1874 +0,0 @@ -/* - * Copyright (C) 2007-2008 Esmertec AG. - * Copyright (C) 2007-2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.mmscommon.mms.pdu; - -import com.android.mmscommon.CharacterSets; -import com.android.mmscommon.ContentType; -import com.android.mmscommon.EncodedStringValue; -import com.android.mmscommon.InvalidHeaderValueException; -import com.android.mmscommon.PduHeaders; - -import android.util.Config; -import android.util.Log; -import android.util.base64.Base64; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.UnsupportedEncodingException; -import java.util.Arrays; -import java.util.HashMap; - -public class PduParser { - /** - * The next are WAP values defined in WSP specification. - */ - private static final int QUOTE = 127; - private static final int LENGTH_QUOTE = 31; - private static final int TEXT_MIN = 32; - private static final int TEXT_MAX = 127; - private static final int SHORT_INTEGER_MAX = 127; - private static final int SHORT_LENGTH_MAX = 30; - private static final int LONG_INTEGER_LENGTH_MAX = 8; - private static final int QUOTED_STRING_FLAG = 34; - private static final int END_STRING_FLAG = 0x00; - //The next two are used by the interface "parseWapString" to - //distinguish Text-String and Quoted-String. - private static final int TYPE_TEXT_STRING = 0; - private static final int TYPE_QUOTED_STRING = 1; - private static final int TYPE_TOKEN_STRING = 2; - - /** - * Specify the part position. - */ - private static final int THE_FIRST_PART = 0; - private static final int THE_LAST_PART = 1; - - /** - * The pdu data. - */ - private ByteArrayInputStream mPduDataStream = null; - - /** - * Store pdu headers - */ - private PduHeaders mHeaders = null; - - /** - * Store pdu parts. - */ - private PduBody mBody = null; - - /** - * Store the "type" parameter in "Content-Type" header field. - */ - private static byte[] mTypeParam = null; - - /** - * Store the "start" parameter in "Content-Type" header field. - */ - private static byte[] mStartParam = null; - - /** - * The log tag. - */ - private static final String LOG_TAG = "PduParser"; - private static final boolean DEBUG = false; - private static final boolean LOCAL_LOGV = DEBUG ? Config.LOGD : Config.LOGV; - - /** - * Constructor. - * - * @param pduDataStream pdu data to be parsed - */ - public PduParser(byte[] pduDataStream) { - mPduDataStream = new ByteArrayInputStream(pduDataStream); - } - - /** - * Parse the pdu. - * - * @return the pdu structure if parsing successfully. - * null if parsing error happened or mandatory fields are not set. - */ - public GenericPdu parse(){ - if (mPduDataStream == null) { - return null; - } - - /* parse headers */ - mHeaders = parseHeaders(mPduDataStream); - if (null == mHeaders) { - // Parse headers failed. - return null; - } - - /* get the message type */ - int messageType = mHeaders.getOctet(PduHeaders.MESSAGE_TYPE); - - /* check mandatory header fields */ - if (false == checkMandatoryHeader(mHeaders)) { - log("check mandatory headers failed!"); - return null; - } - - if ((PduHeaders.MESSAGE_TYPE_SEND_REQ == messageType) || - (PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF == messageType)) { - /* need to parse the parts */ - mBody = parseParts(mPduDataStream); - if (null == mBody) { - // Parse parts failed. - return null; - } - } - - switch (messageType) { - case PduHeaders.MESSAGE_TYPE_SEND_REQ: - SendReq sendReq = new SendReq(mHeaders, mBody); - return sendReq; - case PduHeaders.MESSAGE_TYPE_SEND_CONF: - SendConf sendConf = new SendConf(mHeaders); - return sendConf; - case PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND: - NotificationInd notificationInd = - new NotificationInd(mHeaders); - return notificationInd; - case PduHeaders.MESSAGE_TYPE_NOTIFYRESP_IND: - NotifyRespInd notifyRespInd = - new NotifyRespInd(mHeaders); - return notifyRespInd; - case PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF: - RetrieveConf retrieveConf = - new RetrieveConf(mHeaders, mBody); - - byte[] contentType = retrieveConf.getContentType(); - if (null == contentType) { - return null; - } - String ctTypeStr = new String(contentType); - if (ctTypeStr.equals(ContentType.MULTIPART_MIXED) - || ctTypeStr.equals(ContentType.MULTIPART_RELATED) - || ctTypeStr.equals(ContentType.MULTIPART_ALTERNATIVE)) { - // The MMS content type must be "application/vnd.wap.multipart.mixed" - // or "application/vnd.wap.multipart.related" - // or "application/vnd.wap.multipart.alternative" - return retrieveConf; - } - return null; - case PduHeaders.MESSAGE_TYPE_DELIVERY_IND: - DeliveryInd deliveryInd = - new DeliveryInd(mHeaders); - return deliveryInd; - case PduHeaders.MESSAGE_TYPE_ACKNOWLEDGE_IND: - AcknowledgeInd acknowledgeInd = - new AcknowledgeInd(mHeaders); - return acknowledgeInd; - case PduHeaders.MESSAGE_TYPE_READ_ORIG_IND: - ReadOrigInd readOrigInd = - new ReadOrigInd(mHeaders); - return readOrigInd; - case PduHeaders.MESSAGE_TYPE_READ_REC_IND: - ReadRecInd readRecInd = - new ReadRecInd(mHeaders); - return readRecInd; - default: - log("Parser doesn't support this message type in this version!"); - return null; - } - } - - /** - * Parse pdu headers. - * - * @param pduDataStream pdu data input stream - * @return headers in PduHeaders structure, null when parse fail - */ - protected PduHeaders parseHeaders(ByteArrayInputStream pduDataStream){ - if (pduDataStream == null) { - return null; - } - - boolean keepParsing = true; - PduHeaders headers = new PduHeaders(); - - while (keepParsing && (pduDataStream.available() > 0)) { - int headerField = extractByteValue(pduDataStream); - switch (headerField) { - case PduHeaders.MESSAGE_TYPE: - { - int messageType = extractByteValue(pduDataStream); - switch (messageType) { - // We don't support these kind of messages now. - case PduHeaders.MESSAGE_TYPE_FORWARD_REQ: - case PduHeaders.MESSAGE_TYPE_FORWARD_CONF: - case PduHeaders.MESSAGE_TYPE_MBOX_STORE_REQ: - case PduHeaders.MESSAGE_TYPE_MBOX_STORE_CONF: - case PduHeaders.MESSAGE_TYPE_MBOX_VIEW_REQ: - case PduHeaders.MESSAGE_TYPE_MBOX_VIEW_CONF: - case PduHeaders.MESSAGE_TYPE_MBOX_UPLOAD_REQ: - case PduHeaders.MESSAGE_TYPE_MBOX_UPLOAD_CONF: - case PduHeaders.MESSAGE_TYPE_MBOX_DELETE_REQ: - case PduHeaders.MESSAGE_TYPE_MBOX_DELETE_CONF: - case PduHeaders.MESSAGE_TYPE_MBOX_DESCR: - case PduHeaders.MESSAGE_TYPE_DELETE_REQ: - case PduHeaders.MESSAGE_TYPE_DELETE_CONF: - case PduHeaders.MESSAGE_TYPE_CANCEL_REQ: - case PduHeaders.MESSAGE_TYPE_CANCEL_CONF: - return null; - } - try { - headers.setOctet(messageType, headerField); - } catch(InvalidHeaderValueException e) { - log("Set invalid Octet value: " + messageType + - " into the header filed: " + headerField); - return null; - } catch(RuntimeException e) { - log(headerField + "is not Octet header field!"); - return null; - } - break; - } - /* Octect value */ - case PduHeaders.REPORT_ALLOWED: - case PduHeaders.ADAPTATION_ALLOWED: - case PduHeaders.DELIVERY_REPORT: - case PduHeaders.DRM_CONTENT: - case PduHeaders.DISTRIBUTION_INDICATOR: - case PduHeaders.QUOTAS: - case PduHeaders.READ_REPORT: - case PduHeaders.STORE: - case PduHeaders.STORED: - case PduHeaders.TOTALS: - case PduHeaders.SENDER_VISIBILITY: - case PduHeaders.READ_STATUS: - case PduHeaders.CANCEL_STATUS: - case PduHeaders.PRIORITY: - case PduHeaders.STATUS: - case PduHeaders.REPLY_CHARGING: - case PduHeaders.MM_STATE: - case PduHeaders.RECOMMENDED_RETRIEVAL_MODE: - case PduHeaders.CONTENT_CLASS: - case PduHeaders.RETRIEVE_STATUS: - case PduHeaders.STORE_STATUS: - /** - * The following field has a different value when - * used in the M-Mbox-Delete.conf and M-Delete.conf PDU. - * For now we ignore this fact, since we do not support these PDUs - */ - case PduHeaders.RESPONSE_STATUS: - { - int value = extractByteValue(pduDataStream); - - try { - headers.setOctet(value, headerField); - } catch(InvalidHeaderValueException e) { - log("Set invalid Octet value: " + value + - " into the header filed: " + headerField); - return null; - } catch(RuntimeException e) { - log(headerField + "is not Octet header field!"); - return null; - } - break; - } - - /* Long-Integer */ - case PduHeaders.DATE: - case PduHeaders.REPLY_CHARGING_SIZE: - case PduHeaders.MESSAGE_SIZE: - { - try { - long value = parseLongInteger(pduDataStream); - headers.setLongInteger(value, headerField); - } catch(RuntimeException e) { - log(headerField + "is not Long-Integer header field!"); - return null; - } - break; - } - - /* Integer-Value */ - case PduHeaders.MESSAGE_COUNT: - case PduHeaders.START: - case PduHeaders.LIMIT: - { - try { - long value = parseIntegerValue(pduDataStream); - headers.setLongInteger(value, headerField); - } catch(RuntimeException e) { - log(headerField + "is not Long-Integer header field!"); - return null; - } - break; - } - - /* Text-String */ - case PduHeaders.TRANSACTION_ID: - case PduHeaders.REPLY_CHARGING_ID: - case PduHeaders.AUX_APPLIC_ID: - case PduHeaders.APPLIC_ID: - case PduHeaders.REPLY_APPLIC_ID: - /** - * The next three header fields are email addresses - * as defined in RFC2822, - * not including the characters "<" and ">" - */ - case PduHeaders.MESSAGE_ID: - case PduHeaders.REPLACE_ID: - case PduHeaders.CANCEL_ID: - /** - * The following field has a different value when - * used in the M-Mbox-Delete.conf and M-Delete.conf PDU. - * For now we ignore this fact, since we do not support these PDUs - */ - case PduHeaders.CONTENT_LOCATION: - { - byte[] value = parseWapString(pduDataStream, TYPE_TEXT_STRING); - if (null != value) { - try { - headers.setTextString(value, headerField); - } catch(NullPointerException e) { - log("null pointer error!"); - } catch(RuntimeException e) { - log(headerField + "is not Text-String header field!"); - return null; - } - } - break; - } - - /* Encoded-string-value */ - case PduHeaders.SUBJECT: - case PduHeaders.RECOMMENDED_RETRIEVAL_MODE_TEXT: - case PduHeaders.RETRIEVE_TEXT: - case PduHeaders.STATUS_TEXT: - case PduHeaders.STORE_STATUS_TEXT: - /* the next one is not support - * M-Mbox-Delete.conf and M-Delete.conf now */ - case PduHeaders.RESPONSE_TEXT: - { - EncodedStringValue value = - parseEncodedStringValue(pduDataStream); - if (null != value) { - try { - headers.setEncodedStringValue(value, headerField); - } catch(NullPointerException e) { - log("null pointer error!"); - } catch (RuntimeException e) { - log(headerField + "is not Encoded-String-Value header field!"); - return null; - } - } - break; - } - - /* Addressing model */ - case PduHeaders.BCC: - case PduHeaders.CC: - case PduHeaders.TO: - { - EncodedStringValue value = - parseEncodedStringValue(pduDataStream); - if (null != value) { - byte[] address = value.getTextString(); - if (null != address) { - String str = new String(address); - int endIndex = str.indexOf("/"); - if (endIndex > 0) { - str = str.substring(0, endIndex); - } - try { - value.setTextString(str.getBytes()); - } catch(NullPointerException e) { - log("null pointer error!"); - return null; - } - } - - try { - headers.appendEncodedStringValue(value, headerField); - } catch(NullPointerException e) { - log("null pointer error!"); - } catch(RuntimeException e) { - log(headerField + "is not Encoded-String-Value header field!"); - return null; - } - } - break; - } - - /* Value-length - * (Absolute-token Date-value | Relative-token Delta-seconds-value) */ - case PduHeaders.DELIVERY_TIME: - case PduHeaders.EXPIRY: - case PduHeaders.REPLY_CHARGING_DEADLINE: - { - /* parse Value-length */ - parseValueLength(pduDataStream); - - /* Absolute-token or Relative-token */ - int token = extractByteValue(pduDataStream); - - /* Date-value or Delta-seconds-value */ - long timeValue; - try { - timeValue = parseLongInteger(pduDataStream); - } catch(RuntimeException e) { - log(headerField + "is not Long-Integer header field!"); - return null; - } - if (PduHeaders.VALUE_RELATIVE_TOKEN == token) { - /* need to convert the Delta-seconds-value - * into Date-value */ - timeValue = System.currentTimeMillis()/1000 + timeValue; - } - - try { - headers.setLongInteger(timeValue, headerField); - } catch(RuntimeException e) { - log(headerField + "is not Long-Integer header field!"); - return null; - } - break; - } - - case PduHeaders.FROM: { - /* From-value = - * Value-length - * (Address-present-token Encoded-string-value | Insert-address-token) - */ - EncodedStringValue from = null; - parseValueLength(pduDataStream); /* parse value-length */ - - /* Address-present-token or Insert-address-token */ - int fromToken = extractByteValue(pduDataStream); - - /* Address-present-token or Insert-address-token */ - if (PduHeaders.FROM_ADDRESS_PRESENT_TOKEN == fromToken) { - /* Encoded-string-value */ - from = parseEncodedStringValue(pduDataStream); - if (null != from) { - byte[] address = from.getTextString(); - if (null != address) { - String str = new String(address); - int endIndex = str.indexOf("/"); - if (endIndex > 0) { - str = str.substring(0, endIndex); - } - try { - from.setTextString(str.getBytes()); - } catch(NullPointerException e) { - log("null pointer error!"); - return null; - } - } - } - } else { - try { - from = new EncodedStringValue( - PduHeaders.FROM_INSERT_ADDRESS_TOKEN_STR.getBytes()); - } catch(NullPointerException e) { - log(headerField + "is not Encoded-String-Value header field!"); - return null; - } - } - - try { - headers.setEncodedStringValue(from, PduHeaders.FROM); - } catch(NullPointerException e) { - log("null pointer error!"); - } catch(RuntimeException e) { - log(headerField + "is not Encoded-String-Value header field!"); - return null; - } - break; - } - - case PduHeaders.MESSAGE_CLASS: { - /* Message-class-value = Class-identifier | Token-text */ - pduDataStream.mark(1); - int messageClass = extractByteValue(pduDataStream); - - if (messageClass >= PduHeaders.MESSAGE_CLASS_PERSONAL) { - /* Class-identifier */ - try { - if (PduHeaders.MESSAGE_CLASS_PERSONAL == messageClass) { - headers.setTextString( - PduHeaders.MESSAGE_CLASS_PERSONAL_STR.getBytes(), - PduHeaders.MESSAGE_CLASS); - } else if (PduHeaders.MESSAGE_CLASS_ADVERTISEMENT == messageClass) { - headers.setTextString( - PduHeaders.MESSAGE_CLASS_ADVERTISEMENT_STR.getBytes(), - PduHeaders.MESSAGE_CLASS); - } else if (PduHeaders.MESSAGE_CLASS_INFORMATIONAL == messageClass) { - headers.setTextString( - PduHeaders.MESSAGE_CLASS_INFORMATIONAL_STR.getBytes(), - PduHeaders.MESSAGE_CLASS); - } else if (PduHeaders.MESSAGE_CLASS_AUTO == messageClass) { - headers.setTextString( - PduHeaders.MESSAGE_CLASS_AUTO_STR.getBytes(), - PduHeaders.MESSAGE_CLASS); - } - } catch(NullPointerException e) { - log("null pointer error!"); - } catch(RuntimeException e) { - log(headerField + "is not Text-String header field!"); - return null; - } - } else { - /* Token-text */ - pduDataStream.reset(); - byte[] messageClassString = parseWapString(pduDataStream, TYPE_TEXT_STRING); - if (null != messageClassString) { - try { - headers.setTextString(messageClassString, PduHeaders.MESSAGE_CLASS); - } catch(NullPointerException e) { - log("null pointer error!"); - } catch(RuntimeException e) { - log(headerField + "is not Text-String header field!"); - return null; - } - } - } - break; - } - - case PduHeaders.MMS_VERSION: { - int version = parseShortInteger(pduDataStream); - - try { - headers.setOctet(version, PduHeaders.MMS_VERSION); - } catch(InvalidHeaderValueException e) { - log("Set invalid Octet value: " + version + - " into the header filed: " + headerField); - return null; - } catch(RuntimeException e) { - log(headerField + "is not Octet header field!"); - return null; - } - break; - } - - case PduHeaders.PREVIOUSLY_SENT_BY: { - /* Previously-sent-by-value = - * Value-length Forwarded-count-value Encoded-string-value */ - /* parse value-length */ - parseValueLength(pduDataStream); - - /* parse Forwarded-count-value */ - try { - parseIntegerValue(pduDataStream); - } catch(RuntimeException e) { - log(headerField + " is not Integer-Value"); - return null; - } - - /* parse Encoded-string-value */ - EncodedStringValue previouslySentBy = - parseEncodedStringValue(pduDataStream); - if (null != previouslySentBy) { - try { - headers.setEncodedStringValue(previouslySentBy, - PduHeaders.PREVIOUSLY_SENT_BY); - } catch(NullPointerException e) { - log("null pointer error!"); - } catch(RuntimeException e) { - log(headerField + "is not Encoded-String-Value header field!"); - return null; - } - } - break; - } - - case PduHeaders.PREVIOUSLY_SENT_DATE: { - /* Previously-sent-date-value = - * Value-length Forwarded-count-value Date-value */ - /* parse value-length */ - parseValueLength(pduDataStream); - - /* parse Forwarded-count-value */ - try { - parseIntegerValue(pduDataStream); - } catch(RuntimeException e) { - log(headerField + " is not Integer-Value"); - return null; - } - - /* Date-value */ - try { - long perviouslySentDate = parseLongInteger(pduDataStream); - headers.setLongInteger(perviouslySentDate, - PduHeaders.PREVIOUSLY_SENT_DATE); - } catch(RuntimeException e) { - log(headerField + "is not Long-Integer header field!"); - return null; - } - break; - } - - case PduHeaders.MM_FLAGS: { - /* MM-flags-value = - * Value-length - * ( Add-token | Remove-token | Filter-token ) - * Encoded-string-value - */ - - /* parse Value-length */ - parseValueLength(pduDataStream); - - /* Add-token | Remove-token | Filter-token */ - extractByteValue(pduDataStream); - - /* Encoded-string-value */ - parseEncodedStringValue(pduDataStream); - - /* not store this header filed in "headers", - * because now PduHeaders doesn't support it */ - break; - } - - /* Value-length - * (Message-total-token | Size-total-token) Integer-Value */ - case PduHeaders.MBOX_TOTALS: - case PduHeaders.MBOX_QUOTAS: - { - /* Value-length */ - parseValueLength(pduDataStream); - - /* Message-total-token | Size-total-token */ - extractByteValue(pduDataStream); - - /*Integer-Value*/ - try { - parseIntegerValue(pduDataStream); - } catch(RuntimeException e) { - log(headerField + " is not Integer-Value"); - return null; - } - - /* not store these headers filed in "headers", - because now PduHeaders doesn't support them */ - break; - } - - case PduHeaders.ELEMENT_DESCRIPTOR: { - parseContentType(pduDataStream, null); - - /* not store this header filed in "headers", - because now PduHeaders doesn't support it */ - break; - } - - case PduHeaders.CONTENT_TYPE: { - HashMap map = - new HashMap(); - byte[] contentType = - parseContentType(pduDataStream, map); - - if (null != contentType) { - try { - headers.setTextString(contentType, PduHeaders.CONTENT_TYPE); - } catch(NullPointerException e) { - log("null pointer error!"); - } catch(RuntimeException e) { - log(headerField + "is not Text-String header field!"); - return null; - } - } - - /* get start parameter */ - mStartParam = (byte[]) map.get(PduPart.P_START); - - /* get charset parameter */ - mTypeParam= (byte[]) map.get(PduPart.P_TYPE); - - keepParsing = false; - break; - } - - case PduHeaders.CONTENT: - case PduHeaders.ADDITIONAL_HEADERS: - case PduHeaders.ATTRIBUTES: - default: { - log("Unknown header"); - } - } - } - - return headers; - } - - /** - * Parse pdu parts. - * - * @param pduDataStream pdu data input stream - * @return parts in PduBody structure - */ - protected static PduBody parseParts(ByteArrayInputStream pduDataStream) { - if (pduDataStream == null) { - return null; - } - - int count = parseUnsignedInt(pduDataStream); // get the number of parts - PduBody body = new PduBody(); - - for (int i = 0 ; i < count ; i++) { - int headerLength = parseUnsignedInt(pduDataStream); - int dataLength = parseUnsignedInt(pduDataStream); - PduPart part = new PduPart(); - int startPos = pduDataStream.available(); - if (startPos <= 0) { - // Invalid part. - return null; - } - - /* parse part's content-type */ - HashMap map = new HashMap(); - byte[] contentType = parseContentType(pduDataStream, map); - if (null != contentType) { - part.setContentType(contentType); - } else { - part.setContentType((PduContentTypes.contentTypes[0]).getBytes()); //"*/*" - } - - /* get name parameter */ - byte[] name = (byte[]) map.get(PduPart.P_NAME); - if (null != name) { - part.setName(name); - } - - /* get charset parameter */ - Integer charset = (Integer) map.get(PduPart.P_CHARSET); - if (null != charset) { - part.setCharset(charset); - } - - /* parse part's headers */ - int endPos = pduDataStream.available(); - int partHeaderLen = headerLength - (startPos - endPos); - if (partHeaderLen > 0) { - if (false == parsePartHeaders(pduDataStream, part, partHeaderLen)) { - // Parse part header faild. - return null; - } - } else if (partHeaderLen < 0) { - // Invalid length of content-type. - return null; - } - - /* FIXME: check content-id, name, filename and content location, - * if not set anyone of them, generate a default content-location - */ - if ((null == part.getContentLocation()) - && (null == part.getName()) - && (null == part.getFilename()) - && (null == part.getContentId())) { - part.setContentLocation(Long.toOctalString( - System.currentTimeMillis()).getBytes()); - } - - /* get part's data */ - if (dataLength > 0) { - byte[] partData = new byte[dataLength]; - pduDataStream.read(partData, 0, dataLength); - // Check Content-Transfer-Encoding. - byte[] partDataEncoding = part.getContentTransferEncoding(); - if (null != partDataEncoding) { - String encoding = new String(partDataEncoding); - if (encoding.equalsIgnoreCase(PduPart.P_BASE64)) { - // Decode "base64" into "binary". - partData = Base64.decode(partData, Base64.DEFAULT); - } else if (encoding.equalsIgnoreCase(PduPart.P_QUOTED_PRINTABLE)) { - // Decode "quoted-printable" into "binary". - partData = QuotedPrintable.decodeQuotedPrintable(partData); - } else { - // "binary" is the default encoding. - } - } - if (null == partData) { - log("Decode part data error!"); - return null; - } - part.setData(partData); - } - - /* add this part to body */ - if (THE_FIRST_PART == checkPartPosition(part)) { - /* this is the first part */ - body.addPart(0, part); - } else { - /* add the part to the end */ - body.addPart(part); - } - } - - return body; - } - - /** - * Log status. - * - * @param text log information - */ - private static void log(String text) { - if (LOCAL_LOGV) { - Log.v(LOG_TAG, text); - } - } - - /** - * Parse unsigned integer. - * - * @param pduDataStream pdu data input stream - * @return the integer, -1 when failed - */ - protected static int parseUnsignedInt(ByteArrayInputStream pduDataStream) { - /** - * From wap-230-wsp-20010705-a.pdf - * The maximum size of a uintvar is 32 bits. - * So it will be encoded in no more than 5 octets. - */ - assert(null != pduDataStream); - int result = 0; - int temp = pduDataStream.read(); - if (temp == -1) { - return temp; - } - - while((temp & 0x80) != 0) { - result = result << 7; - result |= temp & 0x7F; - temp = pduDataStream.read(); - if (temp == -1) { - return temp; - } - } - - result = result << 7; - result |= temp & 0x7F; - - return result; - } - - /** - * Parse value length. - * - * @param pduDataStream pdu data input stream - * @return the integer - */ - protected static int parseValueLength(ByteArrayInputStream pduDataStream) { - /** - * From wap-230-wsp-20010705-a.pdf - * Value-length = Short-length | (Length-quote Length) - * Short-length = - * Length-quote = - * Length = Uintvar-integer - * Uintvar-integer = 1*5 OCTET - */ - assert(null != pduDataStream); - int temp = pduDataStream.read(); - assert(-1 != temp); - int first = temp & 0xFF; - - if (first <= SHORT_LENGTH_MAX) { - return first; - } else if (first == LENGTH_QUOTE) { - return parseUnsignedInt(pduDataStream); - } - - throw new RuntimeException ("Value length > LENGTH_QUOTE!"); - } - - /** - * Parse encoded string value. - * - * @param pduDataStream pdu data input stream - * @return the EncodedStringValue - */ - protected static EncodedStringValue parseEncodedStringValue(ByteArrayInputStream pduDataStream){ - /** - * From OMA-TS-MMS-ENC-V1_3-20050927-C.pdf - * Encoded-string-value = Text-string | Value-length Char-set Text-string - */ - assert(null != pduDataStream); - pduDataStream.mark(1); - EncodedStringValue returnValue = null; - int charset = 0; - int temp = pduDataStream.read(); - assert(-1 != temp); - int first = temp & 0xFF; - - pduDataStream.reset(); - if (first < TEXT_MIN) { - parseValueLength(pduDataStream); - - charset = parseShortInteger(pduDataStream); //get the "Charset" - } - - byte[] textString = parseWapString(pduDataStream, TYPE_TEXT_STRING); - - try { - if (0 != charset) { - returnValue = new EncodedStringValue(charset, textString); - } else { - returnValue = new EncodedStringValue(textString); - } - } catch(Exception e) { - return null; - } - - return returnValue; - } - - /** - * Parse Text-String or Quoted-String. - * - * @param pduDataStream pdu data input stream - * @param stringType TYPE_TEXT_STRING or TYPE_QUOTED_STRING - * @return the string without End-of-string in byte array - */ - protected static byte[] parseWapString(ByteArrayInputStream pduDataStream, - int stringType) { - assert(null != pduDataStream); - /** - * From wap-230-wsp-20010705-a.pdf - * Text-string = [Quote] *TEXT End-of-string - * If the first character in the TEXT is in the range of 128-255, - * a Quote character must precede it. - * Otherwise the Quote character must be omitted. - * The Quote is not part of the contents. - * Quote = - * End-of-string = - * - * Quoted-string = *TEXT End-of-string - * - * Token-text = Token End-of-string - */ - - // Mark supposed beginning of Text-string - // We will have to mark again if first char is QUOTE or QUOTED_STRING_FLAG - pduDataStream.mark(1); - - // Check first char - int temp = pduDataStream.read(); - assert(-1 != temp); - if ((TYPE_QUOTED_STRING == stringType) && - (QUOTED_STRING_FLAG == temp)) { - // Mark again if QUOTED_STRING_FLAG and ignore it - pduDataStream.mark(1); - } else if ((TYPE_TEXT_STRING == stringType) && - (QUOTE == temp)) { - // Mark again if QUOTE and ignore it - pduDataStream.mark(1); - } else { - // Otherwise go back to origin - pduDataStream.reset(); - } - - // We are now definitely at the beginning of string - /** - * Return *TOKEN or *TEXT (Text-String without QUOTE, - * Quoted-String without QUOTED_STRING_FLAG and without End-of-string) - */ - return getWapString(pduDataStream, stringType); - } - - /** - * Check TOKEN data defined in RFC2616. - * @param ch checking data - * @return true when ch is TOKEN, false when ch is not TOKEN - */ - protected static boolean isTokenCharacter(int ch) { - /** - * Token = 1* - * separators = "("(40) | ")"(41) | "<"(60) | ">"(62) | "@"(64) - * | ","(44) | ";"(59) | ":"(58) | "\"(92) | <">(34) - * | "/"(47) | "["(91) | "]"(93) | "?"(63) | "="(61) - * | "{"(123) | "}"(125) | SP(32) | HT(9) - * CHAR = - * CTL = - * SP = - * HT = - */ - if((ch < 33) || (ch > 126)) { - return false; - } - - switch(ch) { - case '"': /* '"' */ - case '(': /* '(' */ - case ')': /* ')' */ - case ',': /* ',' */ - case '/': /* '/' */ - case ':': /* ':' */ - case ';': /* ';' */ - case '<': /* '<' */ - case '=': /* '=' */ - case '>': /* '>' */ - case '?': /* '?' */ - case '@': /* '@' */ - case '[': /* '[' */ - case '\\': /* '\' */ - case ']': /* ']' */ - case '{': /* '{' */ - case '}': /* '}' */ - return false; - } - - return true; - } - - /** - * Check TEXT data defined in RFC2616. - * @param ch checking data - * @return true when ch is TEXT, false when ch is not TEXT - */ - protected static boolean isText(int ch) { - /** - * TEXT = - * CTL = - * LWS = [CRLF] 1*( SP | HT ) - * CRLF = CR LF - * CR = - * LF = - */ - if(((ch >= 32) && (ch <= 126)) || ((ch >= 128) && (ch <= 255))) { - return true; - } - - switch(ch) { - case '\t': /* '\t' */ - case '\n': /* '\n' */ - case '\r': /* '\r' */ - return true; - } - - return false; - } - - protected static byte[] getWapString(ByteArrayInputStream pduDataStream, - int stringType) { - assert(null != pduDataStream); - ByteArrayOutputStream out = new ByteArrayOutputStream(); - int temp = pduDataStream.read(); - assert(-1 != temp); - while((-1 != temp) && ('\0' != temp)) { - // check each of the character - if (stringType == TYPE_TOKEN_STRING) { - if (isTokenCharacter(temp)) { - out.write(temp); - } - } else { - if (isText(temp)) { - out.write(temp); - } - } - - temp = pduDataStream.read(); - assert(-1 != temp); - } - - if (out.size() > 0) { - return out.toByteArray(); - } - - return null; - } - - /** - * Extract a byte value from the input stream. - * - * @param pduDataStream pdu data input stream - * @return the byte - */ - protected static int extractByteValue(ByteArrayInputStream pduDataStream) { - assert(null != pduDataStream); - int temp = pduDataStream.read(); - assert(-1 != temp); - return temp & 0xFF; - } - - /** - * Parse Short-Integer. - * - * @param pduDataStream pdu data input stream - * @return the byte - */ - protected static int parseShortInteger(ByteArrayInputStream pduDataStream) { - /** - * From wap-230-wsp-20010705-a.pdf - * Short-integer = OCTET - * Integers in range 0-127 shall be encoded as a one - * octet value with the most significant bit set to one (1xxx xxxx) - * and with the value in the remaining least significant bits. - */ - assert(null != pduDataStream); - int temp = pduDataStream.read(); - assert(-1 != temp); - return temp & 0x7F; - } - - /** - * Parse Long-Integer. - * - * @param pduDataStream pdu data input stream - * @return long integer - */ - protected static long parseLongInteger(ByteArrayInputStream pduDataStream) { - /** - * From wap-230-wsp-20010705-a.pdf - * Long-integer = Short-length Multi-octet-integer - * The Short-length indicates the length of the Multi-octet-integer - * Multi-octet-integer = 1*30 OCTET - * The content octets shall be an unsigned integer value - * with the most significant octet encoded first (big-endian representation). - * The minimum number of octets must be used to encode the value. - * Short-length = - */ - assert(null != pduDataStream); - int temp = pduDataStream.read(); - assert(-1 != temp); - int count = temp & 0xFF; - - if (count > LONG_INTEGER_LENGTH_MAX) { - throw new RuntimeException("Octet count greater than 8 and I can't represent that!"); - } - - long result = 0; - - for (int i = 0 ; i < count ; i++) { - temp = pduDataStream.read(); - assert(-1 != temp); - result <<= 8; - result += (temp & 0xFF); - } - - return result; - } - - /** - * Parse Integer-Value. - * - * @param pduDataStream pdu data input stream - * @return long integer - */ - protected static long parseIntegerValue(ByteArrayInputStream pduDataStream) { - /** - * From wap-230-wsp-20010705-a.pdf - * Integer-Value = Short-integer | Long-integer - */ - assert(null != pduDataStream); - pduDataStream.mark(1); - int temp = pduDataStream.read(); - assert(-1 != temp); - pduDataStream.reset(); - if (temp > SHORT_INTEGER_MAX) { - return parseShortInteger(pduDataStream); - } else { - return parseLongInteger(pduDataStream); - } - } - - /** - * To skip length of the wap value. - * - * @param pduDataStream pdu data input stream - * @param length area size - * @return the values in this area - */ - protected static int skipWapValue(ByteArrayInputStream pduDataStream, int length) { - assert(null != pduDataStream); - byte[] area = new byte[length]; - int readLen = pduDataStream.read(area, 0, length); - if (readLen < length) { //The actually read length is lower than the length - return -1; - } else { - return readLen; - } - } - - /** - * Parse content type parameters. For now we just support - * four parameters used in mms: "type", "start", "name", "charset". - * - * @param pduDataStream pdu data input stream - * @param map to store parameters of Content-Type field - * @param length length of all the parameters - */ - protected static void parseContentTypeParams(ByteArrayInputStream pduDataStream, - HashMap map, Integer length) { - /** - * From wap-230-wsp-20010705-a.pdf - * Parameter = Typed-parameter | Untyped-parameter - * Typed-parameter = Well-known-parameter-token Typed-value - * the actual expected type of the value is implied by the well-known parameter - * Well-known-parameter-token = Integer-value - * the code values used for parameters are specified in the Assigned Numbers appendix - * Typed-value = Compact-value | Text-value - * In addition to the expected type, there may be no value. - * If the value cannot be encoded using the expected type, it shall be encoded as text. - * Compact-value = Integer-value | - * Date-value | Delta-seconds-value | Q-value | Version-value | - * Uri-value - * Untyped-parameter = Token-text Untyped-value - * the type of the value is unknown, but it shall be encoded as an integer, - * if that is possible. - * Untyped-value = Integer-value | Text-value - */ - assert(null != pduDataStream); - assert(length > 0); - - int startPos = pduDataStream.available(); - int tempPos = 0; - int lastLen = length; - while(0 < lastLen) { - int param = pduDataStream.read(); - assert(-1 != param); - lastLen--; - - switch (param) { - /** - * From rfc2387, chapter 3.1 - * The type parameter must be specified and its value is the MIME media - * type of the "root" body part. It permits a MIME user agent to - * determine the content-type without reference to the enclosed body - * part. If the value of the type parameter and the root body part's - * content-type differ then the User Agent's behavior is undefined. - * - * From wap-230-wsp-20010705-a.pdf - * type = Constrained-encoding - * Constrained-encoding = Extension-Media | Short-integer - * Extension-media = *TEXT End-of-string - */ - case PduPart.P_TYPE: - case PduPart.P_CT_MR_TYPE: - pduDataStream.mark(1); - int first = extractByteValue(pduDataStream); - pduDataStream.reset(); - if (first > TEXT_MAX) { - // Short-integer (well-known type) - int index = parseShortInteger(pduDataStream); - - if (index < PduContentTypes.contentTypes.length) { - byte[] type = (PduContentTypes.contentTypes[index]).getBytes(); - map.put(PduPart.P_TYPE, type); - } else { - //not support this type, ignore it. - } - } else { - // Text-String (extension-media) - byte[] type = parseWapString(pduDataStream, TYPE_TEXT_STRING); - if ((null != type) && (null != map)) { - map.put(PduPart.P_TYPE, type); - } - } - - tempPos = pduDataStream.available(); - lastLen = length - (startPos - tempPos); - break; - - /** - * From oma-ts-mms-conf-v1_3.pdf, chapter 10.2.3. - * Start Parameter Referring to Presentation - * - * From rfc2387, chapter 3.2 - * The start parameter, if given, is the content-ID of the compound - * object's "root". If not present the "root" is the first body part in - * the Multipart/Related entity. The "root" is the element the - * applications processes first. - * - * From wap-230-wsp-20010705-a.pdf - * start = Text-String - */ - case PduPart.P_START: - case PduPart.P_DEP_START: - byte[] start = parseWapString(pduDataStream, TYPE_TEXT_STRING); - if ((null != start) && (null != map)) { - map.put(PduPart.P_START, start); - } - - tempPos = pduDataStream.available(); - lastLen = length - (startPos - tempPos); - break; - - /** - * From oma-ts-mms-conf-v1_3.pdf - * In creation, the character set SHALL be either us-ascii - * (IANA MIBenum 3) or utf-8 (IANA MIBenum 106)[Unicode]. - * In retrieval, both us-ascii and utf-8 SHALL be supported. - * - * From wap-230-wsp-20010705-a.pdf - * charset = Well-known-charset|Text-String - * Well-known-charset = Any-charset | Integer-value - * Both are encoded using values from Character Set - * Assignments table in Assigned Numbers - * Any-charset = - * Equivalent to the special RFC2616 charset value "*" - */ - case PduPart.P_CHARSET: - pduDataStream.mark(1); - int firstValue = extractByteValue(pduDataStream); - pduDataStream.reset(); - //Check first char - if (((firstValue > TEXT_MIN) && (firstValue < TEXT_MAX)) || - (END_STRING_FLAG == firstValue)) { - //Text-String (extension-charset) - byte[] charsetStr = parseWapString(pduDataStream, TYPE_TEXT_STRING); - try { - int charsetInt = CharacterSets.getMibEnumValue( - new String(charsetStr)); - map.put(PduPart.P_CHARSET, charsetInt); - } catch (UnsupportedEncodingException e) { - // Not a well-known charset, use "*". - Log.e(LOG_TAG, Arrays.toString(charsetStr), e); - map.put(PduPart.P_CHARSET, CharacterSets.ANY_CHARSET); - } - } else { - //Well-known-charset - int charset = (int) parseIntegerValue(pduDataStream); - if (map != null) { - map.put(PduPart.P_CHARSET, charset); - } - } - - tempPos = pduDataStream.available(); - lastLen = length - (startPos - tempPos); - break; - - /** - * From oma-ts-mms-conf-v1_3.pdf - * A name for multipart object SHALL be encoded using name-parameter - * for Content-Type header in WSP multipart headers. - * - * From wap-230-wsp-20010705-a.pdf - * name = Text-String - */ - case PduPart.P_DEP_NAME: - case PduPart.P_NAME: - byte[] name = parseWapString(pduDataStream, TYPE_TEXT_STRING); - if ((null != name) && (null != map)) { - map.put(PduPart.P_NAME, name); - } - - tempPos = pduDataStream.available(); - lastLen = length - (startPos - tempPos); - break; - default: - if (LOCAL_LOGV) { - Log.v(LOG_TAG, "Not supported Content-Type parameter"); - } - if (-1 == skipWapValue(pduDataStream, lastLen)) { - Log.e(LOG_TAG, "Corrupt Content-Type"); - } else { - lastLen = 0; - } - break; - } - } - - if (0 != lastLen) { - Log.e(LOG_TAG, "Corrupt Content-Type"); - } - } - - /** - * Parse content type. - * - * @param pduDataStream pdu data input stream - * @param map to store parameters in Content-Type header field - * @return Content-Type value - */ - protected static byte[] parseContentType(ByteArrayInputStream pduDataStream, - HashMap map) { - /** - * From wap-230-wsp-20010705-a.pdf - * Content-type-value = Constrained-media | Content-general-form - * Content-general-form = Value-length Media-type - * Media-type = (Well-known-media | Extension-Media) *(Parameter) - */ - assert(null != pduDataStream); - - byte[] contentType = null; - pduDataStream.mark(1); - int temp = pduDataStream.read(); - assert(-1 != temp); - pduDataStream.reset(); - - int cur = (temp & 0xFF); - - if (cur < TEXT_MIN) { - int length = parseValueLength(pduDataStream); - int startPos = pduDataStream.available(); - pduDataStream.mark(1); - temp = pduDataStream.read(); - assert(-1 != temp); - pduDataStream.reset(); - int first = (temp & 0xFF); - - if ((first >= TEXT_MIN) && (first <= TEXT_MAX)) { - contentType = parseWapString(pduDataStream, TYPE_TEXT_STRING); - } else if (first > TEXT_MAX) { - int index = parseShortInteger(pduDataStream); - - if (index < PduContentTypes.contentTypes.length) { //well-known type - contentType = (PduContentTypes.contentTypes[index]).getBytes(); - } else { - pduDataStream.reset(); - contentType = parseWapString(pduDataStream, TYPE_TEXT_STRING); - } - } else { - Log.e(LOG_TAG, "Corrupt content-type"); - return (PduContentTypes.contentTypes[0]).getBytes(); //"*/*" - } - - int endPos = pduDataStream.available(); - int parameterLen = length - (startPos - endPos); - if (parameterLen > 0) {//have parameters - parseContentTypeParams(pduDataStream, map, parameterLen); - } - - if (parameterLen < 0) { - Log.e(LOG_TAG, "Corrupt MMS message"); - return (PduContentTypes.contentTypes[0]).getBytes(); //"*/*" - } - } else if (cur <= TEXT_MAX) { - contentType = parseWapString(pduDataStream, TYPE_TEXT_STRING); - } else { - contentType = - (PduContentTypes.contentTypes[parseShortInteger(pduDataStream)]).getBytes(); - } - - return contentType; - } - - /** - * Parse part's headers. - * - * @param pduDataStream pdu data input stream - * @param part to store the header informations of the part - * @param length length of the headers - * @return true if parse successfully, false otherwise - */ - protected static boolean parsePartHeaders(ByteArrayInputStream pduDataStream, - PduPart part, int length) { - assert(null != pduDataStream); - assert(null != part); - assert(length > 0); - - /** - * From oma-ts-mms-conf-v1_3.pdf, chapter 10.2. - * A name for multipart object SHALL be encoded using name-parameter - * for Content-Type header in WSP multipart headers. - * In decoding, name-parameter of Content-Type SHALL be used if available. - * If name-parameter of Content-Type is not available, - * filename parameter of Content-Disposition header SHALL be used if available. - * If neither name-parameter of Content-Type header nor filename parameter - * of Content-Disposition header is available, - * Content-Location header SHALL be used if available. - * - * Within SMIL part the reference to the media object parts SHALL use - * either Content-ID or Content-Location mechanism [RFC2557] - * and the corresponding WSP part headers in media object parts - * contain the corresponding definitions. - */ - int startPos = pduDataStream.available(); - int tempPos = 0; - int lastLen = length; - while(0 < lastLen) { - int header = pduDataStream.read(); - assert(-1 != header); - lastLen--; - - if (header > TEXT_MAX) { - // Number assigned headers. - switch (header) { - case PduPart.P_CONTENT_LOCATION: - /** - * From wap-230-wsp-20010705-a.pdf, chapter 8.4.2.21 - * Content-location-value = Uri-value - */ - byte[] contentLocation = parseWapString(pduDataStream, TYPE_TEXT_STRING); - if (null != contentLocation) { - part.setContentLocation(contentLocation); - } - - tempPos = pduDataStream.available(); - lastLen = length - (startPos - tempPos); - break; - case PduPart.P_CONTENT_ID: - /** - * From wap-230-wsp-20010705-a.pdf, chapter 8.4.2.21 - * Content-ID-value = Quoted-string - */ - byte[] contentId = parseWapString(pduDataStream, TYPE_QUOTED_STRING); - if (null != contentId) { - part.setContentId(contentId); - } - - tempPos = pduDataStream.available(); - lastLen = length - (startPos - tempPos); - break; - case PduPart.P_DEP_CONTENT_DISPOSITION: - case PduPart.P_CONTENT_DISPOSITION: - /** - * From wap-230-wsp-20010705-a.pdf, chapter 8.4.2.21 - * Content-disposition-value = Value-length Disposition *(Parameter) - * Disposition = Form-data | Attachment | Inline | Token-text - * Form-data = - * Attachment = - * Inline = - */ - int len = parseValueLength(pduDataStream); - pduDataStream.mark(1); - int thisStartPos = pduDataStream.available(); - int thisEndPos = 0; - int value = pduDataStream.read(); - - if (value == PduPart.P_DISPOSITION_FROM_DATA ) { - part.setContentDisposition(PduPart.DISPOSITION_FROM_DATA); - } else if (value == PduPart.P_DISPOSITION_ATTACHMENT) { - part.setContentDisposition(PduPart.DISPOSITION_ATTACHMENT); - } else if (value == PduPart.P_DISPOSITION_INLINE) { - part.setContentDisposition(PduPart.DISPOSITION_INLINE); - } else { - pduDataStream.reset(); - /* Token-text */ - part.setContentDisposition(parseWapString(pduDataStream, TYPE_TEXT_STRING)); - } - - /* get filename parameter and skip other parameters */ - thisEndPos = pduDataStream.available(); - if (thisStartPos - thisEndPos < len) { - value = pduDataStream.read(); - if (value == PduPart.P_FILENAME) { //filename is text-string - part.setFilename(parseWapString(pduDataStream, TYPE_TEXT_STRING)); - } - - /* skip other parameters */ - thisEndPos = pduDataStream.available(); - if (thisStartPos - thisEndPos < len) { - int last = len - (thisStartPos - thisEndPos); - byte[] temp = new byte[last]; - pduDataStream.read(temp, 0, last); - } - } - - tempPos = pduDataStream.available(); - lastLen = length - (startPos - tempPos); - break; - default: - if (LOCAL_LOGV) { - Log.v(LOG_TAG, "Not supported Part headers: " + header); - } - if (-1 == skipWapValue(pduDataStream, lastLen)) { - Log.e(LOG_TAG, "Corrupt Part headers"); - return false; - } - lastLen = 0; - break; - } - } else if ((header >= TEXT_MIN) && (header <= TEXT_MAX)) { - // Not assigned header. - byte[] tempHeader = parseWapString(pduDataStream, TYPE_TEXT_STRING); - byte[] tempValue = parseWapString(pduDataStream, TYPE_TEXT_STRING); - - // Check the header whether it is "Content-Transfer-Encoding". - if (true == - PduPart.CONTENT_TRANSFER_ENCODING.equalsIgnoreCase(new String(tempHeader))) { - part.setContentTransferEncoding(tempValue); - } - - tempPos = pduDataStream.available(); - lastLen = length - (startPos - tempPos); - } else { - if (LOCAL_LOGV) { - Log.v(LOG_TAG, "Not supported Part headers: " + header); - } - // Skip all headers of this part. - if (-1 == skipWapValue(pduDataStream, lastLen)) { - Log.e(LOG_TAG, "Corrupt Part headers"); - return false; - } - lastLen = 0; - } - } - - if (0 != lastLen) { - Log.e(LOG_TAG, "Corrupt Part headers"); - return false; - } - - return true; - } - - /** - * Check the position of a specified part. - * - * @param part the part to be checked - * @return part position, THE_FIRST_PART when it's the - * first one, THE_LAST_PART when it's the last one. - */ - private static int checkPartPosition(PduPart part) { - assert(null != part); - if ((null == mTypeParam) && - (null == mStartParam)) { - return THE_LAST_PART; - } - - /* check part's content-id */ - if (null != mStartParam) { - byte[] contentId = part.getContentId(); - if (null != contentId) { - if (true == Arrays.equals(mStartParam, contentId)) { - return THE_FIRST_PART; - } - } - } - - /* check part's content-type */ - if (null != mTypeParam) { - byte[] contentType = part.getContentType(); - if (null != contentType) { - if (true == Arrays.equals(mTypeParam, contentType)) { - return THE_FIRST_PART; - } - } - } - - return THE_LAST_PART; - } - - /** - * Check mandatory headers of a pdu. - * - * @param headers pdu headers - * @return true if the pdu has all of the mandatory headers, false otherwise. - */ - protected static boolean checkMandatoryHeader(PduHeaders headers) { - if (null == headers) { - return false; - } - - /* get message type */ - int messageType = headers.getOctet(PduHeaders.MESSAGE_TYPE); - - /* check Mms-Version field */ - int mmsVersion = headers.getOctet(PduHeaders.MMS_VERSION); - if (0 == mmsVersion) { - // Every message should have Mms-Version field. - return false; - } - - /* check mandatory header fields */ - switch (messageType) { - case PduHeaders.MESSAGE_TYPE_SEND_REQ: - // Content-Type field. - byte[] srContentType = headers.getTextString(PduHeaders.CONTENT_TYPE); - if (null == srContentType) { - return false; - } - - // From field. - EncodedStringValue srFrom = headers.getEncodedStringValue(PduHeaders.FROM); - if (null == srFrom) { - return false; - } - - // Transaction-Id field. - byte[] srTransactionId = headers.getTextString(PduHeaders.TRANSACTION_ID); - if (null == srTransactionId) { - return false; - } - - break; - case PduHeaders.MESSAGE_TYPE_SEND_CONF: - // Response-Status field. - int scResponseStatus = headers.getOctet(PduHeaders.RESPONSE_STATUS); - if (0 == scResponseStatus) { - return false; - } - - // Transaction-Id field. - byte[] scTransactionId = headers.getTextString(PduHeaders.TRANSACTION_ID); - if (null == scTransactionId) { - return false; - } - - break; - case PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND: - // Content-Location field. - byte[] niContentLocation = headers.getTextString(PduHeaders.CONTENT_LOCATION); - if (null == niContentLocation) { - return false; - } - - // Expiry field. - long niExpiry = headers.getLongInteger(PduHeaders.EXPIRY); - if (-1 == niExpiry) { - return false; - } - - // Message-Class field. - byte[] niMessageClass = headers.getTextString(PduHeaders.MESSAGE_CLASS); - if (null == niMessageClass) { - return false; - } - - // Message-Size field. - long niMessageSize = headers.getLongInteger(PduHeaders.MESSAGE_SIZE); - if (-1 == niMessageSize) { - return false; - } - - // Transaction-Id field. - byte[] niTransactionId = headers.getTextString(PduHeaders.TRANSACTION_ID); - if (null == niTransactionId) { - return false; - } - - break; - case PduHeaders.MESSAGE_TYPE_NOTIFYRESP_IND: - // Status field. - int nriStatus = headers.getOctet(PduHeaders.STATUS); - if (0 == nriStatus) { - return false; - } - - // Transaction-Id field. - byte[] nriTransactionId = headers.getTextString(PduHeaders.TRANSACTION_ID); - if (null == nriTransactionId) { - return false; - } - - break; - case PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF: - // Content-Type field. - byte[] rcContentType = headers.getTextString(PduHeaders.CONTENT_TYPE); - if (null == rcContentType) { - return false; - } - - // Date field. - long rcDate = headers.getLongInteger(PduHeaders.DATE); - if (-1 == rcDate) { - return false; - } - - break; - case PduHeaders.MESSAGE_TYPE_DELIVERY_IND: - // Date field. - long diDate = headers.getLongInteger(PduHeaders.DATE); - if (-1 == diDate) { - return false; - } - - // Message-Id field. - byte[] diMessageId = headers.getTextString(PduHeaders.MESSAGE_ID); - if (null == diMessageId) { - return false; - } - - // Status field. - int diStatus = headers.getOctet(PduHeaders.STATUS); - if (0 == diStatus) { - return false; - } - - // To field. - EncodedStringValue[] diTo = headers.getEncodedStringValues(PduHeaders.TO); - if (null == diTo) { - return false; - } - - break; - case PduHeaders.MESSAGE_TYPE_ACKNOWLEDGE_IND: - // Transaction-Id field. - byte[] aiTransactionId = headers.getTextString(PduHeaders.TRANSACTION_ID); - if (null == aiTransactionId) { - return false; - } - - break; - case PduHeaders.MESSAGE_TYPE_READ_ORIG_IND: - // Date field. - long roDate = headers.getLongInteger(PduHeaders.DATE); - if (-1 == roDate) { - return false; - } - - // From field. - EncodedStringValue roFrom = headers.getEncodedStringValue(PduHeaders.FROM); - if (null == roFrom) { - return false; - } - - // Message-Id field. - byte[] roMessageId = headers.getTextString(PduHeaders.MESSAGE_ID); - if (null == roMessageId) { - return false; - } - - // Read-Status field. - int roReadStatus = headers.getOctet(PduHeaders.READ_STATUS); - if (0 == roReadStatus) { - return false; - } - - // To field. - EncodedStringValue[] roTo = headers.getEncodedStringValues(PduHeaders.TO); - if (null == roTo) { - return false; - } - - break; - case PduHeaders.MESSAGE_TYPE_READ_REC_IND: - // From field. - EncodedStringValue rrFrom = headers.getEncodedStringValue(PduHeaders.FROM); - if (null == rrFrom) { - return false; - } - - // Message-Id field. - byte[] rrMessageId = headers.getTextString(PduHeaders.MESSAGE_ID); - if (null == rrMessageId) { - return false; - } - - // Read-Status field. - int rrReadStatus = headers.getOctet(PduHeaders.READ_STATUS); - if (0 == rrReadStatus) { - return false; - } - - // To field. - EncodedStringValue[] rrTo = headers.getEncodedStringValues(PduHeaders.TO); - if (null == rrTo) { - return false; - } - - break; - default: - // Parser doesn't support this message type in this version. - return false; - } - - return true; - } -} diff --git a/mms-common/java/com/android/mmscommon/mms/pdu/PduPart.java b/mms-common/java/com/android/mmscommon/mms/pdu/PduPart.java deleted file mode 100644 index 7d51b86e9..000000000 --- a/mms-common/java/com/android/mmscommon/mms/pdu/PduPart.java +++ /dev/null @@ -1,402 +0,0 @@ -/* - * Copyright (C) 2007-2008 Esmertec AG. - * Copyright (C) 2007-2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.mmscommon.mms.pdu; - -import android.net.Uri; - -import java.util.HashMap; -import java.util.Map; - -/** - * The pdu part. - */ -public class PduPart { - /** - * Well-Known Parameters. - */ - public static final int P_Q = 0x80; - public static final int P_CHARSET = 0x81; - public static final int P_LEVEL = 0x82; - public static final int P_TYPE = 0x83; - public static final int P_DEP_NAME = 0x85; - public static final int P_DEP_FILENAME = 0x86; - public static final int P_DIFFERENCES = 0x87; - public static final int P_PADDING = 0x88; - // This value of "TYPE" s used with Content-Type: multipart/related - public static final int P_CT_MR_TYPE = 0x89; - public static final int P_DEP_START = 0x8A; - public static final int P_DEP_START_INFO = 0x8B; - public static final int P_DEP_COMMENT = 0x8C; - public static final int P_DEP_DOMAIN = 0x8D; - public static final int P_MAX_AGE = 0x8E; - public static final int P_DEP_PATH = 0x8F; - public static final int P_SECURE = 0x90; - public static final int P_SEC = 0x91; - public static final int P_MAC = 0x92; - public static final int P_CREATION_DATE = 0x93; - public static final int P_MODIFICATION_DATE = 0x94; - public static final int P_READ_DATE = 0x95; - public static final int P_SIZE = 0x96; - public static final int P_NAME = 0x97; - public static final int P_FILENAME = 0x98; - public static final int P_START = 0x99; - public static final int P_START_INFO = 0x9A; - public static final int P_COMMENT = 0x9B; - public static final int P_DOMAIN = 0x9C; - public static final int P_PATH = 0x9D; - - /** - * Header field names. - */ - public static final int P_CONTENT_TYPE = 0x91; - public static final int P_CONTENT_LOCATION = 0x8E; - public static final int P_CONTENT_ID = 0xC0; - public static final int P_DEP_CONTENT_DISPOSITION = 0xAE; - public static final int P_CONTENT_DISPOSITION = 0xC5; - // The next header is unassigned header, use reserved header(0x48) value. - public static final int P_CONTENT_TRANSFER_ENCODING = 0xC8; - - /** - * Content=Transfer-Encoding string. - */ - public static final String CONTENT_TRANSFER_ENCODING = - "Content-Transfer-Encoding"; - - /** - * Value of Content-Transfer-Encoding. - */ - public static final String P_BINARY = "binary"; - public static final String P_7BIT = "7bit"; - public static final String P_8BIT = "8bit"; - public static final String P_BASE64 = "base64"; - public static final String P_QUOTED_PRINTABLE = "quoted-printable"; - - /** - * Value of disposition can be set to PduPart when the value is octet in - * the PDU. - * "from-data" instead of Form-data. - * "attachment" instead of Attachment. - * "inline" instead of Inline. - */ - static final byte[] DISPOSITION_FROM_DATA = "from-data".getBytes(); - static final byte[] DISPOSITION_ATTACHMENT = "attachment".getBytes(); - static final byte[] DISPOSITION_INLINE = "inline".getBytes(); - - /** - * Content-Disposition value. - */ - public static final int P_DISPOSITION_FROM_DATA = 0x80; - public static final int P_DISPOSITION_ATTACHMENT = 0x81; - public static final int P_DISPOSITION_INLINE = 0x82; - - /** - * Header of part. - */ - private Map mPartHeader = null; - - /** - * Data uri. - */ - private Uri mUri = null; - - /** - * Part data. - */ - private byte[] mPartData = null; - - private static final String TAG = "PduPart"; - - /** - * Empty Constructor. - */ - public PduPart() { - mPartHeader = new HashMap(); - } - - /** - * Set part data. The data are stored as byte array. - * - * @param data the data - */ - public void setData(byte[] data) { - if(data == null) { - return; - } - - mPartData = new byte[data.length]; - System.arraycopy(data, 0, mPartData, 0, data.length); - } - - /** - * @return A copy of the part data or null if the data wasn't set or - * the data is stored as Uri. - * @see #getDataUri - */ - public byte[] getData() { - if(mPartData == null) { - return null; - } - - byte[] byteArray = new byte[mPartData.length]; - System.arraycopy(mPartData, 0, byteArray, 0, mPartData.length); - return byteArray; - } - - /** - * Set data uri. The data are stored as Uri. - * - * @param uri the uri - */ - public void setDataUri(Uri uri) { - mUri = uri; - } - - /** - * @return The Uri of the part data or null if the data wasn't set or - * the data is stored as byte array. - * @see #getData - */ - public Uri getDataUri() { - return mUri; - } - - /** - * Set Content-id value - * - * @param contentId the content-id value - * @throws NullPointerException if the value is null. - */ - public void setContentId(byte[] contentId) { - if((contentId == null) || (contentId.length == 0)) { - throw new IllegalArgumentException( - "Content-Id may not be null or empty."); - } - - if ((contentId.length > 1) - && ((char) contentId[0] == '<') - && ((char) contentId[contentId.length - 1] == '>')) { - mPartHeader.put(P_CONTENT_ID, contentId); - return; - } - - // Insert beginning '<' and trailing '>' for Content-Id. - byte[] buffer = new byte[contentId.length + 2]; - buffer[0] = (byte) (0xff & '<'); - buffer[buffer.length - 1] = (byte) (0xff & '>'); - System.arraycopy(contentId, 0, buffer, 1, contentId.length); - mPartHeader.put(P_CONTENT_ID, buffer); - } - - /** - * Get Content-id value. - * - * @return the value - */ - public byte[] getContentId() { - return (byte[]) mPartHeader.get(P_CONTENT_ID); - } - - /** - * Set Char-set value. - * - * @param charset the value - */ - public void setCharset(int charset) { - mPartHeader.put(P_CHARSET, charset); - } - - /** - * Get Char-set value - * - * @return the charset value. Return 0 if charset was not set. - */ - public int getCharset() { - Integer charset = (Integer) mPartHeader.get(P_CHARSET); - if(charset == null) { - return 0; - } else { - return charset.intValue(); - } - } - - /** - * Set Content-Location value. - * - * @param contentLocation the value - * @throws NullPointerException if the value is null. - */ - public void setContentLocation(byte[] contentLocation) { - if(contentLocation == null) { - throw new NullPointerException("null content-location"); - } - - mPartHeader.put(P_CONTENT_LOCATION, contentLocation); - } - - /** - * Get Content-Location value. - * - * @return the value - * return PduPart.disposition[0] instead of (Form-data). - * return PduPart.disposition[1] instead of (Attachment). - * return PduPart.disposition[2] instead of (Inline). - */ - public byte[] getContentLocation() { - return (byte[]) mPartHeader.get(P_CONTENT_LOCATION); - } - - /** - * Set Content-Disposition value. - * Use PduPart.disposition[0] instead of (Form-data). - * Use PduPart.disposition[1] instead of (Attachment). - * Use PduPart.disposition[2] instead of (Inline). - * - * @param contentDisposition the value - * @throws NullPointerException if the value is null. - */ - public void setContentDisposition(byte[] contentDisposition) { - if(contentDisposition == null) { - throw new NullPointerException("null content-disposition"); - } - - mPartHeader.put(P_CONTENT_DISPOSITION, contentDisposition); - } - - /** - * Get Content-Disposition value. - * - * @return the value - */ - public byte[] getContentDisposition() { - return (byte[]) mPartHeader.get(P_CONTENT_DISPOSITION); - } - - /** - * Set Content-Type value. - * - * @param value the value - * @throws NullPointerException if the value is null. - */ - public void setContentType(byte[] contentType) { - if(contentType == null) { - throw new NullPointerException("null content-type"); - } - - mPartHeader.put(P_CONTENT_TYPE, contentType); - } - - /** - * Get Content-Type value of part. - * - * @return the value - */ - public byte[] getContentType() { - return (byte[]) mPartHeader.get(P_CONTENT_TYPE); - } - - /** - * Set Content-Transfer-Encoding value - * - * @param contentId the content-id value - * @throws NullPointerException if the value is null. - */ - public void setContentTransferEncoding(byte[] contentTransferEncoding) { - if(contentTransferEncoding == null) { - throw new NullPointerException("null content-transfer-encoding"); - } - - mPartHeader.put(P_CONTENT_TRANSFER_ENCODING, contentTransferEncoding); - } - - /** - * Get Content-Transfer-Encoding value. - * - * @return the value - */ - public byte[] getContentTransferEncoding() { - return (byte[]) mPartHeader.get(P_CONTENT_TRANSFER_ENCODING); - } - - /** - * Set Content-type parameter: name. - * - * @param name the name value - * @throws NullPointerException if the value is null. - */ - public void setName(byte[] name) { - if(null == name) { - throw new NullPointerException("null content-id"); - } - - mPartHeader.put(P_NAME, name); - } - - /** - * Get content-type parameter: name. - * - * @return the name - */ - public byte[] getName() { - return (byte[]) mPartHeader.get(P_NAME); - } - - /** - * Get Content-disposition parameter: filename - * - * @param fileName the filename value - * @throws NullPointerException if the value is null. - */ - public void setFilename(byte[] fileName) { - if(null == fileName) { - throw new NullPointerException("null content-id"); - } - - mPartHeader.put(P_FILENAME, fileName); - } - - /** - * Set Content-disposition parameter: filename - * - * @return the filename - */ - public byte[] getFilename() { - return (byte[]) mPartHeader.get(P_FILENAME); - } - - public String generateLocation() { - // Assumption: At least one of the content-location / name / filename - // or content-id should be set. This is guaranteed by the PduParser - // for incoming messages and by MM composer for outgoing messages. - byte[] location = (byte[]) mPartHeader.get(P_NAME); - if(null == location) { - location = (byte[]) mPartHeader.get(P_FILENAME); - - if (null == location) { - location = (byte[]) mPartHeader.get(P_CONTENT_LOCATION); - } - } - - if (null == location) { - byte[] contentId = (byte[]) mPartHeader.get(P_CONTENT_ID); - return "cid:" + new String(contentId); - } else { - return new String(location); - } - } -} - diff --git a/mms-common/java/com/android/mmscommon/mms/pdu/PduPersister.java b/mms-common/java/com/android/mmscommon/mms/pdu/PduPersister.java deleted file mode 100644 index d92f0e1d7..000000000 --- a/mms-common/java/com/android/mmscommon/mms/pdu/PduPersister.java +++ /dev/null @@ -1,1270 +0,0 @@ -/* - * Copyright (C) 2007-2008 Esmertec AG. - * Copyright (C) 2007-2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.mmscommon.mms.pdu; - -import com.android.mmscommon.mms.pdu.PduPersister; - -import com.android.mmscommon.ContentType; -import com.android.mmscommon.CharacterSets; -import com.android.mmscommon.EncodedStringValue; -import com.android.mmscommon.InvalidHeaderValueException; -import com.android.mmscommon.MmsException; -import com.android.mmscommon.PduHeaders; -import com.android.mmscommon.mms.util.PduCache; -import com.android.mmscommon.mms.util.PduCacheEntry; -import android.database.sqlite.SqliteWrapper; - -import android.content.ContentResolver; -import android.content.ContentUris; -import android.content.ContentValues; -import android.content.Context; -import android.database.Cursor; -import android.database.DatabaseUtils; -import android.net.Uri; -import com.android.mmscommon.telephony.TelephonyProvider; -import com.android.mmscommon.telephony.TelephonyProvider.Mms; -import com.android.mmscommon.telephony.TelephonyProvider.MmsSms; -import com.android.mmscommon.telephony.TelephonyProvider.Threads; -import com.android.mmscommon.telephony.TelephonyProvider.Mms.Addr; -import com.android.mmscommon.telephony.TelephonyProvider.Mms.Part; -import com.android.mmscommon.telephony.TelephonyProvider.MmsSms.PendingMessages; -import android.text.TextUtils; -import android.util.Config; -import android.util.Log; - -import java.io.ByteArrayOutputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.UnsupportedEncodingException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.Map.Entry; - - -/** - * This class is the high-level manager of PDU storage. - */ -public class PduPersister { - private static final String TAG = "PduPersister"; - private static final boolean DEBUG = false; - private static final boolean LOCAL_LOGV = DEBUG ? Config.LOGD : Config.LOGV; - - private static final long DUMMY_THREAD_ID = Long.MAX_VALUE; - - /** - * The uri of temporary drm objects. - */ - public static final String TEMPORARY_DRM_OBJECT_URI = - "content://mms/" + Long.MAX_VALUE + "/part"; - /** - * Indicate that we transiently failed to process a MM. - */ - public static final int PROC_STATUS_TRANSIENT_FAILURE = 1; - /** - * Indicate that we permanently failed to process a MM. - */ - public static final int PROC_STATUS_PERMANENTLY_FAILURE = 2; - /** - * Indicate that we have successfully processed a MM. - */ - public static final int PROC_STATUS_COMPLETED = 3; - - private static PduPersister sPersister; - private static final PduCache PDU_CACHE_INSTANCE; - - private static final int[] ADDRESS_FIELDS = new int[] { - PduHeaders.BCC, - PduHeaders.CC, - PduHeaders.FROM, - PduHeaders.TO - }; - - private static final String[] PDU_PROJECTION = new String[] { - Mms._ID, - Mms.MESSAGE_BOX, - Mms.THREAD_ID, - Mms.RETRIEVE_TEXT, - Mms.SUBJECT, - Mms.CONTENT_LOCATION, - Mms.CONTENT_TYPE, - Mms.MESSAGE_CLASS, - Mms.MESSAGE_ID, - Mms.RESPONSE_TEXT, - Mms.TRANSACTION_ID, - Mms.CONTENT_CLASS, - Mms.DELIVERY_REPORT, - Mms.MESSAGE_TYPE, - Mms.MMS_VERSION, - Mms.PRIORITY, - Mms.READ_REPORT, - Mms.READ_STATUS, - Mms.REPORT_ALLOWED, - Mms.RETRIEVE_STATUS, - Mms.STATUS, - Mms.DATE, - Mms.DELIVERY_TIME, - Mms.EXPIRY, - Mms.MESSAGE_SIZE, - Mms.SUBJECT_CHARSET, - Mms.RETRIEVE_TEXT_CHARSET, - }; - - private static final int PDU_COLUMN_ID = 0; - private static final int PDU_COLUMN_MESSAGE_BOX = 1; - private static final int PDU_COLUMN_THREAD_ID = 2; - private static final int PDU_COLUMN_RETRIEVE_TEXT = 3; - private static final int PDU_COLUMN_SUBJECT = 4; - private static final int PDU_COLUMN_CONTENT_LOCATION = 5; - private static final int PDU_COLUMN_CONTENT_TYPE = 6; - private static final int PDU_COLUMN_MESSAGE_CLASS = 7; - private static final int PDU_COLUMN_MESSAGE_ID = 8; - private static final int PDU_COLUMN_RESPONSE_TEXT = 9; - private static final int PDU_COLUMN_TRANSACTION_ID = 10; - private static final int PDU_COLUMN_CONTENT_CLASS = 11; - private static final int PDU_COLUMN_DELIVERY_REPORT = 12; - private static final int PDU_COLUMN_MESSAGE_TYPE = 13; - private static final int PDU_COLUMN_MMS_VERSION = 14; - private static final int PDU_COLUMN_PRIORITY = 15; - private static final int PDU_COLUMN_READ_REPORT = 16; - private static final int PDU_COLUMN_READ_STATUS = 17; - private static final int PDU_COLUMN_REPORT_ALLOWED = 18; - private static final int PDU_COLUMN_RETRIEVE_STATUS = 19; - private static final int PDU_COLUMN_STATUS = 20; - private static final int PDU_COLUMN_DATE = 21; - private static final int PDU_COLUMN_DELIVERY_TIME = 22; - private static final int PDU_COLUMN_EXPIRY = 23; - private static final int PDU_COLUMN_MESSAGE_SIZE = 24; - private static final int PDU_COLUMN_SUBJECT_CHARSET = 25; - private static final int PDU_COLUMN_RETRIEVE_TEXT_CHARSET = 26; - - private static final String[] PART_PROJECTION = new String[] { - Part._ID, - Part.CHARSET, - Part.CONTENT_DISPOSITION, - Part.CONTENT_ID, - Part.CONTENT_LOCATION, - Part.CONTENT_TYPE, - Part.FILENAME, - Part.NAME, - Part.TEXT - }; - - private static final int PART_COLUMN_ID = 0; - private static final int PART_COLUMN_CHARSET = 1; - private static final int PART_COLUMN_CONTENT_DISPOSITION = 2; - private static final int PART_COLUMN_CONTENT_ID = 3; - private static final int PART_COLUMN_CONTENT_LOCATION = 4; - private static final int PART_COLUMN_CONTENT_TYPE = 5; - private static final int PART_COLUMN_FILENAME = 6; - private static final int PART_COLUMN_NAME = 7; - private static final int PART_COLUMN_TEXT = 8; - - private static final HashMap MESSAGE_BOX_MAP; - // These map are used for convenience in persist() and load(). - private static final HashMap CHARSET_COLUMN_INDEX_MAP; - private static final HashMap ENCODED_STRING_COLUMN_INDEX_MAP; - private static final HashMap TEXT_STRING_COLUMN_INDEX_MAP; - private static final HashMap OCTET_COLUMN_INDEX_MAP; - private static final HashMap LONG_COLUMN_INDEX_MAP; - private static final HashMap CHARSET_COLUMN_NAME_MAP; - private static final HashMap ENCODED_STRING_COLUMN_NAME_MAP; - private static final HashMap TEXT_STRING_COLUMN_NAME_MAP; - private static final HashMap OCTET_COLUMN_NAME_MAP; - private static final HashMap LONG_COLUMN_NAME_MAP; - - static { - MESSAGE_BOX_MAP = new HashMap(); - MESSAGE_BOX_MAP.put(Mms.Inbox.CONTENT_URI, Mms.MESSAGE_BOX_INBOX); - MESSAGE_BOX_MAP.put(Mms.Sent.CONTENT_URI, Mms.MESSAGE_BOX_SENT); - MESSAGE_BOX_MAP.put(Mms.Draft.CONTENT_URI, Mms.MESSAGE_BOX_DRAFTS); - MESSAGE_BOX_MAP.put(Mms.Outbox.CONTENT_URI, Mms.MESSAGE_BOX_OUTBOX); - - CHARSET_COLUMN_INDEX_MAP = new HashMap(); - CHARSET_COLUMN_INDEX_MAP.put(PduHeaders.SUBJECT, PDU_COLUMN_SUBJECT_CHARSET); - CHARSET_COLUMN_INDEX_MAP.put(PduHeaders.RETRIEVE_TEXT, PDU_COLUMN_RETRIEVE_TEXT_CHARSET); - - CHARSET_COLUMN_NAME_MAP = new HashMap(); - CHARSET_COLUMN_NAME_MAP.put(PduHeaders.SUBJECT, Mms.SUBJECT_CHARSET); - CHARSET_COLUMN_NAME_MAP.put(PduHeaders.RETRIEVE_TEXT, Mms.RETRIEVE_TEXT_CHARSET); - - // Encoded string field code -> column index/name map. - ENCODED_STRING_COLUMN_INDEX_MAP = new HashMap(); - ENCODED_STRING_COLUMN_INDEX_MAP.put(PduHeaders.RETRIEVE_TEXT, PDU_COLUMN_RETRIEVE_TEXT); - ENCODED_STRING_COLUMN_INDEX_MAP.put(PduHeaders.SUBJECT, PDU_COLUMN_SUBJECT); - - ENCODED_STRING_COLUMN_NAME_MAP = new HashMap(); - ENCODED_STRING_COLUMN_NAME_MAP.put(PduHeaders.RETRIEVE_TEXT, Mms.RETRIEVE_TEXT); - ENCODED_STRING_COLUMN_NAME_MAP.put(PduHeaders.SUBJECT, Mms.SUBJECT); - - // Text string field code -> column index/name map. - TEXT_STRING_COLUMN_INDEX_MAP = new HashMap(); - TEXT_STRING_COLUMN_INDEX_MAP.put(PduHeaders.CONTENT_LOCATION, PDU_COLUMN_CONTENT_LOCATION); - TEXT_STRING_COLUMN_INDEX_MAP.put(PduHeaders.CONTENT_TYPE, PDU_COLUMN_CONTENT_TYPE); - TEXT_STRING_COLUMN_INDEX_MAP.put(PduHeaders.MESSAGE_CLASS, PDU_COLUMN_MESSAGE_CLASS); - TEXT_STRING_COLUMN_INDEX_MAP.put(PduHeaders.MESSAGE_ID, PDU_COLUMN_MESSAGE_ID); - TEXT_STRING_COLUMN_INDEX_MAP.put(PduHeaders.RESPONSE_TEXT, PDU_COLUMN_RESPONSE_TEXT); - TEXT_STRING_COLUMN_INDEX_MAP.put(PduHeaders.TRANSACTION_ID, PDU_COLUMN_TRANSACTION_ID); - - TEXT_STRING_COLUMN_NAME_MAP = new HashMap(); - TEXT_STRING_COLUMN_NAME_MAP.put(PduHeaders.CONTENT_LOCATION, Mms.CONTENT_LOCATION); - TEXT_STRING_COLUMN_NAME_MAP.put(PduHeaders.CONTENT_TYPE, Mms.CONTENT_TYPE); - TEXT_STRING_COLUMN_NAME_MAP.put(PduHeaders.MESSAGE_CLASS, Mms.MESSAGE_CLASS); - TEXT_STRING_COLUMN_NAME_MAP.put(PduHeaders.MESSAGE_ID, Mms.MESSAGE_ID); - TEXT_STRING_COLUMN_NAME_MAP.put(PduHeaders.RESPONSE_TEXT, Mms.RESPONSE_TEXT); - TEXT_STRING_COLUMN_NAME_MAP.put(PduHeaders.TRANSACTION_ID, Mms.TRANSACTION_ID); - - // Octet field code -> column index/name map. - OCTET_COLUMN_INDEX_MAP = new HashMap(); - OCTET_COLUMN_INDEX_MAP.put(PduHeaders.CONTENT_CLASS, PDU_COLUMN_CONTENT_CLASS); - OCTET_COLUMN_INDEX_MAP.put(PduHeaders.DELIVERY_REPORT, PDU_COLUMN_DELIVERY_REPORT); - OCTET_COLUMN_INDEX_MAP.put(PduHeaders.MESSAGE_TYPE, PDU_COLUMN_MESSAGE_TYPE); - OCTET_COLUMN_INDEX_MAP.put(PduHeaders.MMS_VERSION, PDU_COLUMN_MMS_VERSION); - OCTET_COLUMN_INDEX_MAP.put(PduHeaders.PRIORITY, PDU_COLUMN_PRIORITY); - OCTET_COLUMN_INDEX_MAP.put(PduHeaders.READ_REPORT, PDU_COLUMN_READ_REPORT); - OCTET_COLUMN_INDEX_MAP.put(PduHeaders.READ_STATUS, PDU_COLUMN_READ_STATUS); - OCTET_COLUMN_INDEX_MAP.put(PduHeaders.REPORT_ALLOWED, PDU_COLUMN_REPORT_ALLOWED); - OCTET_COLUMN_INDEX_MAP.put(PduHeaders.RETRIEVE_STATUS, PDU_COLUMN_RETRIEVE_STATUS); - OCTET_COLUMN_INDEX_MAP.put(PduHeaders.STATUS, PDU_COLUMN_STATUS); - - OCTET_COLUMN_NAME_MAP = new HashMap(); - OCTET_COLUMN_NAME_MAP.put(PduHeaders.CONTENT_CLASS, Mms.CONTENT_CLASS); - OCTET_COLUMN_NAME_MAP.put(PduHeaders.DELIVERY_REPORT, Mms.DELIVERY_REPORT); - OCTET_COLUMN_NAME_MAP.put(PduHeaders.MESSAGE_TYPE, Mms.MESSAGE_TYPE); - OCTET_COLUMN_NAME_MAP.put(PduHeaders.MMS_VERSION, Mms.MMS_VERSION); - OCTET_COLUMN_NAME_MAP.put(PduHeaders.PRIORITY, Mms.PRIORITY); - OCTET_COLUMN_NAME_MAP.put(PduHeaders.READ_REPORT, Mms.READ_REPORT); - OCTET_COLUMN_NAME_MAP.put(PduHeaders.READ_STATUS, Mms.READ_STATUS); - OCTET_COLUMN_NAME_MAP.put(PduHeaders.REPORT_ALLOWED, Mms.REPORT_ALLOWED); - OCTET_COLUMN_NAME_MAP.put(PduHeaders.RETRIEVE_STATUS, Mms.RETRIEVE_STATUS); - OCTET_COLUMN_NAME_MAP.put(PduHeaders.STATUS, Mms.STATUS); - - // Long field code -> column index/name map. - LONG_COLUMN_INDEX_MAP = new HashMap(); - LONG_COLUMN_INDEX_MAP.put(PduHeaders.DATE, PDU_COLUMN_DATE); - LONG_COLUMN_INDEX_MAP.put(PduHeaders.DELIVERY_TIME, PDU_COLUMN_DELIVERY_TIME); - LONG_COLUMN_INDEX_MAP.put(PduHeaders.EXPIRY, PDU_COLUMN_EXPIRY); - LONG_COLUMN_INDEX_MAP.put(PduHeaders.MESSAGE_SIZE, PDU_COLUMN_MESSAGE_SIZE); - - LONG_COLUMN_NAME_MAP = new HashMap(); - LONG_COLUMN_NAME_MAP.put(PduHeaders.DATE, Mms.DATE); - LONG_COLUMN_NAME_MAP.put(PduHeaders.DELIVERY_TIME, Mms.DELIVERY_TIME); - LONG_COLUMN_NAME_MAP.put(PduHeaders.EXPIRY, Mms.EXPIRY); - LONG_COLUMN_NAME_MAP.put(PduHeaders.MESSAGE_SIZE, Mms.MESSAGE_SIZE); - - PDU_CACHE_INSTANCE = PduCache.getInstance(); - } - - private final Context mContext; - private final ContentResolver mContentResolver; - - private PduPersister(Context context) { - mContext = context; - mContentResolver = context.getContentResolver(); - } - - /** Get(or create if not exist) an instance of PduPersister */ - public static PduPersister getPduPersister(Context context) { - if ((sPersister == null) || !context.equals(sPersister.mContext)) { - sPersister = new PduPersister(context); - } - - return sPersister; - } - - private void setEncodedStringValueToHeaders( - Cursor c, int columnIndex, - PduHeaders headers, int mapColumn) { - String s = c.getString(columnIndex); - if ((s != null) && (s.length() > 0)) { - int charsetColumnIndex = CHARSET_COLUMN_INDEX_MAP.get(mapColumn); - int charset = c.getInt(charsetColumnIndex); - EncodedStringValue value = new EncodedStringValue( - charset, getBytes(s)); - headers.setEncodedStringValue(value, mapColumn); - } - } - - private void setTextStringToHeaders( - Cursor c, int columnIndex, - PduHeaders headers, int mapColumn) { - String s = c.getString(columnIndex); - if (s != null) { - headers.setTextString(getBytes(s), mapColumn); - } - } - - private void setOctetToHeaders( - Cursor c, int columnIndex, - PduHeaders headers, int mapColumn) throws InvalidHeaderValueException { - if (!c.isNull(columnIndex)) { - int b = c.getInt(columnIndex); - headers.setOctet(b, mapColumn); - } - } - - private void setLongToHeaders( - Cursor c, int columnIndex, - PduHeaders headers, int mapColumn) { - if (!c.isNull(columnIndex)) { - long l = c.getLong(columnIndex); - headers.setLongInteger(l, mapColumn); - } - } - - private Integer getIntegerFromPartColumn(Cursor c, int columnIndex) { - if (!c.isNull(columnIndex)) { - return c.getInt(columnIndex); - } - return null; - } - - private byte[] getByteArrayFromPartColumn(Cursor c, int columnIndex) { - if (!c.isNull(columnIndex)) { - return getBytes(c.getString(columnIndex)); - } - return null; - } - - private PduPart[] loadParts(long msgId) throws MmsException { - Cursor c = SqliteWrapper.query(mContext, mContentResolver, - Uri.parse("content://mms/" + msgId + "/part"), - PART_PROJECTION, null, null, null); - - PduPart[] parts = null; - - try { - if ((c == null) || (c.getCount() == 0)) { - if (LOCAL_LOGV) { - Log.v(TAG, "loadParts(" + msgId + "): no part to load."); - } - return null; - } - - int partCount = c.getCount(); - int partIdx = 0; - parts = new PduPart[partCount]; - while (c.moveToNext()) { - PduPart part = new PduPart(); - Integer charset = getIntegerFromPartColumn( - c, PART_COLUMN_CHARSET); - if (charset != null) { - part.setCharset(charset); - } - - byte[] contentDisposition = getByteArrayFromPartColumn( - c, PART_COLUMN_CONTENT_DISPOSITION); - if (contentDisposition != null) { - part.setContentDisposition(contentDisposition); - } - - byte[] contentId = getByteArrayFromPartColumn( - c, PART_COLUMN_CONTENT_ID); - if (contentId != null) { - part.setContentId(contentId); - } - - byte[] contentLocation = getByteArrayFromPartColumn( - c, PART_COLUMN_CONTENT_LOCATION); - if (contentLocation != null) { - part.setContentLocation(contentLocation); - } - - byte[] contentType = getByteArrayFromPartColumn( - c, PART_COLUMN_CONTENT_TYPE); - if (contentType != null) { - part.setContentType(contentType); - } else { - throw new MmsException("Content-Type must be set."); - } - - byte[] fileName = getByteArrayFromPartColumn( - c, PART_COLUMN_FILENAME); - if (fileName != null) { - part.setFilename(fileName); - } - - byte[] name = getByteArrayFromPartColumn( - c, PART_COLUMN_NAME); - if (name != null) { - part.setName(name); - } - - // Construct a Uri for this part. - long partId = c.getLong(PART_COLUMN_ID); - Uri partURI = Uri.parse("content://mms/part/" + partId); - part.setDataUri(partURI); - - // For images/audio/video, we won't keep their data in Part - // because their renderer accept Uri as source. - String type = toIsoString(contentType); - if (!ContentType.isImageType(type) - && !ContentType.isAudioType(type) - && !ContentType.isVideoType(type)) { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - InputStream is = null; - - // Store simple string values directly in the database instead of an - // external file. This makes the text searchable and retrieval slightly - // faster. - if (ContentType.TEXT_PLAIN.equals(type) || ContentType.APP_SMIL.equals(type) - || ContentType.TEXT_HTML.equals(type)) { - String text = c.getString(PART_COLUMN_TEXT); - if (text == null) { - text = ""; - } - byte [] blob = new EncodedStringValue(text).getTextString(); - baos.write(blob, 0, blob.length); - } else { - - try { - is = mContentResolver.openInputStream(partURI); - - byte[] buffer = new byte[256]; - int len = is.read(buffer); - while (len >= 0) { - baos.write(buffer, 0, len); - len = is.read(buffer); - } - } catch (IOException e) { - Log.e(TAG, "Failed to load part data", e); - c.close(); - throw new MmsException(e); - } finally { - if (is != null) { - try { - is.close(); - } catch (IOException e) { - Log.e(TAG, "Failed to close stream", e); - } // Ignore - } - } - } - part.setData(baos.toByteArray()); - } - parts[partIdx++] = part; - } - } finally { - if (c != null) { - c.close(); - } - } - - return parts; - } - - private void loadAddress(long msgId, PduHeaders headers) { - Cursor c = SqliteWrapper.query(mContext, mContentResolver, - Uri.parse("content://mms/" + msgId + "/addr"), - new String[] { Addr.ADDRESS, Addr.CHARSET, Addr.TYPE }, - null, null, null); - - if (c != null) { - try { - while (c.moveToNext()) { - String addr = c.getString(0); - if (!TextUtils.isEmpty(addr)) { - int addrType = c.getInt(2); - switch (addrType) { - case PduHeaders.FROM: - headers.setEncodedStringValue( - new EncodedStringValue(c.getInt(1), getBytes(addr)), - addrType); - break; - case PduHeaders.TO: - case PduHeaders.CC: - case PduHeaders.BCC: - headers.appendEncodedStringValue( - new EncodedStringValue(c.getInt(1), getBytes(addr)), - addrType); - break; - default: - Log.e(TAG, "Unknown address type: " + addrType); - break; - } - } - } - } finally { - c.close(); - } - } - } - - /** - * Load a PDU from storage by given Uri. - * - * @param uri The Uri of the PDU to be loaded. - * @return A generic PDU object, it may be cast to dedicated PDU. - * @throws MmsException Failed to load some fields of a PDU. - */ - public GenericPdu load(Uri uri) throws MmsException { - PduCacheEntry cacheEntry = PDU_CACHE_INSTANCE.get(uri); - if (cacheEntry != null) { - return cacheEntry.getPdu(); - } - - Cursor c = SqliteWrapper.query(mContext, mContentResolver, uri, - PDU_PROJECTION, null, null, null); - PduHeaders headers = new PduHeaders(); - Set> set; - long msgId = ContentUris.parseId(uri); - int msgBox; - long threadId; - - try { - if ((c == null) || (c.getCount() != 1) || !c.moveToFirst()) { - throw new MmsException("Bad uri: " + uri); - } - - msgBox = c.getInt(PDU_COLUMN_MESSAGE_BOX); - threadId = c.getLong(PDU_COLUMN_THREAD_ID); - - set = ENCODED_STRING_COLUMN_INDEX_MAP.entrySet(); - for (Entry e : set) { - setEncodedStringValueToHeaders( - c, e.getValue(), headers, e.getKey()); - } - - set = TEXT_STRING_COLUMN_INDEX_MAP.entrySet(); - for (Entry e : set) { - setTextStringToHeaders( - c, e.getValue(), headers, e.getKey()); - } - - set = OCTET_COLUMN_INDEX_MAP.entrySet(); - for (Entry e : set) { - setOctetToHeaders( - c, e.getValue(), headers, e.getKey()); - } - - set = LONG_COLUMN_INDEX_MAP.entrySet(); - for (Entry e : set) { - setLongToHeaders( - c, e.getValue(), headers, e.getKey()); - } - } finally { - if (c != null) { - c.close(); - } - } - - // Check whether 'msgId' has been assigned a valid value. - if (msgId == -1L) { - throw new MmsException("Error! ID of the message: -1."); - } - - // Load address information of the MM. - loadAddress(msgId, headers); - - int msgType = headers.getOctet(PduHeaders.MESSAGE_TYPE); - PduBody body = new PduBody(); - - // For PDU which type is M_retrieve.conf or Send.req, we should - // load multiparts and put them into the body of the PDU. - if ((msgType == PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF) - || (msgType == PduHeaders.MESSAGE_TYPE_SEND_REQ)) { - PduPart[] parts = loadParts(msgId); - if (parts != null) { - int partsNum = parts.length; - for (int i = 0; i < partsNum; i++) { - body.addPart(parts[i]); - } - } - } - - GenericPdu pdu = null; - switch (msgType) { - case PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND: - pdu = new NotificationInd(headers); - break; - case PduHeaders.MESSAGE_TYPE_DELIVERY_IND: - pdu = new DeliveryInd(headers); - break; - case PduHeaders.MESSAGE_TYPE_READ_ORIG_IND: - pdu = new ReadOrigInd(headers); - break; - case PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF: - pdu = new RetrieveConf(headers, body); - break; - case PduHeaders.MESSAGE_TYPE_SEND_REQ: - pdu = new SendReq(headers, body); - break; - case PduHeaders.MESSAGE_TYPE_ACKNOWLEDGE_IND: - pdu = new AcknowledgeInd(headers); - break; - case PduHeaders.MESSAGE_TYPE_NOTIFYRESP_IND: - pdu = new NotifyRespInd(headers); - break; - case PduHeaders.MESSAGE_TYPE_READ_REC_IND: - pdu = new ReadRecInd(headers); - break; - case PduHeaders.MESSAGE_TYPE_SEND_CONF: - case PduHeaders.MESSAGE_TYPE_FORWARD_REQ: - case PduHeaders.MESSAGE_TYPE_FORWARD_CONF: - case PduHeaders.MESSAGE_TYPE_MBOX_STORE_REQ: - case PduHeaders.MESSAGE_TYPE_MBOX_STORE_CONF: - case PduHeaders.MESSAGE_TYPE_MBOX_VIEW_REQ: - case PduHeaders.MESSAGE_TYPE_MBOX_VIEW_CONF: - case PduHeaders.MESSAGE_TYPE_MBOX_UPLOAD_REQ: - case PduHeaders.MESSAGE_TYPE_MBOX_UPLOAD_CONF: - case PduHeaders.MESSAGE_TYPE_MBOX_DELETE_REQ: - case PduHeaders.MESSAGE_TYPE_MBOX_DELETE_CONF: - case PduHeaders.MESSAGE_TYPE_MBOX_DESCR: - case PduHeaders.MESSAGE_TYPE_DELETE_REQ: - case PduHeaders.MESSAGE_TYPE_DELETE_CONF: - case PduHeaders.MESSAGE_TYPE_CANCEL_REQ: - case PduHeaders.MESSAGE_TYPE_CANCEL_CONF: - throw new MmsException( - "Unsupported PDU type: " + Integer.toHexString(msgType)); - - default: - throw new MmsException( - "Unrecognized PDU type: " + Integer.toHexString(msgType)); - } - - cacheEntry = new PduCacheEntry(pdu, msgBox, threadId); - PDU_CACHE_INSTANCE.put(uri, cacheEntry); - return pdu; - } - - private void persistAddress( - long msgId, int type, EncodedStringValue[] array) { - ContentValues values = new ContentValues(3); - - for (EncodedStringValue addr : array) { - values.clear(); // Clear all values first. - values.put(Addr.ADDRESS, toIsoString(addr.getTextString())); - values.put(Addr.CHARSET, addr.getCharacterSet()); - values.put(Addr.TYPE, type); - - Uri uri = Uri.parse("content://mms/" + msgId + "/addr"); - SqliteWrapper.insert(mContext, mContentResolver, uri, values); - } - } - - public Uri persistPart(PduPart part, long msgId) - throws MmsException { - Uri uri = Uri.parse("content://mms/" + msgId + "/part"); - ContentValues values = new ContentValues(8); - - int charset = part.getCharset(); - if (charset != 0 ) { - values.put(Part.CHARSET, charset); - } - - String contentType = null; - if (part.getContentType() != null) { - contentType = toIsoString(part.getContentType()); - values.put(Part.CONTENT_TYPE, contentType); - // To ensure the SMIL part is always the first part. - if (ContentType.APP_SMIL.equals(contentType)) { - values.put(Part.SEQ, -1); - } - } else { - throw new MmsException("MIME type of the part must be set."); - } - - if (part.getFilename() != null) { - String fileName = new String(part.getFilename()); - values.put(Part.FILENAME, fileName); - } - - if (part.getName() != null) { - String name = new String(part.getName()); - values.put(Part.NAME, name); - } - - Object value = null; - if (part.getContentDisposition() != null) { - value = toIsoString(part.getContentDisposition()); - values.put(Part.CONTENT_DISPOSITION, (String) value); - } - - if (part.getContentId() != null) { - value = toIsoString(part.getContentId()); - values.put(Part.CONTENT_ID, (String) value); - } - - if (part.getContentLocation() != null) { - value = toIsoString(part.getContentLocation()); - values.put(Part.CONTENT_LOCATION, (String) value); - } - - Uri res = SqliteWrapper.insert(mContext, mContentResolver, uri, values); - if (res == null) { - throw new MmsException("Failed to persist part, return null."); - } - - persistData(part, res, contentType); - // After successfully store the data, we should update - // the dataUri of the part. - part.setDataUri(res); - - return res; - } - - /** - * Save data of the part into storage. The source data may be given - * by a byte[] or a Uri. If it's a byte[], directly save it - * into storage, otherwise load source data from the dataUri and then - * save it. If the data is an image, we may scale down it according - * to user preference. - * - * @param part The PDU part which contains data to be saved. - * @param uri The URI of the part. - * @param contentType The MIME type of the part. - * @throws MmsException Cannot find source data or error occurred - * while saving the data. - */ - private void persistData(PduPart part, Uri uri, - String contentType) - throws MmsException { - OutputStream os = null; - InputStream is = null; - - try { - byte[] data = part.getData(); - if (ContentType.TEXT_PLAIN.equals(contentType) - || ContentType.APP_SMIL.equals(contentType) - || ContentType.TEXT_HTML.equals(contentType)) { - ContentValues cv = new ContentValues(); - cv.put(TelephonyProvider.Mms.Part.TEXT, new EncodedStringValue(data).getString()); - if (mContentResolver.update(uri, cv, null, null) != 1) { - throw new MmsException("unable to update " + uri.toString()); - } - } else { - os = mContentResolver.openOutputStream(uri); - if (data == null) { - Uri dataUri = part.getDataUri(); - if ((dataUri == null) || (dataUri == uri)) { - Log.w(TAG, "Can't find data for this part."); - return; - } - is = mContentResolver.openInputStream(dataUri); - - if (LOCAL_LOGV) { - Log.v(TAG, "Saving data to: " + uri); - } - - byte[] buffer = new byte[256]; - for (int len = 0; (len = is.read(buffer)) != -1; ) { - os.write(buffer, 0, len); - } - } else { - if (LOCAL_LOGV) { - Log.v(TAG, "Saving data to: " + uri); - } - os.write(data); - } - } - } catch (FileNotFoundException e) { - Log.e(TAG, "Failed to open Input/Output stream.", e); - throw new MmsException(e); - } catch (IOException e) { - Log.e(TAG, "Failed to read/write data.", e); - throw new MmsException(e); - } finally { - if (os != null) { - try { - os.close(); - } catch (IOException e) { - Log.e(TAG, "IOException while closing: " + os, e); - } // Ignore - } - if (is != null) { - try { - is.close(); - } catch (IOException e) { - Log.e(TAG, "IOException while closing: " + is, e); - } // Ignore - } - } - } - - private void updateAddress( - long msgId, int type, EncodedStringValue[] array) { - // Delete old address information and then insert new ones. - SqliteWrapper.delete(mContext, mContentResolver, - Uri.parse("content://mms/" + msgId + "/addr"), - Addr.TYPE + "=" + type, null); - - persistAddress(msgId, type, array); - } - - /** - * Update headers of a SendReq. - * - * @param uri The PDU which need to be updated. - * @param pdu New headers. - * @throws MmsException Bad URI or updating failed. - */ - public void updateHeaders(Uri uri, SendReq sendReq) { - PDU_CACHE_INSTANCE.purge(uri); - - ContentValues values = new ContentValues(10); - byte[] contentType = sendReq.getContentType(); - if (contentType != null) { - values.put(Mms.CONTENT_TYPE, toIsoString(contentType)); - } - - long date = sendReq.getDate(); - if (date != -1) { - values.put(Mms.DATE, date); - } - - int deliveryReport = sendReq.getDeliveryReport(); - if (deliveryReport != 0) { - values.put(Mms.DELIVERY_REPORT, deliveryReport); - } - - long expiry = sendReq.getExpiry(); - if (expiry != -1) { - values.put(Mms.EXPIRY, expiry); - } - - byte[] msgClass = sendReq.getMessageClass(); - if (msgClass != null) { - values.put(Mms.MESSAGE_CLASS, toIsoString(msgClass)); - } - - int priority = sendReq.getPriority(); - if (priority != 0) { - values.put(Mms.PRIORITY, priority); - } - - int readReport = sendReq.getReadReport(); - if (readReport != 0) { - values.put(Mms.READ_REPORT, readReport); - } - - byte[] transId = sendReq.getTransactionId(); - if (transId != null) { - values.put(Mms.TRANSACTION_ID, toIsoString(transId)); - } - - EncodedStringValue subject = sendReq.getSubject(); - if (subject != null) { - values.put(Mms.SUBJECT, toIsoString(subject.getTextString())); - values.put(Mms.SUBJECT_CHARSET, subject.getCharacterSet()); - } else { - values.put(Mms.SUBJECT, ""); - } - - long messageSize = sendReq.getMessageSize(); - if (messageSize > 0) { - values.put(Mms.MESSAGE_SIZE, messageSize); - } - - PduHeaders headers = sendReq.getPduHeaders(); - HashSet recipients = new HashSet(); - for (int addrType : ADDRESS_FIELDS) { - EncodedStringValue[] array = null; - if (addrType == PduHeaders.FROM) { - EncodedStringValue v = headers.getEncodedStringValue(addrType); - if (v != null) { - array = new EncodedStringValue[1]; - array[0] = v; - } - } else { - array = headers.getEncodedStringValues(addrType); - } - - if (array != null) { - long msgId = ContentUris.parseId(uri); - updateAddress(msgId, addrType, array); - if (addrType == PduHeaders.TO) { - for (EncodedStringValue v : array) { - if (v != null) { - recipients.add(v.getString()); - } - } - } - } - } - - long threadId = Threads.getOrCreateThreadId(mContext, recipients); - values.put(Mms.THREAD_ID, threadId); - - SqliteWrapper.update(mContext, mContentResolver, uri, values, null, null); - } - - private void updatePart(Uri uri, PduPart part) throws MmsException { - ContentValues values = new ContentValues(7); - - int charset = part.getCharset(); - if (charset != 0 ) { - values.put(Part.CHARSET, charset); - } - - String contentType = null; - if (part.getContentType() != null) { - contentType = toIsoString(part.getContentType()); - values.put(Part.CONTENT_TYPE, contentType); - } else { - throw new MmsException("MIME type of the part must be set."); - } - - if (part.getFilename() != null) { - String fileName = new String(part.getFilename()); - values.put(Part.FILENAME, fileName); - } - - if (part.getName() != null) { - String name = new String(part.getName()); - values.put(Part.NAME, name); - } - - Object value = null; - if (part.getContentDisposition() != null) { - value = toIsoString(part.getContentDisposition()); - values.put(Part.CONTENT_DISPOSITION, (String) value); - } - - if (part.getContentId() != null) { - value = toIsoString(part.getContentId()); - values.put(Part.CONTENT_ID, (String) value); - } - - if (part.getContentLocation() != null) { - value = toIsoString(part.getContentLocation()); - values.put(Part.CONTENT_LOCATION, (String) value); - } - - SqliteWrapper.update(mContext, mContentResolver, uri, values, null, null); - - // Only update the data when: - // 1. New binary data supplied or - // 2. The Uri of the part is different from the current one. - if ((part.getData() != null) - || (uri != part.getDataUri())) { - persistData(part, uri, contentType); - } - } - - /** - * Update all parts of a PDU. - * - * @param uri The PDU which need to be updated. - * @param body New message body of the PDU. - * @throws MmsException Bad URI or updating failed. - */ - public void updateParts(Uri uri, PduBody body) - throws MmsException { - PduCacheEntry cacheEntry = PDU_CACHE_INSTANCE.get(uri); - if (cacheEntry != null) { - ((MultimediaMessagePdu) cacheEntry.getPdu()).setBody(body); - } - - ArrayList toBeCreated = new ArrayList(); - HashMap toBeUpdated = new HashMap(); - - int partsNum = body.getPartsNum(); - StringBuilder filter = new StringBuilder().append('('); - for (int i = 0; i < partsNum; i++) { - PduPart part = body.getPart(i); - Uri partUri = part.getDataUri(); - if ((partUri == null) || !partUri.getAuthority().startsWith("mms")) { - toBeCreated.add(part); - } else { - toBeUpdated.put(partUri, part); - - // Don't use 'i > 0' to determine whether we should append - // 'AND' since 'i = 0' may be skipped in another branch. - if (filter.length() > 1) { - filter.append(" AND "); - } - - filter.append(Part._ID); - filter.append("!="); - DatabaseUtils.appendEscapedSQLString(filter, partUri.getLastPathSegment()); - } - } - filter.append(')'); - - long msgId = ContentUris.parseId(uri); - - // Remove the parts which doesn't exist anymore. - SqliteWrapper.delete(mContext, mContentResolver, - Uri.parse(Mms.CONTENT_URI + "/" + msgId + "/part"), - filter.length() > 2 ? filter.toString() : null, null); - - // Create new parts which didn't exist before. - for (PduPart part : toBeCreated) { - persistPart(part, msgId); - } - - // Update the modified parts. - for (Map.Entry e : toBeUpdated.entrySet()) { - updatePart(e.getKey(), e.getValue()); - } - } - - /** - * Persist a PDU object to specific location in the storage. - * - * @param pdu The PDU object to be stored. - * @param uri Where to store the given PDU object. - * @return A Uri which can be used to access the stored PDU. - */ - public Uri persist(GenericPdu pdu, Uri uri) throws MmsException { - if (uri == null) { - throw new MmsException("Uri may not be null."); - } - - Integer msgBox = MESSAGE_BOX_MAP.get(uri); - if (msgBox == null) { - throw new MmsException( - "Bad destination, must be one of " - + "content://mms/inbox, content://mms/sent, " - + "content://mms/drafts, content://mms/outbox, " - + "content://mms/temp."); - } - PDU_CACHE_INSTANCE.purge(uri); - - PduHeaders header = pdu.getPduHeaders(); - PduBody body = null; - ContentValues values = new ContentValues(); - Set> set; - - set = ENCODED_STRING_COLUMN_NAME_MAP.entrySet(); - for (Entry e : set) { - int field = e.getKey(); - EncodedStringValue encodedString = header.getEncodedStringValue(field); - if (encodedString != null) { - String charsetColumn = CHARSET_COLUMN_NAME_MAP.get(field); - values.put(e.getValue(), toIsoString(encodedString.getTextString())); - values.put(charsetColumn, encodedString.getCharacterSet()); - } - } - - set = TEXT_STRING_COLUMN_NAME_MAP.entrySet(); - for (Entry e : set){ - byte[] text = header.getTextString(e.getKey()); - if (text != null) { - values.put(e.getValue(), toIsoString(text)); - } - } - - set = OCTET_COLUMN_NAME_MAP.entrySet(); - for (Entry e : set){ - int b = header.getOctet(e.getKey()); - if (b != 0) { - values.put(e.getValue(), b); - } - } - - set = LONG_COLUMN_NAME_MAP.entrySet(); - for (Entry e : set){ - long l = header.getLongInteger(e.getKey()); - if (l != -1L) { - values.put(e.getValue(), l); - } - } - - HashMap addressMap = - new HashMap(ADDRESS_FIELDS.length); - // Save address information. - for (int addrType : ADDRESS_FIELDS) { - EncodedStringValue[] array = null; - if (addrType == PduHeaders.FROM) { - EncodedStringValue v = header.getEncodedStringValue(addrType); - if (v != null) { - array = new EncodedStringValue[1]; - array[0] = v; - } - } else { - array = header.getEncodedStringValues(addrType); - } - addressMap.put(addrType, array); - } - - HashSet recipients = new HashSet(); - long threadId = DUMMY_THREAD_ID; - int msgType = pdu.getMessageType(); - // Here we only allocate thread ID for M-Notification.ind, - // M-Retrieve.conf and M-Send.req. - // Some of other PDU types may be allocated a thread ID outside - // this scope. - if ((msgType == PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND) - || (msgType == PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF) - || (msgType == PduHeaders.MESSAGE_TYPE_SEND_REQ)) { - EncodedStringValue[] array = null; - switch (msgType) { - case PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND: - case PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF: - array = addressMap.get(PduHeaders.FROM); - break; - case PduHeaders.MESSAGE_TYPE_SEND_REQ: - array = addressMap.get(PduHeaders.TO); - break; - } - - if (array != null) { - for (EncodedStringValue v : array) { - if (v != null) { - recipients.add(v.getString()); - } - } - } - threadId = Threads.getOrCreateThreadId(mContext, recipients); - } - values.put(Mms.THREAD_ID, threadId); - - // Save parts first to avoid inconsistent message is loaded - // while saving the parts. - long dummyId = System.currentTimeMillis(); // Dummy ID of the msg. - // Get body if the PDU is a RetrieveConf or SendReq. - if (pdu instanceof MultimediaMessagePdu) { - body = ((MultimediaMessagePdu) pdu).getBody(); - // Start saving parts if necessary. - if (body != null) { - int partsNum = body.getPartsNum(); - for (int i = 0; i < partsNum; i++) { - PduPart part = body.getPart(i); - persistPart(part, dummyId); - } - } - } - - // mark "read" and "seen" - values.put(Mms.READ, 0); - values.put(Mms.SEEN, 0); - - Uri res = SqliteWrapper.insert(mContext, mContentResolver, uri, values); - if (res == null) { - throw new MmsException("persist() failed: return null."); - } - - // Get the real ID of the PDU and update all parts which were - // saved with the dummy ID. - long msgId = ContentUris.parseId(res); - values = new ContentValues(1); - values.put(Part.MSG_ID, msgId); - SqliteWrapper.update(mContext, mContentResolver, - Uri.parse("content://mms/" + dummyId + "/part"), - values, null, null); - // We should return the longest URI of the persisted PDU, for - // example, if input URI is "content://mms/inbox" and the _ID of - // persisted PDU is '8', we should return "content://mms/inbox/8" - // instead of "content://mms/8". - // FIXME: Should the MmsProvider be responsible for this??? - res = Uri.parse(uri + "/" + msgId); - - // Save address information. - for (int addrType : ADDRESS_FIELDS) { - EncodedStringValue[] array = addressMap.get(addrType); - if (array != null) { - persistAddress(msgId, addrType, array); - } - } - - return res; - } - - /** - * Move a PDU object from one location to another. - * - * @param from Specify the PDU object to be moved. - * @param to The destination location, should be one of the following: - * "content://mms/inbox", "content://mms/sent", - * "content://mms/drafts", "content://mms/outbox", - * "content://mms/trash". - * @return New Uri of the moved PDU. - * @throws MmsException Error occurred while moving the message. - */ - public Uri move(Uri from, Uri to) throws MmsException { - // Check whether the 'msgId' has been assigned a valid value. - long msgId = ContentUris.parseId(from); - if (msgId == -1L) { - throw new MmsException("Error! ID of the message: -1."); - } - - // Get corresponding int value of destination box. - Integer msgBox = MESSAGE_BOX_MAP.get(to); - if (msgBox == null) { - throw new MmsException( - "Bad destination, must be one of " - + "content://mms/inbox, content://mms/sent, " - + "content://mms/drafts, content://mms/outbox, " - + "content://mms/temp."); - } - - ContentValues values = new ContentValues(1); - values.put(Mms.MESSAGE_BOX, msgBox); - SqliteWrapper.update(mContext, mContentResolver, from, values, null, null); - return ContentUris.withAppendedId(to, msgId); - } - - /** - * Wrap a byte[] into a String. - */ - public static String toIsoString(byte[] bytes) { - try { - return new String(bytes, CharacterSets.MIMENAME_ISO_8859_1); - } catch (UnsupportedEncodingException e) { - // Impossible to reach here! - Log.e(TAG, "ISO_8859_1 must be supported!", e); - return ""; - } - } - - /** - * Unpack a given String into a byte[]. - */ - public static byte[] getBytes(String data) { - try { - return data.getBytes(CharacterSets.MIMENAME_ISO_8859_1); - } catch (UnsupportedEncodingException e) { - // Impossible to reach here! - Log.e(TAG, "ISO_8859_1 must be supported!", e); - return new byte[0]; - } - } - - /** - * Remove all objects in the temporary path. - */ - public void release() { - Uri uri = Uri.parse(TEMPORARY_DRM_OBJECT_URI); - SqliteWrapper.delete(mContext, mContentResolver, uri, null, null); - } - - /** - * Find all messages to be sent or downloaded before certain time. - */ - public Cursor getPendingMessages(long dueTime) { - Uri.Builder uriBuilder = PendingMessages.CONTENT_URI.buildUpon(); - uriBuilder.appendQueryParameter("protocol", "mms"); - - String selection = PendingMessages.ERROR_TYPE + " < ?" - + " AND " + PendingMessages.DUE_TIME + " <= ?"; - - String[] selectionArgs = new String[] { - String.valueOf(MmsSms.ERR_TYPE_GENERIC_PERMANENT), - String.valueOf(dueTime) - }; - - return SqliteWrapper.query(mContext, mContentResolver, - uriBuilder.build(), null, selection, selectionArgs, - PendingMessages.DUE_TIME); - } -} diff --git a/mms-common/java/com/android/mmscommon/mms/pdu/QuotedPrintable.java b/mms-common/java/com/android/mmscommon/mms/pdu/QuotedPrintable.java deleted file mode 100644 index e9da7df1f..000000000 --- a/mms-common/java/com/android/mmscommon/mms/pdu/QuotedPrintable.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2007 Esmertec AG. - * 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. - */ - -package com.android.mmscommon.mms.pdu; - -import java.io.ByteArrayOutputStream; - -public class QuotedPrintable { - private static byte ESCAPE_CHAR = '='; - - /** - * Decodes an array quoted-printable characters into an array of original bytes. - * Escaped characters are converted back to their original representation. - * - *

- * This function implements a subset of - * quoted-printable encoding specification (rule #1 and rule #2) - * as defined in RFC 1521. - *

- * - * @param bytes array of quoted-printable characters - * @return array of original bytes, - * null if quoted-printable decoding is unsuccessful. - */ - public static final byte[] decodeQuotedPrintable(byte[] bytes) { - if (bytes == null) { - return null; - } - ByteArrayOutputStream buffer = new ByteArrayOutputStream(); - for (int i = 0; i < bytes.length; i++) { - int b = bytes[i]; - if (b == ESCAPE_CHAR) { - try { - if('\r' == (char)bytes[i + 1] && - '\n' == (char)bytes[i + 2]) { - i += 2; - continue; - } - int u = Character.digit((char) bytes[++i], 16); - int l = Character.digit((char) bytes[++i], 16); - if (u == -1 || l == -1) { - return null; - } - buffer.write((char) ((u << 4) + l)); - } catch (ArrayIndexOutOfBoundsException e) { - return null; - } - } else { - buffer.write(b); - } - } - return buffer.toByteArray(); - } -} diff --git a/mms-common/java/com/android/mmscommon/mms/pdu/ReadOrigInd.java b/mms-common/java/com/android/mmscommon/mms/pdu/ReadOrigInd.java deleted file mode 100644 index 967878486..000000000 --- a/mms-common/java/com/android/mmscommon/mms/pdu/ReadOrigInd.java +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright (C) 2007 Esmertec AG. - * 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. - */ - -package com.android.mmscommon.mms.pdu; - -import com.android.mmscommon.EncodedStringValue; -import com.android.mmscommon.InvalidHeaderValueException; -import com.android.mmscommon.PduHeaders; - -public class ReadOrigInd extends GenericPdu { - /** - * Empty constructor. - * Since the Pdu corresponding to this class is constructed - * by the Proxy-Relay server, this class is only instantiated - * by the Pdu Parser. - * - * @throws InvalidHeaderValueException if error occurs. - */ - public ReadOrigInd() throws InvalidHeaderValueException { - super(); - setMessageType(PduHeaders.MESSAGE_TYPE_READ_ORIG_IND); - } - - /** - * Constructor with given headers. - * - * @param headers Headers for this PDU. - */ - ReadOrigInd(PduHeaders headers) { - super(headers); - } - - /** - * Get Date value. - * - * @return the value - */ - public long getDate() { - return mPduHeaders.getLongInteger(PduHeaders.DATE); - } - - /** - * Set Date value. - * - * @param value the value - */ - public void setDate(long value) { - mPduHeaders.setLongInteger(value, PduHeaders.DATE); - } - - /** - * Get From value. - * From-value = Value-length - * (Address-present-token Encoded-string-value | Insert-address-token) - * - * @return the value - */ - public EncodedStringValue getFrom() { - return mPduHeaders.getEncodedStringValue(PduHeaders.FROM); - } - - /** - * Set From value. - * - * @param value the value - * @throws NullPointerException if the value is null. - */ - public void setFrom(EncodedStringValue value) { - mPduHeaders.setEncodedStringValue(value, PduHeaders.FROM); - } - - /** - * Get Message-ID value. - * - * @return the value - */ - public byte[] getMessageId() { - return mPduHeaders.getTextString(PduHeaders.MESSAGE_ID); - } - - /** - * Set Message-ID value. - * - * @param value the value - * @throws NullPointerException if the value is null. - */ - public void setMessageId(byte[] value) { - mPduHeaders.setTextString(value, PduHeaders.MESSAGE_ID); - } - - /** - * Get X-MMS-Read-status value. - * - * @return the value - */ - public int getReadStatus() { - return mPduHeaders.getOctet(PduHeaders.READ_STATUS); - } - - /** - * Set X-MMS-Read-status value. - * - * @param value the value - * @throws InvalidHeaderValueException if the value is invalid. - */ - public void setReadStatus(int value) throws InvalidHeaderValueException { - mPduHeaders.setOctet(value, PduHeaders.READ_STATUS); - } - - /** - * Get To value. - * - * @return the value - */ - public EncodedStringValue[] getTo() { - return mPduHeaders.getEncodedStringValues(PduHeaders.TO); - } - - /** - * Set To value. - * - * @param value the value - * @throws NullPointerException if the value is null. - */ - public void setTo(EncodedStringValue[] value) { - mPduHeaders.setEncodedStringValues(value, PduHeaders.TO); - } - - /* - * Optional, not supported header fields: - * - * public byte[] getApplicId() {return null;} - * public void setApplicId(byte[] value) {} - * - * public byte[] getAuxApplicId() {return null;} - * public void getAuxApplicId(byte[] value) {} - * - * public byte[] getReplyApplicId() {return 0x00;} - * public void setReplyApplicId(byte[] value) {} - */ -} diff --git a/mms-common/java/com/android/mmscommon/mms/pdu/ReadRecInd.java b/mms-common/java/com/android/mmscommon/mms/pdu/ReadRecInd.java deleted file mode 100644 index c1efbbc37..000000000 --- a/mms-common/java/com/android/mmscommon/mms/pdu/ReadRecInd.java +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright (C) 2007 Esmertec AG. - * 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. - */ - -package com.android.mmscommon.mms.pdu; - -import com.android.mmscommon.EncodedStringValue; -import com.android.mmscommon.InvalidHeaderValueException; -import com.android.mmscommon.PduHeaders; - -public class ReadRecInd extends GenericPdu { - /** - * Constructor, used when composing a M-ReadRec.ind pdu. - * - * @param from the from value - * @param messageId the message ID value - * @param mmsVersion current viersion of mms - * @param readStatus the read status value - * @param to the to value - * @throws InvalidHeaderValueException if parameters are invalid. - * NullPointerException if messageId or to is null. - */ - public ReadRecInd(EncodedStringValue from, - byte[] messageId, - int mmsVersion, - int readStatus, - EncodedStringValue[] to) throws InvalidHeaderValueException { - super(); - setMessageType(PduHeaders.MESSAGE_TYPE_READ_REC_IND); - setFrom(from); - setMessageId(messageId); - setMmsVersion(mmsVersion); - setTo(to); - setReadStatus(readStatus); - } - - /** - * Constructor with given headers. - * - * @param headers Headers for this PDU. - */ - ReadRecInd(PduHeaders headers) { - super(headers); - } - - /** - * Get Date value. - * - * @return the value - */ - public long getDate() { - return mPduHeaders.getLongInteger(PduHeaders.DATE); - } - - /** - * Set Date value. - * - * @param value the value - */ - public void setDate(long value) { - mPduHeaders.setLongInteger(value, PduHeaders.DATE); - } - - /** - * Get Message-ID value. - * - * @return the value - */ - public byte[] getMessageId() { - return mPduHeaders.getTextString(PduHeaders.MESSAGE_ID); - } - - /** - * Set Message-ID value. - * - * @param value the value - * @throws NullPointerException if the value is null. - */ - public void setMessageId(byte[] value) { - mPduHeaders.setTextString(value, PduHeaders.MESSAGE_ID); - } - - /** - * Get To value. - * - * @return the value - */ - public EncodedStringValue[] getTo() { - return mPduHeaders.getEncodedStringValues(PduHeaders.TO); - } - - /** - * Set To value. - * - * @param value the value - * @throws NullPointerException if the value is null. - */ - public void setTo(EncodedStringValue[] value) { - mPduHeaders.setEncodedStringValues(value, PduHeaders.TO); - } - - /** - * Get X-MMS-Read-status value. - * - * @return the value - */ - public int getReadStatus() { - return mPduHeaders.getOctet(PduHeaders.READ_STATUS); - } - - /** - * Set X-MMS-Read-status value. - * - * @param value the value - * @throws InvalidHeaderValueException if the value is invalid. - */ - public void setReadStatus(int value) throws InvalidHeaderValueException { - mPduHeaders.setOctet(value, PduHeaders.READ_STATUS); - } - - /* - * Optional, not supported header fields: - * - * public byte[] getApplicId() {return null;} - * public void setApplicId(byte[] value) {} - * - * public byte[] getAuxApplicId() {return null;} - * public void getAuxApplicId(byte[] value) {} - * - * public byte[] getReplyApplicId() {return 0x00;} - * public void setReplyApplicId(byte[] value) {} - */ -} diff --git a/mms-common/java/com/android/mmscommon/mms/pdu/RetrieveConf.java b/mms-common/java/com/android/mmscommon/mms/pdu/RetrieveConf.java deleted file mode 100644 index 442949e95..000000000 --- a/mms-common/java/com/android/mmscommon/mms/pdu/RetrieveConf.java +++ /dev/null @@ -1,302 +0,0 @@ -/* - * Copyright (C) 2007 Esmertec AG. - * 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. - */ - -package com.android.mmscommon.mms.pdu; - -import com.android.mmscommon.EncodedStringValue; -import com.android.mmscommon.InvalidHeaderValueException; -import com.android.mmscommon.PduHeaders; - -/** - * M-Retrive.conf Pdu. - */ -public class RetrieveConf extends MultimediaMessagePdu { - /** - * Empty constructor. - * Since the Pdu corresponding to this class is constructed - * by the Proxy-Relay server, this class is only instantiated - * by the Pdu Parser. - * - * @throws InvalidHeaderValueException if error occurs. - */ - public RetrieveConf() throws InvalidHeaderValueException { - super(); - setMessageType(PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF); - } - - /** - * Constructor with given headers. - * - * @param headers Headers for this PDU. - */ - RetrieveConf(PduHeaders headers) { - super(headers); - } - - /** - * Constructor with given headers and body - * - * @param headers Headers for this PDU. - * @param body Body of this PDu. - */ - RetrieveConf(PduHeaders headers, PduBody body) { - super(headers, body); - } - - /** - * Get CC value. - * - * @return the value - */ - public EncodedStringValue[] getCc() { - return mPduHeaders.getEncodedStringValues(PduHeaders.CC); - } - - /** - * Add a "CC" value. - * - * @param value the value - * @throws NullPointerException if the value is null. - */ - public void addCc(EncodedStringValue value) { - mPduHeaders.appendEncodedStringValue(value, PduHeaders.CC); - } - - /** - * Get Content-type value. - * - * @return the value - */ - public byte[] getContentType() { - return mPduHeaders.getTextString(PduHeaders.CONTENT_TYPE); - } - - /** - * Set Content-type value. - * - * @param value the value - * @throws NullPointerException if the value is null. - */ - public void setContentType(byte[] value) { - mPduHeaders.setTextString(value, PduHeaders.CONTENT_TYPE); - } - - /** - * Get X-Mms-Delivery-Report value. - * - * @return the value - */ - public int getDeliveryReport() { - return mPduHeaders.getOctet(PduHeaders.DELIVERY_REPORT); - } - - /** - * Set X-Mms-Delivery-Report value. - * - * @param value the value - * @throws InvalidHeaderValueException if the value is invalid. - */ - public void setDeliveryReport(int value) throws InvalidHeaderValueException { - mPduHeaders.setOctet(value, PduHeaders.DELIVERY_REPORT); - } - - /** - * Get From value. - * From-value = Value-length - * (Address-present-token Encoded-string-value | Insert-address-token) - * - * @return the value - */ - public EncodedStringValue getFrom() { - return mPduHeaders.getEncodedStringValue(PduHeaders.FROM); - } - - /** - * Set From value. - * - * @param value the value - * @throws NullPointerException if the value is null. - */ - public void setFrom(EncodedStringValue value) { - mPduHeaders.setEncodedStringValue(value, PduHeaders.FROM); - } - - /** - * Get X-Mms-Message-Class value. - * Message-class-value = Class-identifier | Token-text - * Class-identifier = Personal | Advertisement | Informational | Auto - * - * @return the value - */ - public byte[] getMessageClass() { - return mPduHeaders.getTextString(PduHeaders.MESSAGE_CLASS); - } - - /** - * Set X-Mms-Message-Class value. - * - * @param value the value - * @throws NullPointerException if the value is null. - */ - public void setMessageClass(byte[] value) { - mPduHeaders.setTextString(value, PduHeaders.MESSAGE_CLASS); - } - - /** - * Get Message-ID value. - * - * @return the value - */ - public byte[] getMessageId() { - return mPduHeaders.getTextString(PduHeaders.MESSAGE_ID); - } - - /** - * Set Message-ID value. - * - * @param value the value - * @throws NullPointerException if the value is null. - */ - public void setMessageId(byte[] value) { - mPduHeaders.setTextString(value, PduHeaders.MESSAGE_ID); - } - - /** - * Get X-Mms-Read-Report value. - * - * @return the value - */ - public int getReadReport() { - return mPduHeaders.getOctet(PduHeaders.READ_REPORT); - } - - /** - * Set X-Mms-Read-Report value. - * - * @param value the value - * @throws InvalidHeaderValueException if the value is invalid. - */ - public void setReadReport(int value) throws InvalidHeaderValueException { - mPduHeaders.setOctet(value, PduHeaders.READ_REPORT); - } - - /** - * Get X-Mms-Retrieve-Status value. - * - * @return the value - */ - public int getRetrieveStatus() { - return mPduHeaders.getOctet(PduHeaders.RETRIEVE_STATUS); - } - - /** - * Set X-Mms-Retrieve-Status value. - * - * @param value the value - * @throws InvalidHeaderValueException if the value is invalid. - */ - public void setRetrieveStatus(int value) throws InvalidHeaderValueException { - mPduHeaders.setOctet(value, PduHeaders.RETRIEVE_STATUS); - } - - /** - * Get X-Mms-Retrieve-Text value. - * - * @return the value - */ - public EncodedStringValue getRetrieveText() { - return mPduHeaders.getEncodedStringValue(PduHeaders.RETRIEVE_TEXT); - } - - /** - * Set X-Mms-Retrieve-Text value. - * - * @param value the value - * @throws NullPointerException if the value is null. - */ - public void setRetrieveText(EncodedStringValue value) { - mPduHeaders.setEncodedStringValue(value, PduHeaders.RETRIEVE_TEXT); - } - - /** - * Get X-Mms-Transaction-Id. - * - * @return the value - */ - public byte[] getTransactionId() { - return mPduHeaders.getTextString(PduHeaders.TRANSACTION_ID); - } - - /** - * Set X-Mms-Transaction-Id. - * - * @param value the value - * @throws NullPointerException if the value is null. - */ - public void setTransactionId(byte[] value) { - mPduHeaders.setTextString(value, PduHeaders.TRANSACTION_ID); - } - - /* - * Optional, not supported header fields: - * - * public byte[] getApplicId() {return null;} - * public void setApplicId(byte[] value) {} - * - * public byte[] getAuxApplicId() {return null;} - * public void getAuxApplicId(byte[] value) {} - * - * public byte getContentClass() {return 0x00;} - * public void setApplicId(byte value) {} - * - * public byte getDrmContent() {return 0x00;} - * public void setDrmContent(byte value) {} - * - * public byte getDistributionIndicator() {return 0x00;} - * public void setDistributionIndicator(byte value) {} - * - * public PreviouslySentByValue getPreviouslySentBy() {return null;} - * public void setPreviouslySentBy(PreviouslySentByValue value) {} - * - * public PreviouslySentDateValue getPreviouslySentDate() {} - * public void setPreviouslySentDate(PreviouslySentDateValue value) {} - * - * public MmFlagsValue getMmFlags() {return null;} - * public void setMmFlags(MmFlagsValue value) {} - * - * public MmStateValue getMmState() {return null;} - * public void getMmState(MmStateValue value) {} - * - * public byte[] getReplaceId() {return 0x00;} - * public void setReplaceId(byte[] value) {} - * - * public byte[] getReplyApplicId() {return 0x00;} - * public void setReplyApplicId(byte[] value) {} - * - * public byte getReplyCharging() {return 0x00;} - * public void setReplyCharging(byte value) {} - * - * public byte getReplyChargingDeadline() {return 0x00;} - * public void setReplyChargingDeadline(byte value) {} - * - * public byte[] getReplyChargingId() {return 0x00;} - * public void setReplyChargingId(byte[] value) {} - * - * public long getReplyChargingSize() {return 0;} - * public void setReplyChargingSize(long value) {} - */ -} diff --git a/mms-common/java/com/android/mmscommon/mms/pdu/SendConf.java b/mms-common/java/com/android/mmscommon/mms/pdu/SendConf.java deleted file mode 100644 index 0a57b6b2d..000000000 --- a/mms-common/java/com/android/mmscommon/mms/pdu/SendConf.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright (C) 2007 Esmertec AG. - * 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. - */ - -package com.android.mmscommon.mms.pdu; - -import com.android.mmscommon.EncodedStringValue; -import com.android.mmscommon.InvalidHeaderValueException; -import com.android.mmscommon.PduHeaders; - -public class SendConf extends GenericPdu { - /** - * Empty constructor. - * Since the Pdu corresponding to this class is constructed - * by the Proxy-Relay server, this class is only instantiated - * by the Pdu Parser. - * - * @throws InvalidHeaderValueException if error occurs. - */ - public SendConf() throws InvalidHeaderValueException { - super(); - setMessageType(PduHeaders.MESSAGE_TYPE_SEND_CONF); - } - - /** - * Constructor with given headers. - * - * @param headers Headers for this PDU. - */ - SendConf(PduHeaders headers) { - super(headers); - } - - /** - * Get Message-ID value. - * - * @return the value - */ - public byte[] getMessageId() { - return mPduHeaders.getTextString(PduHeaders.MESSAGE_ID); - } - - /** - * Set Message-ID value. - * - * @param value the value - * @throws NullPointerException if the value is null. - */ - public void setMessageId(byte[] value) { - mPduHeaders.setTextString(value, PduHeaders.MESSAGE_ID); - } - - /** - * Get X-Mms-Response-Status. - * - * @return the value - */ - public int getResponseStatus() { - return mPduHeaders.getOctet(PduHeaders.RESPONSE_STATUS); - } - - /** - * Set X-Mms-Response-Status. - * - * @param value the values - * @throws InvalidHeaderValueException if the value is invalid. - */ - public void setResponseStatus(int value) throws InvalidHeaderValueException { - mPduHeaders.setOctet(value, PduHeaders.RESPONSE_STATUS); - } - - /** - * Get X-Mms-Transaction-Id field value. - * - * @return the X-Mms-Report-Allowed value - */ - public byte[] getTransactionId() { - return mPduHeaders.getTextString(PduHeaders.TRANSACTION_ID); - } - - /** - * Set X-Mms-Transaction-Id field value. - * - * @param value the value - * @throws NullPointerException if the value is null. - */ - public void setTransactionId(byte[] value) { - mPduHeaders.setTextString(value, PduHeaders.TRANSACTION_ID); - } - - /* - * Optional, not supported header fields: - * - * public byte[] getContentLocation() {return null;} - * public void setContentLocation(byte[] value) {} - * - * public EncodedStringValue getResponseText() {return null;} - * public void setResponseText(EncodedStringValue value) {} - * - * public byte getStoreStatus() {return 0x00;} - * public void setStoreStatus(byte value) {} - * - * public byte[] getStoreStatusText() {return null;} - * public void setStoreStatusText(byte[] value) {} - */ -} diff --git a/mms-common/java/com/android/mmscommon/mms/pdu/SendReq.java b/mms-common/java/com/android/mmscommon/mms/pdu/SendReq.java deleted file mode 100644 index 5da4719ae..000000000 --- a/mms-common/java/com/android/mmscommon/mms/pdu/SendReq.java +++ /dev/null @@ -1,347 +0,0 @@ -/* - * Copyright (C) 2007-2008 Esmertec AG. - * Copyright (C) 2007-2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.mmscommon.mms.pdu; - -import android.util.Log; - -import com.android.mmscommon.EncodedStringValue; -import com.android.mmscommon.InvalidHeaderValueException; -import com.android.mmscommon.PduHeaders; - -public class SendReq extends MultimediaMessagePdu { - private static final String TAG = "SendReq"; - - public SendReq() { - super(); - - try { - setMessageType(PduHeaders.MESSAGE_TYPE_SEND_REQ); - setMmsVersion(PduHeaders.CURRENT_MMS_VERSION); - // FIXME: Content-type must be decided according to whether - // SMIL part present. - setContentType("application/vnd.wap.multipart.related".getBytes()); - setFrom(new EncodedStringValue(PduHeaders.FROM_INSERT_ADDRESS_TOKEN_STR.getBytes())); - setTransactionId(generateTransactionId()); - } catch (InvalidHeaderValueException e) { - // Impossible to reach here since all headers we set above are valid. - Log.e(TAG, "Unexpected InvalidHeaderValueException.", e); - throw new RuntimeException(e); - } - } - - private byte[] generateTransactionId() { - String transactionId = "T" + Long.toHexString(System.currentTimeMillis()); - return transactionId.getBytes(); - } - - /** - * Constructor, used when composing a M-Send.req pdu. - * - * @param contentType the content type value - * @param from the from value - * @param mmsVersion current viersion of mms - * @param transactionId the transaction-id value - * @throws InvalidHeaderValueException if parameters are invalid. - * NullPointerException if contentType, form or transactionId is null. - */ - public SendReq(byte[] contentType, - EncodedStringValue from, - int mmsVersion, - byte[] transactionId) throws InvalidHeaderValueException { - super(); - setMessageType(PduHeaders.MESSAGE_TYPE_SEND_REQ); - setContentType(contentType); - setFrom(from); - setMmsVersion(mmsVersion); - setTransactionId(transactionId); - } - - /** - * Constructor with given headers. - * - * @param headers Headers for this PDU. - */ - SendReq(PduHeaders headers) { - super(headers); - } - - /** - * Constructor with given headers and body - * - * @param headers Headers for this PDU. - * @param body Body of this PDu. - */ - SendReq(PduHeaders headers, PduBody body) { - super(headers, body); - } - - /** - * Get Bcc value. - * - * @return the value - */ - public EncodedStringValue[] getBcc() { - return mPduHeaders.getEncodedStringValues(PduHeaders.BCC); - } - - /** - * Add a "BCC" value. - * - * @param value the value - * @throws NullPointerException if the value is null. - */ - public void addBcc(EncodedStringValue value) { - mPduHeaders.appendEncodedStringValue(value, PduHeaders.BCC); - } - - /** - * Set "BCC" value. - * - * @param value the value - * @throws NullPointerException if the value is null. - */ - public void setBcc(EncodedStringValue[] value) { - mPduHeaders.setEncodedStringValues(value, PduHeaders.BCC); - } - - /** - * Get CC value. - * - * @return the value - */ - public EncodedStringValue[] getCc() { - return mPduHeaders.getEncodedStringValues(PduHeaders.CC); - } - - /** - * Add a "CC" value. - * - * @param value the value - * @throws NullPointerException if the value is null. - */ - public void addCc(EncodedStringValue value) { - mPduHeaders.appendEncodedStringValue(value, PduHeaders.CC); - } - - /** - * Set "CC" value. - * - * @param value the value - * @throws NullPointerException if the value is null. - */ - public void setCc(EncodedStringValue[] value) { - mPduHeaders.setEncodedStringValues(value, PduHeaders.CC); - } - - /** - * Get Content-type value. - * - * @return the value - */ - public byte[] getContentType() { - return mPduHeaders.getTextString(PduHeaders.CONTENT_TYPE); - } - - /** - * Set Content-type value. - * - * @param value the value - * @throws NullPointerException if the value is null. - */ - public void setContentType(byte[] value) { - mPduHeaders.setTextString(value, PduHeaders.CONTENT_TYPE); - } - - /** - * Get X-Mms-Delivery-Report value. - * - * @return the value - */ - public int getDeliveryReport() { - return mPduHeaders.getOctet(PduHeaders.DELIVERY_REPORT); - } - - /** - * Set X-Mms-Delivery-Report value. - * - * @param value the value - * @throws InvalidHeaderValueException if the value is invalid. - */ - public void setDeliveryReport(int value) throws InvalidHeaderValueException { - mPduHeaders.setOctet(value, PduHeaders.DELIVERY_REPORT); - } - - /** - * Get X-Mms-Expiry value. - * - * Expiry-value = Value-length - * (Absolute-token Date-value | Relative-token Delta-seconds-value) - * - * @return the value - */ - public long getExpiry() { - return mPduHeaders.getLongInteger(PduHeaders.EXPIRY); - } - - /** - * Set X-Mms-Expiry value. - * - * @param value the value - */ - public void setExpiry(long value) { - mPduHeaders.setLongInteger(value, PduHeaders.EXPIRY); - } - - /** - * Get X-Mms-MessageSize value. - * - * Expiry-value = size of message - * - * @return the value - */ - public long getMessageSize() { - return mPduHeaders.getLongInteger(PduHeaders.MESSAGE_SIZE); - } - - /** - * Set X-Mms-MessageSize value. - * - * @param value the value - */ - public void setMessageSize(long value) { - mPduHeaders.setLongInteger(value, PduHeaders.MESSAGE_SIZE); - } - - /** - * Get X-Mms-Message-Class value. - * Message-class-value = Class-identifier | Token-text - * Class-identifier = Personal | Advertisement | Informational | Auto - * - * @return the value - */ - public byte[] getMessageClass() { - return mPduHeaders.getTextString(PduHeaders.MESSAGE_CLASS); - } - - /** - * Set X-Mms-Message-Class value. - * - * @param value the value - * @throws NullPointerException if the value is null. - */ - public void setMessageClass(byte[] value) { - mPduHeaders.setTextString(value, PduHeaders.MESSAGE_CLASS); - } - - /** - * Get X-Mms-Read-Report value. - * - * @return the value - */ - public int getReadReport() { - return mPduHeaders.getOctet(PduHeaders.READ_REPORT); - } - - /** - * Set X-Mms-Read-Report value. - * - * @param value the value - * @throws InvalidHeaderValueException if the value is invalid. - */ - public void setReadReport(int value) throws InvalidHeaderValueException { - mPduHeaders.setOctet(value, PduHeaders.READ_REPORT); - } - - /** - * Set "To" value. - * - * @param value the value - * @throws NullPointerException if the value is null. - */ - public void setTo(EncodedStringValue[] value) { - mPduHeaders.setEncodedStringValues(value, PduHeaders.TO); - } - - /** - * Get X-Mms-Transaction-Id field value. - * - * @return the X-Mms-Report-Allowed value - */ - public byte[] getTransactionId() { - return mPduHeaders.getTextString(PduHeaders.TRANSACTION_ID); - } - - /** - * Set X-Mms-Transaction-Id field value. - * - * @param value the value - * @throws NullPointerException if the value is null. - */ - public void setTransactionId(byte[] value) { - mPduHeaders.setTextString(value, PduHeaders.TRANSACTION_ID); - } - - /* - * Optional, not supported header fields: - * - * public byte getAdaptationAllowed() {return 0}; - * public void setAdaptationAllowed(btye value) {}; - * - * public byte[] getApplicId() {return null;} - * public void setApplicId(byte[] value) {} - * - * public byte[] getAuxApplicId() {return null;} - * public void getAuxApplicId(byte[] value) {} - * - * public byte getContentClass() {return 0x00;} - * public void setApplicId(byte value) {} - * - * public long getDeliveryTime() {return 0}; - * public void setDeliveryTime(long value) {}; - * - * public byte getDrmContent() {return 0x00;} - * public void setDrmContent(byte value) {} - * - * public MmFlagsValue getMmFlags() {return null;} - * public void setMmFlags(MmFlagsValue value) {} - * - * public MmStateValue getMmState() {return null;} - * public void getMmState(MmStateValue value) {} - * - * public byte[] getReplyApplicId() {return 0x00;} - * public void setReplyApplicId(byte[] value) {} - * - * public byte getReplyCharging() {return 0x00;} - * public void setReplyCharging(byte value) {} - * - * public byte getReplyChargingDeadline() {return 0x00;} - * public void setReplyChargingDeadline(byte value) {} - * - * public byte[] getReplyChargingId() {return 0x00;} - * public void setReplyChargingId(byte[] value) {} - * - * public long getReplyChargingSize() {return 0;} - * public void setReplyChargingSize(long value) {} - * - * public byte[] getReplyApplicId() {return 0x00;} - * public void setReplyApplicId(byte[] value) {} - * - * public byte getStore() {return 0x00;} - * public void setStore(byte value) {} - */ -} diff --git a/mms-common/java/com/android/mmscommon/mms/util/AbstractCache.java b/mms-common/java/com/android/mmscommon/mms/util/AbstractCache.java deleted file mode 100644 index 10a6fcedc..000000000 --- a/mms-common/java/com/android/mmscommon/mms/util/AbstractCache.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (C) 2008 Esmertec AG. - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.mmscommon.mms.util; - -import android.util.Config; -import android.util.Log; - -import java.util.HashMap; - -public abstract class AbstractCache { - private static final String TAG = "AbstractCache"; - private static final boolean DEBUG = false; - private static final boolean LOCAL_LOGV = DEBUG ? Config.LOGD : Config.LOGV; - - private static final int MAX_CACHED_ITEMS = 500; - - private final HashMap> mCacheMap; - - protected AbstractCache() { - mCacheMap = new HashMap>(); - } - - public boolean put(K key, V value) { - if (LOCAL_LOGV) { - Log.v(TAG, "Trying to put " + key + " into cache."); - } - - if (mCacheMap.size() >= MAX_CACHED_ITEMS) { - // TODO Should remove the oldest or least hit cached entry - // and then cache the new one. - if (LOCAL_LOGV) { - Log.v(TAG, "Failed! size limitation reached."); - } - return false; - } - - if (key != null) { - CacheEntry cacheEntry = new CacheEntry(); - cacheEntry.value = value; - mCacheMap.put(key, cacheEntry); - - if (LOCAL_LOGV) { - Log.v(TAG, key + " cached, " + mCacheMap.size() + " items total."); - } - return true; - } - return false; - } - - public V get(K key) { - if (LOCAL_LOGV) { - Log.v(TAG, "Trying to get " + key + " from cache."); - } - - if (key != null) { - CacheEntry cacheEntry = mCacheMap.get(key); - if (cacheEntry != null) { - cacheEntry.hit++; - if (LOCAL_LOGV) { - Log.v(TAG, key + " hit " + cacheEntry.hit + " times."); - } - return cacheEntry.value; - } - } - return null; - } - - public V purge(K key) { - if (LOCAL_LOGV) { - Log.v(TAG, "Trying to purge " + key); - } - - CacheEntry v = mCacheMap.remove(key); - - if (LOCAL_LOGV) { - Log.v(TAG, mCacheMap.size() + " items cached."); - } - - return v != null ? v.value : null; - } - - public void purgeAll() { - if (LOCAL_LOGV) { - Log.v(TAG, "Purging cache, " + mCacheMap.size() - + " items dropped."); - } - mCacheMap.clear(); - } - - public int size() { - return mCacheMap.size(); - } - - private static class CacheEntry { - int hit; - V value; - } -} diff --git a/mms-common/java/com/android/mmscommon/mms/util/PduCache.java b/mms-common/java/com/android/mmscommon/mms/util/PduCache.java deleted file mode 100644 index ca5432f07..000000000 --- a/mms-common/java/com/android/mmscommon/mms/util/PduCache.java +++ /dev/null @@ -1,243 +0,0 @@ -/* - * Copyright (C) 2008 Esmertec AG. - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.mmscommon.mms.util; - -import android.content.ContentUris; -import android.content.UriMatcher; -import android.net.Uri; -import com.android.mmscommon.telephony.TelephonyProvider.Mms; -import android.util.Config; -import android.util.Log; - -import java.util.HashMap; -import java.util.HashSet; - -public final class PduCache extends AbstractCache { - private static final String TAG = "PduCache"; - private static final boolean DEBUG = false; - private static final boolean LOCAL_LOGV = DEBUG ? Config.LOGD : Config.LOGV; - - private static final int MMS_ALL = 0; - private static final int MMS_ALL_ID = 1; - private static final int MMS_INBOX = 2; - private static final int MMS_INBOX_ID = 3; - private static final int MMS_SENT = 4; - private static final int MMS_SENT_ID = 5; - private static final int MMS_DRAFTS = 6; - private static final int MMS_DRAFTS_ID = 7; - private static final int MMS_OUTBOX = 8; - private static final int MMS_OUTBOX_ID = 9; - private static final int MMS_CONVERSATION = 10; - private static final int MMS_CONVERSATION_ID = 11; - - private static final UriMatcher URI_MATCHER; - private static final HashMap MATCH_TO_MSGBOX_ID_MAP; - - private static PduCache sInstance; - - static { - URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH); - URI_MATCHER.addURI("mms", null, MMS_ALL); - URI_MATCHER.addURI("mms", "#", MMS_ALL_ID); - URI_MATCHER.addURI("mms", "inbox", MMS_INBOX); - URI_MATCHER.addURI("mms", "inbox/#", MMS_INBOX_ID); - URI_MATCHER.addURI("mms", "sent", MMS_SENT); - URI_MATCHER.addURI("mms", "sent/#", MMS_SENT_ID); - URI_MATCHER.addURI("mms", "drafts", MMS_DRAFTS); - URI_MATCHER.addURI("mms", "drafts/#", MMS_DRAFTS_ID); - URI_MATCHER.addURI("mms", "outbox", MMS_OUTBOX); - URI_MATCHER.addURI("mms", "outbox/#", MMS_OUTBOX_ID); - URI_MATCHER.addURI("mms-sms", "conversations", MMS_CONVERSATION); - URI_MATCHER.addURI("mms-sms", "conversations/#", MMS_CONVERSATION_ID); - - MATCH_TO_MSGBOX_ID_MAP = new HashMap(); - MATCH_TO_MSGBOX_ID_MAP.put(MMS_INBOX, Mms.MESSAGE_BOX_INBOX); - MATCH_TO_MSGBOX_ID_MAP.put(MMS_SENT, Mms.MESSAGE_BOX_SENT); - MATCH_TO_MSGBOX_ID_MAP.put(MMS_DRAFTS, Mms.MESSAGE_BOX_DRAFTS); - MATCH_TO_MSGBOX_ID_MAP.put(MMS_OUTBOX, Mms.MESSAGE_BOX_OUTBOX); - } - - private final HashMap> mMessageBoxes; - private final HashMap> mThreads; - - private PduCache() { - mMessageBoxes = new HashMap>(); - mThreads = new HashMap>(); - } - - synchronized public static final PduCache getInstance() { - if (sInstance == null) { - if (LOCAL_LOGV) { - Log.v(TAG, "Constructing new PduCache instance."); - } - sInstance = new PduCache(); - } - return sInstance; - } - - @Override - synchronized public boolean put(Uri uri, PduCacheEntry entry) { - int msgBoxId = entry.getMessageBox(); - HashSet msgBox = mMessageBoxes.get(msgBoxId); - if (msgBox == null) { - msgBox = new HashSet(); - mMessageBoxes.put(msgBoxId, msgBox); - } - - long threadId = entry.getThreadId(); - HashSet thread = mThreads.get(threadId); - if (thread == null) { - thread = new HashSet(); - mThreads.put(threadId, thread); - } - - Uri finalKey = normalizeKey(uri); - boolean result = super.put(finalKey, entry); - if (result) { - msgBox.add(finalKey); - thread.add(finalKey); - } - return result; - } - - @Override - synchronized public PduCacheEntry purge(Uri uri) { - int match = URI_MATCHER.match(uri); - switch (match) { - case MMS_ALL_ID: - return purgeSingleEntry(uri); - case MMS_INBOX_ID: - case MMS_SENT_ID: - case MMS_DRAFTS_ID: - case MMS_OUTBOX_ID: - String msgId = uri.getLastPathSegment(); - return purgeSingleEntry(Uri.withAppendedPath(Mms.CONTENT_URI, msgId)); - // Implicit batch of purge, return null. - case MMS_ALL: - case MMS_CONVERSATION: - purgeAll(); - return null; - case MMS_INBOX: - case MMS_SENT: - case MMS_DRAFTS: - case MMS_OUTBOX: - purgeByMessageBox(MATCH_TO_MSGBOX_ID_MAP.get(match)); - return null; - case MMS_CONVERSATION_ID: - purgeByThreadId(ContentUris.parseId(uri)); - return null; - default: - return null; - } - } - - private PduCacheEntry purgeSingleEntry(Uri key) { - PduCacheEntry entry = super.purge(key); - if (entry != null) { - removeFromThreads(key, entry); - removeFromMessageBoxes(key, entry); - return entry; - } - return null; - } - - @Override - synchronized public void purgeAll() { - super.purgeAll(); - - mMessageBoxes.clear(); - mThreads.clear(); - } - - /** - * @param uri The Uri to be normalized. - * @return Uri The normalized key of cached entry. - */ - private Uri normalizeKey(Uri uri) { - int match = URI_MATCHER.match(uri); - Uri normalizedKey = null; - - switch (match) { - case MMS_ALL_ID: - normalizedKey = uri; - break; - case MMS_INBOX_ID: - case MMS_SENT_ID: - case MMS_DRAFTS_ID: - case MMS_OUTBOX_ID: - String msgId = uri.getLastPathSegment(); - normalizedKey = Uri.withAppendedPath(Mms.CONTENT_URI, msgId); - break; - default: - return null; - } - - if (LOCAL_LOGV) { - Log.v(TAG, uri + " -> " + normalizedKey); - } - return normalizedKey; - } - - private void purgeByMessageBox(Integer msgBoxId) { - if (LOCAL_LOGV) { - Log.v(TAG, "Purge cache in message box: " + msgBoxId); - } - - if (msgBoxId != null) { - HashSet msgBox = mMessageBoxes.remove(msgBoxId); - if (msgBox != null) { - for (Uri key : msgBox) { - PduCacheEntry entry = super.purge(key); - if (entry != null) { - removeFromThreads(key, entry); - } - } - } - } - } - - private void removeFromThreads(Uri key, PduCacheEntry entry) { - HashSet thread = mThreads.get(entry.getThreadId()); - if (thread != null) { - thread.remove(key); - } - } - - private void purgeByThreadId(long threadId) { - if (LOCAL_LOGV) { - Log.v(TAG, "Purge cache in thread: " + threadId); - } - - HashSet thread = mThreads.remove(threadId); - if (thread != null) { - for (Uri key : thread) { - PduCacheEntry entry = super.purge(key); - if (entry != null) { - removeFromMessageBoxes(key, entry); - } - } - } - } - - private void removeFromMessageBoxes(Uri key, PduCacheEntry entry) { - HashSet msgBox = mThreads.get(entry.getMessageBox()); - if (msgBox != null) { - msgBox.remove(key); - } - } -} diff --git a/mms-common/java/com/android/mmscommon/mms/util/PduCacheEntry.java b/mms-common/java/com/android/mmscommon/mms/util/PduCacheEntry.java deleted file mode 100644 index aed741d2d..000000000 --- a/mms-common/java/com/android/mmscommon/mms/util/PduCacheEntry.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2008 Esmertec AG. - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.mmscommon.mms.util; - -import com.android.mmscommon.mms.pdu.GenericPdu; - -public final class PduCacheEntry { - private final GenericPdu mPdu; - private final int mMessageBox; - private final long mThreadId; - - public PduCacheEntry(GenericPdu pdu, int msgBox, long threadId) { - mPdu = pdu; - mMessageBox = msgBox; - mThreadId = threadId; - } - - public GenericPdu getPdu() { - return mPdu; - } - - public int getMessageBox() { - return mMessageBox; - } - - public long getThreadId() { - return mThreadId; - } -} diff --git a/mms-common/java/com/android/mmscommon/telephony/TelephonyProvider.java b/mms-common/java/com/android/mmscommon/telephony/TelephonyProvider.java deleted file mode 100644 index cfc9231f0..000000000 --- a/mms-common/java/com/android/mmscommon/telephony/TelephonyProvider.java +++ /dev/null @@ -1,1779 +0,0 @@ -/* - * Copyright (C) 2006 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.mmscommon.telephony; - -import java.util.HashSet; -import java.util.Set; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import android.content.ContentResolver; -import android.content.ContentValues; -import android.content.Context; -import android.content.Intent; -import android.database.Cursor; -import android.net.Uri; -import android.provider.BaseColumns; -import android.telephony.SmsMessage; -import android.text.TextUtils; -import android.util.Config; -import android.util.Log; -import android.util.Patterns; - -import android.database.sqlite.SqliteWrapper; - -/** - * The Telephony provider contains data related to phone operation. - * - * @hide - */ - -// This is a copy of the private TelephoneProvider.java file found in: -// com.android.providers.telephony -// TODO: keep these files in sync. - -public final class TelephonyProvider { - private static final String TAG = "Telephony"; - private static final boolean DEBUG = true; - private static final boolean LOCAL_LOGV = DEBUG ? Config.LOGD : Config.LOGV; - - // Constructor - public TelephonyProvider() { - } - - /** - * Base columns for tables that contain text based SMSs. - */ - public interface TextBasedSmsColumns { - /** - * The type of the message - *

Type: INTEGER

- */ - public static final String TYPE = "type"; - - public static final int MESSAGE_TYPE_ALL = 0; - public static final int MESSAGE_TYPE_INBOX = 1; - public static final int MESSAGE_TYPE_SENT = 2; - public static final int MESSAGE_TYPE_DRAFT = 3; - public static final int MESSAGE_TYPE_OUTBOX = 4; - public static final int MESSAGE_TYPE_FAILED = 5; // for failed outgoing messages - public static final int MESSAGE_TYPE_QUEUED = 6; // for messages to send later - - - /** - * The thread ID of the message - *

Type: INTEGER

- */ - public static final String THREAD_ID = "thread_id"; - - /** - * The address of the other party - *

Type: TEXT

- */ - public static final String ADDRESS = "address"; - - /** - * The person ID of the sender - *

Type: INTEGER (long)

- */ - public static final String PERSON_ID = "person"; - - /** - * The date the message was sent - *

Type: INTEGER (long)

- */ - public static final String DATE = "date"; - - /** - * Has the message been read - *

Type: INTEGER (boolean)

- */ - public static final String READ = "read"; - - /** - * Indicates whether this message has been seen by the user. The "seen" flag will be - * used to figure out whether we need to throw up a statusbar notification or not. - */ - public static final String SEEN = "seen"; - - /** - * The TP-Status value for the message, or -1 if no status has - * been received - */ - public static final String STATUS = "status"; - - public static final int STATUS_NONE = -1; - public static final int STATUS_COMPLETE = 0; - public static final int STATUS_PENDING = 64; - public static final int STATUS_FAILED = 128; - - /** - * The subject of the message, if present - *

Type: TEXT

- */ - public static final String SUBJECT = "subject"; - - /** - * The body of the message - *

Type: TEXT

- */ - public static final String BODY = "body"; - - /** - * The id of the sender of the conversation, if present - *

Type: INTEGER (reference to item in content://contacts/people)

- */ - public static final String PERSON = "person"; - - /** - * The protocol identifier code - *

Type: INTEGER

- */ - public static final String PROTOCOL = "protocol"; - - /** - * Whether the TP-Reply-Path bit was set on this message - *

Type: BOOLEAN

- */ - public static final String REPLY_PATH_PRESENT = "reply_path_present"; - - /** - * The service center (SC) through which to send the message, if present - *

Type: TEXT

- */ - public static final String SERVICE_CENTER = "service_center"; - - /** - * Has the message been locked? - *

Type: INTEGER (boolean)

- */ - public static final String LOCKED = "locked"; - - /** - * Error code associated with sending or receiving this message - *

Type: INTEGER

- */ - public static final String ERROR_CODE = "error_code"; -} - - /** - * Contains all text based SMS messages. - */ - public static final class Sms implements BaseColumns, TextBasedSmsColumns { - public static final Cursor query(ContentResolver cr, String[] projection) { - return cr.query(CONTENT_URI, projection, null, null, DEFAULT_SORT_ORDER); - } - - public static final Cursor query(ContentResolver cr, String[] projection, - String where, String orderBy) { - return cr.query(CONTENT_URI, projection, where, - null, orderBy == null ? DEFAULT_SORT_ORDER : orderBy); - } - - /** - * The content:// style URL for this table - */ - public static final Uri CONTENT_URI = - Uri.parse("content://sms"); - - /** - * The default sort order for this table - */ - public static final String DEFAULT_SORT_ORDER = "date DESC"; - - /** - * Add an SMS to the given URI. - * - * @param resolver the content resolver to use - * @param uri the URI to add the message to - * @param address the address of the sender - * @param body the body of the message - * @param subject the psuedo-subject of the message - * @param date the timestamp for the message - * @param read true if the message has been read, false if not - * @param deliveryReport true if a delivery report was requested, false if not - * @return the URI for the new message - */ - public static Uri addMessageToUri(ContentResolver resolver, - Uri uri, String address, String body, String subject, - Long date, boolean read, boolean deliveryReport) { - return addMessageToUri(resolver, uri, address, body, subject, - date, read, deliveryReport, -1L); - } - - /** - * Add an SMS to the given URI with thread_id specified. - * - * @param resolver the content resolver to use - * @param uri the URI to add the message to - * @param address the address of the sender - * @param body the body of the message - * @param subject the psuedo-subject of the message - * @param date the timestamp for the message - * @param read true if the message has been read, false if not - * @param deliveryReport true if a delivery report was requested, false if not - * @param threadId the thread_id of the message - * @return the URI for the new message - */ - public static Uri addMessageToUri(ContentResolver resolver, - Uri uri, String address, String body, String subject, - Long date, boolean read, boolean deliveryReport, long threadId) { - ContentValues values = new ContentValues(7); - - values.put(ADDRESS, address); - if (date != null) { - values.put(DATE, date); - } - values.put(READ, read ? Integer.valueOf(1) : Integer.valueOf(0)); - values.put(SUBJECT, subject); - values.put(BODY, body); - if (deliveryReport) { - values.put(STATUS, STATUS_PENDING); - } - if (threadId != -1L) { - values.put(THREAD_ID, threadId); - } - return resolver.insert(uri, values); - } - - /** - * Move a message to the given folder. - * - * @param context the context to use - * @param uri the message to move - * @param folder the folder to move to - * @return true if the operation succeeded - */ - public static boolean moveMessageToFolder(Context context, - Uri uri, int folder, int error) { - if (uri == null) { - return false; - } - - boolean markAsUnread = false; - boolean markAsRead = false; - switch(folder) { - case MESSAGE_TYPE_INBOX: - case MESSAGE_TYPE_DRAFT: - break; - case MESSAGE_TYPE_OUTBOX: - case MESSAGE_TYPE_SENT: - markAsRead = true; - break; - case MESSAGE_TYPE_FAILED: - case MESSAGE_TYPE_QUEUED: - markAsUnread = true; - break; - default: - return false; - } - - ContentValues values = new ContentValues(3); - - values.put(TYPE, folder); - if (markAsUnread) { - values.put(READ, Integer.valueOf(0)); - } else if (markAsRead) { - values.put(READ, Integer.valueOf(1)); - } - values.put(ERROR_CODE, error); - - return 1 == SqliteWrapper.update(context, context.getContentResolver(), - uri, values, null, null); - } - - /** - * Returns true iff the folder (message type) identifies an - * outgoing message. - */ - public static boolean isOutgoingFolder(int messageType) { - return (messageType == MESSAGE_TYPE_FAILED) - || (messageType == MESSAGE_TYPE_OUTBOX) - || (messageType == MESSAGE_TYPE_SENT) - || (messageType == MESSAGE_TYPE_QUEUED); - } - - /** - * Contains all text based SMS messages in the SMS app's inbox. - */ - public static final class Inbox implements BaseColumns, TextBasedSmsColumns { - /** - * The content:// style URL for this table - */ - public static final Uri CONTENT_URI = - Uri.parse("content://sms/inbox"); - - /** - * The default sort order for this table - */ - public static final String DEFAULT_SORT_ORDER = "date DESC"; - - /** - * Add an SMS to the Draft box. - * - * @param resolver the content resolver to use - * @param address the address of the sender - * @param body the body of the message - * @param subject the psuedo-subject of the message - * @param date the timestamp for the message - * @param read true if the message has been read, false if not - * @return the URI for the new message - */ - public static Uri addMessage(ContentResolver resolver, - String address, String body, String subject, Long date, - boolean read) { - return addMessageToUri(resolver, CONTENT_URI, address, body, - subject, date, read, false); - } - } - - /** - * Contains all sent text based SMS messages in the SMS app's. - */ - public static final class Sent implements BaseColumns, TextBasedSmsColumns { - /** - * The content:// style URL for this table - */ - public static final Uri CONTENT_URI = - Uri.parse("content://sms/sent"); - - /** - * The default sort order for this table - */ - public static final String DEFAULT_SORT_ORDER = "date DESC"; - - /** - * Add an SMS to the Draft box. - * - * @param resolver the content resolver to use - * @param address the address of the sender - * @param body the body of the message - * @param subject the psuedo-subject of the message - * @param date the timestamp for the message - * @return the URI for the new message - */ - public static Uri addMessage(ContentResolver resolver, - String address, String body, String subject, Long date) { - return addMessageToUri(resolver, CONTENT_URI, address, body, - subject, date, true, false); - } - } - - /** - * Contains all sent text based SMS messages in the SMS app's. - */ - public static final class Draft implements BaseColumns, TextBasedSmsColumns { - /** - * The content:// style URL for this table - */ - public static final Uri CONTENT_URI = - Uri.parse("content://sms/draft"); - - /** - * The default sort order for this table - */ - public static final String DEFAULT_SORT_ORDER = "date DESC"; - - /** - * Add an SMS to the Draft box. - * - * @param resolver the content resolver to use - * @param address the address of the sender - * @param body the body of the message - * @param subject the psuedo-subject of the message - * @param date the timestamp for the message - * @return the URI for the new message - */ - public static Uri addMessage(ContentResolver resolver, - String address, String body, String subject, Long date) { - return addMessageToUri(resolver, CONTENT_URI, address, body, - subject, date, true, false); - } - - /** - * Save over an existing draft message. - * - * @param resolver the content resolver to use - * @param uri of existing message - * @param body the new body for the draft message - * @return true is successful, false otherwise - */ - public static boolean saveMessage(ContentResolver resolver, - Uri uri, String body) { - ContentValues values = new ContentValues(2); - values.put(BODY, body); - values.put(DATE, System.currentTimeMillis()); - return resolver.update(uri, values, null, null) == 1; - } - } - - /** - * Contains all pending outgoing text based SMS messages. - */ - public static final class Outbox implements BaseColumns, TextBasedSmsColumns { - /** - * The content:// style URL for this table - */ - public static final Uri CONTENT_URI = - Uri.parse("content://sms/outbox"); - - /** - * The default sort order for this table - */ - public static final String DEFAULT_SORT_ORDER = "date DESC"; - - /** - * Add an SMS to the Out box. - * - * @param resolver the content resolver to use - * @param address the address of the sender - * @param body the body of the message - * @param subject the psuedo-subject of the message - * @param date the timestamp for the message - * @param deliveryReport whether a delivery report was requested for the message - * @return the URI for the new message - */ - public static Uri addMessage(ContentResolver resolver, - String address, String body, String subject, Long date, - boolean deliveryReport, long threadId) { - return addMessageToUri(resolver, CONTENT_URI, address, body, - subject, date, true, deliveryReport, threadId); - } - } - - /** - * Contains all sent text-based SMS messages in the SMS app's. - */ - public static final class Conversations - implements BaseColumns, TextBasedSmsColumns { - /** - * The content:// style URL for this table - */ - public static final Uri CONTENT_URI = - Uri.parse("content://sms/conversations"); - - /** - * The default sort order for this table - */ - public static final String DEFAULT_SORT_ORDER = "date DESC"; - - /** - * The first 45 characters of the body of the message - *

Type: TEXT

- */ - public static final String SNIPPET = "snippet"; - - /** - * The number of messages in the conversation - *

Type: INTEGER

- */ - public static final String MESSAGE_COUNT = "msg_count"; - } - - /** - * Contains info about SMS related Intents that are broadcast. - */ - public static final class Intents { - /** - * Set by BroadcastReceiver. Indicates the message was handled - * successfully. - */ - public static final int RESULT_SMS_HANDLED = 1; - - /** - * Set by BroadcastReceiver. Indicates a generic error while - * processing the message. - */ - public static final int RESULT_SMS_GENERIC_ERROR = 2; - - /** - * Set by BroadcastReceiver. Indicates insufficient memory to store - * the message. - */ - public static final int RESULT_SMS_OUT_OF_MEMORY = 3; - - /** - * Set by BroadcastReceiver. Indicates the message, while - * possibly valid, is of a format or encoding that is not - * supported. - */ - public static final int RESULT_SMS_UNSUPPORTED = 4; - - /** - * Broadcast Action: A new text based SMS message has been received - * by the device. The intent will have the following extra - * values:

- * - *
    - *
  • pdus - An Object[] od byte[]s containing the PDUs - * that make up the message.
  • - *
- * - *

The extra values can be extracted using - * {@link #getMessagesFromIntent(Intent)}.

- * - *

If a BroadcastReceiver encounters an error while processing - * this intent it should set the result code appropriately.

- */ - public static final String SMS_RECEIVED_ACTION = - "android.provider.Telephony.SMS_RECEIVED"; - - /** - * Broadcast Action: A new data based SMS message has been received - * by the device. The intent will have the following extra - * values:

- * - *
    - *
  • pdus - An Object[] od byte[]s containing the PDUs - * that make up the message.
  • - *
- * - *

The extra values can be extracted using - * {@link #getMessagesFromIntent(Intent)}.

- * - *

If a BroadcastReceiver encounters an error while processing - * this intent it should set the result code appropriately.

- */ - public static final String DATA_SMS_RECEIVED_ACTION = - "android.intent.action.DATA_SMS_RECEIVED"; - - /** - * Broadcast Action: A new WAP PUSH message has been received by the - * device. The intent will have the following extra - * values:

- * - *
    - *
  • transactionId (Integer) - The WAP transaction - * ID
  • - *
  • pduType (Integer) - The WAP PDU type
  • - *
  • header (byte[]) - The header of the message
  • - *
  • data (byte[]) - The data payload of the message
  • - *
- * - *

If a BroadcastReceiver encounters an error while processing - * this intent it should set the result code appropriately.

- */ - public static final String WAP_PUSH_RECEIVED_ACTION = - "android.provider.Telephony.WAP_PUSH_RECEIVED"; - - /** - * Broadcast Action: The SIM storage for SMS messages is full. If - * space is not freed, messages targeted for the SIM (class 2) may - * not be saved. - */ - public static final String SIM_FULL_ACTION = - "android.provider.Telephony.SIM_FULL"; - - /** - * Broadcast Action: An incoming SMS has been rejected by the - * telephony framework. This intent is sent in lieu of any - * of the RECEIVED_ACTION intents. The intent will have the - * following extra value:

- * - *
    - *
  • result - An int result code, eg, - * {@link #RESULT_SMS_OUT_OF_MEMORY}, - * indicating the error returned to the network.
  • - *
- - */ - public static final String SMS_REJECTED_ACTION = - "android.provider.Telephony.SMS_REJECTED"; - - /** - * Broadcast Action: The phone service state has changed. The intent will have the following - * extra values:

- *
    - *
  • state - An int with one of the following values: - * {@link android.telephony.ServiceState#STATE_IN_SERVICE}, - * {@link android.telephony.ServiceState#STATE_OUT_OF_SERVICE}, - * {@link android.telephony.ServiceState#STATE_EMERGENCY_ONLY} - * or {@link android.telephony.ServiceState#STATE_POWER_OFF} - *
  • roaming - A boolean value indicating whether the phone is roaming.
  • - *
  • operator-alpha-long - The carrier name as a string.
  • - *
  • operator-alpha-short - A potentially shortened version of the carrier name, - * as a string.
  • - *
  • operator-numeric - A number representing the carrier, as a string. This is - * a five or six digit number consisting of the MCC (Mobile Country Code, 3 digits) - * and MNC (Mobile Network code, 2-3 digits).
  • - *
  • manual - A boolean, where true indicates that the user has chosen to select - * the network manually, and false indicates that network selection is handled by the - * phone.
  • - *
- * - *

- * Requires the READ_PHONE_STATE permission. - * - *

This is a protected intent that can only be sent - * by the system. - */ - public static final String ACTION_SERVICE_STATE_CHANGED = - "android.intent.action.SERVICE_STATE"; - - /** - * Read the PDUs out of an {@link #SMS_RECEIVED_ACTION} or a - * {@link #DATA_SMS_RECEIVED_ACTION} intent. - * - * @param intent the intent to read from - * @return an array of SmsMessages for the PDUs - */ - public static final SmsMessage[] getMessagesFromIntent( - Intent intent) { - Object[] messages = (Object[]) intent.getSerializableExtra("pdus"); - byte[][] pduObjs = new byte[messages.length][]; - - for (int i = 0; i < messages.length; i++) { - pduObjs[i] = (byte[]) messages[i]; - } - byte[][] pdus = new byte[pduObjs.length][]; - int pduCount = pdus.length; - SmsMessage[] msgs = new SmsMessage[pduCount]; - for (int i = 0; i < pduCount; i++) { - pdus[i] = pduObjs[i]; - msgs[i] = SmsMessage.createFromPdu(pdus[i]); - } - return msgs; - } - } - } - - /** - * Base columns for tables that contain MMSs. - */ - public interface BaseMmsColumns extends BaseColumns { - - public static final int MESSAGE_BOX_ALL = 0; - public static final int MESSAGE_BOX_INBOX = 1; - public static final int MESSAGE_BOX_SENT = 2; - public static final int MESSAGE_BOX_DRAFTS = 3; - public static final int MESSAGE_BOX_OUTBOX = 4; - - /** - * The date the message was sent. - *

Type: INTEGER (long)

- */ - public static final String DATE = "date"; - - /** - * The box which the message belong to, for example, MESSAGE_BOX_INBOX. - *

Type: INTEGER

- */ - public static final String MESSAGE_BOX = "msg_box"; - - /** - * Has the message been read. - *

Type: INTEGER (boolean)

- */ - public static final String READ = "read"; - - /** - * Indicates whether this message has been seen by the user. The "seen" flag will be - * used to figure out whether we need to throw up a statusbar notification or not. - */ - public static final String SEEN = "seen"; - - /** - * The Message-ID of the message. - *

Type: TEXT

- */ - public static final String MESSAGE_ID = "m_id"; - - /** - * The subject of the message, if present. - *

Type: TEXT

- */ - public static final String SUBJECT = "sub"; - - /** - * The character set of the subject, if present. - *

Type: INTEGER

- */ - public static final String SUBJECT_CHARSET = "sub_cs"; - - /** - * The Content-Type of the message. - *

Type: TEXT

- */ - public static final String CONTENT_TYPE = "ct_t"; - - /** - * The Content-Location of the message. - *

Type: TEXT

- */ - public static final String CONTENT_LOCATION = "ct_l"; - - /** - * The address of the sender. - *

Type: TEXT

- */ - public static final String FROM = "from"; - - /** - * The address of the recipients. - *

Type: TEXT

- */ - public static final String TO = "to"; - - /** - * The address of the cc. recipients. - *

Type: TEXT

- */ - public static final String CC = "cc"; - - /** - * The address of the bcc. recipients. - *

Type: TEXT

- */ - public static final String BCC = "bcc"; - - /** - * The expiry time of the message. - *

Type: INTEGER

- */ - public static final String EXPIRY = "exp"; - - /** - * The class of the message. - *

Type: TEXT

- */ - public static final String MESSAGE_CLASS = "m_cls"; - - /** - * The type of the message defined by MMS spec. - *

Type: INTEGER

- */ - public static final String MESSAGE_TYPE = "m_type"; - - /** - * The version of specification that this message conform. - *

Type: INTEGER

- */ - public static final String MMS_VERSION = "v"; - - /** - * The size of the message. - *

Type: INTEGER

- */ - public static final String MESSAGE_SIZE = "m_size"; - - /** - * The priority of the message. - *

Type: TEXT

- */ - public static final String PRIORITY = "pri"; - - /** - * The read-report of the message. - *

Type: TEXT

- */ - public static final String READ_REPORT = "rr"; - - /** - * Whether the report is allowed. - *

Type: TEXT

- */ - public static final String REPORT_ALLOWED = "rpt_a"; - - /** - * The response-status of the message. - *

Type: INTEGER

- */ - public static final String RESPONSE_STATUS = "resp_st"; - - /** - * The status of the message. - *

Type: INTEGER

- */ - public static final String STATUS = "st"; - - /** - * The transaction-id of the message. - *

Type: TEXT

- */ - public static final String TRANSACTION_ID = "tr_id"; - - /** - * The retrieve-status of the message. - *

Type: INTEGER

- */ - public static final String RETRIEVE_STATUS = "retr_st"; - - /** - * The retrieve-text of the message. - *

Type: TEXT

- */ - public static final String RETRIEVE_TEXT = "retr_txt"; - - /** - * The character set of the retrieve-text. - *

Type: TEXT

- */ - public static final String RETRIEVE_TEXT_CHARSET = "retr_txt_cs"; - - /** - * The read-status of the message. - *

Type: INTEGER

- */ - public static final String READ_STATUS = "read_status"; - - /** - * The content-class of the message. - *

Type: INTEGER

- */ - public static final String CONTENT_CLASS = "ct_cls"; - - /** - * The delivery-report of the message. - *

Type: INTEGER

- */ - public static final String DELIVERY_REPORT = "d_rpt"; - - /** - * The delivery-time-token of the message. - *

Type: INTEGER

- */ - public static final String DELIVERY_TIME_TOKEN = "d_tm_tok"; - - /** - * The delivery-time of the message. - *

Type: INTEGER

- */ - public static final String DELIVERY_TIME = "d_tm"; - - /** - * The response-text of the message. - *

Type: TEXT

- */ - public static final String RESPONSE_TEXT = "resp_txt"; - - /** - * The sender-visibility of the message. - *

Type: TEXT

- */ - public static final String SENDER_VISIBILITY = "s_vis"; - - /** - * The reply-charging of the message. - *

Type: INTEGER

- */ - public static final String REPLY_CHARGING = "r_chg"; - - /** - * The reply-charging-deadline-token of the message. - *

Type: INTEGER

- */ - public static final String REPLY_CHARGING_DEADLINE_TOKEN = "r_chg_dl_tok"; - - /** - * The reply-charging-deadline of the message. - *

Type: INTEGER

- */ - public static final String REPLY_CHARGING_DEADLINE = "r_chg_dl"; - - /** - * The reply-charging-id of the message. - *

Type: TEXT

- */ - public static final String REPLY_CHARGING_ID = "r_chg_id"; - - /** - * The reply-charging-size of the message. - *

Type: INTEGER

- */ - public static final String REPLY_CHARGING_SIZE = "r_chg_sz"; - - /** - * The previously-sent-by of the message. - *

Type: TEXT

- */ - public static final String PREVIOUSLY_SENT_BY = "p_s_by"; - - /** - * The previously-sent-date of the message. - *

Type: INTEGER

- */ - public static final String PREVIOUSLY_SENT_DATE = "p_s_d"; - - /** - * The store of the message. - *

Type: TEXT

- */ - public static final String STORE = "store"; - - /** - * The mm-state of the message. - *

Type: INTEGER

- */ - public static final String MM_STATE = "mm_st"; - - /** - * The mm-flags-token of the message. - *

Type: INTEGER

- */ - public static final String MM_FLAGS_TOKEN = "mm_flg_tok"; - - /** - * The mm-flags of the message. - *

Type: TEXT

- */ - public static final String MM_FLAGS = "mm_flg"; - - /** - * The store-status of the message. - *

Type: TEXT

- */ - public static final String STORE_STATUS = "store_st"; - - /** - * The store-status-text of the message. - *

Type: TEXT

- */ - public static final String STORE_STATUS_TEXT = "store_st_txt"; - - /** - * The stored of the message. - *

Type: TEXT

- */ - public static final String STORED = "stored"; - - /** - * The totals of the message. - *

Type: TEXT

- */ - public static final String TOTALS = "totals"; - - /** - * The mbox-totals of the message. - *

Type: TEXT

- */ - public static final String MBOX_TOTALS = "mb_t"; - - /** - * The mbox-totals-token of the message. - *

Type: INTEGER

- */ - public static final String MBOX_TOTALS_TOKEN = "mb_t_tok"; - - /** - * The quotas of the message. - *

Type: TEXT

- */ - public static final String QUOTAS = "qt"; - - /** - * The mbox-quotas of the message. - *

Type: TEXT

- */ - public static final String MBOX_QUOTAS = "mb_qt"; - - /** - * The mbox-quotas-token of the message. - *

Type: INTEGER

- */ - public static final String MBOX_QUOTAS_TOKEN = "mb_qt_tok"; - - /** - * The message-count of the message. - *

Type: INTEGER

- */ - public static final String MESSAGE_COUNT = "m_cnt"; - - /** - * The start of the message. - *

Type: INTEGER

- */ - public static final String START = "start"; - - /** - * The distribution-indicator of the message. - *

Type: TEXT

- */ - public static final String DISTRIBUTION_INDICATOR = "d_ind"; - - /** - * The element-descriptor of the message. - *

Type: TEXT

- */ - public static final String ELEMENT_DESCRIPTOR = "e_des"; - - /** - * The limit of the message. - *

Type: INTEGER

- */ - public static final String LIMIT = "limit"; - - /** - * The recommended-retrieval-mode of the message. - *

Type: INTEGER

- */ - public static final String RECOMMENDED_RETRIEVAL_MODE = "r_r_mod"; - - /** - * The recommended-retrieval-mode-text of the message. - *

Type: TEXT

- */ - public static final String RECOMMENDED_RETRIEVAL_MODE_TEXT = "r_r_mod_txt"; - - /** - * The status-text of the message. - *

Type: TEXT

- */ - public static final String STATUS_TEXT = "st_txt"; - - /** - * The applic-id of the message. - *

Type: TEXT

- */ - public static final String APPLIC_ID = "apl_id"; - - /** - * The reply-applic-id of the message. - *

Type: TEXT

- */ - public static final String REPLY_APPLIC_ID = "r_apl_id"; - - /** - * The aux-applic-id of the message. - *

Type: TEXT

- */ - public static final String AUX_APPLIC_ID = "aux_apl_id"; - - /** - * The drm-content of the message. - *

Type: TEXT

- */ - public static final String DRM_CONTENT = "drm_c"; - - /** - * The adaptation-allowed of the message. - *

Type: TEXT

- */ - public static final String ADAPTATION_ALLOWED = "adp_a"; - - /** - * The replace-id of the message. - *

Type: TEXT

- */ - public static final String REPLACE_ID = "repl_id"; - - /** - * The cancel-id of the message. - *

Type: TEXT

- */ - public static final String CANCEL_ID = "cl_id"; - - /** - * The cancel-status of the message. - *

Type: INTEGER

- */ - public static final String CANCEL_STATUS = "cl_st"; - - /** - * The thread ID of the message - *

Type: INTEGER

- */ - public static final String THREAD_ID = "thread_id"; - - /** - * Has the message been locked? - *

Type: INTEGER (boolean)

- */ - public static final String LOCKED = "locked"; - } - - /** - * Columns for the "canonical_addresses" table used by MMS and - * SMS." - */ - public interface CanonicalAddressesColumns extends BaseColumns { - /** - * An address used in MMS or SMS. Email addresses are - * converted to lower case and are compared by string - * equality. Other addresses are compared using - * PHONE_NUMBERS_EQUAL. - *

Type: TEXT

- */ - public static final String ADDRESS = "address"; - } - - /** - * Columns for the "threads" table used by MMS and SMS. - */ - public interface ThreadsColumns extends BaseColumns { - /** - * The date at which the thread was created. - * - *

Type: INTEGER (long)

- */ - public static final String DATE = "date"; - - /** - * A string encoding of the recipient IDs of the recipients of - * the message, in numerical order and separated by spaces. - *

Type: TEXT

- */ - public static final String RECIPIENT_IDS = "recipient_ids"; - - /** - * The message count of the thread. - *

Type: INTEGER

- */ - public static final String MESSAGE_COUNT = "message_count"; - /** - * Indicates whether all messages of the thread have been read. - *

Type: INTEGER

- */ - public static final String READ = "read"; - - /** - * The snippet of the latest message in the thread. - *

Type: TEXT

- */ - public static final String SNIPPET = "snippet"; - /** - * The charset of the snippet. - *

Type: INTEGER

- */ - public static final String SNIPPET_CHARSET = "snippet_cs"; - /** - * Type of the thread, either Threads.COMMON_THREAD or - * Threads.BROADCAST_THREAD. - *

Type: INTEGER

- */ - public static final String TYPE = "type"; - /** - * Indicates whether there is a transmission error in the thread. - *

Type: INTEGER

- */ - public static final String ERROR = "error"; - /** - * Indicates whether this thread contains any attachments. - *

Type: INTEGER

- */ - public static final String HAS_ATTACHMENT = "has_attachment"; - } - - /** - * Helper functions for the "threads" table used by MMS and SMS. - */ - public static final class Threads implements ThreadsColumns { - private static final String[] ID_PROJECTION = { BaseColumns._ID }; - private static final String STANDARD_ENCODING = "UTF-8"; - private static final Uri THREAD_ID_CONTENT_URI = Uri.parse( - "content://mms-sms/threadID"); - public static final Uri CONTENT_URI = Uri.withAppendedPath( - MmsSms.CONTENT_URI, "conversations"); - public static final Uri OBSOLETE_THREADS_URI = Uri.withAppendedPath( - CONTENT_URI, "obsolete"); - - public static final int COMMON_THREAD = 0; - public static final int BROADCAST_THREAD = 1; - - // No one should construct an instance of this class. - private Threads() { - } - - /** - * This is a single-recipient version of - * getOrCreateThreadId. It's convenient for use with SMS - * messages. - */ - public static long getOrCreateThreadId(Context context, String recipient) { - Set recipients = new HashSet(); - - recipients.add(recipient); - return getOrCreateThreadId(context, recipients); - } - - /** - * Given the recipients list and subject of an unsaved message, - * return its thread ID. If the message starts a new thread, - * allocate a new thread ID. Otherwise, use the appropriate - * existing thread ID. - * - * Find the thread ID of the same set of recipients (in - * any order, without any additions). If one - * is found, return it. Otherwise, return a unique thread ID. - */ - public static long getOrCreateThreadId( - Context context, Set recipients) { - Uri.Builder uriBuilder = THREAD_ID_CONTENT_URI.buildUpon(); - - for (String recipient : recipients) { - if (Mms.isEmailAddress(recipient)) { - recipient = Mms.extractAddrSpec(recipient); - } - - uriBuilder.appendQueryParameter("recipient", recipient); - } - - Uri uri = uriBuilder.build(); - if (DEBUG) { - Log.v(TAG, "getOrCreateThreadId uri: " + uri); - } - Cursor cursor = SqliteWrapper.query(context, context.getContentResolver(), - uri, ID_PROJECTION, null, null, null); - if (DEBUG) { - Log.v(TAG, "getOrCreateThreadId cursor cnt: " + cursor.getCount()); - } - if (cursor != null) { - try { - if (cursor.moveToFirst()) { - return cursor.getLong(0); - } else { - Log.e(TAG, "getOrCreateThreadId returned no rows!"); - } - } finally { - cursor.close(); - } - } - - Log.e(TAG, "getOrCreateThreadId failed with uri " + uri.toString()); - throw new IllegalArgumentException("Unable to find or allocate a thread ID."); - } - } - - /** - * Contains all MMS messages. - */ - public static final class Mms implements BaseMmsColumns { - /** - * The content:// style URL for this table - */ - public static final Uri CONTENT_URI = Uri.parse("content://mms"); - - public static final Uri REPORT_REQUEST_URI = Uri.withAppendedPath( - CONTENT_URI, "report-request"); - - public static final Uri REPORT_STATUS_URI = Uri.withAppendedPath( - CONTENT_URI, "report-status"); - - /** - * The default sort order for this table - */ - public static final String DEFAULT_SORT_ORDER = "date DESC"; - - /** - * mailbox = name-addr - * name-addr = [display-name] angle-addr - * angle-addr = [CFWS] "<" addr-spec ">" [CFWS] - */ - public static final Pattern NAME_ADDR_EMAIL_PATTERN = - Pattern.compile("\\s*(\"[^\"]*\"|[^<>\"]+)\\s*<([^<>]+)>\\s*"); - - /** - * quoted-string = [CFWS] - * DQUOTE *([FWS] qcontent) [FWS] DQUOTE - * [CFWS] - */ - public static final Pattern QUOTED_STRING_PATTERN = - Pattern.compile("\\s*\"([^\"]*)\"\\s*"); - - public static final Cursor query( - ContentResolver cr, String[] projection) { - return cr.query(CONTENT_URI, projection, null, null, DEFAULT_SORT_ORDER); - } - - public static final Cursor query( - ContentResolver cr, String[] projection, - String where, String orderBy) { - return cr.query(CONTENT_URI, projection, - where, null, orderBy == null ? DEFAULT_SORT_ORDER : orderBy); - } - - public static final String getMessageBoxName(int msgBox) { - switch (msgBox) { - case MESSAGE_BOX_ALL: - return "all"; - case MESSAGE_BOX_INBOX: - return "inbox"; - case MESSAGE_BOX_SENT: - return "sent"; - case MESSAGE_BOX_DRAFTS: - return "drafts"; - case MESSAGE_BOX_OUTBOX: - return "outbox"; - default: - throw new IllegalArgumentException("Invalid message box: " + msgBox); - } - } - - public static String extractAddrSpec(String address) { - Matcher match = NAME_ADDR_EMAIL_PATTERN.matcher(address); - - if (match.matches()) { - return match.group(2); - } - return address; - } - - /** - * Returns true if the address is an email address - * - * @param address the input address to be tested - * @return true if address is an email address - */ - public static boolean isEmailAddress(String address) { - if (TextUtils.isEmpty(address)) { - return false; - } - - String s = extractAddrSpec(address); - Matcher match = Patterns.EMAIL_ADDRESS.matcher(s); - return match.matches(); - } - - /** - * Returns true if the number is a Phone number - * - * @param number the input number to be tested - * @return true if number is a Phone number - */ - public static boolean isPhoneNumber(String number) { - if (TextUtils.isEmpty(number)) { - return false; - } - - Matcher match = Patterns.PHONE.matcher(number); - return match.matches(); - } - - /** - * Contains all MMS messages in the MMS app's inbox. - */ - public static final class Inbox implements BaseMmsColumns { - /** - * The content:// style URL for this table - */ - public static final Uri - CONTENT_URI = Uri.parse("content://mms/inbox"); - - /** - * The default sort order for this table - */ - public static final String DEFAULT_SORT_ORDER = "date DESC"; - } - - /** - * Contains all MMS messages in the MMS app's sent box. - */ - public static final class Sent implements BaseMmsColumns { - /** - * The content:// style URL for this table - */ - public static final Uri - CONTENT_URI = Uri.parse("content://mms/sent"); - - /** - * The default sort order for this table - */ - public static final String DEFAULT_SORT_ORDER = "date DESC"; - } - - /** - * Contains all MMS messages in the MMS app's drafts box. - */ - public static final class Draft implements BaseMmsColumns { - /** - * The content:// style URL for this table - */ - public static final Uri - CONTENT_URI = Uri.parse("content://mms/drafts"); - - /** - * The default sort order for this table - */ - public static final String DEFAULT_SORT_ORDER = "date DESC"; - } - - /** - * Contains all MMS messages in the MMS app's outbox. - */ - public static final class Outbox implements BaseMmsColumns { - /** - * The content:// style URL for this table - */ - public static final Uri - CONTENT_URI = Uri.parse("content://mms/outbox"); - - /** - * The default sort order for this table - */ - public static final String DEFAULT_SORT_ORDER = "date DESC"; - } - - public static final class Addr implements BaseColumns { - /** - * The ID of MM which this address entry belongs to. - */ - public static final String MSG_ID = "msg_id"; - - /** - * The ID of contact entry in Phone Book. - */ - public static final String CONTACT_ID = "contact_id"; - - /** - * The address text. - */ - public static final String ADDRESS = "address"; - - /** - * Type of address, must be one of PduHeaders.BCC, - * PduHeaders.CC, PduHeaders.FROM, PduHeaders.TO. - */ - public static final String TYPE = "type"; - - /** - * Character set of this entry. - */ - public static final String CHARSET = "charset"; - } - - public static final class Part implements BaseColumns { - /** - * The identifier of the message which this part belongs to. - *

Type: INTEGER

- */ - public static final String MSG_ID = "mid"; - - /** - * The order of the part. - *

Type: INTEGER

- */ - public static final String SEQ = "seq"; - - /** - * The content type of the part. - *

Type: TEXT

- */ - public static final String CONTENT_TYPE = "ct"; - - /** - * The name of the part. - *

Type: TEXT

- */ - public static final String NAME = "name"; - - /** - * The charset of the part. - *

Type: TEXT

- */ - public static final String CHARSET = "chset"; - - /** - * The file name of the part. - *

Type: TEXT

- */ - public static final String FILENAME = "fn"; - - /** - * The content disposition of the part. - *

Type: TEXT

- */ - public static final String CONTENT_DISPOSITION = "cd"; - - /** - * The content ID of the part. - *

Type: INTEGER

- */ - public static final String CONTENT_ID = "cid"; - - /** - * The content location of the part. - *

Type: INTEGER

- */ - public static final String CONTENT_LOCATION = "cl"; - - /** - * The start of content-type of the message. - *

Type: INTEGER

- */ - public static final String CT_START = "ctt_s"; - - /** - * The type of content-type of the message. - *

Type: TEXT

- */ - public static final String CT_TYPE = "ctt_t"; - - /** - * The location(on filesystem) of the binary data of the part. - *

Type: INTEGER

- */ - public static final String _DATA = "_data"; - - public static final String TEXT = "text"; - - } - - public static final class Rate { - public static final Uri CONTENT_URI = Uri.withAppendedPath( - Mms.CONTENT_URI, "rate"); - /** - * When a message was successfully sent. - *

Type: INTEGER

- */ - public static final String SENT_TIME = "sent_time"; - } - - public static final class ScrapSpace { - /** - * The content:// style URL for this table - */ - public static final Uri CONTENT_URI = Uri.parse("content://mms/scrapSpace"); - - /** - * This is the scrap file we use to store the media attachment when the user - * chooses to capture a photo to be attached . We pass {#link@Uri} to the Camera app, - * which streams the captured image to the uri. Internally we write the media content - * to this file. It's named '.temp.jpg' so Gallery won't pick it up. - */ - public static final String SCRAP_FILE_PATH = "/sdcard/mms/scrapSpace/.temp.jpg"; - } - - public static final class Intents { - private Intents() { - // Non-instantiatable. - } - - /** - * The extra field to store the contents of the Intent, - * which should be an array of Uri. - */ - public static final String EXTRA_CONTENTS = "contents"; - /** - * The extra field to store the type of the contents, - * which should be an array of String. - */ - public static final String EXTRA_TYPES = "types"; - /** - * The extra field to store the 'Cc' addresses. - */ - public static final String EXTRA_CC = "cc"; - /** - * The extra field to store the 'Bcc' addresses; - */ - public static final String EXTRA_BCC = "bcc"; - /** - * The extra field to store the 'Subject'. - */ - public static final String EXTRA_SUBJECT = "subject"; - /** - * Indicates that the contents of specified URIs were changed. - * The application which is showing or caching these contents - * should be updated. - */ - public static final String - CONTENT_CHANGED_ACTION = "android.intent.action.CONTENT_CHANGED"; - /** - * An extra field which stores the URI of deleted contents. - */ - public static final String DELETED_CONTENTS = "deleted_contents"; - } - } - - /** - * Contains all MMS and SMS messages. - */ - public static final class MmsSms implements BaseColumns { - /** - * The column to distinguish SMS & MMS messages in query results. - */ - public static final String TYPE_DISCRIMINATOR_COLUMN = - "transport_type"; - - public static final Uri CONTENT_URI = Uri.parse("content://mms-sms/"); - - public static final Uri CONTENT_CONVERSATIONS_URI = Uri.parse( - "content://mms-sms/conversations"); - - public static final Uri CONTENT_FILTER_BYPHONE_URI = Uri.parse( - "content://mms-sms/messages/byphone"); - - public static final Uri CONTENT_UNDELIVERED_URI = Uri.parse( - "content://mms-sms/undelivered"); - - public static final Uri CONTENT_DRAFT_URI = Uri.parse( - "content://mms-sms/draft"); - - public static final Uri CONTENT_LOCKED_URI = Uri.parse( - "content://mms-sms/locked"); - - /*** - * Pass in a query parameter called "pattern" which is the text - * to search for. - * The sort order is fixed to be thread_id ASC,date DESC. - */ - public static final Uri SEARCH_URI = Uri.parse( - "content://mms-sms/search"); - - // Constants for message protocol types. - public static final int SMS_PROTO = 0; - public static final int MMS_PROTO = 1; - - // Constants for error types of pending messages. - public static final int NO_ERROR = 0; - public static final int ERR_TYPE_GENERIC = 1; - public static final int ERR_TYPE_SMS_PROTO_TRANSIENT = 2; - public static final int ERR_TYPE_MMS_PROTO_TRANSIENT = 3; - public static final int ERR_TYPE_TRANSPORT_FAILURE = 4; - public static final int ERR_TYPE_GENERIC_PERMANENT = 10; - public static final int ERR_TYPE_SMS_PROTO_PERMANENT = 11; - public static final int ERR_TYPE_MMS_PROTO_PERMANENT = 12; - - public static final class PendingMessages implements BaseColumns { - public static final Uri CONTENT_URI = Uri.withAppendedPath( - MmsSms.CONTENT_URI, "pending"); - /** - * The type of transport protocol(MMS or SMS). - *

Type: INTEGER

- */ - public static final String PROTO_TYPE = "proto_type"; - /** - * The ID of the message to be sent or downloaded. - *

Type: INTEGER

- */ - public static final String MSG_ID = "msg_id"; - /** - * The type of the message to be sent or downloaded. - * This field is only valid for MM. For SM, its value is always - * set to 0. - */ - public static final String MSG_TYPE = "msg_type"; - /** - * The type of the error code. - *

Type: INTEGER

- */ - public static final String ERROR_TYPE = "err_type"; - /** - * The error code of sending/retrieving process. - *

Type: INTEGER

- */ - public static final String ERROR_CODE = "err_code"; - /** - * How many times we tried to send or download the message. - *

Type: INTEGER

- */ - public static final String RETRY_INDEX = "retry_index"; - /** - * The time to do next retry. - */ - public static final String DUE_TIME = "due_time"; - /** - * The time we last tried to send or download the message. - */ - public static final String LAST_TRY = "last_try"; - } - - public static final class WordsTable { - public static final String ID = "_id"; - public static final String SOURCE_ROW_ID = "source_id"; - public static final String TABLE_ID = "table_to_use"; - public static final String INDEXED_TEXT = "index_text"; - } - } - - public static final class Carriers implements BaseColumns { - /** - * The content:// style URL for this table - */ - public static final Uri CONTENT_URI = - Uri.parse("content://telephony/carriers"); - - /** - * The default sort order for this table - */ - public static final String DEFAULT_SORT_ORDER = "name ASC"; - - public static final String NAME = "name"; - - public static final String APN = "apn"; - - public static final String PROXY = "proxy"; - - public static final String PORT = "port"; - - public static final String MMSPROXY = "mmsproxy"; - - public static final String MMSPORT = "mmsport"; - - public static final String SERVER = "server"; - - public static final String USER = "user"; - - public static final String PASSWORD = "password"; - - public static final String MMSC = "mmsc"; - - public static final String MCC = "mcc"; - - public static final String MNC = "mnc"; - - public static final String NUMERIC = "numeric"; - - public static final String AUTH_TYPE = "authtype"; - - public static final String TYPE = "type"; - - public static final String CURRENT = "current"; - } - - public static final class Intents { - private Intents() { - // Not instantiable - } - - /** - * Broadcast Action: A "secret code" has been entered in the dialer. Secret codes are - * of the form *#*##*#*. The intent will have the data URI:

- * - *

android_secret_code://<code>

- */ - public static final String SECRET_CODE_ACTION = - "android.provider.Telephony.SECRET_CODE"; - - /** - * Broadcast Action: The Service Provider string(s) have been updated. Activities or - * services that use these strings should update their display. - * The intent will have the following extra values:

- *
    - *
  • showPlmn - Boolean that indicates whether the PLMN should be shown.
  • - *
  • plmn - The operator name of the registered network, as a string.
  • - *
  • showSpn - Boolean that indicates whether the SPN should be shown.
  • - *
  • spn - The service provider name, as a string.
  • - *
- * Note that showPlmn may indicate that plmn should be displayed, even - * though the value for plmn is null. This can happen, for example, if the phone - * has not registered to a network yet. In this case the receiver may substitute an - * appropriate placeholder string (eg, "No service"). - * - * It is recommended to display plmn before / above spn if - * both are displayed. - * - *

Note this is a protected intent that can only be sent - * by the system. - */ - public static final String SPN_STRINGS_UPDATED_ACTION = - "android.provider.Telephony.SPN_STRINGS_UPDATED"; - - public static final String EXTRA_SHOW_PLMN = "showPlmn"; - public static final String EXTRA_PLMN = "plmn"; - public static final String EXTRA_SHOW_SPN = "showSpn"; - public static final String EXTRA_SPN = "spn"; - } -} From 30a8e2e1359df7abf4af2078bd1ed694e969f84b Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Tue, 9 Mar 2010 15:00:30 -0800 Subject: [PATCH 197/541] Add ability for some manifest attributes to reference resources. This loosens our restriction on many manifest attributes requiring literal string values, to allow various ones to use values from resources. This is only allowed if the resource value does not change from configuration changes, and the restriction is still in place for attributes that are core to security (requesting permissions) or market operation (used libraries and features etc). Change-Id: I4da02f6a5196cb6a7dbcff9ac25403904c42c2c8 --- include/utils/ResourceTypes.h | 4 ++-- libs/utils/ResourceTypes.cpp | 9 ++++----- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h index 0e796dc56..b701ce74d 100644 --- a/include/utils/ResourceTypes.h +++ b/include/utils/ResourceTypes.h @@ -631,6 +631,8 @@ public: void restart(); + const ResStringPool& getStrings() const; + event_code_t getEventType() const; // Note, unlike XmlPullParser, the first call to next() will return // START_TAG of the first element. @@ -716,8 +718,6 @@ public: void uninit(); - const ResStringPool& getStrings() const; - private: friend class ResXMLParser; diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp index 38d841295..7e0f881af 100644 --- a/libs/utils/ResourceTypes.cpp +++ b/libs/utils/ResourceTypes.cpp @@ -625,6 +625,10 @@ void ResXMLParser::restart() mCurNode = NULL; mEventCode = mTree.mError == NO_ERROR ? START_DOCUMENT : BAD_DOCUMENT; } +const ResStringPool& ResXMLParser::getStrings() const +{ + return mTree.mStrings; +} ResXMLParser::event_code_t ResXMLParser::getEventType() const { @@ -1149,11 +1153,6 @@ void ResXMLTree::uninit() restart(); } -const ResStringPool& ResXMLTree::getStrings() const -{ - return mStrings; -} - status_t ResXMLTree::validateNode(const ResXMLTree_node* node) const { const uint16_t eventCode = dtohs(node->header.type); From 68f64818203cf7d2ef1b4707448191829221898e Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Fri, 19 Mar 2010 16:14:13 -0700 Subject: [PATCH 198/541] libutils Condition are now PRIVATE by default Condition must be initialized with SHARED for the old behavior, where they can be used accross processes. Updated the two places android that require SHARED conditions. PRIVATE conditions (and mutexes) use more efficient syscalls. Change-Id: I9a281a4b88206e92ac559c66554e886b9c62db3a --- include/utils/threads.h | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/include/utils/threads.h b/include/utils/threads.h index 130d83c0d..5ac0c5ee1 100644 --- a/include/utils/threads.h +++ b/include/utils/threads.h @@ -209,7 +209,7 @@ inline thread_id_t getThreadId() { class Mutex { public: enum { - NORMAL = 0, + PRIVATE = 0, SHARED = 1 }; @@ -305,7 +305,13 @@ typedef Mutex::Autolock AutoMutex; */ class Condition { public: + enum { + PRIVATE = 0, + SHARED = 1 + }; + Condition(); + Condition(int type); ~Condition(); // Wait on the condition variable. Lock the mutex before calling. status_t wait(Mutex& mutex); @@ -329,6 +335,17 @@ private: inline Condition::Condition() { pthread_cond_init(&mCond, NULL); } +inline Condition::Condition(int type) { + if (type == SHARED) { + pthread_condattr_t attr; + pthread_condattr_init(&attr); + pthread_condattr_setpshared(&attr, PTHREAD_PROCESS_SHARED); + pthread_cond_init(&mCond, &attr); + pthread_condattr_destroy(&attr); + } else { + pthread_cond_init(&mCond, NULL); + } +} inline Condition::~Condition() { pthread_cond_destroy(&mCond); } From 15d0edcba018d99a356077c8769148e769585a48 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Mon, 29 Mar 2010 13:45:18 -0700 Subject: [PATCH 199/541] fix [2542425] memory leak during video recording Vector::sort() is using _do_copy() incorrectly; _do_copy() calls the copy constructor, not the assignment operator, so we need to destroy the "destination" before copying the item. Change-Id: Iaeeac808fa5341a7d219edeba4aa63d44f31473c --- libs/utils/VectorImpl.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/libs/utils/VectorImpl.cpp b/libs/utils/VectorImpl.cpp index 2c2d6675c..0322af7da 100644 --- a/libs/utils/VectorImpl.cpp +++ b/libs/utils/VectorImpl.cpp @@ -173,9 +173,10 @@ status_t VectorImpl::sort(VectorImpl::compar_r_t cmp, void* state) if (!array) return NO_MEMORY; temp = malloc(mItemSize); if (!temp) return NO_MEMORY; - _do_construct(temp, 1); item = reinterpret_cast(array) + mItemSize*(i); curr = reinterpret_cast(array) + mItemSize*(i-1); + } else { + _do_destroy(temp, 1); } _do_copy(temp, item, 1); @@ -183,12 +184,14 @@ status_t VectorImpl::sort(VectorImpl::compar_r_t cmp, void* state) ssize_t j = i-1; void* next = reinterpret_cast(array) + mItemSize*(i); do { + _do_destroy(next, 1); _do_copy(next, curr, 1); next = curr; --j; curr = reinterpret_cast(array) + mItemSize*(j); } while (j>=0 && (cmp(curr, temp, state) > 0)); + _do_destroy(next, 1); _do_copy(next, temp, 1); } i++; From 4e0ee2fd89a32f406a2065f7115d7ee2bdbc92b5 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Tue, 30 Mar 2010 16:53:40 -0700 Subject: [PATCH 200/541] fix [2542425] memory leak during video recording [Sorted|Keyed]Vector would leak their whole storage when resized from the end and TYPE had trivial dtor and copy operators. Change-Id: I8555bb1aa0863df72de27d67ae50e20706e90cf5 --- libs/utils/VectorImpl.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libs/utils/VectorImpl.cpp b/libs/utils/VectorImpl.cpp index 0322af7da..4954ffee7 100644 --- a/libs/utils/VectorImpl.cpp +++ b/libs/utils/VectorImpl.cpp @@ -351,6 +351,7 @@ void* VectorImpl::_grow(size_t where, size_t amount) { const SharedBuffer* cur_sb = SharedBuffer::sharedBuffer(mStorage); SharedBuffer* sb = cur_sb->editResize(new_capacity * mItemSize); + release_storage(); mStorage = sb->data(); } else { SharedBuffer* sb = SharedBuffer::alloc(new_capacity * mItemSize); @@ -403,6 +404,7 @@ void VectorImpl::_shrink(size_t where, size_t amount) { const SharedBuffer* cur_sb = SharedBuffer::sharedBuffer(mStorage); SharedBuffer* sb = cur_sb->editResize(new_capacity * mItemSize); + release_storage(); mStorage = sb->data(); } else { SharedBuffer* sb = SharedBuffer::alloc(new_capacity * mItemSize); From 2937406b0d5db61852088361789e48be1b51e6f9 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Tue, 30 Mar 2010 21:04:17 -0700 Subject: [PATCH 201/541] Revert "fix [2542425] memory leak during video recording" This reverts commit 544592e14f8d7643238e40ba9879727497900f35. --- libs/utils/VectorImpl.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/libs/utils/VectorImpl.cpp b/libs/utils/VectorImpl.cpp index 4954ffee7..0322af7da 100644 --- a/libs/utils/VectorImpl.cpp +++ b/libs/utils/VectorImpl.cpp @@ -351,7 +351,6 @@ void* VectorImpl::_grow(size_t where, size_t amount) { const SharedBuffer* cur_sb = SharedBuffer::sharedBuffer(mStorage); SharedBuffer* sb = cur_sb->editResize(new_capacity * mItemSize); - release_storage(); mStorage = sb->data(); } else { SharedBuffer* sb = SharedBuffer::alloc(new_capacity * mItemSize); @@ -404,7 +403,6 @@ void VectorImpl::_shrink(size_t where, size_t amount) { const SharedBuffer* cur_sb = SharedBuffer::sharedBuffer(mStorage); SharedBuffer* sb = cur_sb->editResize(new_capacity * mItemSize); - release_storage(); mStorage = sb->data(); } else { SharedBuffer* sb = SharedBuffer::alloc(new_capacity * mItemSize); From 88e49600255db6060e56f8633a79fcb24439b109 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Fri, 23 Apr 2010 17:51:26 -0700 Subject: [PATCH 202/541] New xlarge screen size. Not complete, only for experimentation at this point. This includes a reworking of how screen size configurations are matched, so that if you are on a larger screen we can select configurations for smaller screens if there aren't any exactly matching the current screen. The screen size at which we switch to xlarge has been arbitrarily chosen; the compatibility behavior has not yet been defined. Change-Id: I1a33b3818eeb51a68fb72397568c39ab040a07f5 --- include/utils/ResourceTypes.h | 31 ++++++++++++++++++++++++++++--- libs/utils/ResourceTypes.cpp | 3 +++ 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h index b701ce74d..c7d9ff1dd 100644 --- a/include/utils/ResourceTypes.h +++ b/include/utils/ResourceTypes.h @@ -933,6 +933,7 @@ struct ResTable_config SCREENSIZE_SMALL = 0x01, SCREENSIZE_NORMAL = 0x02, SCREENSIZE_LARGE = 0x03, + SCREENSIZE_XLARGE = 0x04, // screenLayout bits for wide/long screen variation. MASK_SCREENLONG = 0x30, @@ -1208,7 +1209,28 @@ struct ResTable_config if (screenLayout || o.screenLayout) { if (((screenLayout^o.screenLayout) & MASK_SCREENSIZE) != 0 && (requested->screenLayout & MASK_SCREENSIZE)) { - return (screenLayout & MASK_SCREENSIZE); + // A little backwards compatibility here: undefined is + // considered equivalent to normal. But only if the + // requested size is at least normal; otherwise, small + // is better than the default. + int mySL = (screenLayout & MASK_SCREENSIZE); + int oSL = (o.screenLayout & MASK_SCREENSIZE); + int fixedMySL = mySL; + int fixedOSL = oSL; + if ((requested->screenLayout & MASK_SCREENSIZE) >= SCREENSIZE_NORMAL) { + if (fixedMySL == 0) fixedMySL = SCREENSIZE_NORMAL; + if (fixedOSL == 0) fixedOSL = SCREENSIZE_NORMAL; + } + // For screen size, the best match is the one that is + // closest to the requested screen size, but not over + // (the not over part is dealt with in match() below). + if (fixedMySL == fixedOSL) { + // If the two are the same, but 'this' is actually + // undefined, then the other is really a better match. + if (mySL == 0) return false; + return true; + } + return fixedMySL >= fixedOSL; } if (((screenLayout^o.screenLayout) & MASK_SCREENLONG) != 0 && (requested->screenLayout & MASK_SCREENLONG)) { @@ -1370,8 +1392,11 @@ struct ResTable_config if (screenConfig != 0) { const int screenSize = screenLayout&MASK_SCREENSIZE; const int setScreenSize = settings.screenLayout&MASK_SCREENSIZE; - if (setScreenSize != 0 && screenSize != 0 - && screenSize != setScreenSize) { + // Any screen sizes for larger screens than the setting do not + // match. + if ((setScreenSize != 0 && screenSize != 0 + && screenSize > setScreenSize) || + (setScreenSize == 0 && screenSize != 0)) { return false; } diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp index 7e0f881af..a1401addf 100644 --- a/libs/utils/ResourceTypes.cpp +++ b/libs/utils/ResourceTypes.cpp @@ -4178,6 +4178,9 @@ void ResTable::print(bool inclValues) const case ResTable_config::SCREENSIZE_LARGE: printf(" (large)"); break; + case ResTable_config::SCREENSIZE_XLARGE: + printf(" (xlarge)"); + break; } printf(" lng=%d", type->config.screenLayout&ResTable_config::MASK_SCREENLONG); From 88753ae9d8f5432590df5e5338e0906834124c1d Mon Sep 17 00:00:00 2001 From: Dan Egnor Date: Thu, 6 May 2010 00:55:09 -0700 Subject: [PATCH 203/541] Make static versions of libutils and libbinder. Fix some small static-initialization-order issues (and a static- initializers-missing issue) that result from doing so. The static libraries don't actually get used for anything real at the moment -- they're used for perf tests of bug 2660235. Bug: 2660235 Change-Id: Iee2f38f79cc93b395e8d0a5a144ed92461f5ada0 --- libs/utils/Android.mk | 10 ++++++++++ libs/utils/String8.cpp | 9 +++++---- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk index d0eedb43a..afecdcb0c 100644 --- a/libs/utils/Android.mk +++ b/libs/utils/Android.mk @@ -104,3 +104,13 @@ endif # sim LOCAL_MODULE:= libutils include $(BUILD_SHARED_LIBRARY) +ifneq ($(TARGET_SIMULATOR),true) +ifeq ($(TARGET_OS),linux) +include $(CLEAR_VARS) +LOCAL_C_INCLUDES += external/zlib external/icu4c/common +LOCAL_LDLIBS := -lrt -ldl -lpthread +LOCAL_MODULE := libutils +LOCAL_SRC_FILES := $(commonSources) BackupData.cpp BackupHelpers.cpp +include $(BUILD_STATIC_LIBRARY) +endif +endif diff --git a/libs/utils/String8.cpp b/libs/utils/String8.cpp index 3a3483871..636cd8342 100644 --- a/libs/utils/String8.cpp +++ b/libs/utils/String8.cpp @@ -136,10 +136,11 @@ static inline char* getEmptyString() void initialize_string8() { -#ifdef LIBUTILS_NATIVE - // Bite me, Darwin! - gDarwinIsReallyAnnoying = gDarwinCantLoadAllObjects; -#endif + // HACK: This dummy dependency forces linking libutils Static.cpp, + // which is needed to initialize String8/String16 classes. + // These variables are named for Darwin, but are needed elsewhere too, + // including static linking on any platform. + gDarwinIsReallyAnnoying = gDarwinCantLoadAllObjects; SharedBuffer* buf = SharedBuffer::alloc(1); char* str = (char*)buf->data(); From eb502abd56e74b11abe7467566938c6711f88515 Mon Sep 17 00:00:00 2001 From: Dan Morrill Date: Thu, 6 May 2010 14:15:47 -0700 Subject: [PATCH 204/541] Adding PacketVideo attribution in NOTICE. Change-Id: Id2fe398d4deed6ebd8cba8516ffc6c0334a961b0 --- NOTICE | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/NOTICE b/NOTICE index bb9c5f269..20062013e 100644 --- a/NOTICE +++ b/NOTICE @@ -41,6 +41,16 @@ The Apache Software Foundation (http://www.apache.org/). ========================================================================= These files are Copyright 2007 Nuance Communications, but released under +the Apache2 License. + + ========================================================================= + == NOTICE file corresponding to the section 4 d of == + == the Apache License, Version 2.0, == + == in this case for the Media Codecs code. == + ========================================================================= + +Media Codecs +These files are Copyright 1998 - 2009 PacketVideo, but released under the Apache2 License. Apache License From c2b77d2fad41ffc583171156eb54b01a61ea6890 Mon Sep 17 00:00:00 2001 From: Kenny Root Date: Thu, 22 Apr 2010 18:28:29 -0700 Subject: [PATCH 205/541] ZipUtilsRO rewrite based on Dalvik Zip rewrite Change the way zip archives are handled. This is necessary to deal with very large (~1GB) APK files, for which our current approach of mapping the entire file falls over. We now do the classic scavenger hunt for the End Of Central Directory magic on a buffer of data read from the file, instead of a memory-mapped section. We use what we find to create a map that covers the Central Directory only. If the caller is interested in unpacking the file contents, we have to do an additional file read to discover the size of the Local File Header section so we can skip past it. This is based on Change I745fb15abb in the dalvik tree. Both implementations share a common ancestry, but the cost of unifying them outweighs the benefits of wrapping C calls. Change-Id: Iddacb50fe913917c2845708a530872d65fdbe620 --- include/utils/ZipFileCRO.h | 4 +- include/utils/ZipFileRO.h | 33 ++- libs/utils/AssetManager.cpp | 2 +- libs/utils/ZipFileCRO.cpp | 4 +- libs/utils/ZipFileRO.cpp | 510 ++++++++++++++++++++++-------------- 5 files changed, 350 insertions(+), 203 deletions(-) diff --git a/include/utils/ZipFileCRO.h b/include/utils/ZipFileCRO.h index 30e00368e..e38bf669d 100644 --- a/include/utils/ZipFileCRO.h +++ b/include/utils/ZipFileCRO.h @@ -47,8 +47,8 @@ extern ZipEntryCRO ZipFileCRO_findEntryByName(ZipFileCRO zip, const char* fileName); extern bool ZipFileCRO_getEntryInfo(ZipFileCRO zip, ZipEntryCRO entry, - int* pMethod, long* pUncompLen, - long* pCompLen, off_t* pOffset, long* pModWhen, long* pCrc32); + int* pMethod, size_t* pUncompLen, + size_t* pCompLen, off_t* pOffset, long* pModWhen, long* pCrc32); extern bool ZipFileCRO_uncompressEntry(ZipFileCRO zip, ZipEntryCRO entry, int fd); diff --git a/include/utils/ZipFileRO.h b/include/utils/ZipFileRO.h index 51c4f2fb6..97d31f4db 100644 --- a/include/utils/ZipFileRO.h +++ b/include/utils/ZipFileRO.h @@ -58,14 +58,19 @@ typedef void* ZipEntryRO; class ZipFileRO { public: ZipFileRO() - : mFd(-1), mFileMap(NULL), mHashTableSize(-1), mHashTable(NULL) + : mFd(-1), mFileName(NULL), mFileLength(-1), + mDirectoryMap(NULL), + mNumEntries(-1), mDirectoryOffset(-1), + mHashTableSize(-1), mHashTable(NULL) {} ~ZipFileRO() { free(mHashTable); - if (mFileMap) - mFileMap->release(); + if (mDirectoryMap) + mDirectoryMap->release(); if (mFd >= 0) close(mFd); + if (mFileName) + free(mFileName); } /* @@ -118,8 +123,8 @@ public: * Returns "false" if "entry" is bogus or if the data in the Zip file * appears to be bad. */ - bool getEntryInfo(ZipEntryRO entry, int* pMethod, long* pUncompLen, - long* pCompLen, off_t* pOffset, long* pModWhen, long* pCrc32) const; + bool getEntryInfo(ZipEntryRO entry, int* pMethod, size_t* pUncompLen, + size_t* pCompLen, off_t* pOffset, long* pModWhen, long* pCrc32) const; /* * Create a new FileMap object that maps a subset of the archive. For @@ -155,13 +160,13 @@ public: * Utility function: uncompress deflated data, buffer to buffer. */ static bool inflateBuffer(void* outBuf, const void* inBuf, - long uncompLen, long compLen); + size_t uncompLen, size_t compLen); /* * Utility function: uncompress deflated data, buffer to fd. */ static bool inflateBuffer(int fd, const void* inBuf, - long uncompLen, long compLen); + size_t uncompLen, size_t compLen); /* * Some basic functions for raw data manipulation. "LE" means @@ -179,6 +184,9 @@ private: ZipFileRO(const ZipFileRO& src); ZipFileRO& operator=(const ZipFileRO& src); + /* locate and parse the central directory */ + bool mapCentralDirectory(void); + /* parse the archive, prepping internal structures */ bool parseZipArchive(void); @@ -203,12 +211,21 @@ private: /* open Zip archive */ int mFd; + /* zip file name */ + char* mFileName; + + /* length of file */ + size_t mFileLength; + /* mapped file */ - FileMap* mFileMap; + FileMap* mDirectoryMap; /* number of entries in the Zip archive */ int mNumEntries; + /* CD directory offset in the Zip archive */ + off_t mDirectoryOffset; + /* * We know how many entries are in the Zip archive, so we have a * fixed-size hash table. We probe for an empty slot. diff --git a/libs/utils/AssetManager.cpp b/libs/utils/AssetManager.cpp index 5a05e6a61..60a0d82e2 100644 --- a/libs/utils/AssetManager.cpp +++ b/libs/utils/AssetManager.cpp @@ -824,7 +824,7 @@ Asset* AssetManager::openAssetFromZipLocked(const ZipFileRO* pZipFile, // TODO: look for previously-created shared memory slice? int method; - long uncompressedLen; + size_t uncompressedLen; //printf("USING Zip '%s'\n", pEntry->getFileName()); diff --git a/libs/utils/ZipFileCRO.cpp b/libs/utils/ZipFileCRO.cpp index 45f6c8baa..16b219cad 100644 --- a/libs/utils/ZipFileCRO.cpp +++ b/libs/utils/ZipFileCRO.cpp @@ -39,8 +39,8 @@ ZipEntryCRO ZipFileCRO_findEntryByName(ZipFileCRO zipToken, } bool ZipFileCRO_getEntryInfo(ZipFileCRO zipToken, ZipEntryRO entryToken, - int* pMethod, long* pUncompLen, - long* pCompLen, off_t* pOffset, long* pModWhen, long* pCrc32) { + int* pMethod, size_t* pUncompLen, + size_t* pCompLen, off_t* pOffset, long* pModWhen, long* pCrc32) { ZipFileRO* zip = (ZipFileRO*)zipToken; ZipEntryRO entry = (ZipEntryRO)entryToken; return zip->getEntryInfo(entry, pMethod, pUncompLen, pCompLen, pOffset, diff --git a/libs/utils/ZipFileRO.cpp b/libs/utils/ZipFileRO.cpp index 6c701dd0b..28dc512bb 100644 --- a/libs/utils/ZipFileRO.cpp +++ b/libs/utils/ZipFileRO.cpp @@ -29,6 +29,22 @@ #include #include #include +#include + +/* + * TEMP_FAILURE_RETRY is defined by some, but not all, versions of + * . (Alas, it is not as standard as we'd hoped!) So, if it's + * not already defined, then define it here. + */ +#ifndef TEMP_FAILURE_RETRY +/* Used to retry syscalls that can return EINTR. */ +#define TEMP_FAILURE_RETRY(exp) ({ \ + typeof (exp) _rc; \ + do { \ + _rc = (exp); \ + } while (_rc == -1 && errno == EINTR); \ + _rc; }) +#endif using namespace android; @@ -38,6 +54,7 @@ using namespace android; #define kEOCDSignature 0x06054b50 #define kEOCDLen 22 #define kEOCDNumEntries 8 // offset to #of entries in file +#define kEOCDSize 12 // size of the central directory #define kEOCDFileOffset 16 // offset to central directory #define kMaxCommentLen 65535 // longest possible in ushort @@ -90,9 +107,8 @@ int ZipFileRO::entryToIndex(const ZipEntryRO entry) const status_t ZipFileRO::open(const char* zipFileName) { int fd = -1; - off_t length; - assert(mFileMap == NULL); + assert(mDirectoryMap == NULL); /* * Open and map the specified file. @@ -103,172 +119,240 @@ status_t ZipFileRO::open(const char* zipFileName) return NAME_NOT_FOUND; } - length = lseek(fd, 0, SEEK_END); - if (length < 0) { + mFileLength = lseek(fd, 0, SEEK_END); + if (mFileLength < kEOCDLen) { close(fd); return UNKNOWN_ERROR; } - mFileMap = new FileMap(); - if (mFileMap == NULL) { - close(fd); - return NO_MEMORY; - } - if (!mFileMap->create(zipFileName, fd, 0, length, true)) { - LOGW("Unable to map '%s': %s\n", zipFileName, strerror(errno)); - close(fd); - return UNKNOWN_ERROR; + if (mFileName != NULL) { + free(mFileName); } + mFileName = strdup(zipFileName); mFd = fd; /* - * Got it mapped, verify it and create data structures for fast access. + * Find the Central Directory and store its size and number of entries. + */ + if (!mapCentralDirectory()) { + goto bail; + } + + /* + * Verify Central Directory and create data structures for fast access. */ if (!parseZipArchive()) { - mFileMap->release(); - mFileMap = NULL; - return UNKNOWN_ERROR; + goto bail; } return OK; + +bail: + free(mFileName); + mFileName = NULL; + close(fd); + return UNKNOWN_ERROR; } /* * Parse the Zip archive, verifying its contents and initializing internal * data structures. */ +bool ZipFileRO::mapCentralDirectory(void) +{ + size_t readAmount = kMaxEOCDSearch; + if (readAmount > (size_t) mFileLength) + readAmount = mFileLength; + + unsigned char* scanBuf = (unsigned char*) malloc(readAmount); + if (scanBuf == NULL) { + LOGW("couldn't allocate scanBuf: %s", strerror(errno)); + free(scanBuf); + return false; + } + + /* + * Make sure this is a Zip archive. + */ + if (lseek(mFd, 0, SEEK_SET) != 0) { + LOGW("seek to start failed: %s", strerror(errno)); + free(scanBuf); + return false; + } + + ssize_t actual = TEMP_FAILURE_RETRY(read(mFd, scanBuf, sizeof(int32_t))); + if (actual != (ssize_t) sizeof(int32_t)) { + LOGI("couldn't read first signature from zip archive: %s", strerror(errno)); + free(scanBuf); + return false; + } + + { + unsigned int header = get4LE(scanBuf); + if (header == kEOCDSignature) { + LOGI("Found Zip archive, but it looks empty\n"); + free(scanBuf); + return false; + } else if (header != kLFHSignature) { + LOGV("Not a Zip archive (found 0x%08x)\n", val); + free(scanBuf); + return false; + } + } + + /* + * Perform the traditional EOCD snipe hunt. + * + * We're searching for the End of Central Directory magic number, + * which appears at the start of the EOCD block. It's followed by + * 18 bytes of EOCD stuff and up to 64KB of archive comment. We + * need to read the last part of the file into a buffer, dig through + * it to find the magic number, parse some values out, and use those + * to determine the extent of the CD. + * + * We start by pulling in the last part of the file. + */ + off_t searchStart = mFileLength - readAmount; + + if (lseek(mFd, searchStart, SEEK_SET) != searchStart) { + LOGW("seek %ld failed: %s\n", (long) searchStart, strerror(errno)); + free(scanBuf); + return false; + } + actual = TEMP_FAILURE_RETRY(read(mFd, scanBuf, readAmount)); + if (actual != (ssize_t) readAmount) { + LOGW("Zip: read %zd failed: %s\n", readAmount, strerror(errno)); + free(scanBuf); + return false; + } + + /* + * Scan backward for the EOCD magic. In an archive without a trailing + * comment, we'll find it on the first try. (We may want to consider + * doing an initial minimal read; if we don't find it, retry with a + * second read as above.) + */ + int i; + for (i = readAmount - kEOCDLen; i >= 0; i--) { + if (scanBuf[i] == 0x50 && get4LE(&scanBuf[i]) == kEOCDSignature) { + LOGV("+++ Found EOCD at buf+%d\n", i); + break; + } + } + if (i < 0) { + LOGD("Zip: EOCD not found, %s is not zip\n", mFileName); + free(scanBuf); + return false; + } + + off_t eocdOffset = searchStart + i; + const unsigned char* eocdPtr = scanBuf + i; + + assert(eocdOffset < mFileLength); + + /* + * Grab the CD offset and size, and the number of entries in the + * archive. Verify that they look reasonable. + */ + unsigned int numEntries = get2LE(eocdPtr + kEOCDNumEntries); + unsigned int dirSize = get4LE(eocdPtr + kEOCDSize); + unsigned int dirOffset = get4LE(eocdPtr + kEOCDFileOffset); + + if ((long long) dirOffset + (long long) dirSize > (long long) eocdOffset) { + LOGW("bad offsets (dir %ld, size %u, eocd %ld)\n", + (long) dirOffset, dirSize, (long) eocdOffset); + free(scanBuf); + return false; + } + if (numEntries == 0) { + LOGW("empty archive?\n"); + free(scanBuf); + return false; + } + + LOGV("+++ numEntries=%d dirSize=%d dirOffset=%d\n", + numEntries, dirSize, dirOffset); + + mDirectoryMap = new FileMap(); + if (mDirectoryMap == NULL) { + LOGW("Unable to create directory map: %s", strerror(errno)); + free(scanBuf); + return false; + } + + if (!mDirectoryMap->create(mFileName, mFd, dirOffset, dirSize, true)) { + LOGW("Unable to map '%s' (%zd to %zd): %s\n", mFileName, + dirOffset, dirOffset + dirSize, strerror(errno)); + free(scanBuf); + return false; + } + + mNumEntries = numEntries; + mDirectoryOffset = dirOffset; + + return true; +} + bool ZipFileRO::parseZipArchive(void) { -#define CHECK_OFFSET(_off) { \ - if ((unsigned int) (_off) >= maxOffset) { \ - LOGE("ERROR: bad offset %u (max %d): %s\n", \ - (unsigned int) (_off), maxOffset, #_off); \ - goto bail; \ - } \ - } - const unsigned char* basePtr = (const unsigned char*)mFileMap->getDataPtr(); - const unsigned char* ptr; - size_t length = mFileMap->getDataLength(); bool result = false; - unsigned int i, numEntries, cdOffset; - unsigned int val; - - /* - * The first 4 bytes of the file will either be the local header - * signature for the first file (kLFHSignature) or, if the archive doesn't - * have any files in it, the end-of-central-directory signature - * (kEOCDSignature). - */ - val = get4LE(basePtr); - if (val == kEOCDSignature) { - LOGI("Found Zip archive, but it looks empty\n"); - goto bail; - } else if (val != kLFHSignature) { - LOGV("Not a Zip archive (found 0x%08x)\n", val); - goto bail; - } - - /* - * Find the EOCD. We'll find it immediately unless they have a file - * comment. - */ - ptr = basePtr + length - kEOCDLen; - - while (ptr >= basePtr) { - if (*ptr == (kEOCDSignature & 0xff) && get4LE(ptr) == kEOCDSignature) - break; - ptr--; - } - if (ptr < basePtr) { - LOGI("Could not find end-of-central-directory in Zip\n"); - goto bail; - } - - /* - * There are two interesting items in the EOCD block: the number of - * entries in the file, and the file offset of the start of the - * central directory. - * - * (There's actually a count of the #of entries in this file, and for - * all files which comprise a spanned archive, but for our purposes - * we're only interested in the current file. Besides, we expect the - * two to be equivalent for our stuff.) - */ - numEntries = get2LE(ptr + kEOCDNumEntries); - cdOffset = get4LE(ptr + kEOCDFileOffset); - - /* valid offsets are [0,EOCD] */ - unsigned int maxOffset; - maxOffset = (ptr - basePtr) +1; - - LOGV("+++ numEntries=%d cdOffset=%d\n", numEntries, cdOffset); - if (numEntries == 0 || cdOffset >= length) { - LOGW("Invalid entries=%d offset=%d (len=%zd)\n", - numEntries, cdOffset, length); - goto bail; - } + const unsigned char* cdPtr = (const unsigned char*) mDirectoryMap->getDataPtr(); + size_t cdLength = mDirectoryMap->getDataLength(); + int numEntries = mNumEntries; /* * Create hash table. We have a minimum 75% load factor, possibly as * low as 50% after we round off to a power of 2. */ - mNumEntries = numEntries; - mHashTableSize = roundUpPower2(1 + ((numEntries * 4) / 3)); - mHashTable = (HashEntry*) calloc(1, sizeof(HashEntry) * mHashTableSize); + mHashTableSize = roundUpPower2(1 + (numEntries * 4) / 3); + mHashTable = (HashEntry*) calloc(mHashTableSize, sizeof(HashEntry)); /* * Walk through the central directory, adding entries to the hash * table. */ - ptr = basePtr + cdOffset; - for (i = 0; i < numEntries; i++) { - unsigned int fileNameLen, extraLen, commentLen, localHdrOffset; - const unsigned char* localHdr; - unsigned int hash; - + const unsigned char* ptr = cdPtr; + for (int i = 0; i < numEntries; i++) { if (get4LE(ptr) != kCDESignature) { LOGW("Missed a central dir sig (at %d)\n", i); goto bail; } - if (ptr + kCDELen > basePtr + length) { + if (ptr + kCDELen > cdPtr + cdLength) { LOGW("Ran off the end (at %d)\n", i); goto bail; } - localHdrOffset = get4LE(ptr + kCDELocalOffset); - CHECK_OFFSET(localHdrOffset); + long localHdrOffset = (long) get4LE(ptr + kCDELocalOffset); + if (localHdrOffset >= mDirectoryOffset) { + LOGW("bad LFH offset %ld at entry %d\n", localHdrOffset, i); + goto bail; + } + + unsigned int fileNameLen, extraLen, commentLen, hash; + fileNameLen = get2LE(ptr + kCDENameLen); extraLen = get2LE(ptr + kCDEExtraLen); commentLen = get2LE(ptr + kCDECommentLen); - //LOGV("+++ %d: localHdr=%d fnl=%d el=%d cl=%d\n", - // i, localHdrOffset, fileNameLen, extraLen, commentLen); - //LOGV(" '%.*s'\n", fileNameLen, ptr + kCDELen); - /* add the CDE filename to the hash table */ hash = computeHash((const char*)ptr + kCDELen, fileNameLen); addToHash((const char*)ptr + kCDELen, fileNameLen, hash); - localHdr = basePtr + localHdrOffset; - if (get4LE(localHdr) != kLFHSignature) { - LOGW("Bad offset to local header: %d (at %d)\n", - localHdrOffset, i); + ptr += kCDELen + fileNameLen + extraLen + commentLen; + if ((size_t)(ptr - cdPtr) > cdLength) { + LOGW("bad CD advance (%d vs %zd) at entry %d\n", + (int) (ptr - cdPtr), cdLength, i); goto bail; } - - ptr += kCDELen + fileNameLen + extraLen + commentLen; - CHECK_OFFSET(ptr - basePtr); } - + LOGV("+++ zip good scan %d entries\n", numEntries); result = true; bail: return result; -#undef CHECK_OFFSET } - /* * Simple string hash function for non-null-terminated strings. */ @@ -315,7 +399,7 @@ ZipEntryRO ZipFileRO::findEntryByName(const char* fileName) const memcmp(mHashTable[ent].name, fileName, nameLen) == 0) { /* match */ - return (ZipEntryRO) (ent + kZipEntryAdj); + return (ZipEntryRO)(long)(ent + kZipEntryAdj); } ent = (ent + 1) & (mHashTableSize-1); @@ -354,20 +438,24 @@ ZipEntryRO ZipFileRO::findEntryByIndex(int idx) const * Returns "false" if the offsets to the fields or the contents of the fields * appear to be bogus. */ -bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, long* pUncompLen, - long* pCompLen, off_t* pOffset, long* pModWhen, long* pCrc32) const +bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, size_t* pUncompLen, + size_t* pCompLen, off_t* pOffset, long* pModWhen, long* pCrc32) const { - int ent = entryToIndex(entry); + bool ret = false; + + const int ent = entryToIndex(entry); if (ent < 0) return false; + HashEntry hashEntry = mHashTable[ent]; + /* * Recover the start of the central directory entry from the filename - * pointer. + * pointer. The filename is the first entry past the fixed-size data, + * so we can just subtract back from that. */ - const unsigned char* basePtr = (const unsigned char*)mFileMap->getDataPtr(); - const unsigned char* ptr = (const unsigned char*) mHashTable[ent].name; - size_t zipLength = mFileMap->getDataLength(); + const unsigned char* ptr = (const unsigned char*) hashEntry.name; + off_t cdOffset = mDirectoryOffset; ptr -= kCDELen; @@ -380,48 +468,78 @@ bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, long* pUncompLen, if (pCrc32 != NULL) *pCrc32 = get4LE(ptr + kCDECRC); + size_t compLen = get4LE(ptr + kCDECompLen); + if (pCompLen != NULL) + *pCompLen = compLen; + size_t uncompLen = get4LE(ptr + kCDEUncompLen); + if (pUncompLen != NULL) + *pUncompLen = uncompLen; + /* - * We need to make sure that the lengths are not so large that somebody - * trying to map the compressed or uncompressed data runs off the end - * of the mapped region. + * If requested, determine the offset of the start of the data. All we + * have is the offset to the Local File Header, which is variable size, + * so we have to read the contents of the struct to figure out where + * the actual data starts. + * + * We also need to make sure that the lengths are not so large that + * somebody trying to map the compressed or uncompressed data runs + * off the end of the mapped region. + * + * Note we don't verify compLen/uncompLen if they don't request the + * dataOffset, because dataOffset is expensive to determine. However, + * if they don't have the file offset, they're not likely to be doing + * anything with the contents. */ - unsigned long localHdrOffset = get4LE(ptr + kCDELocalOffset); - if (localHdrOffset + kLFHLen >= zipLength) { - LOGE("ERROR: bad local hdr offset in zip\n"); - return false; - } - const unsigned char* localHdr = basePtr + localHdrOffset; - off_t dataOffset = localHdrOffset + kLFHLen - + get2LE(localHdr + kLFHNameLen) + get2LE(localHdr + kLFHExtraLen); - if ((unsigned long) dataOffset >= zipLength) { - LOGE("ERROR: bad data offset in zip\n"); - return false; - } - - if (pCompLen != NULL) { - *pCompLen = get4LE(ptr + kCDECompLen); - if (*pCompLen < 0 || (size_t)(dataOffset + *pCompLen) >= zipLength) { - LOGE("ERROR: bad compressed length in zip\n"); - return false; - } - } - if (pUncompLen != NULL) { - *pUncompLen = get4LE(ptr + kCDEUncompLen); - if (*pUncompLen < 0) { - LOGE("ERROR: negative uncompressed length in zip\n"); - return false; - } - if (method == kCompressStored && - (size_t)(dataOffset + *pUncompLen) >= zipLength) - { - LOGE("ERROR: bad uncompressed length in zip\n"); - return false; - } - } - if (pOffset != NULL) { + long localHdrOffset = get4LE(ptr + kCDELocalOffset); + if (localHdrOffset + kLFHLen >= cdOffset) { + LOGE("ERROR: bad local hdr offset in zip\n"); + return false; + } + + unsigned char lfhBuf[kLFHLen]; + if (lseek(mFd, localHdrOffset, SEEK_SET) != localHdrOffset) { + LOGW("failed seeking to lfh at offset %ld\n", localHdrOffset); + return false; + } + ssize_t actual = + TEMP_FAILURE_RETRY(read(mFd, lfhBuf, sizeof(lfhBuf))); + if (actual != sizeof(lfhBuf)) { + LOGW("failed reading lfh from offset %ld\n", localHdrOffset); + return false; + } + + if (get4LE(lfhBuf) != kLFHSignature) { + LOGW("didn't find signature at start of lfh, offset=%ld\n", + localHdrOffset); + return false; + } + + off_t dataOffset = localHdrOffset + kLFHLen + + get2LE(lfhBuf + kLFHNameLen) + get2LE(lfhBuf + kLFHExtraLen); + if (dataOffset >= cdOffset) { + LOGW("bad data offset %ld in zip\n", (long) dataOffset); + return false; + } + + /* check lengths */ + if ((off_t)(dataOffset + compLen) > cdOffset) { + LOGW("bad compressed length in zip (%ld + %zd > %ld)\n", + (long) dataOffset, compLen, (long) cdOffset); + return false; + } + + if (method == kCompressStored && + (off_t)(dataOffset + uncompLen) > cdOffset) + { + LOGE("ERROR: bad uncompressed length in zip (%ld + %zd > %ld)\n", + (long) dataOffset, uncompLen, (long) cdOffset); + return false; + } + *pOffset = dataOffset; } + return true; } @@ -457,14 +575,14 @@ FileMap* ZipFileRO::createEntryFileMap(ZipEntryRO entry) const */ FileMap* newMap; - long compLen; + size_t compLen; off_t offset; if (!getEntryInfo(entry, NULL, NULL, &compLen, &offset, NULL, NULL)) return NULL; newMap = new FileMap(); - if (!newMap->create(mFileMap->getFileName(), mFd, offset, compLen, true)) { + if (!newMap->create(mFileName, mFd, offset, compLen, true)) { newMap->release(); return NULL; } @@ -480,19 +598,26 @@ FileMap* ZipFileRO::createEntryFileMap(ZipEntryRO entry) const */ bool ZipFileRO::uncompressEntry(ZipEntryRO entry, void* buffer) const { - const int kSequentialMin = 32768; + const size_t kSequentialMin = 32768; bool result = false; int ent = entryToIndex(entry); if (ent < 0) return -1; - const unsigned char* basePtr = (const unsigned char*)mFileMap->getDataPtr(); int method; - long uncompLen, compLen; + size_t uncompLen, compLen; off_t offset; + const unsigned char* ptr; getEntryInfo(entry, &method, &uncompLen, &compLen, &offset, NULL, NULL); + FileMap* file = createEntryFileMap(entry); + if (file == NULL) { + goto bail; + } + + ptr = (const unsigned char*) file->getDataPtr(); + /* * Experiment with madvise hint. When we want to uncompress a file, * we pull some stuff out of the central dir entry and then hit a @@ -507,17 +632,17 @@ bool ZipFileRO::uncompressEntry(ZipEntryRO entry, void* buffer) const * pair of system calls are negated by a reduction in page faults. */ if (compLen > kSequentialMin) - mFileMap->advise(FileMap::SEQUENTIAL); + file->advise(FileMap::SEQUENTIAL); if (method == kCompressStored) { - memcpy(buffer, basePtr + offset, uncompLen); + memcpy(buffer, ptr, uncompLen); } else { - if (!inflateBuffer(buffer, basePtr + offset, uncompLen, compLen)) + if (!inflateBuffer(buffer, ptr, uncompLen, compLen)) goto bail; } if (compLen > kSequentialMin) - mFileMap->advise(FileMap::NORMAL); + file->advise(FileMap::NORMAL); result = true; @@ -537,29 +662,34 @@ bool ZipFileRO::uncompressEntry(ZipEntryRO entry, int fd) const if (ent < 0) return -1; - const unsigned char* basePtr = (const unsigned char*)mFileMap->getDataPtr(); int method; - long uncompLen, compLen; + size_t uncompLen, compLen; off_t offset; + const unsigned char* ptr; getEntryInfo(entry, &method, &uncompLen, &compLen, &offset, NULL, NULL); - if (method == kCompressStored) { - ssize_t actual; + const FileMap* file = createEntryFileMap(entry); + if (file == NULL) { + goto bail; + } - actual = write(fd, basePtr + offset, uncompLen); + ptr = (const unsigned char*) file->getDataPtr(); + + if (method == kCompressStored) { + ssize_t actual = write(fd, ptr, uncompLen); if (actual < 0) { LOGE("Write failed: %s\n", strerror(errno)); goto bail; - } else if (actual != uncompLen) { - LOGE("Partial write during uncompress (%d of %ld)\n", - (int)actual, uncompLen); + } else if ((size_t) actual != uncompLen) { + LOGE("Partial write during uncompress (%zd of %zd)\n", + actual, uncompLen); goto bail; } else { LOGI("+++ successful write\n"); } } else { - if (!inflateBuffer(fd, basePtr+offset, uncompLen, compLen)) + if (!inflateBuffer(fd, ptr, uncompLen, compLen)) goto bail; } @@ -573,7 +703,7 @@ bail: * Uncompress "deflate" data from one buffer to another. */ /*static*/ bool ZipFileRO::inflateBuffer(void* outBuf, const void* inBuf, - long uncompLen, long compLen) + size_t uncompLen, size_t compLen) { bool result = false; z_stream zstream; @@ -582,7 +712,7 @@ bail: /* * Initialize the zlib stream struct. */ - memset(&zstream, 0, sizeof(zstream)); + memset(&zstream, 0, sizeof(zstream)); zstream.zalloc = Z_NULL; zstream.zfree = Z_NULL; zstream.opaque = Z_NULL; @@ -592,10 +722,10 @@ bail: zstream.avail_out = uncompLen; zstream.data_type = Z_UNKNOWN; - /* - * Use the undocumented "negative window bits" feature to tell zlib - * that there's no zlib header waiting for it. - */ + /* + * Use the undocumented "negative window bits" feature to tell zlib + * that there's no zlib header waiting for it. + */ zerr = inflateInit2(&zstream, -MAX_WBITS); if (zerr != Z_OK) { if (zerr == Z_VERSION_ERROR) { @@ -619,8 +749,8 @@ bail: } /* paranoia */ - if ((long) zstream.total_out != uncompLen) { - LOGW("Size mismatch on inflated file (%ld vs %ld)\n", + if (zstream.total_out != uncompLen) { + LOGW("Size mismatch on inflated file (%ld vs %zd)\n", zstream.total_out, uncompLen); goto z_bail; } @@ -638,10 +768,10 @@ bail: * Uncompress "deflate" data from one buffer to an open file descriptor. */ /*static*/ bool ZipFileRO::inflateBuffer(int fd, const void* inBuf, - long uncompLen, long compLen) + size_t uncompLen, size_t compLen) { bool result = false; - const int kWriteBufSize = 32768; + const size_t kWriteBufSize = 32768; unsigned char writeBuf[kWriteBufSize]; z_stream zstream; int zerr; @@ -649,7 +779,7 @@ bail: /* * Initialize the zlib stream struct. */ - memset(&zstream, 0, sizeof(zstream)); + memset(&zstream, 0, sizeof(zstream)); zstream.zalloc = Z_NULL; zstream.zfree = Z_NULL; zstream.opaque = Z_NULL; @@ -659,10 +789,10 @@ bail: zstream.avail_out = sizeof(writeBuf); zstream.data_type = Z_UNKNOWN; - /* - * Use the undocumented "negative window bits" feature to tell zlib - * that there's no zlib header waiting for it. - */ + /* + * Use the undocumented "negative window bits" feature to tell zlib + * that there's no zlib header waiting for it. + */ zerr = inflateInit2(&zstream, -MAX_WBITS); if (zerr != Z_OK) { if (zerr == Z_VERSION_ERROR) { @@ -708,8 +838,8 @@ bail: assert(zerr == Z_STREAM_END); /* other errors should've been caught */ /* paranoia */ - if ((long) zstream.total_out != uncompLen) { - LOGW("Size mismatch on inflated file (%ld vs %ld)\n", + if (zstream.total_out != uncompLen) { + LOGW("Size mismatch on inflated file (%ld vs %zd)\n", zstream.total_out, uncompLen); goto z_bail; } From 9b0be73f2aca790774e7a85f5653afe33947c8ed Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Wed, 19 May 2010 15:11:00 -0700 Subject: [PATCH 206/541] added RWLock C++ wrapper Change-Id: Ia736bf7f6e2c49915a9ab5669551cf89dafa7961 --- include/utils/threads.h | 90 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/include/utils/threads.h b/include/utils/threads.h index 5ac0c5ee1..1bcfaede6 100644 --- a/include/utils/threads.h +++ b/include/utils/threads.h @@ -295,6 +295,96 @@ typedef Mutex::Autolock AutoMutex; /*****************************************************************************/ +#if defined(HAVE_PTHREADS) + +/* + * Simple mutex class. The implementation is system-dependent. + * + * The mutex must be unlocked by the thread that locked it. They are not + * recursive, i.e. the same thread can't lock it multiple times. + */ +class RWLock { +public: + enum { + PRIVATE = 0, + SHARED = 1 + }; + + RWLock(); + RWLock(const char* name); + RWLock(int type, const char* name = NULL); + ~RWLock(); + + status_t readLock(); + status_t tryReadLock(); + status_t writeLock(); + status_t tryWriteLock(); + void unlock(); + + class AutoRLock { + public: + inline AutoRLock(RWLock& rwlock) : mLock(rwlock) { mLock.readLock(); } + inline ~AutoRLock() { mLock.unlock(); } + private: + RWLock& mLock; + }; + + class AutoWLock { + public: + inline AutoWLock(RWLock& rwlock) : mLock(rwlock) { mLock.writeLock(); } + inline ~AutoWLock() { mLock.unlock(); } + private: + RWLock& mLock; + }; + +private: + // A RWLock cannot be copied + RWLock(const RWLock&); + RWLock& operator = (const RWLock&); + + pthread_rwlock_t mRWLock; +}; + +inline RWLock::RWLock() { + pthread_rwlock_init(&mRWLock, NULL); +} +inline RWLock::RWLock(const char* name) { + pthread_rwlock_init(&mRWLock, NULL); +} +inline RWLock::RWLock(int type, const char* name) { + if (type == SHARED) { + pthread_rwlockattr_t attr; + pthread_rwlockattr_init(&attr); + pthread_rwlockattr_setpshared(&attr, PTHREAD_PROCESS_SHARED); + pthread_rwlock_init(&mRWLock, &attr); + pthread_rwlockattr_destroy(&attr); + } else { + pthread_rwlock_init(&mRWLock, NULL); + } +} +inline RWLock::~RWLock() { + pthread_rwlock_destroy(&mRWLock); +} +inline status_t RWLock::readLock() { + return -pthread_rwlock_rdlock(&mRWLock); +} +inline status_t RWLock::tryReadLock() { + return -pthread_rwlock_tryrdlock(&mRWLock); +} +inline status_t RWLock::writeLock() { + return -pthread_rwlock_wrlock(&mRWLock); +} +inline status_t RWLock::tryWriteLock() { + return -pthread_rwlock_trywrlock(&mRWLock); +} +inline void RWLock::unlock() { + pthread_rwlock_unlock(&mRWLock); +} + +#endif // HAVE_PTHREADS + +/*****************************************************************************/ + /* * Condition variable class. The implementation is system-dependent. * From e6cbafd07dfd56e98d28ca93bd31cdd80d9f37a9 Mon Sep 17 00:00:00 2001 From: Daisuke Miyakawa Date: Fri, 14 May 2010 11:17:46 -0700 Subject: [PATCH 207/541] Make vCard code a separated static library. - Move the library to a separate directory in framewokr/base, and rename its package from android.pim.vcard to com.android.vcard. - Move all tests for the library under the directory. - Confirm all tests for vCard are successful. It would be better for us to have this directory somewhere else (like external/). But I'll submit this here now and move it to the right place as soon as possible. From the view of build mechanism, we can do that immediately. BUG: 2689523 Change-Id: I435e10571b7160bfcc029bed7c37aaac1c6fd69a --- vcard/Android.mk | 28 + .../java/com/android/vcard/JapaneseUtils.java | 379 ++++ .../java/com/android/vcard/VCardBuilder.java | 1996 +++++++++++++++++ .../java/com/android/vcard/VCardComposer.java | 677 ++++++ vcard/java/com/android/vcard/VCardConfig.java | 478 ++++ .../com/android/vcard/VCardConstants.java | 160 ++ vcard/java/com/android/vcard/VCardEntry.java | 1423 ++++++++++++ .../android/vcard/VCardEntryCommitter.java | 68 + .../android/vcard/VCardEntryConstructor.java | 240 ++ .../com/android/vcard/VCardEntryCounter.java | 63 + .../com/android/vcard/VCardEntryHandler.java | 43 + .../com/android/vcard/VCardInterpreter.java | 102 + .../vcard/VCardInterpreterCollection.java | 102 + vcard/java/com/android/vcard/VCardParser.java | 55 + .../android/vcard/VCardParserImpl_V21.java | 968 ++++++++ .../android/vcard/VCardParserImpl_V30.java | 317 +++ .../com/android/vcard/VCardParser_V21.java | 113 + .../com/android/vcard/VCardParser_V30.java | 91 + .../android/vcard/VCardSourceDetector.java | 172 ++ vcard/java/com/android/vcard/VCardUtils.java | 658 ++++++ .../VCardAgentNotSupportedException.java | 27 + .../vcard/exception/VCardException.java | 35 + .../VCardInvalidCommentLineException.java | 32 + .../exception/VCardInvalidLineException.java | 31 + .../vcard/exception/VCardNestedException.java | 29 + .../exception/VCardNotSupportedException.java | 33 + .../exception/VCardVersionException.java | 28 + vcard/tests/Android.mk | 25 + vcard/tests/AndroidManifest.xml | 30 + vcard/tests/res/raw/v21_backslash.vcf | 5 + vcard/tests/res/raw/v21_complicated.vcf | 106 + .../res/raw/v21_invalid_comment_line.vcf | 10 + vcard/tests/res/raw/v21_japanese_1.vcf | 6 + vcard/tests/res/raw/v21_japanese_2.vcf | 10 + vcard/tests/res/raw/v21_multiple_entry.vcf | 33 + vcard/tests/res/raw/v21_org_before_title.vcf | 6 + vcard/tests/res/raw/v21_pref_handling.vcf | 15 + vcard/tests/res/raw/v21_simple_1.vcf | 3 + vcard/tests/res/raw/v21_simple_2.vcf | 3 + vcard/tests/res/raw/v21_simple_3.vcf | 4 + vcard/tests/res/raw/v21_title_before_org.vcf | 6 + vcard/tests/res/raw/v21_winmo_65.vcf | 10 + vcard/tests/res/raw/v30_comma_separated.vcf | 5 + vcard/tests/res/raw/v30_simple.vcf | 13 + .../vcard/tests/VCardExporterTests.java | 971 ++++++++ .../vcard/tests/VCardImporterTests.java | 1008 +++++++++ .../vcard/tests/VCardJapanizationTests.java | 436 ++++ .../android/vcard/tests/VCardTestsBase.java | 85 + .../android/vcard/tests/VCardUtilsTests.java | 84 + .../vcard/tests/test_utils/ContactEntry.java | 43 + .../test_utils/ContentValuesBuilder.java | 80 + .../test_utils/ContentValuesVerifier.java | 102 + .../test_utils/ContentValuesVerifierElem.java | 96 + .../tests/test_utils/ExportTestProvider.java | 176 ++ .../tests/test_utils/ExportTestResolver.java | 40 + .../tests/test_utils/ImportTestProvider.java | 274 +++ .../tests/test_utils/ImportTestResolver.java | 57 + .../vcard/tests/test_utils/LineVerifier.java | 66 + .../tests/test_utils/LineVerifierElem.java | 106 + .../vcard/tests/test_utils/PropertyNode.java | 205 ++ .../test_utils/PropertyNodesVerifier.java | 91 + .../test_utils/PropertyNodesVerifierElem.java | 317 +++ .../vcard/tests/test_utils/VCardVerifier.java | 339 +++ .../android/vcard/tests/test_utils/VNode.java | 30 + .../vcard/tests/test_utils/VNodeBuilder.java | 247 ++ 65 files changed, 13491 insertions(+) create mode 100644 vcard/Android.mk create mode 100644 vcard/java/com/android/vcard/JapaneseUtils.java create mode 100644 vcard/java/com/android/vcard/VCardBuilder.java create mode 100644 vcard/java/com/android/vcard/VCardComposer.java create mode 100644 vcard/java/com/android/vcard/VCardConfig.java create mode 100644 vcard/java/com/android/vcard/VCardConstants.java create mode 100644 vcard/java/com/android/vcard/VCardEntry.java create mode 100644 vcard/java/com/android/vcard/VCardEntryCommitter.java create mode 100644 vcard/java/com/android/vcard/VCardEntryConstructor.java create mode 100644 vcard/java/com/android/vcard/VCardEntryCounter.java create mode 100644 vcard/java/com/android/vcard/VCardEntryHandler.java create mode 100644 vcard/java/com/android/vcard/VCardInterpreter.java create mode 100644 vcard/java/com/android/vcard/VCardInterpreterCollection.java create mode 100644 vcard/java/com/android/vcard/VCardParser.java create mode 100644 vcard/java/com/android/vcard/VCardParserImpl_V21.java create mode 100644 vcard/java/com/android/vcard/VCardParserImpl_V30.java create mode 100644 vcard/java/com/android/vcard/VCardParser_V21.java create mode 100644 vcard/java/com/android/vcard/VCardParser_V30.java create mode 100644 vcard/java/com/android/vcard/VCardSourceDetector.java create mode 100644 vcard/java/com/android/vcard/VCardUtils.java create mode 100644 vcard/java/com/android/vcard/exception/VCardAgentNotSupportedException.java create mode 100644 vcard/java/com/android/vcard/exception/VCardException.java create mode 100644 vcard/java/com/android/vcard/exception/VCardInvalidCommentLineException.java create mode 100644 vcard/java/com/android/vcard/exception/VCardInvalidLineException.java create mode 100644 vcard/java/com/android/vcard/exception/VCardNestedException.java create mode 100644 vcard/java/com/android/vcard/exception/VCardNotSupportedException.java create mode 100644 vcard/java/com/android/vcard/exception/VCardVersionException.java create mode 100644 vcard/tests/Android.mk create mode 100644 vcard/tests/AndroidManifest.xml create mode 100644 vcard/tests/res/raw/v21_backslash.vcf create mode 100644 vcard/tests/res/raw/v21_complicated.vcf create mode 100644 vcard/tests/res/raw/v21_invalid_comment_line.vcf create mode 100644 vcard/tests/res/raw/v21_japanese_1.vcf create mode 100644 vcard/tests/res/raw/v21_japanese_2.vcf create mode 100644 vcard/tests/res/raw/v21_multiple_entry.vcf create mode 100644 vcard/tests/res/raw/v21_org_before_title.vcf create mode 100644 vcard/tests/res/raw/v21_pref_handling.vcf create mode 100644 vcard/tests/res/raw/v21_simple_1.vcf create mode 100644 vcard/tests/res/raw/v21_simple_2.vcf create mode 100644 vcard/tests/res/raw/v21_simple_3.vcf create mode 100644 vcard/tests/res/raw/v21_title_before_org.vcf create mode 100644 vcard/tests/res/raw/v21_winmo_65.vcf create mode 100644 vcard/tests/res/raw/v30_comma_separated.vcf create mode 100644 vcard/tests/res/raw/v30_simple.vcf create mode 100644 vcard/tests/src/com/android/vcard/tests/VCardExporterTests.java create mode 100644 vcard/tests/src/com/android/vcard/tests/VCardImporterTests.java create mode 100644 vcard/tests/src/com/android/vcard/tests/VCardJapanizationTests.java create mode 100644 vcard/tests/src/com/android/vcard/tests/VCardTestsBase.java create mode 100644 vcard/tests/src/com/android/vcard/tests/VCardUtilsTests.java create mode 100644 vcard/tests/src/com/android/vcard/tests/test_utils/ContactEntry.java create mode 100644 vcard/tests/src/com/android/vcard/tests/test_utils/ContentValuesBuilder.java create mode 100644 vcard/tests/src/com/android/vcard/tests/test_utils/ContentValuesVerifier.java create mode 100644 vcard/tests/src/com/android/vcard/tests/test_utils/ContentValuesVerifierElem.java create mode 100644 vcard/tests/src/com/android/vcard/tests/test_utils/ExportTestProvider.java create mode 100644 vcard/tests/src/com/android/vcard/tests/test_utils/ExportTestResolver.java create mode 100644 vcard/tests/src/com/android/vcard/tests/test_utils/ImportTestProvider.java create mode 100644 vcard/tests/src/com/android/vcard/tests/test_utils/ImportTestResolver.java create mode 100644 vcard/tests/src/com/android/vcard/tests/test_utils/LineVerifier.java create mode 100644 vcard/tests/src/com/android/vcard/tests/test_utils/LineVerifierElem.java create mode 100644 vcard/tests/src/com/android/vcard/tests/test_utils/PropertyNode.java create mode 100644 vcard/tests/src/com/android/vcard/tests/test_utils/PropertyNodesVerifier.java create mode 100644 vcard/tests/src/com/android/vcard/tests/test_utils/PropertyNodesVerifierElem.java create mode 100644 vcard/tests/src/com/android/vcard/tests/test_utils/VCardVerifier.java create mode 100644 vcard/tests/src/com/android/vcard/tests/test_utils/VNode.java create mode 100644 vcard/tests/src/com/android/vcard/tests/test_utils/VNodeBuilder.java diff --git a/vcard/Android.mk b/vcard/Android.mk new file mode 100644 index 000000000..2bc17aa8c --- /dev/null +++ b/vcard/Android.mk @@ -0,0 +1,28 @@ +# Copyright (C) 2010 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. + +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE := com.android.vcard +LOCAL_SRC_FILES := $(call all-java-files-under, java) + +# Use google-common instead of android-common for using hidden code in telephony library. +# Use ext for using Quoted-Printable codec. +LOCAL_JAVA_LIBRARIES := google-common ext + +include $(BUILD_STATIC_JAVA_LIBRARY) + +# Build the test package. +include $(call all-makefiles-under, $(LOCAL_PATH)) diff --git a/vcard/java/com/android/vcard/JapaneseUtils.java b/vcard/java/com/android/vcard/JapaneseUtils.java new file mode 100644 index 000000000..5b4494469 --- /dev/null +++ b/vcard/java/com/android/vcard/JapaneseUtils.java @@ -0,0 +1,379 @@ +/* + * 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.vcard; + +import java.util.HashMap; +import java.util.Map; + +/** + * TextUtils especially for Japanese. + */ +/* package */ class JapaneseUtils { + static private final Map sHalfWidthMap = + new HashMap(); + + static { + sHalfWidthMap.put('\u3001', "\uFF64"); + sHalfWidthMap.put('\u3002', "\uFF61"); + sHalfWidthMap.put('\u300C', "\uFF62"); + sHalfWidthMap.put('\u300D', "\uFF63"); + sHalfWidthMap.put('\u301C', "~"); + sHalfWidthMap.put('\u3041', "\uFF67"); + sHalfWidthMap.put('\u3042', "\uFF71"); + sHalfWidthMap.put('\u3043', "\uFF68"); + sHalfWidthMap.put('\u3044', "\uFF72"); + sHalfWidthMap.put('\u3045', "\uFF69"); + sHalfWidthMap.put('\u3046', "\uFF73"); + sHalfWidthMap.put('\u3047', "\uFF6A"); + sHalfWidthMap.put('\u3048', "\uFF74"); + sHalfWidthMap.put('\u3049', "\uFF6B"); + sHalfWidthMap.put('\u304A', "\uFF75"); + sHalfWidthMap.put('\u304B', "\uFF76"); + sHalfWidthMap.put('\u304C', "\uFF76\uFF9E"); + sHalfWidthMap.put('\u304D', "\uFF77"); + sHalfWidthMap.put('\u304E', "\uFF77\uFF9E"); + sHalfWidthMap.put('\u304F', "\uFF78"); + sHalfWidthMap.put('\u3050', "\uFF78\uFF9E"); + sHalfWidthMap.put('\u3051', "\uFF79"); + sHalfWidthMap.put('\u3052', "\uFF79\uFF9E"); + sHalfWidthMap.put('\u3053', "\uFF7A"); + sHalfWidthMap.put('\u3054', "\uFF7A\uFF9E"); + sHalfWidthMap.put('\u3055', "\uFF7B"); + sHalfWidthMap.put('\u3056', "\uFF7B\uFF9E"); + sHalfWidthMap.put('\u3057', "\uFF7C"); + sHalfWidthMap.put('\u3058', "\uFF7C\uFF9E"); + sHalfWidthMap.put('\u3059', "\uFF7D"); + sHalfWidthMap.put('\u305A', "\uFF7D\uFF9E"); + sHalfWidthMap.put('\u305B', "\uFF7E"); + sHalfWidthMap.put('\u305C', "\uFF7E\uFF9E"); + sHalfWidthMap.put('\u305D', "\uFF7F"); + sHalfWidthMap.put('\u305E', "\uFF7F\uFF9E"); + sHalfWidthMap.put('\u305F', "\uFF80"); + sHalfWidthMap.put('\u3060', "\uFF80\uFF9E"); + sHalfWidthMap.put('\u3061', "\uFF81"); + sHalfWidthMap.put('\u3062', "\uFF81\uFF9E"); + sHalfWidthMap.put('\u3063', "\uFF6F"); + sHalfWidthMap.put('\u3064', "\uFF82"); + sHalfWidthMap.put('\u3065', "\uFF82\uFF9E"); + sHalfWidthMap.put('\u3066', "\uFF83"); + sHalfWidthMap.put('\u3067', "\uFF83\uFF9E"); + sHalfWidthMap.put('\u3068', "\uFF84"); + sHalfWidthMap.put('\u3069', "\uFF84\uFF9E"); + sHalfWidthMap.put('\u306A', "\uFF85"); + sHalfWidthMap.put('\u306B', "\uFF86"); + sHalfWidthMap.put('\u306C', "\uFF87"); + sHalfWidthMap.put('\u306D', "\uFF88"); + sHalfWidthMap.put('\u306E', "\uFF89"); + sHalfWidthMap.put('\u306F', "\uFF8A"); + sHalfWidthMap.put('\u3070', "\uFF8A\uFF9E"); + sHalfWidthMap.put('\u3071', "\uFF8A\uFF9F"); + sHalfWidthMap.put('\u3072', "\uFF8B"); + sHalfWidthMap.put('\u3073', "\uFF8B\uFF9E"); + sHalfWidthMap.put('\u3074', "\uFF8B\uFF9F"); + sHalfWidthMap.put('\u3075', "\uFF8C"); + sHalfWidthMap.put('\u3076', "\uFF8C\uFF9E"); + sHalfWidthMap.put('\u3077', "\uFF8C\uFF9F"); + sHalfWidthMap.put('\u3078', "\uFF8D"); + sHalfWidthMap.put('\u3079', "\uFF8D\uFF9E"); + sHalfWidthMap.put('\u307A', "\uFF8D\uFF9F"); + sHalfWidthMap.put('\u307B', "\uFF8E"); + sHalfWidthMap.put('\u307C', "\uFF8E\uFF9E"); + sHalfWidthMap.put('\u307D', "\uFF8E\uFF9F"); + sHalfWidthMap.put('\u307E', "\uFF8F"); + sHalfWidthMap.put('\u307F', "\uFF90"); + sHalfWidthMap.put('\u3080', "\uFF91"); + sHalfWidthMap.put('\u3081', "\uFF92"); + sHalfWidthMap.put('\u3082', "\uFF93"); + sHalfWidthMap.put('\u3083', "\uFF6C"); + sHalfWidthMap.put('\u3084', "\uFF94"); + sHalfWidthMap.put('\u3085', "\uFF6D"); + sHalfWidthMap.put('\u3086', "\uFF95"); + sHalfWidthMap.put('\u3087', "\uFF6E"); + sHalfWidthMap.put('\u3088', "\uFF96"); + sHalfWidthMap.put('\u3089', "\uFF97"); + sHalfWidthMap.put('\u308A', "\uFF98"); + sHalfWidthMap.put('\u308B', "\uFF99"); + sHalfWidthMap.put('\u308C', "\uFF9A"); + sHalfWidthMap.put('\u308D', "\uFF9B"); + sHalfWidthMap.put('\u308E', "\uFF9C"); + sHalfWidthMap.put('\u308F', "\uFF9C"); + sHalfWidthMap.put('\u3090', "\uFF72"); + sHalfWidthMap.put('\u3091', "\uFF74"); + sHalfWidthMap.put('\u3092', "\uFF66"); + sHalfWidthMap.put('\u3093', "\uFF9D"); + sHalfWidthMap.put('\u309B', "\uFF9E"); + sHalfWidthMap.put('\u309C', "\uFF9F"); + sHalfWidthMap.put('\u30A1', "\uFF67"); + sHalfWidthMap.put('\u30A2', "\uFF71"); + sHalfWidthMap.put('\u30A3', "\uFF68"); + sHalfWidthMap.put('\u30A4', "\uFF72"); + sHalfWidthMap.put('\u30A5', "\uFF69"); + sHalfWidthMap.put('\u30A6', "\uFF73"); + sHalfWidthMap.put('\u30A7', "\uFF6A"); + sHalfWidthMap.put('\u30A8', "\uFF74"); + sHalfWidthMap.put('\u30A9', "\uFF6B"); + sHalfWidthMap.put('\u30AA', "\uFF75"); + sHalfWidthMap.put('\u30AB', "\uFF76"); + sHalfWidthMap.put('\u30AC', "\uFF76\uFF9E"); + sHalfWidthMap.put('\u30AD', "\uFF77"); + sHalfWidthMap.put('\u30AE', "\uFF77\uFF9E"); + sHalfWidthMap.put('\u30AF', "\uFF78"); + sHalfWidthMap.put('\u30B0', "\uFF78\uFF9E"); + sHalfWidthMap.put('\u30B1', "\uFF79"); + sHalfWidthMap.put('\u30B2', "\uFF79\uFF9E"); + sHalfWidthMap.put('\u30B3', "\uFF7A"); + sHalfWidthMap.put('\u30B4', "\uFF7A\uFF9E"); + sHalfWidthMap.put('\u30B5', "\uFF7B"); + sHalfWidthMap.put('\u30B6', "\uFF7B\uFF9E"); + sHalfWidthMap.put('\u30B7', "\uFF7C"); + sHalfWidthMap.put('\u30B8', "\uFF7C\uFF9E"); + sHalfWidthMap.put('\u30B9', "\uFF7D"); + sHalfWidthMap.put('\u30BA', "\uFF7D\uFF9E"); + sHalfWidthMap.put('\u30BB', "\uFF7E"); + sHalfWidthMap.put('\u30BC', "\uFF7E\uFF9E"); + sHalfWidthMap.put('\u30BD', "\uFF7F"); + sHalfWidthMap.put('\u30BE', "\uFF7F\uFF9E"); + sHalfWidthMap.put('\u30BF', "\uFF80"); + sHalfWidthMap.put('\u30C0', "\uFF80\uFF9E"); + sHalfWidthMap.put('\u30C1', "\uFF81"); + sHalfWidthMap.put('\u30C2', "\uFF81\uFF9E"); + sHalfWidthMap.put('\u30C3', "\uFF6F"); + sHalfWidthMap.put('\u30C4', "\uFF82"); + sHalfWidthMap.put('\u30C5', "\uFF82\uFF9E"); + sHalfWidthMap.put('\u30C6', "\uFF83"); + sHalfWidthMap.put('\u30C7', "\uFF83\uFF9E"); + sHalfWidthMap.put('\u30C8', "\uFF84"); + sHalfWidthMap.put('\u30C9', "\uFF84\uFF9E"); + sHalfWidthMap.put('\u30CA', "\uFF85"); + sHalfWidthMap.put('\u30CB', "\uFF86"); + sHalfWidthMap.put('\u30CC', "\uFF87"); + sHalfWidthMap.put('\u30CD', "\uFF88"); + sHalfWidthMap.put('\u30CE', "\uFF89"); + sHalfWidthMap.put('\u30CF', "\uFF8A"); + sHalfWidthMap.put('\u30D0', "\uFF8A\uFF9E"); + sHalfWidthMap.put('\u30D1', "\uFF8A\uFF9F"); + sHalfWidthMap.put('\u30D2', "\uFF8B"); + sHalfWidthMap.put('\u30D3', "\uFF8B\uFF9E"); + sHalfWidthMap.put('\u30D4', "\uFF8B\uFF9F"); + sHalfWidthMap.put('\u30D5', "\uFF8C"); + sHalfWidthMap.put('\u30D6', "\uFF8C\uFF9E"); + sHalfWidthMap.put('\u30D7', "\uFF8C\uFF9F"); + sHalfWidthMap.put('\u30D8', "\uFF8D"); + sHalfWidthMap.put('\u30D9', "\uFF8D\uFF9E"); + sHalfWidthMap.put('\u30DA', "\uFF8D\uFF9F"); + sHalfWidthMap.put('\u30DB', "\uFF8E"); + sHalfWidthMap.put('\u30DC', "\uFF8E\uFF9E"); + sHalfWidthMap.put('\u30DD', "\uFF8E\uFF9F"); + sHalfWidthMap.put('\u30DE', "\uFF8F"); + sHalfWidthMap.put('\u30DF', "\uFF90"); + sHalfWidthMap.put('\u30E0', "\uFF91"); + sHalfWidthMap.put('\u30E1', "\uFF92"); + sHalfWidthMap.put('\u30E2', "\uFF93"); + sHalfWidthMap.put('\u30E3', "\uFF6C"); + sHalfWidthMap.put('\u30E4', "\uFF94"); + sHalfWidthMap.put('\u30E5', "\uFF6D"); + sHalfWidthMap.put('\u30E6', "\uFF95"); + sHalfWidthMap.put('\u30E7', "\uFF6E"); + sHalfWidthMap.put('\u30E8', "\uFF96"); + sHalfWidthMap.put('\u30E9', "\uFF97"); + sHalfWidthMap.put('\u30EA', "\uFF98"); + sHalfWidthMap.put('\u30EB', "\uFF99"); + sHalfWidthMap.put('\u30EC', "\uFF9A"); + sHalfWidthMap.put('\u30ED', "\uFF9B"); + sHalfWidthMap.put('\u30EE', "\uFF9C"); + sHalfWidthMap.put('\u30EF', "\uFF9C"); + sHalfWidthMap.put('\u30F0', "\uFF72"); + sHalfWidthMap.put('\u30F1', "\uFF74"); + sHalfWidthMap.put('\u30F2', "\uFF66"); + sHalfWidthMap.put('\u30F3', "\uFF9D"); + sHalfWidthMap.put('\u30F4', "\uFF73\uFF9E"); + sHalfWidthMap.put('\u30F5', "\uFF76"); + sHalfWidthMap.put('\u30F6', "\uFF79"); + sHalfWidthMap.put('\u30FB', "\uFF65"); + sHalfWidthMap.put('\u30FC', "\uFF70"); + sHalfWidthMap.put('\uFF01', "!"); + sHalfWidthMap.put('\uFF02', "\""); + sHalfWidthMap.put('\uFF03', "#"); + sHalfWidthMap.put('\uFF04', "$"); + sHalfWidthMap.put('\uFF05', "%"); + sHalfWidthMap.put('\uFF06', "&"); + sHalfWidthMap.put('\uFF07', "'"); + sHalfWidthMap.put('\uFF08', "("); + sHalfWidthMap.put('\uFF09', ")"); + sHalfWidthMap.put('\uFF0A', "*"); + sHalfWidthMap.put('\uFF0B', "+"); + sHalfWidthMap.put('\uFF0C', ","); + sHalfWidthMap.put('\uFF0D', "-"); + sHalfWidthMap.put('\uFF0E', "."); + sHalfWidthMap.put('\uFF0F', "/"); + sHalfWidthMap.put('\uFF10', "0"); + sHalfWidthMap.put('\uFF11', "1"); + sHalfWidthMap.put('\uFF12', "2"); + sHalfWidthMap.put('\uFF13', "3"); + sHalfWidthMap.put('\uFF14', "4"); + sHalfWidthMap.put('\uFF15', "5"); + sHalfWidthMap.put('\uFF16', "6"); + sHalfWidthMap.put('\uFF17', "7"); + sHalfWidthMap.put('\uFF18', "8"); + sHalfWidthMap.put('\uFF19', "9"); + sHalfWidthMap.put('\uFF1A', ":"); + sHalfWidthMap.put('\uFF1B', ";"); + sHalfWidthMap.put('\uFF1C', "<"); + sHalfWidthMap.put('\uFF1D', "="); + sHalfWidthMap.put('\uFF1E', ">"); + sHalfWidthMap.put('\uFF1F', "?"); + sHalfWidthMap.put('\uFF20', "@"); + sHalfWidthMap.put('\uFF21', "A"); + sHalfWidthMap.put('\uFF22', "B"); + sHalfWidthMap.put('\uFF23', "C"); + sHalfWidthMap.put('\uFF24', "D"); + sHalfWidthMap.put('\uFF25', "E"); + sHalfWidthMap.put('\uFF26', "F"); + sHalfWidthMap.put('\uFF27', "G"); + sHalfWidthMap.put('\uFF28', "H"); + sHalfWidthMap.put('\uFF29', "I"); + sHalfWidthMap.put('\uFF2A', "J"); + sHalfWidthMap.put('\uFF2B', "K"); + sHalfWidthMap.put('\uFF2C', "L"); + sHalfWidthMap.put('\uFF2D', "M"); + sHalfWidthMap.put('\uFF2E', "N"); + sHalfWidthMap.put('\uFF2F', "O"); + sHalfWidthMap.put('\uFF30', "P"); + sHalfWidthMap.put('\uFF31', "Q"); + sHalfWidthMap.put('\uFF32', "R"); + sHalfWidthMap.put('\uFF33', "S"); + sHalfWidthMap.put('\uFF34', "T"); + sHalfWidthMap.put('\uFF35', "U"); + sHalfWidthMap.put('\uFF36', "V"); + sHalfWidthMap.put('\uFF37', "W"); + sHalfWidthMap.put('\uFF38', "X"); + sHalfWidthMap.put('\uFF39', "Y"); + sHalfWidthMap.put('\uFF3A', "Z"); + sHalfWidthMap.put('\uFF3B', "["); + sHalfWidthMap.put('\uFF3C', "\\"); + sHalfWidthMap.put('\uFF3D', "]"); + sHalfWidthMap.put('\uFF3E', "^"); + sHalfWidthMap.put('\uFF3F', "_"); + sHalfWidthMap.put('\uFF41', "a"); + sHalfWidthMap.put('\uFF42', "b"); + sHalfWidthMap.put('\uFF43', "c"); + sHalfWidthMap.put('\uFF44', "d"); + sHalfWidthMap.put('\uFF45', "e"); + sHalfWidthMap.put('\uFF46', "f"); + sHalfWidthMap.put('\uFF47', "g"); + sHalfWidthMap.put('\uFF48', "h"); + sHalfWidthMap.put('\uFF49', "i"); + sHalfWidthMap.put('\uFF4A', "j"); + sHalfWidthMap.put('\uFF4B', "k"); + sHalfWidthMap.put('\uFF4C', "l"); + sHalfWidthMap.put('\uFF4D', "m"); + sHalfWidthMap.put('\uFF4E', "n"); + sHalfWidthMap.put('\uFF4F', "o"); + sHalfWidthMap.put('\uFF50', "p"); + sHalfWidthMap.put('\uFF51', "q"); + sHalfWidthMap.put('\uFF52', "r"); + sHalfWidthMap.put('\uFF53', "s"); + sHalfWidthMap.put('\uFF54', "t"); + sHalfWidthMap.put('\uFF55', "u"); + sHalfWidthMap.put('\uFF56', "v"); + sHalfWidthMap.put('\uFF57', "w"); + sHalfWidthMap.put('\uFF58', "x"); + sHalfWidthMap.put('\uFF59', "y"); + sHalfWidthMap.put('\uFF5A', "z"); + sHalfWidthMap.put('\uFF5B', "{"); + sHalfWidthMap.put('\uFF5C', "|"); + sHalfWidthMap.put('\uFF5D', "}"); + sHalfWidthMap.put('\uFF5E', "~"); + sHalfWidthMap.put('\uFF61', "\uFF61"); + sHalfWidthMap.put('\uFF62', "\uFF62"); + sHalfWidthMap.put('\uFF63', "\uFF63"); + sHalfWidthMap.put('\uFF64', "\uFF64"); + sHalfWidthMap.put('\uFF65', "\uFF65"); + sHalfWidthMap.put('\uFF66', "\uFF66"); + sHalfWidthMap.put('\uFF67', "\uFF67"); + sHalfWidthMap.put('\uFF68', "\uFF68"); + sHalfWidthMap.put('\uFF69', "\uFF69"); + sHalfWidthMap.put('\uFF6A', "\uFF6A"); + sHalfWidthMap.put('\uFF6B', "\uFF6B"); + sHalfWidthMap.put('\uFF6C', "\uFF6C"); + sHalfWidthMap.put('\uFF6D', "\uFF6D"); + sHalfWidthMap.put('\uFF6E', "\uFF6E"); + sHalfWidthMap.put('\uFF6F', "\uFF6F"); + sHalfWidthMap.put('\uFF70', "\uFF70"); + sHalfWidthMap.put('\uFF71', "\uFF71"); + sHalfWidthMap.put('\uFF72', "\uFF72"); + sHalfWidthMap.put('\uFF73', "\uFF73"); + sHalfWidthMap.put('\uFF74', "\uFF74"); + sHalfWidthMap.put('\uFF75', "\uFF75"); + sHalfWidthMap.put('\uFF76', "\uFF76"); + sHalfWidthMap.put('\uFF77', "\uFF77"); + sHalfWidthMap.put('\uFF78', "\uFF78"); + sHalfWidthMap.put('\uFF79', "\uFF79"); + sHalfWidthMap.put('\uFF7A', "\uFF7A"); + sHalfWidthMap.put('\uFF7B', "\uFF7B"); + sHalfWidthMap.put('\uFF7C', "\uFF7C"); + sHalfWidthMap.put('\uFF7D', "\uFF7D"); + sHalfWidthMap.put('\uFF7E', "\uFF7E"); + sHalfWidthMap.put('\uFF7F', "\uFF7F"); + sHalfWidthMap.put('\uFF80', "\uFF80"); + sHalfWidthMap.put('\uFF81', "\uFF81"); + sHalfWidthMap.put('\uFF82', "\uFF82"); + sHalfWidthMap.put('\uFF83', "\uFF83"); + sHalfWidthMap.put('\uFF84', "\uFF84"); + sHalfWidthMap.put('\uFF85', "\uFF85"); + sHalfWidthMap.put('\uFF86', "\uFF86"); + sHalfWidthMap.put('\uFF87', "\uFF87"); + sHalfWidthMap.put('\uFF88', "\uFF88"); + sHalfWidthMap.put('\uFF89', "\uFF89"); + sHalfWidthMap.put('\uFF8A', "\uFF8A"); + sHalfWidthMap.put('\uFF8B', "\uFF8B"); + sHalfWidthMap.put('\uFF8C', "\uFF8C"); + sHalfWidthMap.put('\uFF8D', "\uFF8D"); + sHalfWidthMap.put('\uFF8E', "\uFF8E"); + sHalfWidthMap.put('\uFF8F', "\uFF8F"); + sHalfWidthMap.put('\uFF90', "\uFF90"); + sHalfWidthMap.put('\uFF91', "\uFF91"); + sHalfWidthMap.put('\uFF92', "\uFF92"); + sHalfWidthMap.put('\uFF93', "\uFF93"); + sHalfWidthMap.put('\uFF94', "\uFF94"); + sHalfWidthMap.put('\uFF95', "\uFF95"); + sHalfWidthMap.put('\uFF96', "\uFF96"); + sHalfWidthMap.put('\uFF97', "\uFF97"); + sHalfWidthMap.put('\uFF98', "\uFF98"); + sHalfWidthMap.put('\uFF99', "\uFF99"); + sHalfWidthMap.put('\uFF9A', "\uFF9A"); + sHalfWidthMap.put('\uFF9B', "\uFF9B"); + sHalfWidthMap.put('\uFF9C', "\uFF9C"); + sHalfWidthMap.put('\uFF9D', "\uFF9D"); + sHalfWidthMap.put('\uFF9E', "\uFF9E"); + sHalfWidthMap.put('\uFF9F', "\uFF9F"); + sHalfWidthMap.put('\uFFE5', "\u005C\u005C"); + } + + /** + * Returns half-width version of that character if possible. Returns null if not possible + * @param ch input character + * @return CharSequence object if the mapping for ch exists. Return null otherwise. + */ + public static String tryGetHalfWidthText(final char ch) { + if (sHalfWidthMap.containsKey(ch)) { + return sHalfWidthMap.get(ch); + } else { + return null; + } + } +} diff --git a/vcard/java/com/android/vcard/VCardBuilder.java b/vcard/java/com/android/vcard/VCardBuilder.java new file mode 100644 index 000000000..6ef9adad9 --- /dev/null +++ b/vcard/java/com/android/vcard/VCardBuilder.java @@ -0,0 +1,1996 @@ +/* + * 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.vcard; + +import android.content.ContentValues; +import android.provider.ContactsContract.CommonDataKinds.Email; +import android.provider.ContactsContract.CommonDataKinds.Event; +import android.provider.ContactsContract.CommonDataKinds.Im; +import android.provider.ContactsContract.CommonDataKinds.Nickname; +import android.provider.ContactsContract.CommonDataKinds.Note; +import android.provider.ContactsContract.CommonDataKinds.Organization; +import android.provider.ContactsContract.CommonDataKinds.Phone; +import android.provider.ContactsContract.CommonDataKinds.Photo; +import android.provider.ContactsContract.CommonDataKinds.Relation; +import android.provider.ContactsContract.CommonDataKinds.StructuredName; +import android.provider.ContactsContract.CommonDataKinds.StructuredPostal; +import android.provider.ContactsContract.CommonDataKinds.Website; +import android.telephony.PhoneNumberUtils; +import android.text.TextUtils; +import android.util.Base64; +import android.util.CharsetUtils; +import android.util.Log; + +import java.io.UnsupportedEncodingException; +import java.nio.charset.UnsupportedCharsetException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + *

+ * The class which lets users create their own vCard String. Typical usage is as follows: + *

+ *
final VCardBuilder builder = new VCardBuilder(vcardType);
+ * builder.appendNameProperties(contentValuesListMap.get(StructuredName.CONTENT_ITEM_TYPE))
+ *     .appendNickNames(contentValuesListMap.get(Nickname.CONTENT_ITEM_TYPE))
+ *     .appendPhones(contentValuesListMap.get(Phone.CONTENT_ITEM_TYPE))
+ *     .appendEmails(contentValuesListMap.get(Email.CONTENT_ITEM_TYPE))
+ *     .appendPostals(contentValuesListMap.get(StructuredPostal.CONTENT_ITEM_TYPE))
+ *     .appendOrganizations(contentValuesListMap.get(Organization.CONTENT_ITEM_TYPE))
+ *     .appendWebsites(contentValuesListMap.get(Website.CONTENT_ITEM_TYPE))
+ *     .appendPhotos(contentValuesListMap.get(Photo.CONTENT_ITEM_TYPE))
+ *     .appendNotes(contentValuesListMap.get(Note.CONTENT_ITEM_TYPE))
+ *     .appendEvents(contentValuesListMap.get(Event.CONTENT_ITEM_TYPE))
+ *     .appendIms(contentValuesListMap.get(Im.CONTENT_ITEM_TYPE))
+ *     .appendRelation(contentValuesListMap.get(Relation.CONTENT_ITEM_TYPE));
+ * return builder.toString();
+ */ +public class VCardBuilder { + private static final String LOG_TAG = "VCardBuilder"; + + // If you add the other element, please check all the columns are able to be + // converted to String. + // + // e.g. BLOB is not what we can handle here now. + private static final Set sAllowedAndroidPropertySet = + Collections.unmodifiableSet(new HashSet(Arrays.asList( + Nickname.CONTENT_ITEM_TYPE, Event.CONTENT_ITEM_TYPE, + Relation.CONTENT_ITEM_TYPE))); + + public static final int DEFAULT_PHONE_TYPE = Phone.TYPE_HOME; + public static final int DEFAULT_POSTAL_TYPE = StructuredPostal.TYPE_HOME; + public static final int DEFAULT_EMAIL_TYPE = Email.TYPE_OTHER; + + private static final String VCARD_DATA_VCARD = "VCARD"; + private static final String VCARD_DATA_PUBLIC = "PUBLIC"; + + private static final String VCARD_PARAM_SEPARATOR = ";"; + private static final String VCARD_END_OF_LINE = "\r\n"; + private static final String VCARD_DATA_SEPARATOR = ":"; + private static final String VCARD_ITEM_SEPARATOR = ";"; + private static final String VCARD_WS = " "; + private static final String VCARD_PARAM_EQUAL = "="; + + private static final String VCARD_PARAM_ENCODING_QP = + "ENCODING=" + VCardConstants.PARAM_ENCODING_QP; + private static final String VCARD_PARAM_ENCODING_BASE64_V21 = + "ENCODING=" + VCardConstants.PARAM_ENCODING_BASE64; + private static final String VCARD_PARAM_ENCODING_BASE64_V30 = + "ENCODING=" + VCardConstants.PARAM_ENCODING_B; + + private static final String SHIFT_JIS = "SHIFT_JIS"; + + private final int mVCardType; + + private final boolean mIsV30; + private final boolean mIsJapaneseMobilePhone; + private final boolean mOnlyOneNoteFieldIsAvailable; + private final boolean mIsDoCoMo; + private final boolean mShouldUseQuotedPrintable; + private final boolean mUsesAndroidProperty; + private final boolean mUsesDefactProperty; + private final boolean mAppendTypeParamName; + private final boolean mRefrainsQPToNameProperties; + private final boolean mNeedsToConvertPhoneticString; + + private final boolean mShouldAppendCharsetParam; + + private final String mCharset; + private final String mVCardCharsetParameter; + + private StringBuilder mBuilder; + private boolean mEndAppended; + + public VCardBuilder(final int vcardType) { + // Default charset should be used + this(vcardType, null); + } + + /** + * @param vcardType + * @param charset If null, we use default charset for export. + */ + public VCardBuilder(final int vcardType, String charset) { + mVCardType = vcardType; + + mIsV30 = VCardConfig.isV30(vcardType); + mShouldUseQuotedPrintable = VCardConfig.shouldUseQuotedPrintable(vcardType); + mIsDoCoMo = VCardConfig.isDoCoMo(vcardType); + mIsJapaneseMobilePhone = VCardConfig.needsToConvertPhoneticString(vcardType); + mOnlyOneNoteFieldIsAvailable = VCardConfig.onlyOneNoteFieldIsAvailable(vcardType); + mUsesAndroidProperty = VCardConfig.usesAndroidSpecificProperty(vcardType); + mUsesDefactProperty = VCardConfig.usesDefactProperty(vcardType); + mRefrainsQPToNameProperties = VCardConfig.shouldRefrainQPToNameProperties(vcardType); + mAppendTypeParamName = VCardConfig.appendTypeParamName(vcardType); + mNeedsToConvertPhoneticString = VCardConfig.needsToConvertPhoneticString(vcardType); + + // vCard 2.1 requires charset. + // vCard 3.0 does not allow it but we found some devices use it to determine + // the exact charset. + // We currently append it only when charset other than UTF_8 is used. + mShouldAppendCharsetParam = !(mIsV30 && "UTF-8".equalsIgnoreCase(charset)); + + if (VCardConfig.isDoCoMo(vcardType)) { + if (!SHIFT_JIS.equalsIgnoreCase(charset)) { + Log.w(LOG_TAG, + "The charset \"" + charset + "\" is used while " + + SHIFT_JIS + " is needed to be used."); + if (TextUtils.isEmpty(charset)) { + mCharset = SHIFT_JIS; + } else { + try { + charset = CharsetUtils.charsetForVendor(charset).name(); + } catch (UnsupportedCharsetException e) { + Log.i(LOG_TAG, + "Career-specific \"" + charset + "\" was not found (as usual). " + + "Use it as is."); + } + mCharset = charset; + } + } else { + if (mIsDoCoMo) { + try { + charset = CharsetUtils.charsetForVendor(SHIFT_JIS, "docomo").name(); + } catch (UnsupportedCharsetException e) { + Log.e(LOG_TAG, + "DoCoMo-specific SHIFT_JIS was not found. " + + "Use SHIFT_JIS as is."); + charset = SHIFT_JIS; + } + } else { + try { + charset = CharsetUtils.charsetForVendor(SHIFT_JIS).name(); + } catch (UnsupportedCharsetException e) { + Log.e(LOG_TAG, + "Career-specific SHIFT_JIS was not found. " + + "Use SHIFT_JIS as is."); + charset = SHIFT_JIS; + } + } + mCharset = charset; + } + mVCardCharsetParameter = "CHARSET=" + SHIFT_JIS; + } else { + if (TextUtils.isEmpty(charset)) { + Log.i(LOG_TAG, + "Use the charset \"" + VCardConfig.DEFAULT_EXPORT_CHARSET + + "\" for export."); + mCharset = VCardConfig.DEFAULT_EXPORT_CHARSET; + mVCardCharsetParameter = "CHARSET=" + VCardConfig.DEFAULT_EXPORT_CHARSET; + } else { + try { + charset = CharsetUtils.charsetForVendor(charset).name(); + } catch (UnsupportedCharsetException e) { + Log.i(LOG_TAG, + "Career-specific \"" + charset + "\" was not found (as usual). " + + "Use it as is."); + } + mCharset = charset; + mVCardCharsetParameter = "CHARSET=" + charset; + } + } + clear(); + } + + public void clear() { + mBuilder = new StringBuilder(); + mEndAppended = false; + appendLine(VCardConstants.PROPERTY_BEGIN, VCARD_DATA_VCARD); + if (mIsV30) { + appendLine(VCardConstants.PROPERTY_VERSION, VCardConstants.VERSION_V30); + } else { + appendLine(VCardConstants.PROPERTY_VERSION, VCardConstants.VERSION_V21); + } + } + + private boolean containsNonEmptyName(final ContentValues contentValues) { + final String familyName = contentValues.getAsString(StructuredName.FAMILY_NAME); + final String middleName = contentValues.getAsString(StructuredName.MIDDLE_NAME); + final String givenName = contentValues.getAsString(StructuredName.GIVEN_NAME); + final String prefix = contentValues.getAsString(StructuredName.PREFIX); + final String suffix = contentValues.getAsString(StructuredName.SUFFIX); + final String phoneticFamilyName = + contentValues.getAsString(StructuredName.PHONETIC_FAMILY_NAME); + final String phoneticMiddleName = + contentValues.getAsString(StructuredName.PHONETIC_MIDDLE_NAME); + final String phoneticGivenName = + contentValues.getAsString(StructuredName.PHONETIC_GIVEN_NAME); + final String displayName = contentValues.getAsString(StructuredName.DISPLAY_NAME); + return !(TextUtils.isEmpty(familyName) && TextUtils.isEmpty(middleName) && + TextUtils.isEmpty(givenName) && TextUtils.isEmpty(prefix) && + TextUtils.isEmpty(suffix) && TextUtils.isEmpty(phoneticFamilyName) && + TextUtils.isEmpty(phoneticMiddleName) && TextUtils.isEmpty(phoneticGivenName) && + TextUtils.isEmpty(displayName)); + } + + private ContentValues getPrimaryContentValue(final List contentValuesList) { + ContentValues primaryContentValues = null; + ContentValues subprimaryContentValues = null; + for (ContentValues contentValues : contentValuesList) { + if (contentValues == null){ + continue; + } + Integer isSuperPrimary = contentValues.getAsInteger(StructuredName.IS_SUPER_PRIMARY); + if (isSuperPrimary != null && isSuperPrimary > 0) { + // We choose "super primary" ContentValues. + primaryContentValues = contentValues; + break; + } else if (primaryContentValues == null) { + // We choose the first "primary" ContentValues + // if "super primary" ContentValues does not exist. + final Integer isPrimary = contentValues.getAsInteger(StructuredName.IS_PRIMARY); + if (isPrimary != null && isPrimary > 0 && + containsNonEmptyName(contentValues)) { + primaryContentValues = contentValues; + // Do not break, since there may be ContentValues with "super primary" + // afterword. + } else if (subprimaryContentValues == null && + containsNonEmptyName(contentValues)) { + subprimaryContentValues = contentValues; + } + } + } + + if (primaryContentValues == null) { + if (subprimaryContentValues != null) { + // We choose the first ContentValues if any "primary" ContentValues does not exist. + primaryContentValues = subprimaryContentValues; + } else { + Log.e(LOG_TAG, "All ContentValues given from database is empty."); + primaryContentValues = new ContentValues(); + } + } + + return primaryContentValues; + } + + /** + * For safety, we'll emit just one value around StructuredName, as external importers + * may get confused with multiple "N", "FN", etc. properties, though it is valid in + * vCard spec. + */ + public VCardBuilder appendNameProperties(final List contentValuesList) { + if (contentValuesList == null || contentValuesList.isEmpty()) { + if (mIsDoCoMo) { + appendLine(VCardConstants.PROPERTY_N, ""); + } else if (mIsV30) { + // vCard 3.0 requires "N" and "FN" properties. + appendLine(VCardConstants.PROPERTY_N, ""); + appendLine(VCardConstants.PROPERTY_FN, ""); + } + return this; + } + + final ContentValues contentValues = getPrimaryContentValue(contentValuesList); + final String familyName = contentValues.getAsString(StructuredName.FAMILY_NAME); + final String middleName = contentValues.getAsString(StructuredName.MIDDLE_NAME); + final String givenName = contentValues.getAsString(StructuredName.GIVEN_NAME); + final String prefix = contentValues.getAsString(StructuredName.PREFIX); + final String suffix = contentValues.getAsString(StructuredName.SUFFIX); + final String displayName = contentValues.getAsString(StructuredName.DISPLAY_NAME); + + if (!TextUtils.isEmpty(familyName) || !TextUtils.isEmpty(givenName)) { + final boolean reallyAppendCharsetParameterToName = + shouldAppendCharsetParam(familyName, givenName, middleName, prefix, suffix); + final boolean reallyUseQuotedPrintableToName = + (!mRefrainsQPToNameProperties && + !(VCardUtils.containsOnlyNonCrLfPrintableAscii(familyName) && + VCardUtils.containsOnlyNonCrLfPrintableAscii(givenName) && + VCardUtils.containsOnlyNonCrLfPrintableAscii(middleName) && + VCardUtils.containsOnlyNonCrLfPrintableAscii(prefix) && + VCardUtils.containsOnlyNonCrLfPrintableAscii(suffix))); + + final String formattedName; + if (!TextUtils.isEmpty(displayName)) { + formattedName = displayName; + } else { + formattedName = VCardUtils.constructNameFromElements( + VCardConfig.getNameOrderType(mVCardType), + familyName, middleName, givenName, prefix, suffix); + } + final boolean reallyAppendCharsetParameterToFN = + shouldAppendCharsetParam(formattedName); + final boolean reallyUseQuotedPrintableToFN = + !mRefrainsQPToNameProperties && + !VCardUtils.containsOnlyNonCrLfPrintableAscii(formattedName); + + final String encodedFamily; + final String encodedGiven; + final String encodedMiddle; + final String encodedPrefix; + final String encodedSuffix; + if (reallyUseQuotedPrintableToName) { + encodedFamily = encodeQuotedPrintable(familyName); + encodedGiven = encodeQuotedPrintable(givenName); + encodedMiddle = encodeQuotedPrintable(middleName); + encodedPrefix = encodeQuotedPrintable(prefix); + encodedSuffix = encodeQuotedPrintable(suffix); + } else { + encodedFamily = escapeCharacters(familyName); + encodedGiven = escapeCharacters(givenName); + encodedMiddle = escapeCharacters(middleName); + encodedPrefix = escapeCharacters(prefix); + encodedSuffix = escapeCharacters(suffix); + } + + final String encodedFormattedname = + (reallyUseQuotedPrintableToFN ? + encodeQuotedPrintable(formattedName) : escapeCharacters(formattedName)); + + mBuilder.append(VCardConstants.PROPERTY_N); + if (mIsDoCoMo) { + if (reallyAppendCharsetParameterToName) { + mBuilder.append(VCARD_PARAM_SEPARATOR); + mBuilder.append(mVCardCharsetParameter); + } + if (reallyUseQuotedPrintableToName) { + mBuilder.append(VCARD_PARAM_SEPARATOR); + mBuilder.append(VCARD_PARAM_ENCODING_QP); + } + mBuilder.append(VCARD_DATA_SEPARATOR); + // DoCoMo phones require that all the elements in the "family name" field. + mBuilder.append(formattedName); + mBuilder.append(VCARD_ITEM_SEPARATOR); + mBuilder.append(VCARD_ITEM_SEPARATOR); + mBuilder.append(VCARD_ITEM_SEPARATOR); + mBuilder.append(VCARD_ITEM_SEPARATOR); + } else { + if (reallyAppendCharsetParameterToName) { + mBuilder.append(VCARD_PARAM_SEPARATOR); + mBuilder.append(mVCardCharsetParameter); + } + if (reallyUseQuotedPrintableToName) { + mBuilder.append(VCARD_PARAM_SEPARATOR); + mBuilder.append(VCARD_PARAM_ENCODING_QP); + } + mBuilder.append(VCARD_DATA_SEPARATOR); + mBuilder.append(encodedFamily); + mBuilder.append(VCARD_ITEM_SEPARATOR); + mBuilder.append(encodedGiven); + mBuilder.append(VCARD_ITEM_SEPARATOR); + mBuilder.append(encodedMiddle); + mBuilder.append(VCARD_ITEM_SEPARATOR); + mBuilder.append(encodedPrefix); + mBuilder.append(VCARD_ITEM_SEPARATOR); + mBuilder.append(encodedSuffix); + } + mBuilder.append(VCARD_END_OF_LINE); + + // FN property + mBuilder.append(VCardConstants.PROPERTY_FN); + if (reallyAppendCharsetParameterToFN) { + mBuilder.append(VCARD_PARAM_SEPARATOR); + mBuilder.append(mVCardCharsetParameter); + } + if (reallyUseQuotedPrintableToFN) { + mBuilder.append(VCARD_PARAM_SEPARATOR); + mBuilder.append(VCARD_PARAM_ENCODING_QP); + } + mBuilder.append(VCARD_DATA_SEPARATOR); + mBuilder.append(encodedFormattedname); + mBuilder.append(VCARD_END_OF_LINE); + } else if (!TextUtils.isEmpty(displayName)) { + final boolean reallyUseQuotedPrintableToDisplayName = + (!mRefrainsQPToNameProperties && + !VCardUtils.containsOnlyNonCrLfPrintableAscii(displayName)); + final String encodedDisplayName = + reallyUseQuotedPrintableToDisplayName ? + encodeQuotedPrintable(displayName) : + escapeCharacters(displayName); + + mBuilder.append(VCardConstants.PROPERTY_N); + if (shouldAppendCharsetParam(displayName)) { + mBuilder.append(VCARD_PARAM_SEPARATOR); + mBuilder.append(mVCardCharsetParameter); + } + if (reallyUseQuotedPrintableToDisplayName) { + mBuilder.append(VCARD_PARAM_SEPARATOR); + mBuilder.append(VCARD_PARAM_ENCODING_QP); + } + mBuilder.append(VCARD_DATA_SEPARATOR); + mBuilder.append(encodedDisplayName); + mBuilder.append(VCARD_ITEM_SEPARATOR); + mBuilder.append(VCARD_ITEM_SEPARATOR); + mBuilder.append(VCARD_ITEM_SEPARATOR); + mBuilder.append(VCARD_ITEM_SEPARATOR); + mBuilder.append(VCARD_END_OF_LINE); + mBuilder.append(VCardConstants.PROPERTY_FN); + + // Note: "CHARSET" param is not allowed in vCard 3.0, but we may add it + // when it would be useful or necessary for external importers, + // assuming the external importer allows this vioration of the spec. + if (shouldAppendCharsetParam(displayName)) { + mBuilder.append(VCARD_PARAM_SEPARATOR); + mBuilder.append(mVCardCharsetParameter); + } + mBuilder.append(VCARD_DATA_SEPARATOR); + mBuilder.append(encodedDisplayName); + mBuilder.append(VCARD_END_OF_LINE); + } else if (mIsV30) { + // vCard 3.0 specification requires these fields. + appendLine(VCardConstants.PROPERTY_N, ""); + appendLine(VCardConstants.PROPERTY_FN, ""); + } else if (mIsDoCoMo) { + appendLine(VCardConstants.PROPERTY_N, ""); + } + + appendPhoneticNameFields(contentValues); + return this; + } + + private void appendPhoneticNameFields(final ContentValues contentValues) { + final String phoneticFamilyName; + final String phoneticMiddleName; + final String phoneticGivenName; + { + final String tmpPhoneticFamilyName = + contentValues.getAsString(StructuredName.PHONETIC_FAMILY_NAME); + final String tmpPhoneticMiddleName = + contentValues.getAsString(StructuredName.PHONETIC_MIDDLE_NAME); + final String tmpPhoneticGivenName = + contentValues.getAsString(StructuredName.PHONETIC_GIVEN_NAME); + if (mNeedsToConvertPhoneticString) { + phoneticFamilyName = VCardUtils.toHalfWidthString(tmpPhoneticFamilyName); + phoneticMiddleName = VCardUtils.toHalfWidthString(tmpPhoneticMiddleName); + phoneticGivenName = VCardUtils.toHalfWidthString(tmpPhoneticGivenName); + } else { + phoneticFamilyName = tmpPhoneticFamilyName; + phoneticMiddleName = tmpPhoneticMiddleName; + phoneticGivenName = tmpPhoneticGivenName; + } + } + + if (TextUtils.isEmpty(phoneticFamilyName) + && TextUtils.isEmpty(phoneticMiddleName) + && TextUtils.isEmpty(phoneticGivenName)) { + if (mIsDoCoMo) { + mBuilder.append(VCardConstants.PROPERTY_SOUND); + mBuilder.append(VCARD_PARAM_SEPARATOR); + mBuilder.append(VCardConstants.PARAM_TYPE_X_IRMC_N); + mBuilder.append(VCARD_DATA_SEPARATOR); + mBuilder.append(VCARD_ITEM_SEPARATOR); + mBuilder.append(VCARD_ITEM_SEPARATOR); + mBuilder.append(VCARD_ITEM_SEPARATOR); + mBuilder.append(VCARD_ITEM_SEPARATOR); + mBuilder.append(VCARD_END_OF_LINE); + } + return; + } + + // Try to emit the field(s) related to phonetic name. + if (mIsV30) { + final String sortString = VCardUtils + .constructNameFromElements(mVCardType, + phoneticFamilyName, phoneticMiddleName, phoneticGivenName); + mBuilder.append(VCardConstants.PROPERTY_SORT_STRING); + if (shouldAppendCharsetParam(sortString)) { + mBuilder.append(VCARD_PARAM_SEPARATOR); + mBuilder.append(mVCardCharsetParameter); + } + mBuilder.append(VCARD_DATA_SEPARATOR); + mBuilder.append(escapeCharacters(sortString)); + mBuilder.append(VCARD_END_OF_LINE); + } else if (mIsJapaneseMobilePhone) { + // Note: There is no appropriate property for expressing + // phonetic name (Yomigana in Japanese) in vCard 2.1, while there is in + // vCard 3.0 (SORT-STRING). + // We use DoCoMo's way when the device is Japanese one since it is already + // supported by a lot of Japanese mobile phones. + // This is "X-" property, so any parser hopefully would not get + // confused with this. + // + // Also, DoCoMo's specification requires vCard composer to use just the first + // column. + // i.e. + // good: SOUND;X-IRMC-N:Miyakawa Daisuke;;;; + // bad : SOUND;X-IRMC-N:Miyakawa;Daisuke;;; + mBuilder.append(VCardConstants.PROPERTY_SOUND); + mBuilder.append(VCARD_PARAM_SEPARATOR); + mBuilder.append(VCardConstants.PARAM_TYPE_X_IRMC_N); + + boolean reallyUseQuotedPrintable = + (!mRefrainsQPToNameProperties + && !(VCardUtils.containsOnlyNonCrLfPrintableAscii( + phoneticFamilyName) + && VCardUtils.containsOnlyNonCrLfPrintableAscii( + phoneticMiddleName) + && VCardUtils.containsOnlyNonCrLfPrintableAscii( + phoneticGivenName))); + + final String encodedPhoneticFamilyName; + final String encodedPhoneticMiddleName; + final String encodedPhoneticGivenName; + if (reallyUseQuotedPrintable) { + encodedPhoneticFamilyName = encodeQuotedPrintable(phoneticFamilyName); + encodedPhoneticMiddleName = encodeQuotedPrintable(phoneticMiddleName); + encodedPhoneticGivenName = encodeQuotedPrintable(phoneticGivenName); + } else { + encodedPhoneticFamilyName = escapeCharacters(phoneticFamilyName); + encodedPhoneticMiddleName = escapeCharacters(phoneticMiddleName); + encodedPhoneticGivenName = escapeCharacters(phoneticGivenName); + } + + if (shouldAppendCharsetParam(encodedPhoneticFamilyName, + encodedPhoneticMiddleName, encodedPhoneticGivenName)) { + mBuilder.append(VCARD_PARAM_SEPARATOR); + mBuilder.append(mVCardCharsetParameter); + } + mBuilder.append(VCARD_DATA_SEPARATOR); + { + boolean first = true; + if (!TextUtils.isEmpty(encodedPhoneticFamilyName)) { + mBuilder.append(encodedPhoneticFamilyName); + first = false; + } + if (!TextUtils.isEmpty(encodedPhoneticMiddleName)) { + if (first) { + first = false; + } else { + mBuilder.append(' '); + } + mBuilder.append(encodedPhoneticMiddleName); + } + if (!TextUtils.isEmpty(encodedPhoneticGivenName)) { + if (!first) { + mBuilder.append(' '); + } + mBuilder.append(encodedPhoneticGivenName); + } + } + mBuilder.append(VCARD_ITEM_SEPARATOR); // family;given + mBuilder.append(VCARD_ITEM_SEPARATOR); // given;middle + mBuilder.append(VCARD_ITEM_SEPARATOR); // middle;prefix + mBuilder.append(VCARD_ITEM_SEPARATOR); // prefix;suffix + mBuilder.append(VCARD_END_OF_LINE); + } + + if (mUsesDefactProperty) { + if (!TextUtils.isEmpty(phoneticGivenName)) { + final boolean reallyUseQuotedPrintable = + (mShouldUseQuotedPrintable && + !VCardUtils.containsOnlyNonCrLfPrintableAscii(phoneticGivenName)); + final String encodedPhoneticGivenName; + if (reallyUseQuotedPrintable) { + encodedPhoneticGivenName = encodeQuotedPrintable(phoneticGivenName); + } else { + encodedPhoneticGivenName = escapeCharacters(phoneticGivenName); + } + mBuilder.append(VCardConstants.PROPERTY_X_PHONETIC_FIRST_NAME); + if (shouldAppendCharsetParam(phoneticGivenName)) { + mBuilder.append(VCARD_PARAM_SEPARATOR); + mBuilder.append(mVCardCharsetParameter); + } + if (reallyUseQuotedPrintable) { + mBuilder.append(VCARD_PARAM_SEPARATOR); + mBuilder.append(VCARD_PARAM_ENCODING_QP); + } + mBuilder.append(VCARD_DATA_SEPARATOR); + mBuilder.append(encodedPhoneticGivenName); + mBuilder.append(VCARD_END_OF_LINE); + } // if (!TextUtils.isEmpty(phoneticGivenName)) + if (!TextUtils.isEmpty(phoneticMiddleName)) { + final boolean reallyUseQuotedPrintable = + (mShouldUseQuotedPrintable && + !VCardUtils.containsOnlyNonCrLfPrintableAscii(phoneticMiddleName)); + final String encodedPhoneticMiddleName; + if (reallyUseQuotedPrintable) { + encodedPhoneticMiddleName = encodeQuotedPrintable(phoneticMiddleName); + } else { + encodedPhoneticMiddleName = escapeCharacters(phoneticMiddleName); + } + mBuilder.append(VCardConstants.PROPERTY_X_PHONETIC_MIDDLE_NAME); + if (shouldAppendCharsetParam(phoneticMiddleName)) { + mBuilder.append(VCARD_PARAM_SEPARATOR); + mBuilder.append(mVCardCharsetParameter); + } + if (reallyUseQuotedPrintable) { + mBuilder.append(VCARD_PARAM_SEPARATOR); + mBuilder.append(VCARD_PARAM_ENCODING_QP); + } + mBuilder.append(VCARD_DATA_SEPARATOR); + mBuilder.append(encodedPhoneticMiddleName); + mBuilder.append(VCARD_END_OF_LINE); + } // if (!TextUtils.isEmpty(phoneticGivenName)) + if (!TextUtils.isEmpty(phoneticFamilyName)) { + final boolean reallyUseQuotedPrintable = + (mShouldUseQuotedPrintable && + !VCardUtils.containsOnlyNonCrLfPrintableAscii(phoneticFamilyName)); + final String encodedPhoneticFamilyName; + if (reallyUseQuotedPrintable) { + encodedPhoneticFamilyName = encodeQuotedPrintable(phoneticFamilyName); + } else { + encodedPhoneticFamilyName = escapeCharacters(phoneticFamilyName); + } + mBuilder.append(VCardConstants.PROPERTY_X_PHONETIC_LAST_NAME); + if (shouldAppendCharsetParam(phoneticFamilyName)) { + mBuilder.append(VCARD_PARAM_SEPARATOR); + mBuilder.append(mVCardCharsetParameter); + } + if (reallyUseQuotedPrintable) { + mBuilder.append(VCARD_PARAM_SEPARATOR); + mBuilder.append(VCARD_PARAM_ENCODING_QP); + } + mBuilder.append(VCARD_DATA_SEPARATOR); + mBuilder.append(encodedPhoneticFamilyName); + mBuilder.append(VCARD_END_OF_LINE); + } // if (!TextUtils.isEmpty(phoneticFamilyName)) + } + } + + public VCardBuilder appendNickNames(final List contentValuesList) { + final boolean useAndroidProperty; + if (mIsV30) { + useAndroidProperty = false; + } else if (mUsesAndroidProperty) { + useAndroidProperty = true; + } else { + // There's no way to add this field. + return this; + } + if (contentValuesList != null) { + for (ContentValues contentValues : contentValuesList) { + final String nickname = contentValues.getAsString(Nickname.NAME); + if (TextUtils.isEmpty(nickname)) { + continue; + } + if (useAndroidProperty) { + appendAndroidSpecificProperty(Nickname.CONTENT_ITEM_TYPE, contentValues); + } else { + appendLineWithCharsetAndQPDetection(VCardConstants.PROPERTY_NICKNAME, nickname); + } + } + } + return this; + } + + public VCardBuilder appendPhones(final List contentValuesList) { + boolean phoneLineExists = false; + if (contentValuesList != null) { + Set phoneSet = new HashSet(); + for (ContentValues contentValues : contentValuesList) { + final Integer typeAsObject = contentValues.getAsInteger(Phone.TYPE); + final String label = contentValues.getAsString(Phone.LABEL); + final Integer isPrimaryAsInteger = contentValues.getAsInteger(Phone.IS_PRIMARY); + final boolean isPrimary = (isPrimaryAsInteger != null ? + (isPrimaryAsInteger > 0) : false); + String phoneNumber = contentValues.getAsString(Phone.NUMBER); + if (phoneNumber != null) { + phoneNumber = phoneNumber.trim(); + } + if (TextUtils.isEmpty(phoneNumber)) { + continue; + } + + // PAGER number needs unformatted "phone number". + final int type = (typeAsObject != null ? typeAsObject : DEFAULT_PHONE_TYPE); + if (type == Phone.TYPE_PAGER || + VCardConfig.refrainPhoneNumberFormatting(mVCardType)) { + phoneLineExists = true; + if (!phoneSet.contains(phoneNumber)) { + phoneSet.add(phoneNumber); + appendTelLine(type, label, phoneNumber, isPrimary); + } + } else { + final List phoneNumberList = splitAndTrimPhoneNumbers(phoneNumber); + if (phoneNumberList.isEmpty()) { + continue; + } + phoneLineExists = true; + for (String actualPhoneNumber : phoneNumberList) { + if (!phoneSet.contains(actualPhoneNumber)) { + final int format = VCardUtils.getPhoneNumberFormat(mVCardType); + final String formattedPhoneNumber = + PhoneNumberUtils.formatNumber(actualPhoneNumber, format); + phoneSet.add(actualPhoneNumber); + appendTelLine(type, label, formattedPhoneNumber, isPrimary); + } + } // for (String actualPhoneNumber : phoneNumberList) { + } + } + } + + if (!phoneLineExists && mIsDoCoMo) { + appendTelLine(Phone.TYPE_HOME, "", "", false); + } + + return this; + } + + /** + *

+ * Splits a given string expressing phone numbers into several strings, and remove + * unnecessary characters inside them. The size of a returned list becomes 1 when + * no split is needed. + *

+ *

+ * The given number "may" have several phone numbers when the contact entry is corrupted + * because of its original source. + * e.g. "111-222-3333 (Miami)\n444-555-6666 (Broward; 305-653-6796 (Miami)" + *

+ *

+ * This kind of "phone numbers" will not be created with Android vCard implementation, + * but we may encounter them if the source of the input data has already corrupted + * implementation. + *

+ *

+ * To handle this case, this method first splits its input into multiple parts + * (e.g. "111-222-3333 (Miami)", "444-555-6666 (Broward", and 305653-6796 (Miami)") and + * removes unnecessary strings like "(Miami)". + *

+ *

+ * Do not call this method when trimming is inappropriate for its receivers. + *

+ */ + private List splitAndTrimPhoneNumbers(final String phoneNumber) { + final List phoneList = new ArrayList(); + + StringBuilder builder = new StringBuilder(); + final int length = phoneNumber.length(); + for (int i = 0; i < length; i++) { + final char ch = phoneNumber.charAt(i); + if (Character.isDigit(ch) || ch == '+') { + builder.append(ch); + } else if ((ch == ';' || ch == '\n') && builder.length() > 0) { + phoneList.add(builder.toString()); + builder = new StringBuilder(); + } + } + if (builder.length() > 0) { + phoneList.add(builder.toString()); + } + + return phoneList; + } + + public VCardBuilder appendEmails(final List contentValuesList) { + boolean emailAddressExists = false; + if (contentValuesList != null) { + final Set addressSet = new HashSet(); + for (ContentValues contentValues : contentValuesList) { + String emailAddress = contentValues.getAsString(Email.DATA); + if (emailAddress != null) { + emailAddress = emailAddress.trim(); + } + if (TextUtils.isEmpty(emailAddress)) { + continue; + } + Integer typeAsObject = contentValues.getAsInteger(Email.TYPE); + final int type = (typeAsObject != null ? + typeAsObject : DEFAULT_EMAIL_TYPE); + final String label = contentValues.getAsString(Email.LABEL); + Integer isPrimaryAsInteger = contentValues.getAsInteger(Email.IS_PRIMARY); + final boolean isPrimary = (isPrimaryAsInteger != null ? + (isPrimaryAsInteger > 0) : false); + emailAddressExists = true; + if (!addressSet.contains(emailAddress)) { + addressSet.add(emailAddress); + appendEmailLine(type, label, emailAddress, isPrimary); + } + } + } + + if (!emailAddressExists && mIsDoCoMo) { + appendEmailLine(Email.TYPE_HOME, "", "", false); + } + + return this; + } + + public VCardBuilder appendPostals(final List contentValuesList) { + if (contentValuesList == null || contentValuesList.isEmpty()) { + if (mIsDoCoMo) { + mBuilder.append(VCardConstants.PROPERTY_ADR); + mBuilder.append(VCARD_PARAM_SEPARATOR); + mBuilder.append(VCardConstants.PARAM_TYPE_HOME); + mBuilder.append(VCARD_DATA_SEPARATOR); + mBuilder.append(VCARD_END_OF_LINE); + } + } else { + if (mIsDoCoMo) { + appendPostalsForDoCoMo(contentValuesList); + } else { + appendPostalsForGeneric(contentValuesList); + } + } + + return this; + } + + private static final Map sPostalTypePriorityMap; + + static { + sPostalTypePriorityMap = new HashMap(); + sPostalTypePriorityMap.put(StructuredPostal.TYPE_HOME, 0); + sPostalTypePriorityMap.put(StructuredPostal.TYPE_WORK, 1); + sPostalTypePriorityMap.put(StructuredPostal.TYPE_OTHER, 2); + sPostalTypePriorityMap.put(StructuredPostal.TYPE_CUSTOM, 3); + } + + /** + * Tries to append just one line. If there's no appropriate address + * information, append an empty line. + */ + private void appendPostalsForDoCoMo(final List contentValuesList) { + int currentPriority = Integer.MAX_VALUE; + int currentType = Integer.MAX_VALUE; + ContentValues currentContentValues = null; + for (final ContentValues contentValues : contentValuesList) { + if (contentValues == null) { + continue; + } + final Integer typeAsInteger = contentValues.getAsInteger(StructuredPostal.TYPE); + final Integer priorityAsInteger = sPostalTypePriorityMap.get(typeAsInteger); + final int priority = + (priorityAsInteger != null ? priorityAsInteger : Integer.MAX_VALUE); + if (priority < currentPriority) { + currentPriority = priority; + currentType = typeAsInteger; + currentContentValues = contentValues; + if (priority == 0) { + break; + } + } + } + + if (currentContentValues == null) { + Log.w(LOG_TAG, "Should not come here. Must have at least one postal data."); + return; + } + + final String label = currentContentValues.getAsString(StructuredPostal.LABEL); + appendPostalLine(currentType, label, currentContentValues, false, true); + } + + private void appendPostalsForGeneric(final List contentValuesList) { + for (final ContentValues contentValues : contentValuesList) { + if (contentValues == null) { + continue; + } + final Integer typeAsInteger = contentValues.getAsInteger(StructuredPostal.TYPE); + final int type = (typeAsInteger != null ? + typeAsInteger : DEFAULT_POSTAL_TYPE); + final String label = contentValues.getAsString(StructuredPostal.LABEL); + final Integer isPrimaryAsInteger = + contentValues.getAsInteger(StructuredPostal.IS_PRIMARY); + final boolean isPrimary = (isPrimaryAsInteger != null ? + (isPrimaryAsInteger > 0) : false); + appendPostalLine(type, label, contentValues, isPrimary, false); + } + } + + private static class PostalStruct { + final boolean reallyUseQuotedPrintable; + final boolean appendCharset; + final String addressData; + public PostalStruct(final boolean reallyUseQuotedPrintable, + final boolean appendCharset, final String addressData) { + this.reallyUseQuotedPrintable = reallyUseQuotedPrintable; + this.appendCharset = appendCharset; + this.addressData = addressData; + } + } + + /** + * @return null when there's no information available to construct the data. + */ + private PostalStruct tryConstructPostalStruct(ContentValues contentValues) { + // adr-value = 0*6(text-value ";") text-value + // ; PO Box, Extended Address, Street, Locality, Region, Postal + // ; Code, Country Name + final String rawPoBox = contentValues.getAsString(StructuredPostal.POBOX); + final String rawNeighborhood = contentValues.getAsString(StructuredPostal.NEIGHBORHOOD); + final String rawStreet = contentValues.getAsString(StructuredPostal.STREET); + final String rawLocality = contentValues.getAsString(StructuredPostal.CITY); + final String rawRegion = contentValues.getAsString(StructuredPostal.REGION); + final String rawPostalCode = contentValues.getAsString(StructuredPostal.POSTCODE); + final String rawCountry = contentValues.getAsString(StructuredPostal.COUNTRY); + final String[] rawAddressArray = new String[]{ + rawPoBox, rawNeighborhood, rawStreet, rawLocality, + rawRegion, rawPostalCode, rawCountry}; + if (!VCardUtils.areAllEmpty(rawAddressArray)) { + final boolean reallyUseQuotedPrintable = + (mShouldUseQuotedPrintable && + !VCardUtils.containsOnlyNonCrLfPrintableAscii(rawAddressArray)); + final boolean appendCharset = + !VCardUtils.containsOnlyPrintableAscii(rawAddressArray); + final String encodedPoBox; + final String encodedStreet; + final String encodedLocality; + final String encodedRegion; + final String encodedPostalCode; + final String encodedCountry; + final String encodedNeighborhood; + + final String rawLocality2; + // This looks inefficient since we encode rawLocality and rawNeighborhood twice, + // but this is intentional. + // + // QP encoding may add line feeds when needed and the result of + // - encodeQuotedPrintable(rawLocality + " " + rawNeighborhood) + // may be different from + // - encodedLocality + " " + encodedNeighborhood. + // + // We use safer way. + if (TextUtils.isEmpty(rawLocality)) { + if (TextUtils.isEmpty(rawNeighborhood)) { + rawLocality2 = ""; + } else { + rawLocality2 = rawNeighborhood; + } + } else { + if (TextUtils.isEmpty(rawNeighborhood)) { + rawLocality2 = rawLocality; + } else { + rawLocality2 = rawLocality + " " + rawNeighborhood; + } + } + if (reallyUseQuotedPrintable) { + encodedPoBox = encodeQuotedPrintable(rawPoBox); + encodedStreet = encodeQuotedPrintable(rawStreet); + encodedLocality = encodeQuotedPrintable(rawLocality2); + encodedRegion = encodeQuotedPrintable(rawRegion); + encodedPostalCode = encodeQuotedPrintable(rawPostalCode); + encodedCountry = encodeQuotedPrintable(rawCountry); + } else { + encodedPoBox = escapeCharacters(rawPoBox); + encodedStreet = escapeCharacters(rawStreet); + encodedLocality = escapeCharacters(rawLocality2); + encodedRegion = escapeCharacters(rawRegion); + encodedPostalCode = escapeCharacters(rawPostalCode); + encodedCountry = escapeCharacters(rawCountry); + encodedNeighborhood = escapeCharacters(rawNeighborhood); + } + final StringBuilder addressBuilder = new StringBuilder(); + addressBuilder.append(encodedPoBox); + addressBuilder.append(VCARD_ITEM_SEPARATOR); // PO BOX ; Extended Address + addressBuilder.append(VCARD_ITEM_SEPARATOR); // Extended Address : Street + addressBuilder.append(encodedStreet); + addressBuilder.append(VCARD_ITEM_SEPARATOR); // Street : Locality + addressBuilder.append(encodedLocality); + addressBuilder.append(VCARD_ITEM_SEPARATOR); // Locality : Region + addressBuilder.append(encodedRegion); + addressBuilder.append(VCARD_ITEM_SEPARATOR); // Region : Postal Code + addressBuilder.append(encodedPostalCode); + addressBuilder.append(VCARD_ITEM_SEPARATOR); // Postal Code : Country + addressBuilder.append(encodedCountry); + return new PostalStruct( + reallyUseQuotedPrintable, appendCharset, addressBuilder.toString()); + } else { // VCardUtils.areAllEmpty(rawAddressArray) == true + // Try to use FORMATTED_ADDRESS instead. + final String rawFormattedAddress = + contentValues.getAsString(StructuredPostal.FORMATTED_ADDRESS); + if (TextUtils.isEmpty(rawFormattedAddress)) { + return null; + } + final boolean reallyUseQuotedPrintable = + (mShouldUseQuotedPrintable && + !VCardUtils.containsOnlyNonCrLfPrintableAscii(rawFormattedAddress)); + final boolean appendCharset = + !VCardUtils.containsOnlyPrintableAscii(rawFormattedAddress); + final String encodedFormattedAddress; + if (reallyUseQuotedPrintable) { + encodedFormattedAddress = encodeQuotedPrintable(rawFormattedAddress); + } else { + encodedFormattedAddress = escapeCharacters(rawFormattedAddress); + } + + // We use the second value ("Extended Address") just because Japanese mobile phones + // do so. If the other importer expects the value be in the other field, some flag may + // be needed. + final StringBuilder addressBuilder = new StringBuilder(); + addressBuilder.append(VCARD_ITEM_SEPARATOR); // PO BOX ; Extended Address + addressBuilder.append(encodedFormattedAddress); + addressBuilder.append(VCARD_ITEM_SEPARATOR); // Extended Address : Street + addressBuilder.append(VCARD_ITEM_SEPARATOR); // Street : Locality + addressBuilder.append(VCARD_ITEM_SEPARATOR); // Locality : Region + addressBuilder.append(VCARD_ITEM_SEPARATOR); // Region : Postal Code + addressBuilder.append(VCARD_ITEM_SEPARATOR); // Postal Code : Country + return new PostalStruct( + reallyUseQuotedPrintable, appendCharset, addressBuilder.toString()); + } + } + + public VCardBuilder appendIms(final List contentValuesList) { + if (contentValuesList != null) { + for (ContentValues contentValues : contentValuesList) { + final Integer protocolAsObject = contentValues.getAsInteger(Im.PROTOCOL); + if (protocolAsObject == null) { + continue; + } + final String propertyName = VCardUtils.getPropertyNameForIm(protocolAsObject); + if (propertyName == null) { + continue; + } + String data = contentValues.getAsString(Im.DATA); + if (data != null) { + data = data.trim(); + } + if (TextUtils.isEmpty(data)) { + continue; + } + final String typeAsString; + { + final Integer typeAsInteger = contentValues.getAsInteger(Im.TYPE); + switch (typeAsInteger != null ? typeAsInteger : Im.TYPE_OTHER) { + case Im.TYPE_HOME: { + typeAsString = VCardConstants.PARAM_TYPE_HOME; + break; + } + case Im.TYPE_WORK: { + typeAsString = VCardConstants.PARAM_TYPE_WORK; + break; + } + case Im.TYPE_CUSTOM: { + final String label = contentValues.getAsString(Im.LABEL); + typeAsString = (label != null ? "X-" + label : null); + break; + } + case Im.TYPE_OTHER: // Ignore + default: { + typeAsString = null; + break; + } + } + } + + final List parameterList = new ArrayList(); + if (!TextUtils.isEmpty(typeAsString)) { + parameterList.add(typeAsString); + } + final Integer isPrimaryAsInteger = contentValues.getAsInteger(Im.IS_PRIMARY); + final boolean isPrimary = (isPrimaryAsInteger != null ? + (isPrimaryAsInteger > 0) : false); + if (isPrimary) { + parameterList.add(VCardConstants.PARAM_TYPE_PREF); + } + + appendLineWithCharsetAndQPDetection(propertyName, parameterList, data); + } + } + return this; + } + + public VCardBuilder appendWebsites(final List contentValuesList) { + if (contentValuesList != null) { + for (ContentValues contentValues : contentValuesList) { + String website = contentValues.getAsString(Website.URL); + if (website != null) { + website = website.trim(); + } + + // Note: vCard 3.0 does not allow any parameter addition toward "URL" + // property, while there's no document in vCard 2.1. + if (!TextUtils.isEmpty(website)) { + appendLineWithCharsetAndQPDetection(VCardConstants.PROPERTY_URL, website); + } + } + } + return this; + } + + public VCardBuilder appendOrganizations(final List contentValuesList) { + if (contentValuesList != null) { + for (ContentValues contentValues : contentValuesList) { + String company = contentValues.getAsString(Organization.COMPANY); + if (company != null) { + company = company.trim(); + } + String department = contentValues.getAsString(Organization.DEPARTMENT); + if (department != null) { + department = department.trim(); + } + String title = contentValues.getAsString(Organization.TITLE); + if (title != null) { + title = title.trim(); + } + + StringBuilder orgBuilder = new StringBuilder(); + if (!TextUtils.isEmpty(company)) { + orgBuilder.append(company); + } + if (!TextUtils.isEmpty(department)) { + if (orgBuilder.length() > 0) { + orgBuilder.append(';'); + } + orgBuilder.append(department); + } + final String orgline = orgBuilder.toString(); + appendLine(VCardConstants.PROPERTY_ORG, orgline, + !VCardUtils.containsOnlyPrintableAscii(orgline), + (mShouldUseQuotedPrintable && + !VCardUtils.containsOnlyNonCrLfPrintableAscii(orgline))); + + if (!TextUtils.isEmpty(title)) { + appendLine(VCardConstants.PROPERTY_TITLE, title, + !VCardUtils.containsOnlyPrintableAscii(title), + (mShouldUseQuotedPrintable && + !VCardUtils.containsOnlyNonCrLfPrintableAscii(title))); + } + } + } + return this; + } + + public VCardBuilder appendPhotos(final List contentValuesList) { + if (contentValuesList != null) { + for (ContentValues contentValues : contentValuesList) { + if (contentValues == null) { + continue; + } + byte[] data = contentValues.getAsByteArray(Photo.PHOTO); + if (data == null) { + continue; + } + final String photoType = VCardUtils.guessImageType(data); + if (photoType == null) { + Log.d(LOG_TAG, "Unknown photo type. Ignored."); + continue; + } + // TODO: check this works fine. + final String photoString = new String(Base64.encode(data, Base64.NO_WRAP)); + if (!TextUtils.isEmpty(photoString)) { + appendPhotoLine(photoString, photoType); + } + } + } + return this; + } + + public VCardBuilder appendNotes(final List contentValuesList) { + if (contentValuesList != null) { + if (mOnlyOneNoteFieldIsAvailable) { + final StringBuilder noteBuilder = new StringBuilder(); + boolean first = true; + for (final ContentValues contentValues : contentValuesList) { + String note = contentValues.getAsString(Note.NOTE); + if (note == null) { + note = ""; + } + if (note.length() > 0) { + if (first) { + first = false; + } else { + noteBuilder.append('\n'); + } + noteBuilder.append(note); + } + } + final String noteStr = noteBuilder.toString(); + // This means we scan noteStr completely twice, which is redundant. + // But for now, we assume this is not so time-consuming.. + final boolean shouldAppendCharsetInfo = + !VCardUtils.containsOnlyPrintableAscii(noteStr); + final boolean reallyUseQuotedPrintable = + (mShouldUseQuotedPrintable && + !VCardUtils.containsOnlyNonCrLfPrintableAscii(noteStr)); + appendLine(VCardConstants.PROPERTY_NOTE, noteStr, + shouldAppendCharsetInfo, reallyUseQuotedPrintable); + } else { + for (ContentValues contentValues : contentValuesList) { + final String noteStr = contentValues.getAsString(Note.NOTE); + if (!TextUtils.isEmpty(noteStr)) { + final boolean shouldAppendCharsetInfo = + !VCardUtils.containsOnlyPrintableAscii(noteStr); + final boolean reallyUseQuotedPrintable = + (mShouldUseQuotedPrintable && + !VCardUtils.containsOnlyNonCrLfPrintableAscii(noteStr)); + appendLine(VCardConstants.PROPERTY_NOTE, noteStr, + shouldAppendCharsetInfo, reallyUseQuotedPrintable); + } + } + } + } + return this; + } + + public VCardBuilder appendEvents(final List contentValuesList) { + // There's possibility where a given object may have more than one birthday, which + // is inappropriate. We just build one birthday. + if (contentValuesList != null) { + String primaryBirthday = null; + String secondaryBirthday = null; + for (final ContentValues contentValues : contentValuesList) { + if (contentValues == null) { + continue; + } + final Integer eventTypeAsInteger = contentValues.getAsInteger(Event.TYPE); + final int eventType; + if (eventTypeAsInteger != null) { + eventType = eventTypeAsInteger; + } else { + eventType = Event.TYPE_OTHER; + } + if (eventType == Event.TYPE_BIRTHDAY) { + final String birthdayCandidate = contentValues.getAsString(Event.START_DATE); + if (birthdayCandidate == null) { + continue; + } + final Integer isSuperPrimaryAsInteger = + contentValues.getAsInteger(Event.IS_SUPER_PRIMARY); + final boolean isSuperPrimary = (isSuperPrimaryAsInteger != null ? + (isSuperPrimaryAsInteger > 0) : false); + if (isSuperPrimary) { + // "super primary" birthday should the prefered one. + primaryBirthday = birthdayCandidate; + break; + } + final Integer isPrimaryAsInteger = + contentValues.getAsInteger(Event.IS_PRIMARY); + final boolean isPrimary = (isPrimaryAsInteger != null ? + (isPrimaryAsInteger > 0) : false); + if (isPrimary) { + // We don't break here since "super primary" birthday may exist later. + primaryBirthday = birthdayCandidate; + } else if (secondaryBirthday == null) { + // First entry is set to the "secondary" candidate. + secondaryBirthday = birthdayCandidate; + } + } else if (mUsesAndroidProperty) { + // Event types other than Birthday is not supported by vCard. + appendAndroidSpecificProperty(Event.CONTENT_ITEM_TYPE, contentValues); + } + } + if (primaryBirthday != null) { + appendLineWithCharsetAndQPDetection(VCardConstants.PROPERTY_BDAY, + primaryBirthday.trim()); + } else if (secondaryBirthday != null){ + appendLineWithCharsetAndQPDetection(VCardConstants.PROPERTY_BDAY, + secondaryBirthday.trim()); + } + } + return this; + } + + public VCardBuilder appendRelation(final List contentValuesList) { + if (mUsesAndroidProperty && contentValuesList != null) { + for (final ContentValues contentValues : contentValuesList) { + if (contentValues == null) { + continue; + } + appendAndroidSpecificProperty(Relation.CONTENT_ITEM_TYPE, contentValues); + } + } + return this; + } + + /** + * @param emitEveryTime If true, builder builds the line even when there's no entry. + */ + public void appendPostalLine(final int type, final String label, + final ContentValues contentValues, + final boolean isPrimary, final boolean emitEveryTime) { + final boolean reallyUseQuotedPrintable; + final boolean appendCharset; + final String addressValue; + { + PostalStruct postalStruct = tryConstructPostalStruct(contentValues); + if (postalStruct == null) { + if (emitEveryTime) { + reallyUseQuotedPrintable = false; + appendCharset = false; + addressValue = ""; + } else { + return; + } + } else { + reallyUseQuotedPrintable = postalStruct.reallyUseQuotedPrintable; + appendCharset = postalStruct.appendCharset; + addressValue = postalStruct.addressData; + } + } + + List parameterList = new ArrayList(); + if (isPrimary) { + parameterList.add(VCardConstants.PARAM_TYPE_PREF); + } + switch (type) { + case StructuredPostal.TYPE_HOME: { + parameterList.add(VCardConstants.PARAM_TYPE_HOME); + break; + } + case StructuredPostal.TYPE_WORK: { + parameterList.add(VCardConstants.PARAM_TYPE_WORK); + break; + } + case StructuredPostal.TYPE_CUSTOM: { + if (!TextUtils.isEmpty(label) + && VCardUtils.containsOnlyAlphaDigitHyphen(label)) { + // We're not sure whether the label is valid in the spec + // ("IANA-token" in the vCard 3.0 is unclear...) + // Just for safety, we add "X-" at the beggining of each label. + // Also checks the label obeys with vCard 3.0 spec. + parameterList.add("X-" + label); + } + break; + } + case StructuredPostal.TYPE_OTHER: { + break; + } + default: { + Log.e(LOG_TAG, "Unknown StructuredPostal type: " + type); + break; + } + } + + mBuilder.append(VCardConstants.PROPERTY_ADR); + if (!parameterList.isEmpty()) { + mBuilder.append(VCARD_PARAM_SEPARATOR); + appendTypeParameters(parameterList); + } + if (appendCharset) { + // Strictly, vCard 3.0 does not allow exporters to emit charset information, + // but we will add it since the information should be useful for importers, + // + // Assume no parser does not emit error with this parameter in vCard 3.0. + mBuilder.append(VCARD_PARAM_SEPARATOR); + mBuilder.append(mVCardCharsetParameter); + } + if (reallyUseQuotedPrintable) { + mBuilder.append(VCARD_PARAM_SEPARATOR); + mBuilder.append(VCARD_PARAM_ENCODING_QP); + } + mBuilder.append(VCARD_DATA_SEPARATOR); + mBuilder.append(addressValue); + mBuilder.append(VCARD_END_OF_LINE); + } + + public void appendEmailLine(final int type, final String label, + final String rawValue, final boolean isPrimary) { + final String typeAsString; + switch (type) { + case Email.TYPE_CUSTOM: { + if (VCardUtils.isMobilePhoneLabel(label)) { + typeAsString = VCardConstants.PARAM_TYPE_CELL; + } else if (!TextUtils.isEmpty(label) + && VCardUtils.containsOnlyAlphaDigitHyphen(label)) { + typeAsString = "X-" + label; + } else { + typeAsString = null; + } + break; + } + case Email.TYPE_HOME: { + typeAsString = VCardConstants.PARAM_TYPE_HOME; + break; + } + case Email.TYPE_WORK: { + typeAsString = VCardConstants.PARAM_TYPE_WORK; + break; + } + case Email.TYPE_OTHER: { + typeAsString = null; + break; + } + case Email.TYPE_MOBILE: { + typeAsString = VCardConstants.PARAM_TYPE_CELL; + break; + } + default: { + Log.e(LOG_TAG, "Unknown Email type: " + type); + typeAsString = null; + break; + } + } + + final List parameterList = new ArrayList(); + if (isPrimary) { + parameterList.add(VCardConstants.PARAM_TYPE_PREF); + } + if (!TextUtils.isEmpty(typeAsString)) { + parameterList.add(typeAsString); + } + + appendLineWithCharsetAndQPDetection(VCardConstants.PROPERTY_EMAIL, parameterList, + rawValue); + } + + public void appendTelLine(final Integer typeAsInteger, final String label, + final String encodedValue, boolean isPrimary) { + mBuilder.append(VCardConstants.PROPERTY_TEL); + mBuilder.append(VCARD_PARAM_SEPARATOR); + + final int type; + if (typeAsInteger == null) { + type = Phone.TYPE_OTHER; + } else { + type = typeAsInteger; + } + + ArrayList parameterList = new ArrayList(); + switch (type) { + case Phone.TYPE_HOME: { + parameterList.addAll( + Arrays.asList(VCardConstants.PARAM_TYPE_HOME)); + break; + } + case Phone.TYPE_WORK: { + parameterList.addAll( + Arrays.asList(VCardConstants.PARAM_TYPE_WORK)); + break; + } + case Phone.TYPE_FAX_HOME: { + parameterList.addAll( + Arrays.asList(VCardConstants.PARAM_TYPE_HOME, VCardConstants.PARAM_TYPE_FAX)); + break; + } + case Phone.TYPE_FAX_WORK: { + parameterList.addAll( + Arrays.asList(VCardConstants.PARAM_TYPE_WORK, VCardConstants.PARAM_TYPE_FAX)); + break; + } + case Phone.TYPE_MOBILE: { + parameterList.add(VCardConstants.PARAM_TYPE_CELL); + break; + } + case Phone.TYPE_PAGER: { + if (mIsDoCoMo) { + // Not sure about the reason, but previous implementation had + // used "VOICE" instead of "PAGER" + parameterList.add(VCardConstants.PARAM_TYPE_VOICE); + } else { + parameterList.add(VCardConstants.PARAM_TYPE_PAGER); + } + break; + } + case Phone.TYPE_OTHER: { + parameterList.add(VCardConstants.PARAM_TYPE_VOICE); + break; + } + case Phone.TYPE_CAR: { + parameterList.add(VCardConstants.PARAM_TYPE_CAR); + break; + } + case Phone.TYPE_COMPANY_MAIN: { + // There's no relevant field in vCard (at least 2.1). + parameterList.add(VCardConstants.PARAM_TYPE_WORK); + isPrimary = true; + break; + } + case Phone.TYPE_ISDN: { + parameterList.add(VCardConstants.PARAM_TYPE_ISDN); + break; + } + case Phone.TYPE_MAIN: { + isPrimary = true; + break; + } + case Phone.TYPE_OTHER_FAX: { + parameterList.add(VCardConstants.PARAM_TYPE_FAX); + break; + } + case Phone.TYPE_TELEX: { + parameterList.add(VCardConstants.PARAM_TYPE_TLX); + break; + } + case Phone.TYPE_WORK_MOBILE: { + parameterList.addAll( + Arrays.asList(VCardConstants.PARAM_TYPE_WORK, VCardConstants.PARAM_TYPE_CELL)); + break; + } + case Phone.TYPE_WORK_PAGER: { + parameterList.add(VCardConstants.PARAM_TYPE_WORK); + // See above. + if (mIsDoCoMo) { + parameterList.add(VCardConstants.PARAM_TYPE_VOICE); + } else { + parameterList.add(VCardConstants.PARAM_TYPE_PAGER); + } + break; + } + case Phone.TYPE_MMS: { + parameterList.add(VCardConstants.PARAM_TYPE_MSG); + break; + } + case Phone.TYPE_CUSTOM: { + if (TextUtils.isEmpty(label)) { + // Just ignore the custom type. + parameterList.add(VCardConstants.PARAM_TYPE_VOICE); + } else if (VCardUtils.isMobilePhoneLabel(label)) { + parameterList.add(VCardConstants.PARAM_TYPE_CELL); + } else { + final String upperLabel = label.toUpperCase(); + if (VCardUtils.isValidInV21ButUnknownToContactsPhoteType(upperLabel)) { + parameterList.add(upperLabel); + } else if (VCardUtils.containsOnlyAlphaDigitHyphen(label)) { + // Note: Strictly, vCard 2.1 does not allow "X-" parameter without + // "TYPE=" string. + parameterList.add("X-" + label); + } + } + break; + } + case Phone.TYPE_RADIO: + case Phone.TYPE_TTY_TDD: + default: { + break; + } + } + + if (isPrimary) { + parameterList.add(VCardConstants.PARAM_TYPE_PREF); + } + + if (parameterList.isEmpty()) { + appendUncommonPhoneType(mBuilder, type); + } else { + appendTypeParameters(parameterList); + } + + mBuilder.append(VCARD_DATA_SEPARATOR); + mBuilder.append(encodedValue); + mBuilder.append(VCARD_END_OF_LINE); + } + + /** + * Appends phone type string which may not be available in some devices. + */ + private void appendUncommonPhoneType(final StringBuilder builder, final Integer type) { + if (mIsDoCoMo) { + // The previous implementation for DoCoMo had been conservative + // about miscellaneous types. + builder.append(VCardConstants.PARAM_TYPE_VOICE); + } else { + String phoneType = VCardUtils.getPhoneTypeString(type); + if (phoneType != null) { + appendTypeParameter(phoneType); + } else { + Log.e(LOG_TAG, "Unknown or unsupported (by vCard) Phone type: " + type); + } + } + } + + /** + * @param encodedValue Must be encoded by BASE64 + * @param photoType + */ + public void appendPhotoLine(final String encodedValue, final String photoType) { + StringBuilder tmpBuilder = new StringBuilder(); + tmpBuilder.append(VCardConstants.PROPERTY_PHOTO); + tmpBuilder.append(VCARD_PARAM_SEPARATOR); + if (mIsV30) { + tmpBuilder.append(VCARD_PARAM_ENCODING_BASE64_V30); + } else { + tmpBuilder.append(VCARD_PARAM_ENCODING_BASE64_V21); + } + tmpBuilder.append(VCARD_PARAM_SEPARATOR); + appendTypeParameter(tmpBuilder, photoType); + tmpBuilder.append(VCARD_DATA_SEPARATOR); + tmpBuilder.append(encodedValue); + + final String tmpStr = tmpBuilder.toString(); + tmpBuilder = new StringBuilder(); + int lineCount = 0; + final int length = tmpStr.length(); + final int maxNumForFirstLine = VCardConstants.MAX_CHARACTER_NUMS_BASE64_V30 + - VCARD_END_OF_LINE.length(); + final int maxNumInGeneral = maxNumForFirstLine - VCARD_WS.length(); + int maxNum = maxNumForFirstLine; + for (int i = 0; i < length; i++) { + tmpBuilder.append(tmpStr.charAt(i)); + lineCount++; + if (lineCount > maxNum) { + tmpBuilder.append(VCARD_END_OF_LINE); + tmpBuilder.append(VCARD_WS); + maxNum = maxNumInGeneral; + lineCount = 0; + } + } + mBuilder.append(tmpBuilder.toString()); + mBuilder.append(VCARD_END_OF_LINE); + mBuilder.append(VCARD_END_OF_LINE); + } + + public void appendAndroidSpecificProperty( + final String mimeType, ContentValues contentValues) { + if (!sAllowedAndroidPropertySet.contains(mimeType)) { + return; + } + final List rawValueList = new ArrayList(); + for (int i = 1; i <= VCardConstants.MAX_DATA_COLUMN; i++) { + String value = contentValues.getAsString("data" + i); + if (value == null) { + value = ""; + } + rawValueList.add(value); + } + + boolean needCharset = + (mShouldAppendCharsetParam && + !VCardUtils.containsOnlyNonCrLfPrintableAscii(rawValueList)); + boolean reallyUseQuotedPrintable = + (mShouldUseQuotedPrintable && + !VCardUtils.containsOnlyNonCrLfPrintableAscii(rawValueList)); + mBuilder.append(VCardConstants.PROPERTY_X_ANDROID_CUSTOM); + if (needCharset) { + mBuilder.append(VCARD_PARAM_SEPARATOR); + mBuilder.append(mVCardCharsetParameter); + } + if (reallyUseQuotedPrintable) { + mBuilder.append(VCARD_PARAM_SEPARATOR); + mBuilder.append(VCARD_PARAM_ENCODING_QP); + } + mBuilder.append(VCARD_DATA_SEPARATOR); + mBuilder.append(mimeType); // Should not be encoded. + for (String rawValue : rawValueList) { + final String encodedValue; + if (reallyUseQuotedPrintable) { + encodedValue = encodeQuotedPrintable(rawValue); + } else { + // TODO: one line may be too huge, which may be invalid in vCard 3.0 + // (which says "When generating a content line, lines longer than + // 75 characters SHOULD be folded"), though several + // (even well-known) applications do not care this. + encodedValue = escapeCharacters(rawValue); + } + mBuilder.append(VCARD_ITEM_SEPARATOR); + mBuilder.append(encodedValue); + } + mBuilder.append(VCARD_END_OF_LINE); + } + + public void appendLineWithCharsetAndQPDetection(final String propertyName, + final String rawValue) { + appendLineWithCharsetAndQPDetection(propertyName, null, rawValue); + } + + public void appendLineWithCharsetAndQPDetection( + final String propertyName, final List rawValueList) { + appendLineWithCharsetAndQPDetection(propertyName, null, rawValueList); + } + + public void appendLineWithCharsetAndQPDetection(final String propertyName, + final List parameterList, final String rawValue) { + final boolean needCharset = + !VCardUtils.containsOnlyPrintableAscii(rawValue); + final boolean reallyUseQuotedPrintable = + (mShouldUseQuotedPrintable && + !VCardUtils.containsOnlyNonCrLfPrintableAscii(rawValue)); + appendLine(propertyName, parameterList, + rawValue, needCharset, reallyUseQuotedPrintable); + } + + public void appendLineWithCharsetAndQPDetection(final String propertyName, + final List parameterList, final List rawValueList) { + boolean needCharset = + (mShouldAppendCharsetParam && + !VCardUtils.containsOnlyNonCrLfPrintableAscii(rawValueList)); + boolean reallyUseQuotedPrintable = + (mShouldUseQuotedPrintable && + !VCardUtils.containsOnlyNonCrLfPrintableAscii(rawValueList)); + appendLine(propertyName, parameterList, rawValueList, + needCharset, reallyUseQuotedPrintable); + } + + /** + * Appends one line with a given property name and value. + */ + public void appendLine(final String propertyName, final String rawValue) { + appendLine(propertyName, rawValue, false, false); + } + + public void appendLine(final String propertyName, final List rawValueList) { + appendLine(propertyName, rawValueList, false, false); + } + + public void appendLine(final String propertyName, + final String rawValue, final boolean needCharset, + boolean reallyUseQuotedPrintable) { + appendLine(propertyName, null, rawValue, needCharset, reallyUseQuotedPrintable); + } + + public void appendLine(final String propertyName, final List parameterList, + final String rawValue) { + appendLine(propertyName, parameterList, rawValue, false, false); + } + + public void appendLine(final String propertyName, final List parameterList, + final String rawValue, final boolean needCharset, + boolean reallyUseQuotedPrintable) { + mBuilder.append(propertyName); + if (parameterList != null && parameterList.size() > 0) { + mBuilder.append(VCARD_PARAM_SEPARATOR); + appendTypeParameters(parameterList); + } + if (needCharset) { + mBuilder.append(VCARD_PARAM_SEPARATOR); + mBuilder.append(mVCardCharsetParameter); + } + + final String encodedValue; + if (reallyUseQuotedPrintable) { + mBuilder.append(VCARD_PARAM_SEPARATOR); + mBuilder.append(VCARD_PARAM_ENCODING_QP); + encodedValue = encodeQuotedPrintable(rawValue); + } else { + // TODO: one line may be too huge, which may be invalid in vCard spec, though + // several (even well-known) applications do not care that violation. + encodedValue = escapeCharacters(rawValue); + } + + mBuilder.append(VCARD_DATA_SEPARATOR); + mBuilder.append(encodedValue); + mBuilder.append(VCARD_END_OF_LINE); + } + + public void appendLine(final String propertyName, final List rawValueList, + final boolean needCharset, boolean needQuotedPrintable) { + appendLine(propertyName, null, rawValueList, needCharset, needQuotedPrintable); + } + + public void appendLine(final String propertyName, final List parameterList, + final List rawValueList, final boolean needCharset, + final boolean needQuotedPrintable) { + mBuilder.append(propertyName); + if (parameterList != null && parameterList.size() > 0) { + mBuilder.append(VCARD_PARAM_SEPARATOR); + appendTypeParameters(parameterList); + } + if (needCharset) { + mBuilder.append(VCARD_PARAM_SEPARATOR); + mBuilder.append(mVCardCharsetParameter); + } + if (needQuotedPrintable) { + mBuilder.append(VCARD_PARAM_SEPARATOR); + mBuilder.append(VCARD_PARAM_ENCODING_QP); + } + + mBuilder.append(VCARD_DATA_SEPARATOR); + boolean first = true; + for (String rawValue : rawValueList) { + final String encodedValue; + if (needQuotedPrintable) { + encodedValue = encodeQuotedPrintable(rawValue); + } else { + // TODO: one line may be too huge, which may be invalid in vCard 3.0 + // (which says "When generating a content line, lines longer than + // 75 characters SHOULD be folded"), though several + // (even well-known) applications do not care this. + encodedValue = escapeCharacters(rawValue); + } + + if (first) { + first = false; + } else { + mBuilder.append(VCARD_ITEM_SEPARATOR); + } + mBuilder.append(encodedValue); + } + mBuilder.append(VCARD_END_OF_LINE); + } + + /** + * VCARD_PARAM_SEPARATOR must be appended before this method being called. + */ + private void appendTypeParameters(final List types) { + // We may have to make this comma separated form like "TYPE=DOM,WORK" in the future, + // which would be recommended way in vcard 3.0 though not valid in vCard 2.1. + boolean first = true; + for (final String typeValue : types) { + // Note: vCard 3.0 specifies the different type of acceptable type Strings, but + // we don't emit that kind of vCard 3.0 specific type since there should be + // high probabilyty in which external importers cannot understand them. + // + // e.g. TYPE="\u578B\u306B\u3087" (vCard 3.0 allows non-Ascii characters if they + // are quoted.) + if (!VCardUtils.isV21Word(typeValue)) { + continue; + } + if (first) { + first = false; + } else { + mBuilder.append(VCARD_PARAM_SEPARATOR); + } + appendTypeParameter(typeValue); + } + } + + /** + * VCARD_PARAM_SEPARATOR must be appended before this method being called. + */ + private void appendTypeParameter(final String type) { + appendTypeParameter(mBuilder, type); + } + + private void appendTypeParameter(final StringBuilder builder, final String type) { + // Refrain from using appendType() so that "TYPE=" is not be appended when the + // device is DoCoMo's (just for safety). + // + // Note: In vCard 3.0, Type strings also can be like this: "TYPE=HOME,PREF" + if ((mIsV30 || mAppendTypeParamName) && !mIsDoCoMo) { + builder.append(VCardConstants.PARAM_TYPE).append(VCARD_PARAM_EQUAL); + } + builder.append(type); + } + + /** + * Returns true when the property line should contain charset parameter + * information. This method may return true even when vCard version is 3.0. + * + * Strictly, adding charset information is invalid in VCard 3.0. + * However we'll add the info only when charset we use is not UTF-8 + * in vCard 3.0 format, since parser side may be able to use the charset + * via this field, though we may encounter another problem by adding it. + * + * e.g. Japanese mobile phones use Shift_Jis while RFC 2426 + * recommends UTF-8. By adding this field, parsers may be able + * to know this text is NOT UTF-8 but Shift_Jis. + */ + private boolean shouldAppendCharsetParam(String...propertyValueList) { + if (!mShouldAppendCharsetParam) { + return false; + } + for (String propertyValue : propertyValueList) { + if (!VCardUtils.containsOnlyPrintableAscii(propertyValue)) { + return true; + } + } + return false; + } + + private String encodeQuotedPrintable(final String str) { + if (TextUtils.isEmpty(str)) { + return ""; + } + + final StringBuilder builder = new StringBuilder(); + int index = 0; + int lineCount = 0; + byte[] strArray = null; + + try { + strArray = str.getBytes(mCharset); + } catch (UnsupportedEncodingException e) { + Log.e(LOG_TAG, "Charset " + mCharset + " cannot be used. " + + "Try default charset"); + strArray = str.getBytes(); + } + while (index < strArray.length) { + builder.append(String.format("=%02X", strArray[index])); + index += 1; + lineCount += 3; + + if (lineCount >= 67) { + // Specification requires CRLF must be inserted before the + // length of the line + // becomes more than 76. + // Assuming that the next character is a multi-byte character, + // it will become + // 6 bytes. + // 76 - 6 - 3 = 67 + builder.append("=\r\n"); + lineCount = 0; + } + } + + return builder.toString(); + } + + /** + * Append '\' to the characters which should be escaped. The character set is different + * not only between vCard 2.1 and vCard 3.0 but also among each device. + * + * Note that Quoted-Printable string must not be input here. + */ + @SuppressWarnings("fallthrough") + private String escapeCharacters(final String unescaped) { + if (TextUtils.isEmpty(unescaped)) { + return ""; + } + + final StringBuilder tmpBuilder = new StringBuilder(); + final int length = unescaped.length(); + for (int i = 0; i < length; i++) { + final char ch = unescaped.charAt(i); + switch (ch) { + case ';': { + tmpBuilder.append('\\'); + tmpBuilder.append(';'); + break; + } + case '\r': { + if (i + 1 < length) { + char nextChar = unescaped.charAt(i); + if (nextChar == '\n') { + break; + } else { + // fall through + } + } else { + // fall through + } + } + case '\n': { + // In vCard 2.1, there's no specification about this, while + // vCard 3.0 explicitly requires this should be encoded to "\n". + tmpBuilder.append("\\n"); + break; + } + case '\\': { + if (mIsV30) { + tmpBuilder.append("\\\\"); + break; + } else { + // fall through + } + } + case '<': + case '>': { + if (mIsDoCoMo) { + tmpBuilder.append('\\'); + tmpBuilder.append(ch); + } else { + tmpBuilder.append(ch); + } + break; + } + case ',': { + if (mIsV30) { + tmpBuilder.append("\\,"); + } else { + tmpBuilder.append(ch); + } + break; + } + default: { + tmpBuilder.append(ch); + break; + } + } + } + return tmpBuilder.toString(); + } + + @Override + public String toString() { + if (!mEndAppended) { + if (mIsDoCoMo) { + appendLine(VCardConstants.PROPERTY_X_CLASS, VCARD_DATA_PUBLIC); + appendLine(VCardConstants.PROPERTY_X_REDUCTION, ""); + appendLine(VCardConstants.PROPERTY_X_NO, ""); + appendLine(VCardConstants.PROPERTY_X_DCM_HMN_MODE, ""); + } + appendLine(VCardConstants.PROPERTY_END, VCARD_DATA_VCARD); + mEndAppended = true; + } + return mBuilder.toString(); + } +} diff --git a/vcard/java/com/android/vcard/VCardComposer.java b/vcard/java/com/android/vcard/VCardComposer.java new file mode 100644 index 000000000..703895575 --- /dev/null +++ b/vcard/java/com/android/vcard/VCardComposer.java @@ -0,0 +1,677 @@ +/* + * 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.vcard; + +import android.content.ContentResolver; +import android.content.ContentValues; +import android.content.Context; +import android.content.Entity; +import android.content.EntityIterator; +import android.content.Entity.NamedContentValues; +import android.database.Cursor; +import android.database.sqlite.SQLiteException; +import android.net.Uri; +import android.provider.ContactsContract.Contacts; +import android.provider.ContactsContract.Data; +import android.provider.ContactsContract.RawContacts; +import android.provider.ContactsContract.RawContactsEntity; +import android.provider.ContactsContract.CommonDataKinds.Email; +import android.provider.ContactsContract.CommonDataKinds.Event; +import android.provider.ContactsContract.CommonDataKinds.Im; +import android.provider.ContactsContract.CommonDataKinds.Nickname; +import android.provider.ContactsContract.CommonDataKinds.Note; +import android.provider.ContactsContract.CommonDataKinds.Organization; +import android.provider.ContactsContract.CommonDataKinds.Phone; +import android.provider.ContactsContract.CommonDataKinds.Photo; +import android.provider.ContactsContract.CommonDataKinds.Relation; +import android.provider.ContactsContract.CommonDataKinds.StructuredName; +import android.provider.ContactsContract.CommonDataKinds.StructuredPostal; +import android.provider.ContactsContract.CommonDataKinds.Website; +import android.text.TextUtils; +import android.util.CharsetUtils; +import android.util.Log; + +import com.android.vcard.exception.VCardException; + +import java.io.BufferedWriter; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.UnsupportedEncodingException; +import java.io.Writer; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.nio.charset.UnsupportedCharsetException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + *

+ * The class for composing vCard from Contacts information. + *

+ *

+ * Usually, this class should be used like this. + *

+ *
VCardComposer composer = null;
+ * try {
+ *     composer = new VCardComposer(context);
+ *     composer.addHandler(
+ *             composer.new HandlerForOutputStream(outputStream));
+ *     if (!composer.init()) {
+ *         // Do something handling the situation.
+ *         return;
+ *     }
+ *     while (!composer.isAfterLast()) {
+ *         if (mCanceled) {
+ *             // Assume a user may cancel this operation during the export.
+ *             return;
+ *         }
+ *         if (!composer.createOneEntry()) {
+ *             // Do something handling the error situation.
+ *             return;
+ *         }
+ *     }
+ * } finally {
+ *     if (composer != null) {
+ *         composer.terminate();
+ *     }
+ * }
+ *

+ * Users have to manually take care of memory efficiency. Even one vCard may contain + * image of non-trivial size for mobile devices. + *

+ *

+ * {@link VCardBuilder} is used to build each vCard. + *

+ */ +public class VCardComposer { + private static final String LOG_TAG = "VCardComposer"; + + public static final String FAILURE_REASON_FAILED_TO_GET_DATABASE_INFO = + "Failed to get database information"; + + public static final String FAILURE_REASON_NO_ENTRY = + "There's no exportable in the database"; + + public static final String FAILURE_REASON_NOT_INITIALIZED = + "The vCard composer object is not correctly initialized"; + + /** Should be visible only from developers... (no need to translate, hopefully) */ + public static final String FAILURE_REASON_UNSUPPORTED_URI = + "The Uri vCard composer received is not supported by the composer."; + + public static final String NO_ERROR = "No error"; + + public static final String VCARD_TYPE_STRING_DOCOMO = "docomo"; + + // Strictly speaking, "Shift_JIS" is the most appropriate, but we use upper version here, + // since usual vCard devices for Japanese devices already use it. + private static final String SHIFT_JIS = "SHIFT_JIS"; + private static final String UTF_8 = "UTF-8"; + + /** + * Special URI for testing. + */ + public static final String VCARD_TEST_AUTHORITY = "com.android.unit_tests.vcard"; + public static final Uri VCARD_TEST_AUTHORITY_URI = + Uri.parse("content://" + VCARD_TEST_AUTHORITY); + public static final Uri CONTACTS_TEST_CONTENT_URI = + Uri.withAppendedPath(VCARD_TEST_AUTHORITY_URI, "contacts"); + + private static final Map sImMap; + + static { + sImMap = new HashMap(); + sImMap.put(Im.PROTOCOL_AIM, VCardConstants.PROPERTY_X_AIM); + sImMap.put(Im.PROTOCOL_MSN, VCardConstants.PROPERTY_X_MSN); + sImMap.put(Im.PROTOCOL_YAHOO, VCardConstants.PROPERTY_X_YAHOO); + sImMap.put(Im.PROTOCOL_ICQ, VCardConstants.PROPERTY_X_ICQ); + sImMap.put(Im.PROTOCOL_JABBER, VCardConstants.PROPERTY_X_JABBER); + sImMap.put(Im.PROTOCOL_SKYPE, VCardConstants.PROPERTY_X_SKYPE_USERNAME); + // We don't add Google talk here since it has to be handled separately. + } + + public static interface OneEntryHandler { + public boolean onInit(Context context); + public boolean onEntryCreated(String vcard); + public void onTerminate(); + } + + /** + *

+ * An useful handler for emitting vCard String to an OutputStream object one by one. + *

+ *

+ * The input OutputStream object is closed() on {@link #onTerminate()}. + * Must not close the stream outside this class. + *

+ */ + public final class HandlerForOutputStream implements OneEntryHandler { + @SuppressWarnings("hiding") + private static final String LOG_TAG = "VCardComposer.HandlerForOutputStream"; + + private boolean mOnTerminateIsCalled = false; + + private final OutputStream mOutputStream; // mWriter will close this. + private Writer mWriter; + + /** + * Input stream will be closed on the detruction of this object. + */ + public HandlerForOutputStream(final OutputStream outputStream) { + mOutputStream = outputStream; + } + + public boolean onInit(final Context context) { + try { + mWriter = new BufferedWriter(new OutputStreamWriter( + mOutputStream, mCharset)); + } catch (UnsupportedEncodingException e1) { + Log.e(LOG_TAG, "Unsupported charset: " + mCharset); + mErrorReason = "Encoding is not supported (usually this does not happen!): " + + mCharset; + return false; + } + + if (mIsDoCoMo) { + try { + // Create one empty entry. + mWriter.write(createOneEntryInternal("-1", null)); + } catch (VCardException e) { + Log.e(LOG_TAG, "VCardException has been thrown during on Init(): " + + e.getMessage()); + return false; + } catch (IOException e) { + Log.e(LOG_TAG, + "IOException occurred during exportOneContactData: " + + e.getMessage()); + mErrorReason = "IOException occurred: " + e.getMessage(); + return false; + } + } + return true; + } + + public boolean onEntryCreated(String vcard) { + try { + mWriter.write(vcard); + } catch (IOException e) { + Log.e(LOG_TAG, + "IOException occurred during exportOneContactData: " + + e.getMessage()); + mErrorReason = "IOException occurred: " + e.getMessage(); + return false; + } + return true; + } + + public void onTerminate() { + mOnTerminateIsCalled = true; + if (mWriter != null) { + try { + // Flush and sync the data so that a user is able to pull + // the SDCard just after + // the export. + mWriter.flush(); + if (mOutputStream != null + && mOutputStream instanceof FileOutputStream) { + ((FileOutputStream) mOutputStream).getFD().sync(); + } + } catch (IOException e) { + Log.d(LOG_TAG, + "IOException during closing the output stream: " + + e.getMessage()); + } finally { + closeOutputStream(); + } + } + } + + public void closeOutputStream() { + try { + mWriter.close(); + } catch (IOException e) { + Log.w(LOG_TAG, "IOException is thrown during close(). Ignoring."); + } + } + + @Override + public void finalize() { + if (!mOnTerminateIsCalled) { + onTerminate(); + } + } + } + + private final Context mContext; + private final int mVCardType; + private final boolean mCareHandlerErrors; + private final ContentResolver mContentResolver; + + private final boolean mIsDoCoMo; + private Cursor mCursor; + private int mIdColumn; + + private final String mCharset; + private boolean mTerminateIsCalled; + private final List mHandlerList; + + private String mErrorReason = NO_ERROR; + + private static final String[] sContactsProjection = new String[] { + Contacts._ID, + }; + + public VCardComposer(Context context) { + this(context, VCardConfig.VCARD_TYPE_DEFAULT, null, true); + } + + /** + * The variant which sets charset to null and sets careHandlerErrors to true. + */ + public VCardComposer(Context context, int vcardType) { + this(context, vcardType, null, true); + } + + public VCardComposer(Context context, int vcardType, String charset) { + this(context, vcardType, charset, true); + } + + /** + * The variant which sets charset to null. + */ + public VCardComposer(final Context context, final int vcardType, + final boolean careHandlerErrors) { + this(context, vcardType, null, careHandlerErrors); + } + + /** + * Construct for supporting call log entry vCard composing. + * + * @param context Context to be used during the composition. + * @param vcardType The type of vCard, typically available via {@link VCardConfig}. + * @param charset The charset to be used. Use null when you don't need the charset. + * @param careHandlerErrors If true, This object returns false everytime + * a Handler object given via {{@link #addHandler(OneEntryHandler)} returns false. + * If false, this ignores those errors. + */ + public VCardComposer(final Context context, final int vcardType, String charset, + final boolean careHandlerErrors) { + mContext = context; + mVCardType = vcardType; + mCareHandlerErrors = careHandlerErrors; + mContentResolver = context.getContentResolver(); + + mIsDoCoMo = VCardConfig.isDoCoMo(vcardType); + mHandlerList = new ArrayList(); + + charset = (TextUtils.isEmpty(charset) ? VCardConfig.DEFAULT_EXPORT_CHARSET : charset); + final boolean shouldAppendCharsetParam = !( + VCardConfig.isV30(vcardType) && UTF_8.equalsIgnoreCase(charset)); + + if (mIsDoCoMo || shouldAppendCharsetParam) { + if (SHIFT_JIS.equalsIgnoreCase(charset)) { + if (mIsDoCoMo) { + try { + charset = CharsetUtils.charsetForVendor(SHIFT_JIS, "docomo").name(); + } catch (UnsupportedCharsetException e) { + Log.e(LOG_TAG, + "DoCoMo-specific SHIFT_JIS was not found. " + + "Use SHIFT_JIS as is."); + charset = SHIFT_JIS; + } + } else { + try { + charset = CharsetUtils.charsetForVendor(SHIFT_JIS).name(); + } catch (UnsupportedCharsetException e) { + Log.e(LOG_TAG, + "Career-specific SHIFT_JIS was not found. " + + "Use SHIFT_JIS as is."); + charset = SHIFT_JIS; + } + } + mCharset = charset; + } else { + Log.w(LOG_TAG, + "The charset \"" + charset + "\" is used while " + + SHIFT_JIS + " is needed to be used."); + if (TextUtils.isEmpty(charset)) { + mCharset = SHIFT_JIS; + } else { + try { + charset = CharsetUtils.charsetForVendor(charset).name(); + } catch (UnsupportedCharsetException e) { + Log.i(LOG_TAG, + "Career-specific \"" + charset + "\" was not found (as usual). " + + "Use it as is."); + } + mCharset = charset; + } + } + } else { + if (TextUtils.isEmpty(charset)) { + mCharset = UTF_8; + } else { + try { + charset = CharsetUtils.charsetForVendor(charset).name(); + } catch (UnsupportedCharsetException e) { + Log.i(LOG_TAG, + "Career-specific \"" + charset + "\" was not found (as usual). " + + "Use it as is."); + } + mCharset = charset; + } + } + + Log.d(LOG_TAG, "Use the charset \"" + mCharset + "\""); + } + + /** + * Must be called before {@link #init()}. + */ + public void addHandler(OneEntryHandler handler) { + if (handler != null) { + mHandlerList.add(handler); + } + } + + /** + * @return Returns true when initialization is successful and all the other + * methods are available. Returns false otherwise. + */ + public boolean init() { + return init(null, null); + } + + public boolean init(final String selection, final String[] selectionArgs) { + return init(Contacts.CONTENT_URI, selection, selectionArgs, null); + } + + /** + * Note that this is unstable interface, may be deleted in the future. + */ + public boolean init(final Uri contentUri, final String selection, + final String[] selectionArgs, final String sortOrder) { + if (contentUri == null) { + return false; + } + + if (mCareHandlerErrors) { + final List finishedList = new ArrayList( + mHandlerList.size()); + for (OneEntryHandler handler : mHandlerList) { + if (!handler.onInit(mContext)) { + for (OneEntryHandler finished : finishedList) { + finished.onTerminate(); + } + return false; + } + } + } else { + // Just ignore the false returned from onInit(). + for (OneEntryHandler handler : mHandlerList) { + handler.onInit(mContext); + } + } + + final String[] projection; + if (Contacts.CONTENT_URI.equals(contentUri) || + CONTACTS_TEST_CONTENT_URI.equals(contentUri)) { + projection = sContactsProjection; + } else { + mErrorReason = FAILURE_REASON_UNSUPPORTED_URI; + return false; + } + mCursor = mContentResolver.query( + contentUri, projection, selection, selectionArgs, sortOrder); + + if (mCursor == null) { + mErrorReason = FAILURE_REASON_FAILED_TO_GET_DATABASE_INFO; + return false; + } + + if (getCount() == 0 || !mCursor.moveToFirst()) { + try { + mCursor.close(); + } catch (SQLiteException e) { + Log.e(LOG_TAG, "SQLiteException on Cursor#close(): " + e.getMessage()); + } finally { + mCursor = null; + mErrorReason = FAILURE_REASON_NO_ENTRY; + } + return false; + } + + mIdColumn = mCursor.getColumnIndex(Contacts._ID); + + return true; + } + + public boolean createOneEntry() { + return createOneEntry(null); + } + + /** + * @param getEntityIteratorMethod For Dependency Injection. + * @hide just for testing. + */ + public boolean createOneEntry(Method getEntityIteratorMethod) { + if (mCursor == null || mCursor.isAfterLast()) { + mErrorReason = FAILURE_REASON_NOT_INITIALIZED; + return false; + } + final String vcard; + try { + if (mIdColumn >= 0) { + vcard = createOneEntryInternal(mCursor.getString(mIdColumn), + getEntityIteratorMethod); + } else { + Log.e(LOG_TAG, "Incorrect mIdColumn: " + mIdColumn); + return true; + } + } catch (VCardException e) { + Log.e(LOG_TAG, "VCardException has been thrown: " + e.getMessage()); + return false; + } catch (OutOfMemoryError error) { + // Maybe some data (e.g. photo) is too big to have in memory. But it + // should be rare. + Log.e(LOG_TAG, "OutOfMemoryError occured. Ignore the entry."); + System.gc(); + // TODO: should tell users what happened? + return true; + } finally { + mCursor.moveToNext(); + } + + // This function does not care the OutOfMemoryError on the handler side :-P + if (mCareHandlerErrors) { + List finishedList = new ArrayList( + mHandlerList.size()); + for (OneEntryHandler handler : mHandlerList) { + if (!handler.onEntryCreated(vcard)) { + return false; + } + } + } else { + for (OneEntryHandler handler : mHandlerList) { + handler.onEntryCreated(vcard); + } + } + + return true; + } + + private String createOneEntryInternal(final String contactId, + final Method getEntityIteratorMethod) throws VCardException { + final Map> contentValuesListMap = + new HashMap>(); + // The resolver may return the entity iterator with no data. It is possible. + // e.g. If all the data in the contact of the given contact id are not exportable ones, + // they are hidden from the view of this method, though contact id itself exists. + EntityIterator entityIterator = null; + try { + final Uri uri = RawContactsEntity.CONTENT_URI.buildUpon() + // .appendQueryParameter("for_export_only", "1") + .appendQueryParameter(Data.FOR_EXPORT_ONLY, "1") + .build(); + final String selection = Data.CONTACT_ID + "=?"; + final String[] selectionArgs = new String[] {contactId}; + if (getEntityIteratorMethod != null) { + // Please note that this branch is executed by unit tests only + try { + entityIterator = (EntityIterator)getEntityIteratorMethod.invoke(null, + mContentResolver, uri, selection, selectionArgs, null); + } catch (IllegalArgumentException e) { + Log.e(LOG_TAG, "IllegalArgumentException has been thrown: " + + e.getMessage()); + } catch (IllegalAccessException e) { + Log.e(LOG_TAG, "IllegalAccessException has been thrown: " + + e.getMessage()); + } catch (InvocationTargetException e) { + Log.e(LOG_TAG, "InvocationTargetException has been thrown: "); + StackTraceElement[] stackTraceElements = e.getCause().getStackTrace(); + for (StackTraceElement element : stackTraceElements) { + Log.e(LOG_TAG, " at " + element.toString()); + } + throw new VCardException("InvocationTargetException has been thrown: " + + e.getCause().getMessage()); + } + } else { + entityIterator = RawContacts.newEntityIterator(mContentResolver.query( + uri, null, selection, selectionArgs, null)); + } + + if (entityIterator == null) { + Log.e(LOG_TAG, "EntityIterator is null"); + return ""; + } + + if (!entityIterator.hasNext()) { + Log.w(LOG_TAG, "Data does not exist. contactId: " + contactId); + return ""; + } + + while (entityIterator.hasNext()) { + Entity entity = entityIterator.next(); + for (NamedContentValues namedContentValues : entity.getSubValues()) { + ContentValues contentValues = namedContentValues.values; + String key = contentValues.getAsString(Data.MIMETYPE); + if (key != null) { + List contentValuesList = + contentValuesListMap.get(key); + if (contentValuesList == null) { + contentValuesList = new ArrayList(); + contentValuesListMap.put(key, contentValuesList); + } + contentValuesList.add(contentValues); + } + } + } + } finally { + if (entityIterator != null) { + entityIterator.close(); + } + } + + return buildVCard(contentValuesListMap); + } + + /** + * Builds and returns vCard using given map, whose key is CONTENT_ITEM_TYPE defined in + * {ContactsContract}. Developers can override this method to customize the output. + */ + public String buildVCard(final Map> contentValuesListMap) { + if (contentValuesListMap == null) { + Log.e(LOG_TAG, "The given map is null. Ignore and return empty String"); + return ""; + } else { + final VCardBuilder builder = new VCardBuilder(mVCardType, mCharset); + builder.appendNameProperties(contentValuesListMap.get(StructuredName.CONTENT_ITEM_TYPE)) + .appendNickNames(contentValuesListMap.get(Nickname.CONTENT_ITEM_TYPE)) + .appendPhones(contentValuesListMap.get(Phone.CONTENT_ITEM_TYPE)) + .appendEmails(contentValuesListMap.get(Email.CONTENT_ITEM_TYPE)) + .appendPostals(contentValuesListMap.get(StructuredPostal.CONTENT_ITEM_TYPE)) + .appendOrganizations(contentValuesListMap.get(Organization.CONTENT_ITEM_TYPE)) + .appendWebsites(contentValuesListMap.get(Website.CONTENT_ITEM_TYPE)) + .appendPhotos(contentValuesListMap.get(Photo.CONTENT_ITEM_TYPE)) + .appendNotes(contentValuesListMap.get(Note.CONTENT_ITEM_TYPE)) + .appendEvents(contentValuesListMap.get(Event.CONTENT_ITEM_TYPE)) + .appendIms(contentValuesListMap.get(Im.CONTENT_ITEM_TYPE)) + .appendRelation(contentValuesListMap.get(Relation.CONTENT_ITEM_TYPE)); + return builder.toString(); + } + } + + public void terminate() { + for (OneEntryHandler handler : mHandlerList) { + handler.onTerminate(); + } + + if (mCursor != null) { + try { + mCursor.close(); + } catch (SQLiteException e) { + Log.e(LOG_TAG, "SQLiteException on Cursor#close(): " + e.getMessage()); + } + mCursor = null; + } + + mTerminateIsCalled = true; + } + + @Override + public void finalize() { + if (!mTerminateIsCalled) { + Log.w(LOG_TAG, "terminate() is not called yet. We call it in finalize() step."); + terminate(); + } + } + + /** + * @return returns the number of available entities. The return value is undefined + * when this object is not ready yet (typically when {{@link #init()} is not called + * or when {@link #terminate()} is already called). + */ + public int getCount() { + if (mCursor == null) { + Log.w(LOG_TAG, "This object is not ready yet."); + return 0; + } + return mCursor.getCount(); + } + + /** + * @return true when there's no entity to be built. The return value is undefined + * when this object is not ready yet. + */ + public boolean isAfterLast() { + if (mCursor == null) { + Log.w(LOG_TAG, "This object is not ready yet."); + return false; + } + return mCursor.isAfterLast(); + } + + /** + * @return Returns the error reason. + */ + public String getErrorReason() { + return mErrorReason; + } +} diff --git a/vcard/java/com/android/vcard/VCardConfig.java b/vcard/java/com/android/vcard/VCardConfig.java new file mode 100644 index 000000000..fc95922d9 --- /dev/null +++ b/vcard/java/com/android/vcard/VCardConfig.java @@ -0,0 +1,478 @@ +/* + * 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.vcard; + +import android.telephony.PhoneNumberUtils; +import android.util.Log; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +/** + * The class representing VCard related configurations. Useful static methods are not in this class + * but in VCardUtils. + */ +public class VCardConfig { + private static final String LOG_TAG = "VCardConfig"; + + /* package */ static final int LOG_LEVEL_NONE = 0; + /* package */ static final int LOG_LEVEL_PERFORMANCE_MEASUREMENT = 0x1; + /* package */ static final int LOG_LEVEL_SHOW_WARNING = 0x2; + /* package */ static final int LOG_LEVEL_VERBOSE = + LOG_LEVEL_PERFORMANCE_MEASUREMENT | LOG_LEVEL_SHOW_WARNING; + + /* package */ static final int LOG_LEVEL = LOG_LEVEL_NONE; + + /** + *

+ * The charset used during import. + *

+ *

+ * We cannot determine which charset should be used to interpret a given vCard file + * at first, while we have to decode sime encoded data (e.g. BASE64) to binary. + * In order to avoid "misinterpretation" of charset as much as possible, + * "ISO-8859-1" (a.k.a Latin-1) is first used for reading a stream. + * When charset is specified in a property (with "CHARSET=..." parameter), + * the string is decoded to raw bytes and encoded into the specific charset, + * assuming "ISO-8859-1" is able to map "all" 8bit characters to some unicode, + * and it has 1 to 1 mapping in all 8bit characters. + * If the assumption is not correct, this setting will cause some bug. + *

+ */ + public static final String DEFAULT_INTERMEDIATE_CHARSET = "ISO-8859-1"; + + /** + * The charset used when there's no information affbout what charset should be used to + * encode the binary given from vCard. + */ + public static final String DEFAULT_IMPORT_CHARSET = "UTF-8"; + public static final String DEFAULT_EXPORT_CHARSET = "UTF-8"; + + public static final int FLAG_V21 = 0; + public static final int FLAG_V30 = 1; + + // 0x2 is reserved for the future use ... + + public static final int NAME_ORDER_DEFAULT = 0; + public static final int NAME_ORDER_EUROPE = 0x4; + public static final int NAME_ORDER_JAPANESE = 0x8; + private static final int NAME_ORDER_MASK = 0xC; + + // 0x10 is reserved for safety + + /** + *

+ * The flag indicating the vCard composer will add some "X-" properties used only in Android + * when the formal vCard specification does not have appropriate fields for that data. + *

+ *

+ * For example, Android accepts nickname information while vCard 2.1 does not. + * When this flag is on, vCard composer emits alternative "X-" property (like "X-NICKNAME") + * instead of just dropping it. + *

+ *

+ * vCard parser code automatically parses the field emitted even when this flag is off. + *

+ */ + private static final int FLAG_USE_ANDROID_PROPERTY = 0x80000000; + + /** + *

+ * The flag indicating the vCard composer will add some "X-" properties seen in the + * vCard data emitted by the other softwares/devices when the formal vCard specification + * does not have appropriate field(s) for that data. + *

+ *

+ * One example is X-PHONETIC-FIRST-NAME/X-PHONETIC-MIDDLE-NAME/X-PHONETIC-LAST-NAME, which are + * for phonetic name (how the name is pronounced), seen in the vCard emitted by some other + * non-Android devices/softwares. We chose to enable the vCard composer to use those + * defact properties since they are also useful for Android devices. + *

+ *

+ * Note for developers: only "X-" properties should be added with this flag. vCard 2.1/3.0 + * allows any kind of "X-" properties but does not allow non-"X-" properties (except IANA tokens + * in vCard 3.0). Some external parsers may get confused with non-valid, non-"X-" properties. + *

+ */ + private static final int FLAG_USE_DEFACT_PROPERTY = 0x40000000; + + /** + *

+ * The flag indicating some specific dialect seen in vCard of DoCoMo (one of Japanese + * mobile careers) should be used. This flag does not include any other information like + * that "the vCard is for Japanese". So it is "possible" that "the vCard should have DoCoMo's + * dialect but the name order should be European", but it is not recommended. + *

+ */ + private static final int FLAG_DOCOMO = 0x20000000; + + /** + *

+ * The flag indicating the vCard composer does "NOT" use Quoted-Printable toward "primary" + * properties even though it is required by vCard 2.1 (QP is prohibited in vCard 3.0). + *

+ *

+ * We actually cannot define what is the "primary" property. Note that this is NOT defined + * in vCard specification either. Also be aware that it is NOT related to "primary" notion + * used in {@link android.provider.ContactsContract}. + * This notion is just for vCard composition in Android. + *

+ *

+ * We added this Android-specific notion since some (incomplete) vCard exporters for vCard 2.1 + * do NOT use Quoted-Printable encoding toward some properties related names like "N", "FN", etc. + * even when their values contain non-ascii or/and CR/LF, while they use the encoding in the + * other properties like "ADR", "ORG", etc. + *

+ * We are afraid of the case where some vCard importer also forget handling QP presuming QP is + * not used in such fields. + *

+ *

+ * This flag is useful when some target importer you are going to focus on does not accept + * such properties with Quoted-Printable encoding. + *

+ *

+ * Again, we should not use this flag at all for complying vCard 2.1 spec. + *

+ *

+ * In vCard 3.0, Quoted-Printable is explicitly "prohibitted", so we don't need to care this + * kind of problem (hopefully). + *

+ * @hide + */ + public static final int FLAG_REFRAIN_QP_TO_NAME_PROPERTIES = 0x10000000; + + /** + *

+ * The flag indicating that phonetic name related fields must be converted to + * appropriate form. Note that "appropriate" is not defined in any vCard specification. + * This is Android-specific. + *

+ *

+ * One typical (and currently sole) example where we need this flag is the time when + * we need to emit Japanese phonetic names into vCard entries. The property values + * should be encoded into half-width katakana when the target importer is Japanese mobile + * phones', which are probably not able to parse full-width hiragana/katakana for + * historical reasons, while the vCard importers embedded to softwares for PC should be + * able to parse them as we expect. + *

+ */ + public static final int FLAG_CONVERT_PHONETIC_NAME_STRINGS = 0x08000000; + + /** + *

+ * The flag indicating the vCard composer "for 2.1" emits "TYPE=" string toward TYPE params + * every time possible. The default behavior does not emit it and is valid in the spec. + * In vCrad 3.0, this flag is unnecessary, since "TYPE=" is MUST in vCard 3.0 specification. + *

+ *

+ * Detail: + * How more than one TYPE fields are expressed is different between vCard 2.1 and vCard 3.0. + *

+ *

+ * e.g. + *

+ *
    + *
  1. Probably valid in both vCard 2.1 and vCard 3.0: "ADR;TYPE=DOM;TYPE=HOME:..."
  2. + *
  3. Valid in vCard 2.1 but not in vCard 3.0: "ADR;DOM;HOME:..."
  4. + *
  5. Valid in vCard 3.0 but not in vCard 2.1: "ADR;TYPE=DOM,HOME:..."
  6. + *
+ *

+ * If you are targeting to the importer which cannot accept TYPE params without "TYPE=" + * strings (which should be rare though), please use this flag. + *

+ *

+ * Example usage: + *

int type = (VCARD_TYPE_V21_GENERIC | FLAG_APPEND_TYPE_PARAM);
+ *

+ */ + public static final int FLAG_APPEND_TYPE_PARAM = 0x04000000; + + /** + *

+ * The flag indicating the vCard composer does touch nothing toward phone number Strings + * but leave it as is. + *

+ *

+ * The vCard specifications mention nothing toward phone numbers, while some devices + * do (wrongly, but with innevitable reasons). + * For example, there's a possibility Japanese mobile phones are expected to have + * just numbers, hypens, plus, etc. but not usual alphabets, while US mobile phones + * should get such characters. To make exported vCard simple for external parsers, + * we have used {@link PhoneNumberUtils#formatNumber(String)} during export, and + * removed unnecessary characters inside the number (e.g. "111-222-3333 (Miami)" + * becomes "111-222-3333"). + * Unfortunate side effect of that use was some control characters used in the other + * areas may be badly affected by the formatting. + *

+ *

+ * This flag disables that formatting, affecting both importer and exporter. + * If the user is aware of some side effects due to the implicit formatting, use this flag. + *

+ */ + public static final int FLAG_REFRAIN_PHONE_NUMBER_FORMATTING = 0x02000000; + + /** + *

+ * For importer only. Ignored in exporter. + *

+ *

+ * The flag indicating the parser should handle a nested vCard, in which vCard clause starts + * in another vCard clause. Here's a typical example. + *

+ *
BEGIN:VCARD
+     * BEGIN:VCARD
+     * VERSION:2.1
+     * ...
+     * END:VCARD
+     * END:VCARD
+ *

+ * The vCard 2.1 specification allows the nest, but also let parsers ignore nested entries, + * while some mobile devices emit nested ones as primary data to be imported. + *

+ *

+ * This flag forces a vCard parser to torelate such a nest and understand its content. + *

+ */ + public static final int FLAG_TORELATE_NEST = 0x01000000; + + //// The followings are VCard types available from importer/exporter. //// + + /** + *

+ * The type indicating nothing. Used by {@link VCardSourceDetector} when it + * was not able to guess the exact vCard type. + *

+ */ + public static final int VCARD_TYPE_UNKNOWN = 0; + + /** + *

+ * Generic vCard format with the vCard 2.1. When composing a vCard entry, + * the US convension will be used toward formatting some values. + *

+ *

+ * e.g. The order of the display name would be "Prefix Given Middle Family Suffix", + * while it should be "Prefix Family Middle Given Suffix" in Japan for example. + *

+ *

+ * Uses UTF-8 for the charset as a charset for exporting. Note that old vCard importer + * outside Android cannot accept it since vCard 2.1 specifically does not allow + * that charset, while we need to use it to support various languages around the world. + *

+ *

+ * If you want to use alternative charset, you should notify the charset to the other + * compontent to be used. + *

+ */ + public static final int VCARD_TYPE_V21_GENERIC = + (FLAG_V21 | NAME_ORDER_DEFAULT | FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY); + + /* package */ static String VCARD_TYPE_V21_GENERIC_STR = "v21_generic"; + + /** + *

+ * General vCard format with the version 3.0. Uses UTF-8 for the charset. + *

+ *

+ * Not fully ready yet. Use with caution when you use this. + *

+ */ + public static final int VCARD_TYPE_V30_GENERIC = + (FLAG_V30 | NAME_ORDER_DEFAULT | FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY); + + /* package */ static final String VCARD_TYPE_V30_GENERIC_STR = "v30_generic"; + + /** + *

+ * General vCard format for the vCard 2.1 with some Europe convension. Uses Utf-8. + * Currently, only name order is considered ("Prefix Middle Given Family Suffix") + *

+ */ + public static final int VCARD_TYPE_V21_EUROPE = + (FLAG_V21 | NAME_ORDER_EUROPE | FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY); + + /* package */ static final String VCARD_TYPE_V21_EUROPE_STR = "v21_europe"; + + /** + *

+ * General vCard format with the version 3.0 with some Europe convension. Uses UTF-8. + *

+ *

+ * Not ready yet. Use with caution when you use this. + *

+ */ + public static final int VCARD_TYPE_V30_EUROPE = + (FLAG_V30 | NAME_ORDER_EUROPE | FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY); + + /* package */ static final String VCARD_TYPE_V30_EUROPE_STR = "v30_europe"; + + /** + *

+ * The vCard 2.1 format for miscellaneous Japanese devices, using UTF-8 as default charset. + *

+ *

+ * Not ready yet. Use with caution when you use this. + *

+ */ + public static final int VCARD_TYPE_V21_JAPANESE = + (FLAG_V21 | NAME_ORDER_JAPANESE | FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY); + + /* package */ static final String VCARD_TYPE_V21_JAPANESE_STR = "v21_japanese_utf8"; + + /** + *

+ * The vCard 3.0 format for miscellaneous Japanese devices, using UTF-8 as default charset. + *

+ *

+ * Not ready yet. Use with caution when you use this. + *

+ */ + public static final int VCARD_TYPE_V30_JAPANESE = + (FLAG_V30 | NAME_ORDER_JAPANESE | FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY); + + /* package */ static final String VCARD_TYPE_V30_JAPANESE_STR = "v30_japanese_utf8"; + + /** + *

+ * The vCard 2.1 based format which (partially) considers the convention in Japanese + * mobile phones, where phonetic names are translated to half-width katakana if + * possible, etc. It would be better to use Shift_JIS as a charset for maximum + * compatibility. + *

+ * @hide Should not be available world wide. + */ + public static final int VCARD_TYPE_V21_JAPANESE_MOBILE = + (FLAG_V21 | NAME_ORDER_JAPANESE | + FLAG_CONVERT_PHONETIC_NAME_STRINGS | FLAG_REFRAIN_QP_TO_NAME_PROPERTIES); + + /* package */ static final String VCARD_TYPE_V21_JAPANESE_MOBILE_STR = "v21_japanese_mobile"; + + /** + *

+ * The vCard format used in DoCoMo, which is one of Japanese mobile phone careers. + *

+ *

+ * Base version is vCard 2.1, but the data has several DoCoMo-specific convensions. + * No Android-specific property nor defact property is included. The "Primary" properties + * are NOT encoded to Quoted-Printable. + *

+ * @hide Should not be available world wide. + */ + public static final int VCARD_TYPE_DOCOMO = + (VCARD_TYPE_V21_JAPANESE_MOBILE | FLAG_DOCOMO); + + /* package */ static final String VCARD_TYPE_DOCOMO_STR = "docomo"; + + public static int VCARD_TYPE_DEFAULT = VCARD_TYPE_V21_GENERIC; + + private static final Map sVCardTypeMap; + private static final Set sJapaneseMobileTypeSet; + + static { + sVCardTypeMap = new HashMap(); + sVCardTypeMap.put(VCARD_TYPE_V21_GENERIC_STR, VCARD_TYPE_V21_GENERIC); + sVCardTypeMap.put(VCARD_TYPE_V30_GENERIC_STR, VCARD_TYPE_V30_GENERIC); + sVCardTypeMap.put(VCARD_TYPE_V21_EUROPE_STR, VCARD_TYPE_V21_EUROPE); + sVCardTypeMap.put(VCARD_TYPE_V30_EUROPE_STR, VCARD_TYPE_V30_EUROPE); + sVCardTypeMap.put(VCARD_TYPE_V21_JAPANESE_STR, VCARD_TYPE_V21_JAPANESE); + sVCardTypeMap.put(VCARD_TYPE_V30_JAPANESE_STR, VCARD_TYPE_V30_JAPANESE); + sVCardTypeMap.put(VCARD_TYPE_V21_JAPANESE_MOBILE_STR, VCARD_TYPE_V21_JAPANESE_MOBILE); + sVCardTypeMap.put(VCARD_TYPE_DOCOMO_STR, VCARD_TYPE_DOCOMO); + + sJapaneseMobileTypeSet = new HashSet(); + sJapaneseMobileTypeSet.add(VCARD_TYPE_V21_JAPANESE); + sJapaneseMobileTypeSet.add(VCARD_TYPE_V30_JAPANESE); + sJapaneseMobileTypeSet.add(VCARD_TYPE_V21_JAPANESE_MOBILE); + sJapaneseMobileTypeSet.add(VCARD_TYPE_DOCOMO); + } + + public static int getVCardTypeFromString(final String vcardTypeString) { + final String loweredKey = vcardTypeString.toLowerCase(); + if (sVCardTypeMap.containsKey(loweredKey)) { + return sVCardTypeMap.get(loweredKey); + } else if ("default".equalsIgnoreCase(vcardTypeString)) { + return VCARD_TYPE_DEFAULT; + } else { + Log.e(LOG_TAG, "Unknown vCard type String: \"" + vcardTypeString + "\""); + return VCARD_TYPE_DEFAULT; + } + } + + public static boolean isV30(final int vcardType) { + return ((vcardType & FLAG_V30) != 0); + } + + public static boolean shouldUseQuotedPrintable(final int vcardType) { + return !isV30(vcardType); + } + + public static int getNameOrderType(final int vcardType) { + return vcardType & NAME_ORDER_MASK; + } + + public static boolean usesAndroidSpecificProperty(final int vcardType) { + return ((vcardType & FLAG_USE_ANDROID_PROPERTY) != 0); + } + + public static boolean usesDefactProperty(final int vcardType) { + return ((vcardType & FLAG_USE_DEFACT_PROPERTY) != 0); + } + + public static boolean showPerformanceLog() { + return (VCardConfig.LOG_LEVEL & VCardConfig.LOG_LEVEL_PERFORMANCE_MEASUREMENT) != 0; + } + + public static boolean shouldRefrainQPToNameProperties(final int vcardType) { + return (!shouldUseQuotedPrintable(vcardType) || + ((vcardType & FLAG_REFRAIN_QP_TO_NAME_PROPERTIES) != 0)); + } + + public static boolean appendTypeParamName(final int vcardType) { + return (isV30(vcardType) || ((vcardType & FLAG_APPEND_TYPE_PARAM) != 0)); + } + + /** + * @return true if the device is Japanese and some Japanese convension is + * applied to creating "formatted" something like FORMATTED_ADDRESS. + */ + public static boolean isJapaneseDevice(final int vcardType) { + // TODO: Some mask will be required so that this method wrongly interpret + // Japanese"-like" vCard type. + // e.g. VCARD_TYPE_V21_JAPANESE_SJIS | FLAG_APPEND_TYPE_PARAMS + return sJapaneseMobileTypeSet.contains(vcardType); + } + + /* package */ static boolean refrainPhoneNumberFormatting(final int vcardType) { + return ((vcardType & FLAG_REFRAIN_PHONE_NUMBER_FORMATTING) != 0); + } + + public static boolean needsToConvertPhoneticString(final int vcardType) { + return ((vcardType & FLAG_CONVERT_PHONETIC_NAME_STRINGS) != 0); + } + + public static boolean onlyOneNoteFieldIsAvailable(final int vcardType) { + return vcardType == VCARD_TYPE_DOCOMO; + } + + public static boolean isDoCoMo(final int vcardType) { + return ((vcardType & FLAG_DOCOMO) != 0); + } + + private VCardConfig() { + } +} \ No newline at end of file diff --git a/vcard/java/com/android/vcard/VCardConstants.java b/vcard/java/com/android/vcard/VCardConstants.java new file mode 100644 index 000000000..862c9edcb --- /dev/null +++ b/vcard/java/com/android/vcard/VCardConstants.java @@ -0,0 +1,160 @@ +/* + * 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.vcard; + +/** + * Constants used in both exporter and importer code. + */ +public class VCardConstants { + public static final String VERSION_V21 = "2.1"; + public static final String VERSION_V30 = "3.0"; + + // The property names valid both in vCard 2.1 and 3.0. + public static final String PROPERTY_BEGIN = "BEGIN"; + public static final String PROPERTY_VERSION = "VERSION"; + public static final String PROPERTY_N = "N"; + public static final String PROPERTY_FN = "FN"; + public static final String PROPERTY_ADR = "ADR"; + public static final String PROPERTY_EMAIL = "EMAIL"; + public static final String PROPERTY_NOTE = "NOTE"; + public static final String PROPERTY_ORG = "ORG"; + public static final String PROPERTY_SOUND = "SOUND"; // Not fully supported. + public static final String PROPERTY_TEL = "TEL"; + public static final String PROPERTY_TITLE = "TITLE"; + public static final String PROPERTY_ROLE = "ROLE"; + public static final String PROPERTY_PHOTO = "PHOTO"; + public static final String PROPERTY_LOGO = "LOGO"; + public static final String PROPERTY_URL = "URL"; + public static final String PROPERTY_BDAY = "BDAY"; // Birthday + public static final String PROPERTY_END = "END"; + + // Valid property names not supported (not appropriately handled) by our vCard importer now. + public static final String PROPERTY_REV = "REV"; + public static final String PROPERTY_AGENT = "AGENT"; + + // Available in vCard 3.0. Shoud not use when composing vCard 2.1 file. + public static final String PROPERTY_NAME = "NAME"; + public static final String PROPERTY_NICKNAME = "NICKNAME"; + public static final String PROPERTY_SORT_STRING = "SORT-STRING"; + + // De-fact property values expressing phonetic names. + public static final String PROPERTY_X_PHONETIC_FIRST_NAME = "X-PHONETIC-FIRST-NAME"; + public static final String PROPERTY_X_PHONETIC_MIDDLE_NAME = "X-PHONETIC-MIDDLE-NAME"; + public static final String PROPERTY_X_PHONETIC_LAST_NAME = "X-PHONETIC-LAST-NAME"; + + // Properties both ContactsStruct in Eclair and de-fact vCard extensions + // shown in http://en.wikipedia.org/wiki/VCard support are defined here. + public static final String PROPERTY_X_AIM = "X-AIM"; + public static final String PROPERTY_X_MSN = "X-MSN"; + public static final String PROPERTY_X_YAHOO = "X-YAHOO"; + public static final String PROPERTY_X_ICQ = "X-ICQ"; + public static final String PROPERTY_X_JABBER = "X-JABBER"; + public static final String PROPERTY_X_GOOGLE_TALK = "X-GOOGLE-TALK"; + public static final String PROPERTY_X_SKYPE_USERNAME = "X-SKYPE-USERNAME"; + // Properties only ContactsStruct has. We alse use this. + public static final String PROPERTY_X_QQ = "X-QQ"; + public static final String PROPERTY_X_NETMEETING = "X-NETMEETING"; + + // Phone number for Skype, available as usual phone. + public static final String PROPERTY_X_SKYPE_PSTNNUMBER = "X-SKYPE-PSTNNUMBER"; + + // Property for Android-specific fields. + public static final String PROPERTY_X_ANDROID_CUSTOM = "X-ANDROID-CUSTOM"; + + // Properties for DoCoMo vCard. + public static final String PROPERTY_X_CLASS = "X-CLASS"; + public static final String PROPERTY_X_REDUCTION = "X-REDUCTION"; + public static final String PROPERTY_X_NO = "X-NO"; + public static final String PROPERTY_X_DCM_HMN_MODE = "X-DCM-HMN-MODE"; + + public static final String PARAM_TYPE = "TYPE"; + + public static final String PARAM_TYPE_HOME = "HOME"; + public static final String PARAM_TYPE_WORK = "WORK"; + public static final String PARAM_TYPE_FAX = "FAX"; + public static final String PARAM_TYPE_CELL = "CELL"; + public static final String PARAM_TYPE_VOICE = "VOICE"; + public static final String PARAM_TYPE_INTERNET = "INTERNET"; + + // Abbreviation of "prefered" according to vCard 2.1 specification. + // We interpret this value as "primary" property during import/export. + // + // Note: Both vCard specs does not mention anything about the requirement for this parameter, + // but there may be some vCard importer which will get confused with more than + // one "PREF"s in one property name, while Android accepts them. + public static final String PARAM_TYPE_PREF = "PREF"; + + // Phone type parameters valid in vCard and known to ContactsContract, but not so common. + public static final String PARAM_TYPE_CAR = "CAR"; + public static final String PARAM_TYPE_ISDN = "ISDN"; + public static final String PARAM_TYPE_PAGER = "PAGER"; + public static final String PARAM_TYPE_TLX = "TLX"; // Telex + + // Phone types existing in vCard 2.1 but not known to ContactsContract. + public static final String PARAM_TYPE_MODEM = "MODEM"; + public static final String PARAM_TYPE_MSG = "MSG"; + public static final String PARAM_TYPE_BBS = "BBS"; + public static final String PARAM_TYPE_VIDEO = "VIDEO"; + + public static final String PARAM_ENCODING_7BIT = "7BIT"; + public static final String PARAM_ENCODING_8BIT = "8BIT"; + public static final String PARAM_ENCODING_QP = "QUOTED-PRINTABLE"; + public static final String PARAM_ENCODING_BASE64 = "BASE64"; // Available in vCard 2.1 + public static final String PARAM_ENCODING_B = "B"; // Available in vCard 3.0 + + // TYPE parameters for Phones, which are not formally valid in vCard (at least 2.1). + // These types are basically encoded to "X-" parameters when composing vCard. + // Parser passes these when "X-" is added to the parameter or not. + public static final String PARAM_PHONE_EXTRA_TYPE_CALLBACK = "CALLBACK"; + public static final String PARAM_PHONE_EXTRA_TYPE_RADIO = "RADIO"; + public static final String PARAM_PHONE_EXTRA_TYPE_TTY_TDD = "TTY-TDD"; + public static final String PARAM_PHONE_EXTRA_TYPE_ASSISTANT = "ASSISTANT"; + // vCard composer translates this type to "WORK" + "PREF". Just for parsing. + public static final String PARAM_PHONE_EXTRA_TYPE_COMPANY_MAIN = "COMPANY-MAIN"; + // vCard composer translates this type to "VOICE" Just for parsing. + public static final String PARAM_PHONE_EXTRA_TYPE_OTHER = "OTHER"; + + // TYPE parameters for postal addresses. + public static final String PARAM_ADR_TYPE_PARCEL = "PARCEL"; + public static final String PARAM_ADR_TYPE_DOM = "DOM"; + public static final String PARAM_ADR_TYPE_INTL = "INTL"; + + // TYPE parameters not officially valid but used in some vCard exporter. + // Do not use in composer side. + public static final String PARAM_EXTRA_TYPE_COMPANY = "COMPANY"; + + public interface ImportOnly { + public static final String PROPERTY_X_NICKNAME = "X-NICKNAME"; + // Some device emits this "X-" parameter for expressing Google Talk, + // which is specifically invalid but should be always properly accepted, and emitted + // in some special case (for that device/application). + public static final String PROPERTY_X_GOOGLE_TALK_WITH_SPACE = "X-GOOGLE TALK"; + } + + //// Mainly for package constants. + + // DoCoMo specific type parameter. Used with "SOUND" property, which is alternate of + // SORT-STRING invCard 3.0. + /* package */ static final String PARAM_TYPE_X_IRMC_N = "X-IRMC-N"; + + /* package */ static final int MAX_DATA_COLUMN = 15; + + /* package */ static final int MAX_CHARACTER_NUMS_QP = 76; + static final int MAX_CHARACTER_NUMS_BASE64_V30 = 75; + + private VCardConstants() { + } +} \ No newline at end of file diff --git a/vcard/java/com/android/vcard/VCardEntry.java b/vcard/java/com/android/vcard/VCardEntry.java new file mode 100644 index 000000000..624407a35 --- /dev/null +++ b/vcard/java/com/android/vcard/VCardEntry.java @@ -0,0 +1,1423 @@ +/* + * 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.vcard; + +import android.accounts.Account; +import android.content.ContentProviderOperation; +import android.content.ContentProviderResult; +import android.content.ContentResolver; +import android.content.OperationApplicationException; +import android.net.Uri; +import android.os.RemoteException; +import android.provider.ContactsContract; +import android.provider.ContactsContract.Contacts; +import android.provider.ContactsContract.Data; +import android.provider.ContactsContract.RawContacts; +import android.provider.ContactsContract.CommonDataKinds.Email; +import android.provider.ContactsContract.CommonDataKinds.Event; +import android.provider.ContactsContract.CommonDataKinds.GroupMembership; +import android.provider.ContactsContract.CommonDataKinds.Im; +import android.provider.ContactsContract.CommonDataKinds.Nickname; +import android.provider.ContactsContract.CommonDataKinds.Note; +import android.provider.ContactsContract.CommonDataKinds.Organization; +import android.provider.ContactsContract.CommonDataKinds.Phone; +import android.provider.ContactsContract.CommonDataKinds.Photo; +import android.provider.ContactsContract.CommonDataKinds.StructuredName; +import android.provider.ContactsContract.CommonDataKinds.StructuredPostal; +import android.provider.ContactsContract.CommonDataKinds.Website; +import android.telephony.PhoneNumberUtils; +import android.text.TextUtils; +import android.util.Log; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; + +/** + * This class bridges between data structure of Contact app and VCard data. + */ +public class VCardEntry { + private static final String LOG_TAG = "VCardEntry"; + + private final static int DEFAULT_ORGANIZATION_TYPE = Organization.TYPE_WORK; + + private static final Map sImMap = new HashMap(); + + static { + sImMap.put(VCardConstants.PROPERTY_X_AIM, Im.PROTOCOL_AIM); + sImMap.put(VCardConstants.PROPERTY_X_MSN, Im.PROTOCOL_MSN); + sImMap.put(VCardConstants.PROPERTY_X_YAHOO, Im.PROTOCOL_YAHOO); + sImMap.put(VCardConstants.PROPERTY_X_ICQ, Im.PROTOCOL_ICQ); + sImMap.put(VCardConstants.PROPERTY_X_JABBER, Im.PROTOCOL_JABBER); + sImMap.put(VCardConstants.PROPERTY_X_SKYPE_USERNAME, Im.PROTOCOL_SKYPE); + sImMap.put(VCardConstants.PROPERTY_X_GOOGLE_TALK, Im.PROTOCOL_GOOGLE_TALK); + sImMap.put(VCardConstants.ImportOnly.PROPERTY_X_GOOGLE_TALK_WITH_SPACE, + Im.PROTOCOL_GOOGLE_TALK); + } + + public static class PhoneData { + public final int type; + public final String data; + public final String label; + // isPrimary is (not final but) changable, only when there's no appropriate one existing + // in the original VCard. + public boolean isPrimary; + public PhoneData(int type, String data, String label, boolean isPrimary) { + this.type = type; + this.data = data; + this.label = label; + this.isPrimary = isPrimary; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof PhoneData)) { + return false; + } + PhoneData phoneData = (PhoneData)obj; + return (type == phoneData.type && data.equals(phoneData.data) && + label.equals(phoneData.label) && isPrimary == phoneData.isPrimary); + } + + @Override + public String toString() { + return String.format("type: %d, data: %s, label: %s, isPrimary: %s", + type, data, label, isPrimary); + } + } + + public static class EmailData { + public final int type; + public final String data; + // Used only when TYPE is TYPE_CUSTOM. + public final String label; + public boolean isPrimary; + public EmailData(int type, String data, String label, boolean isPrimary) { + this.type = type; + this.data = data; + this.label = label; + this.isPrimary = isPrimary; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof EmailData)) { + return false; + } + EmailData emailData = (EmailData)obj; + return (type == emailData.type && data.equals(emailData.data) && + label.equals(emailData.label) && isPrimary == emailData.isPrimary); + } + + @Override + public String toString() { + return String.format("type: %d, data: %s, label: %s, isPrimary: %s", + type, data, label, isPrimary); + } + } + + public static class PostalData { + // Determined by vCard specification. + // - PO Box, Extended Addr, Street, Locality, Region, Postal Code, Country Name + public static final int ADDR_MAX_DATA_SIZE = 7; + private final String[] dataArray; + public final String pobox; + public final String extendedAddress; + public final String street; + public final String localty; + public final String region; + public final String postalCode; + public final String country; + public final int type; + public final String label; + public boolean isPrimary; + + public PostalData(final int type, final List propValueList, + final String label, boolean isPrimary) { + this.type = type; + dataArray = new String[ADDR_MAX_DATA_SIZE]; + + int size = propValueList.size(); + if (size > ADDR_MAX_DATA_SIZE) { + size = ADDR_MAX_DATA_SIZE; + } + + // adr-value = 0*6(text-value ";") text-value + // ; PO Box, Extended Address, Street, Locality, Region, Postal + // ; Code, Country Name + // + // Use Iterator assuming List may be LinkedList, though actually it is + // always ArrayList in the current implementation. + int i = 0; + for (String addressElement : propValueList) { + dataArray[i] = addressElement; + if (++i >= size) { + break; + } + } + while (i < ADDR_MAX_DATA_SIZE) { + dataArray[i++] = null; + } + + this.pobox = dataArray[0]; + this.extendedAddress = dataArray[1]; + this.street = dataArray[2]; + this.localty = dataArray[3]; + this.region = dataArray[4]; + this.postalCode = dataArray[5]; + this.country = dataArray[6]; + this.label = label; + this.isPrimary = isPrimary; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof PostalData)) { + return false; + } + final PostalData postalData = (PostalData)obj; + return (Arrays.equals(dataArray, postalData.dataArray) && + (type == postalData.type && + (type == StructuredPostal.TYPE_CUSTOM ? + (label == postalData.label) : true)) && + (isPrimary == postalData.isPrimary)); + } + + public String getFormattedAddress(final int vcardType) { + StringBuilder builder = new StringBuilder(); + boolean empty = true; + if (VCardConfig.isJapaneseDevice(vcardType)) { + // In Japan, the order is reversed. + for (int i = ADDR_MAX_DATA_SIZE - 1; i >= 0; i--) { + String addressPart = dataArray[i]; + if (!TextUtils.isEmpty(addressPart)) { + if (!empty) { + builder.append(' '); + } else { + empty = false; + } + builder.append(addressPart); + } + } + } else { + for (int i = 0; i < ADDR_MAX_DATA_SIZE; i++) { + String addressPart = dataArray[i]; + if (!TextUtils.isEmpty(addressPart)) { + if (!empty) { + builder.append(' '); + } else { + empty = false; + } + builder.append(addressPart); + } + } + } + + return builder.toString().trim(); + } + + @Override + public String toString() { + return String.format("type: %d, label: %s, isPrimary: %s", + type, label, isPrimary); + } + } + + public static class OrganizationData { + public final int type; + // non-final is Intentional: we may change the values since this info is separated into + // two parts in vCard: "ORG" + "TITLE", and we have to cope with each field in + // different timing. + public String companyName; + public String departmentName; + public String titleName; + public boolean isPrimary; + + public OrganizationData(int type, + String companyName, + String departmentName, + String titleName, + boolean isPrimary) { + this.type = type; + this.companyName = companyName; + this.departmentName = departmentName; + this.titleName = titleName; + this.isPrimary = isPrimary; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof OrganizationData)) { + return false; + } + OrganizationData organization = (OrganizationData)obj; + return (type == organization.type && + TextUtils.equals(companyName, organization.companyName) && + TextUtils.equals(departmentName, organization.departmentName) && + TextUtils.equals(titleName, organization.titleName) && + isPrimary == organization.isPrimary); + } + + public String getFormattedString() { + final StringBuilder builder = new StringBuilder(); + if (!TextUtils.isEmpty(companyName)) { + builder.append(companyName); + } + + if (!TextUtils.isEmpty(departmentName)) { + if (builder.length() > 0) { + builder.append(", "); + } + builder.append(departmentName); + } + + if (!TextUtils.isEmpty(titleName)) { + if (builder.length() > 0) { + builder.append(", "); + } + builder.append(titleName); + } + + return builder.toString(); + } + + @Override + public String toString() { + return String.format( + "type: %d, company: %s, department: %s, title: %s, isPrimary: %s", + type, companyName, departmentName, titleName, isPrimary); + } + } + + public static class ImData { + public final int protocol; + public final String customProtocol; + public final int type; + public final String data; + public final boolean isPrimary; + + public ImData(final int protocol, final String customProtocol, final int type, + final String data, final boolean isPrimary) { + this.protocol = protocol; + this.customProtocol = customProtocol; + this.type = type; + this.data = data; + this.isPrimary = isPrimary; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof ImData)) { + return false; + } + ImData imData = (ImData)obj; + return (type == imData.type && protocol == imData.protocol + && (customProtocol != null ? customProtocol.equals(imData.customProtocol) : + (imData.customProtocol == null)) + && (data != null ? data.equals(imData.data) : (imData.data == null)) + && isPrimary == imData.isPrimary); + } + + @Override + public String toString() { + return String.format( + "type: %d, protocol: %d, custom_protcol: %s, data: %s, isPrimary: %s", + type, protocol, customProtocol, data, isPrimary); + } + } + + public static class PhotoData { + public static final String FORMAT_FLASH = "SWF"; + public final int type; + public final String formatName; // used when type is not defined in ContactsContract. + public final byte[] photoBytes; + public final boolean isPrimary; + + public PhotoData(int type, String formatName, byte[] photoBytes, boolean isPrimary) { + this.type = type; + this.formatName = formatName; + this.photoBytes = photoBytes; + this.isPrimary = isPrimary; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof PhotoData)) { + return false; + } + PhotoData photoData = (PhotoData)obj; + return (type == photoData.type && + (formatName == null ? (photoData.formatName == null) : + formatName.equals(photoData.formatName)) && + (Arrays.equals(photoBytes, photoData.photoBytes)) && + (isPrimary == photoData.isPrimary)); + } + + @Override + public String toString() { + return String.format("type: %d, format: %s: size: %d, isPrimary: %s", + type, formatName, photoBytes.length, isPrimary); + } + } + + /* package */ static class Property { + private String mPropertyName; + private Map> mParameterMap = + new HashMap>(); + private List mPropertyValueList = new ArrayList(); + private byte[] mPropertyBytes; + + public void setPropertyName(final String propertyName) { + mPropertyName = propertyName; + } + + public void addParameter(final String paramName, final String paramValue) { + Collection values; + if (!mParameterMap.containsKey(paramName)) { + if (paramName.equals("TYPE")) { + values = new HashSet(); + } else { + values = new ArrayList(); + } + mParameterMap.put(paramName, values); + } else { + values = mParameterMap.get(paramName); + } + values.add(paramValue); + } + + public void addToPropertyValueList(final String propertyValue) { + mPropertyValueList.add(propertyValue); + } + + public void setPropertyBytes(final byte[] propertyBytes) { + mPropertyBytes = propertyBytes; + } + + public final Collection getParameters(String type) { + return mParameterMap.get(type); + } + + public final List getPropertyValueList() { + return mPropertyValueList; + } + + public void clear() { + mPropertyName = null; + mParameterMap.clear(); + mPropertyValueList.clear(); + mPropertyBytes = null; + } + } + + private String mFamilyName; + private String mGivenName; + private String mMiddleName; + private String mPrefix; + private String mSuffix; + + // Used only when no family nor given name is found. + private String mFormattedName; + + private String mPhoneticFamilyName; + private String mPhoneticGivenName; + private String mPhoneticMiddleName; + + private String mPhoneticFullName; + + private List mNickNameList; + + private String mDisplayName; + + private String mBirthday; + + private List mNoteList; + private List mPhoneList; + private List mEmailList; + private List mPostalList; + private List mOrganizationList; + private List mImList; + private List mPhotoList; + private List mWebsiteList; + private List> mAndroidCustomPropertyList; + + private final int mVCardType; + private final Account mAccount; + + public VCardEntry() { + this(VCardConfig.VCARD_TYPE_V21_GENERIC); + } + + public VCardEntry(int vcardType) { + this(vcardType, null); + } + + public VCardEntry(int vcardType, Account account) { + mVCardType = vcardType; + mAccount = account; + } + + private void addPhone(int type, String data, String label, boolean isPrimary) { + if (mPhoneList == null) { + mPhoneList = new ArrayList(); + } + final StringBuilder builder = new StringBuilder(); + final String trimed = data.trim(); + final String formattedNumber; + if (type == Phone.TYPE_PAGER || VCardConfig.refrainPhoneNumberFormatting(mVCardType)) { + formattedNumber = trimed; + } else { + final int length = trimed.length(); + for (int i = 0; i < length; i++) { + char ch = trimed.charAt(i); + if (('0' <= ch && ch <= '9') || (i == 0 && ch == '+')) { + builder.append(ch); + } + } + + final int formattingType = VCardUtils.getPhoneNumberFormat(mVCardType); + formattedNumber = PhoneNumberUtils.formatNumber(builder.toString(), formattingType); + } + PhoneData phoneData = new PhoneData(type, formattedNumber, label, isPrimary); + mPhoneList.add(phoneData); + } + + private void addNickName(final String nickName) { + if (mNickNameList == null) { + mNickNameList = new ArrayList(); + } + mNickNameList.add(nickName); + } + + private void addEmail(int type, String data, String label, boolean isPrimary){ + if (mEmailList == null) { + mEmailList = new ArrayList(); + } + mEmailList.add(new EmailData(type, data, label, isPrimary)); + } + + private void addPostal(int type, List propValueList, String label, boolean isPrimary){ + if (mPostalList == null) { + mPostalList = new ArrayList(0); + } + mPostalList.add(new PostalData(type, propValueList, label, isPrimary)); + } + + /** + * Should be called via {@link #handleOrgValue(int, List, boolean)} or + * {@link #handleTitleValue(String)}. + */ + private void addNewOrganization(int type, final String companyName, + final String departmentName, + final String titleName, boolean isPrimary) { + if (mOrganizationList == null) { + mOrganizationList = new ArrayList(); + } + mOrganizationList.add(new OrganizationData(type, companyName, + departmentName, titleName, isPrimary)); + } + + private static final List sEmptyList = + Collections.unmodifiableList(new ArrayList(0)); + + /** + * Set "ORG" related values to the appropriate data. If there's more than one + * {@link OrganizationData} objects, this input data are attached to the last one which + * does not have valid values (not including empty but only null). If there's no + * {@link OrganizationData} object, a new {@link OrganizationData} is created, + * whose title is set to null. + */ + private void handleOrgValue(final int type, List orgList, boolean isPrimary) { + if (orgList == null) { + orgList = sEmptyList; + } + final String companyName; + final String departmentName; + final int size = orgList.size(); + switch (size) { + case 0: { + companyName = ""; + departmentName = null; + break; + } + case 1: { + companyName = orgList.get(0); + departmentName = null; + break; + } + default: { // More than 1. + companyName = orgList.get(0); + // We're not sure which is the correct string for department. + // In order to keep all the data, concatinate the rest of elements. + StringBuilder builder = new StringBuilder(); + for (int i = 1; i < size; i++) { + if (i > 1) { + builder.append(' '); + } + builder.append(orgList.get(i)); + } + departmentName = builder.toString(); + } + } + if (mOrganizationList == null) { + // Create new first organization entry, with "null" title which may be + // added via handleTitleValue(). + addNewOrganization(type, companyName, departmentName, null, isPrimary); + return; + } + for (OrganizationData organizationData : mOrganizationList) { + // Not use TextUtils.isEmpty() since ORG was set but the elements might be empty. + // e.g. "ORG;PREF:;" -> Both companyName and departmentName become empty but not null. + if (organizationData.companyName == null && + organizationData.departmentName == null) { + // Probably the "TITLE" property comes before the "ORG" property via + // handleTitleLine(). + organizationData.companyName = companyName; + organizationData.departmentName = departmentName; + organizationData.isPrimary = isPrimary; + return; + } + } + // No OrganizatioData is available. Create another one, with "null" title, which may be + // added via handleTitleValue(). + addNewOrganization(type, companyName, departmentName, null, isPrimary); + } + + /** + * Set "title" value to the appropriate data. If there's more than one + * OrganizationData objects, this input is attached to the last one which does not + * have valid title value (not including empty but only null). If there's no + * OrganizationData object, a new OrganizationData is created, whose company name is + * set to null. + */ + private void handleTitleValue(final String title) { + if (mOrganizationList == null) { + // Create new first organization entry, with "null" other info, which may be + // added via handleOrgValue(). + addNewOrganization(DEFAULT_ORGANIZATION_TYPE, null, null, title, false); + return; + } + for (OrganizationData organizationData : mOrganizationList) { + if (organizationData.titleName == null) { + organizationData.titleName = title; + return; + } + } + // No Organization is available. Create another one, with "null" other info, which may be + // added via handleOrgValue(). + addNewOrganization(DEFAULT_ORGANIZATION_TYPE, null, null, title, false); + } + + private void addIm(int protocol, String customProtocol, int type, + String propValue, boolean isPrimary) { + if (mImList == null) { + mImList = new ArrayList(); + } + mImList.add(new ImData(protocol, customProtocol, type, propValue, isPrimary)); + } + + private void addNote(final String note) { + if (mNoteList == null) { + mNoteList = new ArrayList(1); + } + mNoteList.add(note); + } + + private void addPhotoBytes(String formatName, byte[] photoBytes, boolean isPrimary) { + if (mPhotoList == null) { + mPhotoList = new ArrayList(1); + } + final PhotoData photoData = new PhotoData(0, null, photoBytes, isPrimary); + mPhotoList.add(photoData); + } + + @SuppressWarnings("fallthrough") + private void handleNProperty(List elems) { + // Family, Given, Middle, Prefix, Suffix. (1 - 5) + int size; + if (elems == null || (size = elems.size()) < 1) { + return; + } + if (size > 5) { + size = 5; + } + + switch (size) { + // fallthrough + case 5: mSuffix = elems.get(4); + case 4: mPrefix = elems.get(3); + case 3: mMiddleName = elems.get(2); + case 2: mGivenName = elems.get(1); + default: mFamilyName = elems.get(0); + } + } + + /** + * Note: Some Japanese mobile phones use this field for phonetic name, + * since vCard 2.1 does not have "SORT-STRING" type. + * Also, in some cases, the field has some ';'s in it. + * Assume the ';' means the same meaning in N property + */ + @SuppressWarnings("fallthrough") + private void handlePhoneticNameFromSound(List elems) { + if (!(TextUtils.isEmpty(mPhoneticFamilyName) && + TextUtils.isEmpty(mPhoneticMiddleName) && + TextUtils.isEmpty(mPhoneticGivenName))) { + // This means the other properties like "X-PHONETIC-FIRST-NAME" was already found. + // Ignore "SOUND;X-IRMC-N". + return; + } + + int size; + if (elems == null || (size = elems.size()) < 1) { + return; + } + + // Assume that the order is "Family, Given, Middle". + // This is not from specification but mere assumption. Some Japanese phones use this order. + if (size > 3) { + size = 3; + } + + if (elems.get(0).length() > 0) { + boolean onlyFirstElemIsNonEmpty = true; + for (int i = 1; i < size; i++) { + if (elems.get(i).length() > 0) { + onlyFirstElemIsNonEmpty = false; + break; + } + } + if (onlyFirstElemIsNonEmpty) { + final String[] namesArray = elems.get(0).split(" "); + final int nameArrayLength = namesArray.length; + if (nameArrayLength == 3) { + // Assume the string is "Family Middle Given". + mPhoneticFamilyName = namesArray[0]; + mPhoneticMiddleName = namesArray[1]; + mPhoneticGivenName = namesArray[2]; + } else if (nameArrayLength == 2) { + // Assume the string is "Family Given" based on the Japanese mobile + // phones' preference. + mPhoneticFamilyName = namesArray[0]; + mPhoneticGivenName = namesArray[1]; + } else { + mPhoneticFullName = elems.get(0); + } + return; + } + } + + switch (size) { + // fallthrough + case 3: mPhoneticMiddleName = elems.get(2); + case 2: mPhoneticGivenName = elems.get(1); + default: mPhoneticFamilyName = elems.get(0); + } + } + + public void addProperty(final Property property) { + final String propName = property.mPropertyName; + final Map> paramMap = property.mParameterMap; + final List propValueList = property.mPropertyValueList; + byte[] propBytes = property.mPropertyBytes; + + if (propValueList.size() == 0) { + return; + } + final String propValue = listToString(propValueList).trim(); + + if (propName.equals(VCardConstants.PROPERTY_VERSION)) { + // vCard version. Ignore this. + } else if (propName.equals(VCardConstants.PROPERTY_FN)) { + mFormattedName = propValue; + } else if (propName.equals(VCardConstants.PROPERTY_NAME) && mFormattedName == null) { + // Only in vCard 3.0. Use this if FN, which must exist in vCard 3.0 but may not + // actually exist in the real vCard data, does not exist. + mFormattedName = propValue; + } else if (propName.equals(VCardConstants.PROPERTY_N)) { + handleNProperty(propValueList); + } else if (propName.equals(VCardConstants.PROPERTY_SORT_STRING)) { + mPhoneticFullName = propValue; + } else if (propName.equals(VCardConstants.PROPERTY_NICKNAME) || + propName.equals(VCardConstants.ImportOnly.PROPERTY_X_NICKNAME)) { + addNickName(propValue); + } else if (propName.equals(VCardConstants.PROPERTY_SOUND)) { + Collection typeCollection = paramMap.get(VCardConstants.PARAM_TYPE); + if (typeCollection != null + && typeCollection.contains(VCardConstants.PARAM_TYPE_X_IRMC_N)) { + // As of 2009-10-08, Parser side does not split a property value into separated + // values using ';' (in other words, propValueList.size() == 1), + // which is correct behavior from the view of vCard 2.1. + // But we want it to be separated, so do the separation here. + final List phoneticNameList = + VCardUtils.constructListFromValue(propValue, + VCardConfig.isV30(mVCardType)); + handlePhoneticNameFromSound(phoneticNameList); + } else { + // Ignore this field since Android cannot understand what it is. + } + } else if (propName.equals(VCardConstants.PROPERTY_ADR)) { + boolean valuesAreAllEmpty = true; + for (String value : propValueList) { + if (value.length() > 0) { + valuesAreAllEmpty = false; + break; + } + } + if (valuesAreAllEmpty) { + return; + } + + int type = -1; + String label = ""; + boolean isPrimary = false; + Collection typeCollection = paramMap.get(VCardConstants.PARAM_TYPE); + if (typeCollection != null) { + for (String typeString : typeCollection) { + typeString = typeString.toUpperCase(); + if (typeString.equals(VCardConstants.PARAM_TYPE_PREF)) { + isPrimary = true; + } else if (typeString.equals(VCardConstants.PARAM_TYPE_HOME)) { + type = StructuredPostal.TYPE_HOME; + label = ""; + } else if (typeString.equals(VCardConstants.PARAM_TYPE_WORK) || + typeString.equalsIgnoreCase(VCardConstants.PARAM_EXTRA_TYPE_COMPANY)) { + // "COMPANY" seems emitted by Windows Mobile, which is not + // specifically supported by vCard 2.1. We assume this is same + // as "WORK". + type = StructuredPostal.TYPE_WORK; + label = ""; + } else if (typeString.equals(VCardConstants.PARAM_ADR_TYPE_PARCEL) || + typeString.equals(VCardConstants.PARAM_ADR_TYPE_DOM) || + typeString.equals(VCardConstants.PARAM_ADR_TYPE_INTL)) { + // We do not have any appropriate way to store this information. + } else { + if (typeString.startsWith("X-") && type < 0) { + typeString = typeString.substring(2); + } + // vCard 3.0 allows iana-token. Also some vCard 2.1 exporters + // emit non-standard types. We do not handle their values now. + type = StructuredPostal.TYPE_CUSTOM; + label = typeString; + } + } + } + // We use "HOME" as default + if (type < 0) { + type = StructuredPostal.TYPE_HOME; + } + + addPostal(type, propValueList, label, isPrimary); + } else if (propName.equals(VCardConstants.PROPERTY_EMAIL)) { + int type = -1; + String label = null; + boolean isPrimary = false; + Collection typeCollection = paramMap.get(VCardConstants.PARAM_TYPE); + if (typeCollection != null) { + for (String typeString : typeCollection) { + typeString = typeString.toUpperCase(); + if (typeString.equals(VCardConstants.PARAM_TYPE_PREF)) { + isPrimary = true; + } else if (typeString.equals(VCardConstants.PARAM_TYPE_HOME)) { + type = Email.TYPE_HOME; + } else if (typeString.equals(VCardConstants.PARAM_TYPE_WORK)) { + type = Email.TYPE_WORK; + } else if (typeString.equals(VCardConstants.PARAM_TYPE_CELL)) { + type = Email.TYPE_MOBILE; + } else { + if (typeString.startsWith("X-") && type < 0) { + typeString = typeString.substring(2); + } + // vCard 3.0 allows iana-token. + // We may have INTERNET (specified in vCard spec), + // SCHOOL, etc. + type = Email.TYPE_CUSTOM; + label = typeString; + } + } + } + if (type < 0) { + type = Email.TYPE_OTHER; + } + addEmail(type, propValue, label, isPrimary); + } else if (propName.equals(VCardConstants.PROPERTY_ORG)) { + // vCard specification does not specify other types. + final int type = Organization.TYPE_WORK; + boolean isPrimary = false; + Collection typeCollection = paramMap.get(VCardConstants.PARAM_TYPE); + if (typeCollection != null) { + for (String typeString : typeCollection) { + if (typeString.equals(VCardConstants.PARAM_TYPE_PREF)) { + isPrimary = true; + } + } + } + handleOrgValue(type, propValueList, isPrimary); + } else if (propName.equals(VCardConstants.PROPERTY_TITLE)) { + handleTitleValue(propValue); + } else if (propName.equals(VCardConstants.PROPERTY_ROLE)) { + // This conflicts with TITLE. Ignore for now... + // handleTitleValue(propValue); + } else if (propName.equals(VCardConstants.PROPERTY_PHOTO) || + propName.equals(VCardConstants.PROPERTY_LOGO)) { + Collection paramMapValue = paramMap.get("VALUE"); + if (paramMapValue != null && paramMapValue.contains("URL")) { + // Currently we do not have appropriate example for testing this case. + } else { + final Collection typeCollection = paramMap.get("TYPE"); + String formatName = null; + boolean isPrimary = false; + if (typeCollection != null) { + for (String typeValue : typeCollection) { + if (VCardConstants.PARAM_TYPE_PREF.equals(typeValue)) { + isPrimary = true; + } else if (formatName == null){ + formatName = typeValue; + } + } + } + addPhotoBytes(formatName, propBytes, isPrimary); + } + } else if (propName.equals(VCardConstants.PROPERTY_TEL)) { + final Collection typeCollection = paramMap.get(VCardConstants.PARAM_TYPE); + final Object typeObject = + VCardUtils.getPhoneTypeFromStrings(typeCollection, propValue); + final int type; + final String label; + if (typeObject instanceof Integer) { + type = (Integer)typeObject; + label = null; + } else { + type = Phone.TYPE_CUSTOM; + label = typeObject.toString(); + } + + final boolean isPrimary; + if (typeCollection != null && typeCollection.contains(VCardConstants.PARAM_TYPE_PREF)) { + isPrimary = true; + } else { + isPrimary = false; + } + addPhone(type, propValue, label, isPrimary); + } else if (propName.equals(VCardConstants.PROPERTY_X_SKYPE_PSTNNUMBER)) { + // The phone number available via Skype. + Collection typeCollection = paramMap.get(VCardConstants.PARAM_TYPE); + final int type = Phone.TYPE_OTHER; + final boolean isPrimary; + if (typeCollection != null && typeCollection.contains(VCardConstants.PARAM_TYPE_PREF)) { + isPrimary = true; + } else { + isPrimary = false; + } + addPhone(type, propValue, null, isPrimary); + } else if (sImMap.containsKey(propName)) { + final int protocol = sImMap.get(propName); + boolean isPrimary = false; + int type = -1; + final Collection typeCollection = paramMap.get(VCardConstants.PARAM_TYPE); + if (typeCollection != null) { + for (String typeString : typeCollection) { + if (typeString.equals(VCardConstants.PARAM_TYPE_PREF)) { + isPrimary = true; + } else if (type < 0) { + if (typeString.equalsIgnoreCase(VCardConstants.PARAM_TYPE_HOME)) { + type = Im.TYPE_HOME; + } else if (typeString.equalsIgnoreCase(VCardConstants.PARAM_TYPE_WORK)) { + type = Im.TYPE_WORK; + } + } + } + } + if (type < 0) { + type = Phone.TYPE_HOME; + } + addIm(protocol, null, type, propValue, isPrimary); + } else if (propName.equals(VCardConstants.PROPERTY_NOTE)) { + addNote(propValue); + } else if (propName.equals(VCardConstants.PROPERTY_URL)) { + if (mWebsiteList == null) { + mWebsiteList = new ArrayList(1); + } + mWebsiteList.add(propValue); + } else if (propName.equals(VCardConstants.PROPERTY_BDAY)) { + mBirthday = propValue; + } else if (propName.equals(VCardConstants.PROPERTY_X_PHONETIC_FIRST_NAME)) { + mPhoneticGivenName = propValue; + } else if (propName.equals(VCardConstants.PROPERTY_X_PHONETIC_MIDDLE_NAME)) { + mPhoneticMiddleName = propValue; + } else if (propName.equals(VCardConstants.PROPERTY_X_PHONETIC_LAST_NAME)) { + mPhoneticFamilyName = propValue; + } else if (propName.equals(VCardConstants.PROPERTY_X_ANDROID_CUSTOM)) { + final List customPropertyList = + VCardUtils.constructListFromValue(propValue, + VCardConfig.isV30(mVCardType)); + handleAndroidCustomProperty(customPropertyList); + /*} else if (propName.equals("REV")) { + // Revision of this VCard entry. I think we can ignore this. + } else if (propName.equals("UID")) { + } else if (propName.equals("KEY")) { + // Type is X509 or PGP? I don't know how to handle this... + } else if (propName.equals("MAILER")) { + } else if (propName.equals("TZ")) { + } else if (propName.equals("GEO")) { + } else if (propName.equals("CLASS")) { + // vCard 3.0 only. + // e.g. CLASS:CONFIDENTIAL + } else if (propName.equals("PROFILE")) { + // VCard 3.0 only. Must be "VCARD". I think we can ignore this. + } else if (propName.equals("CATEGORIES")) { + // VCard 3.0 only. + // e.g. CATEGORIES:INTERNET,IETF,INDUSTRY,INFORMATION TECHNOLOGY + } else if (propName.equals("SOURCE")) { + // VCard 3.0 only. + } else if (propName.equals("PRODID")) { + // VCard 3.0 only. + // To specify the identifier for the product that created + // the vCard object.*/ + } else { + // Unknown X- words and IANA token. + } + } + + private void handleAndroidCustomProperty(final List customPropertyList) { + if (mAndroidCustomPropertyList == null) { + mAndroidCustomPropertyList = new ArrayList>(); + } + mAndroidCustomPropertyList.add(customPropertyList); + } + + /** + * Construct the display name. The constructed data must not be null. + */ + private void constructDisplayName() { + // FullName (created via "FN" or "NAME" field) is prefered. + if (!TextUtils.isEmpty(mFormattedName)) { + mDisplayName = mFormattedName; + } else if (!(TextUtils.isEmpty(mFamilyName) && TextUtils.isEmpty(mGivenName))) { + mDisplayName = VCardUtils.constructNameFromElements(mVCardType, + mFamilyName, mMiddleName, mGivenName, mPrefix, mSuffix); + } else if (!(TextUtils.isEmpty(mPhoneticFamilyName) && + TextUtils.isEmpty(mPhoneticGivenName))) { + mDisplayName = VCardUtils.constructNameFromElements(mVCardType, + mPhoneticFamilyName, mPhoneticMiddleName, mPhoneticGivenName); + } else if (mEmailList != null && mEmailList.size() > 0) { + mDisplayName = mEmailList.get(0).data; + } else if (mPhoneList != null && mPhoneList.size() > 0) { + mDisplayName = mPhoneList.get(0).data; + } else if (mPostalList != null && mPostalList.size() > 0) { + mDisplayName = mPostalList.get(0).getFormattedAddress(mVCardType); + } else if (mOrganizationList != null && mOrganizationList.size() > 0) { + mDisplayName = mOrganizationList.get(0).getFormattedString(); + } + + if (mDisplayName == null) { + mDisplayName = ""; + } + } + + /** + * Consolidate several fielsds (like mName) using name candidates, + */ + public void consolidateFields() { + constructDisplayName(); + + if (mPhoneticFullName != null) { + mPhoneticFullName = mPhoneticFullName.trim(); + } + } + + public Uri pushIntoContentResolver(ContentResolver resolver) { + ArrayList operationList = + new ArrayList(); + // After applying the batch the first result's Uri is returned so it is important that + // the RawContact is the first operation that gets inserted into the list + ContentProviderOperation.Builder builder = + ContentProviderOperation.newInsert(RawContacts.CONTENT_URI); + String myGroupsId = null; + if (mAccount != null) { + builder.withValue(RawContacts.ACCOUNT_NAME, mAccount.name); + builder.withValue(RawContacts.ACCOUNT_TYPE, mAccount.type); + } else { + builder.withValue(RawContacts.ACCOUNT_NAME, null); + builder.withValue(RawContacts.ACCOUNT_TYPE, null); + } + operationList.add(builder.build()); + + if (!nameFieldsAreEmpty()) { + builder = ContentProviderOperation.newInsert(Data.CONTENT_URI); + builder.withValueBackReference(StructuredName.RAW_CONTACT_ID, 0); + builder.withValue(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE); + + builder.withValue(StructuredName.GIVEN_NAME, mGivenName); + builder.withValue(StructuredName.FAMILY_NAME, mFamilyName); + builder.withValue(StructuredName.MIDDLE_NAME, mMiddleName); + builder.withValue(StructuredName.PREFIX, mPrefix); + builder.withValue(StructuredName.SUFFIX, mSuffix); + + if (!(TextUtils.isEmpty(mPhoneticGivenName) + && TextUtils.isEmpty(mPhoneticFamilyName) + && TextUtils.isEmpty(mPhoneticMiddleName))) { + builder.withValue(StructuredName.PHONETIC_GIVEN_NAME, mPhoneticGivenName); + builder.withValue(StructuredName.PHONETIC_FAMILY_NAME, mPhoneticFamilyName); + builder.withValue(StructuredName.PHONETIC_MIDDLE_NAME, mPhoneticMiddleName); + } else if (!TextUtils.isEmpty(mPhoneticFullName)) { + builder.withValue(StructuredName.PHONETIC_GIVEN_NAME, mPhoneticFullName); + } + + builder.withValue(StructuredName.DISPLAY_NAME, getDisplayName()); + operationList.add(builder.build()); + } + + if (mNickNameList != null && mNickNameList.size() > 0) { + for (String nickName : mNickNameList) { + builder = ContentProviderOperation.newInsert(Data.CONTENT_URI); + builder.withValueBackReference(Nickname.RAW_CONTACT_ID, 0); + builder.withValue(Data.MIMETYPE, Nickname.CONTENT_ITEM_TYPE); + builder.withValue(Nickname.TYPE, Nickname.TYPE_DEFAULT); + builder.withValue(Nickname.NAME, nickName); + operationList.add(builder.build()); + } + } + + if (mPhoneList != null) { + for (PhoneData phoneData : mPhoneList) { + builder = ContentProviderOperation.newInsert(Data.CONTENT_URI); + builder.withValueBackReference(Phone.RAW_CONTACT_ID, 0); + builder.withValue(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE); + + builder.withValue(Phone.TYPE, phoneData.type); + if (phoneData.type == Phone.TYPE_CUSTOM) { + builder.withValue(Phone.LABEL, phoneData.label); + } + builder.withValue(Phone.NUMBER, phoneData.data); + if (phoneData.isPrimary) { + builder.withValue(Phone.IS_PRIMARY, 1); + } + operationList.add(builder.build()); + } + } + + if (mOrganizationList != null) { + for (OrganizationData organizationData : mOrganizationList) { + builder = ContentProviderOperation.newInsert(Data.CONTENT_URI); + builder.withValueBackReference(Organization.RAW_CONTACT_ID, 0); + builder.withValue(Data.MIMETYPE, Organization.CONTENT_ITEM_TYPE); + builder.withValue(Organization.TYPE, organizationData.type); + if (organizationData.companyName != null) { + builder.withValue(Organization.COMPANY, organizationData.companyName); + } + if (organizationData.departmentName != null) { + builder.withValue(Organization.DEPARTMENT, organizationData.departmentName); + } + if (organizationData.titleName != null) { + builder.withValue(Organization.TITLE, organizationData.titleName); + } + if (organizationData.isPrimary) { + builder.withValue(Organization.IS_PRIMARY, 1); + } + operationList.add(builder.build()); + } + } + + if (mEmailList != null) { + for (EmailData emailData : mEmailList) { + builder = ContentProviderOperation.newInsert(Data.CONTENT_URI); + builder.withValueBackReference(Email.RAW_CONTACT_ID, 0); + builder.withValue(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE); + + builder.withValue(Email.TYPE, emailData.type); + if (emailData.type == Email.TYPE_CUSTOM) { + builder.withValue(Email.LABEL, emailData.label); + } + builder.withValue(Email.DATA, emailData.data); + if (emailData.isPrimary) { + builder.withValue(Data.IS_PRIMARY, 1); + } + operationList.add(builder.build()); + } + } + + if (mPostalList != null) { + for (PostalData postalData : mPostalList) { + builder = ContentProviderOperation.newInsert(Data.CONTENT_URI); + VCardUtils.insertStructuredPostalDataUsingContactsStruct( + mVCardType, builder, postalData); + operationList.add(builder.build()); + } + } + + if (mImList != null) { + for (ImData imData : mImList) { + builder = ContentProviderOperation.newInsert(Data.CONTENT_URI); + builder.withValueBackReference(Im.RAW_CONTACT_ID, 0); + builder.withValue(Data.MIMETYPE, Im.CONTENT_ITEM_TYPE); + builder.withValue(Im.TYPE, imData.type); + builder.withValue(Im.PROTOCOL, imData.protocol); + if (imData.protocol == Im.PROTOCOL_CUSTOM) { + builder.withValue(Im.CUSTOM_PROTOCOL, imData.customProtocol); + } + if (imData.isPrimary) { + builder.withValue(Data.IS_PRIMARY, 1); + } + } + } + + if (mNoteList != null) { + for (String note : mNoteList) { + builder = ContentProviderOperation.newInsert(Data.CONTENT_URI); + builder.withValueBackReference(Note.RAW_CONTACT_ID, 0); + builder.withValue(Data.MIMETYPE, Note.CONTENT_ITEM_TYPE); + builder.withValue(Note.NOTE, note); + operationList.add(builder.build()); + } + } + + if (mPhotoList != null) { + for (PhotoData photoData : mPhotoList) { + builder = ContentProviderOperation.newInsert(Data.CONTENT_URI); + builder.withValueBackReference(Photo.RAW_CONTACT_ID, 0); + builder.withValue(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE); + builder.withValue(Photo.PHOTO, photoData.photoBytes); + if (photoData.isPrimary) { + builder.withValue(Photo.IS_PRIMARY, 1); + } + operationList.add(builder.build()); + } + } + + if (mWebsiteList != null) { + for (String website : mWebsiteList) { + builder = ContentProviderOperation.newInsert(Data.CONTENT_URI); + builder.withValueBackReference(Website.RAW_CONTACT_ID, 0); + builder.withValue(Data.MIMETYPE, Website.CONTENT_ITEM_TYPE); + builder.withValue(Website.URL, website); + // There's no information about the type of URL in vCard. + // We use TYPE_HOMEPAGE for safety. + builder.withValue(Website.TYPE, Website.TYPE_HOMEPAGE); + operationList.add(builder.build()); + } + } + + if (!TextUtils.isEmpty(mBirthday)) { + builder = ContentProviderOperation.newInsert(Data.CONTENT_URI); + builder.withValueBackReference(Event.RAW_CONTACT_ID, 0); + builder.withValue(Data.MIMETYPE, Event.CONTENT_ITEM_TYPE); + builder.withValue(Event.START_DATE, mBirthday); + builder.withValue(Event.TYPE, Event.TYPE_BIRTHDAY); + operationList.add(builder.build()); + } + + if (mAndroidCustomPropertyList != null) { + for (List customPropertyList : mAndroidCustomPropertyList) { + int size = customPropertyList.size(); + if (size < 2 || TextUtils.isEmpty(customPropertyList.get(0))) { + continue; + } else if (size > VCardConstants.MAX_DATA_COLUMN + 1) { + size = VCardConstants.MAX_DATA_COLUMN + 1; + customPropertyList = + customPropertyList.subList(0, VCardConstants.MAX_DATA_COLUMN + 2); + } + + int i = 0; + for (final String customPropertyValue : customPropertyList) { + if (i == 0) { + final String mimeType = customPropertyValue; + builder = ContentProviderOperation.newInsert(Data.CONTENT_URI); + builder.withValueBackReference(GroupMembership.RAW_CONTACT_ID, 0); + builder.withValue(Data.MIMETYPE, mimeType); + } else { // 1 <= i && i <= MAX_DATA_COLUMNS + if (!TextUtils.isEmpty(customPropertyValue)) { + builder.withValue("data" + i, customPropertyValue); + } + } + + i++; + } + operationList.add(builder.build()); + } + } + + if (myGroupsId != null) { + builder = ContentProviderOperation.newInsert(Data.CONTENT_URI); + builder.withValueBackReference(GroupMembership.RAW_CONTACT_ID, 0); + builder.withValue(Data.MIMETYPE, GroupMembership.CONTENT_ITEM_TYPE); + builder.withValue(GroupMembership.GROUP_SOURCE_ID, myGroupsId); + operationList.add(builder.build()); + } + + try { + ContentProviderResult[] results = resolver.applyBatch( + ContactsContract.AUTHORITY, operationList); + // the first result is always the raw_contact. return it's uri so + // that it can be found later. do null checking for badly behaving + // ContentResolvers + return (results == null || results.length == 0 || results[0] == null) + ? null + : results[0].uri; + } catch (RemoteException e) { + Log.e(LOG_TAG, String.format("%s: %s", e.toString(), e.getMessage())); + return null; + } catch (OperationApplicationException e) { + Log.e(LOG_TAG, String.format("%s: %s", e.toString(), e.getMessage())); + return null; + } + } + + public static VCardEntry buildFromResolver(ContentResolver resolver) { + return buildFromResolver(resolver, Contacts.CONTENT_URI); + } + + public static VCardEntry buildFromResolver(ContentResolver resolver, Uri uri) { + + return null; + } + + private boolean nameFieldsAreEmpty() { + return (TextUtils.isEmpty(mFamilyName) + && TextUtils.isEmpty(mMiddleName) + && TextUtils.isEmpty(mGivenName) + && TextUtils.isEmpty(mPrefix) + && TextUtils.isEmpty(mSuffix) + && TextUtils.isEmpty(mFormattedName) + && TextUtils.isEmpty(mPhoneticFamilyName) + && TextUtils.isEmpty(mPhoneticMiddleName) + && TextUtils.isEmpty(mPhoneticGivenName) + && TextUtils.isEmpty(mPhoneticFullName)); + } + + public boolean isIgnorable() { + return getDisplayName().length() == 0; + } + + private String listToString(List list){ + final int size = list.size(); + if (size > 1) { + StringBuilder builder = new StringBuilder(); + int i = 0; + for (String type : list) { + builder.append(type); + if (i < size - 1) { + builder.append(";"); + } + } + return builder.toString(); + } else if (size == 1) { + return list.get(0); + } else { + return ""; + } + } + + // All getter methods should be used carefully, since they may change + // in the future as of 2009-10-05, on which I cannot be sure this structure + // is completely consolidated. + // + // Also note that these getter methods should be used only after + // all properties being pushed into this object. If not, incorrect + // value will "be stored in the local cache and" be returned to you. + + public String getFamilyName() { + return mFamilyName; + } + + public String getGivenName() { + return mGivenName; + } + + public String getMiddleName() { + return mMiddleName; + } + + public String getPrefix() { + return mPrefix; + } + + public String getSuffix() { + return mSuffix; + } + + public String getFullName() { + return mFormattedName; + } + + public String getPhoneticFamilyName() { + return mPhoneticFamilyName; + } + + public String getPhoneticGivenName() { + return mPhoneticGivenName; + } + + public String getPhoneticMiddleName() { + return mPhoneticMiddleName; + } + + public String getPhoneticFullName() { + return mPhoneticFullName; + } + + public final List getNickNameList() { + return mNickNameList; + } + + public String getBirthday() { + return mBirthday; + } + + public final List getNotes() { + return mNoteList; + } + + public final List getPhoneList() { + return mPhoneList; + } + + public final List getEmailList() { + return mEmailList; + } + + public final List getPostalList() { + return mPostalList; + } + + public final List getOrganizationList() { + return mOrganizationList; + } + + public final List getImList() { + return mImList; + } + + public final List getPhotoList() { + return mPhotoList; + } + + public final List getWebsiteList() { + return mWebsiteList; + } + + public String getDisplayName() { + if (mDisplayName == null) { + constructDisplayName(); + } + return mDisplayName; + } +} diff --git a/vcard/java/com/android/vcard/VCardEntryCommitter.java b/vcard/java/com/android/vcard/VCardEntryCommitter.java new file mode 100644 index 000000000..7bd314eb3 --- /dev/null +++ b/vcard/java/com/android/vcard/VCardEntryCommitter.java @@ -0,0 +1,68 @@ +/* + * 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.vcard; + +import android.content.ContentResolver; +import android.net.Uri; +import android.util.Log; + +import java.util.ArrayList; + +/** + *

+ * {@link VCardEntryHandler} implementation which commits the entry to ContentResolver. + *

+ *

+ * Note:
+ * Each vCard may contain big photo images encoded by BASE64, + * If we store all vCard entries in memory, OutOfMemoryError may be thrown. + * Thus, this class push each VCard entry into ContentResolver immediately. + *

+ */ +public class VCardEntryCommitter implements VCardEntryHandler { + public static String LOG_TAG = "VCardEntryComitter"; + + private final ContentResolver mContentResolver; + private long mTimeToCommit; + private ArrayList mCreatedUris = new ArrayList(); + + public VCardEntryCommitter(ContentResolver resolver) { + mContentResolver = resolver; + } + + public void onStart() { + } + + public void onEnd() { + if (VCardConfig.showPerformanceLog()) { + Log.d(LOG_TAG, String.format("time to commit entries: %d ms", mTimeToCommit)); + } + } + + public void onEntryCreated(final VCardEntry vcardEntry) { + long start = System.currentTimeMillis(); + mCreatedUris.add(vcardEntry.pushIntoContentResolver(mContentResolver)); + mTimeToCommit += System.currentTimeMillis() - start; + } + + /** + * Returns the list of created Uris. This list should not be modified by the caller as it is + * not a clone. + */ + public ArrayList getCreatedUris() { + return mCreatedUris; + } +} \ No newline at end of file diff --git a/vcard/java/com/android/vcard/VCardEntryConstructor.java b/vcard/java/com/android/vcard/VCardEntryConstructor.java new file mode 100644 index 000000000..2679e238a --- /dev/null +++ b/vcard/java/com/android/vcard/VCardEntryConstructor.java @@ -0,0 +1,240 @@ +/* + * 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.vcard; + +import android.accounts.Account; +import android.text.TextUtils; +import android.util.Base64; +import android.util.CharsetUtils; +import android.util.Log; + +import java.io.UnsupportedEncodingException; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + *

+ * The {@link VCardInterpreter} implementation which enables {@link VCardEntryHandler} objects + * to easily handle each vCard entry. + *

+ *

+ * This class understand details inside vCard and translates it to {@link VCardEntry}. + * Then the class throw it to {@link VCardEntryHandler} registered via + * {@link #addEntryHandler(VCardEntryHandler)}, so that all those registered objects + * are able to handle the {@link VCardEntry} object. + *

+ *

+ * If you want to know the detail inside vCard, it would be better to implement + * {@link VCardInterpreter} directly, instead of relying on this class and + * {@link VCardEntry} created by the object. + *

+ */ +public class VCardEntryConstructor implements VCardInterpreter { + private static String LOG_TAG = "VCardEntryConstructor"; + + private VCardEntry.Property mCurrentProperty = new VCardEntry.Property(); + private VCardEntry mCurrentVCardEntry; + private String mParamType; + + // The charset using which {@link VCardInterpreter} parses the text. + // Each String is first decoded into binary stream with this charset, and encoded back + // to "target charset", which may be explicitly specified by the vCard with "CHARSET" + // property or implicitly mentioned by its version (e.g. vCard 3.0 recommends UTF-8). + private final String mSourceCharset; + + private final boolean mStrictLineBreaking; + private final int mVCardType; + private final Account mAccount; + + // For measuring performance. + private long mTimePushIntoContentResolver; + + private final List mEntryHandlers = new ArrayList(); + + public VCardEntryConstructor() { + this(VCardConfig.VCARD_TYPE_V21_GENERIC, null, null, false); + } + + public VCardEntryConstructor(final int vcardType) { + this(vcardType, null, null, false); + } + + public VCardEntryConstructor(final int vcardType, final Account account) { + this(vcardType, account, null, false); + } + + public VCardEntryConstructor(final int vcardType, final Account account, + final String inputCharset) { + this(vcardType, account, inputCharset, false); + } + + /** + * @hide + */ + public VCardEntryConstructor(final int vcardType, final Account account, + final String inputCharset, final boolean strictLineBreakParsing) { + if (inputCharset != null) { + mSourceCharset = inputCharset; + } else { + mSourceCharset = VCardConfig.DEFAULT_INTERMEDIATE_CHARSET; + } + mStrictLineBreaking = strictLineBreakParsing; + mVCardType = vcardType; + mAccount = account; + } + + public void addEntryHandler(VCardEntryHandler entryHandler) { + mEntryHandlers.add(entryHandler); + } + + public void start() { + for (VCardEntryHandler entryHandler : mEntryHandlers) { + entryHandler.onStart(); + } + } + + public void end() { + for (VCardEntryHandler entryHandler : mEntryHandlers) { + entryHandler.onEnd(); + } + } + + public void clear() { + mCurrentVCardEntry = null; + mCurrentProperty = new VCardEntry.Property(); + } + + public void startEntry() { + if (mCurrentVCardEntry != null) { + Log.e(LOG_TAG, "Nested VCard code is not supported now."); + } + mCurrentVCardEntry = new VCardEntry(mVCardType, mAccount); + } + + public void endEntry() { + mCurrentVCardEntry.consolidateFields(); + for (VCardEntryHandler entryHandler : mEntryHandlers) { + entryHandler.onEntryCreated(mCurrentVCardEntry); + } + mCurrentVCardEntry = null; + } + + public void startProperty() { + mCurrentProperty.clear(); + } + + public void endProperty() { + mCurrentVCardEntry.addProperty(mCurrentProperty); + } + + public void propertyName(String name) { + mCurrentProperty.setPropertyName(name); + } + + public void propertyGroup(String group) { + } + + public void propertyParamType(String type) { + if (mParamType != null) { + Log.e(LOG_TAG, "propertyParamType() is called more than once " + + "before propertyParamValue() is called"); + } + mParamType = type; + } + + public void propertyParamValue(String value) { + if (mParamType == null) { + // From vCard 2.1 specification. vCard 3.0 formally does not allow this case. + mParamType = "TYPE"; + } + mCurrentProperty.addParameter(mParamType, value); + mParamType = null; + } + + private static String encodeToSystemCharset(String originalString, + String sourceCharset, String targetCharset) { + if (sourceCharset.equalsIgnoreCase(targetCharset)) { + return originalString; + } + final Charset charset = Charset.forName(sourceCharset); + final ByteBuffer byteBuffer = charset.encode(originalString); + // byteBuffer.array() "may" return byte array which is larger than + // byteBuffer.remaining(). Here, we keep on the safe side. + final byte[] bytes = new byte[byteBuffer.remaining()]; + byteBuffer.get(bytes); + try { + String ret = new String(bytes, targetCharset); + return ret; + } catch (UnsupportedEncodingException e) { + Log.e(LOG_TAG, "Failed to encode: charset=" + targetCharset); + return null; + } + } + + private String handleOneValue(String value, + String sourceCharset, String targetCharset, String encoding) { + if (value == null) { + Log.w(LOG_TAG, "Null is given."); + value = ""; + } + + if (encoding != null) { + if (encoding.equals("BASE64") || encoding.equals("B")) { + mCurrentProperty.setPropertyBytes(Base64.decode(value.getBytes(), Base64.DEFAULT)); + return value; + } else if (encoding.equals("QUOTED-PRINTABLE")) { + return VCardUtils.parseQuotedPrintable( + value, mStrictLineBreaking, sourceCharset, targetCharset); + } + Log.w(LOG_TAG, "Unknown encoding. Fall back to default."); + } + + // Just translate the charset of a given String from inputCharset to a system one. + return encodeToSystemCharset(value, sourceCharset, targetCharset); + } + + public void propertyValues(List values) { + if (values == null || values.isEmpty()) { + return; + } + + final Collection charsetCollection = mCurrentProperty.getParameters("CHARSET"); + final Collection encodingCollection = mCurrentProperty.getParameters("ENCODING"); + final String encoding = + ((encodingCollection != null) ? encodingCollection.iterator().next() : null); + String targetCharset = CharsetUtils.nameForDefaultVendor( + ((charsetCollection != null) ? charsetCollection.iterator().next() : null)); + if (TextUtils.isEmpty(targetCharset)) { + targetCharset = VCardConfig.DEFAULT_IMPORT_CHARSET; + } + + for (final String value : values) { + mCurrentProperty.addToPropertyValueList( + handleOneValue(value, mSourceCharset, targetCharset, encoding)); + } + } + + /** + * @hide + */ + public void showPerformanceInfo() { + Log.d(LOG_TAG, "time for insert ContactStruct to database: " + + mTimePushIntoContentResolver + " ms"); + } +} diff --git a/vcard/java/com/android/vcard/VCardEntryCounter.java b/vcard/java/com/android/vcard/VCardEntryCounter.java new file mode 100644 index 000000000..7bfe9773f --- /dev/null +++ b/vcard/java/com/android/vcard/VCardEntryCounter.java @@ -0,0 +1,63 @@ +/* + * 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.vcard; + +import java.util.List; + +/** + * The class which just counts the number of vCard entries in the specified input. + */ +public class VCardEntryCounter implements VCardInterpreter { + private int mCount; + + public int getCount() { + return mCount; + } + + public void start() { + } + + public void end() { + } + + public void startEntry() { + } + + public void endEntry() { + mCount++; + } + + public void startProperty() { + } + + public void endProperty() { + } + + public void propertyGroup(String group) { + } + + public void propertyName(String name) { + } + + public void propertyParamType(String type) { + } + + public void propertyParamValue(String value) { + } + + public void propertyValues(List values) { + } +} diff --git a/vcard/java/com/android/vcard/VCardEntryHandler.java b/vcard/java/com/android/vcard/VCardEntryHandler.java new file mode 100644 index 000000000..ef35a20a2 --- /dev/null +++ b/vcard/java/com/android/vcard/VCardEntryHandler.java @@ -0,0 +1,43 @@ +/* + * 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.vcard; + +/** + *

+ * The interface called by {@link VCardEntryConstructor}. + *

+ *

+ * This class is useful when you don't want to know vCard data in detail. If you want to know + * it, it would be better to consider using {@link VCardInterpreter}. + *

+ */ +public interface VCardEntryHandler { + /** + * Called when the parsing started. + */ + public void onStart(); + + /** + * The method called when one VCard entry is successfully created + */ + public void onEntryCreated(final VCardEntry entry); + + /** + * Called when the parsing ended. + * Able to be use this method for showing performance log, etc. + */ + public void onEnd(); +} diff --git a/vcard/java/com/android/vcard/VCardInterpreter.java b/vcard/java/com/android/vcard/VCardInterpreter.java new file mode 100644 index 000000000..2d987644f --- /dev/null +++ b/vcard/java/com/android/vcard/VCardInterpreter.java @@ -0,0 +1,102 @@ +/* + * 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.vcard; + +import java.util.List; + +/** + *

+ * The interface which should be implemented by the classes which have to analyze each + * vCard entry minutely. + *

+ *

+ * Here, there are several terms specific to vCard (and this library). + *

+ *

+ * The term "entry" is one vCard representation in the input, which should start with "BEGIN:VCARD" + * and end with "END:VCARD". + *

+ *

+ * The term "property" is one line in vCard entry, which consists of "group", "property name", + * "parameter(param) names and values", and "property values". + *

+ *

+ * e.g. group1.propName;paramName1=paramValue1;paramName2=paramValue2;propertyValue1;propertyValue2... + *

+ */ +public interface VCardInterpreter { + /** + * Called when vCard interpretation started. + */ + void start(); + + /** + * Called when vCard interpretation finished. + */ + void end(); + + /** + * Called when parsing one vCard entry started. + * More specifically, this method is called when "BEGIN:VCARD" is read. + */ + void startEntry(); + + /** + * Called when parsing one vCard entry ended. + * More specifically, this method is called when "END:VCARD" is read. + * Note that {@link #startEntry()} may be called since + * vCard (especially 2.1) allows nested vCard. + */ + void endEntry(); + + /** + * Called when reading one property started. + */ + void startProperty(); + + /** + * Called when reading one property ended. + */ + void endProperty(); + + /** + * @param group A group name. This method may be called more than once or may not be + * called at all, depending on how many gruoups are appended to the property. + */ + void propertyGroup(String group); + + /** + * @param name A property name like "N", "FN", "ADR", etc. + */ + void propertyName(String name); + + /** + * @param type A parameter name like "ENCODING", "CHARSET", etc. + */ + void propertyParamType(String type); + + /** + * @param value A parameter value. This method may be called without + * {@link #propertyParamType(String)} being called (when the vCard is vCard 2.1). + */ + void propertyParamValue(String value); + + /** + * @param values List of property values. The size of values would be 1 unless + * coressponding property name is "N", "ADR", or "ORG". + */ + void propertyValues(List values); +} diff --git a/vcard/java/com/android/vcard/VCardInterpreterCollection.java b/vcard/java/com/android/vcard/VCardInterpreterCollection.java new file mode 100644 index 000000000..4a40d9312 --- /dev/null +++ b/vcard/java/com/android/vcard/VCardInterpreterCollection.java @@ -0,0 +1,102 @@ +/* + * 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.vcard; + +import java.util.Collection; +import java.util.List; + +/** + * The {@link VCardInterpreter} implementation which aggregates more than one + * {@link VCardInterpreter} objects and make a user object treat them as one + * {@link VCardInterpreter} object. + */ +public final class VCardInterpreterCollection implements VCardInterpreter { + private final Collection mInterpreterCollection; + + public VCardInterpreterCollection(Collection interpreterCollection) { + mInterpreterCollection = interpreterCollection; + } + + public Collection getCollection() { + return mInterpreterCollection; + } + + public void start() { + for (VCardInterpreter builder : mInterpreterCollection) { + builder.start(); + } + } + + public void end() { + for (VCardInterpreter builder : mInterpreterCollection) { + builder.end(); + } + } + + public void startEntry() { + for (VCardInterpreter builder : mInterpreterCollection) { + builder.startEntry(); + } + } + + public void endEntry() { + for (VCardInterpreter builder : mInterpreterCollection) { + builder.endEntry(); + } + } + + public void startProperty() { + for (VCardInterpreter builder : mInterpreterCollection) { + builder.startProperty(); + } + } + + public void endProperty() { + for (VCardInterpreter builder : mInterpreterCollection) { + builder.endProperty(); + } + } + + public void propertyGroup(String group) { + for (VCardInterpreter builder : mInterpreterCollection) { + builder.propertyGroup(group); + } + } + + public void propertyName(String name) { + for (VCardInterpreter builder : mInterpreterCollection) { + builder.propertyName(name); + } + } + + public void propertyParamType(String type) { + for (VCardInterpreter builder : mInterpreterCollection) { + builder.propertyParamType(type); + } + } + + public void propertyParamValue(String value) { + for (VCardInterpreter builder : mInterpreterCollection) { + builder.propertyParamValue(value); + } + } + + public void propertyValues(List values) { + for (VCardInterpreter builder : mInterpreterCollection) { + builder.propertyValues(values); + } + } +} diff --git a/vcard/java/com/android/vcard/VCardParser.java b/vcard/java/com/android/vcard/VCardParser.java new file mode 100644 index 000000000..b7b8291dd --- /dev/null +++ b/vcard/java/com/android/vcard/VCardParser.java @@ -0,0 +1,55 @@ +/* + * 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.vcard; + +import com.android.vcard.exception.VCardException; + +import java.io.IOException; +import java.io.InputStream; + +public interface VCardParser { + /** + *

+ * Parses the given stream and send the vCard data into VCardBuilderBase object. + *

. + *

+ * Note that vCard 2.1 specification allows "CHARSET" parameter, and some career sets + * local encoding to it. For example, Japanese phone career uses Shift_JIS, which is + * formally allowed in vCard 2.1, but not allowed in vCard 3.0. In vCard 2.1, + * In some exreme case, it is allowed for vCard to have different charsets in one vCard. + *

+ *

+ * We recommend you use {@link VCardSourceDetector} and detect which kind of source the + * vCard comes from and explicitly specify a charset using the result. + *

+ * + * @param is The source to parse. + * @param interepreter A {@link VCardInterpreter} object which used to construct data. + * @throws IOException, VCardException + */ + public void parse(InputStream is, VCardInterpreter interepreter) + throws IOException, VCardException; + + /** + *

+ * Cancel parsing vCard. Useful when you want to stop the parse in the other threads. + *

+ *

+ * Actual cancel is done after parsing the current vcard. + *

+ */ + public abstract void cancel(); +} diff --git a/vcard/java/com/android/vcard/VCardParserImpl_V21.java b/vcard/java/com/android/vcard/VCardParserImpl_V21.java new file mode 100644 index 000000000..00ae6c91d --- /dev/null +++ b/vcard/java/com/android/vcard/VCardParserImpl_V21.java @@ -0,0 +1,968 @@ +/* + * Copyright (C) 2010 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.vcard; + +import android.text.TextUtils; +import android.util.Log; + +import com.android.vcard.exception.VCardAgentNotSupportedException; +import com.android.vcard.exception.VCardException; +import com.android.vcard.exception.VCardInvalidCommentLineException; +import com.android.vcard.exception.VCardInvalidLineException; +import com.android.vcard.exception.VCardNestedException; +import com.android.vcard.exception.VCardVersionException; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Set; + +/** + *

+ * Basic implementation achieving vCard parsing. Based on vCard 2.1, + *

+ * @hide + */ +/* package */ class VCardParserImpl_V21 { + private static final String LOG_TAG = "VCardParserImpl_V21"; + + private static final class CustomBufferedReader extends BufferedReader { + private long mTime; + + public CustomBufferedReader(Reader in) { + super(in); + } + + @Override + public String readLine() throws IOException { + long start = System.currentTimeMillis(); + String ret = super.readLine(); + long end = System.currentTimeMillis(); + mTime += end - start; + return ret; + } + + public long getTotalmillisecond() { + return mTime; + } + } + + private static final String sDefaultEncoding = "8BIT"; + + protected boolean mCanceled; + protected VCardInterpreter mInterpreter; + + protected final String mImportCharset; + + /** + *

+ * The encoding type for deconding byte streams. This member variable is + * reset to a default encoding every time when a new item comes. + *

+ *

+ * "Encoding" in vCard is different from "Charset". It is mainly used for + * addresses, notes, images. "7BIT", "8BIT", "BASE64", and + * "QUOTED-PRINTABLE" are known examples. + *

+ */ + protected String mCurrentEncoding; + + /** + *

+ * The reader object to be used internally. + *

+ *

+ * Developers should not directly read a line from this object. Use + * getLine() unless there some reason. + *

+ */ + protected BufferedReader mReader; + + /** + *

+ * Set for storing unkonwn TYPE attributes, which is not acceptable in vCard + * specification, but happens to be seen in real world vCard. + *

+ */ + protected final Set mUnknownTypeSet = new HashSet(); + + /** + *

+ * Set for storing unkonwn VALUE attributes, which is not acceptable in + * vCard specification, but happens to be seen in real world vCard. + *

+ */ + protected final Set mUnknownValueSet = new HashSet(); + + + // In some cases, vCard is nested. Currently, we only consider the most + // interior vCard data. + // See v21_foma_1.vcf in test directory for more information. + // TODO: Don't ignore by using count, but read all of information outside vCard. + private int mNestCount; + + // Used only for parsing END:VCARD. + private String mPreviousLine; + + // For measuring performance. + private long mTimeTotal; + private long mTimeReadStartRecord; + private long mTimeReadEndRecord; + private long mTimeStartProperty; + private long mTimeEndProperty; + private long mTimeParseItems; + private long mTimeParseLineAndHandleGroup; + private long mTimeParsePropertyValues; + private long mTimeParseAdrOrgN; + private long mTimeHandleMiscPropertyValue; + private long mTimeHandleQuotedPrintable; + private long mTimeHandleBase64; + + public VCardParserImpl_V21() { + this(VCardConfig.VCARD_TYPE_DEFAULT, null); + } + + public VCardParserImpl_V21(int vcardType) { + this(vcardType, null); + } + + public VCardParserImpl_V21(int vcardType, String importCharset) { + if ((vcardType & VCardConfig.FLAG_TORELATE_NEST) != 0) { + mNestCount = 1; + } + + mImportCharset = (!TextUtils.isEmpty(importCharset) ? importCharset : + VCardConfig.DEFAULT_INTERMEDIATE_CHARSET); + } + + /** + *

+ * Parses the file at the given position. + *

+ */ + //
vcard_file = [wsls] vcard [wsls]
+ protected void parseVCardFile() throws IOException, VCardException { + boolean readingFirstFile = true; + while (true) { + if (mCanceled) { + break; + } + if (!parseOneVCard(readingFirstFile)) { + break; + } + readingFirstFile = false; + } + + if (mNestCount > 0) { + boolean useCache = true; + for (int i = 0; i < mNestCount; i++) { + readEndVCard(useCache, true); + useCache = false; + } + } + } + + /** + * @return true when a given property name is a valid property name. + */ + protected boolean isValidPropertyName(final String propertyName) { + if (!(getKnownPropertyNameSet().contains(propertyName.toUpperCase()) || + propertyName.startsWith("X-")) + && !mUnknownTypeSet.contains(propertyName)) { + mUnknownTypeSet.add(propertyName); + Log.w(LOG_TAG, "Property name unsupported by vCard 2.1: " + propertyName); + } + return true; + } + + /** + * @return String. It may be null, or its length may be 0 + * @throws IOException + */ + protected String getLine() throws IOException { + return mReader.readLine(); + } + + /** + * @return String with it's length > 0 + * @throws IOException + * @throws VCardException when the stream reached end of line + */ + protected String getNonEmptyLine() throws IOException, VCardException { + String line; + while (true) { + line = getLine(); + if (line == null) { + throw new VCardException("Reached end of buffer."); + } else if (line.trim().length() > 0) { + return line; + } + } + } + + /* + * vcard = "BEGIN" [ws] ":" [ws] "VCARD" [ws] 1*CRLF + * items *CRLF + * "END" [ws] ":" [ws] "VCARD" + */ + private boolean parseOneVCard(boolean firstRead) throws IOException, VCardException { + boolean allowGarbage = false; + if (firstRead) { + if (mNestCount > 0) { + for (int i = 0; i < mNestCount; i++) { + if (!readBeginVCard(allowGarbage)) { + return false; + } + allowGarbage = true; + } + } + } + + if (!readBeginVCard(allowGarbage)) { + return false; + } + long start; + if (mInterpreter != null) { + start = System.currentTimeMillis(); + mInterpreter.startEntry(); + mTimeReadStartRecord += System.currentTimeMillis() - start; + } + start = System.currentTimeMillis(); + parseItems(); + mTimeParseItems += System.currentTimeMillis() - start; + readEndVCard(true, false); + if (mInterpreter != null) { + start = System.currentTimeMillis(); + mInterpreter.endEntry(); + mTimeReadEndRecord += System.currentTimeMillis() - start; + } + return true; + } + + /** + * @return True when successful. False when reaching the end of line + * @throws IOException + * @throws VCardException + */ + protected boolean readBeginVCard(boolean allowGarbage) throws IOException, VCardException { + String line; + do { + while (true) { + line = getLine(); + if (line == null) { + return false; + } else if (line.trim().length() > 0) { + break; + } + } + String[] strArray = line.split(":", 2); + int length = strArray.length; + + // Though vCard 2.1/3.0 specification does not allow lower cases, + // vCard file emitted by some external vCard expoter have such + // invalid Strings. + // So we allow it. + // e.g. BEGIN:vCard + if (length == 2 && strArray[0].trim().equalsIgnoreCase("BEGIN") + && strArray[1].trim().equalsIgnoreCase("VCARD")) { + return true; + } else if (!allowGarbage) { + if (mNestCount > 0) { + mPreviousLine = line; + return false; + } else { + throw new VCardException("Expected String \"BEGIN:VCARD\" did not come " + + "(Instead, \"" + line + "\" came)"); + } + } + } while (allowGarbage); + + throw new VCardException("Reached where must not be reached."); + } + + /** + *

+ * The arguments useCache and allowGarbase are usually true and false + * accordingly when this function is called outside this function itself. + *

+ * + * @param useCache When true, line is obtained from mPreviousline. + * Otherwise, getLine() is used. + * @param allowGarbage When true, ignore non "END:VCARD" line. + * @throws IOException + * @throws VCardException + */ + protected void readEndVCard(boolean useCache, boolean allowGarbage) throws IOException, + VCardException { + String line; + do { + if (useCache) { + // Though vCard specification does not allow lower cases, + // some data may have them, so we allow it. + line = mPreviousLine; + } else { + while (true) { + line = getLine(); + if (line == null) { + throw new VCardException("Expected END:VCARD was not found."); + } else if (line.trim().length() > 0) { + break; + } + } + } + + String[] strArray = line.split(":", 2); + if (strArray.length == 2 && strArray[0].trim().equalsIgnoreCase("END") + && strArray[1].trim().equalsIgnoreCase("VCARD")) { + return; + } else if (!allowGarbage) { + throw new VCardException("END:VCARD != \"" + mPreviousLine + "\""); + } + useCache = false; + } while (allowGarbage); + } + + /* + * items = *CRLF item / item + */ + protected void parseItems() throws IOException, VCardException { + boolean ended = false; + + if (mInterpreter != null) { + long start = System.currentTimeMillis(); + mInterpreter.startProperty(); + mTimeStartProperty += System.currentTimeMillis() - start; + } + ended = parseItem(); + if (mInterpreter != null && !ended) { + long start = System.currentTimeMillis(); + mInterpreter.endProperty(); + mTimeEndProperty += System.currentTimeMillis() - start; + } + + while (!ended) { + // follow VCARD ,it wont reach endProperty + if (mInterpreter != null) { + long start = System.currentTimeMillis(); + mInterpreter.startProperty(); + mTimeStartProperty += System.currentTimeMillis() - start; + } + try { + ended = parseItem(); + } catch (VCardInvalidCommentLineException e) { + Log.e(LOG_TAG, "Invalid line which looks like some comment was found. Ignored."); + ended = false; + } + if (mInterpreter != null && !ended) { + long start = System.currentTimeMillis(); + mInterpreter.endProperty(); + mTimeEndProperty += System.currentTimeMillis() - start; + } + } + } + + /* + * item = [groups "."] name [params] ":" value CRLF / [groups "."] "ADR" + * [params] ":" addressparts CRLF / [groups "."] "ORG" [params] ":" orgparts + * CRLF / [groups "."] "N" [params] ":" nameparts CRLF / [groups "."] + * "AGENT" [params] ":" vcard CRLF + */ + protected boolean parseItem() throws IOException, VCardException { + mCurrentEncoding = sDefaultEncoding; + + final String line = getNonEmptyLine(); + long start = System.currentTimeMillis(); + + String[] propertyNameAndValue = separateLineAndHandleGroup(line); + if (propertyNameAndValue == null) { + return true; + } + if (propertyNameAndValue.length != 2) { + throw new VCardInvalidLineException("Invalid line \"" + line + "\""); + } + String propertyName = propertyNameAndValue[0].toUpperCase(); + String propertyValue = propertyNameAndValue[1]; + + mTimeParseLineAndHandleGroup += System.currentTimeMillis() - start; + + if (propertyName.equals("ADR") || propertyName.equals("ORG") || propertyName.equals("N")) { + start = System.currentTimeMillis(); + handleMultiplePropertyValue(propertyName, propertyValue); + mTimeParseAdrOrgN += System.currentTimeMillis() - start; + return false; + } else if (propertyName.equals("AGENT")) { + handleAgent(propertyValue); + return false; + } else if (isValidPropertyName(propertyName)) { + if (propertyName.equals("BEGIN")) { + if (propertyValue.equals("VCARD")) { + throw new VCardNestedException("This vCard has nested vCard data in it."); + } else { + throw new VCardException("Unknown BEGIN type: " + propertyValue); + } + } else if (propertyName.equals("VERSION") && !propertyValue.equals(getVersionString())) { + throw new VCardVersionException("Incompatible version: " + propertyValue + " != " + + getVersionString()); + } + start = System.currentTimeMillis(); + handlePropertyValue(propertyName, propertyValue); + mTimeParsePropertyValues += System.currentTimeMillis() - start; + return false; + } + + throw new VCardException("Unknown property name: \"" + propertyName + "\""); + } + + // For performance reason, the states for group and property name are merged into one. + static private final int STATE_GROUP_OR_PROPERTY_NAME = 0; + static private final int STATE_PARAMS = 1; + // vCard 3.0 specification allows double-quoted parameters, while vCard 2.1 does not. + static private final int STATE_PARAMS_IN_DQUOTE = 2; + + protected String[] separateLineAndHandleGroup(String line) throws VCardException { + final String[] propertyNameAndValue = new String[2]; + final int length = line.length(); + if (length > 0 && line.charAt(0) == '#') { + throw new VCardInvalidCommentLineException(); + } + + int state = STATE_GROUP_OR_PROPERTY_NAME; + int nameIndex = 0; + + // This loop is developed so that we don't have to take care of bottle neck here. + // Refactor carefully when you need to do so. + for (int i = 0; i < length; i++) { + final char ch = line.charAt(i); + switch (state) { + case STATE_GROUP_OR_PROPERTY_NAME: { + if (ch == ':') { // End of a property name. + final String propertyName = line.substring(nameIndex, i); + if (propertyName.equalsIgnoreCase("END")) { + mPreviousLine = line; + return null; + } + if (mInterpreter != null) { + mInterpreter.propertyName(propertyName); + } + propertyNameAndValue[0] = propertyName; + if (i < length - 1) { + propertyNameAndValue[1] = line.substring(i + 1); + } else { + propertyNameAndValue[1] = ""; + } + return propertyNameAndValue; + } else if (ch == '.') { // Each group is followed by the dot. + final String groupName = line.substring(nameIndex, i); + if (groupName.length() == 0) { + Log.w(LOG_TAG, "Empty group found. Ignoring."); + } else if (mInterpreter != null) { + mInterpreter.propertyGroup(groupName); + } + nameIndex = i + 1; // Next should be another group or a property name. + } else if (ch == ';') { // End of property name and beginneng of parameters. + final String propertyName = line.substring(nameIndex, i); + if (propertyName.equalsIgnoreCase("END")) { + mPreviousLine = line; + return null; + } + if (mInterpreter != null) { + mInterpreter.propertyName(propertyName); + } + propertyNameAndValue[0] = propertyName; + nameIndex = i + 1; + state = STATE_PARAMS; // Start parameter parsing. + } + break; + } + case STATE_PARAMS: { + if (ch == '"') { + if (VCardConstants.VERSION_V21.equalsIgnoreCase(getVersionString())) { + Log.w(LOG_TAG, "Double-quoted params found in vCard 2.1. " + + "Silently allow it"); + } + state = STATE_PARAMS_IN_DQUOTE; + } else if (ch == ';') { // Starts another param. + handleParams(line.substring(nameIndex, i)); + nameIndex = i + 1; + } else if (ch == ':') { // End of param and beginenning of values. + handleParams(line.substring(nameIndex, i)); + if (i < length - 1) { + propertyNameAndValue[1] = line.substring(i + 1); + } else { + propertyNameAndValue[1] = ""; + } + return propertyNameAndValue; + } + break; + } + case STATE_PARAMS_IN_DQUOTE: { + if (ch == '"') { + if (VCardConstants.VERSION_V21.equalsIgnoreCase(getVersionString())) { + Log.w(LOG_TAG, "Double-quoted params found in vCard 2.1. " + + "Silently allow it"); + } + state = STATE_PARAMS; + } + break; + } + } + } + + throw new VCardInvalidLineException("Invalid line: \"" + line + "\""); + } + + /* + * params = ";" [ws] paramlist paramlist = paramlist [ws] ";" [ws] param / + * param param = "TYPE" [ws] "=" [ws] ptypeval / "VALUE" [ws] "=" [ws] + * pvalueval / "ENCODING" [ws] "=" [ws] pencodingval / "CHARSET" [ws] "=" + * [ws] charsetval / "LANGUAGE" [ws] "=" [ws] langval / "X-" word [ws] "=" + * [ws] word / knowntype + */ + protected void handleParams(String params) throws VCardException { + final String[] strArray = params.split("=", 2); + if (strArray.length == 2) { + final String paramName = strArray[0].trim().toUpperCase(); + String paramValue = strArray[1].trim(); + if (paramName.equals("TYPE")) { + handleType(paramValue); + } else if (paramName.equals("VALUE")) { + handleValue(paramValue); + } else if (paramName.equals("ENCODING")) { + handleEncoding(paramValue); + } else if (paramName.equals("CHARSET")) { + handleCharset(paramValue); + } else if (paramName.equals("LANGUAGE")) { + handleLanguage(paramValue); + } else if (paramName.startsWith("X-")) { + handleAnyParam(paramName, paramValue); + } else { + throw new VCardException("Unknown type \"" + paramName + "\""); + } + } else { + handleParamWithoutName(strArray[0]); + } + } + + /** + * vCard 3.0 parser implementation may throw VCardException. + */ + @SuppressWarnings("unused") + protected void handleParamWithoutName(final String paramValue) throws VCardException { + handleType(paramValue); + } + + /* + * ptypeval = knowntype / "X-" word + */ + protected void handleType(final String ptypeval) { + if (!(getKnownTypeSet().contains(ptypeval.toUpperCase()) + || ptypeval.startsWith("X-")) + && !mUnknownTypeSet.contains(ptypeval)) { + mUnknownTypeSet.add(ptypeval); + Log.w(LOG_TAG, String.format("TYPE unsupported by %s: ", getVersion(), ptypeval)); + } + if (mInterpreter != null) { + mInterpreter.propertyParamType("TYPE"); + mInterpreter.propertyParamValue(ptypeval); + } + } + + /* + * pvalueval = "INLINE" / "URL" / "CONTENT-ID" / "CID" / "X-" word + */ + protected void handleValue(final String pvalueval) { + if (!(getKnownValueSet().contains(pvalueval.toUpperCase()) + || pvalueval.startsWith("X-") + || mUnknownValueSet.contains(pvalueval))) { + mUnknownValueSet.add(pvalueval); + Log.w(LOG_TAG, String.format( + "The value unsupported by TYPE of %s: ", getVersion(), pvalueval)); + } + if (mInterpreter != null) { + mInterpreter.propertyParamType("VALUE"); + mInterpreter.propertyParamValue(pvalueval); + } + } + + /* + * pencodingval = "7BIT" / "8BIT" / "QUOTED-PRINTABLE" / "BASE64" / "X-" word + */ + protected void handleEncoding(String pencodingval) throws VCardException { + if (getAvailableEncodingSet().contains(pencodingval) || + pencodingval.startsWith("X-")) { + if (mInterpreter != null) { + mInterpreter.propertyParamType("ENCODING"); + mInterpreter.propertyParamValue(pencodingval); + } + mCurrentEncoding = pencodingval; + } else { + throw new VCardException("Unknown encoding \"" + pencodingval + "\""); + } + } + + /** + *

+ * vCard 2.1 specification only allows us-ascii and iso-8859-xxx (See RFC 1521), + * but recent vCard files often contain other charset like UTF-8, SHIFT_JIS, etc. + * We allow any charset. + *

+ */ + protected void handleCharset(String charsetval) { + if (mInterpreter != null) { + mInterpreter.propertyParamType("CHARSET"); + mInterpreter.propertyParamValue(charsetval); + } + } + + /** + * See also Section 7.1 of RFC 1521 + */ + protected void handleLanguage(String langval) throws VCardException { + String[] strArray = langval.split("-"); + if (strArray.length != 2) { + throw new VCardException("Invalid Language: \"" + langval + "\""); + } + String tmp = strArray[0]; + int length = tmp.length(); + for (int i = 0; i < length; i++) { + if (!isAsciiLetter(tmp.charAt(i))) { + throw new VCardException("Invalid Language: \"" + langval + "\""); + } + } + tmp = strArray[1]; + length = tmp.length(); + for (int i = 0; i < length; i++) { + if (!isAsciiLetter(tmp.charAt(i))) { + throw new VCardException("Invalid Language: \"" + langval + "\""); + } + } + if (mInterpreter != null) { + mInterpreter.propertyParamType("LANGUAGE"); + mInterpreter.propertyParamValue(langval); + } + } + + private boolean isAsciiLetter(char ch) { + if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) { + return true; + } + return false; + } + + /** + * Mainly for "X-" type. This accepts any kind of type without check. + */ + protected void handleAnyParam(String paramName, String paramValue) { + if (mInterpreter != null) { + mInterpreter.propertyParamType(paramName); + mInterpreter.propertyParamValue(paramValue); + } + } + + protected void handlePropertyValue(String propertyName, String propertyValue) + throws IOException, VCardException { + final String upperEncoding = mCurrentEncoding.toUpperCase(); + if (upperEncoding.equals(VCardConstants.PARAM_ENCODING_QP)) { + final long start = System.currentTimeMillis(); + final String result = getQuotedPrintable(propertyValue); + if (mInterpreter != null) { + ArrayList v = new ArrayList(); + v.add(result); + mInterpreter.propertyValues(v); + } + mTimeHandleQuotedPrintable += System.currentTimeMillis() - start; + } else if (upperEncoding.equals(VCardConstants.PARAM_ENCODING_BASE64) + || upperEncoding.equals(VCardConstants.PARAM_ENCODING_B)) { + final long start = System.currentTimeMillis(); + // It is very rare, but some BASE64 data may be so big that + // OutOfMemoryError occurs. To ignore such cases, use try-catch. + try { + final String result = getBase64(propertyValue); + if (mInterpreter != null) { + ArrayList arrayList = new ArrayList(); + arrayList.add(result); + mInterpreter.propertyValues(arrayList); + } + } catch (OutOfMemoryError error) { + Log.e(LOG_TAG, "OutOfMemoryError happened during parsing BASE64 data!"); + if (mInterpreter != null) { + mInterpreter.propertyValues(null); + } + } + mTimeHandleBase64 += System.currentTimeMillis() - start; + } else { + if (!(upperEncoding.equals("7BIT") || upperEncoding.equals("8BIT") || + upperEncoding.startsWith("X-"))) { + Log.w(LOG_TAG, + String.format("The encoding \"%s\" is unsupported by vCard %s", + mCurrentEncoding, getVersionString())); + } + + final long start = System.currentTimeMillis(); + if (mInterpreter != null) { + ArrayList v = new ArrayList(); + v.add(maybeUnescapeText(propertyValue)); + mInterpreter.propertyValues(v); + } + mTimeHandleMiscPropertyValue += System.currentTimeMillis() - start; + } + } + + /** + *

+ * Parses and returns Quoted-Printable. + *

+ * + * @param firstString The string following a parameter name and attributes. + * Example: "string" in + * "ADR:ENCODING=QUOTED-PRINTABLE:string\n\r". + * @return whole Quoted-Printable string, including a given argument and + * following lines. Excludes the last empty line following to Quoted + * Printable lines. + * @throws IOException + * @throws VCardException + */ + private String getQuotedPrintable(String firstString) throws IOException, VCardException { + // Specifically, there may be some padding between = and CRLF. + // See the following: + // + // qp-line := *(qp-segment transport-padding CRLF) + // qp-part transport-padding + // qp-segment := qp-section *(SPACE / TAB) "=" + // ; Maximum length of 76 characters + // + // e.g. (from RFC 2045) + // Now's the time = + // for all folk to come= + // to the aid of their country. + if (firstString.trim().endsWith("=")) { + // remove "transport-padding" + int pos = firstString.length() - 1; + while (firstString.charAt(pos) != '=') { + } + StringBuilder builder = new StringBuilder(); + builder.append(firstString.substring(0, pos + 1)); + builder.append("\r\n"); + String line; + while (true) { + line = getLine(); + if (line == null) { + throw new VCardException("File ended during parsing a Quoted-Printable String"); + } + if (line.trim().endsWith("=")) { + // remove "transport-padding" + pos = line.length() - 1; + while (line.charAt(pos) != '=') { + } + builder.append(line.substring(0, pos + 1)); + builder.append("\r\n"); + } else { + builder.append(line); + break; + } + } + return builder.toString(); + } else { + return firstString; + } + } + + protected String getBase64(String firstString) throws IOException, VCardException { + StringBuilder builder = new StringBuilder(); + builder.append(firstString); + + while (true) { + String line = getLine(); + if (line == null) { + throw new VCardException("File ended during parsing BASE64 binary"); + } + if (line.length() == 0) { + break; + } + builder.append(line); + } + + return builder.toString(); + } + + /** + *

+ * Mainly for "ADR", "ORG", and "N" + *

+ */ + /* + * addressparts = 0*6(strnosemi ";") strnosemi ; PO Box, Extended Addr, + * Street, Locality, Region, Postal Code, Country Name orgparts = + * *(strnosemi ";") strnosemi ; First is Organization Name, remainder are + * Organization Units. nameparts = 0*4(strnosemi ";") strnosemi ; Family, + * Given, Middle, Prefix, Suffix. ; Example:Public;John;Q.;Reverend Dr.;III, + * Esq. strnosemi = *(*nonsemi ("\;" / "\" CRLF)) *nonsemi ; To include a + * semicolon in this string, it must be escaped ; with a "\" character. We + * do not care the number of "strnosemi" here. We are not sure whether we + * should add "\" CRLF to each value. We exclude them for now. + */ + protected void handleMultiplePropertyValue(String propertyName, String propertyValue) + throws IOException, VCardException { + // vCard 2.1 does not allow QUOTED-PRINTABLE here, but some + // softwares/devices + // emit such data. + if (mCurrentEncoding.equalsIgnoreCase("QUOTED-PRINTABLE")) { + propertyValue = getQuotedPrintable(propertyValue); + } + + if (mInterpreter != null) { + mInterpreter.propertyValues(VCardUtils.constructListFromValue(propertyValue, + (getVersion() == VCardConfig.FLAG_V30))); + } + } + + /* + * vCard 2.1 specifies AGENT allows one vcard entry. Currently we emit an + * error toward the AGENT property. + * // TODO: Support AGENT property. + * item = + * ... / [groups "."] "AGENT" [params] ":" vcard CRLF vcard = "BEGIN" [ws] + * ":" [ws] "VCARD" [ws] 1*CRLF items *CRLF "END" [ws] ":" [ws] "VCARD" + */ + protected void handleAgent(final String propertyValue) throws VCardException { + if (!propertyValue.toUpperCase().contains("BEGIN:VCARD")) { + // Apparently invalid line seen in Windows Mobile 6.5. Ignore them. + return; + } else { + throw new VCardAgentNotSupportedException("AGENT Property is not supported now."); + } + } + + /** + * For vCard 3.0. + */ + protected String maybeUnescapeText(final String text) { + return text; + } + + /** + * Returns unescaped String if the character should be unescaped. Return + * null otherwise. e.g. In vCard 2.1, "\;" should be unescaped into ";" + * while "\x" should not be. + */ + protected String maybeUnescapeCharacter(final char ch) { + return unescapeCharacter(ch); + } + + /* package */ static String unescapeCharacter(final char ch) { + // Original vCard 2.1 specification does not allow transformation + // "\:" -> ":", "\," -> ",", and "\\" -> "\", but previous + // implementation of + // this class allowed them, so keep it as is. + if (ch == '\\' || ch == ';' || ch == ':' || ch == ',') { + return String.valueOf(ch); + } else { + return null; + } + } + + private void showPerformanceInfo() { + Log.d(LOG_TAG, "Total parsing time: " + mTimeTotal + " ms"); + if (mReader instanceof CustomBufferedReader) { + Log.d(LOG_TAG, "Total readLine time: " + + ((CustomBufferedReader) mReader).getTotalmillisecond() + " ms"); + } + Log.d(LOG_TAG, "Time for handling the beggining of the record: " + mTimeReadStartRecord + + " ms"); + Log.d(LOG_TAG, "Time for handling the end of the record: " + mTimeReadEndRecord + " ms"); + Log.d(LOG_TAG, "Time for parsing line, and handling group: " + mTimeParseLineAndHandleGroup + + " ms"); + Log.d(LOG_TAG, "Time for parsing ADR, ORG, and N fields:" + mTimeParseAdrOrgN + " ms"); + Log.d(LOG_TAG, "Time for parsing property values: " + mTimeParsePropertyValues + " ms"); + Log.d(LOG_TAG, "Time for handling normal property values: " + mTimeHandleMiscPropertyValue + + " ms"); + Log.d(LOG_TAG, "Time for handling Quoted-Printable: " + mTimeHandleQuotedPrintable + " ms"); + Log.d(LOG_TAG, "Time for handling Base64: " + mTimeHandleBase64 + " ms"); + } + + /** + * @return {@link VCardConfig#FLAG_V21} + */ + protected int getVersion() { + return VCardConfig.FLAG_V21; + } + + /** + * @return {@link VCardConfig#FLAG_V30} + */ + protected String getVersionString() { + return VCardConstants.VERSION_V21; + } + + protected Set getKnownPropertyNameSet() { + return VCardParser_V21.sKnownPropertyNameSet; + } + + protected Set getKnownTypeSet() { + return VCardParser_V21.sKnownTypeSet; + } + + protected Set getKnownValueSet() { + return VCardParser_V21.sKnownValueSet; + } + + protected Set getAvailableEncodingSet() { + return VCardParser_V21.sAvailableEncoding; + } + + protected String getDefaultEncoding() { + return sDefaultEncoding; + } + + + public void parse(InputStream is, VCardInterpreter interpreter) + throws IOException, VCardException { + if (is == null) { + throw new NullPointerException("InputStream must not be null."); + } + + final InputStreamReader tmpReader = new InputStreamReader(is, mImportCharset); + if (VCardConfig.showPerformanceLog()) { + mReader = new CustomBufferedReader(tmpReader); + } else { + mReader = new BufferedReader(tmpReader); + } + + mInterpreter = interpreter; + + final long start = System.currentTimeMillis(); + if (mInterpreter != null) { + mInterpreter.start(); + } + parseVCardFile(); + if (mInterpreter != null) { + mInterpreter.end(); + } + mTimeTotal += System.currentTimeMillis() - start; + + if (VCardConfig.showPerformanceLog()) { + showPerformanceInfo(); + } + } + + public final void cancel() { + mCanceled = true; + } +} diff --git a/vcard/java/com/android/vcard/VCardParserImpl_V30.java b/vcard/java/com/android/vcard/VCardParserImpl_V30.java new file mode 100644 index 000000000..61d045598 --- /dev/null +++ b/vcard/java/com/android/vcard/VCardParserImpl_V30.java @@ -0,0 +1,317 @@ +/* + * Copyright (C) 2010 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.vcard; + +import android.util.Log; + +import com.android.vcard.exception.VCardException; + +import java.io.IOException; +import java.util.Set; + +/** + *

+ * Basic implementation achieving vCard 3.0 parsing. + *

+ *

+ * This class inherits vCard 2.1 implementation since technically they are similar, + * while specifically there's logical no relevance between them. + * So that developers are not confused with the inheritance, + * {@link VCardParser_V30} does not inherit {@link VCardParser_V21}, while + * {@link VCardParserImpl_V30} inherits {@link VCardParserImpl_V21}. + *

+ * @hide + */ +/* package */ class VCardParserImpl_V30 extends VCardParserImpl_V21 { + private static final String LOG_TAG = "VCardParserImpl_V30"; + + private String mPreviousLine; + private boolean mEmittedAgentWarning = false; + + public VCardParserImpl_V30() { + super(); + } + + public VCardParserImpl_V30(int vcardType) { + super(vcardType, null); + } + + public VCardParserImpl_V30(int vcardType, String importCharset) { + super(vcardType, importCharset); + } + + @Override + protected int getVersion() { + return VCardConfig.FLAG_V30; + } + + @Override + protected String getVersionString() { + return VCardConstants.VERSION_V30; + } + + @Override + protected String getLine() throws IOException { + if (mPreviousLine != null) { + String ret = mPreviousLine; + mPreviousLine = null; + return ret; + } else { + return mReader.readLine(); + } + } + + /** + * vCard 3.0 requires that the line with space at the beginning of the line + * must be combined with previous line. + */ + @Override + protected String getNonEmptyLine() throws IOException, VCardException { + String line; + StringBuilder builder = null; + while (true) { + line = mReader.readLine(); + if (line == null) { + if (builder != null) { + return builder.toString(); + } else if (mPreviousLine != null) { + String ret = mPreviousLine; + mPreviousLine = null; + return ret; + } + throw new VCardException("Reached end of buffer."); + } else if (line.length() == 0) { + if (builder != null) { + return builder.toString(); + } else if (mPreviousLine != null) { + String ret = mPreviousLine; + mPreviousLine = null; + return ret; + } + } else if (line.charAt(0) == ' ' || line.charAt(0) == '\t') { + if (builder != null) { + // See Section 5.8.1 of RFC 2425 (MIME-DIR document). + // Following is the excerpts from it. + // + // DESCRIPTION:This is a long description that exists on a long line. + // + // Can be represented as: + // + // DESCRIPTION:This is a long description + // that exists on a long line. + // + // It could also be represented as: + // + // DESCRIPTION:This is a long descrip + // tion that exists o + // n a long line. + builder.append(line.substring(1)); + } else if (mPreviousLine != null) { + builder = new StringBuilder(); + builder.append(mPreviousLine); + mPreviousLine = null; + builder.append(line.substring(1)); + } else { + throw new VCardException("Space exists at the beginning of the line"); + } + } else { + if (mPreviousLine == null) { + mPreviousLine = line; + if (builder != null) { + return builder.toString(); + } + } else { + String ret = mPreviousLine; + mPreviousLine = line; + return ret; + } + } + } + } + + /* + * vcard = [group "."] "BEGIN" ":" "VCARD" 1 * CRLF + * 1 * (contentline) + * ;A vCard object MUST include the VERSION, FN and N types. + * [group "."] "END" ":" "VCARD" 1 * CRLF + */ + @Override + protected boolean readBeginVCard(boolean allowGarbage) throws IOException, VCardException { + // TODO: vCard 3.0 supports group. + return super.readBeginVCard(allowGarbage); + } + + @Override + protected void readEndVCard(boolean useCache, boolean allowGarbage) + throws IOException, VCardException { + // TODO: vCard 3.0 supports group. + super.readEndVCard(useCache, allowGarbage); + } + + /** + * vCard 3.0 allows iana-token as paramType, while vCard 2.1 does not. + */ + @Override + protected void handleParams(final String params) throws VCardException { + try { + super.handleParams(params); + } catch (VCardException e) { + // maybe IANA type + String[] strArray = params.split("=", 2); + if (strArray.length == 2) { + handleAnyParam(strArray[0], strArray[1]); + } else { + // Must not come here in the current implementation. + throw new VCardException( + "Unknown params value: " + params); + } + } + } + + @Override + protected void handleAnyParam(final String paramName, final String paramValue) { + super.handleAnyParam(paramName, paramValue); + } + + @Override + protected void handleParamWithoutName(final String paramValue) throws VCardException { + super.handleParamWithoutName(paramValue); + } + + /* + * vCard 3.0 defines + * + * param = param-name "=" param-value *("," param-value) + * param-name = iana-token / x-name + * param-value = ptext / quoted-string + * quoted-string = DQUOTE QSAFE-CHAR DQUOTE + */ + @Override + protected void handleType(final String ptypevalues) { + String[] ptypeArray = ptypevalues.split(","); + mInterpreter.propertyParamType("TYPE"); + for (String value : ptypeArray) { + int length = value.length(); + if (length >= 2 && value.startsWith("\"") && value.endsWith("\"")) { + mInterpreter.propertyParamValue(value.substring(1, value.length() - 1)); + } else { + mInterpreter.propertyParamValue(value); + } + } + } + + @Override + protected void handleAgent(final String propertyValue) { + // The way how vCard 3.0 supports "AGENT" is completely different from vCard 2.1. + // + // e.g. + // AGENT:BEGIN:VCARD\nFN:Joe Friday\nTEL:+1-919-555-7878\n + // TITLE:Area Administrator\, Assistant\n EMAIL\;TYPE=INTERN\n + // ET:jfriday@host.com\nEND:VCARD\n + // + // TODO: fix this. + // + // issue: + // vCard 3.0 also allows this as an example. + // + // AGENT;VALUE=uri: + // CID:JQPUBLIC.part3.960129T083020.xyzMail@host3.com + // + // This is not vCard. Should we support this? + // + // Just ignore the line for now, since we cannot know how to handle it... + if (!mEmittedAgentWarning) { + Log.w(LOG_TAG, "AGENT in vCard 3.0 is not supported yet. Ignore it"); + mEmittedAgentWarning = true; + } + } + + /** + * vCard 3.0 does not require two CRLF at the last of BASE64 data. + * It only requires that data should be MIME-encoded. + */ + @Override + protected String getBase64(final String firstString) + throws IOException, VCardException { + final StringBuilder builder = new StringBuilder(); + builder.append(firstString); + + while (true) { + final String line = getLine(); + if (line == null) { + throw new VCardException("File ended during parsing BASE64 binary"); + } + if (line.length() == 0) { + break; + } else if (!line.startsWith(" ") && !line.startsWith("\t")) { + mPreviousLine = line; + break; + } + builder.append(line); + } + + return builder.toString(); + } + + /** + * ESCAPED-CHAR = "\\" / "\;" / "\," / "\n" / "\N") + * ; \\ encodes \, \n or \N encodes newline + * ; \; encodes ;, \, encodes , + * + * Note: Apple escapes ':' into '\:' while does not escape '\' + */ + @Override + protected String maybeUnescapeText(final String text) { + return unescapeText(text); + } + + public static String unescapeText(final String text) { + StringBuilder builder = new StringBuilder(); + final int length = text.length(); + for (int i = 0; i < length; i++) { + char ch = text.charAt(i); + if (ch == '\\' && i < length - 1) { + final char next_ch = text.charAt(++i); + if (next_ch == 'n' || next_ch == 'N') { + builder.append("\n"); + } else { + builder.append(next_ch); + } + } else { + builder.append(ch); + } + } + return builder.toString(); + } + + @Override + protected String maybeUnescapeCharacter(final char ch) { + return unescapeCharacter(ch); + } + + public static String unescapeCharacter(final char ch) { + if (ch == 'n' || ch == 'N') { + return "\n"; + } else { + return String.valueOf(ch); + } + } + + @Override + protected Set getKnownPropertyNameSet() { + return VCardParser_V30.sKnownPropertyNameSet; + } +} diff --git a/vcard/java/com/android/vcard/VCardParser_V21.java b/vcard/java/com/android/vcard/VCardParser_V21.java new file mode 100644 index 000000000..2a5e31326 --- /dev/null +++ b/vcard/java/com/android/vcard/VCardParser_V21.java @@ -0,0 +1,113 @@ +/* + * 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.vcard; + +import com.android.vcard.exception.VCardException; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +/** + *

+ * vCard parser for vCard 2.1. See the specification for more detail about the spec itself. + *

+ *

+ * The spec is written in 1996, and currently various types of "vCard 2.1" exist. + * To handle real the world vCard formats appropriately and effectively, this class does not + * obey with strict vCard 2.1. + * In stead, not only vCard spec but also real world vCard is considered. + *

+ * e.g. A lot of devices and softwares let vCard importer/exporter to use + * the PNG format to determine the type of image, while it is not allowed in + * the original specification. As of 2010, we can see even the FLV format + * (possible in Japanese mobile phones). + *

+ */ +public final class VCardParser_V21 implements VCardParser { + /** + * A unmodifiable Set storing the property names available in the vCard 2.1 specification. + */ + /* package */ static final Set sKnownPropertyNameSet = + Collections.unmodifiableSet(new HashSet( + Arrays.asList("BEGIN", "LOGO", "PHOTO", "LABEL", "FN", "TITLE", "SOUND", + "VERSION", "TEL", "EMAIL", "TZ", "GEO", "NOTE", "URL", + "BDAY", "ROLE", "REV", "UID", "KEY", "MAILER"))); + + /** + * A unmodifiable Set storing the types known in vCard 2.1. + */ + /* package */ static final Set sKnownTypeSet = + Collections.unmodifiableSet(new HashSet( + Arrays.asList("DOM", "INTL", "POSTAL", "PARCEL", "HOME", "WORK", + "PREF", "VOICE", "FAX", "MSG", "CELL", "PAGER", "BBS", + "MODEM", "CAR", "ISDN", "VIDEO", "AOL", "APPLELINK", + "ATTMAIL", "CIS", "EWORLD", "INTERNET", "IBMMAIL", + "MCIMAIL", "POWERSHARE", "PRODIGY", "TLX", "X400", "GIF", + "CGM", "WMF", "BMP", "MET", "PMB", "DIB", "PICT", "TIFF", + "PDF", "PS", "JPEG", "QTIME", "MPEG", "MPEG2", "AVI", + "WAVE", "AIFF", "PCM", "X509", "PGP"))); + + /** + * A unmodifiable Set storing the values for the type "VALUE", available in the vCard 2.1. + */ + /* package */ static final Set sKnownValueSet = + Collections.unmodifiableSet(new HashSet( + Arrays.asList("INLINE", "URL", "CONTENT-ID", "CID"))); + + /** + *

+ * A unmodifiable Set storing the values for the type "ENCODING", available in the vCard 2.1. + *

+ *

+ * Though vCard 2.1 specification does not allow "B" encoding, some data may have it. + * We allow it for safety. + *

+ */ + /* package */ static final Set sAvailableEncoding = + Collections.unmodifiableSet(new HashSet( + Arrays.asList(VCardConstants.PARAM_ENCODING_7BIT, + VCardConstants.PARAM_ENCODING_8BIT, + VCardConstants.PARAM_ENCODING_QP, + VCardConstants.PARAM_ENCODING_BASE64, + VCardConstants.PARAM_ENCODING_B))); + + private final VCardParserImpl_V21 mVCardParserImpl; + + public VCardParser_V21() { + mVCardParserImpl = new VCardParserImpl_V21(); + } + + public VCardParser_V21(int vcardType) { + mVCardParserImpl = new VCardParserImpl_V21(vcardType); + } + + public VCardParser_V21(int parseType, String inputCharset) { + mVCardParserImpl = new VCardParserImpl_V21(parseType, null); + } + + public void parse(InputStream is, VCardInterpreter interepreter) + throws IOException, VCardException { + mVCardParserImpl.parse(is, interepreter); + } + + public void cancel() { + mVCardParserImpl.cancel(); + } +} diff --git a/vcard/java/com/android/vcard/VCardParser_V30.java b/vcard/java/com/android/vcard/VCardParser_V30.java new file mode 100644 index 000000000..179869b21 --- /dev/null +++ b/vcard/java/com/android/vcard/VCardParser_V30.java @@ -0,0 +1,91 @@ +/* + * 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.vcard; + +import com.android.vcard.exception.VCardException; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +/** + *

+ * vCard parser for vCard 3.0. See RFC 2426 for more detail. + *

+ *

+ * This parser allows vCard format which is not allowed in the RFC, since + * we have seen several vCard 3.0 files which don't comply with it. + *

+ *

+ * e.g. vCard 3.0 does not allow "CHARSET" attribute, but some actual files + * have it and they uses non UTF-8 charsets. UTF-8 is recommended in RFC 2426, + * but it is not a must. We silently allow "CHARSET". + *

+ */ +public class VCardParser_V30 implements VCardParser { + /* package */ static final Set sKnownPropertyNameSet = + Collections.unmodifiableSet(new HashSet(Arrays.asList( + "BEGIN", "LOGO", "PHOTO", "LABEL", "FN", "TITLE", "SOUND", + "VERSION", "TEL", "EMAIL", "TZ", "GEO", "NOTE", "URL", + "BDAY", "ROLE", "REV", "UID", "KEY", "MAILER", // 2.1 + "NAME", "PROFILE", "SOURCE", "NICKNAME", "CLASS", + "SORT-STRING", "CATEGORIES", "PRODID"))); // 3.0 + + /** + *

+ * A unmodifiable Set storing the values for the type "ENCODING", available in the vCard 3.0. + *

+ *

+ * Though vCard 2.1 specification does not allow "7BIT" or "BASE64", we allow them for safety. + *

+ *

+ * "QUOTED-PRINTABLE" is not allowed in vCard 3.0 and not in this parser either, + * because the encoding ambiguates how the vCard file to be parsed. + *

+ */ + /* package */ static final Set sAcceptableEncoding = + Collections.unmodifiableSet(new HashSet(Arrays.asList( + VCardConstants.PARAM_ENCODING_7BIT, + VCardConstants.PARAM_ENCODING_8BIT, + VCardConstants.PARAM_ENCODING_BASE64, + VCardConstants.PARAM_ENCODING_B))); + + private final VCardParserImpl_V30 mVCardParserImpl; + + public VCardParser_V30() { + mVCardParserImpl = new VCardParserImpl_V30(); + } + + public VCardParser_V30(int vcardType) { + mVCardParserImpl = new VCardParserImpl_V30(vcardType); + } + + public VCardParser_V30(int vcardType, String importCharset) { + mVCardParserImpl = new VCardParserImpl_V30(vcardType, importCharset); + } + + public void parse(InputStream is, VCardInterpreter interepreter) + throws IOException, VCardException { + mVCardParserImpl.parse(is, interepreter); + } + + public void cancel() { + mVCardParserImpl.cancel(); + } +} diff --git a/vcard/java/com/android/vcard/VCardSourceDetector.java b/vcard/java/com/android/vcard/VCardSourceDetector.java new file mode 100644 index 000000000..e70d4961c --- /dev/null +++ b/vcard/java/com/android/vcard/VCardSourceDetector.java @@ -0,0 +1,172 @@ +/* + * 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.vcard; + +import android.text.TextUtils; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + *

+ * The class which tries to detects the source of a vCard file from its contents. + *

+ *

+ * The specification of vCard (including both 2.1 and 3.0) is not so strict as to + * guess its format just by reading beginning few lines (usually we can, but in + * some most pessimistic case, we cannot until at almost the end of the file). + * Also we cannot store all vCard entries in memory, while there's no specification + * how big the vCard entry would become after the parse. + *

+ *

+ * This class is usually used for the "first scan", in which we can understand which vCard + * version is used (and how many entries exist in a file). + *

+ */ +public class VCardSourceDetector implements VCardInterpreter { + private static Set APPLE_SIGNS = new HashSet(Arrays.asList( + "X-PHONETIC-FIRST-NAME", "X-PHONETIC-MIDDLE-NAME", "X-PHONETIC-LAST-NAME", + "X-ABADR", "X-ABUID")); + + private static Set JAPANESE_MOBILE_PHONE_SIGNS = new HashSet(Arrays.asList( + "X-GNO", "X-GN", "X-REDUCTION")); + + private static Set WINDOWS_MOBILE_PHONE_SIGNS = new HashSet(Arrays.asList( + "X-MICROSOFT-ASST_TEL", "X-MICROSOFT-ASSISTANT", "X-MICROSOFT-OFFICELOC")); + + // Note: these signes appears before the signs of the other type (e.g. "X-GN"). + // In other words, Japanese FOMA mobile phones are detected as FOMA, not JAPANESE_MOBILE_PHONES. + private static Set FOMA_SIGNS = new HashSet(Arrays.asList( + "X-SD-VERN", "X-SD-FORMAT_VER", "X-SD-CATEGORIES", "X-SD-CLASS", "X-SD-DCREATED", + "X-SD-DESCRIPTION")); + private static String TYPE_FOMA_CHARSET_SIGN = "X-SD-CHAR_CODE"; + + + // TODO: Should replace this with types in VCardConfig + private static final int PARSE_TYPE_UNKNOWN = 0; + // For Apple's software, which does not mean this type is effective for all its products. + // We confirmed they usually use UTF-8, but not sure about vCard type. + private static final int PARSE_TYPE_APPLE = 1; + // For Japanese mobile phones, which are usually using Shift_JIS as a charset. + private static final int PARSE_TYPE_MOBILE_PHONE_JP = 2; + // For some of mobile phones released from DoCoMo, which use nested vCard. + private static final int PARSE_TYPE_DOCOMO_TORELATE_NEST = 3; + // For Japanese Windows Mobel phones. It's version is supposed to be 6.5. + private static final int PARSE_TYPE_WINDOWS_MOBILE_V65_JP = 4; + + private int mParseType = 0; // Not sure. + + // Some mobile phones (like FOMA) tells us the charset of the data. + private boolean mNeedParseSpecifiedCharset; + private String mSpecifiedCharset; + + public void start() { + } + + public void end() { + } + + public void startEntry() { + } + + public void startProperty() { + mNeedParseSpecifiedCharset = false; + } + + public void endProperty() { + } + + public void endEntry() { + } + + public void propertyGroup(String group) { + } + + public void propertyName(String name) { + if (name.equalsIgnoreCase(TYPE_FOMA_CHARSET_SIGN)) { + mParseType = PARSE_TYPE_DOCOMO_TORELATE_NEST; + // Probably Shift_JIS is used, but we should double confirm. + mNeedParseSpecifiedCharset = true; + return; + } + if (mParseType != PARSE_TYPE_UNKNOWN) { + return; + } + if (WINDOWS_MOBILE_PHONE_SIGNS.contains(name)) { + mParseType = PARSE_TYPE_WINDOWS_MOBILE_V65_JP; + } else if (FOMA_SIGNS.contains(name)) { + mParseType = PARSE_TYPE_DOCOMO_TORELATE_NEST; + } else if (JAPANESE_MOBILE_PHONE_SIGNS.contains(name)) { + mParseType = PARSE_TYPE_MOBILE_PHONE_JP; + } else if (APPLE_SIGNS.contains(name)) { + mParseType = PARSE_TYPE_APPLE; + } + } + + public void propertyParamType(String type) { + } + + public void propertyParamValue(String value) { + } + + public void propertyValues(List values) { + if (mNeedParseSpecifiedCharset && values.size() > 0) { + mSpecifiedCharset = values.get(0); + } + } + + /** + * @return The available type can be used with vCard parser. You probably need to + * use {{@link #getEstimatedCharset()} to understand the charset to be used. + */ + public int getEstimatedType() { + switch (mParseType) { + case PARSE_TYPE_DOCOMO_TORELATE_NEST: + return VCardConfig.VCARD_TYPE_DOCOMO | VCardConfig.FLAG_TORELATE_NEST; + case PARSE_TYPE_MOBILE_PHONE_JP: + return VCardConfig.VCARD_TYPE_V21_JAPANESE_MOBILE; + case PARSE_TYPE_APPLE: + case PARSE_TYPE_WINDOWS_MOBILE_V65_JP: + default: + return VCardConfig.VCARD_TYPE_UNKNOWN; + } + } + + /** + *

+ * Returns charset String guessed from the source's properties. + * This method must be called after parsing target file(s). + *

+ * @return Charset String. Null is returned if guessing the source fails. + */ + public String getEstimatedCharset() { + if (TextUtils.isEmpty(mSpecifiedCharset)) { + return mSpecifiedCharset; + } + switch (mParseType) { + case PARSE_TYPE_WINDOWS_MOBILE_V65_JP: + case PARSE_TYPE_DOCOMO_TORELATE_NEST: + case PARSE_TYPE_MOBILE_PHONE_JP: + return "SHIFT_JIS"; + case PARSE_TYPE_APPLE: + return "UTF-8"; + default: + return null; + } + } +} diff --git a/vcard/java/com/android/vcard/VCardUtils.java b/vcard/java/com/android/vcard/VCardUtils.java new file mode 100644 index 000000000..fb0c2e7b6 --- /dev/null +++ b/vcard/java/com/android/vcard/VCardUtils.java @@ -0,0 +1,658 @@ +/* + * 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.vcard; + +import org.apache.commons.codec.DecoderException; +import org.apache.commons.codec.net.QuotedPrintableCodec; + +import android.content.ContentProviderOperation; +import android.provider.ContactsContract.Data; +import android.provider.ContactsContract.CommonDataKinds.Im; +import android.provider.ContactsContract.CommonDataKinds.Phone; +import android.provider.ContactsContract.CommonDataKinds.StructuredPostal; +import android.telephony.PhoneNumberUtils; +import android.text.TextUtils; +import android.util.Log; + +import java.io.UnsupportedEncodingException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Utilities for VCard handling codes. + */ +public class VCardUtils { + private static final String LOG_TAG = "VCardUtils"; + + // Note that not all types are included in this map/set, since, for example, TYPE_HOME_FAX is + // converted to two parameter Strings. These only contain some minor fields valid in both + // vCard and current (as of 2009-08-07) Contacts structure. + private static final Map sKnownPhoneTypesMap_ItoS; + private static final Set sPhoneTypesUnknownToContactsSet; + private static final Map sKnownPhoneTypeMap_StoI; + private static final Map sKnownImPropNameMap_ItoS; + private static final Set sMobilePhoneLabelSet; + + static { + sKnownPhoneTypesMap_ItoS = new HashMap(); + sKnownPhoneTypeMap_StoI = new HashMap(); + + sKnownPhoneTypesMap_ItoS.put(Phone.TYPE_CAR, VCardConstants.PARAM_TYPE_CAR); + sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_TYPE_CAR, Phone.TYPE_CAR); + sKnownPhoneTypesMap_ItoS.put(Phone.TYPE_PAGER, VCardConstants.PARAM_TYPE_PAGER); + sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_TYPE_PAGER, Phone.TYPE_PAGER); + sKnownPhoneTypesMap_ItoS.put(Phone.TYPE_ISDN, VCardConstants.PARAM_TYPE_ISDN); + sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_TYPE_ISDN, Phone.TYPE_ISDN); + + sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_TYPE_HOME, Phone.TYPE_HOME); + sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_TYPE_WORK, Phone.TYPE_WORK); + sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_TYPE_CELL, Phone.TYPE_MOBILE); + + sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_PHONE_EXTRA_TYPE_OTHER, Phone.TYPE_OTHER); + sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_PHONE_EXTRA_TYPE_CALLBACK, + Phone.TYPE_CALLBACK); + sKnownPhoneTypeMap_StoI.put( + VCardConstants.PARAM_PHONE_EXTRA_TYPE_COMPANY_MAIN, Phone.TYPE_COMPANY_MAIN); + sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_PHONE_EXTRA_TYPE_RADIO, Phone.TYPE_RADIO); + sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_PHONE_EXTRA_TYPE_TTY_TDD, + Phone.TYPE_TTY_TDD); + sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_PHONE_EXTRA_TYPE_ASSISTANT, + Phone.TYPE_ASSISTANT); + + sPhoneTypesUnknownToContactsSet = new HashSet(); + sPhoneTypesUnknownToContactsSet.add(VCardConstants.PARAM_TYPE_MODEM); + sPhoneTypesUnknownToContactsSet.add(VCardConstants.PARAM_TYPE_MSG); + sPhoneTypesUnknownToContactsSet.add(VCardConstants.PARAM_TYPE_BBS); + sPhoneTypesUnknownToContactsSet.add(VCardConstants.PARAM_TYPE_VIDEO); + + sKnownImPropNameMap_ItoS = new HashMap(); + sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_AIM, VCardConstants.PROPERTY_X_AIM); + sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_MSN, VCardConstants.PROPERTY_X_MSN); + sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_YAHOO, VCardConstants.PROPERTY_X_YAHOO); + sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_SKYPE, VCardConstants.PROPERTY_X_SKYPE_USERNAME); + sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_GOOGLE_TALK, + VCardConstants.PROPERTY_X_GOOGLE_TALK); + sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_ICQ, VCardConstants.PROPERTY_X_ICQ); + sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_JABBER, VCardConstants.PROPERTY_X_JABBER); + sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_QQ, VCardConstants.PROPERTY_X_QQ); + sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_NETMEETING, VCardConstants.PROPERTY_X_NETMEETING); + + // \u643A\u5E2F\u96FB\u8A71 = Full-width Hiragana "Keitai-Denwa" (mobile phone) + // \u643A\u5E2F = Full-width Hiragana "Keitai" (mobile phone) + // \u30B1\u30A4\u30BF\u30A4 = Full-width Katakana "Keitai" (mobile phone) + // \uFF79\uFF72\uFF80\uFF72 = Half-width Katakana "Keitai" (mobile phone) + sMobilePhoneLabelSet = new HashSet(Arrays.asList( + "MOBILE", "\u643A\u5E2F\u96FB\u8A71", "\u643A\u5E2F", "\u30B1\u30A4\u30BF\u30A4", + "\uFF79\uFF72\uFF80\uFF72")); + } + + public static String getPhoneTypeString(Integer type) { + return sKnownPhoneTypesMap_ItoS.get(type); + } + + /** + * Returns Interger when the given types can be parsed as known type. Returns String object + * when not, which should be set to label. + */ + public static Object getPhoneTypeFromStrings(Collection types, + String number) { + if (number == null) { + number = ""; + } + int type = -1; + String label = null; + boolean isFax = false; + boolean hasPref = false; + + if (types != null) { + for (String typeString : types) { + if (typeString == null) { + continue; + } + typeString = typeString.toUpperCase(); + if (typeString.equals(VCardConstants.PARAM_TYPE_PREF)) { + hasPref = true; + } else if (typeString.equals(VCardConstants.PARAM_TYPE_FAX)) { + isFax = true; + } else { + if (typeString.startsWith("X-") && type < 0) { + typeString = typeString.substring(2); + } + if (typeString.length() == 0) { + continue; + } + final Integer tmp = sKnownPhoneTypeMap_StoI.get(typeString); + if (tmp != null) { + final int typeCandidate = tmp; + // TYPE_PAGER is prefered when the number contains @ surronded by + // a pager number and a domain name. + // e.g. + // o 1111@domain.com + // x @domain.com + // x 1111@ + final int indexOfAt = number.indexOf("@"); + if ((typeCandidate == Phone.TYPE_PAGER + && 0 < indexOfAt && indexOfAt < number.length() - 1) + || type < 0 + || type == Phone.TYPE_CUSTOM) { + type = tmp; + } + } else if (type < 0) { + type = Phone.TYPE_CUSTOM; + label = typeString; + } + } + } + } + if (type < 0) { + if (hasPref) { + type = Phone.TYPE_MAIN; + } else { + // default to TYPE_HOME + type = Phone.TYPE_HOME; + } + } + if (isFax) { + if (type == Phone.TYPE_HOME) { + type = Phone.TYPE_FAX_HOME; + } else if (type == Phone.TYPE_WORK) { + type = Phone.TYPE_FAX_WORK; + } else if (type == Phone.TYPE_OTHER) { + type = Phone.TYPE_OTHER_FAX; + } + } + if (type == Phone.TYPE_CUSTOM) { + return label; + } else { + return type; + } + } + + @SuppressWarnings("deprecation") + public static boolean isMobilePhoneLabel(final String label) { + // For backward compatibility. + // Detail: Until Donut, there isn't TYPE_MOBILE for email while there is now. + // To support mobile type at that time, this custom label had been used. + return ("_AUTO_CELL".equals(label) || sMobilePhoneLabelSet.contains(label)); + } + + public static boolean isValidInV21ButUnknownToContactsPhoteType(final String label) { + return sPhoneTypesUnknownToContactsSet.contains(label); + } + + public static String getPropertyNameForIm(final int protocol) { + return sKnownImPropNameMap_ItoS.get(protocol); + } + + public static String[] sortNameElements(final int vcardType, + final String familyName, final String middleName, final String givenName) { + final String[] list = new String[3]; + final int nameOrderType = VCardConfig.getNameOrderType(vcardType); + switch (nameOrderType) { + case VCardConfig.NAME_ORDER_JAPANESE: { + if (containsOnlyPrintableAscii(familyName) && + containsOnlyPrintableAscii(givenName)) { + list[0] = givenName; + list[1] = middleName; + list[2] = familyName; + } else { + list[0] = familyName; + list[1] = middleName; + list[2] = givenName; + } + break; + } + case VCardConfig.NAME_ORDER_EUROPE: { + list[0] = middleName; + list[1] = givenName; + list[2] = familyName; + break; + } + default: { + list[0] = givenName; + list[1] = middleName; + list[2] = familyName; + break; + } + } + return list; + } + + public static int getPhoneNumberFormat(final int vcardType) { + if (VCardConfig.isJapaneseDevice(vcardType)) { + return PhoneNumberUtils.FORMAT_JAPAN; + } else { + return PhoneNumberUtils.FORMAT_NANP; + } + } + + /** + *

+ * Inserts postal data into the builder object. + *

+ *

+ * Note that the data structure of ContactsContract is different from that defined in vCard. + * So some conversion may be performed in this method. + *

+ */ + public static void insertStructuredPostalDataUsingContactsStruct(int vcardType, + final ContentProviderOperation.Builder builder, + final VCardEntry.PostalData postalData) { + builder.withValueBackReference(StructuredPostal.RAW_CONTACT_ID, 0); + builder.withValue(Data.MIMETYPE, StructuredPostal.CONTENT_ITEM_TYPE); + + builder.withValue(StructuredPostal.TYPE, postalData.type); + if (postalData.type == StructuredPostal.TYPE_CUSTOM) { + builder.withValue(StructuredPostal.LABEL, postalData.label); + } + + final String streetString; + if (TextUtils.isEmpty(postalData.street)) { + if (TextUtils.isEmpty(postalData.extendedAddress)) { + streetString = null; + } else { + streetString = postalData.extendedAddress; + } + } else { + if (TextUtils.isEmpty(postalData.extendedAddress)) { + streetString = postalData.street; + } else { + streetString = postalData.street + " " + postalData.extendedAddress; + } + } + builder.withValue(StructuredPostal.POBOX, postalData.pobox); + builder.withValue(StructuredPostal.STREET, streetString); + builder.withValue(StructuredPostal.CITY, postalData.localty); + builder.withValue(StructuredPostal.REGION, postalData.region); + builder.withValue(StructuredPostal.POSTCODE, postalData.postalCode); + builder.withValue(StructuredPostal.COUNTRY, postalData.country); + + builder.withValue(StructuredPostal.FORMATTED_ADDRESS, + postalData.getFormattedAddress(vcardType)); + if (postalData.isPrimary) { + builder.withValue(Data.IS_PRIMARY, 1); + } + } + + public static String constructNameFromElements(final int vcardType, + final String familyName, final String middleName, final String givenName) { + return constructNameFromElements(vcardType, familyName, middleName, givenName, + null, null); + } + + public static String constructNameFromElements(final int vcardType, + final String familyName, final String middleName, final String givenName, + final String prefix, final String suffix) { + final StringBuilder builder = new StringBuilder(); + final String[] nameList = sortNameElements(vcardType, familyName, middleName, givenName); + boolean first = true; + if (!TextUtils.isEmpty(prefix)) { + first = false; + builder.append(prefix); + } + for (final String namePart : nameList) { + if (!TextUtils.isEmpty(namePart)) { + if (first) { + first = false; + } else { + builder.append(' '); + } + builder.append(namePart); + } + } + if (!TextUtils.isEmpty(suffix)) { + if (!first) { + builder.append(' '); + } + builder.append(suffix); + } + return builder.toString(); + } + + public static List constructListFromValue(final String value, + final boolean isV30) { + final List list = new ArrayList(); + StringBuilder builder = new StringBuilder(); + int length = value.length(); + for (int i = 0; i < length; i++) { + char ch = value.charAt(i); + if (ch == '\\' && i < length - 1) { + char nextCh = value.charAt(i + 1); + final String unescapedString = + (isV30 ? VCardParserImpl_V30.unescapeCharacter(nextCh) : + VCardParserImpl_V21.unescapeCharacter(nextCh)); + if (unescapedString != null) { + builder.append(unescapedString); + i++; + } else { + builder.append(ch); + } + } else if (ch == ';') { + list.add(builder.toString()); + builder = new StringBuilder(); + } else { + builder.append(ch); + } + } + list.add(builder.toString()); + return list; + } + + public static boolean containsOnlyPrintableAscii(final String...values) { + if (values == null) { + return true; + } + return containsOnlyPrintableAscii(Arrays.asList(values)); + } + + public static boolean containsOnlyPrintableAscii(final Collection values) { + if (values == null) { + return true; + } + for (final String value : values) { + if (TextUtils.isEmpty(value)) { + continue; + } + if (!TextUtils.isPrintableAsciiOnly(value)) { + return false; + } + } + return true; + } + + /** + *

+ * This is useful when checking the string should be encoded into quoted-printable + * or not, which is required by vCard 2.1. + *

+ *

+ * See the definition of "7bit" in vCard 2.1 spec for more information. + *

+ */ + public static boolean containsOnlyNonCrLfPrintableAscii(final String...values) { + if (values == null) { + return true; + } + return containsOnlyNonCrLfPrintableAscii(Arrays.asList(values)); + } + + public static boolean containsOnlyNonCrLfPrintableAscii(final Collection values) { + if (values == null) { + return true; + } + final int asciiFirst = 0x20; + final int asciiLast = 0x7E; // included + for (final String value : values) { + if (TextUtils.isEmpty(value)) { + continue; + } + final int length = value.length(); + for (int i = 0; i < length; i = value.offsetByCodePoints(i, 1)) { + final int c = value.codePointAt(i); + if (!(asciiFirst <= c && c <= asciiLast)) { + return false; + } + } + } + return true; + } + + private static final Set sUnAcceptableAsciiInV21WordSet = + new HashSet(Arrays.asList('[', ']', '=', ':', '.', ',', ' ')); + + /** + *

+ * This is useful since vCard 3.0 often requires the ("X-") properties and groups + * should contain only alphabets, digits, and hyphen. + *

+ *

+ * Note: It is already known some devices (wrongly) outputs properties with characters + * which should not be in the field. One example is "X-GOOGLE TALK". We accept + * such kind of input but must never output it unless the target is very specific + * to the device which is able to parse the malformed input. + *

+ */ + public static boolean containsOnlyAlphaDigitHyphen(final String...values) { + if (values == null) { + return true; + } + return containsOnlyAlphaDigitHyphen(Arrays.asList(values)); + } + + public static boolean containsOnlyAlphaDigitHyphen(final Collection values) { + if (values == null) { + return true; + } + final int upperAlphabetFirst = 0x41; // A + final int upperAlphabetAfterLast = 0x5b; // [ + final int lowerAlphabetFirst = 0x61; // a + final int lowerAlphabetAfterLast = 0x7b; // { + final int digitFirst = 0x30; // 0 + final int digitAfterLast = 0x3A; // : + final int hyphen = '-'; + for (final String str : values) { + if (TextUtils.isEmpty(str)) { + continue; + } + final int length = str.length(); + for (int i = 0; i < length; i = str.offsetByCodePoints(i, 1)) { + int codepoint = str.codePointAt(i); + if (!((lowerAlphabetFirst <= codepoint && codepoint < lowerAlphabetAfterLast) || + (upperAlphabetFirst <= codepoint && codepoint < upperAlphabetAfterLast) || + (digitFirst <= codepoint && codepoint < digitAfterLast) || + (codepoint == hyphen))) { + return false; + } + } + } + return true; + } + + /** + *

+ * Returns true when the given String is categorized as "word" specified in vCard spec 2.1. + *

+ *

+ * vCard 2.1 specifies:
+ * word = <any printable 7bit us-ascii except []=:., > + *

+ */ + public static boolean isV21Word(final String value) { + if (TextUtils.isEmpty(value)) { + return true; + } + final int asciiFirst = 0x20; + final int asciiLast = 0x7E; // included + final int length = value.length(); + for (int i = 0; i < length; i = value.offsetByCodePoints(i, 1)) { + final int c = value.codePointAt(i); + if (!(asciiFirst <= c && c <= asciiLast) || + sUnAcceptableAsciiInV21WordSet.contains((char)c)) { + return false; + } + } + return true; + } + + public static String toHalfWidthString(final String orgString) { + if (TextUtils.isEmpty(orgString)) { + return null; + } + final StringBuilder builder = new StringBuilder(); + final int length = orgString.length(); + for (int i = 0; i < length; i = orgString.offsetByCodePoints(i, 1)) { + // All Japanese character is able to be expressed by char. + // Do not need to use String#codepPointAt(). + final char ch = orgString.charAt(i); + final String halfWidthText = JapaneseUtils.tryGetHalfWidthText(ch); + if (halfWidthText != null) { + builder.append(halfWidthText); + } else { + builder.append(ch); + } + } + return builder.toString(); + } + + /** + * Guesses the format of input image. Currently just the first few bytes are used. + * The type "GIF", "PNG", or "JPEG" is returned when possible. Returns null when + * the guess failed. + * @param input Image as byte array. + * @return The image type or null when the type cannot be determined. + */ + public static String guessImageType(final byte[] input) { + if (input == null) { + return null; + } + if (input.length >= 3 && input[0] == 'G' && input[1] == 'I' && input[2] == 'F') { + return "GIF"; + } else if (input.length >= 4 && input[0] == (byte) 0x89 + && input[1] == 'P' && input[2] == 'N' && input[3] == 'G') { + // Note: vCard 2.1 officially does not support PNG, but we may have it and + // using X- word like "X-PNG" may not let importers know it is PNG. + // So we use the String "PNG" as is... + return "PNG"; + } else if (input.length >= 2 && input[0] == (byte) 0xff + && input[1] == (byte) 0xd8) { + return "JPEG"; + } else { + return null; + } + } + + /** + * @return True when all the given values are null or empty Strings. + */ + public static boolean areAllEmpty(final String...values) { + if (values == null) { + return true; + } + + for (final String value : values) { + if (!TextUtils.isEmpty(value)) { + return false; + } + } + return true; + } + + //// The methods bellow may be used by unit test. + + /** + * @hide + */ + public static String parseQuotedPrintable(String value, boolean strictLineBreaking, + String sourceCharset, String targetCharset) { + // "= " -> " ", "=\t" -> "\t". + // Previous code had done this replacement. Keep on the safe side. + final String quotedPrintable; + { + final StringBuilder builder = new StringBuilder(); + final int length = value.length(); + for (int i = 0; i < length; i++) { + char ch = value.charAt(i); + if (ch == '=' && i < length - 1) { + char nextCh = value.charAt(i + 1); + if (nextCh == ' ' || nextCh == '\t') { + builder.append(nextCh); + i++; + continue; + } + } + builder.append(ch); + } + quotedPrintable = builder.toString(); + } + + String[] lines; + if (strictLineBreaking) { + lines = quotedPrintable.split("\r\n"); + } else { + StringBuilder builder = new StringBuilder(); + final int length = quotedPrintable.length(); + ArrayList list = new ArrayList(); + for (int i = 0; i < length; i++) { + char ch = quotedPrintable.charAt(i); + if (ch == '\n') { + list.add(builder.toString()); + builder = new StringBuilder(); + } else if (ch == '\r') { + list.add(builder.toString()); + builder = new StringBuilder(); + if (i < length - 1) { + char nextCh = quotedPrintable.charAt(i + 1); + if (nextCh == '\n') { + i++; + } + } + } else { + builder.append(ch); + } + } + final String lastLine = builder.toString(); + if (lastLine.length() > 0) { + list.add(lastLine); + } + lines = list.toArray(new String[0]); + } + + final StringBuilder builder = new StringBuilder(); + for (String line : lines) { + if (line.endsWith("=")) { + line = line.substring(0, line.length() - 1); + } + builder.append(line); + } + + final String rawString = builder.toString(); + if (TextUtils.isEmpty(rawString)) { + Log.w(LOG_TAG, "Given raw string is empty."); + } + + byte[] rawBytes = null; + try { + rawBytes = rawString.getBytes(sourceCharset); + } catch (UnsupportedEncodingException e) { + Log.w(LOG_TAG, "Failed to decode: " + sourceCharset); + rawBytes = rawString.getBytes(); + } + + byte[] decodedBytes = null; + try { + decodedBytes = QuotedPrintableCodec.decodeQuotedPrintable(rawBytes); + } catch (DecoderException e) { + Log.e(LOG_TAG, "DecoderException is thrown."); + decodedBytes = rawBytes; + } + + try { + return new String(decodedBytes, targetCharset); + } catch (UnsupportedEncodingException e) { + Log.e(LOG_TAG, "Failed to encode: charset=" + targetCharset); + return new String(decodedBytes); + } + } + + private VCardUtils() { + } +} diff --git a/vcard/java/com/android/vcard/exception/VCardAgentNotSupportedException.java b/vcard/java/com/android/vcard/exception/VCardAgentNotSupportedException.java new file mode 100644 index 000000000..c408716e5 --- /dev/null +++ b/vcard/java/com/android/vcard/exception/VCardAgentNotSupportedException.java @@ -0,0 +1,27 @@ +/* + * 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.vcard.exception; + +public class VCardAgentNotSupportedException extends VCardNotSupportedException { + public VCardAgentNotSupportedException() { + super(); + } + + public VCardAgentNotSupportedException(String message) { + super(message); + } + +} \ No newline at end of file diff --git a/vcard/java/com/android/vcard/exception/VCardException.java b/vcard/java/com/android/vcard/exception/VCardException.java new file mode 100644 index 000000000..3ad7fd38b --- /dev/null +++ b/vcard/java/com/android/vcard/exception/VCardException.java @@ -0,0 +1,35 @@ +/* + * 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.vcard.exception; + +public class VCardException extends java.lang.Exception { + /** + * Constructs a VCardException object + */ + public VCardException() { + super(); + } + + /** + * Constructs a VCardException object + * + * @param message the error message + */ + public VCardException(String message) { + super(message); + } + +} diff --git a/vcard/java/com/android/vcard/exception/VCardInvalidCommentLineException.java b/vcard/java/com/android/vcard/exception/VCardInvalidCommentLineException.java new file mode 100644 index 000000000..342769ef5 --- /dev/null +++ b/vcard/java/com/android/vcard/exception/VCardInvalidCommentLineException.java @@ -0,0 +1,32 @@ +/* + * 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.vcard.exception; + +/** + * Thrown when the vCard has some line starting with '#'. In the specification, + * both vCard 2.1 and vCard 3.0 does not allow such line, but some actual exporter emit + * such lines. + */ +public class VCardInvalidCommentLineException extends VCardInvalidLineException { + public VCardInvalidCommentLineException() { + super(); + } + + public VCardInvalidCommentLineException(final String message) { + super(message); + } +} diff --git a/vcard/java/com/android/vcard/exception/VCardInvalidLineException.java b/vcard/java/com/android/vcard/exception/VCardInvalidLineException.java new file mode 100644 index 000000000..5c2250fc3 --- /dev/null +++ b/vcard/java/com/android/vcard/exception/VCardInvalidLineException.java @@ -0,0 +1,31 @@ +/* + * 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.vcard.exception; + +/** + * Thrown when the vCard has some line starting with '#'. In the specification, + * both vCard 2.1 and vCard 3.0 does not allow such line, but some actual exporter emit + * such lines. + */ +public class VCardInvalidLineException extends VCardException { + public VCardInvalidLineException() { + super(); + } + + public VCardInvalidLineException(final String message) { + super(message); + } +} diff --git a/vcard/java/com/android/vcard/exception/VCardNestedException.java b/vcard/java/com/android/vcard/exception/VCardNestedException.java new file mode 100644 index 000000000..2b9b1acf5 --- /dev/null +++ b/vcard/java/com/android/vcard/exception/VCardNestedException.java @@ -0,0 +1,29 @@ +/* + * 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.vcard.exception; + +/** + * VCardException thrown when VCard is nested without VCardParser's being notified. + */ +public class VCardNestedException extends VCardNotSupportedException { + public VCardNestedException() { + super(); + } + public VCardNestedException(String message) { + super(message); + } +} diff --git a/vcard/java/com/android/vcard/exception/VCardNotSupportedException.java b/vcard/java/com/android/vcard/exception/VCardNotSupportedException.java new file mode 100644 index 000000000..61ff752c9 --- /dev/null +++ b/vcard/java/com/android/vcard/exception/VCardNotSupportedException.java @@ -0,0 +1,33 @@ +/* + * 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.vcard.exception; + +/** + * The exception which tells that the input VCard is probably valid from the view of + * specification but not supported in the current framework for now. + * + * This is a kind of a good news from the view of development. + * It may be good to ask users to send a report with the VCard example + * for the future development. + */ +public class VCardNotSupportedException extends VCardException { + public VCardNotSupportedException() { + super(); + } + public VCardNotSupportedException(String message) { + super(message); + } +} \ No newline at end of file diff --git a/vcard/java/com/android/vcard/exception/VCardVersionException.java b/vcard/java/com/android/vcard/exception/VCardVersionException.java new file mode 100644 index 000000000..047c58053 --- /dev/null +++ b/vcard/java/com/android/vcard/exception/VCardVersionException.java @@ -0,0 +1,28 @@ +/* + * 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.vcard.exception; + +/** + * VCardException used only when the version of the vCard is different. + */ +public class VCardVersionException extends VCardException { + public VCardVersionException() { + super(); + } + public VCardVersionException(String message) { + super(message); + } +} diff --git a/vcard/tests/Android.mk b/vcard/tests/Android.mk new file mode 100644 index 000000000..853ee1451 --- /dev/null +++ b/vcard/tests/Android.mk @@ -0,0 +1,25 @@ +# Copyright (C) 2010 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. + +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_CERTIFICATE := platform +LOCAL_MODULE_TAGS := tests +LOCAL_PACKAGE_NAME := AndroidVCardTests +LOCAL_SRC_FILES := $(call all-java-files-under, src) +LOCAL_JAVA_LIBRARIES := android.test.runner google-common +LOCAL_STATIC_JAVA_LIBRARIES := com.android.vcard + +include $(BUILD_PACKAGE) diff --git a/vcard/tests/AndroidManifest.xml b/vcard/tests/AndroidManifest.xml new file mode 100644 index 000000000..fcbf76721 --- /dev/null +++ b/vcard/tests/AndroidManifest.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + diff --git a/vcard/tests/res/raw/v21_backslash.vcf b/vcard/tests/res/raw/v21_backslash.vcf new file mode 100644 index 000000000..bd3002b32 --- /dev/null +++ b/vcard/tests/res/raw/v21_backslash.vcf @@ -0,0 +1,5 @@ +BEGIN:VCARD +VERSION:2.1 +N:;A\;B\\;C\\\;;D;\:E;\\\\; +FN:A;B\C\;D:E\\ +END:VCARD diff --git a/vcard/tests/res/raw/v21_complicated.vcf b/vcard/tests/res/raw/v21_complicated.vcf new file mode 100644 index 000000000..de34e1668 --- /dev/null +++ b/vcard/tests/res/raw/v21_complicated.vcf @@ -0,0 +1,106 @@ +BEGIN:VCARD +VERSION:2.1 +N:Gump;Forrest;Hoge;Pos;Tao +FN:Joe Due +ORG:Gump Shrimp Co.;Sales Dept.\;Manager;Fish keeper +ROLE:Fish Cake Keeper! +X-CLASS:PUBLIC +TITLE:Shrimp Man +TEL;WORK;VOICE:(111) 555-1212 +TEL;HOME;VOICE:(404) 555-1212 +TEL;CELL:0311111111 +TEL;VIDEO:0322222222 +TEL;VOICE:0333333333 +ADR;WORK:;;100 Waters Edge;Baytown;LA;30314;United States of America +LABEL;WORK;ENCODING=QUOTED-PRINTABLE:100 Waters Edge=0D=0ABaytown, LA 30314=0D=0AUnited States of America +ADR;HOME:;;42 Plantation St.;Baytown;LA;30314;United States of America +LABEL;HOME;ENCODING=QUOTED-PRINTABLE:42 Plantation St.=0D=0A= +Baytown, LA 30314=0D=0A= +United States of America +EMAIL;PREF;INTERNET:forrestgump@walladalla.com +EMAIL;CELL:cell@example.com +NOTE:The following note is the example from RFC 2045. +NOTE;ENCODING=QUOTED-PRINTABLE:Now's the time = +for all folk to come= + to the aid of their country. + +PHOTO;ENCODING=BASE64;TYPE=JPEG: + /9j/4QoPRXhpZgAATU0AKgAAAAgADQEOAAIAAAAPAAAAqgEPAAIAAAAHAAAAugEQAAIAAAAG + AAAAwgESAAMAAAABAAEAAAEaAAUAAAABAAAAyAEbAAUAAAABAAAA0AEoAAMAAAABAAIAAAEx + AAIAAAAOAAAA2AEyAAIAAAAUAAAA5gITAAMAAAABAAEAAIKYAAIAAAAOAAAA+odpAAQAAAAB + AAABhMSlAAcAAAB8AAABCAAABB4yMDA4MTAyOTEzNTUzMQAARG9Db01vAABEOTA1aQAAAABI + AAAAAQAAAEgAAAABRDkwNWkgVmVyMS4wMAAyMDA4OjEwOjI5IDEzOjU1OjQ3ACAgICAgICAg + ICAgICAAUHJpbnRJTQAwMzAwAAAABgABABQAFAACAQAAAAADAAAANAEABQAAAAEBAQAAAAEQ + gAAAAAAAEQkAACcQAAAPCwAAJxAAAAWXAAAnEAAACLAAACcQAAAcAQAAJxAAAAJeAAAnEAAA + AIsAACcQAAADywAAJxAAABvlAAAnEAAogpoABQAAAAEAAANqgp0ABQAAAAEAAANyiCIAAwAA + AAEAAgAAkAAABwAAAAQwMjIwkAMAAgAAABQAAAN6kAQAAgAAABQAAAOOkQEABwAAAAQBAgMA + kQIABQAAAAEAAAOikgEACgAAAAEAAAOqkgIABQAAAAEAAAOykgQACgAAAAEAAAO6kgUABQAA + AAEAAAPCkgcAAwAAAAEAAgAAkggAAwAAAAEAAAAAkgkAAwAAAAEAAAAAkgoABQAAAAEAAAPK + knwABwAAAAEAAAAAkoYABwAAABYAAAPSoAAABwAAAAQwMTAwoAEAAwAAAAEAAQAAoAIAAwAA + AAEAYAAAoAMAAwAAAAEASAAAoAUABAAAAAEAAAQAog4ABQAAAAEAAAPoog8ABQAAAAEAAAPw + ohAAAwAAAAEAAgAAohcAAwAAAAEAAgAAowAABwAAAAEDAAAAowEABwAAAAEBAAAApAEAAwAA + AAEAAAAApAIAAwAAAAEAAAAApAMAAwAAAAEAAAAApAQABQAAAAEAAAP4pAUAAwAAAAEAHQAA + pAYAAwAAAAEAAAAApAcAAwAAAAEAAAAApAgAAwAAAAEAAAAApAkAAwAAAAEAAAAApAoAAwAA + AAEAAAAApAwAAwAAAAEAAgAAAAAAAAAAAFMAACcQAAABXgAAAGQyMDA4OjEwOjI5IDEzOjU1 + OjMxADIwMDg6MTA6MjkgMTM6NTU6NDcAAAApiAAAGwAAAAKyAAAAZAAAAV4AAABkAAAAAAAA + AGQAAAAlAAAACgAADpIAAAPoAAAAAAAAAAAyMDA4MTAyOTEzNTUzMQAAICoAAAAKAAAq4gAA + AAoAAAAAAAAAAQACAAEAAgAAAARSOTgAAAIABwAAAAQwMTAwAAAAAAAGAQMAAwAAAAEABgAA + ARoABQAAAAEAAARsARsABQAAAAEAAAR0ASgAAwAAAAEAAgAAAgEABAAAAAEAAAR8AgIABAAA + AAEAAAWLAAAAAAAAAEgAAAABAAAASAAAAAH/2P/bAIQAIBYYHBgUIBwaHCQiICYwUDQwLCww + YkZKOlB0Znp4cmZwboCQuJyAiK6KbnCg2qKuvsTO0M58muLy4MjwuMrOxgEiJCQwKjBeNDRe + xoRwhMbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbG + /8AAEQgAeACgAwEhAAIRAQMRAf/EAaIAAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKCxAA + AgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkK + FhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWG + h4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl + 5ufo6erx8vP09fb3+Pn6AQADAQEBAQEBAQEBAAAAAAAAAQIDBAUGBwgJCgsRAAIBAgQEAwQH + BQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEXGBka + JicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWGh4iJipKT + lJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uLj5OXm5+jp6vLz + 9PX29/j5+v/aAAwDAQACEQMRAD8AFFSqKkZIoqRVpgSKKeBTEOApwFADsUYpgIRSEUANIppF + ICNhUTCgCMio2FICJhULCgC0oqVaAJFFSqKBkgFOApiHCnCgB2KMUCENJQA0imEUDGMKiYUA + RtUbUgIWqJhQBZSpFoAlWpVoGPFPFMQ7tSK2ODQA4yKO9HmKe9FxAzDHFIOlAAaYaAGNUTUD + ImqNqQETVE1AE6VKKAJFNSqaAHg08GmANIFFQM5Y5qJMBuT60ZNQIcrkVYSQMKuLGKaaasQx + qiagZE1RtSAjaomoAkQ1KpoAlU1IpoAkU07OBTArO+5qkV12Y71lfUBmaKkCRSuznrTFba2a + oCwGyM0E1qIjY1GxoGRNUZNICNqiagByGplNAEimpFNMB4YDvSucpxSYEIU04KazsAu1qArU + WELtPpTSposBNETt5pxNaoCNjUbGgCNjUZoGRtUTUgFU1KpoAkBqQHigCFnO7rUqOdlZp6gA + c+tODn1pXAXzD60eYfWncQvmNSGQ07gOMhCVEJGz1ptgS5yKYxqwGE1GxoAiamGkMapqVTQB + Kpp+eKAICfmqWM/Kaz6gANOBqQFzRmmAuaTNACsfkqMHmm9wJs8U0mtRDGNRsaAI2phpDI1N + SqaAJFNSA8UCISfmqSM/Kaz6jAHmnA1ICg0uaAFzSZpgKx+SmDrTe4E2eKaTWoiMmmMaAIzT + DSGRKakU0ASKaeDTERseakjPyms+oxAacDUgOBpc0gFzSZpgOY/KKYv3qrqIlpprQBjGoyaA + GGmmkMgU1IppgPBqQGgQu0Gn4wvFKwEQpwNZDHZpc0ALmigRKBleaQKBWtgA001QDGqM0gGm + mGkMrqakBoAepp4NMRIDTwaAE2A008GokgHxjd1pzKFpW0uAg5NSBBTirgOpDWgDTTTQAw0w + 0gGGmmgZWBp4pASKaeDTEOBp4NADwajbrUyBEkXWnSUdAGr1qeiAMSkNWAhphoAaaYaQDDTT + SGVRTwaYDxTwaBDwaeDQA4GlK5oauIeo20pGaLaAKqgU6hKwBSGmAhphoAaaYaQxhpppDKgN + PFMB4p4oEPFOBpgPBp4NAhwpwoAWloAKSgBDTTQMYaYaQDTTTSGA/9n/2wCEAAoHBwgHBgoI + CAgLCgoLDhgQDg0NDh0VFhEYIx8lJCIfIiEmKzcvJik0KSEiMEExNDk7Pj4+JS5ESUM8SDc9 + PjsBCgsLDg0OHBAQHDsoIig7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7 + Ozs7Ozs7Ozs7Ozs7O//AABEIAEgAYAMBIQACEQEDEQH/xAGiAAABBQEBAQEBAQAAAAAAAAAA + AQIDBAUGBwgJCgsQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNC + scEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hp + anN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS + 09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+gEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcI + CQoLEQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVi + ctEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4 + eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY + 2dri4+Tl5ufo6ery8/T19vf4+fr/2gAMAwEAAhEDEQA/AJ7SLgcVr20I4rNFGvbQAAHFaEUX + SrQi5HCMdKk8oY6VSJYx4hjpVaWMelAFC4iGDxWPdR8mkxmRdxjBrEvI+tZjN20xtHNbNqAc + UIDXg6Cr0WKtCY8XKQOyzOB3FKNWsyceZ+lS6sY6NkjvPSdwImBHUmmy4q076oCjOODWPdgc + 0MpGPdAYNYl4o5rNjNKzkyorZtXxihAa1vIDip7m9Frb7/4jwKcnyxbEzN3ieJppZsyZ4U1H + urzZau4mWVlNrGk0UuWPVa1YroXEIkHfrXZh5W90RWncAHmsi6bJNdQ0ZNw3BrGuiMGs2Mks + puBzWzbzdOaEBeOpR2oUtkk9hTru7iuo4m8wgemKyqTi04sBsfkEf68j8KlUQZz9o/SuZRj3 + JYriAji4/Sp7W6htbV2aXcu70ramoxle4gN7HcIXjbis+4k5NdaaauhmVcv1rHuW61DGiG1m + 6c1s20/TmgAv5vmj57VKk3+ixnPc1xVV70h9CVJuOtSrL71hFgxzScUkkn+iY/2q1i9xDrGT + 9y31pJ5Otd1L+GhMy7mTrWXO2SapjRn28vTmta3nxjmgGOvJd2w1Kkv+ipz/ABGuOoveYdCe + ObjrU6y5rlsA8ycUksn+ij/eNaw6iJLNsW59zTJn6816FP4EJmbO+Saz5m602UjIgk4HNadv + LwKaBl+MpIMOMipp490SCJeF7CoqQvF2JuRqWQ4YEGrSiQJuKnHrXByMpki73GFBNXIoh9n2 + SrnnOK6MPTbd3sSwIVF2qMCqkzHmuy1lYRnTHrVGWpZaMKB+BWlbycYoQM0IZDxzV+GU8c1a + IYy5Y+dnHatAsfsAHfArmS1mPoh1gT8x9qtk1rQX7tCe5DIapzGtGBQm71SlqGWjnIH6Vowt + zmhAy/E3vV6F6tEMuxlWIyAfrVxCCAO1VZEEyYA4AApxNGwyJ+lVJRUsaKMw61SlFQzRAP/Z + +X-ATTRIBUTE:Some String +BDAY:19800101 +GEO:35.6563854,139.6994233 +URL:http://www.example.com/ +REV:20080424T195243Z +END:VCARD \ No newline at end of file diff --git a/vcard/tests/res/raw/v21_invalid_comment_line.vcf b/vcard/tests/res/raw/v21_invalid_comment_line.vcf new file mode 100644 index 000000000..f910710af --- /dev/null +++ b/vcard/tests/res/raw/v21_invalid_comment_line.vcf @@ -0,0 +1,10 @@ +BEGIN:vCard +VERSION:2.1 +UID:357 +N:;Conference Call +FN:Conference Call +# This line must be ignored. +NOTE;ENCODING=QUOTED-PRINTABLE:This is an (sharp ->= +#<- sharp) example. This message must NOT be ignored. +# This line must be ignored too. +END:vCard diff --git a/vcard/tests/res/raw/v21_japanese_1.vcf b/vcard/tests/res/raw/v21_japanese_1.vcf new file mode 100644 index 000000000..d05e2fffb --- /dev/null +++ b/vcard/tests/res/raw/v21_japanese_1.vcf @@ -0,0 +1,6 @@ +BEGIN:VCARD +VERSION:2.1 +N;CHARSET=SHIFT_JIS:ˆÀ“¡ƒƒCƒh;;;; +SOUND;X-IRMC-N;CHARSET=SHIFT_JIS:±ÝÄÞ³Û²ÄÞ;;;; +TEL;PREF;VOICE:0300000000 +END:VCARD diff --git a/vcard/tests/res/raw/v21_japanese_2.vcf b/vcard/tests/res/raw/v21_japanese_2.vcf new file mode 100644 index 000000000..fa54acbc8 --- /dev/null +++ b/vcard/tests/res/raw/v21_japanese_2.vcf @@ -0,0 +1,10 @@ +BEGIN:VCARD +VERSION:2.1 +FN;CHARSET=SHIFT_JIS:ˆÀ“¡ ƒƒCƒh 1 +N;CHARSET=SHIFT_JIS:ˆÀ“¡;ƒƒCƒh1;;; +SOUND;X-IRMC-N;CHARSET=SHIFT_JIS:±ÝÄÞ³;Û²ÄÞ1;;; +ADR;HOME;CHARSET=SHIFT_JIS;ENCODING=QUOTED-PRINTABLE:;=93=8C=8B=9E=93=73= +=8F=61=92=4A=8B=E6=8D=F7=8B=75=92=AC26-1=83=5A=83=8B=83=8A=83=41=83=93= +=83=5E=83=8F=81=5B6=8A=4B;;;;150-8512; +NOTE;CHARSET=SHIFT_JIS;ENCODING=QUOTED-PRINTABLE:=83=81=83=82 +END:VCARD diff --git a/vcard/tests/res/raw/v21_multiple_entry.vcf b/vcard/tests/res/raw/v21_multiple_entry.vcf new file mode 100644 index 000000000..ebbb19a4b --- /dev/null +++ b/vcard/tests/res/raw/v21_multiple_entry.vcf @@ -0,0 +1,33 @@ +BEGIN:VCARD +VERSION:2.1 +N;CHARSET=SHIFT_JIS:ˆÀ“¡ƒƒCƒh3;;;; +SOUND;X-IRMC-N;CHARSET=SHIFT_JIS:±ÝÄÞ³Û²ÄÞ3;;;; +TEL;X-NEC-SECRET:9 +TEL;X-NEC-HOTEL:10 +TEL;X-NEC-SCHOOL:11 +TEL;HOME;FAX:12 +END:VCARD + + +BEGIN:VCARD +VERSION:2.1 +N;CHARSET=SHIFT_JIS:ˆÀ“¡ƒƒCƒh4;;;; +SOUND;X-IRMC-N;CHARSET=SHIFT_JIS:±ÝÄÞ³Û²ÄÞ4;;;; +TEL;MODEM:13 +TEL;PAGER:14 +TEL;X-NEC-FAMILY:15 +TEL;X-NEC-GIRL:16 +END:VCARD + + +BEGIN:VCARD +VERSION:2.1 +N;CHARSET=SHIFT_JIS:ˆÀ“¡ƒƒCƒh5;;;; +SOUND;X-IRMC-N;CHARSET=SHIFT_JIS:±ÝÄÞ³Û²ÄÞ5;;;; +TEL;X-NEC-BOY:17 +TEL;X-NEC-FRIEND:18 +TEL;X-NEC-PHS:19 +TEL;X-NEC-RESTAURANT:20 +END:VCARD + + diff --git a/vcard/tests/res/raw/v21_org_before_title.vcf b/vcard/tests/res/raw/v21_org_before_title.vcf new file mode 100644 index 000000000..8ff1190f1 --- /dev/null +++ b/vcard/tests/res/raw/v21_org_before_title.vcf @@ -0,0 +1,6 @@ +BEGIN:VCARD +VERSION:2.1 +FN:Normal Guy +ORG:Company;Organization;Devision;Room;Sheet No. +TITLE:Excellent Janitor +END:VCARD diff --git a/vcard/tests/res/raw/v21_pref_handling.vcf b/vcard/tests/res/raw/v21_pref_handling.vcf new file mode 100644 index 000000000..51053101a --- /dev/null +++ b/vcard/tests/res/raw/v21_pref_handling.vcf @@ -0,0 +1,15 @@ +BEGIN:VCARD +VERSION:2.1 +FN:Smith +TEL;HOME:1 +TEL;WORK;PREF:2 +TEL;ISDN:3 +EMAIL;PREF;HOME:test@example.com +EMAIL;CELL;PREF:test2@examination.com +ORG:Company +TITLE:Engineer +ORG:Mystery +TITLE:Blogger +ORG:Poetry +TITLE:Poet +END:VCARD diff --git a/vcard/tests/res/raw/v21_simple_1.vcf b/vcard/tests/res/raw/v21_simple_1.vcf new file mode 100644 index 000000000..6aabb4c02 --- /dev/null +++ b/vcard/tests/res/raw/v21_simple_1.vcf @@ -0,0 +1,3 @@ +BEGIN:VCARD +N:Ando;Roid; +END:VCARD diff --git a/vcard/tests/res/raw/v21_simple_2.vcf b/vcard/tests/res/raw/v21_simple_2.vcf new file mode 100644 index 000000000..f0d5ab506 --- /dev/null +++ b/vcard/tests/res/raw/v21_simple_2.vcf @@ -0,0 +1,3 @@ +BEGIN:VCARD +FN:Ando Roid +END:VCARD diff --git a/vcard/tests/res/raw/v21_simple_3.vcf b/vcard/tests/res/raw/v21_simple_3.vcf new file mode 100644 index 000000000..beddabb96 --- /dev/null +++ b/vcard/tests/res/raw/v21_simple_3.vcf @@ -0,0 +1,4 @@ +BEGIN:VCARD +N:Ando;Roid; +FN:Ando Roid +END:VCARD diff --git a/vcard/tests/res/raw/v21_title_before_org.vcf b/vcard/tests/res/raw/v21_title_before_org.vcf new file mode 100644 index 000000000..9fdc7389c --- /dev/null +++ b/vcard/tests/res/raw/v21_title_before_org.vcf @@ -0,0 +1,6 @@ +BEGIN:VCARD +VERSION:2.1 +FN:Nice Guy +TITLE:Cool Title +ORG:Marverous;Perfect;Great;Good;Bad;Poor +END:VCARD diff --git a/vcard/tests/res/raw/v21_winmo_65.vcf b/vcard/tests/res/raw/v21_winmo_65.vcf new file mode 100644 index 000000000..f380d0d5c --- /dev/null +++ b/vcard/tests/res/raw/v21_winmo_65.vcf @@ -0,0 +1,10 @@ +BEGIN:VCARD +VERSION:2.1 +N:Example;;;; +FN:Example +ANNIVERSARY;VALUE=DATE:20091010 +AGENT:Invalid line which must be handled correctly. +X-CLASS:PUBLIC +X-REDUCTION: +X-NO: +END:VCARD diff --git a/vcard/tests/res/raw/v30_comma_separated.vcf b/vcard/tests/res/raw/v30_comma_separated.vcf new file mode 100644 index 000000000..98a7f2058 --- /dev/null +++ b/vcard/tests/res/raw/v30_comma_separated.vcf @@ -0,0 +1,5 @@ +BEGIN:VCARD +VERSION:3.0 +N:F;G;M;; +TEL;TYPE=PAGER,WORK,MSG:6101231234@pagersample.com +END:VCARD diff --git a/vcard/tests/res/raw/v30_simple.vcf b/vcard/tests/res/raw/v30_simple.vcf new file mode 100644 index 000000000..418661f74 --- /dev/null +++ b/vcard/tests/res/raw/v30_simple.vcf @@ -0,0 +1,13 @@ +BEGIN:VCARD +VERSION:3.0 +FN:And Roid +N:And;Roid;;; +ORG:Open;Handset; Alliance +SORT-STRING:android +TEL;TYPE=PREF;TYPE=VOICE:0300000000 +CLASS:PUBLIC +X-GNO:0 +X-GN:group0 +X-REDUCTION:0 +REV:20081031T065854Z +END:VCARD diff --git a/vcard/tests/src/com/android/vcard/tests/VCardExporterTests.java b/vcard/tests/src/com/android/vcard/tests/VCardExporterTests.java new file mode 100644 index 000000000..b6419c36f --- /dev/null +++ b/vcard/tests/src/com/android/vcard/tests/VCardExporterTests.java @@ -0,0 +1,971 @@ +/* + * 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.vcard.tests; + +import android.content.ContentValues; +import android.provider.ContactsContract.CommonDataKinds.Email; +import android.provider.ContactsContract.CommonDataKinds.Event; +import android.provider.ContactsContract.CommonDataKinds.Im; +import android.provider.ContactsContract.CommonDataKinds.Nickname; +import android.provider.ContactsContract.CommonDataKinds.Note; +import android.provider.ContactsContract.CommonDataKinds.Organization; +import android.provider.ContactsContract.CommonDataKinds.Phone; +import android.provider.ContactsContract.CommonDataKinds.Photo; +import android.provider.ContactsContract.CommonDataKinds.Relation; +import android.provider.ContactsContract.CommonDataKinds.StructuredName; +import android.provider.ContactsContract.CommonDataKinds.StructuredPostal; +import android.provider.ContactsContract.CommonDataKinds.Website; + +import com.android.vcard.VCardConfig; +import com.android.vcard.tests.test_utils.ContactEntry; +import com.android.vcard.tests.test_utils.PropertyNodesVerifierElem; +import com.android.vcard.tests.test_utils.PropertyNodesVerifierElem.TypeSet; + +import java.util.Arrays; + +/** + * Tests for the code related to vCard exporter, inculding vCard composer. + * This test class depends on vCard importer code, so if tests for vCard importer fail, + * the result of this class will not be reliable. + */ +public class VCardExporterTests extends VCardTestsBase { + private static final byte[] sPhotoByteArray = + VCardImporterTests.sPhotoByteArrayForComplicatedCase; + + public void testSimpleV21() { + mVerifier.initForExportTest(V21); + mVerifier.addInputEntry().addContentValues(StructuredName.CONTENT_ITEM_TYPE) + .put(StructuredName.FAMILY_NAME, "Ando") + .put(StructuredName.GIVEN_NAME, "Roid"); + mVerifier.addPropertyNodesVerifierElem() + .addExpectedNode("FN", "Roid Ando") + .addExpectedNode("N", "Ando;Roid;;;", + Arrays.asList("Ando", "Roid", "", "", "")); + } + + private void testStructuredNameBasic(int vcardType) { + final boolean isV30 = VCardConfig.isV30(vcardType); + mVerifier.initForExportTest(vcardType); + mVerifier.addInputEntry().addContentValues(StructuredName.CONTENT_ITEM_TYPE) + .put(StructuredName.FAMILY_NAME, "AppropriateFamilyName") + .put(StructuredName.GIVEN_NAME, "AppropriateGivenName") + .put(StructuredName.MIDDLE_NAME, "AppropriateMiddleName") + .put(StructuredName.PREFIX, "AppropriatePrefix") + .put(StructuredName.SUFFIX, "AppropriateSuffix") + .put(StructuredName.PHONETIC_FAMILY_NAME, "AppropriatePhoneticFamily") + .put(StructuredName.PHONETIC_GIVEN_NAME, "AppropriatePhoneticGiven") + .put(StructuredName.PHONETIC_MIDDLE_NAME, "AppropriatePhoneticMiddle"); + + PropertyNodesVerifierElem elem = mVerifier.addPropertyNodesVerifierElem() + .addExpectedNodeWithOrder("N", + "AppropriateFamilyName;AppropriateGivenName;AppropriateMiddleName;" + + "AppropriatePrefix;AppropriateSuffix", + Arrays.asList("AppropriateFamilyName", "AppropriateGivenName", + "AppropriateMiddleName", "AppropriatePrefix", "AppropriateSuffix")) + .addExpectedNodeWithOrder("FN", + "AppropriatePrefix AppropriateGivenName " + + "AppropriateMiddleName AppropriateFamilyName AppropriateSuffix") + .addExpectedNode("X-PHONETIC-FIRST-NAME", "AppropriatePhoneticGiven") + .addExpectedNode("X-PHONETIC-MIDDLE-NAME", "AppropriatePhoneticMiddle") + .addExpectedNode("X-PHONETIC-LAST-NAME", "AppropriatePhoneticFamily"); + + if (isV30) { + elem.addExpectedNode("SORT-STRING", + "AppropriatePhoneticGiven AppropriatePhoneticMiddle " + + "AppropriatePhoneticFamily"); + } + } + + public void testStructuredNameBasicV21() { + testStructuredNameBasic(V21); + } + + public void testStructuredNameBasicV30() { + testStructuredNameBasic(V30); + } + + /** + * Test that only "primary" StructuredName is emitted, so that our vCard file + * will not confuse the external importer, assuming there may be some importer + * which presume that there's only one property toward each of "N", "FN", etc. + * Note that more than one "N", "FN", etc. properties are acceptable in vCard spec. + */ + private void testStructuredNameUsePrimaryCommon(int vcardType) { + final boolean isV30 = (vcardType == V30); + mVerifier.initForExportTest(vcardType); + ContactEntry entry = mVerifier.addInputEntry(); + entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE) + .put(StructuredName.FAMILY_NAME, "DoNotEmitFamilyName1") + .put(StructuredName.GIVEN_NAME, "DoNotEmitGivenName1") + .put(StructuredName.MIDDLE_NAME, "DoNotEmitMiddleName1") + .put(StructuredName.PREFIX, "DoNotEmitPrefix1") + .put(StructuredName.SUFFIX, "DoNotEmitSuffix1") + .put(StructuredName.PHONETIC_FAMILY_NAME, "DoNotEmitPhoneticFamily1") + .put(StructuredName.PHONETIC_GIVEN_NAME, "DoNotEmitPhoneticGiven1") + .put(StructuredName.PHONETIC_MIDDLE_NAME, "DoNotEmitPhoneticMiddle1"); + + // With "IS_PRIMARY=1". This is what we should use. + entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE) + .put(StructuredName.FAMILY_NAME, "AppropriateFamilyName") + .put(StructuredName.GIVEN_NAME, "AppropriateGivenName") + .put(StructuredName.MIDDLE_NAME, "AppropriateMiddleName") + .put(StructuredName.PREFIX, "AppropriatePrefix") + .put(StructuredName.SUFFIX, "AppropriateSuffix") + .put(StructuredName.PHONETIC_FAMILY_NAME, "AppropriatePhoneticFamily") + .put(StructuredName.PHONETIC_GIVEN_NAME, "AppropriatePhoneticGiven") + .put(StructuredName.PHONETIC_MIDDLE_NAME, "AppropriatePhoneticMiddle") + .put(StructuredName.IS_PRIMARY, 1); + + // With "IS_PRIMARY=1", but we should ignore this time, since this is second, not first. + entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE) + .put(StructuredName.FAMILY_NAME, "DoNotEmitFamilyName2") + .put(StructuredName.GIVEN_NAME, "DoNotEmitGivenName2") + .put(StructuredName.MIDDLE_NAME, "DoNotEmitMiddleName2") + .put(StructuredName.PREFIX, "DoNotEmitPrefix2") + .put(StructuredName.SUFFIX, "DoNotEmitSuffix2") + .put(StructuredName.PHONETIC_FAMILY_NAME, "DoNotEmitPhoneticFamily2") + .put(StructuredName.PHONETIC_GIVEN_NAME, "DoNotEmitPhoneticGiven2") + .put(StructuredName.PHONETIC_MIDDLE_NAME, "DoNotEmitPhoneticMiddle2") + .put(StructuredName.IS_PRIMARY, 1); + + PropertyNodesVerifierElem elem = mVerifier.addPropertyNodesVerifierElem() + .addExpectedNodeWithOrder("N", + "AppropriateFamilyName;AppropriateGivenName;AppropriateMiddleName;" + + "AppropriatePrefix;AppropriateSuffix", + Arrays.asList("AppropriateFamilyName", "AppropriateGivenName", + "AppropriateMiddleName", "AppropriatePrefix", "AppropriateSuffix")) + .addExpectedNodeWithOrder("FN", + "AppropriatePrefix AppropriateGivenName " + + "AppropriateMiddleName AppropriateFamilyName AppropriateSuffix") + .addExpectedNode("X-PHONETIC-FIRST-NAME", "AppropriatePhoneticGiven") + .addExpectedNode("X-PHONETIC-MIDDLE-NAME", "AppropriatePhoneticMiddle") + .addExpectedNode("X-PHONETIC-LAST-NAME", "AppropriatePhoneticFamily"); + + if (isV30) { + elem.addExpectedNode("SORT-STRING", + "AppropriatePhoneticGiven AppropriatePhoneticMiddle " + + "AppropriatePhoneticFamily"); + } + } + + public void testStructuredNameUsePrimaryV21() { + testStructuredNameUsePrimaryCommon(V21); + } + + public void testStructuredNameUsePrimaryV30() { + testStructuredNameUsePrimaryCommon(V30); + } + + /** + * Tests that only "super primary" StructuredName is emitted. + * See also the comment in {@link #testStructuredNameUsePrimaryCommon(int)}. + */ + private void testStructuredNameUseSuperPrimaryCommon(int vcardType) { + final boolean isV30 = (vcardType == V30); + mVerifier.initForExportTest(vcardType); + ContactEntry entry = mVerifier.addInputEntry(); + entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE) + .put(StructuredName.FAMILY_NAME, "DoNotEmitFamilyName1") + .put(StructuredName.GIVEN_NAME, "DoNotEmitGivenName1") + .put(StructuredName.MIDDLE_NAME, "DoNotEmitMiddleName1") + .put(StructuredName.PREFIX, "DoNotEmitPrefix1") + .put(StructuredName.SUFFIX, "DoNotEmitSuffix1") + .put(StructuredName.PHONETIC_FAMILY_NAME, "DoNotEmitPhoneticFamily1") + .put(StructuredName.PHONETIC_GIVEN_NAME, "DoNotEmitPhoneticGiven1") + .put(StructuredName.PHONETIC_MIDDLE_NAME, "DoNotEmitPhoneticMiddle1"); + + // With "IS_PRIMARY=1", but we should ignore this time. + entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE) + .put(StructuredName.FAMILY_NAME, "DoNotEmitFamilyName2") + .put(StructuredName.GIVEN_NAME, "DoNotEmitGivenName2") + .put(StructuredName.MIDDLE_NAME, "DoNotEmitMiddleName2") + .put(StructuredName.PREFIX, "DoNotEmitPrefix2") + .put(StructuredName.SUFFIX, "DoNotEmitSuffix2") + .put(StructuredName.PHONETIC_FAMILY_NAME, "DoNotEmitPhoneticFamily2") + .put(StructuredName.PHONETIC_GIVEN_NAME, "DoNotEmitPhoneticGiven2") + .put(StructuredName.PHONETIC_MIDDLE_NAME, "DoNotEmitPhoneticMiddle2") + .put(StructuredName.IS_PRIMARY, 1); + + // With "IS_SUPER_PRIMARY=1". This is what we should use. + entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE) + .put(StructuredName.FAMILY_NAME, "AppropriateFamilyName") + .put(StructuredName.GIVEN_NAME, "AppropriateGivenName") + .put(StructuredName.MIDDLE_NAME, "AppropriateMiddleName") + .put(StructuredName.PREFIX, "AppropriatePrefix") + .put(StructuredName.SUFFIX, "AppropriateSuffix") + .put(StructuredName.PHONETIC_FAMILY_NAME, "AppropriatePhoneticFamily") + .put(StructuredName.PHONETIC_GIVEN_NAME, "AppropriatePhoneticGiven") + .put(StructuredName.PHONETIC_MIDDLE_NAME, "AppropriatePhoneticMiddle") + .put(StructuredName.IS_SUPER_PRIMARY, 1); + + entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE) + .put(StructuredName.FAMILY_NAME, "DoNotEmitFamilyName3") + .put(StructuredName.GIVEN_NAME, "DoNotEmitGivenName3") + .put(StructuredName.MIDDLE_NAME, "DoNotEmitMiddleName3") + .put(StructuredName.PREFIX, "DoNotEmitPrefix3") + .put(StructuredName.SUFFIX, "DoNotEmitSuffix3") + .put(StructuredName.PHONETIC_FAMILY_NAME, "DoNotEmitPhoneticFamily3") + .put(StructuredName.PHONETIC_GIVEN_NAME, "DoNotEmitPhoneticGiven3") + .put(StructuredName.PHONETIC_MIDDLE_NAME, "DoNotEmitPhoneticMiddle3") + .put(StructuredName.IS_PRIMARY, 1); + + PropertyNodesVerifierElem elem = mVerifier.addPropertyNodesVerifierElem() + .addExpectedNodeWithOrder("N", + "AppropriateFamilyName;AppropriateGivenName;AppropriateMiddleName;" + + "AppropriatePrefix;AppropriateSuffix", + Arrays.asList("AppropriateFamilyName", "AppropriateGivenName", + "AppropriateMiddleName", "AppropriatePrefix", "AppropriateSuffix")) + .addExpectedNodeWithOrder("FN", + "AppropriatePrefix AppropriateGivenName " + + "AppropriateMiddleName AppropriateFamilyName AppropriateSuffix") + .addExpectedNode("X-PHONETIC-FIRST-NAME", "AppropriatePhoneticGiven") + .addExpectedNode("X-PHONETIC-MIDDLE-NAME", "AppropriatePhoneticMiddle") + .addExpectedNode("X-PHONETIC-LAST-NAME", "AppropriatePhoneticFamily"); + + if (isV30) { + elem.addExpectedNode("SORT-STRING", + "AppropriatePhoneticGiven AppropriatePhoneticMiddle" + + " AppropriatePhoneticFamily"); + } + } + + public void testStructuredNameUseSuperPrimaryV21() { + testStructuredNameUseSuperPrimaryCommon(V21); + } + + public void testStructuredNameUseSuperPrimaryV30() { + testStructuredNameUseSuperPrimaryCommon(V30); + } + + public void testNickNameV30() { + mVerifier.initForExportTest(V30); + mVerifier.addInputEntry().addContentValues(Nickname.CONTENT_ITEM_TYPE) + .put(Nickname.NAME, "Nicky"); + + mVerifier.addPropertyNodesVerifierElemWithEmptyName() + .addExpectedNodeWithOrder("NICKNAME", "Nicky"); + } + + private void testPhoneBasicCommon(int vcardType) { + mVerifier.initForExportTest(vcardType); + mVerifier.addInputEntry().addContentValues(Phone.CONTENT_ITEM_TYPE) + .put(Phone.NUMBER, "1") + .put(Phone.TYPE, Phone.TYPE_HOME); + mVerifier.addPropertyNodesVerifierElemWithEmptyName() + .addExpectedNode("TEL", "1", new TypeSet("HOME")); + } + + public void testPhoneBasicV21() { + testPhoneBasicCommon(V21); + } + + public void testPhoneBasicV30() { + testPhoneBasicCommon(V30); + } + + public void testPhoneRefrainFormatting() { + mVerifier.initForExportTest(V21 | VCardConfig.FLAG_REFRAIN_PHONE_NUMBER_FORMATTING); + mVerifier.addInputEntry().addContentValues(Phone.CONTENT_ITEM_TYPE) + .put(Phone.NUMBER, "1234567890(abcdefghijklmnopqrstuvwxyz)") + .put(Phone.TYPE, Phone.TYPE_HOME); + mVerifier.addPropertyNodesVerifierElemWithEmptyName() + .addExpectedNode("TEL", "1234567890(abcdefghijklmnopqrstuvwxyz)", + new TypeSet("HOME")); + } + + /** + * Tests that vCard composer emits corresponding type param which we expect. + */ + private void testPhoneVariousTypeSupport(int vcardType) { + mVerifier.initForExportTest(vcardType); + ContactEntry entry = mVerifier.addInputEntry(); + entry.addContentValues(Phone.CONTENT_ITEM_TYPE) + .put(Phone.NUMBER, "10") + .put(Phone.TYPE, Phone.TYPE_HOME); + entry.addContentValues(Phone.CONTENT_ITEM_TYPE) + .put(Phone.NUMBER, "20") + .put(Phone.TYPE, Phone.TYPE_WORK); + entry.addContentValues(Phone.CONTENT_ITEM_TYPE) + .put(Phone.NUMBER, "30") + .put(Phone.TYPE, Phone.TYPE_FAX_HOME); + entry.addContentValues(Phone.CONTENT_ITEM_TYPE) + .put(Phone.NUMBER, "40") + .put(Phone.TYPE, Phone.TYPE_FAX_WORK); + entry.addContentValues(Phone.CONTENT_ITEM_TYPE) + .put(Phone.NUMBER, "50") + .put(Phone.TYPE, Phone.TYPE_MOBILE); + entry.addContentValues(Phone.CONTENT_ITEM_TYPE) + .put(Phone.NUMBER, "60") + .put(Phone.TYPE, Phone.TYPE_PAGER); + entry.addContentValues(Phone.CONTENT_ITEM_TYPE) + .put(Phone.NUMBER, "70") + .put(Phone.TYPE, Phone.TYPE_OTHER); + entry.addContentValues(Phone.CONTENT_ITEM_TYPE) + .put(Phone.NUMBER, "80") + .put(Phone.TYPE, Phone.TYPE_CAR); + entry.addContentValues(Phone.CONTENT_ITEM_TYPE) + .put(Phone.NUMBER, "90") + .put(Phone.TYPE, Phone.TYPE_COMPANY_MAIN); + entry.addContentValues(Phone.CONTENT_ITEM_TYPE) + .put(Phone.NUMBER, "100") + .put(Phone.TYPE, Phone.TYPE_ISDN); + entry.addContentValues(Phone.CONTENT_ITEM_TYPE) + .put(Phone.NUMBER, "110") + .put(Phone.TYPE, Phone.TYPE_MAIN); + entry.addContentValues(Phone.CONTENT_ITEM_TYPE) + .put(Phone.NUMBER, "120") + .put(Phone.TYPE, Phone.TYPE_OTHER_FAX); + entry.addContentValues(Phone.CONTENT_ITEM_TYPE) + .put(Phone.NUMBER, "130") + .put(Phone.TYPE, Phone.TYPE_TELEX); + entry.addContentValues(Phone.CONTENT_ITEM_TYPE) + .put(Phone.NUMBER, "140") + .put(Phone.TYPE, Phone.TYPE_WORK_MOBILE); + entry.addContentValues(Phone.CONTENT_ITEM_TYPE) + .put(Phone.NUMBER, "150") + .put(Phone.TYPE, Phone.TYPE_WORK_PAGER); + entry.addContentValues(Phone.CONTENT_ITEM_TYPE) + .put(Phone.NUMBER, "160") + .put(Phone.TYPE, Phone.TYPE_MMS); + + mVerifier.addPropertyNodesVerifierElemWithEmptyName() + .addExpectedNode("TEL", "10", new TypeSet("HOME")) + .addExpectedNode("TEL", "20", new TypeSet("WORK")) + .addExpectedNode("TEL", "30", new TypeSet("HOME", "FAX")) + .addExpectedNode("TEL", "40", new TypeSet("WORK", "FAX")) + .addExpectedNode("TEL", "50", new TypeSet("CELL")) + .addExpectedNode("TEL", "60", new TypeSet("PAGER")) + .addExpectedNode("TEL", "70", new TypeSet("VOICE")) + .addExpectedNode("TEL", "80", new TypeSet("CAR")) + .addExpectedNode("TEL", "90", new TypeSet("WORK", "PREF")) + .addExpectedNode("TEL", "100", new TypeSet("ISDN")) + .addExpectedNode("TEL", "110", new TypeSet("PREF")) + .addExpectedNode("TEL", "120", new TypeSet("FAX")) + .addExpectedNode("TEL", "130", new TypeSet("TLX")) + .addExpectedNode("TEL", "140", new TypeSet("WORK", "CELL")) + .addExpectedNode("TEL", "150", new TypeSet("WORK", "PAGER")) + .addExpectedNode("TEL", "160", new TypeSet("MSG")); + } + + public void testPhoneVariousTypeSupportV21() { + testPhoneVariousTypeSupport(V21); + } + + public void testPhoneVariousTypeSupportV30() { + testPhoneVariousTypeSupport(V30); + } + + /** + * Tests that "PREF"s are emitted appropriately. + */ + private void testPhonePrefHandlingCommon(int vcardType) { + mVerifier.initForExportTest(vcardType); + ContactEntry entry = mVerifier.addInputEntry(); + entry.addContentValues(Phone.CONTENT_ITEM_TYPE) + .put(Phone.NUMBER, "1") + .put(Phone.TYPE, Phone.TYPE_HOME); + entry.addContentValues(Phone.CONTENT_ITEM_TYPE) + .put(Phone.NUMBER, "2") + .put(Phone.TYPE, Phone.TYPE_WORK) + .put(Phone.IS_PRIMARY, 1); + entry.addContentValues(Phone.CONTENT_ITEM_TYPE) + .put(Phone.NUMBER, "3") + .put(Phone.TYPE, Phone.TYPE_FAX_HOME) + .put(Phone.IS_PRIMARY, 1); + entry.addContentValues(Phone.CONTENT_ITEM_TYPE) + .put(Phone.NUMBER, "4") + .put(Phone.TYPE, Phone.TYPE_FAX_WORK); + + mVerifier.addPropertyNodesVerifierElemWithEmptyName() + .addExpectedNode("TEL", "4", new TypeSet("WORK", "FAX")) + .addExpectedNode("TEL", "3", new TypeSet("HOME", "FAX", "PREF")) + .addExpectedNode("TEL", "2", new TypeSet("WORK", "PREF")) + .addExpectedNode("TEL", "1", new TypeSet("HOME")); + } + + public void testPhonePrefHandlingV21() { + testPhonePrefHandlingCommon(V21); + } + + public void testPhonePrefHandlingV30() { + testPhonePrefHandlingCommon(V30); + } + + private void testMiscPhoneTypeHandling(int vcardType) { + mVerifier.initForExportTest(vcardType); + ContactEntry entry = mVerifier.addInputEntry(); + entry.addContentValues(Phone.CONTENT_ITEM_TYPE) + .put(Phone.NUMBER, "1") + .put(Phone.TYPE, Phone.TYPE_CUSTOM) + .put(Phone.LABEL, "Modem"); + entry.addContentValues(Phone.CONTENT_ITEM_TYPE) + .put(Phone.NUMBER, "2") + .put(Phone.TYPE, Phone.TYPE_CUSTOM) + .put(Phone.LABEL, "MSG"); + entry.addContentValues(Phone.CONTENT_ITEM_TYPE) + .put(Phone.NUMBER, "3") + .put(Phone.TYPE, Phone.TYPE_CUSTOM) + .put(Phone.LABEL, "BBS"); + entry.addContentValues(Phone.CONTENT_ITEM_TYPE) + .put(Phone.NUMBER, "4") + .put(Phone.TYPE, Phone.TYPE_CUSTOM) + .put(Phone.LABEL, "VIDEO"); + entry.addContentValues(Phone.CONTENT_ITEM_TYPE) + .put(Phone.NUMBER, "5") + .put(Phone.TYPE, Phone.TYPE_CUSTOM); + entry.addContentValues(Phone.CONTENT_ITEM_TYPE) + .put(Phone.NUMBER, "6") + .put(Phone.TYPE, Phone.TYPE_CUSTOM) + .put(Phone.LABEL, "_AUTO_CELL"); // The old indicator for the type mobile. + entry.addContentValues(Phone.CONTENT_ITEM_TYPE) + .put(Phone.NUMBER, "7") + .put(Phone.TYPE, Phone.TYPE_CUSTOM) + .put(Phone.LABEL, "\u643A\u5E2F"); // Mobile phone in Japanese Kanji + entry.addContentValues(Phone.CONTENT_ITEM_TYPE) + .put(Phone.NUMBER, "8") + .put(Phone.TYPE, Phone.TYPE_CUSTOM) + .put(Phone.LABEL, "invalid"); + PropertyNodesVerifierElem elem = mVerifier.addPropertyNodesVerifierElemWithEmptyName(); + elem.addExpectedNode("TEL", "1", new TypeSet("MODEM")) + .addExpectedNode("TEL", "2", new TypeSet("MSG")) + .addExpectedNode("TEL", "3", new TypeSet("BBS")) + .addExpectedNode("TEL", "4", new TypeSet("VIDEO")) + .addExpectedNode("TEL", "5", new TypeSet("VOICE")) + .addExpectedNode("TEL", "6", new TypeSet("CELL")) + .addExpectedNode("TEL", "7", new TypeSet("CELL")) + .addExpectedNode("TEL", "8", new TypeSet("X-invalid")); + } + + public void testPhoneTypeHandlingV21() { + testMiscPhoneTypeHandling(V21); + } + + public void testPhoneTypeHandlingV30() { + testMiscPhoneTypeHandling(V30); + } + + private void testEmailBasicCommon(int vcardType) { + mVerifier.initForExportTest(vcardType); + mVerifier.addInputEntry().addContentValues(Email.CONTENT_ITEM_TYPE) + .put(Email.DATA, "sample@example.com"); + mVerifier.addPropertyNodesVerifierElemWithEmptyName() + .addExpectedNode("EMAIL", "sample@example.com"); + } + + public void testEmailBasicV21() { + testEmailBasicCommon(V21); + } + + public void testEmailBasicV30() { + testEmailBasicCommon(V30); + } + + private void testEmailVariousTypeSupportCommon(int vcardType) { + mVerifier.initForExportTest(vcardType); + ContactEntry entry = mVerifier.addInputEntry(); + entry.addContentValues(Email.CONTENT_ITEM_TYPE) + .put(Email.DATA, "type_home@example.com") + .put(Email.TYPE, Email.TYPE_HOME); + entry.addContentValues(Email.CONTENT_ITEM_TYPE) + .put(Email.DATA, "type_work@example.com") + .put(Email.TYPE, Email.TYPE_WORK); + entry.addContentValues(Email.CONTENT_ITEM_TYPE) + .put(Email.DATA, "type_mobile@example.com") + .put(Email.TYPE, Email.TYPE_MOBILE); + entry.addContentValues(Email.CONTENT_ITEM_TYPE) + .put(Email.DATA, "type_other@example.com") + .put(Email.TYPE, Email.TYPE_OTHER); + mVerifier.addPropertyNodesVerifierElemWithEmptyName() + .addExpectedNode("EMAIL", "type_home@example.com", new TypeSet("HOME")) + .addExpectedNode("EMAIL", "type_work@example.com", new TypeSet("WORK")) + .addExpectedNode("EMAIL", "type_mobile@example.com", new TypeSet("CELL")) + .addExpectedNode("EMAIL", "type_other@example.com"); + } + + public void testEmailVariousTypeSupportV21() { + testEmailVariousTypeSupportCommon(V21); + } + + public void testEmailVariousTypeSupportV30() { + testEmailVariousTypeSupportCommon(V30); + } + + private void testEmailPrefHandlingCommon(int vcardType) { + mVerifier.initForExportTest(vcardType); + ContactEntry entry = mVerifier.addInputEntry(); + entry.addContentValues(Email.CONTENT_ITEM_TYPE) + .put(Email.DATA, "type_home@example.com") + .put(Email.TYPE, Email.TYPE_HOME) + .put(Email.IS_PRIMARY, 1); + entry.addContentValues(Email.CONTENT_ITEM_TYPE) + .put(Email.DATA, "type_notype@example.com") + .put(Email.IS_PRIMARY, 1); + + mVerifier.addPropertyNodesVerifierElemWithEmptyName() + .addExpectedNode("EMAIL", "type_notype@example.com", new TypeSet("PREF")) + .addExpectedNode("EMAIL", "type_home@example.com", new TypeSet("HOME", "PREF")); + } + + public void testEmailPrefHandlingV21() { + testEmailPrefHandlingCommon(V21); + } + + public void testEmailPrefHandlingV30() { + testEmailPrefHandlingCommon(V30); + } + + private void testPostalAddressCommon(int vcardType) { + mVerifier.initForExportTest(vcardType); + mVerifier.addInputEntry().addContentValues(StructuredPostal.CONTENT_ITEM_TYPE) + .put(StructuredPostal.POBOX, "Pobox") + .put(StructuredPostal.NEIGHBORHOOD, "Neighborhood") + .put(StructuredPostal.STREET, "Street") + .put(StructuredPostal.CITY, "City") + .put(StructuredPostal.REGION, "Region") + .put(StructuredPostal.POSTCODE, "100") + .put(StructuredPostal.COUNTRY, "Country") + .put(StructuredPostal.FORMATTED_ADDRESS, "Formatted Address") + .put(StructuredPostal.TYPE, StructuredPostal.TYPE_WORK); + // adr-value = 0*6(text-value ";") text-value + // ; PO Box, Extended Address, Street, Locality, Region, Postal Code, + // ; Country Name + // + // The NEIGHBORHOOD field is appended after the CITY field. + mVerifier.addPropertyNodesVerifierElemWithEmptyName() + .addExpectedNode("ADR", + Arrays.asList("Pobox", "", "Street", "City Neighborhood", + "Region", "100", "Country"), new TypeSet("WORK")); + } + + public void testPostalAddressV21() { + testPostalAddressCommon(V21); + } + + public void testPostalAddressV30() { + testPostalAddressCommon(V30); + } + + private void testPostalAddressNonNeighborhood(int vcardType) { + mVerifier.initForExportTest(vcardType); + mVerifier.addInputEntry().addContentValues(StructuredPostal.CONTENT_ITEM_TYPE) + .put(StructuredPostal.CITY, "City"); + mVerifier.addPropertyNodesVerifierElemWithEmptyName() + .addExpectedNode("ADR", + Arrays.asList("", "", "", "City", "", "", ""), new TypeSet("HOME")); + } + + public void testPostalAddressNonNeighborhoodV21() { + testPostalAddressNonNeighborhood(V21); + } + + public void testPostalAddressNonNeighborhoodV30() { + testPostalAddressNonNeighborhood(V30); + } + + private void testPostalAddressNonCity(int vcardType) { + mVerifier.initForExportTest(vcardType); + mVerifier.addInputEntry().addContentValues(StructuredPostal.CONTENT_ITEM_TYPE) + .put(StructuredPostal.NEIGHBORHOOD, "Neighborhood"); + mVerifier.addPropertyNodesVerifierElemWithEmptyName() + .addExpectedNode("ADR", + Arrays.asList("", "", "", "Neighborhood", "", "", ""), new TypeSet("HOME")); + } + + public void testPostalAddressNonCityV21() { + testPostalAddressNonCity(V21); + } + + public void testPostalAddressNonCityV30() { + testPostalAddressNonCity(V30); + } + + private void testPostalOnlyWithFormattedAddressCommon(int vcardType) { + mVerifier.initForExportTest(vcardType); + mVerifier.addInputEntry().addContentValues(StructuredPostal.CONTENT_ITEM_TYPE) + .put(StructuredPostal.REGION, "") // Must be ignored. + .put(StructuredPostal.FORMATTED_ADDRESS, + "Formatted address CA 123-334 United Statue"); + mVerifier.addPropertyNodesVerifierElemWithEmptyName() + .addExpectedNodeWithOrder("ADR", ";Formatted address CA 123-334 United Statue;;;;;", + Arrays.asList("", "Formatted address CA 123-334 United Statue", + "", "", "", "", ""), new TypeSet("HOME")); + } + + public void testPostalOnlyWithFormattedAddressV21() { + testPostalOnlyWithFormattedAddressCommon(V21); + } + + public void testPostalOnlyWithFormattedAddressV30() { + testPostalOnlyWithFormattedAddressCommon(V30); + } + + /** + * Tests that the vCard composer honors formatted data when it is available + * even when it is partial. + */ + private void testPostalWithBothStructuredAndFormattedCommon(int vcardType) { + mVerifier.initForExportTest(vcardType); + mVerifier.addInputEntry().addContentValues(StructuredPostal.CONTENT_ITEM_TYPE) + .put(StructuredPostal.POBOX, "Pobox") + .put(StructuredPostal.COUNTRY, "Country") + .put(StructuredPostal.FORMATTED_ADDRESS, + "Formatted address CA 123-334 United Statue"); // Should be ignored + mVerifier.addPropertyNodesVerifierElemWithEmptyName() + .addExpectedNode("ADR", "Pobox;;;;;;Country", + Arrays.asList("Pobox", "", "", "", "", "", "Country"), + new TypeSet("HOME")); + } + + public void testPostalWithBothStructuredAndFormattedV21() { + testPostalWithBothStructuredAndFormattedCommon(V21); + } + + public void testPostalWithBothStructuredAndFormattedV30() { + testPostalWithBothStructuredAndFormattedCommon(V30); + } + + private void testOrganizationCommon(int vcardType) { + mVerifier.initForExportTest(vcardType); + ContactEntry entry = mVerifier.addInputEntry(); + entry.addContentValues(Organization.CONTENT_ITEM_TYPE) + .put(Organization.COMPANY, "CompanyX") + .put(Organization.DEPARTMENT, "DepartmentY") + .put(Organization.TITLE, "TitleZ") + .put(Organization.JOB_DESCRIPTION, "Description Rambda") // Ignored. + .put(Organization.OFFICE_LOCATION, "Mountain View") // Ignored. + .put(Organization.PHONETIC_NAME, "PhoneticName!") // Ignored + .put(Organization.SYMBOL, "(^o^)/~~"); // Ignore him (her). + entry.addContentValues(Organization.CONTENT_ITEM_TYPE) + .putNull(Organization.COMPANY) + .put(Organization.DEPARTMENT, "DepartmentXX") + .putNull(Organization.TITLE); + entry.addContentValues(Organization.CONTENT_ITEM_TYPE) + .put(Organization.COMPANY, "CompanyXYZ") + .putNull(Organization.DEPARTMENT) + .put(Organization.TITLE, "TitleXYZYX"); + // Currently we do not use group but depend on the order. + mVerifier.addPropertyNodesVerifierElemWithEmptyName() + .addExpectedNodeWithOrder("ORG", "CompanyX;DepartmentY", + Arrays.asList("CompanyX", "DepartmentY")) + .addExpectedNodeWithOrder("TITLE", "TitleZ") + .addExpectedNodeWithOrder("ORG", "DepartmentXX") + .addExpectedNodeWithOrder("ORG", "CompanyXYZ") + .addExpectedNodeWithOrder("TITLE", "TitleXYZYX"); + } + + public void testOrganizationV21() { + testOrganizationCommon(V21); + } + + public void testOrganizationV30() { + testOrganizationCommon(V30); + } + + private void testImVariousTypeSupportCommon(int vcardType) { + mVerifier.initForExportTest(vcardType); + ContactEntry entry = mVerifier.addInputEntry(); + entry.addContentValues(Im.CONTENT_ITEM_TYPE) + .put(Im.PROTOCOL, Im.PROTOCOL_AIM) + .put(Im.DATA, "aim"); + entry.addContentValues(Im.CONTENT_ITEM_TYPE) + .put(Im.PROTOCOL, Im.PROTOCOL_MSN) + .put(Im.DATA, "msn"); + entry.addContentValues(Im.CONTENT_ITEM_TYPE) + .put(Im.PROTOCOL, Im.PROTOCOL_YAHOO) + .put(Im.DATA, "yahoo"); + entry.addContentValues(Im.CONTENT_ITEM_TYPE) + .put(Im.PROTOCOL, Im.PROTOCOL_SKYPE) + .put(Im.DATA, "skype"); + entry.addContentValues(Im.CONTENT_ITEM_TYPE) + .put(Im.PROTOCOL, Im.PROTOCOL_QQ) + .put(Im.DATA, "qq"); + entry.addContentValues(Im.CONTENT_ITEM_TYPE) + .put(Im.PROTOCOL, Im.PROTOCOL_GOOGLE_TALK) + .put(Im.DATA, "google talk"); + entry.addContentValues(Im.CONTENT_ITEM_TYPE) + .put(Im.PROTOCOL, Im.PROTOCOL_ICQ) + .put(Im.DATA, "icq"); + entry.addContentValues(Im.CONTENT_ITEM_TYPE) + .put(Im.PROTOCOL, Im.PROTOCOL_JABBER) + .put(Im.DATA, "jabber"); + entry.addContentValues(Im.CONTENT_ITEM_TYPE) + .put(Im.PROTOCOL, Im.PROTOCOL_NETMEETING) + .put(Im.DATA, "netmeeting"); + + // No determined way to express unknown type... + mVerifier.addPropertyNodesVerifierElemWithEmptyName() + .addExpectedNode("X-JABBER", "jabber") + .addExpectedNode("X-ICQ", "icq") + .addExpectedNode("X-GOOGLE-TALK", "google talk") + .addExpectedNode("X-QQ", "qq") + .addExpectedNode("X-SKYPE-USERNAME", "skype") + .addExpectedNode("X-YAHOO", "yahoo") + .addExpectedNode("X-MSN", "msn") + .addExpectedNode("X-NETMEETING", "netmeeting") + .addExpectedNode("X-AIM", "aim"); + } + + public void testImBasiV21() { + testImVariousTypeSupportCommon(V21); + } + + public void testImBasicV30() { + testImVariousTypeSupportCommon(V30); + } + + private void testImPrefHandlingCommon(int vcardType) { + mVerifier.initForExportTest(vcardType); + ContactEntry entry = mVerifier.addInputEntry(); + entry.addContentValues(Im.CONTENT_ITEM_TYPE) + .put(Im.PROTOCOL, Im.PROTOCOL_AIM) + .put(Im.DATA, "aim1"); + entry.addContentValues(Im.CONTENT_ITEM_TYPE) + .put(Im.PROTOCOL, Im.PROTOCOL_AIM) + .put(Im.DATA, "aim2") + .put(Im.TYPE, Im.TYPE_HOME) + .put(Im.IS_PRIMARY, 1); + + mVerifier.addPropertyNodesVerifierElemWithEmptyName() + .addExpectedNode("X-AIM", "aim1") + .addExpectedNode("X-AIM", "aim2", new TypeSet("HOME", "PREF")); + } + + public void testImPrefHandlingV21() { + testImPrefHandlingCommon(V21); + } + + public void testImPrefHandlingV30() { + testImPrefHandlingCommon(V30); + } + + private void testWebsiteCommon(int vcardType) { + mVerifier.initForExportTest(vcardType); + ContactEntry entry = mVerifier.addInputEntry(); + entry.addContentValues(Website.CONTENT_ITEM_TYPE) + .put(Website.URL, "http://website.example.android.com/index.html") + .put(Website.TYPE, Website.TYPE_BLOG); + entry.addContentValues(Website.CONTENT_ITEM_TYPE) + .put(Website.URL, "ftp://ftp.example.android.com/index.html") + .put(Website.TYPE, Website.TYPE_FTP); + + // We drop TYPE information since vCard (especially 3.0) does not allow us to emit it. + mVerifier.addPropertyNodesVerifierElemWithEmptyName() + .addExpectedNode("URL", "ftp://ftp.example.android.com/index.html") + .addExpectedNode("URL", "http://website.example.android.com/index.html"); + } + + public void testWebsiteV21() { + testWebsiteCommon(V21); + } + + public void testWebsiteV30() { + testWebsiteCommon(V30); + } + + private String getAndroidPropValue(final String mimeType, String value, Integer type) { + return getAndroidPropValue(mimeType, value, type, null); + } + + private String getAndroidPropValue(final String mimeType, String value, + Integer type, String label) { + return (mimeType + ";" + value + ";" + + (type != null ? type : "") + ";" + + (label != null ? label : "") + ";;;;;;;;;;;;"); + } + + private void testEventCommon(int vcardType) { + mVerifier.initForExportTest(vcardType); + ContactEntry entry = mVerifier.addInputEntry(); + entry.addContentValues(Event.CONTENT_ITEM_TYPE) + .put(Event.TYPE, Event.TYPE_ANNIVERSARY) + .put(Event.START_DATE, "1982-06-16"); + entry.addContentValues(Event.CONTENT_ITEM_TYPE) + .put(Event.TYPE, Event.TYPE_BIRTHDAY) + .put(Event.START_DATE, "2008-10-22"); + entry.addContentValues(Event.CONTENT_ITEM_TYPE) + .put(Event.TYPE, Event.TYPE_OTHER) + .put(Event.START_DATE, "2018-03-12"); + entry.addContentValues(Event.CONTENT_ITEM_TYPE) + .put(Event.TYPE, Event.TYPE_CUSTOM) + .put(Event.LABEL, "The last day") + .put(Event.START_DATE, "When the Tower of Hanoi with 64 rings is completed."); + entry.addContentValues(Event.CONTENT_ITEM_TYPE) + .put(Event.TYPE, Event.TYPE_BIRTHDAY) + .put(Event.START_DATE, "2009-05-19"); // Should be ignored. + mVerifier.addPropertyNodesVerifierElemWithEmptyName() + .addExpectedNode("BDAY", "2008-10-22") + .addExpectedNode("X-ANDROID-CUSTOM", + getAndroidPropValue( + Event.CONTENT_ITEM_TYPE, "1982-06-16", Event.TYPE_ANNIVERSARY)) + .addExpectedNode("X-ANDROID-CUSTOM", + getAndroidPropValue( + Event.CONTENT_ITEM_TYPE, "2018-03-12", Event.TYPE_OTHER)) + .addExpectedNode("X-ANDROID-CUSTOM", + getAndroidPropValue( + Event.CONTENT_ITEM_TYPE, + "When the Tower of Hanoi with 64 rings is completed.", + Event.TYPE_CUSTOM, "The last day")); + } + + public void testEventV21() { + testEventCommon(V21); + } + + public void testEventV30() { + testEventCommon(V30); + } + + private void testNoteCommon(int vcardType) { + mVerifier.initForExportTest(vcardType); + ContactEntry entry = mVerifier.addInputEntry(); + entry.addContentValues(Note.CONTENT_ITEM_TYPE) + .put(Note.NOTE, "note1"); + entry.addContentValues(Note.CONTENT_ITEM_TYPE) + .put(Note.NOTE, "note2") + .put(Note.IS_PRIMARY, 1); // Just ignored. + mVerifier.addPropertyNodesVerifierElemWithEmptyName() + .addExpectedNodeWithOrder("NOTE", "note1") + .addExpectedNodeWithOrder("NOTE", "note2"); + } + + public void testNoteV21() { + testNoteCommon(V21); + } + + public void testNoteV30() { + testNoteCommon(V30); + } + + private void testPhotoCommon(int vcardType) { + final boolean isV30 = vcardType == V30; + mVerifier.initForExportTest(vcardType); + ContactEntry entry = mVerifier.addInputEntry(); + entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE) + .put(StructuredName.FAMILY_NAME, "PhotoTest"); + entry.addContentValues(Photo.CONTENT_ITEM_TYPE) + .put(Photo.PHOTO, sPhotoByteArray); + + ContentValues contentValuesForPhoto = new ContentValues(); + contentValuesForPhoto.put("ENCODING", (isV30 ? "b" : "BASE64")); + mVerifier.addPropertyNodesVerifierElem() + .addExpectedNode("FN", "PhotoTest") + .addExpectedNode("N", "PhotoTest;;;;", + Arrays.asList("PhotoTest", "", "", "", "")) + .addExpectedNodeWithOrder("PHOTO", null, null, sPhotoByteArray, + contentValuesForPhoto, new TypeSet("JPEG"), null); + } + + public void testPhotoV21() { + testPhotoCommon(V21); + } + + public void testPhotoV30() { + testPhotoCommon(V30); + } + + private void testRelationCommon(int vcardType) { + mVerifier.initForExportTest(vcardType); + mVerifier.addInputEntry().addContentValues(Relation.CONTENT_ITEM_TYPE) + .put(Relation.TYPE, Relation.TYPE_MOTHER) + .put(Relation.NAME, "Ms. Mother"); + mVerifier.addContentValuesVerifierElem().addExpected(Relation.CONTENT_ITEM_TYPE) + .put(Relation.TYPE, Relation.TYPE_MOTHER) + .put(Relation.NAME, "Ms. Mother"); + } + + public void testRelationV21() { + testRelationCommon(V21); + } + + public void testRelationV30() { + testRelationCommon(V30); + } + + public void testV30HandleEscape() { + mVerifier.initForExportTest(V30); + mVerifier.addInputEntry().addContentValues(StructuredName.CONTENT_ITEM_TYPE) + .put(StructuredName.FAMILY_NAME, "\\") + .put(StructuredName.GIVEN_NAME, ";") + .put(StructuredName.MIDDLE_NAME, ",") + .put(StructuredName.PREFIX, "\n") + .put(StructuredName.DISPLAY_NAME, "[<{Unescaped:Asciis}>]"); + // Verifies the vCard String correctly escapes each character which must be escaped. + mVerifier.addLineVerifierElem() + .addExpected("N:\\\\;\\;;\\,;\\n;") + .addExpected("FN:[<{Unescaped:Asciis}>]"); + mVerifier.addPropertyNodesVerifierElem() + .addExpectedNode("FN", "[<{Unescaped:Asciis}>]") + .addExpectedNode("N", Arrays.asList("\\", ";", ",", "\n", "")); + } + + /** + * There's no "NICKNAME" property in vCard 2.1, while there is in vCard 3.0. + * We use Android-specific "X-ANDROID-CUSTOM" property. + * This test verifies the functionality. + */ + public void testNickNameV21() { + mVerifier.initForExportTest(V21); + mVerifier.addInputEntry().addContentValues(Nickname.CONTENT_ITEM_TYPE) + .put(Nickname.NAME, "Nicky"); + mVerifier.addPropertyNodesVerifierElemWithEmptyName() + .addExpectedNode("X-ANDROID-CUSTOM", + Nickname.CONTENT_ITEM_TYPE + ";Nicky;;;;;;;;;;;;;;"); + mVerifier.addContentValuesVerifierElem().addExpected(Nickname.CONTENT_ITEM_TYPE) + .put(Nickname.NAME, "Nicky"); + } + + public void testTolerateBrokenPhoneNumberEntryV21() { + mVerifier.initForExportTest(V21); + ContactEntry entry = mVerifier.addInputEntry(); + entry.addContentValues(Phone.CONTENT_ITEM_TYPE) + .put(Phone.TYPE, Phone.TYPE_HOME) + .put(Phone.NUMBER, "111-222-3333 (Miami)\n444-5555-666 (Tokyo);" + + "777-888-9999 (Chicago);111-222-3333 (Miami)"); + mVerifier.addPropertyNodesVerifierElemWithEmptyName() + .addExpectedNode("TEL", "111-222-3333", new TypeSet("HOME")) + .addExpectedNode("TEL", "444-555-5666", new TypeSet("HOME")) + .addExpectedNode("TEL", "777-888-9999", new TypeSet("HOME")); + } + + private void testPickUpNonEmptyContentValuesCommon(int vcardType) { + mVerifier.initForExportTest(vcardType); + ContactEntry entry = mVerifier.addInputEntry(); + entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE) + .put(StructuredName.IS_PRIMARY, 1); // Empty name. Should be ignored. + entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE) + .put(StructuredName.FAMILY_NAME, "family1"); // Not primary. Should be ignored. + entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE) + .put(StructuredName.IS_PRIMARY, 1) + .put(StructuredName.FAMILY_NAME, "family2"); // This entry is what we want. + entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE) + .put(StructuredName.IS_PRIMARY, 1) + .put(StructuredName.FAMILY_NAME, "family3"); + entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE) + .put(StructuredName.FAMILY_NAME, "family4"); + mVerifier.addPropertyNodesVerifierElem() + .addExpectedNode("N", Arrays.asList("family2", "", "", "", "")) + .addExpectedNode("FN", "family2"); + } + + public void testPickUpNonEmptyContentValuesV21() { + testPickUpNonEmptyContentValuesCommon(V21); + } + + public void testPickUpNonEmptyContentValuesV30() { + testPickUpNonEmptyContentValuesCommon(V30); + } +} diff --git a/vcard/tests/src/com/android/vcard/tests/VCardImporterTests.java b/vcard/tests/src/com/android/vcard/tests/VCardImporterTests.java new file mode 100644 index 000000000..045c0d94e --- /dev/null +++ b/vcard/tests/src/com/android/vcard/tests/VCardImporterTests.java @@ -0,0 +1,1008 @@ +/* + * 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.vcard.tests; + +import android.content.ContentValues; +import android.provider.ContactsContract.Data; +import android.provider.ContactsContract.CommonDataKinds.Email; +import android.provider.ContactsContract.CommonDataKinds.Event; +import android.provider.ContactsContract.CommonDataKinds.Note; +import android.provider.ContactsContract.CommonDataKinds.Organization; +import android.provider.ContactsContract.CommonDataKinds.Phone; +import android.provider.ContactsContract.CommonDataKinds.Photo; +import android.provider.ContactsContract.CommonDataKinds.StructuredName; +import android.provider.ContactsContract.CommonDataKinds.StructuredPostal; +import android.provider.ContactsContract.CommonDataKinds.Website; + +import com.android.vcard.VCardConfig; +import com.android.vcard.tests.test_utils.ContentValuesVerifier; +import com.android.vcard.tests.test_utils.ContentValuesVerifierElem; +import com.android.vcard.tests.test_utils.PropertyNodesVerifierElem.TypeSet; + +import java.util.Arrays; + +public class VCardImporterTests extends VCardTestsBase { + // Push data into int array at first since values like 0x80 are + // interpreted as int by the compiler and casting all of them is + // cumbersome... + private static final int[] sPhotoIntArrayForComplicatedCase = { + 0xff, 0xd8, 0xff, 0xe1, 0x0a, 0x0f, 0x45, 0x78, 0x69, 0x66, 0x00, + 0x00, 0x4d, 0x4d, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0d, + 0x01, 0x0e, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, + 0xaa, 0x01, 0x0f, 0x00, 0x02, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, + 0x00, 0xba, 0x01, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x06, 0x00, + 0x00, 0x00, 0xc2, 0x01, 0x12, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x00, 0x01, 0x1a, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0xc8, 0x01, 0x1b, 0x00, 0x05, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0xd0, 0x01, 0x28, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x01, 0x31, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0xd8, 0x01, 0x32, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0xe6, 0x02, 0x13, + 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x82, + 0x98, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0xfa, + 0x87, 0x69, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, + 0x84, 0xc4, 0xa5, 0x00, 0x07, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, + 0x01, 0x08, 0x00, 0x00, 0x04, 0x1e, 0x32, 0x30, 0x30, 0x38, 0x31, + 0x30, 0x32, 0x39, 0x31, 0x33, 0x35, 0x35, 0x33, 0x31, 0x00, 0x00, + 0x44, 0x6f, 0x43, 0x6f, 0x4d, 0x6f, 0x00, 0x00, 0x44, 0x39, 0x30, + 0x35, 0x69, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x01, 0x44, 0x39, 0x30, + 0x35, 0x69, 0x20, 0x56, 0x65, 0x72, 0x31, 0x2e, 0x30, 0x30, 0x00, + 0x32, 0x30, 0x30, 0x38, 0x3a, 0x31, 0x30, 0x3a, 0x32, 0x39, 0x20, + 0x31, 0x33, 0x3a, 0x35, 0x35, 0x3a, 0x34, 0x37, 0x00, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x00, 0x50, 0x72, 0x69, 0x6e, 0x74, 0x49, 0x4d, 0x00, 0x30, 0x33, + 0x30, 0x30, 0x00, 0x00, 0x00, 0x06, 0x00, 0x01, 0x00, 0x14, 0x00, + 0x14, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x00, 0x34, 0x01, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x01, 0x10, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x11, 0x09, 0x00, 0x00, 0x27, 0x10, 0x00, 0x00, 0x0f, 0x0b, 0x00, + 0x00, 0x27, 0x10, 0x00, 0x00, 0x05, 0x97, 0x00, 0x00, 0x27, 0x10, + 0x00, 0x00, 0x08, 0xb0, 0x00, 0x00, 0x27, 0x10, 0x00, 0x00, 0x1c, + 0x01, 0x00, 0x00, 0x27, 0x10, 0x00, 0x00, 0x02, 0x5e, 0x00, 0x00, + 0x27, 0x10, 0x00, 0x00, 0x00, 0x8b, 0x00, 0x00, 0x27, 0x10, 0x00, + 0x00, 0x03, 0xcb, 0x00, 0x00, 0x27, 0x10, 0x00, 0x00, 0x1b, 0xe5, + 0x00, 0x00, 0x27, 0x10, 0x00, 0x28, 0x82, 0x9a, 0x00, 0x05, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0x6a, 0x82, 0x9d, 0x00, 0x05, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0x72, 0x88, 0x22, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x90, 0x00, + 0x00, 0x07, 0x00, 0x00, 0x00, 0x04, 0x30, 0x32, 0x32, 0x30, 0x90, + 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x03, 0x7a, + 0x90, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x03, + 0x8e, 0x91, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x04, 0x01, 0x02, + 0x03, 0x00, 0x91, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x03, 0xa2, 0x92, 0x01, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x03, 0xaa, 0x92, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x03, 0xb2, 0x92, 0x04, 0x00, 0x0a, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x03, 0xba, 0x92, 0x05, 0x00, 0x05, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0xc2, 0x92, 0x07, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x92, 0x08, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x92, 0x09, + 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x92, + 0x0a, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0xca, + 0x92, 0x7c, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x92, 0x86, 0x00, 0x07, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, + 0x03, 0xd2, 0xa0, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x04, 0x30, + 0x31, 0x30, 0x30, 0xa0, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x01, 0x00, 0x00, 0xa0, 0x02, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x60, 0x00, 0x00, 0xa0, 0x03, 0x00, 0x03, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x48, 0x00, 0x00, 0xa0, 0x05, 0x00, 0x04, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x00, 0xa2, 0x0e, 0x00, 0x05, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0xe8, 0xa2, 0x0f, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0xf0, 0xa2, 0x10, + 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0xa2, + 0x17, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, + 0xa3, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, + 0x00, 0xa3, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x00, 0xa4, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0xa4, 0x02, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0xa4, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x04, 0x00, 0x05, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x03, 0xf8, 0xa4, 0x05, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x1d, 0x00, 0x00, 0xa4, 0x06, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x07, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x08, + 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xa4, + 0x09, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0xa4, 0x0a, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0xa4, 0x0c, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, 0x00, + 0x00, 0x27, 0x10, 0x00, 0x00, 0x01, 0x5e, 0x00, 0x00, 0x00, 0x64, + 0x32, 0x30, 0x30, 0x38, 0x3a, 0x31, 0x30, 0x3a, 0x32, 0x39, 0x20, + 0x31, 0x33, 0x3a, 0x35, 0x35, 0x3a, 0x33, 0x31, 0x00, 0x32, 0x30, + 0x30, 0x38, 0x3a, 0x31, 0x30, 0x3a, 0x32, 0x39, 0x20, 0x31, 0x33, + 0x3a, 0x35, 0x35, 0x3a, 0x34, 0x37, 0x00, 0x00, 0x00, 0x29, 0x88, + 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x02, 0xb2, 0x00, 0x00, 0x00, + 0x64, 0x00, 0x00, 0x01, 0x5e, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x25, 0x00, + 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0e, 0x92, 0x00, 0x00, 0x03, 0xe8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x30, 0x30, + 0x38, 0x31, 0x30, 0x32, 0x39, 0x31, 0x33, 0x35, 0x35, 0x33, 0x31, + 0x00, 0x00, 0x20, 0x2a, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x2a, + 0xe2, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x04, 0x52, 0x39, 0x38, 0x00, 0x00, 0x02, 0x00, 0x07, 0x00, 0x00, + 0x00, 0x04, 0x30, 0x31, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x06, 0x01, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x06, + 0x00, 0x00, 0x01, 0x1a, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x04, 0x6c, 0x01, 0x1b, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x04, 0x74, 0x01, 0x28, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x02, 0x00, 0x00, 0x02, 0x01, 0x00, 0x04, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x04, 0x7c, 0x02, 0x02, 0x00, 0x04, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x05, 0x8b, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x00, 0x00, 0x01, 0xff, 0xd8, 0xff, 0xdb, 0x00, 0x84, + 0x00, 0x20, 0x16, 0x18, 0x1c, 0x18, 0x14, 0x20, 0x1c, 0x1a, 0x1c, + 0x24, 0x22, 0x20, 0x26, 0x30, 0x50, 0x34, 0x30, 0x2c, 0x2c, 0x30, + 0x62, 0x46, 0x4a, 0x3a, 0x50, 0x74, 0x66, 0x7a, 0x78, 0x72, 0x66, + 0x70, 0x6e, 0x80, 0x90, 0xb8, 0x9c, 0x80, 0x88, 0xae, 0x8a, 0x6e, + 0x70, 0xa0, 0xda, 0xa2, 0xae, 0xbe, 0xc4, 0xce, 0xd0, 0xce, 0x7c, + 0x9a, 0xe2, 0xf2, 0xe0, 0xc8, 0xf0, 0xb8, 0xca, 0xce, 0xc6, 0x01, + 0x22, 0x24, 0x24, 0x30, 0x2a, 0x30, 0x5e, 0x34, 0x34, 0x5e, 0xc6, + 0x84, 0x70, 0x84, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xff, 0xc0, + 0x00, 0x11, 0x08, 0x00, 0x78, 0x00, 0xa0, 0x03, 0x01, 0x21, 0x00, + 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, 0xff, 0xc4, 0x01, 0xa2, 0x00, + 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, + 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x10, 0x00, 0x02, 0x01, 0x03, + 0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, + 0x7d, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, + 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, + 0x91, 0xa1, 0x08, 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, + 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, + 0x1a, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, + 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, + 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, + 0x79, 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, + 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, + 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, + 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, + 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, + 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1, + 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0x01, 0x00, + 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, + 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x11, 0x00, 0x02, 0x01, 0x02, 0x04, + 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77, + 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, + 0x41, 0x51, 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, + 0x42, 0x91, 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15, + 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, + 0x18, 0x19, 0x1a, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, + 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, + 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, + 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, + 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, + 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, + 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, + 0xda, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2, + 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xff, 0xda, 0x00, + 0x0c, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00, + 0x14, 0x54, 0xaa, 0x2a, 0x46, 0x48, 0xa2, 0xa4, 0x55, 0xa6, 0x04, + 0x8a, 0x29, 0xe0, 0x53, 0x10, 0xe0, 0x29, 0xc0, 0x50, 0x03, 0xb1, + 0x46, 0x29, 0x80, 0x84, 0x52, 0x11, 0x40, 0x0d, 0x22, 0x9a, 0x45, + 0x20, 0x23, 0x61, 0x51, 0x30, 0xa0, 0x08, 0xc8, 0xa8, 0xd8, 0x52, + 0x02, 0x26, 0x15, 0x0b, 0x0a, 0x00, 0xb4, 0xa2, 0xa5, 0x5a, 0x00, + 0x91, 0x45, 0x4a, 0xa2, 0x81, 0x92, 0x01, 0x4e, 0x02, 0x98, 0x87, + 0x0a, 0x70, 0xa0, 0x07, 0x62, 0x8c, 0x50, 0x21, 0x0d, 0x25, 0x00, + 0x34, 0x8a, 0x61, 0x14, 0x0c, 0x63, 0x0a, 0x89, 0x85, 0x00, 0x46, + 0xd5, 0x1b, 0x52, 0x02, 0x16, 0xa8, 0x98, 0x50, 0x05, 0x94, 0xa9, + 0x16, 0x80, 0x25, 0x5a, 0x95, 0x68, 0x18, 0xf1, 0x4f, 0x14, 0xc4, + 0x3b, 0xb5, 0x22, 0xb6, 0x38, 0x34, 0x00, 0xe3, 0x22, 0x8e, 0xf4, + 0x79, 0x8a, 0x7b, 0xd1, 0x71, 0x03, 0x30, 0xc7, 0x14, 0x83, 0xa5, + 0x00, 0x06, 0x98, 0x68, 0x01, 0x8d, 0x51, 0x35, 0x03, 0x22, 0x6a, + 0x8d, 0xa9, 0x01, 0x13, 0x54, 0x4d, 0x40, 0x13, 0xa5, 0x4a, 0x28, + 0x02, 0x45, 0x35, 0x2a, 0x9a, 0x00, 0x78, 0x34, 0xf0, 0x69, 0x80, + 0x34, 0x81, 0x45, 0x40, 0xce, 0x58, 0xe6, 0xa2, 0x4c, 0x06, 0xe4, + 0xfa, 0xd1, 0x93, 0x50, 0x21, 0xca, 0xe4, 0x55, 0x84, 0x90, 0x30, + 0xab, 0x8b, 0x18, 0xa6, 0x9a, 0x6a, 0xc4, 0x31, 0xaa, 0x26, 0xa0, + 0x64, 0x4d, 0x51, 0xb5, 0x20, 0x23, 0x6a, 0x89, 0xa8, 0x02, 0x44, + 0x35, 0x2a, 0x9a, 0x00, 0x95, 0x4d, 0x48, 0xa6, 0x80, 0x24, 0x53, + 0x4e, 0xce, 0x05, 0x30, 0x2b, 0x3b, 0xee, 0x6a, 0x91, 0x5d, 0x76, + 0x63, 0xbd, 0x65, 0x7d, 0x40, 0x66, 0x68, 0xa9, 0x02, 0x45, 0x2b, + 0xb3, 0x9e, 0xb4, 0xc5, 0x6d, 0xad, 0x9a, 0xa0, 0x2c, 0x06, 0xc8, + 0xcd, 0x04, 0xd6, 0xa2, 0x23, 0x63, 0x51, 0xb1, 0xa0, 0x64, 0x4d, + 0x51, 0x93, 0x48, 0x08, 0xda, 0xa2, 0x6a, 0x00, 0x72, 0x1a, 0x99, + 0x4d, 0x00, 0x48, 0xa6, 0xa4, 0x53, 0x4c, 0x07, 0x86, 0x03, 0xbd, + 0x2b, 0x9c, 0xa7, 0x14, 0x98, 0x10, 0x85, 0x34, 0xe0, 0xa6, 0xb3, + 0xb0, 0x0b, 0xb5, 0xa8, 0x0a, 0xd4, 0x58, 0x42, 0xed, 0x3e, 0x94, + 0xd2, 0xa6, 0x8b, 0x01, 0x34, 0x44, 0xed, 0xe6, 0x9c, 0x4d, 0x6a, + 0x80, 0x8d, 0x8d, 0x46, 0xc6, 0x80, 0x23, 0x63, 0x51, 0x9a, 0x06, + 0x46, 0xd5, 0x13, 0x52, 0x01, 0x54, 0xd4, 0xaa, 0x68, 0x02, 0x40, + 0x6a, 0x40, 0x78, 0xa0, 0x08, 0x59, 0xce, 0xee, 0xb5, 0x2a, 0x39, + 0xd9, 0x59, 0xa7, 0xa8, 0x00, 0x73, 0xeb, 0x4e, 0x0e, 0x7d, 0x69, + 0x5c, 0x05, 0xf3, 0x0f, 0xad, 0x1e, 0x61, 0xf5, 0xa7, 0x71, 0x0b, + 0xe6, 0x35, 0x21, 0x90, 0xd3, 0xb8, 0x0e, 0x32, 0x10, 0x95, 0x10, + 0x91, 0xb3, 0xd6, 0x9b, 0x60, 0x4b, 0x9c, 0x8a, 0x63, 0x1a, 0xb0, + 0x18, 0x4d, 0x46, 0xc6, 0x80, 0x22, 0x6a, 0x61, 0xa4, 0x31, 0xaa, + 0x6a, 0x55, 0x34, 0x01, 0x2a, 0x9a, 0x7e, 0x78, 0xa0, 0x08, 0x09, + 0xf9, 0xaa, 0x58, 0xcf, 0xca, 0x6b, 0x3e, 0xa0, 0x00, 0xd3, 0x81, + 0xa9, 0x01, 0x73, 0x46, 0x69, 0x80, 0xb9, 0xa4, 0xcd, 0x00, 0x2b, + 0x1f, 0x92, 0xa3, 0x07, 0x9a, 0x6f, 0x70, 0x26, 0xcf, 0x14, 0xd2, + 0x6b, 0x51, 0x0c, 0x63, 0x51, 0xb1, 0xa0, 0x08, 0xda, 0x98, 0x69, + 0x0c, 0x8d, 0x4d, 0x4a, 0xa6, 0x80, 0x24, 0x53, 0x52, 0x03, 0xc5, + 0x02, 0x21, 0x27, 0xe6, 0xa9, 0x23, 0x3f, 0x29, 0xac, 0xfa, 0x8c, + 0x01, 0xe6, 0x9c, 0x0d, 0x48, 0x0a, 0x0d, 0x2e, 0x68, 0x01, 0x73, + 0x49, 0x9a, 0x60, 0x2b, 0x1f, 0x92, 0x98, 0x3a, 0xd3, 0x7b, 0x81, + 0x36, 0x78, 0xa6, 0x93, 0x5a, 0x88, 0x8c, 0x9a, 0x63, 0x1a, 0x00, + 0x8c, 0xd3, 0x0d, 0x21, 0x91, 0x29, 0xa9, 0x14, 0xd0, 0x04, 0x8a, + 0x69, 0xe0, 0xd3, 0x11, 0x1b, 0x1e, 0x6a, 0x48, 0xcf, 0xca, 0x6b, + 0x3e, 0xa3, 0x10, 0x1a, 0x70, 0x35, 0x20, 0x38, 0x1a, 0x5c, 0xd2, + 0x01, 0x73, 0x49, 0x9a, 0x60, 0x39, 0x8f, 0xca, 0x29, 0x8b, 0xf7, + 0xaa, 0xba, 0x88, 0x96, 0x9a, 0x6b, 0x40, 0x18, 0xc6, 0xa3, 0x26, + 0x80, 0x18, 0x69, 0xa6, 0x90, 0xc8, 0x14, 0xd4, 0x8a, 0x69, 0x80, + 0xf0, 0x6a, 0x40, 0x68, 0x10, 0xbb, 0x41, 0xa7, 0xe3, 0x0b, 0xc5, + 0x2b, 0x01, 0x10, 0xa7, 0x03, 0x59, 0x0c, 0x76, 0x69, 0x73, 0x40, + 0x0b, 0x9a, 0x28, 0x11, 0x28, 0x19, 0x5e, 0x69, 0x02, 0x81, 0x5a, + 0xd8, 0x00, 0xd3, 0x4d, 0x50, 0x0c, 0x6a, 0x8c, 0xd2, 0x01, 0xa6, + 0x98, 0x69, 0x0c, 0xae, 0xa6, 0xa4, 0x06, 0x80, 0x1e, 0xa6, 0x9e, + 0x0d, 0x31, 0x12, 0x03, 0x4f, 0x06, 0x80, 0x13, 0x60, 0x34, 0xd3, + 0xc1, 0xa8, 0x92, 0x01, 0xf1, 0x8d, 0xdd, 0x69, 0xcc, 0xa1, 0x69, + 0x5b, 0x4b, 0x80, 0x83, 0x93, 0x52, 0x04, 0x14, 0xe2, 0xae, 0x03, + 0xa9, 0x0d, 0x68, 0x03, 0x4d, 0x34, 0xd0, 0x03, 0x0d, 0x30, 0xd2, + 0x01, 0x86, 0x9a, 0x68, 0x19, 0x58, 0x1a, 0x78, 0xa4, 0x04, 0x8a, + 0x69, 0xe0, 0xd3, 0x10, 0xe0, 0x69, 0xe0, 0xd0, 0x03, 0xc1, 0xa8, + 0xdb, 0xad, 0x4c, 0x81, 0x12, 0x45, 0xd6, 0x9d, 0x25, 0x1d, 0x00, + 0x6a, 0xf5, 0xa9, 0xe8, 0x80, 0x31, 0x29, 0x0d, 0x58, 0x08, 0x69, + 0x86, 0x80, 0x1a, 0x69, 0x86, 0x90, 0x0c, 0x34, 0xd3, 0x48, 0x65, + 0x51, 0x4f, 0x06, 0x98, 0x0f, 0x14, 0xf0, 0x68, 0x10, 0xf0, 0x69, + 0xe0, 0xd0, 0x03, 0x81, 0xa5, 0x2b, 0x9a, 0x1a, 0xb8, 0x87, 0xa8, + 0xdb, 0x4a, 0x46, 0x68, 0xb6, 0x80, 0x2a, 0xa8, 0x14, 0xea, 0x12, + 0xb0, 0x05, 0x21, 0xa6, 0x02, 0x1a, 0x61, 0xa0, 0x06, 0x9a, 0x61, + 0xa4, 0x31, 0x86, 0x9a, 0x69, 0x0c, 0xa8, 0x0d, 0x3c, 0x53, 0x01, + 0xe2, 0x9e, 0x28, 0x10, 0xf1, 0x4e, 0x06, 0x98, 0x0f, 0x06, 0x9e, + 0x0d, 0x02, 0x1c, 0x29, 0xc2, 0x80, 0x16, 0x96, 0x80, 0x0a, 0x4a, + 0x00, 0x43, 0x4d, 0x34, 0x0c, 0x61, 0xa6, 0x1a, 0x40, 0x34, 0xd3, + 0x4d, 0x21, 0x80, 0xff, 0xd9, 0xff, 0xdb, 0x00, 0x84, 0x00, 0x0a, + 0x07, 0x07, 0x08, 0x07, 0x06, 0x0a, 0x08, 0x08, 0x08, 0x0b, 0x0a, + 0x0a, 0x0b, 0x0e, 0x18, 0x10, 0x0e, 0x0d, 0x0d, 0x0e, 0x1d, 0x15, + 0x16, 0x11, 0x18, 0x23, 0x1f, 0x25, 0x24, 0x22, 0x1f, 0x22, 0x21, + 0x26, 0x2b, 0x37, 0x2f, 0x26, 0x29, 0x34, 0x29, 0x21, 0x22, 0x30, + 0x41, 0x31, 0x34, 0x39, 0x3b, 0x3e, 0x3e, 0x3e, 0x25, 0x2e, 0x44, + 0x49, 0x43, 0x3c, 0x48, 0x37, 0x3d, 0x3e, 0x3b, 0x01, 0x0a, 0x0b, + 0x0b, 0x0e, 0x0d, 0x0e, 0x1c, 0x10, 0x10, 0x1c, 0x3b, 0x28, 0x22, + 0x28, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, + 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, + 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, + 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, + 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0xff, 0xc0, 0x00, 0x11, + 0x08, 0x00, 0x48, 0x00, 0x60, 0x03, 0x01, 0x21, 0x00, 0x02, 0x11, + 0x01, 0x03, 0x11, 0x01, 0xff, 0xc4, 0x01, 0xa2, 0x00, 0x00, 0x01, + 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, + 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7d, 0x01, + 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, + 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, + 0x08, 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x24, 0x33, + 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25, + 0x26, 0x27, 0x28, 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, + 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, + 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, + 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, + 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, + 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, + 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, + 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, + 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1, 0xf2, 0xf3, + 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0x01, 0x00, 0x03, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x11, 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, + 0x04, 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77, 0x00, 0x01, + 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, + 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, + 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15, 0x62, 0x72, + 0xd1, 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, + 0x1a, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, 0x39, + 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, + 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, + 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, + 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, + 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, + 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe2, + 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4, + 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xff, 0xda, 0x00, 0x0c, 0x03, + 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00, 0x9e, 0xd2, + 0x2e, 0x07, 0x15, 0xaf, 0x6d, 0x08, 0xe2, 0xb3, 0x45, 0x1a, 0xf6, + 0xd0, 0x00, 0x01, 0xc5, 0x68, 0x45, 0x17, 0x4a, 0xb4, 0x22, 0xe4, + 0x70, 0x8c, 0x74, 0xa9, 0x3c, 0xa1, 0x8e, 0x95, 0x48, 0x96, 0x31, + 0xe2, 0x18, 0xe9, 0x55, 0xa5, 0x8c, 0x7a, 0x50, 0x05, 0x0b, 0x88, + 0x86, 0x0f, 0x15, 0x8f, 0x75, 0x1f, 0x26, 0x93, 0x19, 0x91, 0x77, + 0x18, 0xc1, 0xac, 0x4b, 0xc8, 0xfa, 0xd6, 0x63, 0x37, 0x6d, 0x31, + 0xb4, 0x73, 0x5b, 0x36, 0xa0, 0x1c, 0x50, 0x80, 0xd7, 0x83, 0xa0, + 0xab, 0xd1, 0x62, 0xad, 0x09, 0x8f, 0x17, 0x29, 0x03, 0xb2, 0xcc, + 0xe0, 0x77, 0x14, 0xa3, 0x56, 0xb3, 0x27, 0x1e, 0x67, 0xe9, 0x52, + 0xea, 0xc6, 0x3a, 0x36, 0x48, 0xef, 0x3d, 0x27, 0x70, 0x22, 0x60, + 0x47, 0x52, 0x69, 0xb2, 0xe2, 0xad, 0x3b, 0xea, 0x80, 0xa3, 0x38, + 0xe0, 0xd6, 0x3d, 0xd8, 0x1c, 0xd0, 0xca, 0x46, 0x3d, 0xd0, 0x18, + 0x35, 0x89, 0x78, 0xa3, 0x9a, 0xcd, 0x8c, 0xd2, 0xb3, 0x93, 0x2a, + 0x2b, 0x66, 0xd5, 0xf1, 0x8a, 0x10, 0x1a, 0xd6, 0xf2, 0x03, 0x8a, + 0x9e, 0xe6, 0xf4, 0x5a, 0xdb, 0xef, 0xfe, 0x23, 0xc0, 0xa7, 0x27, + 0xcb, 0x16, 0xc4, 0xcc, 0xdd, 0xe2, 0x78, 0x9a, 0x69, 0x66, 0xcc, + 0x99, 0xe1, 0x4d, 0x47, 0xba, 0xbc, 0xd9, 0x6a, 0xee, 0x26, 0x59, + 0x59, 0x4d, 0xac, 0x69, 0x34, 0x52, 0xe5, 0x8f, 0x55, 0xad, 0x58, + 0xae, 0x85, 0xc4, 0x22, 0x41, 0xdf, 0xad, 0x76, 0x61, 0xe5, 0x6f, + 0x74, 0x45, 0x69, 0xdc, 0x00, 0x79, 0xac, 0x8b, 0xa6, 0xc9, 0x35, + 0xd4, 0x34, 0x64, 0xdc, 0x37, 0x06, 0xb1, 0xae, 0x88, 0xc1, 0xac, + 0xd8, 0xc9, 0x2c, 0xa6, 0xe0, 0x73, 0x5b, 0x36, 0xf3, 0x74, 0xe6, + 0x84, 0x05, 0xe3, 0xa9, 0x47, 0x6a, 0x14, 0xb6, 0x49, 0x3d, 0x85, + 0x3a, 0xee, 0xee, 0x2b, 0xa8, 0xe2, 0x6f, 0x30, 0x81, 0xe9, 0x8a, + 0xca, 0xa4, 0xe2, 0xd3, 0x8b, 0x01, 0xb1, 0xf9, 0x04, 0x7f, 0xaf, + 0x23, 0xf0, 0xa9, 0x54, 0x41, 0x9c, 0xfd, 0xa3, 0xf4, 0xae, 0x65, + 0x18, 0xf7, 0x25, 0x8a, 0xe2, 0x02, 0x38, 0xb8, 0xfd, 0x2a, 0x7b, + 0x5b, 0xa8, 0x6d, 0x6d, 0x5d, 0x9a, 0x5d, 0xcb, 0xbb, 0xd2, 0xb6, + 0xa6, 0xa3, 0x19, 0x5e, 0xe2, 0x03, 0x7b, 0x1d, 0xc2, 0x17, 0x8d, + 0xb8, 0xac, 0xfb, 0x89, 0x39, 0x35, 0xd6, 0x9a, 0x6a, 0xe8, 0x66, + 0x55, 0xcb, 0xf5, 0xac, 0x7b, 0x96, 0xeb, 0x50, 0xc6, 0x88, 0x6d, + 0x66, 0xe9, 0xcd, 0x6c, 0xdb, 0x4f, 0xd3, 0x9a, 0x00, 0x2f, 0xe6, + 0xf9, 0xa3, 0xe7, 0xb5, 0x4a, 0x93, 0x7f, 0xa2, 0xc6, 0x73, 0xdc, + 0xd7, 0x15, 0x55, 0xef, 0x48, 0x7d, 0x09, 0x52, 0x6e, 0x3a, 0xd4, + 0xab, 0x2f, 0xbd, 0x61, 0x16, 0x0c, 0x73, 0x49, 0xc5, 0x24, 0x92, + 0x7f, 0xa2, 0x63, 0xfd, 0xaa, 0xd6, 0x2f, 0x71, 0x0e, 0xb1, 0x93, + 0xf7, 0x2d, 0xf5, 0xa4, 0x9e, 0x4e, 0xb5, 0xdd, 0x4b, 0xf8, 0x68, + 0x4c, 0xcb, 0xb9, 0x93, 0xad, 0x65, 0xce, 0xd9, 0x26, 0xa9, 0x8d, + 0x19, 0xf6, 0xf2, 0xf4, 0xe6, 0xb5, 0xad, 0xe7, 0xc6, 0x39, 0xa0, + 0x18, 0xeb, 0xc9, 0x77, 0x6c, 0x35, 0x2a, 0x4b, 0xfe, 0x8a, 0x9c, + 0xff, 0x00, 0x11, 0xae, 0x3a, 0x8b, 0xde, 0x61, 0xd0, 0x9e, 0x39, + 0xb8, 0xeb, 0x53, 0xac, 0xb9, 0xae, 0x5b, 0x00, 0xf3, 0x27, 0x14, + 0x92, 0xc9, 0xfe, 0x8a, 0x3f, 0xde, 0x35, 0xac, 0x3a, 0x88, 0x92, + 0xcd, 0xb1, 0x6e, 0x7d, 0xcd, 0x32, 0x67, 0xeb, 0xcd, 0x7a, 0x14, + 0xfe, 0x04, 0x26, 0x66, 0xce, 0xf9, 0x26, 0xb3, 0xe6, 0x6e, 0xb4, + 0xd9, 0x48, 0xc8, 0x82, 0x4e, 0x07, 0x35, 0xa7, 0x6f, 0x2f, 0x02, + 0x9a, 0x06, 0x5f, 0x8c, 0xa4, 0x83, 0x0e, 0x32, 0x2a, 0x69, 0xe3, + 0xdd, 0x12, 0x08, 0x97, 0x85, 0xec, 0x2a, 0x2a, 0x42, 0xf1, 0x76, + 0x26, 0xe4, 0x6a, 0x59, 0x0e, 0x18, 0x10, 0x6a, 0xd2, 0x89, 0x02, + 0x6e, 0x2a, 0x71, 0xeb, 0x5c, 0x1c, 0x8c, 0xa6, 0x48, 0xbb, 0xdc, + 0x61, 0x41, 0x35, 0x72, 0x28, 0x87, 0xd9, 0xf6, 0x4a, 0xb9, 0xe7, + 0x38, 0xae, 0x8c, 0x3d, 0x36, 0xdd, 0xde, 0xc4, 0xb0, 0x21, 0x51, + 0x76, 0xa8, 0xc0, 0xaa, 0x93, 0x31, 0xe6, 0xbb, 0x2d, 0x65, 0x61, + 0x19, 0xd3, 0x1e, 0xb5, 0x46, 0x5a, 0x96, 0x5a, 0x30, 0xa0, 0x7e, + 0x05, 0x69, 0x5b, 0xc9, 0xc6, 0x28, 0x40, 0xcd, 0x08, 0x64, 0x3c, + 0x73, 0x57, 0xe1, 0x94, 0xf1, 0xcd, 0x5a, 0x21, 0x8c, 0xb9, 0x63, + 0xe7, 0x67, 0x1d, 0xab, 0x40, 0xb1, 0xfb, 0x00, 0x1d, 0xf0, 0x2b, + 0x99, 0x2d, 0x66, 0x3e, 0x88, 0x75, 0x81, 0x3f, 0x31, 0xf6, 0xab, + 0x64, 0xd6, 0xb4, 0x17, 0xee, 0xd0, 0x9e, 0xe4, 0x32, 0x1a, 0xa7, + 0x31, 0xad, 0x18, 0x14, 0x26, 0xef, 0x54, 0xa5, 0xa8, 0x65, 0xa3, + 0x9c, 0x81, 0xfa, 0x56, 0x8c, 0x2d, 0xce, 0x68, 0x40, 0xcb, 0xf1, + 0x37, 0xbd, 0x5e, 0x85, 0xea, 0xd1, 0x0c, 0xbb, 0x19, 0x56, 0x23, + 0x20, 0x1f, 0xad, 0x5c, 0x42, 0x08, 0x03, 0xb5, 0x55, 0x91, 0x04, + 0xc9, 0x80, 0x38, 0x00, 0x0a, 0x71, 0x34, 0x6c, 0x32, 0x27, 0xe9, + 0x55, 0x25, 0x15, 0x2c, 0x68, 0xa3, 0x30, 0xeb, 0x54, 0xa5, 0x15, + 0x0c, 0xd1, 0x00, 0xff, 0xd9}; + + /* package */ static final byte[] sPhotoByteArrayForComplicatedCase; + + static { + final int length = sPhotoIntArrayForComplicatedCase.length; + sPhotoByteArrayForComplicatedCase = new byte[length]; + for (int i = 0; i < length; i++) { + sPhotoByteArrayForComplicatedCase[i] = (byte)sPhotoIntArrayForComplicatedCase[i]; + } + } + + public void testV21SimpleCase1_Parsing() { + mVerifier.initForImportTest(V21, R.raw.v21_simple_1); + mVerifier.addPropertyNodesVerifierElem() + .addExpectedNodeWithOrder("N", "Ando;Roid;", Arrays.asList("Ando", "Roid", "")); + } + + public void testV21SimpleCase1_Type_Generic() { + mVerifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_GENERIC, R.raw.v21_simple_1); + mVerifier.addContentValuesVerifierElem() + .addExpected(StructuredName.CONTENT_ITEM_TYPE) + .put(StructuredName.FAMILY_NAME, "Ando") + .put(StructuredName.GIVEN_NAME, "Roid") + .put(StructuredName.DISPLAY_NAME, "Roid Ando"); + } + + public void testV21SimpleCase1_Type_Japanese() { + mVerifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE, R.raw.v21_simple_1); + mVerifier.addContentValuesVerifierElem() + .addExpected(StructuredName.CONTENT_ITEM_TYPE) + .put(StructuredName.FAMILY_NAME, "Ando") + .put(StructuredName.GIVEN_NAME, "Roid") + // If name-related strings only contains printable Ascii, + // the order is remained to be US's: + // "Prefix Given Middle Family Suffix" + .put(StructuredName.DISPLAY_NAME, "Roid Ando"); + } + + public void testV21SimpleCase2() { + mVerifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE, R.raw.v21_simple_2); + mVerifier.addContentValuesVerifierElem() + .addExpected(StructuredName.CONTENT_ITEM_TYPE) + .put(StructuredName.DISPLAY_NAME, "Ando Roid"); + } + + public void testV21SimpleCase3() { + mVerifier.initForImportTest(V21, R.raw.v21_simple_3); + mVerifier.addContentValuesVerifierElem() + .addExpected(StructuredName.CONTENT_ITEM_TYPE) + .put(StructuredName.FAMILY_NAME, "Ando") + .put(StructuredName.GIVEN_NAME, "Roid") + // "FN" field should be prefered since it should contain the original + // order intended by the author of the file. + .put(StructuredName.DISPLAY_NAME, "Ando Roid"); + } + + /** + * Tests ';' is properly handled by VCardParser implementation. + */ + public void testV21BackslashCase_Parsing() { + mVerifier.initForImportTest(V21, R.raw.v21_backslash); + mVerifier.addPropertyNodesVerifierElem() + .addExpectedNodeWithOrder("VERSION", "2.1") + .addExpectedNodeWithOrder("N", ";A;B\\;C\\;;D;:E;\\\\;", + Arrays.asList("", "A;B\\", "C\\;", "D", ":E", "\\\\", "")) + .addExpectedNodeWithOrder("FN", "A;B\\C\\;D:E\\\\"); + + } + + /** + * Tests ContactStruct correctly ignores redundant fields in "N" property values and + * inserts name related data. + */ + public void testV21BackslashCase() { + mVerifier.initForImportTest(V21, R.raw.v21_backslash); + mVerifier.addContentValuesVerifierElem() + .addExpected(StructuredName.CONTENT_ITEM_TYPE) + // FAMILY_NAME is empty and removed in this test... + .put(StructuredName.GIVEN_NAME, "A;B\\") + .put(StructuredName.MIDDLE_NAME, "C\\;") + .put(StructuredName.PREFIX, "D") + .put(StructuredName.SUFFIX, ":E") + .put(StructuredName.DISPLAY_NAME, "A;B\\C\\;D:E\\\\"); + } + + public void testOrgBeforTitle() { + mVerifier.initForImportTest(V21, R.raw.v21_org_before_title); + ContentValuesVerifierElem elem = mVerifier.addContentValuesVerifierElem(); + elem.addExpected(StructuredName.CONTENT_ITEM_TYPE) + .put(StructuredName.DISPLAY_NAME, "Normal Guy"); + elem.addExpected(Organization.CONTENT_ITEM_TYPE) + .put(Organization.COMPANY, "Company") + .put(Organization.DEPARTMENT, "Organization Devision Room Sheet No.") + .put(Organization.TITLE, "Excellent Janitor") + .put(Organization.TYPE, Organization.TYPE_WORK); + } + + public void testTitleBeforOrg() { + mVerifier.initForImportTest(V21, R.raw.v21_title_before_org); + ContentValuesVerifierElem elem = mVerifier.addContentValuesVerifierElem(); + elem.addExpected(StructuredName.CONTENT_ITEM_TYPE) + .put(StructuredName.DISPLAY_NAME, "Nice Guy"); + elem.addExpected(Organization.CONTENT_ITEM_TYPE) + .put(Organization.COMPANY, "Marverous") + .put(Organization.DEPARTMENT, "Perfect Great Good Bad Poor") + .put(Organization.TITLE, "Cool Title") + .put(Organization.TYPE, Organization.TYPE_WORK); + } + + /** + * Verifies that vCard importer correctly interpret "PREF" attribute to IS_PRIMARY. + * The data contain three cases: one "PREF", no "PREF" and multiple "PREF", in each type. + */ + public void testV21PrefToIsPrimary() { + mVerifier.initForImportTest(V21, R.raw.v21_pref_handling); + ContentValuesVerifierElem elem = mVerifier.addContentValuesVerifierElem(); + elem.addExpected(StructuredName.CONTENT_ITEM_TYPE) + .put(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE) + .put(StructuredName.DISPLAY_NAME, "Smith"); + elem.addExpected(Phone.CONTENT_ITEM_TYPE) + .put(Phone.NUMBER, "1") + .put(Phone.TYPE, Phone.TYPE_HOME); + elem.addExpected(Phone.CONTENT_ITEM_TYPE) + .put(Phone.NUMBER, "2") + .put(Phone.TYPE, Phone.TYPE_WORK) + .put(Phone.IS_PRIMARY, 1); + elem.addExpected(Phone.CONTENT_ITEM_TYPE) + .put(Phone.NUMBER, "3") + .put(Phone.TYPE, Phone.TYPE_ISDN); + elem.addExpected(Email.CONTENT_ITEM_TYPE) + .put(Email.DATA, "test@example.com") + .put(Email.TYPE, Email.TYPE_HOME) + .put(Email.IS_PRIMARY, 1); + elem.addExpected(Email.CONTENT_ITEM_TYPE) + .put(Email.DATA, "test2@examination.com") + .put(Email.TYPE, Email.TYPE_MOBILE) + .put(Email.IS_PRIMARY, 1); + elem.addExpected(Organization.CONTENT_ITEM_TYPE) + .put(Organization.COMPANY, "Company") + .put(Organization.TITLE, "Engineer") + .put(Organization.TYPE, Organization.TYPE_WORK); + elem.addExpected(Organization.CONTENT_ITEM_TYPE) + .put(Organization.COMPANY, "Mystery") + .put(Organization.TITLE, "Blogger") + .put(Organization.TYPE, Organization.TYPE_WORK); + elem.addExpected(Organization.CONTENT_ITEM_TYPE) + .put(Organization.COMPANY, "Poetry") + .put(Organization.TITLE, "Poet") + .put(Organization.TYPE, Organization.TYPE_WORK); + } + + /** + * Tests all the properties in a complicated vCard are correctly parsed by the VCardParser. + */ + public void testV21ComplicatedCase_Parsing() { + mVerifier.initForImportTest(V21, R.raw.v21_complicated); + mVerifier.addPropertyNodesVerifierElem() + .addExpectedNodeWithOrder("VERSION", "2.1") + .addExpectedNodeWithOrder("N", "Gump;Forrest;Hoge;Pos;Tao", + Arrays.asList("Gump", "Forrest", "Hoge", "Pos", "Tao")) + .addExpectedNodeWithOrder("FN", "Joe Due") + .addExpectedNodeWithOrder("ORG", "Gump Shrimp Co.;Sales Dept.;Manager;Fish keeper", + Arrays.asList("Gump Shrimp Co.", "Sales Dept.;Manager", "Fish keeper")) + .addExpectedNodeWithOrder("ROLE", "Fish Cake Keeper!") + .addExpectedNodeWithOrder("TITLE", "Shrimp Man") + .addExpectedNodeWithOrder("X-CLASS", "PUBLIC") + .addExpectedNodeWithOrder("TEL", "(111) 555-1212", new TypeSet("WORK", "VOICE")) + .addExpectedNodeWithOrder("TEL", "(404) 555-1212", new TypeSet("HOME", "VOICE")) + .addExpectedNodeWithOrder("TEL", "0311111111", new TypeSet("CELL")) + .addExpectedNodeWithOrder("TEL", "0322222222", new TypeSet("VIDEO")) + .addExpectedNodeWithOrder("TEL", "0333333333", new TypeSet("VOICE")) + .addExpectedNodeWithOrder("ADR", + ";;100 Waters Edge;Baytown;LA;30314;United States of America", + Arrays.asList("", "", "100 Waters Edge", "Baytown", + "LA", "30314", "United States of America"), + null, null, new TypeSet("WORK"), null) + .addExpectedNodeWithOrder("LABEL", + "100 Waters Edge\r\nBaytown, LA 30314\r\nUnited States of America", + null, null, mContentValuesForQP, new TypeSet("WORK"), null) + .addExpectedNodeWithOrder("ADR", + ";;42 Plantation St.;Baytown;LA;30314;United States of America", + Arrays.asList("", "", "42 Plantation St.", "Baytown", + "LA", "30314", "United States of America"), null, null, + new TypeSet("HOME"), null) + .addExpectedNodeWithOrder("LABEL", + "42 Plantation St.\r\nBaytown, LA 30314\r\nUnited States of America", + null, null, mContentValuesForQP, + new TypeSet("HOME"), null) + .addExpectedNodeWithOrder("EMAIL", "forrestgump@walladalla.com", + new TypeSet("PREF", "INTERNET")) + .addExpectedNodeWithOrder("EMAIL", "cell@example.com", new TypeSet("CELL")) + .addExpectedNodeWithOrder("NOTE", "The following note is the example from RFC 2045.") + .addExpectedNodeWithOrder("NOTE", + "Now's the time for all folk to come to the aid of their country.", + null, null, mContentValuesForQP, null, null) + .addExpectedNodeWithOrder("PHOTO", null, + null, sPhotoByteArrayForComplicatedCase, mContentValuesForBase64V21, + new TypeSet("JPEG"), null) + .addExpectedNodeWithOrder("X-ATTRIBUTE", "Some String") + .addExpectedNodeWithOrder("BDAY", "19800101") + .addExpectedNodeWithOrder("GEO", "35.6563854,139.6994233") + .addExpectedNodeWithOrder("URL", "http://www.example.com/") + .addExpectedNodeWithOrder("REV", "20080424T195243Z"); + } + + /** + * Checks ContactStruct correctly inserts values in a complicated vCard + * into ContentResolver. + */ + public void testV21ComplicatedCase() { + mVerifier.initForImportTest(V21, R.raw.v21_complicated); + ContentValuesVerifierElem elem = mVerifier.addContentValuesVerifierElem(); + elem.addExpected(StructuredName.CONTENT_ITEM_TYPE) + .put(StructuredName.FAMILY_NAME, "Gump") + .put(StructuredName.GIVEN_NAME, "Forrest") + .put(StructuredName.MIDDLE_NAME, "Hoge") + .put(StructuredName.PREFIX, "Pos") + .put(StructuredName.SUFFIX, "Tao") + .put(StructuredName.DISPLAY_NAME, "Joe Due"); + elem.addExpected(Organization.CONTENT_ITEM_TYPE) + .put(Organization.TYPE, Organization.TYPE_WORK) + .put(Organization.COMPANY, "Gump Shrimp Co.") + .put(Organization.DEPARTMENT, "Sales Dept.;Manager Fish keeper") + .put(Organization.TITLE, "Shrimp Man"); + elem.addExpected(Phone.CONTENT_ITEM_TYPE) + .put(Phone.TYPE, Phone.TYPE_WORK) + // Phone number is expected to be formated with NAMP format in default. + .put(Phone.NUMBER, "111-555-1212"); + elem.addExpected(Phone.CONTENT_ITEM_TYPE) + .put(Phone.TYPE, Phone.TYPE_HOME) + .put(Phone.NUMBER, "404-555-1212"); + elem.addExpected(Phone.CONTENT_ITEM_TYPE) + .put(Phone.TYPE, Phone.TYPE_MOBILE) + .put(Phone.NUMBER, "031-111-1111"); + elem.addExpected(Phone.CONTENT_ITEM_TYPE) + .put(Phone.TYPE, Phone.TYPE_CUSTOM) + .put(Phone.LABEL, "VIDEO") + .put(Phone.NUMBER, "032-222-2222"); + elem.addExpected(Phone.CONTENT_ITEM_TYPE) + .put(Phone.TYPE, Phone.TYPE_CUSTOM) + .put(Phone.LABEL, "VOICE") + .put(Phone.NUMBER, "033-333-3333"); + elem.addExpected(StructuredPostal.CONTENT_ITEM_TYPE) + .put(StructuredPostal.TYPE, StructuredPostal.TYPE_WORK) + .put(StructuredPostal.COUNTRY, "United States of America") + .put(StructuredPostal.POSTCODE, "30314") + .put(StructuredPostal.REGION, "LA") + .put(StructuredPostal.CITY, "Baytown") + .put(StructuredPostal.STREET, "100 Waters Edge") + .put(StructuredPostal.FORMATTED_ADDRESS, + "100 Waters Edge Baytown LA 30314 United States of America"); + elem.addExpected(StructuredPostal.CONTENT_ITEM_TYPE) + .put(StructuredPostal.TYPE, StructuredPostal.TYPE_HOME) + .put(StructuredPostal.COUNTRY, "United States of America") + .put(StructuredPostal.POSTCODE, "30314") + .put(StructuredPostal.REGION, "LA") + .put(StructuredPostal.CITY, "Baytown") + .put(StructuredPostal.STREET, "42 Plantation St.") + .put(StructuredPostal.FORMATTED_ADDRESS, + "42 Plantation St. Baytown LA 30314 United States of America"); + elem.addExpected(Email.CONTENT_ITEM_TYPE) + // "TYPE=INTERNET" -> TYPE_CUSTOM + the label "INTERNET" + .put(Email.TYPE, Email.TYPE_CUSTOM) + .put(Email.LABEL, "INTERNET") + .put(Email.DATA, "forrestgump@walladalla.com") + .put(Email.IS_PRIMARY, 1); + elem.addExpected(Email.CONTENT_ITEM_TYPE) + .put(Email.TYPE, Email.TYPE_MOBILE) + .put(Email.DATA, "cell@example.com"); + elem.addExpected(Note.CONTENT_ITEM_TYPE) + .put(Note.NOTE, "The following note is the example from RFC 2045."); + elem.addExpected(Note.CONTENT_ITEM_TYPE) + .put(Note.NOTE, + "Now's the time for all folk to come to the aid of their country."); + elem.addExpected(Photo.CONTENT_ITEM_TYPE) + // No information about its image format can be inserted. + .put(Photo.PHOTO, sPhotoByteArrayForComplicatedCase); + elem.addExpected(Event.CONTENT_ITEM_TYPE) + .put(Event.START_DATE, "19800101") + .put(Event.TYPE, Event.TYPE_BIRTHDAY); + elem.addExpected(Website.CONTENT_ITEM_TYPE) + .put(Website.URL, "http://www.example.com/") + .put(Website.TYPE, Website.TYPE_HOMEPAGE); + } + + public void testV30Simple_Parsing() { + mVerifier.initForImportTest(V30, R.raw.v30_simple); + mVerifier.addPropertyNodesVerifierElem() + .addExpectedNodeWithOrder("VERSION", "3.0") + .addExpectedNodeWithOrder("FN", "And Roid") + .addExpectedNodeWithOrder("N", "And;Roid;;;", Arrays.asList("And", "Roid", "", "", "")) + .addExpectedNodeWithOrder("ORG", "Open;Handset; Alliance", + Arrays.asList("Open", "Handset", " Alliance")) + .addExpectedNodeWithOrder("SORT-STRING", "android") + .addExpectedNodeWithOrder("TEL", "0300000000", new TypeSet("PREF", "VOICE")) + .addExpectedNodeWithOrder("CLASS", "PUBLIC") + .addExpectedNodeWithOrder("X-GNO", "0") + .addExpectedNodeWithOrder("X-GN", "group0") + .addExpectedNodeWithOrder("X-REDUCTION", "0") + .addExpectedNodeWithOrder("REV", "20081031T065854Z"); + } + + public void testV30Simple() { + mVerifier.initForImportTest(V30, R.raw.v30_simple); + ContentValuesVerifierElem elem = mVerifier.addContentValuesVerifierElem(); + elem.addExpected(StructuredName.CONTENT_ITEM_TYPE) + .put(StructuredName.FAMILY_NAME, "And") + .put(StructuredName.GIVEN_NAME, "Roid") + .put(StructuredName.DISPLAY_NAME, "And Roid") + .put(StructuredName.PHONETIC_GIVEN_NAME, "android"); + elem.addExpected(Organization.CONTENT_ITEM_TYPE) + .put(Organization.COMPANY, "Open") + .put(Organization.DEPARTMENT, "Handset Alliance") + .put(Organization.TYPE, Organization.TYPE_WORK); + elem.addExpected(Phone.CONTENT_ITEM_TYPE) + .put(Phone.TYPE, Phone.TYPE_CUSTOM) + .put(Phone.LABEL, "VOICE") + .put(Phone.NUMBER, "030-000-0000") + .put(Phone.IS_PRIMARY, 1); + } + + public void testV21Japanese1_Parsing() { + // Though Japanese careers append ";;;;" at the end of the value of "SOUND", + // vCard 2.1/3.0 specification does not allow multiple values. + // Do not need to handle it as multiple values. + mVerifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE, R.raw.v21_japanese_1); + mVerifier.addPropertyNodesVerifierElem() + .addExpectedNodeWithOrder("VERSION", "2.1", null, null, null, null, null) + .addExpectedNodeWithOrder("N", "\u5B89\u85E4\u30ED\u30A4\u30C9;;;;", + Arrays.asList("\u5B89\u85E4\u30ED\u30A4\u30C9", "", "", "", ""), + null, mContentValuesForSJis, null, null) + .addExpectedNodeWithOrder("SOUND", + "\uFF71\uFF9D\uFF84\uFF9E\uFF73\uFF9B\uFF72\uFF84\uFF9E;;;;", + null, null, mContentValuesForSJis, + new TypeSet("X-IRMC-N"), null) + .addExpectedNodeWithOrder("TEL", "0300000000", null, null, null, + new TypeSet("VOICE", "PREF"), null); + } + + private void testV21Japanese1Common(int resId, int vcardType, boolean japanese) { + mVerifier.initForImportTest(vcardType, resId); + ContentValuesVerifierElem elem = mVerifier.addContentValuesVerifierElem(); + elem.addExpected(StructuredName.CONTENT_ITEM_TYPE) + .put(StructuredName.FAMILY_NAME, "\u5B89\u85E4\u30ED\u30A4\u30C9") + .put(StructuredName.DISPLAY_NAME, "\u5B89\u85E4\u30ED\u30A4\u30C9") + // While vCard parser does not split "SOUND" property values, + // ContactStruct care it. + .put(StructuredName.PHONETIC_GIVEN_NAME, + "\uFF71\uFF9D\uFF84\uFF9E\uFF73\uFF9B\uFF72\uFF84\uFF9E"); + elem.addExpected(Phone.CONTENT_ITEM_TYPE) + // Phone number formatting is different. + .put(Phone.NUMBER, (japanese ? "03-0000-0000" : "030-000-0000")) + .put(Phone.TYPE, Phone.TYPE_CUSTOM) + .put(Phone.LABEL, "VOICE") + .put(Phone.IS_PRIMARY, 1); + } + + /** + * Verifies vCard with Japanese can be parsed correctly with + * {@link android.pim.vcard.VCardConfig#VCARD_TYPE_V21_GENERIC}. + */ + public void testV21Japanese1_Type_Generic_Utf8() { + testV21Japanese1Common( + R.raw.v21_japanese_1, VCardConfig.VCARD_TYPE_V21_GENERIC, false); + } + + /** + * Verifies vCard with Japanese can be parsed correctly with + * {@link android.pim.vcard.VCardConfig#VCARD_TYPE_V21_JAPANESE}. + */ + public void testV21Japanese1_Type_Japanese_Sjis() { + testV21Japanese1Common( + R.raw.v21_japanese_1, VCardConfig.VCARD_TYPE_V21_JAPANESE, true); + } + + /** + * Verifies vCard with Japanese can be parsed correctly with + * {@link android.pim.vcard.VCardConfig#VCARD_TYPE_V21_JAPANESE}. + * since vCard 2.1 specifies the charset of each line if it contains non-Ascii. + */ + public void testV21Japanese1_Type_Japanese_Utf8() { + testV21Japanese1Common( + R.raw.v21_japanese_1, VCardConfig.VCARD_TYPE_V21_JAPANESE, true); + } + + public void testV21Japanese2_Parsing() { + mVerifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE, R.raw.v21_japanese_2); + mVerifier.addPropertyNodesVerifierElem() + .addExpectedNodeWithOrder("VERSION", "2.1") + .addExpectedNodeWithOrder("N", "\u5B89\u85E4;\u30ED\u30A4\u30C9\u0031;;;", + Arrays.asList("\u5B89\u85E4", "\u30ED\u30A4\u30C9\u0031", + "", "", ""), + null, mContentValuesForSJis, null, null) + .addExpectedNodeWithOrder("FN", "\u5B89\u85E4\u0020\u30ED\u30A4\u30C9\u0020\u0031", + null, null, mContentValuesForSJis, null, null) + .addExpectedNodeWithOrder("SOUND", + "\uFF71\uFF9D\uFF84\uFF9E\uFF73;\uFF9B\uFF72\uFF84\uFF9E\u0031;;;", + null, null, mContentValuesForSJis, + new TypeSet("X-IRMC-N"), null) + .addExpectedNodeWithOrder("ADR", + ";\u6771\u4EAC\u90FD\u6E0B\u8C37\u533A\u685C" + + "\u4E18\u753A\u0032\u0036\u002D\u0031\u30BB" + + "\u30EB\u30EA\u30A2\u30F3\u30BF\u30EF\u30FC\u0036" + + "\u968E;;;;150-8512;", + Arrays.asList("", + "\u6771\u4EAC\u90FD\u6E0B\u8C37\u533A\u685C" + + "\u4E18\u753A\u0032\u0036\u002D\u0031\u30BB" + + "\u30EB\u30EA\u30A2\u30F3\u30BF\u30EF\u30FC" + + "\u0036\u968E", "", "", "", "150-8512", ""), + null, mContentValuesForQPAndSJis, new TypeSet("HOME"), null) + .addExpectedNodeWithOrder("NOTE", "\u30E1\u30E2", null, null, + mContentValuesForQPAndSJis, null, null); + } + + public void testV21Japanese2_Type_Generic_Utf8() { + mVerifier.initForImportTest(V21, R.raw.v21_japanese_2); + ContentValuesVerifierElem elem = mVerifier.addContentValuesVerifierElem(); + elem.addExpected(StructuredName.CONTENT_ITEM_TYPE) + .put(StructuredName.FAMILY_NAME, "\u5B89\u85E4") + .put(StructuredName.GIVEN_NAME, "\u30ED\u30A4\u30C9\u0031") + .put(StructuredName.DISPLAY_NAME, + "\u5B89\u85E4\u0020\u30ED\u30A4\u30C9\u0020\u0031") + // ContactStruct should correctly split "SOUND" property into several elements, + // even though VCardParser side does not care it. + .put(StructuredName.PHONETIC_FAMILY_NAME, "\uFF71\uFF9D\uFF84\uFF9E\uFF73") + .put(StructuredName.PHONETIC_GIVEN_NAME, "\uFF9B\uFF72\uFF84\uFF9E\u0031"); + elem.addExpected(StructuredPostal.CONTENT_ITEM_TYPE) + .put(StructuredPostal.POSTCODE, "150-8512") + .put(StructuredPostal.STREET, + "\u6771\u4EAC\u90FD\u6E0B\u8C37\u533A\u685C" + + "\u4E18\u753A\u0032\u0036\u002D\u0031\u30BB" + + "\u30EB\u30EA\u30A2\u30F3\u30BF\u30EF\u30FC" + + "\u0036\u968E") + .put(StructuredPostal.FORMATTED_ADDRESS, + "\u6771\u4EAC\u90FD\u6E0B\u8C37\u533A\u685C" + + "\u4E18\u753A\u0032\u0036\u002D\u0031\u30BB" + + "\u30EB\u30EA\u30A2\u30F3\u30BF\u30EF\u30FC" + + "\u0036\u968E 150-8512") + .put(StructuredPostal.TYPE, StructuredPostal.TYPE_HOME); + elem.addExpected(Note.CONTENT_ITEM_TYPE) + .put(Note.NOTE, "\u30E1\u30E2"); + } + + public void testV21MultipleEntryCase_Parse() { + mVerifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE, R.raw.v21_multiple_entry); + mVerifier.addPropertyNodesVerifierElem() + .addExpectedNodeWithOrder("VERSION", "2.1") + .addExpectedNodeWithOrder("N", "\u5B89\u85E4\u30ED\u30A4\u30C9\u0033;;;;", + Arrays.asList("\u5B89\u85E4\u30ED\u30A4\u30C9\u0033", "", "", "", ""), + null, mContentValuesForSJis, null, null) + .addExpectedNodeWithOrder("SOUND", + "\uFF71\uFF9D\uFF84\uFF9E\uFF73\uFF9B\uFF72\uFF84\uFF9E\u0033;;;;", + null, null, mContentValuesForSJis, + new TypeSet("X-IRMC-N"), null) + .addExpectedNodeWithOrder("TEL", "9", new TypeSet("X-NEC-SECRET")) + .addExpectedNodeWithOrder("TEL", "10", new TypeSet("X-NEC-HOTEL")) + .addExpectedNodeWithOrder("TEL", "11", new TypeSet("X-NEC-SCHOOL")) + .addExpectedNodeWithOrder("TEL", "12", new TypeSet("FAX", "HOME")); + mVerifier.addPropertyNodesVerifierElem() + .addExpectedNodeWithOrder("VERSION", "2.1") + .addExpectedNodeWithOrder("N", "\u5B89\u85E4\u30ED\u30A4\u30C9\u0034;;;;", + Arrays.asList("\u5B89\u85E4\u30ED\u30A4\u30C9\u0034", "", "", "", ""), + null, mContentValuesForSJis, null, null) + .addExpectedNodeWithOrder("SOUND", + "\uFF71\uFF9D\uFF84\uFF9E\uFF73\uFF9B\uFF72\uFF84\uFF9E\u0034;;;;", + null, null, mContentValuesForSJis, + new TypeSet("X-IRMC-N"), null) + .addExpectedNodeWithOrder("TEL", "13", new TypeSet("MODEM")) + .addExpectedNodeWithOrder("TEL", "14", new TypeSet("PAGER")) + .addExpectedNodeWithOrder("TEL", "15", new TypeSet("X-NEC-FAMILY")) + .addExpectedNodeWithOrder("TEL", "16", new TypeSet("X-NEC-GIRL")); + mVerifier.addPropertyNodesVerifierElem() + .addExpectedNodeWithOrder("VERSION", "2.1") + .addExpectedNodeWithOrder("N", "\u5B89\u85E4\u30ED\u30A4\u30C9\u0035;;;;", + Arrays.asList("\u5B89\u85E4\u30ED\u30A4\u30C9\u0035", "", "", "", ""), + null, mContentValuesForSJis, null, null) + .addExpectedNodeWithOrder("SOUND", + "\uFF71\uFF9D\uFF84\uFF9E\uFF73\uFF9B\uFF72\uFF84\uFF9E\u0035;;;;", + null, null, mContentValuesForSJis, + new TypeSet("X-IRMC-N"), null) + .addExpectedNodeWithOrder("TEL", "17", new TypeSet("X-NEC-BOY")) + .addExpectedNodeWithOrder("TEL", "18", new TypeSet("X-NEC-FRIEND")) + .addExpectedNodeWithOrder("TEL", "19", new TypeSet("X-NEC-PHS")) + .addExpectedNodeWithOrder("TEL", "20", new TypeSet("X-NEC-RESTAURANT")); + } + + public void testV21MultipleEntryCase() { + mVerifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE, R.raw.v21_multiple_entry); + ContentValuesVerifierElem elem = mVerifier.addContentValuesVerifierElem(); + elem.addExpected(StructuredName.CONTENT_ITEM_TYPE) + .put(StructuredName.FAMILY_NAME, "\u5B89\u85E4\u30ED\u30A4\u30C9\u0033") + .put(StructuredName.DISPLAY_NAME, "\u5B89\u85E4\u30ED\u30A4\u30C9\u0033") + .put(StructuredName.PHONETIC_GIVEN_NAME, + "\uFF71\uFF9D\uFF84\uFF9E\uFF73\uFF9B\uFF72\uFF84\uFF9E\u0033"); + elem.addExpected(Phone.CONTENT_ITEM_TYPE) + .put(Phone.TYPE, Phone.TYPE_CUSTOM) + .put(Phone.LABEL, "NEC-SECRET") + .put(Phone.NUMBER, "9"); + elem.addExpected(Phone.CONTENT_ITEM_TYPE) + .put(Phone.TYPE, Phone.TYPE_CUSTOM) + .put(Phone.LABEL, "NEC-HOTEL") + .put(Phone.NUMBER, "10"); + elem.addExpected(Phone.CONTENT_ITEM_TYPE) + .put(Phone.TYPE, Phone.TYPE_CUSTOM) + .put(Phone.LABEL, "NEC-SCHOOL") + .put(Phone.NUMBER, "11"); + elem.addExpected(Phone.CONTENT_ITEM_TYPE) + .put(Phone.TYPE, Phone.TYPE_FAX_HOME) + .put(Phone.NUMBER, "12"); + + elem = mVerifier.addContentValuesVerifierElem(); + elem.addExpected(StructuredName.CONTENT_ITEM_TYPE) + .put(StructuredName.FAMILY_NAME, "\u5B89\u85E4\u30ED\u30A4\u30C9\u0034") + .put(StructuredName.DISPLAY_NAME, "\u5B89\u85E4\u30ED\u30A4\u30C9\u0034") + .put(StructuredName.PHONETIC_GIVEN_NAME, + "\uFF71\uFF9D\uFF84\uFF9E\uFF73\uFF9B\uFF72\uFF84\uFF9E\u0034"); + elem.addExpected(Phone.CONTENT_ITEM_TYPE) + .put(Phone.TYPE, Phone.TYPE_CUSTOM) + .put(Phone.LABEL, "MODEM") + .put(Phone.NUMBER, "13"); + elem.addExpected(Phone.CONTENT_ITEM_TYPE) + .put(Phone.TYPE, Phone.TYPE_PAGER) + .put(Phone.NUMBER, "14"); + elem.addExpected(Phone.CONTENT_ITEM_TYPE) + .put(Phone.TYPE, Phone.TYPE_CUSTOM) + .put(Phone.LABEL, "NEC-FAMILY") + .put(Phone.NUMBER, "15"); + elem.addExpected(Phone.CONTENT_ITEM_TYPE) + .put(Phone.TYPE, Phone.TYPE_CUSTOM) + .put(Phone.LABEL, "NEC-GIRL") + .put(Phone.NUMBER, "16"); + + elem = mVerifier.addContentValuesVerifierElem(); + elem.addExpected(StructuredName.CONTENT_ITEM_TYPE) + .put(StructuredName.FAMILY_NAME, "\u5B89\u85E4\u30ED\u30A4\u30C9\u0035") + .put(StructuredName.DISPLAY_NAME, "\u5B89\u85E4\u30ED\u30A4\u30C9\u0035") + .put(StructuredName.PHONETIC_GIVEN_NAME, + "\uFF71\uFF9D\uFF84\uFF9E\uFF73\uFF9B\uFF72\uFF84\uFF9E\u0035"); + elem.addExpected(Phone.CONTENT_ITEM_TYPE) + .put(Phone.TYPE, Phone.TYPE_CUSTOM) + .put(Phone.LABEL, "NEC-BOY") + .put(Phone.NUMBER, "17"); + elem.addExpected(Phone.CONTENT_ITEM_TYPE) + .put(Phone.TYPE, Phone.TYPE_CUSTOM) + .put(Phone.LABEL, "NEC-FRIEND") + .put(Phone.NUMBER, "18"); + elem.addExpected(Phone.CONTENT_ITEM_TYPE) + .put(Phone.TYPE, Phone.TYPE_CUSTOM) + .put(Phone.LABEL, "NEC-PHS") + .put(Phone.NUMBER, "19"); + elem.addExpected(Phone.CONTENT_ITEM_TYPE) + .put(Phone.TYPE, Phone.TYPE_CUSTOM) + .put(Phone.LABEL, "NEC-RESTAURANT") + .put(Phone.NUMBER, "20"); + } + + public void testIgnoreAgentV21_Parse() { + mVerifier.initForImportTest(V21, R.raw.v21_winmo_65); + ContentValues contentValuesForValue = new ContentValues(); + contentValuesForValue.put("VALUE", "DATE"); + mVerifier.addPropertyNodesVerifierElem() + .addExpectedNodeWithOrder("VERSION", "2.1") + .addExpectedNodeWithOrder("N", Arrays.asList("Example", "", "", "", "")) + .addExpectedNodeWithOrder("FN", "Example") + .addExpectedNodeWithOrder("ANNIVERSARY", "20091010", contentValuesForValue) + .addExpectedNodeWithOrder("AGENT", "") + .addExpectedNodeWithOrder("X-CLASS", "PUBLIC") + .addExpectedNodeWithOrder("X-REDUCTION", "") + .addExpectedNodeWithOrder("X-NO", ""); + } + + public void testIgnoreAgentV21() { + mVerifier.initForImportTest(V21, R.raw.v21_winmo_65); + ContentValuesVerifier verifier = new ContentValuesVerifier(); + ContentValuesVerifierElem elem = mVerifier.addContentValuesVerifierElem(); + elem.addExpected(StructuredName.CONTENT_ITEM_TYPE) + .put(StructuredName.FAMILY_NAME, "Example") + .put(StructuredName.DISPLAY_NAME, "Example"); + } + + public void testTolerateInvalidCommentLikeLineV21() { + mVerifier.initForImportTest(V21, R.raw.v21_invalid_comment_line); + ContentValuesVerifierElem elem = mVerifier.addContentValuesVerifierElem(); + elem.addExpected(StructuredName.CONTENT_ITEM_TYPE) + .put(StructuredName.GIVEN_NAME, "Conference Call") + .put(StructuredName.DISPLAY_NAME, "Conference Call"); + elem.addExpected(Note.CONTENT_ITEM_TYPE) + .put(Note.NOTE, "This is an (sharp ->#<- sharp) example. " + + "This message must NOT be ignored."); + } + + public void testPagerV30_Parse() { + mVerifier.initForImportTest(V30, R.raw.v30_comma_separated); + mVerifier.addPropertyNodesVerifierElem() + .addExpectedNodeWithOrder("VERSION", "3.0") + .addExpectedNodeWithOrder("N", Arrays.asList("F", "G", "M", "", "")) + .addExpectedNodeWithOrder("TEL", "6101231234@pagersample.com", + new TypeSet("WORK", "MSG", "PAGER")); + } + + public void testPagerV30() { + mVerifier.initForImportTest(V30, R.raw.v30_comma_separated); + ContentValuesVerifierElem elem = mVerifier.addContentValuesVerifierElem(); + elem.addExpected(StructuredName.CONTENT_ITEM_TYPE) + .put(StructuredName.FAMILY_NAME, "F") + .put(StructuredName.MIDDLE_NAME, "M") + .put(StructuredName.GIVEN_NAME, "G") + .put(StructuredName.DISPLAY_NAME, "G M F"); + elem.addExpected(Phone.CONTENT_ITEM_TYPE) + .put(Phone.TYPE, Phone.TYPE_PAGER) + .put(Phone.NUMBER, "6101231234@pagersample.com"); + } +} diff --git a/vcard/tests/src/com/android/vcard/tests/VCardJapanizationTests.java b/vcard/tests/src/com/android/vcard/tests/VCardJapanizationTests.java new file mode 100644 index 000000000..0d0b9f10a --- /dev/null +++ b/vcard/tests/src/com/android/vcard/tests/VCardJapanizationTests.java @@ -0,0 +1,436 @@ +/* + * 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.vcard.tests; + +import android.content.ContentValues; +import android.provider.ContactsContract.CommonDataKinds.Nickname; +import android.provider.ContactsContract.CommonDataKinds.Note; +import android.provider.ContactsContract.CommonDataKinds.Phone; +import android.provider.ContactsContract.CommonDataKinds.StructuredName; +import android.provider.ContactsContract.CommonDataKinds.StructuredPostal; + +import com.android.vcard.VCardConfig; +import com.android.vcard.tests.test_utils.ContactEntry; +import com.android.vcard.tests.test_utils.ContentValuesBuilder; +import com.android.vcard.tests.test_utils.PropertyNodesVerifierElem; +import com.android.vcard.tests.test_utils.PropertyNodesVerifierElem.TypeSet; + +import java.util.Arrays; + +public class VCardJapanizationTests extends VCardTestsBase { + private void testNameUtf8Common(int vcardType) { + mVerifier.initForExportTest(vcardType); + ContactEntry entry = mVerifier.addInputEntry(); + entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE) + .put(StructuredName.FAMILY_NAME, "\u3075\u308B\u3069") + .put(StructuredName.GIVEN_NAME, "\u3091\u308A\u304B") + .put(StructuredName.MIDDLE_NAME, "B") + .put(StructuredName.PREFIX, "Dr.") + .put(StructuredName.SUFFIX, "Ph.D"); + ContentValues contentValues = + (VCardConfig.isV30(vcardType) ? null : mContentValuesForQPAndUtf8); + mVerifier.addPropertyNodesVerifierElem() + .addExpectedNode("FN", "Dr. \u3075\u308B\u3069 B \u3091\u308A\u304B Ph.D", + contentValues) + .addExpectedNode("N", "\u3075\u308B\u3069;\u3091\u308A\u304B;B;Dr.;Ph.D", + Arrays.asList( + "\u3075\u308B\u3069", "\u3091\u308A\u304B", "B", "Dr.", "Ph.D"), + null, contentValues, null, null); + } + + public void testNameUtf8V21() { + testNameUtf8Common(VCardConfig.VCARD_TYPE_V21_JAPANESE); + } + + public void testNameUtf8V30() { + testNameUtf8Common(VCardConfig.VCARD_TYPE_V30_JAPANESE); + } + + public void testNameShiftJis() { + mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_V30_JAPANESE, "Shift_JIS"); + ContactEntry entry = mVerifier.addInputEntry(); + entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE) + .put(StructuredName.FAMILY_NAME, "\u3075\u308B\u3069") + .put(StructuredName.GIVEN_NAME, "\u3091\u308A\u304B") + .put(StructuredName.MIDDLE_NAME, "B") + .put(StructuredName.PREFIX, "Dr.") + .put(StructuredName.SUFFIX, "Ph.D"); + + mVerifier.addPropertyNodesVerifierElem() + .addExpectedNode("FN", "Dr. \u3075\u308B\u3069 B \u3091\u308A\u304B Ph.D", + mContentValuesForSJis) + .addExpectedNode("N", "\u3075\u308B\u3069;\u3091\u308A\u304B;B;Dr.;Ph.D", + Arrays.asList( + "\u3075\u308B\u3069", "\u3091\u308A\u304B", "B", "Dr.", "Ph.D"), + null, mContentValuesForSJis, null, null); + } + + /** + * DoCoMo phones require all name elements should be in "family name" field. + */ + public void testNameDoCoMo() { + mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO, "Shift_JIS"); + ContactEntry entry = mVerifier.addInputEntry(); + entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE) + .put(StructuredName.FAMILY_NAME, "\u3075\u308B\u3069") + .put(StructuredName.GIVEN_NAME, "\u3091\u308A\u304B") + .put(StructuredName.MIDDLE_NAME, "B") + .put(StructuredName.PREFIX, "Dr.") + .put(StructuredName.SUFFIX, "Ph.D"); + + final String fullName = "Dr. \u3075\u308B\u3069 B \u3091\u308A\u304B Ph.D"; + mVerifier.addPropertyNodesVerifierElem() + .addExpectedNode("N", fullName + ";;;;", + Arrays.asList(fullName, "", "", "", ""), + null, mContentValuesForSJis, null, null) + .addExpectedNode("FN", fullName, mContentValuesForSJis) + .addExpectedNode("SOUND", ";;;;", new TypeSet("X-IRMC-N")) + .addExpectedNode("TEL", "", new TypeSet("HOME")) + .addExpectedNode("EMAIL", "", new TypeSet("HOME")) + .addExpectedNode("ADR", "", new TypeSet("HOME")) + .addExpectedNode("X-CLASS", "PUBLIC") + .addExpectedNode("X-REDUCTION", "") + .addExpectedNode("X-NO", "") + .addExpectedNode("X-DCM-HMN-MODE", ""); + } + + private void testPhoneticNameCommon(int vcardType, String charset) { + mVerifier.initForExportTest(vcardType, charset); + ContactEntry entry = mVerifier.addInputEntry(); + entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE) + .put(StructuredName.PHONETIC_FAMILY_NAME, "\u3084\u307E\u3060") + .put(StructuredName.PHONETIC_MIDDLE_NAME, "\u30DF\u30C9\u30EB\u30CD\u30FC\u30E0") + .put(StructuredName.PHONETIC_GIVEN_NAME, "\u305F\u308D\u3046"); + + final ContentValues contentValues = + ("SHIFT_JIS".equalsIgnoreCase(charset) ? + (VCardConfig.isV30(vcardType) ? mContentValuesForSJis : + mContentValuesForQPAndSJis) : + (VCardConfig.isV30(vcardType) ? null : mContentValuesForQPAndUtf8)); + PropertyNodesVerifierElem elem = mVerifier.addPropertyNodesVerifierElemWithEmptyName(); + elem.addExpectedNode("X-PHONETIC-LAST-NAME", "\u3084\u307E\u3060", + contentValues) + .addExpectedNode("X-PHONETIC-MIDDLE-NAME", + "\u30DF\u30C9\u30EB\u30CD\u30FC\u30E0", + contentValues) + .addExpectedNode("X-PHONETIC-FIRST-NAME", "\u305F\u308D\u3046", + contentValues); + if (VCardConfig.isV30(vcardType)) { + elem.addExpectedNode("SORT-STRING", + "\u3084\u307E\u3060 \u30DF\u30C9\u30EB\u30CD\u30FC\u30E0 \u305F\u308D\u3046", + contentValues); + } + ContentValuesBuilder builder = mVerifier.addContentValuesVerifierElem() + .addExpected(StructuredName.CONTENT_ITEM_TYPE); + builder.put(StructuredName.PHONETIC_FAMILY_NAME, "\u3084\u307E\u3060") + .put(StructuredName.PHONETIC_MIDDLE_NAME, "\u30DF\u30C9\u30EB\u30CD\u30FC\u30E0") + .put(StructuredName.PHONETIC_GIVEN_NAME, "\u305F\u308D\u3046") + .put(StructuredName.DISPLAY_NAME, + "\u3084\u307E\u3060 \u30DF\u30C9\u30EB\u30CD\u30FC\u30E0 " + + "\u305F\u308D\u3046"); + } + + public void testPhoneticNameForJapaneseV21Utf8() { + testPhoneticNameCommon(VCardConfig.VCARD_TYPE_V21_JAPANESE, null); + } + + public void testPhoneticNameForJapaneseV21Sjis() { + testPhoneticNameCommon(VCardConfig.VCARD_TYPE_V21_JAPANESE, "Shift_JIS"); + } + + public void testPhoneticNameForJapaneseV30Utf8() { + testPhoneticNameCommon(VCardConfig.VCARD_TYPE_V30_JAPANESE, null); + } + + public void testPhoneticNameForJapaneseV30SJis() { + testPhoneticNameCommon(VCardConfig.VCARD_TYPE_V30_JAPANESE, "Shift_JIS"); + } + + public void testPhoneticNameForMobileV21_1() { + mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE_MOBILE, "Shift_JIS"); + ContactEntry entry = mVerifier.addInputEntry(); + entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE) + .put(StructuredName.PHONETIC_FAMILY_NAME, "\u3084\u307E\u3060") + .put(StructuredName.PHONETIC_MIDDLE_NAME, "\u30DF\u30C9\u30EB\u30CD\u30FC\u30E0") + .put(StructuredName.PHONETIC_GIVEN_NAME, "\u305F\u308D\u3046"); + + mVerifier.addPropertyNodesVerifierElem() + .addExpectedNode("SOUND", + "\uFF94\uFF8F\uFF80\uFF9E \uFF90\uFF84\uFF9E\uFF99\uFF88\uFF70\uFF91 " + + "\uFF80\uFF9B\uFF73;;;;", + mContentValuesForSJis, new TypeSet("X-IRMC-N")); + ContentValuesBuilder builder = mVerifier.addContentValuesVerifierElem() + .addExpected(StructuredName.CONTENT_ITEM_TYPE); + builder.put(StructuredName.PHONETIC_FAMILY_NAME, "\uFF94\uFF8F\uFF80\uFF9E") + .put(StructuredName.PHONETIC_MIDDLE_NAME, + "\uFF90\uFF84\uFF9E\uFF99\uFF88\uFF70\uFF91") + .put(StructuredName.PHONETIC_GIVEN_NAME, "\uFF80\uFF9B\uFF73") + .put(StructuredName.DISPLAY_NAME, + "\uFF94\uFF8F\uFF80\uFF9E \uFF90\uFF84\uFF9E\uFF99\uFF88\uFF70\uFF91 " + + "\uFF80\uFF9B\uFF73"); + } + + public void testPhoneticNameForMobileV21_2() { + mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE_MOBILE, "Shift_JIS"); + ContactEntry entry = mVerifier.addInputEntry(); + entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE) + .put(StructuredName.PHONETIC_FAMILY_NAME, "\u3084\u307E\u3060") + .put(StructuredName.PHONETIC_GIVEN_NAME, "\u305F\u308D\u3046"); + + mVerifier.addPropertyNodesVerifierElem() + .addExpectedNode("SOUND", "\uFF94\uFF8F\uFF80\uFF9E \uFF80\uFF9B\uFF73;;;;", + mContentValuesForSJis, new TypeSet("X-IRMC-N")); + ContentValuesBuilder builder = mVerifier.addContentValuesVerifierElem() + .addExpected(StructuredName.CONTENT_ITEM_TYPE); + builder.put(StructuredName.PHONETIC_FAMILY_NAME, "\uFF94\uFF8F\uFF80\uFF9E") + .put(StructuredName.PHONETIC_GIVEN_NAME, "\uFF80\uFF9B\uFF73") + .put(StructuredName.DISPLAY_NAME, "\uFF94\uFF8F\uFF80\uFF9E \uFF80\uFF9B\uFF73"); + } + + private void testPostalAddressWithJapaneseCommon(int vcardType, String charset) { + mVerifier.initForExportTest(vcardType, charset); + ContactEntry entry = mVerifier.addInputEntry(); + entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE) + .put(StructuredPostal.POBOX, "\u79C1\u66F8\u7BB107") + .put(StructuredPostal.STREET, "\u96DB\u898B\u6CA2\u6751") + .put(StructuredPostal.CITY, "\u9E7F\u9AA8\u5E02") + .put(StructuredPostal.REGION, "\u00D7\u00D7\u770C") + .put(StructuredPostal.POSTCODE, "494-1313") + .put(StructuredPostal.COUNTRY, "\u65E5\u672C") + .put(StructuredPostal.FORMATTED_ADDRESS, + "\u3053\u3093\u306A\u3068\u3053\u308D\u3092\u898B" + + "\u308B\u306A\u3093\u3066\u6687\u4EBA\u3067\u3059\u304B\uFF1F") + .put(StructuredPostal.TYPE, StructuredPostal.TYPE_CUSTOM) + .put(StructuredPostal.LABEL, "\u304A\u3082\u3061\u304B\u3048\u308A"); + + ContentValues contentValues = ("UTF-8".equalsIgnoreCase(charset) ? + (VCardConfig.isV30(vcardType) ? mContentValuesForSJis : + mContentValuesForQPAndSJis) : + (VCardConfig.isV30(vcardType) ? mContentValuesForUtf8 : + mContentValuesForQPAndUtf8)); + + PropertyNodesVerifierElem elem = mVerifier.addPropertyNodesVerifierElemWithEmptyName(); + // LABEL must be ignored in vCard 2.1. As for vCard 3.0, the current behavior is + // same as that in vCard 3.0, which can be changed in the future. + elem.addExpectedNode("ADR", Arrays.asList("\u79C1\u66F8\u7BB107", + "", "\u96DB\u898B\u6CA2\u6751", "\u9E7F\u9AA8\u5E02", "\u00D7\u00D7\u770C", + "494-1313", "\u65E5\u672C"), + contentValues); + mVerifier.addContentValuesVerifierElem().addExpected(StructuredPostal.CONTENT_ITEM_TYPE) + .put(StructuredPostal.POBOX, "\u79C1\u66F8\u7BB107") + .put(StructuredPostal.STREET, "\u96DB\u898B\u6CA2\u6751") + .put(StructuredPostal.CITY, "\u9E7F\u9AA8\u5E02") + .put(StructuredPostal.REGION, "\u00D7\u00D7\u770C") + .put(StructuredPostal.POSTCODE, "494-1313") + .put(StructuredPostal.COUNTRY, "\u65E5\u672C") + .put(StructuredPostal.FORMATTED_ADDRESS, + "\u65E5\u672C 494-1313 \u00D7\u00D7\u770C \u9E7F\u9AA8\u5E02 " + + "\u96DB\u898B\u6CA2\u6751 " + "\u79C1\u66F8\u7BB107") + .put(StructuredPostal.TYPE, StructuredPostal.TYPE_HOME); + } + public void testPostalAddresswithJapaneseV21() { + testPostalAddressWithJapaneseCommon(VCardConfig.VCARD_TYPE_V21_JAPANESE, "Shift_JIS"); + } + + /** + * Verifies that only one address field is emitted toward DoCoMo phones. + * Prefered type must (should?) be: HOME > WORK > OTHER > CUSTOM + */ + public void testPostalAdrressForDoCoMo_1() { + mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO, "Shift_JIS"); + ContactEntry entry = mVerifier.addInputEntry(); + entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE) + .put(StructuredPostal.TYPE, StructuredPostal.TYPE_WORK) + .put(StructuredPostal.POBOX, "1"); + entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE) + .put(StructuredPostal.TYPE, StructuredPostal.TYPE_OTHER) + .put(StructuredPostal.POBOX, "2"); + entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE) + .put(StructuredPostal.TYPE, StructuredPostal.TYPE_HOME) + .put(StructuredPostal.POBOX, "3"); + entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE) + .put(StructuredPostal.TYPE, StructuredPostal.TYPE_CUSTOM) + .put(StructuredPostal.LABEL, "custom") + .put(StructuredPostal.POBOX, "4"); + + mVerifier.addPropertyNodesVerifierElemWithEmptyName() + .addExpectedNode("TEL", "", new TypeSet("HOME")) + .addExpectedNode("EMAIL", "", new TypeSet("HOME")) + .addExpectedNode("X-CLASS", "PUBLIC") + .addExpectedNode("X-REDUCTION", "") + .addExpectedNode("X-NO", "") + .addExpectedNode("X-DCM-HMN-MODE", "") + .addExpectedNode("ADR", + Arrays.asList("3", "", "", "", "", "", ""), new TypeSet("HOME")); + } + + public void testPostalAdrressForDoCoMo_2() { + mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO, "Shift_JIS"); + ContactEntry entry = mVerifier.addInputEntry(); + entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE) + .put(StructuredPostal.TYPE, StructuredPostal.TYPE_OTHER) + .put(StructuredPostal.POBOX, "1"); + entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE) + .put(StructuredPostal.TYPE, StructuredPostal.TYPE_WORK) + .put(StructuredPostal.POBOX, "2"); + entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE) + .put(StructuredPostal.TYPE, StructuredPostal.TYPE_CUSTOM) + .put(StructuredPostal.LABEL, "custom") + .put(StructuredPostal.POBOX, "3"); + + mVerifier.addPropertyNodesVerifierElemWithEmptyName() + .addExpectedNode("TEL", "", new TypeSet("HOME")) + .addExpectedNode("EMAIL", "", new TypeSet("HOME")) + .addExpectedNode("X-CLASS", "PUBLIC") + .addExpectedNode("X-REDUCTION", "") + .addExpectedNode("X-NO", "") + .addExpectedNode("X-DCM-HMN-MODE", "") + .addExpectedNode("ADR", + Arrays.asList("2", "", "", "", "", "", ""), new TypeSet("WORK")); + } + + public void testPostalAdrressForDoCoMo_3() { + mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO, "Shift_JIS"); + ContactEntry entry = mVerifier.addInputEntry(); + entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE) + .put(StructuredPostal.TYPE, StructuredPostal.TYPE_CUSTOM) + .put(StructuredPostal.LABEL, "custom1") + .put(StructuredPostal.POBOX, "1"); + entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE) + .put(StructuredPostal.TYPE, StructuredPostal.TYPE_OTHER) + .put(StructuredPostal.POBOX, "2"); + entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE) + .put(StructuredPostal.TYPE, StructuredPostal.TYPE_CUSTOM) + .put(StructuredPostal.LABEL, "custom2") + .put(StructuredPostal.POBOX, "3"); + + mVerifier.addPropertyNodesVerifierElemWithEmptyName() + .addExpectedNode("TEL", "", new TypeSet("HOME")) + .addExpectedNode("EMAIL", "", new TypeSet("HOME")) + .addExpectedNode("X-CLASS", "PUBLIC") + .addExpectedNode("X-REDUCTION", "") + .addExpectedNode("X-NO", "") + .addExpectedNode("X-DCM-HMN-MODE", "") + .addExpectedNode("ADR", Arrays.asList("2", "", "", "", "", "", "")); + } + + /** + * Verifies the vCard exporter tolerates null TYPE. + */ + public void testPostalAdrressForDoCoMo_4() { + mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO, "Shift_JIS"); + ContactEntry entry = mVerifier.addInputEntry(); + entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE) + .put(StructuredPostal.POBOX, "1"); + entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE) + .put(StructuredPostal.TYPE, StructuredPostal.TYPE_OTHER) + .put(StructuredPostal.POBOX, "2"); + entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE) + .put(StructuredPostal.TYPE, StructuredPostal.TYPE_HOME) + .put(StructuredPostal.POBOX, "3"); + entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE) + .put(StructuredPostal.TYPE, StructuredPostal.TYPE_WORK) + .put(StructuredPostal.POBOX, "4"); + entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE) + .put(StructuredPostal.POBOX, "5"); + + mVerifier.addPropertyNodesVerifierElemWithEmptyName() + .addExpectedNode("TEL", "", new TypeSet("HOME")) + .addExpectedNode("EMAIL", "", new TypeSet("HOME")) + .addExpectedNode("X-CLASS", "PUBLIC") + .addExpectedNode("X-REDUCTION", "") + .addExpectedNode("X-NO", "") + .addExpectedNode("X-DCM-HMN-MODE", "") + .addExpectedNode("ADR", + Arrays.asList("3", "", "", "", "", "", ""), new TypeSet("HOME")); + } + + private void testJapanesePhoneNumberCommon(int vcardType) { + mVerifier.initForExportTest(vcardType); + ContactEntry entry = mVerifier.addInputEntry(); + entry.addContentValues(Phone.CONTENT_ITEM_TYPE) + .put(Phone.NUMBER, "0312341234") + .put(Phone.TYPE, Phone.TYPE_HOME); + entry.addContentValues(Phone.CONTENT_ITEM_TYPE) + .put(Phone.NUMBER, "09012341234") + .put(Phone.TYPE, Phone.TYPE_MOBILE); + mVerifier.addPropertyNodesVerifierElemWithEmptyName() + .addExpectedNode("TEL", "03-1234-1234", new TypeSet("HOME")) + .addExpectedNode("TEL", "090-1234-1234", new TypeSet("CELL")); + } + + public void testJapanesePhoneNumberV21_1() { + testJapanesePhoneNumberCommon(VCardConfig.VCARD_TYPE_V21_JAPANESE); + } + + public void testJapanesePhoneNumberV30() { + testJapanesePhoneNumberCommon(VCardConfig.VCARD_TYPE_V30_JAPANESE); + } + + public void testJapanesePhoneNumberDoCoMo() { + mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO, "Shift_JIS"); + ContactEntry entry = mVerifier.addInputEntry(); + entry.addContentValues(Phone.CONTENT_ITEM_TYPE) + .put(Phone.NUMBER, "0312341234") + .put(Phone.TYPE, Phone.TYPE_HOME); + entry.addContentValues(Phone.CONTENT_ITEM_TYPE) + .put(Phone.NUMBER, "09012341234") + .put(Phone.TYPE, Phone.TYPE_MOBILE); + mVerifier.addPropertyNodesVerifierElemWithEmptyName() + .addExpectedNode("EMAIL", "", new TypeSet("HOME")) + .addExpectedNode("X-CLASS", "PUBLIC") + .addExpectedNode("X-REDUCTION", "") + .addExpectedNode("X-NO", "") + .addExpectedNode("X-DCM-HMN-MODE", "") + .addExpectedNode("ADR", "", new TypeSet("HOME")) + .addExpectedNode("TEL", "03-1234-1234", new TypeSet("HOME")) + .addExpectedNode("TEL", "090-1234-1234", new TypeSet("CELL")); + } + + public void testNoteDoCoMo() { + mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO, "Shift_JIS"); + ContactEntry entry = mVerifier.addInputEntry(); + entry.addContentValues(Note.CONTENT_ITEM_TYPE) + .put(Note.NOTE, "note1"); + entry.addContentValues(Note.CONTENT_ITEM_TYPE) + .put(Note.NOTE, "note2"); + entry.addContentValues(Note.CONTENT_ITEM_TYPE) + .put(Note.NOTE, "note3"); + + // More than one note fields must be aggregated into one note. + mVerifier.addPropertyNodesVerifierElemWithEmptyName() + .addExpectedNode("TEL", "", new TypeSet("HOME")) + .addExpectedNode("EMAIL", "", new TypeSet("HOME")) + .addExpectedNode("X-CLASS", "PUBLIC") + .addExpectedNode("X-REDUCTION", "") + .addExpectedNode("X-NO", "") + .addExpectedNode("X-DCM-HMN-MODE", "") + .addExpectedNode("ADR", "", new TypeSet("HOME")) + .addExpectedNode("NOTE", "note1\nnote2\nnote3", mContentValuesForQP); + } + + public void testAndroidCustomV21() { + mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_V21_GENERIC); + mVerifier.addInputEntry().addContentValues(Nickname.CONTENT_ITEM_TYPE) + .put(Nickname.NAME, "\u304D\u3083\u30FC\u30A8\u30C3\u30C1\u30FC"); + mVerifier.addPropertyNodesVerifierElemWithEmptyName() + .addExpectedNode("X-ANDROID-CUSTOM", + Arrays.asList(Nickname.CONTENT_ITEM_TYPE, + "\u304D\u3083\u30FC\u30A8\u30C3\u30C1\u30FC", + "", "", "", "", "", "", "", "", "", "", "", "", "", ""), + mContentValuesForQPAndUtf8); + } +} diff --git a/vcard/tests/src/com/android/vcard/tests/VCardTestsBase.java b/vcard/tests/src/com/android/vcard/tests/VCardTestsBase.java new file mode 100644 index 000000000..8998b3caf --- /dev/null +++ b/vcard/tests/src/com/android/vcard/tests/VCardTestsBase.java @@ -0,0 +1,85 @@ +/* + * 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.vcard.tests; + +import android.content.ContentValues; +import android.test.AndroidTestCase; + +import com.android.vcard.VCardConfig; +import com.android.vcard.tests.test_utils.VCardVerifier; + +/** + * BaseClass for vCard unit tests with utility classes. + * Please do not add each unit test here. + */ +/* package */ class VCardTestsBase extends AndroidTestCase { + public static final int V21 = VCardConfig.VCARD_TYPE_V21_GENERIC; + public static final int V30 = VCardConfig.VCARD_TYPE_V30_GENERIC; + + // Do not modify these during tests. + protected final ContentValues mContentValuesForQP; + protected final ContentValues mContentValuesForSJis; + protected final ContentValues mContentValuesForUtf8; + protected final ContentValues mContentValuesForQPAndSJis; + protected final ContentValues mContentValuesForQPAndUtf8; + protected final ContentValues mContentValuesForBase64V21; + protected final ContentValues mContentValuesForBase64V30; + + protected VCardVerifier mVerifier; + private boolean mSkipVerification; + + public VCardTestsBase() { + super(); + // Not using constants in vCard code since it may be wrong. + mContentValuesForQP = new ContentValues(); + mContentValuesForQP.put("ENCODING", "QUOTED-PRINTABLE"); + mContentValuesForSJis = new ContentValues(); + mContentValuesForSJis.put("CHARSET", "SHIFT_JIS"); + mContentValuesForUtf8 = new ContentValues(); + mContentValuesForUtf8.put("CHARSET", "UTF-8"); + mContentValuesForQPAndSJis = new ContentValues(); + mContentValuesForQPAndSJis.put("ENCODING", "QUOTED-PRINTABLE"); + mContentValuesForQPAndSJis.put("CHARSET", "SHIFT_JIS"); + mContentValuesForQPAndUtf8 = new ContentValues(); + mContentValuesForQPAndUtf8.put("ENCODING", "QUOTED-PRINTABLE"); + mContentValuesForQPAndUtf8.put("CHARSET", "UTF-8"); + mContentValuesForBase64V21 = new ContentValues(); + mContentValuesForBase64V21.put("ENCODING", "BASE64"); + mContentValuesForBase64V30 = new ContentValues(); + mContentValuesForBase64V30.put("ENCODING", "b"); + } + + @Override + public void testAndroidTestCaseSetupProperly() { + super.testAndroidTestCaseSetupProperly(); + mSkipVerification = true; + } + + @Override + public void setUp() throws Exception{ + super.setUp(); + mVerifier = new VCardVerifier(this); + mSkipVerification = false; + } + + @Override + public void tearDown() throws Exception { + super.tearDown(); + if (!mSkipVerification) { + mVerifier.verify(); + } + } +} diff --git a/vcard/tests/src/com/android/vcard/tests/VCardUtilsTests.java b/vcard/tests/src/com/android/vcard/tests/VCardUtilsTests.java new file mode 100644 index 000000000..732009a36 --- /dev/null +++ b/vcard/tests/src/com/android/vcard/tests/VCardUtilsTests.java @@ -0,0 +1,84 @@ +/* + * 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.vcard.tests; + +import com.android.vcard.VCardUtils; + +import junit.framework.TestCase; + +import java.util.List; + +public class VCardUtilsTests extends TestCase { + public void testContainsOnlyPrintableAscii() { + assertTrue(VCardUtils.containsOnlyPrintableAscii((String)null)); + assertTrue(VCardUtils.containsOnlyPrintableAscii((String[])null)); + assertTrue(VCardUtils.containsOnlyPrintableAscii((List)null)); + assertTrue(VCardUtils.containsOnlyPrintableAscii("")); + assertTrue(VCardUtils.containsOnlyPrintableAscii("abcdefghijklmnopqrstuvwxyz")); + assertTrue(VCardUtils.containsOnlyPrintableAscii("ABCDEFGHIJKLMNOPQRSTUVWXYZ")); + StringBuilder builder = new StringBuilder(); + for (int i = 0x20; i < 0x7F; i++) { + builder.append((char)i); + } + assertTrue(VCardUtils.containsOnlyPrintableAscii(builder.toString())); + assertTrue(VCardUtils.containsOnlyPrintableAscii("\r\n")); + assertFalse(VCardUtils.containsOnlyPrintableAscii("\u0019")); + assertFalse(VCardUtils.containsOnlyPrintableAscii("\u007F")); + } + + public void testContainsOnlyNonCrLfPrintableAscii() { + assertTrue(VCardUtils.containsOnlyNonCrLfPrintableAscii((String)null)); + assertTrue(VCardUtils.containsOnlyNonCrLfPrintableAscii((String[])null)); + assertTrue(VCardUtils.containsOnlyNonCrLfPrintableAscii((List)null)); + assertTrue(VCardUtils.containsOnlyNonCrLfPrintableAscii("")); + assertTrue(VCardUtils.containsOnlyNonCrLfPrintableAscii("abcdefghijklmnopqrstuvwxyz")); + assertTrue(VCardUtils.containsOnlyNonCrLfPrintableAscii("ABCDEFGHIJKLMNOPQRSTUVWXYZ")); + StringBuilder builder = new StringBuilder(); + for (int i = 0x20; i < 0x7F; i++) { + builder.append((char)i); + } + assertTrue(VCardUtils.containsOnlyNonCrLfPrintableAscii(builder.toString())); + assertFalse(VCardUtils.containsOnlyNonCrLfPrintableAscii("\u0019")); + assertFalse(VCardUtils.containsOnlyNonCrLfPrintableAscii("\u007F")); + assertFalse(VCardUtils.containsOnlyNonCrLfPrintableAscii("\r")); + assertFalse(VCardUtils.containsOnlyNonCrLfPrintableAscii("\n")); + } + + public void testContainsOnlyAlphaDigitHyphen() { + assertTrue(VCardUtils.containsOnlyAlphaDigitHyphen((String)null)); + assertTrue(VCardUtils.containsOnlyAlphaDigitHyphen((String[])null)); + assertTrue(VCardUtils.containsOnlyAlphaDigitHyphen((List)null)); + assertTrue(VCardUtils.containsOnlyAlphaDigitHyphen("")); + assertTrue(VCardUtils.containsOnlyNonCrLfPrintableAscii("abcdefghijklmnopqrstuvwxyz")); + assertTrue(VCardUtils.containsOnlyNonCrLfPrintableAscii("ABCDEFGHIJKLMNOPQRSTUVWXYZ")); + assertTrue(VCardUtils.containsOnlyNonCrLfPrintableAscii("0123456789-")); + for (int i = 0; i < 0x30; i++) { + if (i == 0x2D) { // - + continue; + } + assertFalse(VCardUtils.containsOnlyAlphaDigitHyphen(String.valueOf((char)i))); + } + for (int i = 0x3A; i < 0x41; i++) { + assertFalse(VCardUtils.containsOnlyAlphaDigitHyphen(String.valueOf((char)i))); + } + for (int i = 0x5B; i < 0x61; i++) { + assertFalse(VCardUtils.containsOnlyAlphaDigitHyphen(String.valueOf((char)i))); + } + for (int i = 0x7B; i < 0x100; i++) { + assertFalse(VCardUtils.containsOnlyAlphaDigitHyphen(String.valueOf((char)i))); + } + } +} diff --git a/vcard/tests/src/com/android/vcard/tests/test_utils/ContactEntry.java b/vcard/tests/src/com/android/vcard/tests/test_utils/ContactEntry.java new file mode 100644 index 000000000..dff1f0510 --- /dev/null +++ b/vcard/tests/src/com/android/vcard/tests/test_utils/ContactEntry.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2010 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.vcard.tests.test_utils; + +import android.content.ContentValues; +import android.provider.ContactsContract.Data; + +import java.util.ArrayList; +import java.util.List; + +/** + *

+ * The class representing one contact, which should contain multiple ContentValues like + * StructuredName, Email, etc. + *

+ */ +public final class ContactEntry { + private final List mContentValuesList = new ArrayList(); + + public ContentValuesBuilder addContentValues(String mimeType) { + ContentValues contentValues = new ContentValues(); + contentValues.put(Data.MIMETYPE, mimeType); + mContentValuesList.add(contentValues); + return new ContentValuesBuilder(contentValues); + } + + public List getList() { + return mContentValuesList; + } +} \ No newline at end of file diff --git a/vcard/tests/src/com/android/vcard/tests/test_utils/ContentValuesBuilder.java b/vcard/tests/src/com/android/vcard/tests/test_utils/ContentValuesBuilder.java new file mode 100644 index 000000000..fb53b8f9a --- /dev/null +++ b/vcard/tests/src/com/android/vcard/tests/test_utils/ContentValuesBuilder.java @@ -0,0 +1,80 @@ +/* + * 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.vcard.tests.test_utils; + +import android.content.ContentValues; + +/** + * ContentValues-like class which enables users to chain put() methods and restricts + * the other methods. + */ +public class ContentValuesBuilder { + private final ContentValues mContentValues; + + public ContentValuesBuilder(final ContentValues contentValues) { + mContentValues = contentValues; + } + + public ContentValuesBuilder put(String key, String value) { + mContentValues.put(key, value); + return this; + } + + public ContentValuesBuilder put(String key, Byte value) { + mContentValues.put(key, value); + return this; + } + + public ContentValuesBuilder put(String key, Short value) { + mContentValues.put(key, value); + return this; + } + + public ContentValuesBuilder put(String key, Integer value) { + mContentValues.put(key, value); + return this; + } + + public ContentValuesBuilder put(String key, Long value) { + mContentValues.put(key, value); + return this; + } + + public ContentValuesBuilder put(String key, Float value) { + mContentValues.put(key, value); + return this; + } + + public ContentValuesBuilder put(String key, Double value) { + mContentValues.put(key, value); + return this; + } + + public ContentValuesBuilder put(String key, Boolean value) { + mContentValues.put(key, value); + return this; + } + + public ContentValuesBuilder put(String key, byte[] value) { + mContentValues.put(key, value); + return this; + } + + public ContentValuesBuilder putNull(String key) { + mContentValues.putNull(key); + return this; + } +} diff --git a/vcard/tests/src/com/android/vcard/tests/test_utils/ContentValuesVerifier.java b/vcard/tests/src/com/android/vcard/tests/test_utils/ContentValuesVerifier.java new file mode 100644 index 000000000..7d6db53f3 --- /dev/null +++ b/vcard/tests/src/com/android/vcard/tests/test_utils/ContentValuesVerifier.java @@ -0,0 +1,102 @@ +/* + * 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.vcard.tests.test_utils; + +import android.test.AndroidTestCase; + +import com.android.vcard.VCardConfig; +import com.android.vcard.VCardEntry; +import com.android.vcard.VCardEntryConstructor; +import com.android.vcard.VCardEntryHandler; +import com.android.vcard.VCardParser; +import com.android.vcard.VCardParser_V21; +import com.android.vcard.VCardParser_V30; +import com.android.vcard.exception.VCardException; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +public class ContentValuesVerifier implements VCardEntryHandler { + private AndroidTestCase mTestCase; + private List mContentValuesVerifierElemList = + new ArrayList(); + private int mIndex; + + public ContentValuesVerifierElem addElem(AndroidTestCase androidTestCase) { + mTestCase = androidTestCase; + ContentValuesVerifierElem importVerifier = new ContentValuesVerifierElem(androidTestCase); + mContentValuesVerifierElemList.add(importVerifier); + return importVerifier; + } + + public void verify(int resId, int vCardType) throws IOException, VCardException { + verify(mTestCase.getContext().getResources().openRawResource(resId), vCardType); + } + + public void verify(int resId, int vCardType, final VCardParser vCardParser) + throws IOException, VCardException { + verify(mTestCase.getContext().getResources().openRawResource(resId), + vCardType, vCardParser); + } + + public void verify(InputStream is, int vCardType) throws IOException, VCardException { + final VCardParser vCardParser; + if (VCardConfig.isV30(vCardType)) { + vCardParser = new VCardParser_V30(); + } else { + vCardParser = new VCardParser_V21(); + } + verify(is, vCardType, vCardParser); + } + + public void verify(InputStream is, int vCardType, final VCardParser vCardParser) + throws IOException, VCardException { + VCardEntryConstructor builder = + new VCardEntryConstructor(vCardType, null, null, false); + builder.addEntryHandler(this); + try { + vCardParser.parse(is, builder); + } finally { + if (is != null) { + try { + is.close(); + } catch (IOException e) { + } + } + } + } + + public void onStart() { + for (ContentValuesVerifierElem elem : mContentValuesVerifierElemList) { + elem.onParsingStart(); + } + } + + public void onEntryCreated(VCardEntry entry) { + mTestCase.assertTrue(mIndex < mContentValuesVerifierElemList.size()); + mContentValuesVerifierElemList.get(mIndex).onEntryCreated(entry); + mIndex++; + } + + public void onEnd() { + for (ContentValuesVerifierElem elem : mContentValuesVerifierElemList) { + elem.onParsingEnd(); + elem.verifyResolver(); + } + } +} diff --git a/vcard/tests/src/com/android/vcard/tests/test_utils/ContentValuesVerifierElem.java b/vcard/tests/src/com/android/vcard/tests/test_utils/ContentValuesVerifierElem.java new file mode 100644 index 000000000..ecf4a2b69 --- /dev/null +++ b/vcard/tests/src/com/android/vcard/tests/test_utils/ContentValuesVerifierElem.java @@ -0,0 +1,96 @@ +/* + * 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.vcard.tests.test_utils; + +import android.content.ContentValues; +import android.provider.ContactsContract.Data; +import android.test.AndroidTestCase; + +import com.android.vcard.VCardConfig; +import com.android.vcard.VCardEntry; +import com.android.vcard.VCardEntryCommitter; +import com.android.vcard.VCardEntryConstructor; +import com.android.vcard.VCardEntryHandler; +import com.android.vcard.VCardParser; +import com.android.vcard.VCardParser_V21; +import com.android.vcard.VCardParser_V30; +import com.android.vcard.exception.VCardException; + +import java.io.IOException; +import java.io.InputStream; + +public class ContentValuesVerifierElem { + private final AndroidTestCase mTestCase; + private final ImportTestResolver mResolver; + private final VCardEntryHandler mHandler; + + public ContentValuesVerifierElem(AndroidTestCase androidTestCase) { + mTestCase = androidTestCase; + mResolver = new ImportTestResolver(androidTestCase); + mHandler = new VCardEntryCommitter(mResolver); + } + + public ContentValuesBuilder addExpected(String mimeType) { + ContentValues contentValues = new ContentValues(); + contentValues.put(Data.MIMETYPE, mimeType); + mResolver.addExpectedContentValues(contentValues); + return new ContentValuesBuilder(contentValues); + } + + public void verify(int resId, int vCardType) + throws IOException, VCardException { + verify(mTestCase.getContext().getResources().openRawResource(resId), vCardType); + } + + public void verify(InputStream is, int vCardType) throws IOException, VCardException { + final VCardParser vCardParser; + if (VCardConfig.isV30(vCardType)) { + vCardParser = new VCardParser_V30(); + } else { + vCardParser = new VCardParser_V21(); + } + VCardEntryConstructor builder = + new VCardEntryConstructor(vCardType, null, null, false); + builder.addEntryHandler(mHandler); + try { + vCardParser.parse(is, builder); + } finally { + if (is != null) { + try { + is.close(); + } catch (IOException e) { + } + } + } + verifyResolver(); + } + + public void verifyResolver() { + mResolver.verify(); + } + + public void onParsingStart() { + mHandler.onStart(); + } + + public void onEntryCreated(VCardEntry entry) { + mHandler.onEntryCreated(entry); + } + + public void onParsingEnd() { + mHandler.onEnd(); + } +} diff --git a/vcard/tests/src/com/android/vcard/tests/test_utils/ExportTestProvider.java b/vcard/tests/src/com/android/vcard/tests/test_utils/ExportTestProvider.java new file mode 100644 index 000000000..caedf9dca --- /dev/null +++ b/vcard/tests/src/com/android/vcard/tests/test_utils/ExportTestProvider.java @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2010 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.vcard.tests.test_utils; + +import android.content.ContentResolver; +import android.content.ContentValues; +import android.content.Entity; +import android.content.EntityIterator; +import android.database.Cursor; +import android.net.Uri; +import android.provider.ContactsContract.Contacts; +import android.provider.ContactsContract.Data; +import android.provider.ContactsContract.RawContacts; +import android.test.mock.MockContentProvider; +import android.test.mock.MockCursor; + +import com.android.vcard.VCardComposer; + +import junit.framework.TestCase; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +/* package */ class ExportTestProvider extends MockContentProvider { + final private TestCase mTestCase; + final private ArrayList mContactEntryList = new ArrayList(); + + private static class MockEntityIterator implements EntityIterator { + List mEntityList; + Iterator mIterator; + + public MockEntityIterator(List contentValuesList) { + mEntityList = new ArrayList(); + Entity entity = new Entity(new ContentValues()); + for (ContentValues contentValues : contentValuesList) { + entity.addSubValue(Data.CONTENT_URI, contentValues); + } + mEntityList.add(entity); + mIterator = mEntityList.iterator(); + } + + public boolean hasNext() { + return mIterator.hasNext(); + } + + public Entity next() { + return mIterator.next(); + } + + public void remove() { + throw new UnsupportedOperationException("remove not supported"); + } + + public void reset() { + mIterator = mEntityList.iterator(); + } + + public void close() { + } + } + + public ExportTestProvider(TestCase testCase) { + mTestCase = testCase; + } + + public ContactEntry buildInputEntry() { + ContactEntry contactEntry = new ContactEntry(); + mContactEntryList.add(contactEntry); + return contactEntry; + } + + /** + *

+ * An old method which had existed but was removed from ContentResolver. + *

+ *

+ * We still keep using this method since we don't have a propeer way to know + * which value in the ContentValue corresponds to the entry in Contacts database. + *

+ */ + public EntityIterator queryEntities(Uri uri, + String selection, String[] selectionArgs, String sortOrder) { + mTestCase.assertTrue(uri != null); + mTestCase.assertTrue(ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())); + final String authority = uri.getAuthority(); + mTestCase.assertTrue(RawContacts.CONTENT_URI.getAuthority().equals(authority)); + mTestCase.assertTrue((Data.CONTACT_ID + "=?").equals(selection)); + mTestCase.assertEquals(1, selectionArgs.length); + final int id = Integer.parseInt(selectionArgs[0]); + mTestCase.assertTrue(id >= 0 && id < mContactEntryList.size()); + + return new MockEntityIterator(mContactEntryList.get(id).getList()); + } + + @Override + public Cursor query(Uri uri,String[] projection, + String selection, String[] selectionArgs, String sortOrder) { + mTestCase.assertTrue(VCardComposer.CONTACTS_TEST_CONTENT_URI.equals(uri)); + // In this test, following arguments are not supported. + mTestCase.assertNull(selection); + mTestCase.assertNull(selectionArgs); + mTestCase.assertNull(sortOrder); + + return new MockCursor() { + int mCurrentPosition = -1; + + @Override + public int getCount() { + return mContactEntryList.size(); + } + + @Override + public boolean moveToFirst() { + mCurrentPosition = 0; + return true; + } + + @Override + public boolean moveToNext() { + if (mCurrentPosition < mContactEntryList.size()) { + mCurrentPosition++; + return true; + } else { + return false; + } + } + + @Override + public boolean isBeforeFirst() { + return mCurrentPosition < 0; + } + + @Override + public boolean isAfterLast() { + return mCurrentPosition >= mContactEntryList.size(); + } + + @Override + public int getColumnIndex(String columnName) { + mTestCase.assertEquals(Contacts._ID, columnName); + return 0; + } + + @Override + public int getInt(int columnIndex) { + mTestCase.assertEquals(0, columnIndex); + mTestCase.assertTrue(mCurrentPosition >= 0 + && mCurrentPosition < mContactEntryList.size()); + return mCurrentPosition; + } + + @Override + public String getString(int columnIndex) { + return String.valueOf(getInt(columnIndex)); + } + + @Override + public void close() { + } + }; + } +} \ No newline at end of file diff --git a/vcard/tests/src/com/android/vcard/tests/test_utils/ExportTestResolver.java b/vcard/tests/src/com/android/vcard/tests/test_utils/ExportTestResolver.java new file mode 100644 index 000000000..3cd014ce4 --- /dev/null +++ b/vcard/tests/src/com/android/vcard/tests/test_utils/ExportTestResolver.java @@ -0,0 +1,40 @@ +/* + * 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.vcard.tests.test_utils; + +import android.provider.ContactsContract.RawContacts; +import android.test.mock.MockContentResolver; + +import com.android.vcard.VCardComposer; + +import junit.framework.TestCase; + +/* package */ class ExportTestResolver extends MockContentResolver { + private final ExportTestProvider mProvider; + public ExportTestResolver(TestCase testCase) { + mProvider = new ExportTestProvider(testCase); + addProvider(VCardComposer.VCARD_TEST_AUTHORITY, mProvider); + addProvider(RawContacts.CONTENT_URI.getAuthority(), mProvider); + } + + public ContactEntry addInputContactEntry() { + return mProvider.buildInputEntry(); + } + + public ExportTestProvider getProvider() { + return mProvider; + } +} diff --git a/vcard/tests/src/com/android/vcard/tests/test_utils/ImportTestProvider.java b/vcard/tests/src/com/android/vcard/tests/test_utils/ImportTestProvider.java new file mode 100644 index 000000000..3d7cb60a0 --- /dev/null +++ b/vcard/tests/src/com/android/vcard/tests/test_utils/ImportTestProvider.java @@ -0,0 +1,274 @@ +/* + * Copyright (C) 2010 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.vcard.tests.test_utils; + +import android.content.ContentProviderOperation; +import android.content.ContentProviderResult; +import android.content.ContentValues; +import android.net.Uri; +import android.provider.ContactsContract.Data; +import android.provider.ContactsContract.RawContacts; +import android.provider.ContactsContract.CommonDataKinds.Email; +import android.provider.ContactsContract.CommonDataKinds.Event; +import android.provider.ContactsContract.CommonDataKinds.GroupMembership; +import android.provider.ContactsContract.CommonDataKinds.Im; +import android.provider.ContactsContract.CommonDataKinds.Nickname; +import android.provider.ContactsContract.CommonDataKinds.Note; +import android.provider.ContactsContract.CommonDataKinds.Organization; +import android.provider.ContactsContract.CommonDataKinds.Phone; +import android.provider.ContactsContract.CommonDataKinds.Photo; +import android.provider.ContactsContract.CommonDataKinds.Relation; +import android.provider.ContactsContract.CommonDataKinds.StructuredName; +import android.provider.ContactsContract.CommonDataKinds.StructuredPostal; +import android.provider.ContactsContract.CommonDataKinds.Website; +import android.test.mock.MockContentProvider; +import android.text.TextUtils; +import android.util.Log; + +import junit.framework.TestCase; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.SortedMap; +import java.util.TreeMap; +import java.util.Map.Entry; + +/* package */ class ImportTestProvider extends MockContentProvider { + private static final Set sKnownMimeTypeSet = + new HashSet(Arrays.asList(StructuredName.CONTENT_ITEM_TYPE, + Nickname.CONTENT_ITEM_TYPE, Phone.CONTENT_ITEM_TYPE, + Email.CONTENT_ITEM_TYPE, StructuredPostal.CONTENT_ITEM_TYPE, + Im.CONTENT_ITEM_TYPE, Organization.CONTENT_ITEM_TYPE, + Event.CONTENT_ITEM_TYPE, Photo.CONTENT_ITEM_TYPE, + Note.CONTENT_ITEM_TYPE, Website.CONTENT_ITEM_TYPE, + Relation.CONTENT_ITEM_TYPE, Event.CONTENT_ITEM_TYPE, + GroupMembership.CONTENT_ITEM_TYPE)); + + final Map> mMimeTypeToExpectedContentValues; + + private final TestCase mTestCase; + + public ImportTestProvider(TestCase testCase) { + mTestCase = testCase; + mMimeTypeToExpectedContentValues = + new HashMap>(); + for (String acceptanbleMimeType : sKnownMimeTypeSet) { + // Do not use HashSet since the current implementation changes the content of + // ContentValues after the insertion, which make the result of hashCode() + // changes... + mMimeTypeToExpectedContentValues.put( + acceptanbleMimeType, new ArrayList()); + } + } + + public void addExpectedContentValues(ContentValues expectedContentValues) { + final String mimeType = expectedContentValues.getAsString(Data.MIMETYPE); + if (!sKnownMimeTypeSet.contains(mimeType)) { + mTestCase.fail(String.format( + "Unknow MimeType %s in the test code. Test code should be broken.", + mimeType)); + } + + final Collection contentValuesCollection = + mMimeTypeToExpectedContentValues.get(mimeType); + contentValuesCollection.add(expectedContentValues); + } + + @Override + public ContentProviderResult[] applyBatch( + ArrayList operations) { + if (operations == null) { + mTestCase.fail("There is no operation."); + } + + final int size = operations.size(); + ContentProviderResult[] fakeResultArray = new ContentProviderResult[size]; + for (int i = 0; i < size; i++) { + Uri uri = Uri.withAppendedPath(RawContacts.CONTENT_URI, String.valueOf(i)); + fakeResultArray[i] = new ContentProviderResult(uri); + } + + for (int i = 0; i < size; i++) { + ContentProviderOperation operation = operations.get(i); + ContentValues contentValues = operation.resolveValueBackReferences( + fakeResultArray, i); + } + for (int i = 0; i < size; i++) { + ContentProviderOperation operation = operations.get(i); + ContentValues actualContentValues = operation.resolveValueBackReferences( + fakeResultArray, i); + final Uri uri = operation.getUri(); + if (uri.equals(RawContacts.CONTENT_URI)) { + mTestCase.assertNull(actualContentValues.get(RawContacts.ACCOUNT_NAME)); + mTestCase.assertNull(actualContentValues.get(RawContacts.ACCOUNT_TYPE)); + } else if (uri.equals(Data.CONTENT_URI)) { + final String mimeType = actualContentValues.getAsString(Data.MIMETYPE); + if (!sKnownMimeTypeSet.contains(mimeType)) { + mTestCase.fail(String.format( + "Unknown MimeType %s. Probably added after developing this test", + mimeType)); + } + // Remove data meaningless in this unit tests. + // Specifically, Data.DATA1 - DATA7 are set to null or empty String + // regardless of the input, but it may change depending on how + // resolver-related code handles it. + // Here, we ignore these implementation-dependent specs and + // just check whether vCard importer correctly inserts rellevent data. + Set keyToBeRemoved = new HashSet(); + for (Entry entry : actualContentValues.valueSet()) { + Object value = entry.getValue(); + if (value == null || TextUtils.isEmpty(value.toString())) { + keyToBeRemoved.add(entry.getKey()); + } + } + for (String key: keyToBeRemoved) { + actualContentValues.remove(key); + } + /* for testing + Log.d("@@@", + String.format("MimeType: %s, data: %s", + mimeType, actualContentValues.toString())); */ + // Remove RAW_CONTACT_ID entry just for safety, since we do not care + // how resolver-related code handles the entry in this unit test, + if (actualContentValues.containsKey(Data.RAW_CONTACT_ID)) { + actualContentValues.remove(Data.RAW_CONTACT_ID); + } + final Collection contentValuesCollection = + mMimeTypeToExpectedContentValues.get(mimeType); + if (contentValuesCollection.isEmpty()) { + mTestCase.fail("ContentValues for MimeType " + mimeType + + " is not expected at all (" + actualContentValues + ")"); + } + boolean checked = false; + for (ContentValues expectedContentValues : contentValuesCollection) { + /*for testing + Log.d("@@@", "expected: " + + convertToEasilyReadableString(expectedContentValues)); + Log.d("@@@", "actual : " + + convertToEasilyReadableString(actualContentValues));*/ + if (equalsForContentValues(expectedContentValues, + actualContentValues)) { + mTestCase.assertTrue(contentValuesCollection.remove(expectedContentValues)); + checked = true; + break; + } + } + if (!checked) { + final StringBuilder builder = new StringBuilder(); + builder.append("Unexpected: "); + builder.append(convertToEasilyReadableString(actualContentValues)); + builder.append("\nExpected: "); + for (ContentValues expectedContentValues : contentValuesCollection) { + builder.append(convertToEasilyReadableString(expectedContentValues)); + } + mTestCase.fail(builder.toString()); + } + } else { + mTestCase.fail("Unexpected Uri has come: " + uri); + } + } // for (int i = 0; i < size; i++) { + return fakeResultArray; + } + + public void verify() { + StringBuilder builder = new StringBuilder(); + for (Collection contentValuesCollection : + mMimeTypeToExpectedContentValues.values()) { + for (ContentValues expectedContentValues: contentValuesCollection) { + builder.append(convertToEasilyReadableString(expectedContentValues)); + builder.append("\n"); + } + } + if (builder.length() > 0) { + final String failMsg = + "There is(are) remaining expected ContentValues instance(s): \n" + + builder.toString(); + mTestCase.fail(failMsg); + } + } + + /** + * Utility method to print ContentValues whose content is printed with sorted keys. + */ + private String convertToEasilyReadableString(ContentValues contentValues) { + if (contentValues == null) { + return "null"; + } + String mimeTypeValue = ""; + SortedMap sortedMap = new TreeMap(); + for (Entry entry : contentValues.valueSet()) { + final String key = entry.getKey(); + final Object value = entry.getValue(); + final String valueString = (value != null ? value.toString() : null); + if (Data.MIMETYPE.equals(key)) { + mimeTypeValue = valueString; + } else { + mTestCase.assertNotNull(key); + sortedMap.put(key, valueString); + } + } + StringBuilder builder = new StringBuilder(); + builder.append(Data.MIMETYPE); + builder.append('='); + builder.append(mimeTypeValue); + for (Entry entry : sortedMap.entrySet()) { + final String key = entry.getKey(); + final String value = entry.getValue(); + builder.append(' '); + builder.append(key); + builder.append("=\""); + builder.append(value); + builder.append('"'); + } + return builder.toString(); + } + + private static boolean equalsForContentValues( + ContentValues expected, ContentValues actual) { + if (expected == actual) { + return true; + } else if (expected == null || actual == null || expected.size() != actual.size()) { + return false; + } + + for (Entry entry : expected.valueSet()) { + final String key = entry.getKey(); + final Object value = entry.getValue(); + if (!actual.containsKey(key)) { + return false; + } + if (value instanceof byte[]) { + Object actualValue = actual.get(key); + if (!Arrays.equals((byte[])value, (byte[])actualValue)) { + byte[] e = (byte[])value; + byte[] a = (byte[])actualValue; + Log.d("@@@", "expected (len: " + e.length + "): " + Arrays.toString(e)); + Log.d("@@@", "actual (len: " + a.length + "): " + Arrays.toString(a)); + return false; + } + } else if (!value.equals(actual.get(key))) { + Log.d("@@@", "different."); + return false; + } + } + return true; + } +} \ No newline at end of file diff --git a/vcard/tests/src/com/android/vcard/tests/test_utils/ImportTestResolver.java b/vcard/tests/src/com/android/vcard/tests/test_utils/ImportTestResolver.java new file mode 100644 index 000000000..645e9dbea --- /dev/null +++ b/vcard/tests/src/com/android/vcard/tests/test_utils/ImportTestResolver.java @@ -0,0 +1,57 @@ +/* + * 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.vcard.tests.test_utils; + +import android.content.ContentProviderOperation; +import android.content.ContentProviderResult; +import android.content.ContentValues; +import android.provider.ContactsContract.RawContacts; +import android.test.mock.MockContentResolver; + +import junit.framework.TestCase; + +import java.util.ArrayList; + +/* package */ class ImportTestResolver extends MockContentResolver { + private final ImportTestProvider mProvider; + + public ImportTestResolver(TestCase testCase) { + mProvider = new ImportTestProvider(testCase); + } + + @Override + public ContentProviderResult[] applyBatch(String authority, + ArrayList operations) { + equalsString(authority, RawContacts.CONTENT_URI.toString()); + return mProvider.applyBatch(operations); + } + + public void addExpectedContentValues(ContentValues expectedContentValues) { + mProvider.addExpectedContentValues(expectedContentValues); + } + + public void verify() { + mProvider.verify(); + } + + private static boolean equalsString(String a, String b) { + if (a == null || a.length() == 0) { + return b == null || b.length() == 0; + } else { + return a.equals(b); + } + } +} diff --git a/vcard/tests/src/com/android/vcard/tests/test_utils/LineVerifier.java b/vcard/tests/src/com/android/vcard/tests/test_utils/LineVerifier.java new file mode 100644 index 000000000..d8cfe5b48 --- /dev/null +++ b/vcard/tests/src/com/android/vcard/tests/test_utils/LineVerifier.java @@ -0,0 +1,66 @@ +/* + * 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.vcard.tests.test_utils; + +import com.android.vcard.VCardComposer; + +import android.content.Context; + +import junit.framework.TestCase; + +import java.util.ArrayList; + +public class LineVerifier implements VCardComposer.OneEntryHandler { + private final TestCase mTestCase; + private final ArrayList mLineVerifierElemList; + private int mVCardType; + private int index; + + public LineVerifier(TestCase testCase, int vcardType) { + mTestCase = testCase; + mLineVerifierElemList = new ArrayList(); + mVCardType = vcardType; + } + + public LineVerifierElem addLineVerifierElem() { + LineVerifierElem lineVerifier = new LineVerifierElem(mTestCase, mVCardType); + mLineVerifierElemList.add(lineVerifier); + return lineVerifier; + } + + public void verify(String vcard) { + if (index >= mLineVerifierElemList.size()) { + mTestCase.fail("Insufficient number of LineVerifier (" + index + ")"); + } + + LineVerifierElem lineVerifier = mLineVerifierElemList.get(index); + lineVerifier.verify(vcard); + + index++; + } + + public boolean onEntryCreated(String vcard) { + verify(vcard); + return true; + } + + public boolean onInit(Context context) { + return true; + } + + public void onTerminate() { + } +} diff --git a/vcard/tests/src/com/android/vcard/tests/test_utils/LineVerifierElem.java b/vcard/tests/src/com/android/vcard/tests/test_utils/LineVerifierElem.java new file mode 100644 index 000000000..3ec6ba39b --- /dev/null +++ b/vcard/tests/src/com/android/vcard/tests/test_utils/LineVerifierElem.java @@ -0,0 +1,106 @@ +/* + * 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.vcard.tests.test_utils; + +import android.text.TextUtils; + +import com.android.vcard.VCardConfig; + +import junit.framework.TestCase; + +import java.util.ArrayList; +import java.util.List; + +public class LineVerifierElem { + private final TestCase mTestCase; + private final List mExpectedLineList = new ArrayList(); + private final boolean mIsV30; + + public LineVerifierElem(TestCase testCase, int vcardType) { + mTestCase = testCase; + mIsV30 = VCardConfig.isV30(vcardType); + } + + public LineVerifierElem addExpected(final String line) { + if (!TextUtils.isEmpty(line)) { + mExpectedLineList.add(line); + } + return this; + } + + public void verify(final String vcard) { + final String[] lineArray = vcard.split("\\r?\\n"); + final int length = lineArray.length; + boolean beginExists = false; + boolean endExists = false; + boolean versionExists = false; + + for (int i = 0; i < length; i++) { + final String line = lineArray[i]; + if (TextUtils.isEmpty(line)) { + continue; + } + + if ("BEGIN:VCARD".equalsIgnoreCase(line)) { + if (beginExists) { + mTestCase.fail("Multiple \"BEGIN:VCARD\" line found"); + } else { + beginExists = true; + continue; + } + } else if ("END:VCARD".equalsIgnoreCase(line)) { + if (endExists) { + mTestCase.fail("Multiple \"END:VCARD\" line found"); + } else { + endExists = true; + continue; + } + } else if ((mIsV30 ? "VERSION:3.0" : "VERSION:2.1").equalsIgnoreCase(line)) { + if (versionExists) { + mTestCase.fail("Multiple VERSION line + found"); + } else { + versionExists = true; + continue; + } + } + + if (!beginExists) { + mTestCase.fail("Property other than BEGIN came before BEGIN property: " + + line); + } else if (endExists) { + mTestCase.fail("Property other than END came after END property: " + + line); + } + + final int index = mExpectedLineList.indexOf(line); + if (index >= 0) { + mExpectedLineList.remove(index); + } else { + mTestCase.fail("Unexpected line: " + line); + } + } + + if (!mExpectedLineList.isEmpty()) { + StringBuffer buffer = new StringBuffer(); + for (String expectedLine : mExpectedLineList) { + buffer.append(expectedLine); + buffer.append("\n"); + } + + mTestCase.fail("Expected line(s) not found:" + buffer.toString()); + } + } +} diff --git a/vcard/tests/src/com/android/vcard/tests/test_utils/PropertyNode.java b/vcard/tests/src/com/android/vcard/tests/test_utils/PropertyNode.java new file mode 100644 index 000000000..14c8d6cb2 --- /dev/null +++ b/vcard/tests/src/com/android/vcard/tests/test_utils/PropertyNode.java @@ -0,0 +1,205 @@ +/* + * 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.vcard.tests.test_utils; + +import android.content.ContentValues; + +import com.android.vcard.VCardEntry; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + *

+ * The class representing one property (e.g. "N;ENCODING=UTF-8:family:given:middle:prefix:suffix"). + *

+ *

+ * Previously used in main vCard handling code but now exists only for testing. + *

+ *

+ * Especially useful for testing parser code (VCardParser), since all properties can be + * checked via this class unlike {@link VCardEntry}, which only emits the result of + * interpretation of the content of each vCard. We cannot know whether vCard parser or + * {@link VCardEntry} is wrong without this class. + *

+ */ +public class PropertyNode { + public String propName; + public String propValue; + public List propValue_vector; + + /** Store value as byte[],after decode. + * Used when propValue is encoded by something like BASE64, QUOTED-PRINTABLE, etc. + */ + public byte[] propValue_bytes; + + /** + * param store: key=paramType, value=paramValue + * Note that currently PropertyNode class does not support multiple param-values + * defined in vCard 3.0 (See also RFC 2426). multiple-values are stored as + * one String value like "A,B", not ["A", "B"]... + * TODO: fix this. + */ + public ContentValues paramMap; + + /** Only for TYPE=??? param store. */ + public Set paramMap_TYPE; + + /** Store group values. Used only in VCard. */ + public Set propGroupSet; + + public PropertyNode() { + propName = ""; + propValue = ""; + propValue_vector = new ArrayList(); + paramMap = new ContentValues(); + paramMap_TYPE = new HashSet(); + propGroupSet = new HashSet(); + } + + public PropertyNode( + String propName, String propValue, List propValue_vector, + byte[] propValue_bytes, ContentValues paramMap, Set paramMap_TYPE, + Set propGroupSet) { + if (propName != null) { + this.propName = propName; + } else { + this.propName = ""; + } + if (propValue != null) { + this.propValue = propValue; + } else { + this.propValue = ""; + } + if (propValue_vector != null) { + this.propValue_vector = propValue_vector; + } else { + this.propValue_vector = new ArrayList(); + } + this.propValue_bytes = propValue_bytes; + if (paramMap != null) { + this.paramMap = paramMap; + } else { + this.paramMap = new ContentValues(); + } + if (paramMap_TYPE != null) { + this.paramMap_TYPE = paramMap_TYPE; + } else { + this.paramMap_TYPE = new HashSet(); + } + if (propGroupSet != null) { + this.propGroupSet = propGroupSet; + } else { + this.propGroupSet = new HashSet(); + } + } + + @Override + public int hashCode() { + // vCard may contain more than one same line in one entry, while HashSet or any other + // library which utilize hashCode() does not honor that, so intentionally throw an + // Exception. + throw new UnsupportedOperationException( + "PropertyNode does not provide hashCode() implementation intentionally."); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof PropertyNode)) { + return false; + } + + PropertyNode node = (PropertyNode)obj; + + if (propName == null || !propName.equals(node.propName)) { + return false; + } else if (!paramMap_TYPE.equals(node.paramMap_TYPE)) { + return false; + } else if (!paramMap_TYPE.equals(node.paramMap_TYPE)) { + return false; + } else if (!propGroupSet.equals(node.propGroupSet)) { + return false; + } + + if (propValue_bytes != null && Arrays.equals(propValue_bytes, node.propValue_bytes)) { + return true; + } else { + if (!propValue.equals(node.propValue)) { + return false; + } + + // The value in propValue_vector is not decoded even if it should be + // decoded by BASE64 or QUOTED-PRINTABLE. When the size of propValue_vector + // is 1, the encoded value is stored in propValue, so we do not have to + // check it. + return (propValue_vector.equals(node.propValue_vector) || + propValue_vector.size() == 1 || + node.propValue_vector.size() == 1); + } + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("propName: "); + builder.append(propName); + builder.append(", paramMap: "); + builder.append(paramMap.toString()); + builder.append(", paramMap_TYPE: ["); + boolean first = true; + for (String elem : paramMap_TYPE) { + if (first) { + first = false; + } else { + builder.append(", "); + } + builder.append('"'); + builder.append(elem); + builder.append('"'); + } + builder.append("]"); + if (!propGroupSet.isEmpty()) { + builder.append(", propGroupSet: ["); + first = true; + for (String elem : propGroupSet) { + if (first) { + first = false; + } else { + builder.append(", "); + } + builder.append('"'); + builder.append(elem); + builder.append('"'); + } + builder.append("]"); + } + if (propValue_vector != null && propValue_vector.size() > 1) { + builder.append(", propValue_vector size: "); + builder.append(propValue_vector.size()); + } + if (propValue_bytes != null) { + builder.append(", propValue_bytes size: "); + builder.append(propValue_bytes.length); + } + builder.append(", propValue: \""); + builder.append(propValue); + builder.append("\""); + return builder.toString(); + } +} diff --git a/vcard/tests/src/com/android/vcard/tests/test_utils/PropertyNodesVerifier.java b/vcard/tests/src/com/android/vcard/tests/test_utils/PropertyNodesVerifier.java new file mode 100644 index 000000000..de33a36a4 --- /dev/null +++ b/vcard/tests/src/com/android/vcard/tests/test_utils/PropertyNodesVerifier.java @@ -0,0 +1,91 @@ +/* + * 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.vcard.tests.test_utils; + +import android.test.AndroidTestCase; + +import com.android.vcard.VCardConfig; +import com.android.vcard.VCardParser; +import com.android.vcard.VCardParser_V21; +import com.android.vcard.VCardParser_V30; +import com.android.vcard.exception.VCardException; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +public class PropertyNodesVerifier extends VNodeBuilder { + private final List mPropertyNodesVerifierElemList; + private final AndroidTestCase mAndroidTestCase; + private int mIndex; + + public PropertyNodesVerifier(AndroidTestCase testCase) { + super(); + mPropertyNodesVerifierElemList = new ArrayList(); + mAndroidTestCase = testCase; + } + + public PropertyNodesVerifierElem addPropertyNodesVerifierElem() { + PropertyNodesVerifierElem elem = new PropertyNodesVerifierElem(mAndroidTestCase); + mPropertyNodesVerifierElemList.add(elem); + return elem; + } + + public void verify(int resId, int vCardType) + throws IOException, VCardException { + verify(mAndroidTestCase.getContext().getResources().openRawResource(resId), vCardType); + } + + public void verify(int resId, int vCardType, final VCardParser vCardParser) + throws IOException, VCardException { + verify(mAndroidTestCase.getContext().getResources().openRawResource(resId), + vCardType, vCardParser); + } + + public void verify(InputStream is, int vCardType) throws IOException, VCardException { + final VCardParser vCardParser; + if (VCardConfig.isV30(vCardType)) { + vCardParser = new VCardParser_V30(); + } else { + vCardParser = new VCardParser_V21(); + } + verify(is, vCardType, vCardParser); + } + + public void verify(InputStream is, int vCardType, final VCardParser vCardParser) + throws IOException, VCardException { + try { + vCardParser.parse(is, this); + } finally { + if (is != null) { + try { + is.close(); + } catch (IOException e) { + } + } + } + } + + @Override + public void endEntry() { + super.endEntry(); + mAndroidTestCase.assertTrue(mIndex < mPropertyNodesVerifierElemList.size()); + mAndroidTestCase.assertTrue(mIndex < vNodeList.size()); + mPropertyNodesVerifierElemList.get(mIndex).verify(vNodeList.get(mIndex)); + mIndex++; + } +} diff --git a/vcard/tests/src/com/android/vcard/tests/test_utils/PropertyNodesVerifierElem.java b/vcard/tests/src/com/android/vcard/tests/test_utils/PropertyNodesVerifierElem.java new file mode 100644 index 000000000..6eb84983e --- /dev/null +++ b/vcard/tests/src/com/android/vcard/tests/test_utils/PropertyNodesVerifierElem.java @@ -0,0 +1,317 @@ +/* + * Copyright (C) 2010 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.vcard.tests.test_utils; + +import android.content.ContentValues; + +import junit.framework.TestCase; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; + +/** + * Utility class which verifies input VNode. + * + * This class first checks whether each propertyNode in the VNode is in the + * "ordered expected property list". + * If the node does not exist in the "ordered list", the class refers to + * "unorderd expected property set" and checks the node is expected somewhere. + */ +public class PropertyNodesVerifierElem { + public static class TypeSet extends HashSet { + public TypeSet(String ... array) { + super(Arrays.asList(array)); + } + } + + public static class GroupSet extends HashSet { + public GroupSet(String ... array) { + super(Arrays.asList(array)); + } + } + + private final HashMap> mOrderedNodeMap; + // Intentionally use ArrayList instead of Set, assuming there may be more than one + // exactly same objects. + private final ArrayList mUnorderedNodeList; + private final TestCase mTestCase; + + public PropertyNodesVerifierElem(TestCase testCase) { + mOrderedNodeMap = new HashMap>(); + mUnorderedNodeList = new ArrayList(); + mTestCase = testCase; + } + + // WithOrder + + public PropertyNodesVerifierElem addExpectedNodeWithOrder(String propName, String propValue) { + return addExpectedNodeWithOrder(propName, propValue, null, null, null, null, null); + } + + public PropertyNodesVerifierElem addExpectedNodeWithOrder( + String propName, String propValue, ContentValues contentValues) { + return addExpectedNodeWithOrder(propName, propValue, null, + null, contentValues, null, null); + } + + public PropertyNodesVerifierElem addExpectedNodeWithOrder( + String propName, List propValueList, ContentValues contentValues) { + return addExpectedNodeWithOrder(propName, null, propValueList, + null, contentValues, null, null); + } + + public PropertyNodesVerifierElem addExpectedNodeWithOrder( + String propName, String propValue, List propValueList) { + return addExpectedNodeWithOrder(propName, propValue, propValueList, null, + null, null, null); + } + + public PropertyNodesVerifierElem addExpectedNodeWithOrder( + String propName, List propValueList) { + final String propValue = concatinateListWithSemiColon(propValueList); + return addExpectedNodeWithOrder(propName, propValue.toString(), propValueList, + null, null, null, null); + } + + public PropertyNodesVerifierElem addExpectedNodeWithOrder(String propName, String propValue, + TypeSet paramMap_TYPE) { + return addExpectedNodeWithOrder(propName, propValue, null, + null, null, paramMap_TYPE, null); + } + + public PropertyNodesVerifierElem addExpectedNodeWithOrder(String propName, + List propValueList, TypeSet paramMap_TYPE) { + return addExpectedNodeWithOrder(propName, null, propValueList, null, null, + paramMap_TYPE, null); + } + + public PropertyNodesVerifierElem addExpectedNodeWithOrder(String propName, String propValue, + ContentValues paramMap, TypeSet paramMap_TYPE) { + return addExpectedNodeWithOrder(propName, propValue, null, null, + paramMap, paramMap_TYPE, null); + } + + public PropertyNodesVerifierElem addExpectedNodeWithOrder(String propName, String propValue, + List propValueList, TypeSet paramMap_TYPE) { + return addExpectedNodeWithOrder(propName, propValue, propValueList, null, null, + paramMap_TYPE, null); + } + + public PropertyNodesVerifierElem addExpectedNodeWithOrder(String propName, String propValue, + List propValueList, byte[] propValue_bytes, + ContentValues paramMap, TypeSet paramMap_TYPE, GroupSet propGroupSet) { + if (propValue == null && propValueList != null) { + propValue = concatinateListWithSemiColon(propValueList); + } + PropertyNode propertyNode = new PropertyNode(propName, + propValue, propValueList, propValue_bytes, + paramMap, paramMap_TYPE, propGroupSet); + List expectedNodeList = mOrderedNodeMap.get(propName); + if (expectedNodeList == null) { + expectedNodeList = new ArrayList(); + mOrderedNodeMap.put(propName, expectedNodeList); + } + expectedNodeList.add(propertyNode); + return this; + } + + // WithoutOrder + + public PropertyNodesVerifierElem addExpectedNode(String propName, String propValue) { + return addExpectedNode(propName, propValue, null, null, null, null, null); + } + + public PropertyNodesVerifierElem addExpectedNode(String propName, String propValue, + ContentValues contentValues) { + return addExpectedNode(propName, propValue, null, null, contentValues, null, null); + } + + public PropertyNodesVerifierElem addExpectedNode(String propName, + List propValueList, ContentValues contentValues) { + return addExpectedNode(propName, null, + propValueList, null, contentValues, null, null); + } + + public PropertyNodesVerifierElem addExpectedNode(String propName, String propValue, + List propValueList) { + return addExpectedNode(propName, propValue, propValueList, null, null, null, null); + } + + public PropertyNodesVerifierElem addExpectedNode(String propName, + List propValueList) { + return addExpectedNode(propName, null, propValueList, + null, null, null, null); + } + + public PropertyNodesVerifierElem addExpectedNode(String propName, String propValue, + TypeSet paramMap_TYPE) { + return addExpectedNode(propName, propValue, null, null, null, paramMap_TYPE, null); + } + + public PropertyNodesVerifierElem addExpectedNode(String propName, + List propValueList, TypeSet paramMap_TYPE) { + final String propValue = concatinateListWithSemiColon(propValueList); + return addExpectedNode(propName, propValue, propValueList, null, null, + paramMap_TYPE, null); + } + + public PropertyNodesVerifierElem addExpectedNode(String propName, String propValue, + List propValueList, TypeSet paramMap_TYPE) { + return addExpectedNode(propName, propValue, propValueList, null, null, + paramMap_TYPE, null); + } + + public PropertyNodesVerifierElem addExpectedNode(String propName, String propValue, + ContentValues paramMap, TypeSet paramMap_TYPE) { + return addExpectedNode(propName, propValue, null, null, + paramMap, paramMap_TYPE, null); + } + + public PropertyNodesVerifierElem addExpectedNode(String propName, String propValue, + List propValueList, byte[] propValue_bytes, + ContentValues paramMap, TypeSet paramMap_TYPE, GroupSet propGroupSet) { + if (propValue == null && propValueList != null) { + propValue = concatinateListWithSemiColon(propValueList); + } + mUnorderedNodeList.add(new PropertyNode(propName, propValue, + propValueList, propValue_bytes, paramMap, paramMap_TYPE, propGroupSet)); + return this; + } + + public void verify(VNode vnode) { + for (PropertyNode actualNode : vnode.propList) { + verifyNode(actualNode.propName, actualNode); + } + if (!mOrderedNodeMap.isEmpty() || !mUnorderedNodeList.isEmpty()) { + List expectedProps = new ArrayList(); + for (List nodes : mOrderedNodeMap.values()) { + for (PropertyNode node : nodes) { + if (!expectedProps.contains(node.propName)) { + expectedProps.add(node.propName); + } + } + } + for (PropertyNode node : mUnorderedNodeList) { + if (!expectedProps.contains(node.propName)) { + expectedProps.add(node.propName); + } + } + mTestCase.fail("Expected property " + Arrays.toString(expectedProps.toArray()) + + " was not found."); + } + } + + private void verifyNode(final String propName, final PropertyNode actualNode) { + List expectedNodeList = mOrderedNodeMap.get(propName); + final int size = (expectedNodeList != null ? expectedNodeList.size() : 0); + if (size > 0) { + for (int i = 0; i < size; i++) { + PropertyNode expectedNode = expectedNodeList.get(i); + List expectedButDifferentValueList = new ArrayList(); + if (expectedNode.propName.equals(propName)) { + if (expectedNode.equals(actualNode)) { + expectedNodeList.remove(i); + if (expectedNodeList.size() == 0) { + mOrderedNodeMap.remove(propName); + } + return; + } else { + expectedButDifferentValueList.add(expectedNode); + } + } + + // "actualNode" is not in ordered expected list. + // Try looking over unordered expected list. + if (tryFoundExpectedNodeFromUnorderedList(actualNode, + expectedButDifferentValueList)) { + return; + } + + if (!expectedButDifferentValueList.isEmpty()) { + // Same propName exists but with different value(s). + failWithExpectedNodeList(propName, actualNode, + expectedButDifferentValueList); + } else { + // There's no expected node with same propName. + mTestCase.fail("Unexpected property \"" + propName + "\" exists."); + } + } + } else { + List expectedButDifferentValueList = + new ArrayList(); + if (tryFoundExpectedNodeFromUnorderedList(actualNode, expectedButDifferentValueList)) { + return; + } else { + if (!expectedButDifferentValueList.isEmpty()) { + // Same propName exists but with different value(s). + failWithExpectedNodeList(propName, actualNode, + expectedButDifferentValueList); + } else { + // There's no expected node with same propName. + mTestCase.fail("Unexpected property \"" + propName + "\" exists."); + } + } + } + } + + private String concatinateListWithSemiColon(List array) { + StringBuffer buffer = new StringBuffer(); + boolean first = true; + for (String propValueElem : array) { + if (first) { + first = false; + } else { + buffer.append(';'); + } + buffer.append(propValueElem); + } + + return buffer.toString(); + } + + private boolean tryFoundExpectedNodeFromUnorderedList(PropertyNode actualNode, + List expectedButDifferentValueList) { + final String propName = actualNode.propName; + int unorderedListSize = mUnorderedNodeList.size(); + for (int i = 0; i < unorderedListSize; i++) { + PropertyNode unorderedExpectedNode = mUnorderedNodeList.get(i); + if (unorderedExpectedNode.propName.equals(propName)) { + if (unorderedExpectedNode.equals(actualNode)) { + mUnorderedNodeList.remove(i); + return true; + } + expectedButDifferentValueList.add(unorderedExpectedNode); + } + } + return false; + } + + private void failWithExpectedNodeList(String propName, PropertyNode actualNode, + List expectedNodeList) { + StringBuilder builder = new StringBuilder(); + for (PropertyNode expectedNode : expectedNodeList) { + builder.append("expected: "); + builder.append(expectedNode.toString()); + builder.append("\n"); + } + mTestCase.fail("Property \"" + propName + "\" has wrong value.\n" + + builder.toString() + + " actual: " + actualNode.toString()); + } +} diff --git a/vcard/tests/src/com/android/vcard/tests/test_utils/VCardVerifier.java b/vcard/tests/src/com/android/vcard/tests/test_utils/VCardVerifier.java new file mode 100644 index 000000000..87d82d202 --- /dev/null +++ b/vcard/tests/src/com/android/vcard/tests/test_utils/VCardVerifier.java @@ -0,0 +1,339 @@ +/* + * 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.vcard.tests.test_utils; + +import android.content.ContentResolver; +import android.content.Context; +import android.content.EntityIterator; +import android.net.Uri; +import android.test.AndroidTestCase; +import android.test.mock.MockContext; +import android.text.TextUtils; +import android.util.Log; + +import com.android.vcard.VCardComposer; +import com.android.vcard.VCardConfig; +import com.android.vcard.VCardEntryConstructor; +import com.android.vcard.VCardInterpreter; +import com.android.vcard.VCardInterpreterCollection; +import com.android.vcard.VCardParser; +import com.android.vcard.VCardParser_V21; +import com.android.vcard.VCardParser_V30; +import com.android.vcard.exception.VCardException; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Method; +import java.util.Arrays; + +/** + *

+ * The class lets users checks that given expected vCard data are same as given actual vCard data. + * Able to verify both vCard importer/exporter. + *

+ *

+ * First a user has to initialize the object by calling either + * {@link #initForImportTest(int, int)} or {@link #initForExportTest(int)}. + * "Round trip test" (import -> export -> import, or export -> import -> export) is not supported. + *

+ */ +public class VCardVerifier { + private static final String LOG_TAG = "VCardVerifier"; + + private static class CustomMockContext extends MockContext { + final ContentResolver mResolver; + public CustomMockContext(ContentResolver resolver) { + mResolver = resolver; + } + + @Override + public ContentResolver getContentResolver() { + return mResolver; + } + } + + private class VCardVerifierInternal implements VCardComposer.OneEntryHandler { + public boolean onInit(Context context) { + return true; + } + public boolean onEntryCreated(String vcard) { + verifyOneVCard(vcard); + return true; + } + public void onTerminate() { + } + } + + private final AndroidTestCase mTestCase; + private final VCardVerifierInternal mVCardVerifierInternal; + private int mVCardType; + private boolean mIsV30; + private boolean mIsDoCoMo; + + // Only one of them must be non-empty. + private ExportTestResolver mExportTestResolver; + private InputStream mInputStream; + + // To allow duplication, use list instead of set. + // When null, we don't need to do the verification. + private PropertyNodesVerifier mPropertyNodesVerifier; + private LineVerifier mLineVerifier; + private ContentValuesVerifier mContentValuesVerifier; + private boolean mInitialized; + private boolean mVerified = false; + private String mCharset; + + // Called by VCardTestsBase + public VCardVerifier(AndroidTestCase testCase) { + mTestCase = testCase; + mVCardVerifierInternal = new VCardVerifierInternal(); + mExportTestResolver = null; + mInputStream = null; + mInitialized = false; + mVerified = false; + } + + // Should be called at the beginning of each import test. + public void initForImportTest(int vcardType, int resId) { + if (mInitialized) { + mTestCase.fail("Already initialized"); + } + mVCardType = vcardType; + mIsV30 = VCardConfig.isV30(vcardType); + mIsDoCoMo = VCardConfig.isDoCoMo(vcardType); + setInputResourceId(resId); + mInitialized = true; + } + + // Should be called at the beginning of each export test. + public void initForExportTest(int vcardType) { + initForExportTest(vcardType, "UTF-8"); + } + + public void initForExportTest(int vcardType, String charset) { + if (mInitialized) { + mTestCase.fail("Already initialized"); + } + mExportTestResolver = new ExportTestResolver(mTestCase); + mVCardType = vcardType; + mIsV30 = VCardConfig.isV30(vcardType); + mIsDoCoMo = VCardConfig.isDoCoMo(vcardType); + mInitialized = true; + if (TextUtils.isEmpty(charset)) { + mCharset = "UTF-8"; + } else { + mCharset = charset; + } + } + + private void setInputResourceId(int resId) { + InputStream inputStream = mTestCase.getContext().getResources().openRawResource(resId); + if (inputStream == null) { + mTestCase.fail("Wrong resId: " + resId); + } + setInputStream(inputStream); + } + + private void setInputStream(InputStream inputStream) { + if (mExportTestResolver != null) { + mTestCase.fail("addInputEntry() is called."); + } else if (mInputStream != null) { + mTestCase.fail("InputStream is already set"); + } + mInputStream = inputStream; + } + + public ContactEntry addInputEntry() { + if (!mInitialized) { + mTestCase.fail("Not initialized"); + } + if (mInputStream != null) { + mTestCase.fail("setInputStream is called"); + } + return mExportTestResolver.addInputContactEntry(); + } + + public PropertyNodesVerifierElem addPropertyNodesVerifierElem() { + if (!mInitialized) { + mTestCase.fail("Not initialized"); + } + if (mPropertyNodesVerifier == null) { + mPropertyNodesVerifier = new PropertyNodesVerifier(mTestCase); + } + PropertyNodesVerifierElem elem = + mPropertyNodesVerifier.addPropertyNodesVerifierElem(); + elem.addExpectedNodeWithOrder("VERSION", (mIsV30 ? "3.0" : "2.1")); + + return elem; + } + + public PropertyNodesVerifierElem addPropertyNodesVerifierElemWithEmptyName() { + if (!mInitialized) { + mTestCase.fail("Not initialized"); + } + PropertyNodesVerifierElem elem = addPropertyNodesVerifierElem(); + if (mIsV30) { + elem.addExpectedNodeWithOrder("N", "").addExpectedNodeWithOrder("FN", ""); + } else if (mIsDoCoMo) { + elem.addExpectedNodeWithOrder("N", ""); + } + return elem; + } + + public LineVerifierElem addLineVerifierElem() { + if (!mInitialized) { + mTestCase.fail("Not initialized"); + } + if (mLineVerifier == null) { + mLineVerifier = new LineVerifier(mTestCase, mVCardType); + } + return mLineVerifier.addLineVerifierElem(); + } + + public ContentValuesVerifierElem addContentValuesVerifierElem() { + if (!mInitialized) { + mTestCase.fail("Not initialized"); + } + if (mContentValuesVerifier == null) { + mContentValuesVerifier = new ContentValuesVerifier(); + } + + return mContentValuesVerifier.addElem(mTestCase); + } + + private void verifyOneVCard(final String vcard) { + Log.d(LOG_TAG, vcard); + final VCardInterpreter builder; + if (mContentValuesVerifier != null) { + final VNodeBuilder vnodeBuilder = mPropertyNodesVerifier; + final VCardEntryConstructor vcardDataBuilder = + new VCardEntryConstructor(mVCardType); + vcardDataBuilder.addEntryHandler(mContentValuesVerifier); + if (mPropertyNodesVerifier != null) { + builder = new VCardInterpreterCollection(Arrays.asList( + mPropertyNodesVerifier, vcardDataBuilder)); + } else { + builder = vnodeBuilder; + } + } else { + if (mPropertyNodesVerifier != null) { + builder = mPropertyNodesVerifier; + } else { + return; + } + } + + InputStream is = null; + try { + // Note: we must not specify charset toward vCard parsers. This code checks whether + // those parsers are able to encode given binary without any extra information for + // charset. + final VCardParser parser = (mIsV30 ? + new VCardParser_V30(mVCardType) : new VCardParser_V21(mVCardType)); + is = new ByteArrayInputStream(vcard.getBytes(mCharset)); + parser.parse(is, builder); + } catch (IOException e) { + mTestCase.fail("Unexpected IOException: " + e.getMessage()); + } catch (VCardException e) { + mTestCase.fail("Unexpected VCardException: " + e.getMessage()); + } finally { + if (is != null) { + try { + is.close(); + } catch (IOException e) { + mTestCase.fail("Unexpected IOException: " + e.getMessage()); + } + } + } + } + + public void verify() { + if (!mInitialized) { + mTestCase.fail("Not initialized."); + } + if (mVerified) { + mTestCase.fail("verify() was called twice."); + } + if (mInputStream != null) { + try { + verifyForImportTest(); + } catch (IOException e) { + mTestCase.fail("IOException was thrown: " + e.getMessage()); + } catch (VCardException e) { + mTestCase.fail("VCardException was thrown: " + e.getMessage()); + } + } else if (mExportTestResolver != null){ + verifyForExportTest(); + } else { + mTestCase.fail("No input is determined"); + } + mVerified = true; + } + + private void verifyForImportTest() throws IOException, VCardException { + if (mLineVerifier != null) { + mTestCase.fail("Not supported now."); + } + if (mContentValuesVerifier != null) { + mContentValuesVerifier.verify(mInputStream, mVCardType); + } + } + + public static EntityIterator mockGetEntityIteratorMethod( + final ContentResolver resolver, + final Uri uri, final String selection, + final String[] selectionArgs, final String sortOrder) { + if (ExportTestResolver.class.equals(resolver.getClass())) { + return ((ExportTestResolver)resolver).getProvider().queryEntities( + uri, selection, selectionArgs, sortOrder); + } + + Log.e(LOG_TAG, "Unexpected provider given."); + return null; + } + + private Method getMockGetEntityIteratorMethod() + throws SecurityException, NoSuchMethodException { + return this.getClass().getMethod("mockGetEntityIteratorMethod", + ContentResolver.class, Uri.class, String.class, String[].class, String.class); + } + + private void verifyForExportTest() { + final VCardComposer composer = + new VCardComposer(new CustomMockContext(mExportTestResolver), mVCardType, mCharset); + composer.addHandler(mLineVerifier); + composer.addHandler(mVCardVerifierInternal); + if (!composer.init(VCardComposer.CONTACTS_TEST_CONTENT_URI, null, null, null)) { + mTestCase.fail("init() failed. Reason: " + composer.getErrorReason()); + } + mTestCase.assertFalse(composer.isAfterLast()); + try { + while (!composer.isAfterLast()) { + try { + final Method mockGetEntityIteratorMethod = getMockGetEntityIteratorMethod(); + mTestCase.assertNotNull(mockGetEntityIteratorMethod); + mTestCase.assertTrue(composer.createOneEntry(mockGetEntityIteratorMethod)); + } catch (Exception e) { + e.printStackTrace(); + mTestCase.fail(); + } + } + } finally { + composer.terminate(); + } + } +} diff --git a/vcard/tests/src/com/android/vcard/tests/test_utils/VNode.java b/vcard/tests/src/com/android/vcard/tests/test_utils/VNode.java new file mode 100644 index 000000000..2ca762b0a --- /dev/null +++ b/vcard/tests/src/com/android/vcard/tests/test_utils/VNode.java @@ -0,0 +1,30 @@ +/* + * 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.vcard.tests.test_utils; + +import java.util.ArrayList; + +/** + * Previously used in main vCard handling code but now exists only for testing. + */ +public class VNode { + public String VName; + + public ArrayList propList = new ArrayList(); + + /** 0:parse over. 1:parsing. */ + public int parseStatus = 1; +} diff --git a/vcard/tests/src/com/android/vcard/tests/test_utils/VNodeBuilder.java b/vcard/tests/src/com/android/vcard/tests/test_utils/VNodeBuilder.java new file mode 100644 index 000000000..9b4fe8358 --- /dev/null +++ b/vcard/tests/src/com/android/vcard/tests/test_utils/VNodeBuilder.java @@ -0,0 +1,247 @@ +/* + * 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.vcard.tests.test_utils; + +import android.content.ContentValues; +import android.util.Base64; +import android.util.CharsetUtils; +import android.util.Log; + +import com.android.vcard.VCardConfig; +import com.android.vcard.VCardInterpreter; +import com.android.vcard.VCardUtils; + +import java.io.UnsupportedEncodingException; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.List; + +/** + *

+ * The class storing the parse result to custom datastruct: + * {@link VNode}, and {@link PropertyNode}. + * Maybe several vcard instance, so use vNodeList to store. + *

+ *

+ * This is called VNode, not VCardNode, since it was used for expressing vCalendar (iCal). + *

+ */ +/* package */ class VNodeBuilder implements VCardInterpreter { + static private String LOG_TAG = "VNodeBuilder"; + + public List vNodeList = new ArrayList(); + private int mNodeListPos = 0; + private VNode mCurrentVNode; + private PropertyNode mCurrentPropNode; + private String mCurrentParamType; + + /** + * The charset using which VParser parses the text. + */ + private String mSourceCharset; + + /** + * The charset with which byte array is encoded to String. + */ + private String mTargetCharset; + + private boolean mStrictLineBreakParsing; + + public VNodeBuilder() { + this(VCardConfig.DEFAULT_INTERMEDIATE_CHARSET, VCardConfig.DEFAULT_IMPORT_CHARSET, false); + } + + public VNodeBuilder(String targetCharset, boolean strictLineBreakParsing) { + this(null, targetCharset, strictLineBreakParsing); + } + + /** + * @hide sourceCharset is temporal. + */ + public VNodeBuilder(String sourceCharset, String targetCharset, + boolean strictLineBreakParsing) { + if (sourceCharset != null) { + mSourceCharset = sourceCharset; + } else { + mSourceCharset = VCardConfig.DEFAULT_INTERMEDIATE_CHARSET; + } + if (targetCharset != null) { + mTargetCharset = targetCharset; + } else { + mTargetCharset = VCardConfig.DEFAULT_IMPORT_CHARSET; + } + mStrictLineBreakParsing = strictLineBreakParsing; + } + + public void start() { + } + + public void end() { + } + + // Note: I guess that this code assumes the Record may nest like this: + // START:VPOS + // ... + // START:VPOS2 + // ... + // END:VPOS2 + // ... + // END:VPOS + // + // However the following code has a bug. + // When error occurs after calling startRecord(), the entry which is probably + // the cause of the error remains to be in vNodeList, while endRecord() is not called. + // + // I leave this code as is since I'm not familiar with vcalendar specification. + // But I believe we should refactor this code in the future. + // Until this, the last entry has to be removed when some error occurs. + public void startEntry() { + VNode vnode = new VNode(); + vnode.parseStatus = 1; + vnode.VName = "VCARD"; + // I feel this should be done in endRecord(), but it cannot be done because of + // the reason above. + vNodeList.add(vnode); + mNodeListPos = vNodeList.size() - 1; + mCurrentVNode = vNodeList.get(mNodeListPos); + } + + public void endEntry() { + VNode endNode = vNodeList.get(mNodeListPos); + endNode.parseStatus = 0; + while(mNodeListPos > 0){ + mNodeListPos--; + if((vNodeList.get(mNodeListPos)).parseStatus == 1) + break; + } + mCurrentVNode = vNodeList.get(mNodeListPos); + } + + public void startProperty() { + mCurrentPropNode = new PropertyNode(); + } + + public void endProperty() { + mCurrentVNode.propList.add(mCurrentPropNode); + } + + public void propertyName(String name) { + mCurrentPropNode.propName = name; + } + + public void propertyGroup(String group) { + mCurrentPropNode.propGroupSet.add(group); + } + + public void propertyParamType(String type) { + mCurrentParamType = type; + } + + public void propertyParamValue(String value) { + if (mCurrentParamType == null || + mCurrentParamType.equalsIgnoreCase("TYPE")) { + mCurrentPropNode.paramMap_TYPE.add(value); + } else { + mCurrentPropNode.paramMap.put(mCurrentParamType, value); + } + + mCurrentParamType = null; + } + + private String encodeString(String originalString, String targetCharset) { + if (mSourceCharset.equalsIgnoreCase(targetCharset)) { + return originalString; + } + Charset charset = Charset.forName(mSourceCharset); + ByteBuffer byteBuffer = charset.encode(originalString); + // byteBuffer.array() "may" return byte array which is larger than + // byteBuffer.remaining(). Here, we keep on the safe side. + byte[] bytes = new byte[byteBuffer.remaining()]; + byteBuffer.get(bytes); + try { + return new String(bytes, targetCharset); + } catch (UnsupportedEncodingException e) { + Log.e(LOG_TAG, "Failed to encode: charset=" + targetCharset); + return null; + } + } + + private String handleOneValue(String value, String targetCharset, String encoding) { + if (encoding != null) { + encoding = encoding.toUpperCase(); + if (encoding.equals("BASE64") || encoding.equals("B")) { + // Assume BASE64 is used only when the number of values is 1. + mCurrentPropNode.propValue_bytes = Base64.decode(value.getBytes(), Base64.NO_WRAP); + return value; + } else if (encoding.equals("QUOTED-PRINTABLE")) { + return VCardUtils.parseQuotedPrintable( + value, mStrictLineBreakParsing, mSourceCharset, targetCharset); + } + // Unknown encoding. Fall back to default. + } + return encodeString(value, targetCharset); + } + + public void propertyValues(List values) { + if (values == null || values.size() == 0) { + mCurrentPropNode.propValue_bytes = null; + mCurrentPropNode.propValue_vector.clear(); + mCurrentPropNode.propValue_vector.add(""); + mCurrentPropNode.propValue = ""; + return; + } + + ContentValues paramMap = mCurrentPropNode.paramMap; + + String targetCharset = CharsetUtils.nameForDefaultVendor(paramMap.getAsString("CHARSET")); + String encoding = paramMap.getAsString("ENCODING"); + + if (targetCharset == null || targetCharset.length() == 0) { + targetCharset = mTargetCharset; + } + + for (String value : values) { + mCurrentPropNode.propValue_vector.add( + handleOneValue(value, targetCharset, encoding)); + } + + mCurrentPropNode.propValue = listToString(mCurrentPropNode.propValue_vector); + } + + private String listToString(List list){ + int size = list.size(); + if (size > 1) { + StringBuilder typeListB = new StringBuilder(); + for (String type : list) { + typeListB.append(type).append(";"); + } + int len = typeListB.length(); + if (len > 0 && typeListB.charAt(len - 1) == ';') { + return typeListB.substring(0, len - 1); + } + return typeListB.toString(); + } else if (size == 1) { + return list.get(0); + } else { + return ""; + } + } + + public String getResult(){ + throw new RuntimeException("Not supported"); + } +} From fcf836d32f181e522f4cc01332319d786a7579b7 Mon Sep 17 00:00:00 2001 From: Daisuke Miyakawa Date: Fri, 21 May 2010 17:01:12 -0700 Subject: [PATCH 208/541] Small fix around intermideate charset (ISO-8859-1). After a few investigation, it is found that ISO-8859-1 is confirmed to be the best charset for that use. Modify comment so that we explicitly mention it. Change-Id: I16e487fab33964a1665a1dd6991f2e8598f8895e --- vcard/java/com/android/vcard/VCardConfig.java | 21 ++++++++++++------- .../android/vcard/VCardEntryConstructor.java | 4 ++-- .../android/vcard/VCardParserImpl_V21.java | 20 +++++++----------- .../android/vcard/VCardParserImpl_V30.java | 6 +----- .../com/android/vcard/VCardParser_V21.java | 4 ---- .../com/android/vcard/VCardParser_V30.java | 4 ---- .../test_utils/ContentValuesVerifier.java | 3 +-- .../test_utils/ContentValuesVerifierElem.java | 3 +-- .../vcard/tests/test_utils/VNodeBuilder.java | 16 ++------------ 9 files changed, 27 insertions(+), 54 deletions(-) diff --git a/vcard/java/com/android/vcard/VCardConfig.java b/vcard/java/com/android/vcard/VCardConfig.java index fc95922d9..a011d8e27 100644 --- a/vcard/java/com/android/vcard/VCardConfig.java +++ b/vcard/java/com/android/vcard/VCardConfig.java @@ -43,15 +43,20 @@ public class VCardConfig { * The charset used during import. *

*

- * We cannot determine which charset should be used to interpret a given vCard file - * at first, while we have to decode sime encoded data (e.g. BASE64) to binary. - * In order to avoid "misinterpretation" of charset as much as possible, - * "ISO-8859-1" (a.k.a Latin-1) is first used for reading a stream. - * When charset is specified in a property (with "CHARSET=..." parameter), + * We cannot determine which charset should be used to interpret lines in vCard, + * while Java requires us to specify it when InputStream is used. + * We need to rely on the mechanism due to some performance reason. + *

+ *

+ * In order to avoid "misinterpretation" of charset and lose any data in vCard, + * "ISO-8859-1" is first used for reading the stream. + * When a charset is specified in a property (with "CHARSET=..." parameter), * the string is decoded to raw bytes and encoded into the specific charset, - * assuming "ISO-8859-1" is able to map "all" 8bit characters to some unicode, - * and it has 1 to 1 mapping in all 8bit characters. - * If the assumption is not correct, this setting will cause some bug. + *

+ *

+ * Unicode specification there's a one to one mapping between each byte in ISO-8859-1 + * and a codepoint, and Java specification requires runtime must have the charset. + * Thus, ISO-8859-1 is one effective mapping for intermediate mapping. *

*/ public static final String DEFAULT_INTERMEDIATE_CHARSET = "ISO-8859-1"; diff --git a/vcard/java/com/android/vcard/VCardEntryConstructor.java b/vcard/java/com/android/vcard/VCardEntryConstructor.java index 2679e238a..6cee0704b 100644 --- a/vcard/java/com/android/vcard/VCardEntryConstructor.java +++ b/vcard/java/com/android/vcard/VCardEntryConstructor.java @@ -68,7 +68,7 @@ public class VCardEntryConstructor implements VCardInterpreter { private final List mEntryHandlers = new ArrayList(); public VCardEntryConstructor() { - this(VCardConfig.VCARD_TYPE_V21_GENERIC, null, null, false); + this(VCardConfig.VCARD_TYPE_V21_GENERIC, null); } public VCardEntryConstructor(final int vcardType) { @@ -85,7 +85,7 @@ public class VCardEntryConstructor implements VCardInterpreter { } /** - * @hide + * @hide Just for testing. */ public VCardEntryConstructor(final int vcardType, final Account account, final String inputCharset, final boolean strictLineBreakParsing) { diff --git a/vcard/java/com/android/vcard/VCardParserImpl_V21.java b/vcard/java/com/android/vcard/VCardParserImpl_V21.java index 00ae6c91d..b8343ae8e 100644 --- a/vcard/java/com/android/vcard/VCardParserImpl_V21.java +++ b/vcard/java/com/android/vcard/VCardParserImpl_V21.java @@ -15,7 +15,6 @@ */ package com.android.vcard; -import android.text.TextUtils; import android.util.Log; import com.android.vcard.exception.VCardAgentNotSupportedException; @@ -64,12 +63,12 @@ import java.util.Set; } } - private static final String sDefaultEncoding = "8BIT"; + private static final String DEFAULT_ENCODING = "8BIT"; protected boolean mCanceled; protected VCardInterpreter mInterpreter; - protected final String mImportCharset; + protected final String mIntermediateCharset; /** *

@@ -136,20 +135,15 @@ import java.util.Set; private long mTimeHandleBase64; public VCardParserImpl_V21() { - this(VCardConfig.VCARD_TYPE_DEFAULT, null); + this(VCardConfig.VCARD_TYPE_DEFAULT); } public VCardParserImpl_V21(int vcardType) { - this(vcardType, null); - } - - public VCardParserImpl_V21(int vcardType, String importCharset) { if ((vcardType & VCardConfig.FLAG_TORELATE_NEST) != 0) { mNestCount = 1; } - mImportCharset = (!TextUtils.isEmpty(importCharset) ? importCharset : - VCardConfig.DEFAULT_INTERMEDIATE_CHARSET); + mIntermediateCharset = VCardConfig.DEFAULT_INTERMEDIATE_CHARSET; } /** @@ -385,7 +379,7 @@ import java.util.Set; * "AGENT" [params] ":" vcard CRLF */ protected boolean parseItem() throws IOException, VCardException { - mCurrentEncoding = sDefaultEncoding; + mCurrentEncoding = DEFAULT_ENCODING; final String line = getNonEmptyLine(); long start = System.currentTimeMillis(); @@ -928,7 +922,7 @@ import java.util.Set; } protected String getDefaultEncoding() { - return sDefaultEncoding; + return DEFAULT_ENCODING; } @@ -938,7 +932,7 @@ import java.util.Set; throw new NullPointerException("InputStream must not be null."); } - final InputStreamReader tmpReader = new InputStreamReader(is, mImportCharset); + final InputStreamReader tmpReader = new InputStreamReader(is, mIntermediateCharset); if (VCardConfig.showPerformanceLog()) { mReader = new CustomBufferedReader(tmpReader); } else { diff --git a/vcard/java/com/android/vcard/VCardParserImpl_V30.java b/vcard/java/com/android/vcard/VCardParserImpl_V30.java index 61d045598..def149563 100644 --- a/vcard/java/com/android/vcard/VCardParserImpl_V30.java +++ b/vcard/java/com/android/vcard/VCardParserImpl_V30.java @@ -46,11 +46,7 @@ import java.util.Set; } public VCardParserImpl_V30(int vcardType) { - super(vcardType, null); - } - - public VCardParserImpl_V30(int vcardType, String importCharset) { - super(vcardType, importCharset); + super(vcardType); } @Override diff --git a/vcard/java/com/android/vcard/VCardParser_V21.java b/vcard/java/com/android/vcard/VCardParser_V21.java index 2a5e31326..7aa7a822e 100644 --- a/vcard/java/com/android/vcard/VCardParser_V21.java +++ b/vcard/java/com/android/vcard/VCardParser_V21.java @@ -98,10 +98,6 @@ public final class VCardParser_V21 implements VCardParser { mVCardParserImpl = new VCardParserImpl_V21(vcardType); } - public VCardParser_V21(int parseType, String inputCharset) { - mVCardParserImpl = new VCardParserImpl_V21(parseType, null); - } - public void parse(InputStream is, VCardInterpreter interepreter) throws IOException, VCardException { mVCardParserImpl.parse(is, interepreter); diff --git a/vcard/java/com/android/vcard/VCardParser_V30.java b/vcard/java/com/android/vcard/VCardParser_V30.java index 179869b21..475534c42 100644 --- a/vcard/java/com/android/vcard/VCardParser_V30.java +++ b/vcard/java/com/android/vcard/VCardParser_V30.java @@ -76,10 +76,6 @@ public class VCardParser_V30 implements VCardParser { mVCardParserImpl = new VCardParserImpl_V30(vcardType); } - public VCardParser_V30(int vcardType, String importCharset) { - mVCardParserImpl = new VCardParserImpl_V30(vcardType, importCharset); - } - public void parse(InputStream is, VCardInterpreter interepreter) throws IOException, VCardException { mVCardParserImpl.parse(is, interepreter); diff --git a/vcard/tests/src/com/android/vcard/tests/test_utils/ContentValuesVerifier.java b/vcard/tests/src/com/android/vcard/tests/test_utils/ContentValuesVerifier.java index 7d6db53f3..2b3e3ab4a 100644 --- a/vcard/tests/src/com/android/vcard/tests/test_utils/ContentValuesVerifier.java +++ b/vcard/tests/src/com/android/vcard/tests/test_utils/ContentValuesVerifier.java @@ -66,8 +66,7 @@ public class ContentValuesVerifier implements VCardEntryHandler { public void verify(InputStream is, int vCardType, final VCardParser vCardParser) throws IOException, VCardException { - VCardEntryConstructor builder = - new VCardEntryConstructor(vCardType, null, null, false); + VCardEntryConstructor builder = new VCardEntryConstructor(vCardType, null); builder.addEntryHandler(this); try { vCardParser.parse(is, builder); diff --git a/vcard/tests/src/com/android/vcard/tests/test_utils/ContentValuesVerifierElem.java b/vcard/tests/src/com/android/vcard/tests/test_utils/ContentValuesVerifierElem.java index ecf4a2b69..6c09693b1 100644 --- a/vcard/tests/src/com/android/vcard/tests/test_utils/ContentValuesVerifierElem.java +++ b/vcard/tests/src/com/android/vcard/tests/test_utils/ContentValuesVerifierElem.java @@ -62,8 +62,7 @@ public class ContentValuesVerifierElem { } else { vCardParser = new VCardParser_V21(); } - VCardEntryConstructor builder = - new VCardEntryConstructor(vCardType, null, null, false); + final VCardEntryConstructor builder = new VCardEntryConstructor(vCardType, null); builder.addEntryHandler(mHandler); try { vCardParser.parse(is, builder); diff --git a/vcard/tests/src/com/android/vcard/tests/test_utils/VNodeBuilder.java b/vcard/tests/src/com/android/vcard/tests/test_utils/VNodeBuilder.java index 9b4fe8358..77a28ad2a 100644 --- a/vcard/tests/src/com/android/vcard/tests/test_utils/VNodeBuilder.java +++ b/vcard/tests/src/com/android/vcard/tests/test_utils/VNodeBuilder.java @@ -62,23 +62,11 @@ import java.util.List; private boolean mStrictLineBreakParsing; public VNodeBuilder() { - this(VCardConfig.DEFAULT_INTERMEDIATE_CHARSET, VCardConfig.DEFAULT_IMPORT_CHARSET, false); + this(VCardConfig.DEFAULT_IMPORT_CHARSET, false); } public VNodeBuilder(String targetCharset, boolean strictLineBreakParsing) { - this(null, targetCharset, strictLineBreakParsing); - } - - /** - * @hide sourceCharset is temporal. - */ - public VNodeBuilder(String sourceCharset, String targetCharset, - boolean strictLineBreakParsing) { - if (sourceCharset != null) { - mSourceCharset = sourceCharset; - } else { - mSourceCharset = VCardConfig.DEFAULT_INTERMEDIATE_CHARSET; - } + mSourceCharset = VCardConfig.DEFAULT_INTERMEDIATE_CHARSET; if (targetCharset != null) { mTargetCharset = targetCharset; } else { From 24ed9052bd607b303f8aff0b0e027a3eb39315ac Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Fri, 28 May 2010 15:13:30 -0700 Subject: [PATCH 209/541] Fix a typo in Singleton<> it could cause the sLock field to be emitted several times in different compilation unit. it also prevented to have 2 Singleton<> in the same file. --- include/utils/Singleton.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/include/utils/Singleton.h b/include/utils/Singleton.h index bc7626a82..3b975b4c4 100644 --- a/include/utils/Singleton.h +++ b/include/utils/Singleton.h @@ -54,11 +54,13 @@ private: * (eg: .cpp) to create the static instance of Singleton<>'s attributes, * and avoid to have a copy of them in each compilation units Singleton * is used. + * NOTE: we use a version of Mutex ctor that takes a parameter, because + * for some unknown reason using the default ctor doesn't emit the variable! */ -#define ANDROID_SINGLETON_STATIC_INSTANCE(TYPE) \ - template class Singleton< TYPE >; \ - template< class TYPE > Mutex Singleton< TYPE >::sLock; \ +#define ANDROID_SINGLETON_STATIC_INSTANCE(TYPE) \ + template class Singleton< TYPE >; \ + template<> Mutex Singleton< TYPE >::sLock(Mutex::PRIVATE); \ template<> TYPE* Singleton< TYPE >::sInstance(0); From 3073020282de4910fe1d3e65b0fc003078a109f8 Mon Sep 17 00:00:00 2001 From: Daisuke Miyakawa Date: Wed, 2 Jun 2010 12:12:18 -0700 Subject: [PATCH 210/541] Remove stale and inappropriate dependency. Change-Id: I2e9412075c157a8649ac06e89a8987a36544d78b --- vcard/Android.mk | 3 +-- vcard/tests/Android.mk | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/vcard/Android.mk b/vcard/Android.mk index 2bc17aa8c..2a6881962 100644 --- a/vcard/Android.mk +++ b/vcard/Android.mk @@ -18,9 +18,8 @@ include $(CLEAR_VARS) LOCAL_MODULE := com.android.vcard LOCAL_SRC_FILES := $(call all-java-files-under, java) -# Use google-common instead of android-common for using hidden code in telephony library. # Use ext for using Quoted-Printable codec. -LOCAL_JAVA_LIBRARIES := google-common ext +LOCAL_JAVA_LIBRARIES := ext include $(BUILD_STATIC_JAVA_LIBRARY) diff --git a/vcard/tests/Android.mk b/vcard/tests/Android.mk index 853ee1451..c34d3265b 100644 --- a/vcard/tests/Android.mk +++ b/vcard/tests/Android.mk @@ -19,7 +19,7 @@ LOCAL_CERTIFICATE := platform LOCAL_MODULE_TAGS := tests LOCAL_PACKAGE_NAME := AndroidVCardTests LOCAL_SRC_FILES := $(call all-java-files-under, src) -LOCAL_JAVA_LIBRARIES := android.test.runner google-common +LOCAL_JAVA_LIBRARIES := android.test.runner LOCAL_STATIC_JAVA_LIBRARIES := com.android.vcard include $(BUILD_PACKAGE) From f16344acab081bf89ae1a9797ba81025ce67e662 Mon Sep 17 00:00:00 2001 From: Daisuke Miyakawa Date: Thu, 3 Jun 2010 15:16:07 -0700 Subject: [PATCH 211/541] Delete vCard code to platform/framework/opt/vcard. Now all the code and related tests are in (top)/framework/opt/vcard. See also 33mb46cb82 in platform/framework/opt/vcard. Bug: 2735156 Change-Id: Ic3413110580874761e596b8fe9ad3c2e3c60795b --- vcard/Android.mk | 27 - .../java/com/android/vcard/JapaneseUtils.java | 379 ---- .../java/com/android/vcard/VCardBuilder.java | 1996 ----------------- .../java/com/android/vcard/VCardComposer.java | 677 ------ vcard/java/com/android/vcard/VCardConfig.java | 483 ---- .../com/android/vcard/VCardConstants.java | 160 -- vcard/java/com/android/vcard/VCardEntry.java | 1423 ------------ .../android/vcard/VCardEntryCommitter.java | 68 - .../android/vcard/VCardEntryConstructor.java | 240 -- .../com/android/vcard/VCardEntryCounter.java | 63 - .../com/android/vcard/VCardEntryHandler.java | 43 - .../com/android/vcard/VCardInterpreter.java | 102 - .../vcard/VCardInterpreterCollection.java | 102 - vcard/java/com/android/vcard/VCardParser.java | 55 - .../android/vcard/VCardParserImpl_V21.java | 962 -------- .../android/vcard/VCardParserImpl_V30.java | 313 --- .../com/android/vcard/VCardParser_V21.java | 109 - .../com/android/vcard/VCardParser_V30.java | 87 - .../android/vcard/VCardSourceDetector.java | 172 -- vcard/java/com/android/vcard/VCardUtils.java | 658 ------ .../VCardAgentNotSupportedException.java | 27 - .../vcard/exception/VCardException.java | 35 - .../VCardInvalidCommentLineException.java | 32 - .../exception/VCardInvalidLineException.java | 31 - .../vcard/exception/VCardNestedException.java | 29 - .../exception/VCardNotSupportedException.java | 33 - .../exception/VCardVersionException.java | 28 - vcard/tests/Android.mk | 25 - vcard/tests/AndroidManifest.xml | 30 - vcard/tests/res/raw/v21_backslash.vcf | 5 - vcard/tests/res/raw/v21_complicated.vcf | 106 - .../res/raw/v21_invalid_comment_line.vcf | 10 - vcard/tests/res/raw/v21_japanese_1.vcf | 6 - vcard/tests/res/raw/v21_japanese_2.vcf | 10 - vcard/tests/res/raw/v21_multiple_entry.vcf | 33 - vcard/tests/res/raw/v21_org_before_title.vcf | 6 - vcard/tests/res/raw/v21_pref_handling.vcf | 15 - vcard/tests/res/raw/v21_simple_1.vcf | 3 - vcard/tests/res/raw/v21_simple_2.vcf | 3 - vcard/tests/res/raw/v21_simple_3.vcf | 4 - vcard/tests/res/raw/v21_title_before_org.vcf | 6 - vcard/tests/res/raw/v21_winmo_65.vcf | 10 - vcard/tests/res/raw/v30_comma_separated.vcf | 5 - vcard/tests/res/raw/v30_simple.vcf | 13 - .../vcard/tests/VCardExporterTests.java | 971 -------- .../vcard/tests/VCardImporterTests.java | 1008 --------- .../vcard/tests/VCardJapanizationTests.java | 436 ---- .../android/vcard/tests/VCardTestsBase.java | 85 - .../android/vcard/tests/VCardUtilsTests.java | 84 - .../vcard/tests/test_utils/ContactEntry.java | 43 - .../test_utils/ContentValuesBuilder.java | 80 - .../test_utils/ContentValuesVerifier.java | 101 - .../test_utils/ContentValuesVerifierElem.java | 95 - .../tests/test_utils/ExportTestProvider.java | 176 -- .../tests/test_utils/ExportTestResolver.java | 40 - .../tests/test_utils/ImportTestProvider.java | 274 --- .../tests/test_utils/ImportTestResolver.java | 57 - .../vcard/tests/test_utils/LineVerifier.java | 66 - .../tests/test_utils/LineVerifierElem.java | 106 - .../vcard/tests/test_utils/PropertyNode.java | 205 -- .../test_utils/PropertyNodesVerifier.java | 91 - .../test_utils/PropertyNodesVerifierElem.java | 317 --- .../vcard/tests/test_utils/VCardVerifier.java | 339 --- .../android/vcard/tests/test_utils/VNode.java | 30 - .../vcard/tests/test_utils/VNodeBuilder.java | 235 -- 65 files changed, 13463 deletions(-) delete mode 100644 vcard/Android.mk delete mode 100644 vcard/java/com/android/vcard/JapaneseUtils.java delete mode 100644 vcard/java/com/android/vcard/VCardBuilder.java delete mode 100644 vcard/java/com/android/vcard/VCardComposer.java delete mode 100644 vcard/java/com/android/vcard/VCardConfig.java delete mode 100644 vcard/java/com/android/vcard/VCardConstants.java delete mode 100644 vcard/java/com/android/vcard/VCardEntry.java delete mode 100644 vcard/java/com/android/vcard/VCardEntryCommitter.java delete mode 100644 vcard/java/com/android/vcard/VCardEntryConstructor.java delete mode 100644 vcard/java/com/android/vcard/VCardEntryCounter.java delete mode 100644 vcard/java/com/android/vcard/VCardEntryHandler.java delete mode 100644 vcard/java/com/android/vcard/VCardInterpreter.java delete mode 100644 vcard/java/com/android/vcard/VCardInterpreterCollection.java delete mode 100644 vcard/java/com/android/vcard/VCardParser.java delete mode 100644 vcard/java/com/android/vcard/VCardParserImpl_V21.java delete mode 100644 vcard/java/com/android/vcard/VCardParserImpl_V30.java delete mode 100644 vcard/java/com/android/vcard/VCardParser_V21.java delete mode 100644 vcard/java/com/android/vcard/VCardParser_V30.java delete mode 100644 vcard/java/com/android/vcard/VCardSourceDetector.java delete mode 100644 vcard/java/com/android/vcard/VCardUtils.java delete mode 100644 vcard/java/com/android/vcard/exception/VCardAgentNotSupportedException.java delete mode 100644 vcard/java/com/android/vcard/exception/VCardException.java delete mode 100644 vcard/java/com/android/vcard/exception/VCardInvalidCommentLineException.java delete mode 100644 vcard/java/com/android/vcard/exception/VCardInvalidLineException.java delete mode 100644 vcard/java/com/android/vcard/exception/VCardNestedException.java delete mode 100644 vcard/java/com/android/vcard/exception/VCardNotSupportedException.java delete mode 100644 vcard/java/com/android/vcard/exception/VCardVersionException.java delete mode 100644 vcard/tests/Android.mk delete mode 100644 vcard/tests/AndroidManifest.xml delete mode 100644 vcard/tests/res/raw/v21_backslash.vcf delete mode 100644 vcard/tests/res/raw/v21_complicated.vcf delete mode 100644 vcard/tests/res/raw/v21_invalid_comment_line.vcf delete mode 100644 vcard/tests/res/raw/v21_japanese_1.vcf delete mode 100644 vcard/tests/res/raw/v21_japanese_2.vcf delete mode 100644 vcard/tests/res/raw/v21_multiple_entry.vcf delete mode 100644 vcard/tests/res/raw/v21_org_before_title.vcf delete mode 100644 vcard/tests/res/raw/v21_pref_handling.vcf delete mode 100644 vcard/tests/res/raw/v21_simple_1.vcf delete mode 100644 vcard/tests/res/raw/v21_simple_2.vcf delete mode 100644 vcard/tests/res/raw/v21_simple_3.vcf delete mode 100644 vcard/tests/res/raw/v21_title_before_org.vcf delete mode 100644 vcard/tests/res/raw/v21_winmo_65.vcf delete mode 100644 vcard/tests/res/raw/v30_comma_separated.vcf delete mode 100644 vcard/tests/res/raw/v30_simple.vcf delete mode 100644 vcard/tests/src/com/android/vcard/tests/VCardExporterTests.java delete mode 100644 vcard/tests/src/com/android/vcard/tests/VCardImporterTests.java delete mode 100644 vcard/tests/src/com/android/vcard/tests/VCardJapanizationTests.java delete mode 100644 vcard/tests/src/com/android/vcard/tests/VCardTestsBase.java delete mode 100644 vcard/tests/src/com/android/vcard/tests/VCardUtilsTests.java delete mode 100644 vcard/tests/src/com/android/vcard/tests/test_utils/ContactEntry.java delete mode 100644 vcard/tests/src/com/android/vcard/tests/test_utils/ContentValuesBuilder.java delete mode 100644 vcard/tests/src/com/android/vcard/tests/test_utils/ContentValuesVerifier.java delete mode 100644 vcard/tests/src/com/android/vcard/tests/test_utils/ContentValuesVerifierElem.java delete mode 100644 vcard/tests/src/com/android/vcard/tests/test_utils/ExportTestProvider.java delete mode 100644 vcard/tests/src/com/android/vcard/tests/test_utils/ExportTestResolver.java delete mode 100644 vcard/tests/src/com/android/vcard/tests/test_utils/ImportTestProvider.java delete mode 100644 vcard/tests/src/com/android/vcard/tests/test_utils/ImportTestResolver.java delete mode 100644 vcard/tests/src/com/android/vcard/tests/test_utils/LineVerifier.java delete mode 100644 vcard/tests/src/com/android/vcard/tests/test_utils/LineVerifierElem.java delete mode 100644 vcard/tests/src/com/android/vcard/tests/test_utils/PropertyNode.java delete mode 100644 vcard/tests/src/com/android/vcard/tests/test_utils/PropertyNodesVerifier.java delete mode 100644 vcard/tests/src/com/android/vcard/tests/test_utils/PropertyNodesVerifierElem.java delete mode 100644 vcard/tests/src/com/android/vcard/tests/test_utils/VCardVerifier.java delete mode 100644 vcard/tests/src/com/android/vcard/tests/test_utils/VNode.java delete mode 100644 vcard/tests/src/com/android/vcard/tests/test_utils/VNodeBuilder.java diff --git a/vcard/Android.mk b/vcard/Android.mk deleted file mode 100644 index 2a6881962..000000000 --- a/vcard/Android.mk +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright (C) 2010 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. - -LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_MODULE := com.android.vcard -LOCAL_SRC_FILES := $(call all-java-files-under, java) - -# Use ext for using Quoted-Printable codec. -LOCAL_JAVA_LIBRARIES := ext - -include $(BUILD_STATIC_JAVA_LIBRARY) - -# Build the test package. -include $(call all-makefiles-under, $(LOCAL_PATH)) diff --git a/vcard/java/com/android/vcard/JapaneseUtils.java b/vcard/java/com/android/vcard/JapaneseUtils.java deleted file mode 100644 index 5b4494469..000000000 --- a/vcard/java/com/android/vcard/JapaneseUtils.java +++ /dev/null @@ -1,379 +0,0 @@ -/* - * 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.vcard; - -import java.util.HashMap; -import java.util.Map; - -/** - * TextUtils especially for Japanese. - */ -/* package */ class JapaneseUtils { - static private final Map sHalfWidthMap = - new HashMap(); - - static { - sHalfWidthMap.put('\u3001', "\uFF64"); - sHalfWidthMap.put('\u3002', "\uFF61"); - sHalfWidthMap.put('\u300C', "\uFF62"); - sHalfWidthMap.put('\u300D', "\uFF63"); - sHalfWidthMap.put('\u301C', "~"); - sHalfWidthMap.put('\u3041', "\uFF67"); - sHalfWidthMap.put('\u3042', "\uFF71"); - sHalfWidthMap.put('\u3043', "\uFF68"); - sHalfWidthMap.put('\u3044', "\uFF72"); - sHalfWidthMap.put('\u3045', "\uFF69"); - sHalfWidthMap.put('\u3046', "\uFF73"); - sHalfWidthMap.put('\u3047', "\uFF6A"); - sHalfWidthMap.put('\u3048', "\uFF74"); - sHalfWidthMap.put('\u3049', "\uFF6B"); - sHalfWidthMap.put('\u304A', "\uFF75"); - sHalfWidthMap.put('\u304B', "\uFF76"); - sHalfWidthMap.put('\u304C', "\uFF76\uFF9E"); - sHalfWidthMap.put('\u304D', "\uFF77"); - sHalfWidthMap.put('\u304E', "\uFF77\uFF9E"); - sHalfWidthMap.put('\u304F', "\uFF78"); - sHalfWidthMap.put('\u3050', "\uFF78\uFF9E"); - sHalfWidthMap.put('\u3051', "\uFF79"); - sHalfWidthMap.put('\u3052', "\uFF79\uFF9E"); - sHalfWidthMap.put('\u3053', "\uFF7A"); - sHalfWidthMap.put('\u3054', "\uFF7A\uFF9E"); - sHalfWidthMap.put('\u3055', "\uFF7B"); - sHalfWidthMap.put('\u3056', "\uFF7B\uFF9E"); - sHalfWidthMap.put('\u3057', "\uFF7C"); - sHalfWidthMap.put('\u3058', "\uFF7C\uFF9E"); - sHalfWidthMap.put('\u3059', "\uFF7D"); - sHalfWidthMap.put('\u305A', "\uFF7D\uFF9E"); - sHalfWidthMap.put('\u305B', "\uFF7E"); - sHalfWidthMap.put('\u305C', "\uFF7E\uFF9E"); - sHalfWidthMap.put('\u305D', "\uFF7F"); - sHalfWidthMap.put('\u305E', "\uFF7F\uFF9E"); - sHalfWidthMap.put('\u305F', "\uFF80"); - sHalfWidthMap.put('\u3060', "\uFF80\uFF9E"); - sHalfWidthMap.put('\u3061', "\uFF81"); - sHalfWidthMap.put('\u3062', "\uFF81\uFF9E"); - sHalfWidthMap.put('\u3063', "\uFF6F"); - sHalfWidthMap.put('\u3064', "\uFF82"); - sHalfWidthMap.put('\u3065', "\uFF82\uFF9E"); - sHalfWidthMap.put('\u3066', "\uFF83"); - sHalfWidthMap.put('\u3067', "\uFF83\uFF9E"); - sHalfWidthMap.put('\u3068', "\uFF84"); - sHalfWidthMap.put('\u3069', "\uFF84\uFF9E"); - sHalfWidthMap.put('\u306A', "\uFF85"); - sHalfWidthMap.put('\u306B', "\uFF86"); - sHalfWidthMap.put('\u306C', "\uFF87"); - sHalfWidthMap.put('\u306D', "\uFF88"); - sHalfWidthMap.put('\u306E', "\uFF89"); - sHalfWidthMap.put('\u306F', "\uFF8A"); - sHalfWidthMap.put('\u3070', "\uFF8A\uFF9E"); - sHalfWidthMap.put('\u3071', "\uFF8A\uFF9F"); - sHalfWidthMap.put('\u3072', "\uFF8B"); - sHalfWidthMap.put('\u3073', "\uFF8B\uFF9E"); - sHalfWidthMap.put('\u3074', "\uFF8B\uFF9F"); - sHalfWidthMap.put('\u3075', "\uFF8C"); - sHalfWidthMap.put('\u3076', "\uFF8C\uFF9E"); - sHalfWidthMap.put('\u3077', "\uFF8C\uFF9F"); - sHalfWidthMap.put('\u3078', "\uFF8D"); - sHalfWidthMap.put('\u3079', "\uFF8D\uFF9E"); - sHalfWidthMap.put('\u307A', "\uFF8D\uFF9F"); - sHalfWidthMap.put('\u307B', "\uFF8E"); - sHalfWidthMap.put('\u307C', "\uFF8E\uFF9E"); - sHalfWidthMap.put('\u307D', "\uFF8E\uFF9F"); - sHalfWidthMap.put('\u307E', "\uFF8F"); - sHalfWidthMap.put('\u307F', "\uFF90"); - sHalfWidthMap.put('\u3080', "\uFF91"); - sHalfWidthMap.put('\u3081', "\uFF92"); - sHalfWidthMap.put('\u3082', "\uFF93"); - sHalfWidthMap.put('\u3083', "\uFF6C"); - sHalfWidthMap.put('\u3084', "\uFF94"); - sHalfWidthMap.put('\u3085', "\uFF6D"); - sHalfWidthMap.put('\u3086', "\uFF95"); - sHalfWidthMap.put('\u3087', "\uFF6E"); - sHalfWidthMap.put('\u3088', "\uFF96"); - sHalfWidthMap.put('\u3089', "\uFF97"); - sHalfWidthMap.put('\u308A', "\uFF98"); - sHalfWidthMap.put('\u308B', "\uFF99"); - sHalfWidthMap.put('\u308C', "\uFF9A"); - sHalfWidthMap.put('\u308D', "\uFF9B"); - sHalfWidthMap.put('\u308E', "\uFF9C"); - sHalfWidthMap.put('\u308F', "\uFF9C"); - sHalfWidthMap.put('\u3090', "\uFF72"); - sHalfWidthMap.put('\u3091', "\uFF74"); - sHalfWidthMap.put('\u3092', "\uFF66"); - sHalfWidthMap.put('\u3093', "\uFF9D"); - sHalfWidthMap.put('\u309B', "\uFF9E"); - sHalfWidthMap.put('\u309C', "\uFF9F"); - sHalfWidthMap.put('\u30A1', "\uFF67"); - sHalfWidthMap.put('\u30A2', "\uFF71"); - sHalfWidthMap.put('\u30A3', "\uFF68"); - sHalfWidthMap.put('\u30A4', "\uFF72"); - sHalfWidthMap.put('\u30A5', "\uFF69"); - sHalfWidthMap.put('\u30A6', "\uFF73"); - sHalfWidthMap.put('\u30A7', "\uFF6A"); - sHalfWidthMap.put('\u30A8', "\uFF74"); - sHalfWidthMap.put('\u30A9', "\uFF6B"); - sHalfWidthMap.put('\u30AA', "\uFF75"); - sHalfWidthMap.put('\u30AB', "\uFF76"); - sHalfWidthMap.put('\u30AC', "\uFF76\uFF9E"); - sHalfWidthMap.put('\u30AD', "\uFF77"); - sHalfWidthMap.put('\u30AE', "\uFF77\uFF9E"); - sHalfWidthMap.put('\u30AF', "\uFF78"); - sHalfWidthMap.put('\u30B0', "\uFF78\uFF9E"); - sHalfWidthMap.put('\u30B1', "\uFF79"); - sHalfWidthMap.put('\u30B2', "\uFF79\uFF9E"); - sHalfWidthMap.put('\u30B3', "\uFF7A"); - sHalfWidthMap.put('\u30B4', "\uFF7A\uFF9E"); - sHalfWidthMap.put('\u30B5', "\uFF7B"); - sHalfWidthMap.put('\u30B6', "\uFF7B\uFF9E"); - sHalfWidthMap.put('\u30B7', "\uFF7C"); - sHalfWidthMap.put('\u30B8', "\uFF7C\uFF9E"); - sHalfWidthMap.put('\u30B9', "\uFF7D"); - sHalfWidthMap.put('\u30BA', "\uFF7D\uFF9E"); - sHalfWidthMap.put('\u30BB', "\uFF7E"); - sHalfWidthMap.put('\u30BC', "\uFF7E\uFF9E"); - sHalfWidthMap.put('\u30BD', "\uFF7F"); - sHalfWidthMap.put('\u30BE', "\uFF7F\uFF9E"); - sHalfWidthMap.put('\u30BF', "\uFF80"); - sHalfWidthMap.put('\u30C0', "\uFF80\uFF9E"); - sHalfWidthMap.put('\u30C1', "\uFF81"); - sHalfWidthMap.put('\u30C2', "\uFF81\uFF9E"); - sHalfWidthMap.put('\u30C3', "\uFF6F"); - sHalfWidthMap.put('\u30C4', "\uFF82"); - sHalfWidthMap.put('\u30C5', "\uFF82\uFF9E"); - sHalfWidthMap.put('\u30C6', "\uFF83"); - sHalfWidthMap.put('\u30C7', "\uFF83\uFF9E"); - sHalfWidthMap.put('\u30C8', "\uFF84"); - sHalfWidthMap.put('\u30C9', "\uFF84\uFF9E"); - sHalfWidthMap.put('\u30CA', "\uFF85"); - sHalfWidthMap.put('\u30CB', "\uFF86"); - sHalfWidthMap.put('\u30CC', "\uFF87"); - sHalfWidthMap.put('\u30CD', "\uFF88"); - sHalfWidthMap.put('\u30CE', "\uFF89"); - sHalfWidthMap.put('\u30CF', "\uFF8A"); - sHalfWidthMap.put('\u30D0', "\uFF8A\uFF9E"); - sHalfWidthMap.put('\u30D1', "\uFF8A\uFF9F"); - sHalfWidthMap.put('\u30D2', "\uFF8B"); - sHalfWidthMap.put('\u30D3', "\uFF8B\uFF9E"); - sHalfWidthMap.put('\u30D4', "\uFF8B\uFF9F"); - sHalfWidthMap.put('\u30D5', "\uFF8C"); - sHalfWidthMap.put('\u30D6', "\uFF8C\uFF9E"); - sHalfWidthMap.put('\u30D7', "\uFF8C\uFF9F"); - sHalfWidthMap.put('\u30D8', "\uFF8D"); - sHalfWidthMap.put('\u30D9', "\uFF8D\uFF9E"); - sHalfWidthMap.put('\u30DA', "\uFF8D\uFF9F"); - sHalfWidthMap.put('\u30DB', "\uFF8E"); - sHalfWidthMap.put('\u30DC', "\uFF8E\uFF9E"); - sHalfWidthMap.put('\u30DD', "\uFF8E\uFF9F"); - sHalfWidthMap.put('\u30DE', "\uFF8F"); - sHalfWidthMap.put('\u30DF', "\uFF90"); - sHalfWidthMap.put('\u30E0', "\uFF91"); - sHalfWidthMap.put('\u30E1', "\uFF92"); - sHalfWidthMap.put('\u30E2', "\uFF93"); - sHalfWidthMap.put('\u30E3', "\uFF6C"); - sHalfWidthMap.put('\u30E4', "\uFF94"); - sHalfWidthMap.put('\u30E5', "\uFF6D"); - sHalfWidthMap.put('\u30E6', "\uFF95"); - sHalfWidthMap.put('\u30E7', "\uFF6E"); - sHalfWidthMap.put('\u30E8', "\uFF96"); - sHalfWidthMap.put('\u30E9', "\uFF97"); - sHalfWidthMap.put('\u30EA', "\uFF98"); - sHalfWidthMap.put('\u30EB', "\uFF99"); - sHalfWidthMap.put('\u30EC', "\uFF9A"); - sHalfWidthMap.put('\u30ED', "\uFF9B"); - sHalfWidthMap.put('\u30EE', "\uFF9C"); - sHalfWidthMap.put('\u30EF', "\uFF9C"); - sHalfWidthMap.put('\u30F0', "\uFF72"); - sHalfWidthMap.put('\u30F1', "\uFF74"); - sHalfWidthMap.put('\u30F2', "\uFF66"); - sHalfWidthMap.put('\u30F3', "\uFF9D"); - sHalfWidthMap.put('\u30F4', "\uFF73\uFF9E"); - sHalfWidthMap.put('\u30F5', "\uFF76"); - sHalfWidthMap.put('\u30F6', "\uFF79"); - sHalfWidthMap.put('\u30FB', "\uFF65"); - sHalfWidthMap.put('\u30FC', "\uFF70"); - sHalfWidthMap.put('\uFF01', "!"); - sHalfWidthMap.put('\uFF02', "\""); - sHalfWidthMap.put('\uFF03', "#"); - sHalfWidthMap.put('\uFF04', "$"); - sHalfWidthMap.put('\uFF05', "%"); - sHalfWidthMap.put('\uFF06', "&"); - sHalfWidthMap.put('\uFF07', "'"); - sHalfWidthMap.put('\uFF08', "("); - sHalfWidthMap.put('\uFF09', ")"); - sHalfWidthMap.put('\uFF0A', "*"); - sHalfWidthMap.put('\uFF0B', "+"); - sHalfWidthMap.put('\uFF0C', ","); - sHalfWidthMap.put('\uFF0D', "-"); - sHalfWidthMap.put('\uFF0E', "."); - sHalfWidthMap.put('\uFF0F', "/"); - sHalfWidthMap.put('\uFF10', "0"); - sHalfWidthMap.put('\uFF11', "1"); - sHalfWidthMap.put('\uFF12', "2"); - sHalfWidthMap.put('\uFF13', "3"); - sHalfWidthMap.put('\uFF14', "4"); - sHalfWidthMap.put('\uFF15', "5"); - sHalfWidthMap.put('\uFF16', "6"); - sHalfWidthMap.put('\uFF17', "7"); - sHalfWidthMap.put('\uFF18', "8"); - sHalfWidthMap.put('\uFF19', "9"); - sHalfWidthMap.put('\uFF1A', ":"); - sHalfWidthMap.put('\uFF1B', ";"); - sHalfWidthMap.put('\uFF1C', "<"); - sHalfWidthMap.put('\uFF1D', "="); - sHalfWidthMap.put('\uFF1E', ">"); - sHalfWidthMap.put('\uFF1F', "?"); - sHalfWidthMap.put('\uFF20', "@"); - sHalfWidthMap.put('\uFF21', "A"); - sHalfWidthMap.put('\uFF22', "B"); - sHalfWidthMap.put('\uFF23', "C"); - sHalfWidthMap.put('\uFF24', "D"); - sHalfWidthMap.put('\uFF25', "E"); - sHalfWidthMap.put('\uFF26', "F"); - sHalfWidthMap.put('\uFF27', "G"); - sHalfWidthMap.put('\uFF28', "H"); - sHalfWidthMap.put('\uFF29', "I"); - sHalfWidthMap.put('\uFF2A', "J"); - sHalfWidthMap.put('\uFF2B', "K"); - sHalfWidthMap.put('\uFF2C', "L"); - sHalfWidthMap.put('\uFF2D', "M"); - sHalfWidthMap.put('\uFF2E', "N"); - sHalfWidthMap.put('\uFF2F', "O"); - sHalfWidthMap.put('\uFF30', "P"); - sHalfWidthMap.put('\uFF31', "Q"); - sHalfWidthMap.put('\uFF32', "R"); - sHalfWidthMap.put('\uFF33', "S"); - sHalfWidthMap.put('\uFF34', "T"); - sHalfWidthMap.put('\uFF35', "U"); - sHalfWidthMap.put('\uFF36', "V"); - sHalfWidthMap.put('\uFF37', "W"); - sHalfWidthMap.put('\uFF38', "X"); - sHalfWidthMap.put('\uFF39', "Y"); - sHalfWidthMap.put('\uFF3A', "Z"); - sHalfWidthMap.put('\uFF3B', "["); - sHalfWidthMap.put('\uFF3C', "\\"); - sHalfWidthMap.put('\uFF3D', "]"); - sHalfWidthMap.put('\uFF3E', "^"); - sHalfWidthMap.put('\uFF3F', "_"); - sHalfWidthMap.put('\uFF41', "a"); - sHalfWidthMap.put('\uFF42', "b"); - sHalfWidthMap.put('\uFF43', "c"); - sHalfWidthMap.put('\uFF44', "d"); - sHalfWidthMap.put('\uFF45', "e"); - sHalfWidthMap.put('\uFF46', "f"); - sHalfWidthMap.put('\uFF47', "g"); - sHalfWidthMap.put('\uFF48', "h"); - sHalfWidthMap.put('\uFF49', "i"); - sHalfWidthMap.put('\uFF4A', "j"); - sHalfWidthMap.put('\uFF4B', "k"); - sHalfWidthMap.put('\uFF4C', "l"); - sHalfWidthMap.put('\uFF4D', "m"); - sHalfWidthMap.put('\uFF4E', "n"); - sHalfWidthMap.put('\uFF4F', "o"); - sHalfWidthMap.put('\uFF50', "p"); - sHalfWidthMap.put('\uFF51', "q"); - sHalfWidthMap.put('\uFF52', "r"); - sHalfWidthMap.put('\uFF53', "s"); - sHalfWidthMap.put('\uFF54', "t"); - sHalfWidthMap.put('\uFF55', "u"); - sHalfWidthMap.put('\uFF56', "v"); - sHalfWidthMap.put('\uFF57', "w"); - sHalfWidthMap.put('\uFF58', "x"); - sHalfWidthMap.put('\uFF59', "y"); - sHalfWidthMap.put('\uFF5A', "z"); - sHalfWidthMap.put('\uFF5B', "{"); - sHalfWidthMap.put('\uFF5C', "|"); - sHalfWidthMap.put('\uFF5D', "}"); - sHalfWidthMap.put('\uFF5E', "~"); - sHalfWidthMap.put('\uFF61', "\uFF61"); - sHalfWidthMap.put('\uFF62', "\uFF62"); - sHalfWidthMap.put('\uFF63', "\uFF63"); - sHalfWidthMap.put('\uFF64', "\uFF64"); - sHalfWidthMap.put('\uFF65', "\uFF65"); - sHalfWidthMap.put('\uFF66', "\uFF66"); - sHalfWidthMap.put('\uFF67', "\uFF67"); - sHalfWidthMap.put('\uFF68', "\uFF68"); - sHalfWidthMap.put('\uFF69', "\uFF69"); - sHalfWidthMap.put('\uFF6A', "\uFF6A"); - sHalfWidthMap.put('\uFF6B', "\uFF6B"); - sHalfWidthMap.put('\uFF6C', "\uFF6C"); - sHalfWidthMap.put('\uFF6D', "\uFF6D"); - sHalfWidthMap.put('\uFF6E', "\uFF6E"); - sHalfWidthMap.put('\uFF6F', "\uFF6F"); - sHalfWidthMap.put('\uFF70', "\uFF70"); - sHalfWidthMap.put('\uFF71', "\uFF71"); - sHalfWidthMap.put('\uFF72', "\uFF72"); - sHalfWidthMap.put('\uFF73', "\uFF73"); - sHalfWidthMap.put('\uFF74', "\uFF74"); - sHalfWidthMap.put('\uFF75', "\uFF75"); - sHalfWidthMap.put('\uFF76', "\uFF76"); - sHalfWidthMap.put('\uFF77', "\uFF77"); - sHalfWidthMap.put('\uFF78', "\uFF78"); - sHalfWidthMap.put('\uFF79', "\uFF79"); - sHalfWidthMap.put('\uFF7A', "\uFF7A"); - sHalfWidthMap.put('\uFF7B', "\uFF7B"); - sHalfWidthMap.put('\uFF7C', "\uFF7C"); - sHalfWidthMap.put('\uFF7D', "\uFF7D"); - sHalfWidthMap.put('\uFF7E', "\uFF7E"); - sHalfWidthMap.put('\uFF7F', "\uFF7F"); - sHalfWidthMap.put('\uFF80', "\uFF80"); - sHalfWidthMap.put('\uFF81', "\uFF81"); - sHalfWidthMap.put('\uFF82', "\uFF82"); - sHalfWidthMap.put('\uFF83', "\uFF83"); - sHalfWidthMap.put('\uFF84', "\uFF84"); - sHalfWidthMap.put('\uFF85', "\uFF85"); - sHalfWidthMap.put('\uFF86', "\uFF86"); - sHalfWidthMap.put('\uFF87', "\uFF87"); - sHalfWidthMap.put('\uFF88', "\uFF88"); - sHalfWidthMap.put('\uFF89', "\uFF89"); - sHalfWidthMap.put('\uFF8A', "\uFF8A"); - sHalfWidthMap.put('\uFF8B', "\uFF8B"); - sHalfWidthMap.put('\uFF8C', "\uFF8C"); - sHalfWidthMap.put('\uFF8D', "\uFF8D"); - sHalfWidthMap.put('\uFF8E', "\uFF8E"); - sHalfWidthMap.put('\uFF8F', "\uFF8F"); - sHalfWidthMap.put('\uFF90', "\uFF90"); - sHalfWidthMap.put('\uFF91', "\uFF91"); - sHalfWidthMap.put('\uFF92', "\uFF92"); - sHalfWidthMap.put('\uFF93', "\uFF93"); - sHalfWidthMap.put('\uFF94', "\uFF94"); - sHalfWidthMap.put('\uFF95', "\uFF95"); - sHalfWidthMap.put('\uFF96', "\uFF96"); - sHalfWidthMap.put('\uFF97', "\uFF97"); - sHalfWidthMap.put('\uFF98', "\uFF98"); - sHalfWidthMap.put('\uFF99', "\uFF99"); - sHalfWidthMap.put('\uFF9A', "\uFF9A"); - sHalfWidthMap.put('\uFF9B', "\uFF9B"); - sHalfWidthMap.put('\uFF9C', "\uFF9C"); - sHalfWidthMap.put('\uFF9D', "\uFF9D"); - sHalfWidthMap.put('\uFF9E', "\uFF9E"); - sHalfWidthMap.put('\uFF9F', "\uFF9F"); - sHalfWidthMap.put('\uFFE5', "\u005C\u005C"); - } - - /** - * Returns half-width version of that character if possible. Returns null if not possible - * @param ch input character - * @return CharSequence object if the mapping for ch exists. Return null otherwise. - */ - public static String tryGetHalfWidthText(final char ch) { - if (sHalfWidthMap.containsKey(ch)) { - return sHalfWidthMap.get(ch); - } else { - return null; - } - } -} diff --git a/vcard/java/com/android/vcard/VCardBuilder.java b/vcard/java/com/android/vcard/VCardBuilder.java deleted file mode 100644 index 6ef9adad9..000000000 --- a/vcard/java/com/android/vcard/VCardBuilder.java +++ /dev/null @@ -1,1996 +0,0 @@ -/* - * 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.vcard; - -import android.content.ContentValues; -import android.provider.ContactsContract.CommonDataKinds.Email; -import android.provider.ContactsContract.CommonDataKinds.Event; -import android.provider.ContactsContract.CommonDataKinds.Im; -import android.provider.ContactsContract.CommonDataKinds.Nickname; -import android.provider.ContactsContract.CommonDataKinds.Note; -import android.provider.ContactsContract.CommonDataKinds.Organization; -import android.provider.ContactsContract.CommonDataKinds.Phone; -import android.provider.ContactsContract.CommonDataKinds.Photo; -import android.provider.ContactsContract.CommonDataKinds.Relation; -import android.provider.ContactsContract.CommonDataKinds.StructuredName; -import android.provider.ContactsContract.CommonDataKinds.StructuredPostal; -import android.provider.ContactsContract.CommonDataKinds.Website; -import android.telephony.PhoneNumberUtils; -import android.text.TextUtils; -import android.util.Base64; -import android.util.CharsetUtils; -import android.util.Log; - -import java.io.UnsupportedEncodingException; -import java.nio.charset.UnsupportedCharsetException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - *

- * The class which lets users create their own vCard String. Typical usage is as follows: - *

- *
final VCardBuilder builder = new VCardBuilder(vcardType);
- * builder.appendNameProperties(contentValuesListMap.get(StructuredName.CONTENT_ITEM_TYPE))
- *     .appendNickNames(contentValuesListMap.get(Nickname.CONTENT_ITEM_TYPE))
- *     .appendPhones(contentValuesListMap.get(Phone.CONTENT_ITEM_TYPE))
- *     .appendEmails(contentValuesListMap.get(Email.CONTENT_ITEM_TYPE))
- *     .appendPostals(contentValuesListMap.get(StructuredPostal.CONTENT_ITEM_TYPE))
- *     .appendOrganizations(contentValuesListMap.get(Organization.CONTENT_ITEM_TYPE))
- *     .appendWebsites(contentValuesListMap.get(Website.CONTENT_ITEM_TYPE))
- *     .appendPhotos(contentValuesListMap.get(Photo.CONTENT_ITEM_TYPE))
- *     .appendNotes(contentValuesListMap.get(Note.CONTENT_ITEM_TYPE))
- *     .appendEvents(contentValuesListMap.get(Event.CONTENT_ITEM_TYPE))
- *     .appendIms(contentValuesListMap.get(Im.CONTENT_ITEM_TYPE))
- *     .appendRelation(contentValuesListMap.get(Relation.CONTENT_ITEM_TYPE));
- * return builder.toString();
- */ -public class VCardBuilder { - private static final String LOG_TAG = "VCardBuilder"; - - // If you add the other element, please check all the columns are able to be - // converted to String. - // - // e.g. BLOB is not what we can handle here now. - private static final Set sAllowedAndroidPropertySet = - Collections.unmodifiableSet(new HashSet(Arrays.asList( - Nickname.CONTENT_ITEM_TYPE, Event.CONTENT_ITEM_TYPE, - Relation.CONTENT_ITEM_TYPE))); - - public static final int DEFAULT_PHONE_TYPE = Phone.TYPE_HOME; - public static final int DEFAULT_POSTAL_TYPE = StructuredPostal.TYPE_HOME; - public static final int DEFAULT_EMAIL_TYPE = Email.TYPE_OTHER; - - private static final String VCARD_DATA_VCARD = "VCARD"; - private static final String VCARD_DATA_PUBLIC = "PUBLIC"; - - private static final String VCARD_PARAM_SEPARATOR = ";"; - private static final String VCARD_END_OF_LINE = "\r\n"; - private static final String VCARD_DATA_SEPARATOR = ":"; - private static final String VCARD_ITEM_SEPARATOR = ";"; - private static final String VCARD_WS = " "; - private static final String VCARD_PARAM_EQUAL = "="; - - private static final String VCARD_PARAM_ENCODING_QP = - "ENCODING=" + VCardConstants.PARAM_ENCODING_QP; - private static final String VCARD_PARAM_ENCODING_BASE64_V21 = - "ENCODING=" + VCardConstants.PARAM_ENCODING_BASE64; - private static final String VCARD_PARAM_ENCODING_BASE64_V30 = - "ENCODING=" + VCardConstants.PARAM_ENCODING_B; - - private static final String SHIFT_JIS = "SHIFT_JIS"; - - private final int mVCardType; - - private final boolean mIsV30; - private final boolean mIsJapaneseMobilePhone; - private final boolean mOnlyOneNoteFieldIsAvailable; - private final boolean mIsDoCoMo; - private final boolean mShouldUseQuotedPrintable; - private final boolean mUsesAndroidProperty; - private final boolean mUsesDefactProperty; - private final boolean mAppendTypeParamName; - private final boolean mRefrainsQPToNameProperties; - private final boolean mNeedsToConvertPhoneticString; - - private final boolean mShouldAppendCharsetParam; - - private final String mCharset; - private final String mVCardCharsetParameter; - - private StringBuilder mBuilder; - private boolean mEndAppended; - - public VCardBuilder(final int vcardType) { - // Default charset should be used - this(vcardType, null); - } - - /** - * @param vcardType - * @param charset If null, we use default charset for export. - */ - public VCardBuilder(final int vcardType, String charset) { - mVCardType = vcardType; - - mIsV30 = VCardConfig.isV30(vcardType); - mShouldUseQuotedPrintable = VCardConfig.shouldUseQuotedPrintable(vcardType); - mIsDoCoMo = VCardConfig.isDoCoMo(vcardType); - mIsJapaneseMobilePhone = VCardConfig.needsToConvertPhoneticString(vcardType); - mOnlyOneNoteFieldIsAvailable = VCardConfig.onlyOneNoteFieldIsAvailable(vcardType); - mUsesAndroidProperty = VCardConfig.usesAndroidSpecificProperty(vcardType); - mUsesDefactProperty = VCardConfig.usesDefactProperty(vcardType); - mRefrainsQPToNameProperties = VCardConfig.shouldRefrainQPToNameProperties(vcardType); - mAppendTypeParamName = VCardConfig.appendTypeParamName(vcardType); - mNeedsToConvertPhoneticString = VCardConfig.needsToConvertPhoneticString(vcardType); - - // vCard 2.1 requires charset. - // vCard 3.0 does not allow it but we found some devices use it to determine - // the exact charset. - // We currently append it only when charset other than UTF_8 is used. - mShouldAppendCharsetParam = !(mIsV30 && "UTF-8".equalsIgnoreCase(charset)); - - if (VCardConfig.isDoCoMo(vcardType)) { - if (!SHIFT_JIS.equalsIgnoreCase(charset)) { - Log.w(LOG_TAG, - "The charset \"" + charset + "\" is used while " - + SHIFT_JIS + " is needed to be used."); - if (TextUtils.isEmpty(charset)) { - mCharset = SHIFT_JIS; - } else { - try { - charset = CharsetUtils.charsetForVendor(charset).name(); - } catch (UnsupportedCharsetException e) { - Log.i(LOG_TAG, - "Career-specific \"" + charset + "\" was not found (as usual). " - + "Use it as is."); - } - mCharset = charset; - } - } else { - if (mIsDoCoMo) { - try { - charset = CharsetUtils.charsetForVendor(SHIFT_JIS, "docomo").name(); - } catch (UnsupportedCharsetException e) { - Log.e(LOG_TAG, - "DoCoMo-specific SHIFT_JIS was not found. " - + "Use SHIFT_JIS as is."); - charset = SHIFT_JIS; - } - } else { - try { - charset = CharsetUtils.charsetForVendor(SHIFT_JIS).name(); - } catch (UnsupportedCharsetException e) { - Log.e(LOG_TAG, - "Career-specific SHIFT_JIS was not found. " - + "Use SHIFT_JIS as is."); - charset = SHIFT_JIS; - } - } - mCharset = charset; - } - mVCardCharsetParameter = "CHARSET=" + SHIFT_JIS; - } else { - if (TextUtils.isEmpty(charset)) { - Log.i(LOG_TAG, - "Use the charset \"" + VCardConfig.DEFAULT_EXPORT_CHARSET - + "\" for export."); - mCharset = VCardConfig.DEFAULT_EXPORT_CHARSET; - mVCardCharsetParameter = "CHARSET=" + VCardConfig.DEFAULT_EXPORT_CHARSET; - } else { - try { - charset = CharsetUtils.charsetForVendor(charset).name(); - } catch (UnsupportedCharsetException e) { - Log.i(LOG_TAG, - "Career-specific \"" + charset + "\" was not found (as usual). " - + "Use it as is."); - } - mCharset = charset; - mVCardCharsetParameter = "CHARSET=" + charset; - } - } - clear(); - } - - public void clear() { - mBuilder = new StringBuilder(); - mEndAppended = false; - appendLine(VCardConstants.PROPERTY_BEGIN, VCARD_DATA_VCARD); - if (mIsV30) { - appendLine(VCardConstants.PROPERTY_VERSION, VCardConstants.VERSION_V30); - } else { - appendLine(VCardConstants.PROPERTY_VERSION, VCardConstants.VERSION_V21); - } - } - - private boolean containsNonEmptyName(final ContentValues contentValues) { - final String familyName = contentValues.getAsString(StructuredName.FAMILY_NAME); - final String middleName = contentValues.getAsString(StructuredName.MIDDLE_NAME); - final String givenName = contentValues.getAsString(StructuredName.GIVEN_NAME); - final String prefix = contentValues.getAsString(StructuredName.PREFIX); - final String suffix = contentValues.getAsString(StructuredName.SUFFIX); - final String phoneticFamilyName = - contentValues.getAsString(StructuredName.PHONETIC_FAMILY_NAME); - final String phoneticMiddleName = - contentValues.getAsString(StructuredName.PHONETIC_MIDDLE_NAME); - final String phoneticGivenName = - contentValues.getAsString(StructuredName.PHONETIC_GIVEN_NAME); - final String displayName = contentValues.getAsString(StructuredName.DISPLAY_NAME); - return !(TextUtils.isEmpty(familyName) && TextUtils.isEmpty(middleName) && - TextUtils.isEmpty(givenName) && TextUtils.isEmpty(prefix) && - TextUtils.isEmpty(suffix) && TextUtils.isEmpty(phoneticFamilyName) && - TextUtils.isEmpty(phoneticMiddleName) && TextUtils.isEmpty(phoneticGivenName) && - TextUtils.isEmpty(displayName)); - } - - private ContentValues getPrimaryContentValue(final List contentValuesList) { - ContentValues primaryContentValues = null; - ContentValues subprimaryContentValues = null; - for (ContentValues contentValues : contentValuesList) { - if (contentValues == null){ - continue; - } - Integer isSuperPrimary = contentValues.getAsInteger(StructuredName.IS_SUPER_PRIMARY); - if (isSuperPrimary != null && isSuperPrimary > 0) { - // We choose "super primary" ContentValues. - primaryContentValues = contentValues; - break; - } else if (primaryContentValues == null) { - // We choose the first "primary" ContentValues - // if "super primary" ContentValues does not exist. - final Integer isPrimary = contentValues.getAsInteger(StructuredName.IS_PRIMARY); - if (isPrimary != null && isPrimary > 0 && - containsNonEmptyName(contentValues)) { - primaryContentValues = contentValues; - // Do not break, since there may be ContentValues with "super primary" - // afterword. - } else if (subprimaryContentValues == null && - containsNonEmptyName(contentValues)) { - subprimaryContentValues = contentValues; - } - } - } - - if (primaryContentValues == null) { - if (subprimaryContentValues != null) { - // We choose the first ContentValues if any "primary" ContentValues does not exist. - primaryContentValues = subprimaryContentValues; - } else { - Log.e(LOG_TAG, "All ContentValues given from database is empty."); - primaryContentValues = new ContentValues(); - } - } - - return primaryContentValues; - } - - /** - * For safety, we'll emit just one value around StructuredName, as external importers - * may get confused with multiple "N", "FN", etc. properties, though it is valid in - * vCard spec. - */ - public VCardBuilder appendNameProperties(final List contentValuesList) { - if (contentValuesList == null || contentValuesList.isEmpty()) { - if (mIsDoCoMo) { - appendLine(VCardConstants.PROPERTY_N, ""); - } else if (mIsV30) { - // vCard 3.0 requires "N" and "FN" properties. - appendLine(VCardConstants.PROPERTY_N, ""); - appendLine(VCardConstants.PROPERTY_FN, ""); - } - return this; - } - - final ContentValues contentValues = getPrimaryContentValue(contentValuesList); - final String familyName = contentValues.getAsString(StructuredName.FAMILY_NAME); - final String middleName = contentValues.getAsString(StructuredName.MIDDLE_NAME); - final String givenName = contentValues.getAsString(StructuredName.GIVEN_NAME); - final String prefix = contentValues.getAsString(StructuredName.PREFIX); - final String suffix = contentValues.getAsString(StructuredName.SUFFIX); - final String displayName = contentValues.getAsString(StructuredName.DISPLAY_NAME); - - if (!TextUtils.isEmpty(familyName) || !TextUtils.isEmpty(givenName)) { - final boolean reallyAppendCharsetParameterToName = - shouldAppendCharsetParam(familyName, givenName, middleName, prefix, suffix); - final boolean reallyUseQuotedPrintableToName = - (!mRefrainsQPToNameProperties && - !(VCardUtils.containsOnlyNonCrLfPrintableAscii(familyName) && - VCardUtils.containsOnlyNonCrLfPrintableAscii(givenName) && - VCardUtils.containsOnlyNonCrLfPrintableAscii(middleName) && - VCardUtils.containsOnlyNonCrLfPrintableAscii(prefix) && - VCardUtils.containsOnlyNonCrLfPrintableAscii(suffix))); - - final String formattedName; - if (!TextUtils.isEmpty(displayName)) { - formattedName = displayName; - } else { - formattedName = VCardUtils.constructNameFromElements( - VCardConfig.getNameOrderType(mVCardType), - familyName, middleName, givenName, prefix, suffix); - } - final boolean reallyAppendCharsetParameterToFN = - shouldAppendCharsetParam(formattedName); - final boolean reallyUseQuotedPrintableToFN = - !mRefrainsQPToNameProperties && - !VCardUtils.containsOnlyNonCrLfPrintableAscii(formattedName); - - final String encodedFamily; - final String encodedGiven; - final String encodedMiddle; - final String encodedPrefix; - final String encodedSuffix; - if (reallyUseQuotedPrintableToName) { - encodedFamily = encodeQuotedPrintable(familyName); - encodedGiven = encodeQuotedPrintable(givenName); - encodedMiddle = encodeQuotedPrintable(middleName); - encodedPrefix = encodeQuotedPrintable(prefix); - encodedSuffix = encodeQuotedPrintable(suffix); - } else { - encodedFamily = escapeCharacters(familyName); - encodedGiven = escapeCharacters(givenName); - encodedMiddle = escapeCharacters(middleName); - encodedPrefix = escapeCharacters(prefix); - encodedSuffix = escapeCharacters(suffix); - } - - final String encodedFormattedname = - (reallyUseQuotedPrintableToFN ? - encodeQuotedPrintable(formattedName) : escapeCharacters(formattedName)); - - mBuilder.append(VCardConstants.PROPERTY_N); - if (mIsDoCoMo) { - if (reallyAppendCharsetParameterToName) { - mBuilder.append(VCARD_PARAM_SEPARATOR); - mBuilder.append(mVCardCharsetParameter); - } - if (reallyUseQuotedPrintableToName) { - mBuilder.append(VCARD_PARAM_SEPARATOR); - mBuilder.append(VCARD_PARAM_ENCODING_QP); - } - mBuilder.append(VCARD_DATA_SEPARATOR); - // DoCoMo phones require that all the elements in the "family name" field. - mBuilder.append(formattedName); - mBuilder.append(VCARD_ITEM_SEPARATOR); - mBuilder.append(VCARD_ITEM_SEPARATOR); - mBuilder.append(VCARD_ITEM_SEPARATOR); - mBuilder.append(VCARD_ITEM_SEPARATOR); - } else { - if (reallyAppendCharsetParameterToName) { - mBuilder.append(VCARD_PARAM_SEPARATOR); - mBuilder.append(mVCardCharsetParameter); - } - if (reallyUseQuotedPrintableToName) { - mBuilder.append(VCARD_PARAM_SEPARATOR); - mBuilder.append(VCARD_PARAM_ENCODING_QP); - } - mBuilder.append(VCARD_DATA_SEPARATOR); - mBuilder.append(encodedFamily); - mBuilder.append(VCARD_ITEM_SEPARATOR); - mBuilder.append(encodedGiven); - mBuilder.append(VCARD_ITEM_SEPARATOR); - mBuilder.append(encodedMiddle); - mBuilder.append(VCARD_ITEM_SEPARATOR); - mBuilder.append(encodedPrefix); - mBuilder.append(VCARD_ITEM_SEPARATOR); - mBuilder.append(encodedSuffix); - } - mBuilder.append(VCARD_END_OF_LINE); - - // FN property - mBuilder.append(VCardConstants.PROPERTY_FN); - if (reallyAppendCharsetParameterToFN) { - mBuilder.append(VCARD_PARAM_SEPARATOR); - mBuilder.append(mVCardCharsetParameter); - } - if (reallyUseQuotedPrintableToFN) { - mBuilder.append(VCARD_PARAM_SEPARATOR); - mBuilder.append(VCARD_PARAM_ENCODING_QP); - } - mBuilder.append(VCARD_DATA_SEPARATOR); - mBuilder.append(encodedFormattedname); - mBuilder.append(VCARD_END_OF_LINE); - } else if (!TextUtils.isEmpty(displayName)) { - final boolean reallyUseQuotedPrintableToDisplayName = - (!mRefrainsQPToNameProperties && - !VCardUtils.containsOnlyNonCrLfPrintableAscii(displayName)); - final String encodedDisplayName = - reallyUseQuotedPrintableToDisplayName ? - encodeQuotedPrintable(displayName) : - escapeCharacters(displayName); - - mBuilder.append(VCardConstants.PROPERTY_N); - if (shouldAppendCharsetParam(displayName)) { - mBuilder.append(VCARD_PARAM_SEPARATOR); - mBuilder.append(mVCardCharsetParameter); - } - if (reallyUseQuotedPrintableToDisplayName) { - mBuilder.append(VCARD_PARAM_SEPARATOR); - mBuilder.append(VCARD_PARAM_ENCODING_QP); - } - mBuilder.append(VCARD_DATA_SEPARATOR); - mBuilder.append(encodedDisplayName); - mBuilder.append(VCARD_ITEM_SEPARATOR); - mBuilder.append(VCARD_ITEM_SEPARATOR); - mBuilder.append(VCARD_ITEM_SEPARATOR); - mBuilder.append(VCARD_ITEM_SEPARATOR); - mBuilder.append(VCARD_END_OF_LINE); - mBuilder.append(VCardConstants.PROPERTY_FN); - - // Note: "CHARSET" param is not allowed in vCard 3.0, but we may add it - // when it would be useful or necessary for external importers, - // assuming the external importer allows this vioration of the spec. - if (shouldAppendCharsetParam(displayName)) { - mBuilder.append(VCARD_PARAM_SEPARATOR); - mBuilder.append(mVCardCharsetParameter); - } - mBuilder.append(VCARD_DATA_SEPARATOR); - mBuilder.append(encodedDisplayName); - mBuilder.append(VCARD_END_OF_LINE); - } else if (mIsV30) { - // vCard 3.0 specification requires these fields. - appendLine(VCardConstants.PROPERTY_N, ""); - appendLine(VCardConstants.PROPERTY_FN, ""); - } else if (mIsDoCoMo) { - appendLine(VCardConstants.PROPERTY_N, ""); - } - - appendPhoneticNameFields(contentValues); - return this; - } - - private void appendPhoneticNameFields(final ContentValues contentValues) { - final String phoneticFamilyName; - final String phoneticMiddleName; - final String phoneticGivenName; - { - final String tmpPhoneticFamilyName = - contentValues.getAsString(StructuredName.PHONETIC_FAMILY_NAME); - final String tmpPhoneticMiddleName = - contentValues.getAsString(StructuredName.PHONETIC_MIDDLE_NAME); - final String tmpPhoneticGivenName = - contentValues.getAsString(StructuredName.PHONETIC_GIVEN_NAME); - if (mNeedsToConvertPhoneticString) { - phoneticFamilyName = VCardUtils.toHalfWidthString(tmpPhoneticFamilyName); - phoneticMiddleName = VCardUtils.toHalfWidthString(tmpPhoneticMiddleName); - phoneticGivenName = VCardUtils.toHalfWidthString(tmpPhoneticGivenName); - } else { - phoneticFamilyName = tmpPhoneticFamilyName; - phoneticMiddleName = tmpPhoneticMiddleName; - phoneticGivenName = tmpPhoneticGivenName; - } - } - - if (TextUtils.isEmpty(phoneticFamilyName) - && TextUtils.isEmpty(phoneticMiddleName) - && TextUtils.isEmpty(phoneticGivenName)) { - if (mIsDoCoMo) { - mBuilder.append(VCardConstants.PROPERTY_SOUND); - mBuilder.append(VCARD_PARAM_SEPARATOR); - mBuilder.append(VCardConstants.PARAM_TYPE_X_IRMC_N); - mBuilder.append(VCARD_DATA_SEPARATOR); - mBuilder.append(VCARD_ITEM_SEPARATOR); - mBuilder.append(VCARD_ITEM_SEPARATOR); - mBuilder.append(VCARD_ITEM_SEPARATOR); - mBuilder.append(VCARD_ITEM_SEPARATOR); - mBuilder.append(VCARD_END_OF_LINE); - } - return; - } - - // Try to emit the field(s) related to phonetic name. - if (mIsV30) { - final String sortString = VCardUtils - .constructNameFromElements(mVCardType, - phoneticFamilyName, phoneticMiddleName, phoneticGivenName); - mBuilder.append(VCardConstants.PROPERTY_SORT_STRING); - if (shouldAppendCharsetParam(sortString)) { - mBuilder.append(VCARD_PARAM_SEPARATOR); - mBuilder.append(mVCardCharsetParameter); - } - mBuilder.append(VCARD_DATA_SEPARATOR); - mBuilder.append(escapeCharacters(sortString)); - mBuilder.append(VCARD_END_OF_LINE); - } else if (mIsJapaneseMobilePhone) { - // Note: There is no appropriate property for expressing - // phonetic name (Yomigana in Japanese) in vCard 2.1, while there is in - // vCard 3.0 (SORT-STRING). - // We use DoCoMo's way when the device is Japanese one since it is already - // supported by a lot of Japanese mobile phones. - // This is "X-" property, so any parser hopefully would not get - // confused with this. - // - // Also, DoCoMo's specification requires vCard composer to use just the first - // column. - // i.e. - // good: SOUND;X-IRMC-N:Miyakawa Daisuke;;;; - // bad : SOUND;X-IRMC-N:Miyakawa;Daisuke;;; - mBuilder.append(VCardConstants.PROPERTY_SOUND); - mBuilder.append(VCARD_PARAM_SEPARATOR); - mBuilder.append(VCardConstants.PARAM_TYPE_X_IRMC_N); - - boolean reallyUseQuotedPrintable = - (!mRefrainsQPToNameProperties - && !(VCardUtils.containsOnlyNonCrLfPrintableAscii( - phoneticFamilyName) - && VCardUtils.containsOnlyNonCrLfPrintableAscii( - phoneticMiddleName) - && VCardUtils.containsOnlyNonCrLfPrintableAscii( - phoneticGivenName))); - - final String encodedPhoneticFamilyName; - final String encodedPhoneticMiddleName; - final String encodedPhoneticGivenName; - if (reallyUseQuotedPrintable) { - encodedPhoneticFamilyName = encodeQuotedPrintable(phoneticFamilyName); - encodedPhoneticMiddleName = encodeQuotedPrintable(phoneticMiddleName); - encodedPhoneticGivenName = encodeQuotedPrintable(phoneticGivenName); - } else { - encodedPhoneticFamilyName = escapeCharacters(phoneticFamilyName); - encodedPhoneticMiddleName = escapeCharacters(phoneticMiddleName); - encodedPhoneticGivenName = escapeCharacters(phoneticGivenName); - } - - if (shouldAppendCharsetParam(encodedPhoneticFamilyName, - encodedPhoneticMiddleName, encodedPhoneticGivenName)) { - mBuilder.append(VCARD_PARAM_SEPARATOR); - mBuilder.append(mVCardCharsetParameter); - } - mBuilder.append(VCARD_DATA_SEPARATOR); - { - boolean first = true; - if (!TextUtils.isEmpty(encodedPhoneticFamilyName)) { - mBuilder.append(encodedPhoneticFamilyName); - first = false; - } - if (!TextUtils.isEmpty(encodedPhoneticMiddleName)) { - if (first) { - first = false; - } else { - mBuilder.append(' '); - } - mBuilder.append(encodedPhoneticMiddleName); - } - if (!TextUtils.isEmpty(encodedPhoneticGivenName)) { - if (!first) { - mBuilder.append(' '); - } - mBuilder.append(encodedPhoneticGivenName); - } - } - mBuilder.append(VCARD_ITEM_SEPARATOR); // family;given - mBuilder.append(VCARD_ITEM_SEPARATOR); // given;middle - mBuilder.append(VCARD_ITEM_SEPARATOR); // middle;prefix - mBuilder.append(VCARD_ITEM_SEPARATOR); // prefix;suffix - mBuilder.append(VCARD_END_OF_LINE); - } - - if (mUsesDefactProperty) { - if (!TextUtils.isEmpty(phoneticGivenName)) { - final boolean reallyUseQuotedPrintable = - (mShouldUseQuotedPrintable && - !VCardUtils.containsOnlyNonCrLfPrintableAscii(phoneticGivenName)); - final String encodedPhoneticGivenName; - if (reallyUseQuotedPrintable) { - encodedPhoneticGivenName = encodeQuotedPrintable(phoneticGivenName); - } else { - encodedPhoneticGivenName = escapeCharacters(phoneticGivenName); - } - mBuilder.append(VCardConstants.PROPERTY_X_PHONETIC_FIRST_NAME); - if (shouldAppendCharsetParam(phoneticGivenName)) { - mBuilder.append(VCARD_PARAM_SEPARATOR); - mBuilder.append(mVCardCharsetParameter); - } - if (reallyUseQuotedPrintable) { - mBuilder.append(VCARD_PARAM_SEPARATOR); - mBuilder.append(VCARD_PARAM_ENCODING_QP); - } - mBuilder.append(VCARD_DATA_SEPARATOR); - mBuilder.append(encodedPhoneticGivenName); - mBuilder.append(VCARD_END_OF_LINE); - } // if (!TextUtils.isEmpty(phoneticGivenName)) - if (!TextUtils.isEmpty(phoneticMiddleName)) { - final boolean reallyUseQuotedPrintable = - (mShouldUseQuotedPrintable && - !VCardUtils.containsOnlyNonCrLfPrintableAscii(phoneticMiddleName)); - final String encodedPhoneticMiddleName; - if (reallyUseQuotedPrintable) { - encodedPhoneticMiddleName = encodeQuotedPrintable(phoneticMiddleName); - } else { - encodedPhoneticMiddleName = escapeCharacters(phoneticMiddleName); - } - mBuilder.append(VCardConstants.PROPERTY_X_PHONETIC_MIDDLE_NAME); - if (shouldAppendCharsetParam(phoneticMiddleName)) { - mBuilder.append(VCARD_PARAM_SEPARATOR); - mBuilder.append(mVCardCharsetParameter); - } - if (reallyUseQuotedPrintable) { - mBuilder.append(VCARD_PARAM_SEPARATOR); - mBuilder.append(VCARD_PARAM_ENCODING_QP); - } - mBuilder.append(VCARD_DATA_SEPARATOR); - mBuilder.append(encodedPhoneticMiddleName); - mBuilder.append(VCARD_END_OF_LINE); - } // if (!TextUtils.isEmpty(phoneticGivenName)) - if (!TextUtils.isEmpty(phoneticFamilyName)) { - final boolean reallyUseQuotedPrintable = - (mShouldUseQuotedPrintable && - !VCardUtils.containsOnlyNonCrLfPrintableAscii(phoneticFamilyName)); - final String encodedPhoneticFamilyName; - if (reallyUseQuotedPrintable) { - encodedPhoneticFamilyName = encodeQuotedPrintable(phoneticFamilyName); - } else { - encodedPhoneticFamilyName = escapeCharacters(phoneticFamilyName); - } - mBuilder.append(VCardConstants.PROPERTY_X_PHONETIC_LAST_NAME); - if (shouldAppendCharsetParam(phoneticFamilyName)) { - mBuilder.append(VCARD_PARAM_SEPARATOR); - mBuilder.append(mVCardCharsetParameter); - } - if (reallyUseQuotedPrintable) { - mBuilder.append(VCARD_PARAM_SEPARATOR); - mBuilder.append(VCARD_PARAM_ENCODING_QP); - } - mBuilder.append(VCARD_DATA_SEPARATOR); - mBuilder.append(encodedPhoneticFamilyName); - mBuilder.append(VCARD_END_OF_LINE); - } // if (!TextUtils.isEmpty(phoneticFamilyName)) - } - } - - public VCardBuilder appendNickNames(final List contentValuesList) { - final boolean useAndroidProperty; - if (mIsV30) { - useAndroidProperty = false; - } else if (mUsesAndroidProperty) { - useAndroidProperty = true; - } else { - // There's no way to add this field. - return this; - } - if (contentValuesList != null) { - for (ContentValues contentValues : contentValuesList) { - final String nickname = contentValues.getAsString(Nickname.NAME); - if (TextUtils.isEmpty(nickname)) { - continue; - } - if (useAndroidProperty) { - appendAndroidSpecificProperty(Nickname.CONTENT_ITEM_TYPE, contentValues); - } else { - appendLineWithCharsetAndQPDetection(VCardConstants.PROPERTY_NICKNAME, nickname); - } - } - } - return this; - } - - public VCardBuilder appendPhones(final List contentValuesList) { - boolean phoneLineExists = false; - if (contentValuesList != null) { - Set phoneSet = new HashSet(); - for (ContentValues contentValues : contentValuesList) { - final Integer typeAsObject = contentValues.getAsInteger(Phone.TYPE); - final String label = contentValues.getAsString(Phone.LABEL); - final Integer isPrimaryAsInteger = contentValues.getAsInteger(Phone.IS_PRIMARY); - final boolean isPrimary = (isPrimaryAsInteger != null ? - (isPrimaryAsInteger > 0) : false); - String phoneNumber = contentValues.getAsString(Phone.NUMBER); - if (phoneNumber != null) { - phoneNumber = phoneNumber.trim(); - } - if (TextUtils.isEmpty(phoneNumber)) { - continue; - } - - // PAGER number needs unformatted "phone number". - final int type = (typeAsObject != null ? typeAsObject : DEFAULT_PHONE_TYPE); - if (type == Phone.TYPE_PAGER || - VCardConfig.refrainPhoneNumberFormatting(mVCardType)) { - phoneLineExists = true; - if (!phoneSet.contains(phoneNumber)) { - phoneSet.add(phoneNumber); - appendTelLine(type, label, phoneNumber, isPrimary); - } - } else { - final List phoneNumberList = splitAndTrimPhoneNumbers(phoneNumber); - if (phoneNumberList.isEmpty()) { - continue; - } - phoneLineExists = true; - for (String actualPhoneNumber : phoneNumberList) { - if (!phoneSet.contains(actualPhoneNumber)) { - final int format = VCardUtils.getPhoneNumberFormat(mVCardType); - final String formattedPhoneNumber = - PhoneNumberUtils.formatNumber(actualPhoneNumber, format); - phoneSet.add(actualPhoneNumber); - appendTelLine(type, label, formattedPhoneNumber, isPrimary); - } - } // for (String actualPhoneNumber : phoneNumberList) { - } - } - } - - if (!phoneLineExists && mIsDoCoMo) { - appendTelLine(Phone.TYPE_HOME, "", "", false); - } - - return this; - } - - /** - *

- * Splits a given string expressing phone numbers into several strings, and remove - * unnecessary characters inside them. The size of a returned list becomes 1 when - * no split is needed. - *

- *

- * The given number "may" have several phone numbers when the contact entry is corrupted - * because of its original source. - * e.g. "111-222-3333 (Miami)\n444-555-6666 (Broward; 305-653-6796 (Miami)" - *

- *

- * This kind of "phone numbers" will not be created with Android vCard implementation, - * but we may encounter them if the source of the input data has already corrupted - * implementation. - *

- *

- * To handle this case, this method first splits its input into multiple parts - * (e.g. "111-222-3333 (Miami)", "444-555-6666 (Broward", and 305653-6796 (Miami)") and - * removes unnecessary strings like "(Miami)". - *

- *

- * Do not call this method when trimming is inappropriate for its receivers. - *

- */ - private List splitAndTrimPhoneNumbers(final String phoneNumber) { - final List phoneList = new ArrayList(); - - StringBuilder builder = new StringBuilder(); - final int length = phoneNumber.length(); - for (int i = 0; i < length; i++) { - final char ch = phoneNumber.charAt(i); - if (Character.isDigit(ch) || ch == '+') { - builder.append(ch); - } else if ((ch == ';' || ch == '\n') && builder.length() > 0) { - phoneList.add(builder.toString()); - builder = new StringBuilder(); - } - } - if (builder.length() > 0) { - phoneList.add(builder.toString()); - } - - return phoneList; - } - - public VCardBuilder appendEmails(final List contentValuesList) { - boolean emailAddressExists = false; - if (contentValuesList != null) { - final Set addressSet = new HashSet(); - for (ContentValues contentValues : contentValuesList) { - String emailAddress = contentValues.getAsString(Email.DATA); - if (emailAddress != null) { - emailAddress = emailAddress.trim(); - } - if (TextUtils.isEmpty(emailAddress)) { - continue; - } - Integer typeAsObject = contentValues.getAsInteger(Email.TYPE); - final int type = (typeAsObject != null ? - typeAsObject : DEFAULT_EMAIL_TYPE); - final String label = contentValues.getAsString(Email.LABEL); - Integer isPrimaryAsInteger = contentValues.getAsInteger(Email.IS_PRIMARY); - final boolean isPrimary = (isPrimaryAsInteger != null ? - (isPrimaryAsInteger > 0) : false); - emailAddressExists = true; - if (!addressSet.contains(emailAddress)) { - addressSet.add(emailAddress); - appendEmailLine(type, label, emailAddress, isPrimary); - } - } - } - - if (!emailAddressExists && mIsDoCoMo) { - appendEmailLine(Email.TYPE_HOME, "", "", false); - } - - return this; - } - - public VCardBuilder appendPostals(final List contentValuesList) { - if (contentValuesList == null || contentValuesList.isEmpty()) { - if (mIsDoCoMo) { - mBuilder.append(VCardConstants.PROPERTY_ADR); - mBuilder.append(VCARD_PARAM_SEPARATOR); - mBuilder.append(VCardConstants.PARAM_TYPE_HOME); - mBuilder.append(VCARD_DATA_SEPARATOR); - mBuilder.append(VCARD_END_OF_LINE); - } - } else { - if (mIsDoCoMo) { - appendPostalsForDoCoMo(contentValuesList); - } else { - appendPostalsForGeneric(contentValuesList); - } - } - - return this; - } - - private static final Map sPostalTypePriorityMap; - - static { - sPostalTypePriorityMap = new HashMap(); - sPostalTypePriorityMap.put(StructuredPostal.TYPE_HOME, 0); - sPostalTypePriorityMap.put(StructuredPostal.TYPE_WORK, 1); - sPostalTypePriorityMap.put(StructuredPostal.TYPE_OTHER, 2); - sPostalTypePriorityMap.put(StructuredPostal.TYPE_CUSTOM, 3); - } - - /** - * Tries to append just one line. If there's no appropriate address - * information, append an empty line. - */ - private void appendPostalsForDoCoMo(final List contentValuesList) { - int currentPriority = Integer.MAX_VALUE; - int currentType = Integer.MAX_VALUE; - ContentValues currentContentValues = null; - for (final ContentValues contentValues : contentValuesList) { - if (contentValues == null) { - continue; - } - final Integer typeAsInteger = contentValues.getAsInteger(StructuredPostal.TYPE); - final Integer priorityAsInteger = sPostalTypePriorityMap.get(typeAsInteger); - final int priority = - (priorityAsInteger != null ? priorityAsInteger : Integer.MAX_VALUE); - if (priority < currentPriority) { - currentPriority = priority; - currentType = typeAsInteger; - currentContentValues = contentValues; - if (priority == 0) { - break; - } - } - } - - if (currentContentValues == null) { - Log.w(LOG_TAG, "Should not come here. Must have at least one postal data."); - return; - } - - final String label = currentContentValues.getAsString(StructuredPostal.LABEL); - appendPostalLine(currentType, label, currentContentValues, false, true); - } - - private void appendPostalsForGeneric(final List contentValuesList) { - for (final ContentValues contentValues : contentValuesList) { - if (contentValues == null) { - continue; - } - final Integer typeAsInteger = contentValues.getAsInteger(StructuredPostal.TYPE); - final int type = (typeAsInteger != null ? - typeAsInteger : DEFAULT_POSTAL_TYPE); - final String label = contentValues.getAsString(StructuredPostal.LABEL); - final Integer isPrimaryAsInteger = - contentValues.getAsInteger(StructuredPostal.IS_PRIMARY); - final boolean isPrimary = (isPrimaryAsInteger != null ? - (isPrimaryAsInteger > 0) : false); - appendPostalLine(type, label, contentValues, isPrimary, false); - } - } - - private static class PostalStruct { - final boolean reallyUseQuotedPrintable; - final boolean appendCharset; - final String addressData; - public PostalStruct(final boolean reallyUseQuotedPrintable, - final boolean appendCharset, final String addressData) { - this.reallyUseQuotedPrintable = reallyUseQuotedPrintable; - this.appendCharset = appendCharset; - this.addressData = addressData; - } - } - - /** - * @return null when there's no information available to construct the data. - */ - private PostalStruct tryConstructPostalStruct(ContentValues contentValues) { - // adr-value = 0*6(text-value ";") text-value - // ; PO Box, Extended Address, Street, Locality, Region, Postal - // ; Code, Country Name - final String rawPoBox = contentValues.getAsString(StructuredPostal.POBOX); - final String rawNeighborhood = contentValues.getAsString(StructuredPostal.NEIGHBORHOOD); - final String rawStreet = contentValues.getAsString(StructuredPostal.STREET); - final String rawLocality = contentValues.getAsString(StructuredPostal.CITY); - final String rawRegion = contentValues.getAsString(StructuredPostal.REGION); - final String rawPostalCode = contentValues.getAsString(StructuredPostal.POSTCODE); - final String rawCountry = contentValues.getAsString(StructuredPostal.COUNTRY); - final String[] rawAddressArray = new String[]{ - rawPoBox, rawNeighborhood, rawStreet, rawLocality, - rawRegion, rawPostalCode, rawCountry}; - if (!VCardUtils.areAllEmpty(rawAddressArray)) { - final boolean reallyUseQuotedPrintable = - (mShouldUseQuotedPrintable && - !VCardUtils.containsOnlyNonCrLfPrintableAscii(rawAddressArray)); - final boolean appendCharset = - !VCardUtils.containsOnlyPrintableAscii(rawAddressArray); - final String encodedPoBox; - final String encodedStreet; - final String encodedLocality; - final String encodedRegion; - final String encodedPostalCode; - final String encodedCountry; - final String encodedNeighborhood; - - final String rawLocality2; - // This looks inefficient since we encode rawLocality and rawNeighborhood twice, - // but this is intentional. - // - // QP encoding may add line feeds when needed and the result of - // - encodeQuotedPrintable(rawLocality + " " + rawNeighborhood) - // may be different from - // - encodedLocality + " " + encodedNeighborhood. - // - // We use safer way. - if (TextUtils.isEmpty(rawLocality)) { - if (TextUtils.isEmpty(rawNeighborhood)) { - rawLocality2 = ""; - } else { - rawLocality2 = rawNeighborhood; - } - } else { - if (TextUtils.isEmpty(rawNeighborhood)) { - rawLocality2 = rawLocality; - } else { - rawLocality2 = rawLocality + " " + rawNeighborhood; - } - } - if (reallyUseQuotedPrintable) { - encodedPoBox = encodeQuotedPrintable(rawPoBox); - encodedStreet = encodeQuotedPrintable(rawStreet); - encodedLocality = encodeQuotedPrintable(rawLocality2); - encodedRegion = encodeQuotedPrintable(rawRegion); - encodedPostalCode = encodeQuotedPrintable(rawPostalCode); - encodedCountry = encodeQuotedPrintable(rawCountry); - } else { - encodedPoBox = escapeCharacters(rawPoBox); - encodedStreet = escapeCharacters(rawStreet); - encodedLocality = escapeCharacters(rawLocality2); - encodedRegion = escapeCharacters(rawRegion); - encodedPostalCode = escapeCharacters(rawPostalCode); - encodedCountry = escapeCharacters(rawCountry); - encodedNeighborhood = escapeCharacters(rawNeighborhood); - } - final StringBuilder addressBuilder = new StringBuilder(); - addressBuilder.append(encodedPoBox); - addressBuilder.append(VCARD_ITEM_SEPARATOR); // PO BOX ; Extended Address - addressBuilder.append(VCARD_ITEM_SEPARATOR); // Extended Address : Street - addressBuilder.append(encodedStreet); - addressBuilder.append(VCARD_ITEM_SEPARATOR); // Street : Locality - addressBuilder.append(encodedLocality); - addressBuilder.append(VCARD_ITEM_SEPARATOR); // Locality : Region - addressBuilder.append(encodedRegion); - addressBuilder.append(VCARD_ITEM_SEPARATOR); // Region : Postal Code - addressBuilder.append(encodedPostalCode); - addressBuilder.append(VCARD_ITEM_SEPARATOR); // Postal Code : Country - addressBuilder.append(encodedCountry); - return new PostalStruct( - reallyUseQuotedPrintable, appendCharset, addressBuilder.toString()); - } else { // VCardUtils.areAllEmpty(rawAddressArray) == true - // Try to use FORMATTED_ADDRESS instead. - final String rawFormattedAddress = - contentValues.getAsString(StructuredPostal.FORMATTED_ADDRESS); - if (TextUtils.isEmpty(rawFormattedAddress)) { - return null; - } - final boolean reallyUseQuotedPrintable = - (mShouldUseQuotedPrintable && - !VCardUtils.containsOnlyNonCrLfPrintableAscii(rawFormattedAddress)); - final boolean appendCharset = - !VCardUtils.containsOnlyPrintableAscii(rawFormattedAddress); - final String encodedFormattedAddress; - if (reallyUseQuotedPrintable) { - encodedFormattedAddress = encodeQuotedPrintable(rawFormattedAddress); - } else { - encodedFormattedAddress = escapeCharacters(rawFormattedAddress); - } - - // We use the second value ("Extended Address") just because Japanese mobile phones - // do so. If the other importer expects the value be in the other field, some flag may - // be needed. - final StringBuilder addressBuilder = new StringBuilder(); - addressBuilder.append(VCARD_ITEM_SEPARATOR); // PO BOX ; Extended Address - addressBuilder.append(encodedFormattedAddress); - addressBuilder.append(VCARD_ITEM_SEPARATOR); // Extended Address : Street - addressBuilder.append(VCARD_ITEM_SEPARATOR); // Street : Locality - addressBuilder.append(VCARD_ITEM_SEPARATOR); // Locality : Region - addressBuilder.append(VCARD_ITEM_SEPARATOR); // Region : Postal Code - addressBuilder.append(VCARD_ITEM_SEPARATOR); // Postal Code : Country - return new PostalStruct( - reallyUseQuotedPrintable, appendCharset, addressBuilder.toString()); - } - } - - public VCardBuilder appendIms(final List contentValuesList) { - if (contentValuesList != null) { - for (ContentValues contentValues : contentValuesList) { - final Integer protocolAsObject = contentValues.getAsInteger(Im.PROTOCOL); - if (protocolAsObject == null) { - continue; - } - final String propertyName = VCardUtils.getPropertyNameForIm(protocolAsObject); - if (propertyName == null) { - continue; - } - String data = contentValues.getAsString(Im.DATA); - if (data != null) { - data = data.trim(); - } - if (TextUtils.isEmpty(data)) { - continue; - } - final String typeAsString; - { - final Integer typeAsInteger = contentValues.getAsInteger(Im.TYPE); - switch (typeAsInteger != null ? typeAsInteger : Im.TYPE_OTHER) { - case Im.TYPE_HOME: { - typeAsString = VCardConstants.PARAM_TYPE_HOME; - break; - } - case Im.TYPE_WORK: { - typeAsString = VCardConstants.PARAM_TYPE_WORK; - break; - } - case Im.TYPE_CUSTOM: { - final String label = contentValues.getAsString(Im.LABEL); - typeAsString = (label != null ? "X-" + label : null); - break; - } - case Im.TYPE_OTHER: // Ignore - default: { - typeAsString = null; - break; - } - } - } - - final List parameterList = new ArrayList(); - if (!TextUtils.isEmpty(typeAsString)) { - parameterList.add(typeAsString); - } - final Integer isPrimaryAsInteger = contentValues.getAsInteger(Im.IS_PRIMARY); - final boolean isPrimary = (isPrimaryAsInteger != null ? - (isPrimaryAsInteger > 0) : false); - if (isPrimary) { - parameterList.add(VCardConstants.PARAM_TYPE_PREF); - } - - appendLineWithCharsetAndQPDetection(propertyName, parameterList, data); - } - } - return this; - } - - public VCardBuilder appendWebsites(final List contentValuesList) { - if (contentValuesList != null) { - for (ContentValues contentValues : contentValuesList) { - String website = contentValues.getAsString(Website.URL); - if (website != null) { - website = website.trim(); - } - - // Note: vCard 3.0 does not allow any parameter addition toward "URL" - // property, while there's no document in vCard 2.1. - if (!TextUtils.isEmpty(website)) { - appendLineWithCharsetAndQPDetection(VCardConstants.PROPERTY_URL, website); - } - } - } - return this; - } - - public VCardBuilder appendOrganizations(final List contentValuesList) { - if (contentValuesList != null) { - for (ContentValues contentValues : contentValuesList) { - String company = contentValues.getAsString(Organization.COMPANY); - if (company != null) { - company = company.trim(); - } - String department = contentValues.getAsString(Organization.DEPARTMENT); - if (department != null) { - department = department.trim(); - } - String title = contentValues.getAsString(Organization.TITLE); - if (title != null) { - title = title.trim(); - } - - StringBuilder orgBuilder = new StringBuilder(); - if (!TextUtils.isEmpty(company)) { - orgBuilder.append(company); - } - if (!TextUtils.isEmpty(department)) { - if (orgBuilder.length() > 0) { - orgBuilder.append(';'); - } - orgBuilder.append(department); - } - final String orgline = orgBuilder.toString(); - appendLine(VCardConstants.PROPERTY_ORG, orgline, - !VCardUtils.containsOnlyPrintableAscii(orgline), - (mShouldUseQuotedPrintable && - !VCardUtils.containsOnlyNonCrLfPrintableAscii(orgline))); - - if (!TextUtils.isEmpty(title)) { - appendLine(VCardConstants.PROPERTY_TITLE, title, - !VCardUtils.containsOnlyPrintableAscii(title), - (mShouldUseQuotedPrintable && - !VCardUtils.containsOnlyNonCrLfPrintableAscii(title))); - } - } - } - return this; - } - - public VCardBuilder appendPhotos(final List contentValuesList) { - if (contentValuesList != null) { - for (ContentValues contentValues : contentValuesList) { - if (contentValues == null) { - continue; - } - byte[] data = contentValues.getAsByteArray(Photo.PHOTO); - if (data == null) { - continue; - } - final String photoType = VCardUtils.guessImageType(data); - if (photoType == null) { - Log.d(LOG_TAG, "Unknown photo type. Ignored."); - continue; - } - // TODO: check this works fine. - final String photoString = new String(Base64.encode(data, Base64.NO_WRAP)); - if (!TextUtils.isEmpty(photoString)) { - appendPhotoLine(photoString, photoType); - } - } - } - return this; - } - - public VCardBuilder appendNotes(final List contentValuesList) { - if (contentValuesList != null) { - if (mOnlyOneNoteFieldIsAvailable) { - final StringBuilder noteBuilder = new StringBuilder(); - boolean first = true; - for (final ContentValues contentValues : contentValuesList) { - String note = contentValues.getAsString(Note.NOTE); - if (note == null) { - note = ""; - } - if (note.length() > 0) { - if (first) { - first = false; - } else { - noteBuilder.append('\n'); - } - noteBuilder.append(note); - } - } - final String noteStr = noteBuilder.toString(); - // This means we scan noteStr completely twice, which is redundant. - // But for now, we assume this is not so time-consuming.. - final boolean shouldAppendCharsetInfo = - !VCardUtils.containsOnlyPrintableAscii(noteStr); - final boolean reallyUseQuotedPrintable = - (mShouldUseQuotedPrintable && - !VCardUtils.containsOnlyNonCrLfPrintableAscii(noteStr)); - appendLine(VCardConstants.PROPERTY_NOTE, noteStr, - shouldAppendCharsetInfo, reallyUseQuotedPrintable); - } else { - for (ContentValues contentValues : contentValuesList) { - final String noteStr = contentValues.getAsString(Note.NOTE); - if (!TextUtils.isEmpty(noteStr)) { - final boolean shouldAppendCharsetInfo = - !VCardUtils.containsOnlyPrintableAscii(noteStr); - final boolean reallyUseQuotedPrintable = - (mShouldUseQuotedPrintable && - !VCardUtils.containsOnlyNonCrLfPrintableAscii(noteStr)); - appendLine(VCardConstants.PROPERTY_NOTE, noteStr, - shouldAppendCharsetInfo, reallyUseQuotedPrintable); - } - } - } - } - return this; - } - - public VCardBuilder appendEvents(final List contentValuesList) { - // There's possibility where a given object may have more than one birthday, which - // is inappropriate. We just build one birthday. - if (contentValuesList != null) { - String primaryBirthday = null; - String secondaryBirthday = null; - for (final ContentValues contentValues : contentValuesList) { - if (contentValues == null) { - continue; - } - final Integer eventTypeAsInteger = contentValues.getAsInteger(Event.TYPE); - final int eventType; - if (eventTypeAsInteger != null) { - eventType = eventTypeAsInteger; - } else { - eventType = Event.TYPE_OTHER; - } - if (eventType == Event.TYPE_BIRTHDAY) { - final String birthdayCandidate = contentValues.getAsString(Event.START_DATE); - if (birthdayCandidate == null) { - continue; - } - final Integer isSuperPrimaryAsInteger = - contentValues.getAsInteger(Event.IS_SUPER_PRIMARY); - final boolean isSuperPrimary = (isSuperPrimaryAsInteger != null ? - (isSuperPrimaryAsInteger > 0) : false); - if (isSuperPrimary) { - // "super primary" birthday should the prefered one. - primaryBirthday = birthdayCandidate; - break; - } - final Integer isPrimaryAsInteger = - contentValues.getAsInteger(Event.IS_PRIMARY); - final boolean isPrimary = (isPrimaryAsInteger != null ? - (isPrimaryAsInteger > 0) : false); - if (isPrimary) { - // We don't break here since "super primary" birthday may exist later. - primaryBirthday = birthdayCandidate; - } else if (secondaryBirthday == null) { - // First entry is set to the "secondary" candidate. - secondaryBirthday = birthdayCandidate; - } - } else if (mUsesAndroidProperty) { - // Event types other than Birthday is not supported by vCard. - appendAndroidSpecificProperty(Event.CONTENT_ITEM_TYPE, contentValues); - } - } - if (primaryBirthday != null) { - appendLineWithCharsetAndQPDetection(VCardConstants.PROPERTY_BDAY, - primaryBirthday.trim()); - } else if (secondaryBirthday != null){ - appendLineWithCharsetAndQPDetection(VCardConstants.PROPERTY_BDAY, - secondaryBirthday.trim()); - } - } - return this; - } - - public VCardBuilder appendRelation(final List contentValuesList) { - if (mUsesAndroidProperty && contentValuesList != null) { - for (final ContentValues contentValues : contentValuesList) { - if (contentValues == null) { - continue; - } - appendAndroidSpecificProperty(Relation.CONTENT_ITEM_TYPE, contentValues); - } - } - return this; - } - - /** - * @param emitEveryTime If true, builder builds the line even when there's no entry. - */ - public void appendPostalLine(final int type, final String label, - final ContentValues contentValues, - final boolean isPrimary, final boolean emitEveryTime) { - final boolean reallyUseQuotedPrintable; - final boolean appendCharset; - final String addressValue; - { - PostalStruct postalStruct = tryConstructPostalStruct(contentValues); - if (postalStruct == null) { - if (emitEveryTime) { - reallyUseQuotedPrintable = false; - appendCharset = false; - addressValue = ""; - } else { - return; - } - } else { - reallyUseQuotedPrintable = postalStruct.reallyUseQuotedPrintable; - appendCharset = postalStruct.appendCharset; - addressValue = postalStruct.addressData; - } - } - - List parameterList = new ArrayList(); - if (isPrimary) { - parameterList.add(VCardConstants.PARAM_TYPE_PREF); - } - switch (type) { - case StructuredPostal.TYPE_HOME: { - parameterList.add(VCardConstants.PARAM_TYPE_HOME); - break; - } - case StructuredPostal.TYPE_WORK: { - parameterList.add(VCardConstants.PARAM_TYPE_WORK); - break; - } - case StructuredPostal.TYPE_CUSTOM: { - if (!TextUtils.isEmpty(label) - && VCardUtils.containsOnlyAlphaDigitHyphen(label)) { - // We're not sure whether the label is valid in the spec - // ("IANA-token" in the vCard 3.0 is unclear...) - // Just for safety, we add "X-" at the beggining of each label. - // Also checks the label obeys with vCard 3.0 spec. - parameterList.add("X-" + label); - } - break; - } - case StructuredPostal.TYPE_OTHER: { - break; - } - default: { - Log.e(LOG_TAG, "Unknown StructuredPostal type: " + type); - break; - } - } - - mBuilder.append(VCardConstants.PROPERTY_ADR); - if (!parameterList.isEmpty()) { - mBuilder.append(VCARD_PARAM_SEPARATOR); - appendTypeParameters(parameterList); - } - if (appendCharset) { - // Strictly, vCard 3.0 does not allow exporters to emit charset information, - // but we will add it since the information should be useful for importers, - // - // Assume no parser does not emit error with this parameter in vCard 3.0. - mBuilder.append(VCARD_PARAM_SEPARATOR); - mBuilder.append(mVCardCharsetParameter); - } - if (reallyUseQuotedPrintable) { - mBuilder.append(VCARD_PARAM_SEPARATOR); - mBuilder.append(VCARD_PARAM_ENCODING_QP); - } - mBuilder.append(VCARD_DATA_SEPARATOR); - mBuilder.append(addressValue); - mBuilder.append(VCARD_END_OF_LINE); - } - - public void appendEmailLine(final int type, final String label, - final String rawValue, final boolean isPrimary) { - final String typeAsString; - switch (type) { - case Email.TYPE_CUSTOM: { - if (VCardUtils.isMobilePhoneLabel(label)) { - typeAsString = VCardConstants.PARAM_TYPE_CELL; - } else if (!TextUtils.isEmpty(label) - && VCardUtils.containsOnlyAlphaDigitHyphen(label)) { - typeAsString = "X-" + label; - } else { - typeAsString = null; - } - break; - } - case Email.TYPE_HOME: { - typeAsString = VCardConstants.PARAM_TYPE_HOME; - break; - } - case Email.TYPE_WORK: { - typeAsString = VCardConstants.PARAM_TYPE_WORK; - break; - } - case Email.TYPE_OTHER: { - typeAsString = null; - break; - } - case Email.TYPE_MOBILE: { - typeAsString = VCardConstants.PARAM_TYPE_CELL; - break; - } - default: { - Log.e(LOG_TAG, "Unknown Email type: " + type); - typeAsString = null; - break; - } - } - - final List parameterList = new ArrayList(); - if (isPrimary) { - parameterList.add(VCardConstants.PARAM_TYPE_PREF); - } - if (!TextUtils.isEmpty(typeAsString)) { - parameterList.add(typeAsString); - } - - appendLineWithCharsetAndQPDetection(VCardConstants.PROPERTY_EMAIL, parameterList, - rawValue); - } - - public void appendTelLine(final Integer typeAsInteger, final String label, - final String encodedValue, boolean isPrimary) { - mBuilder.append(VCardConstants.PROPERTY_TEL); - mBuilder.append(VCARD_PARAM_SEPARATOR); - - final int type; - if (typeAsInteger == null) { - type = Phone.TYPE_OTHER; - } else { - type = typeAsInteger; - } - - ArrayList parameterList = new ArrayList(); - switch (type) { - case Phone.TYPE_HOME: { - parameterList.addAll( - Arrays.asList(VCardConstants.PARAM_TYPE_HOME)); - break; - } - case Phone.TYPE_WORK: { - parameterList.addAll( - Arrays.asList(VCardConstants.PARAM_TYPE_WORK)); - break; - } - case Phone.TYPE_FAX_HOME: { - parameterList.addAll( - Arrays.asList(VCardConstants.PARAM_TYPE_HOME, VCardConstants.PARAM_TYPE_FAX)); - break; - } - case Phone.TYPE_FAX_WORK: { - parameterList.addAll( - Arrays.asList(VCardConstants.PARAM_TYPE_WORK, VCardConstants.PARAM_TYPE_FAX)); - break; - } - case Phone.TYPE_MOBILE: { - parameterList.add(VCardConstants.PARAM_TYPE_CELL); - break; - } - case Phone.TYPE_PAGER: { - if (mIsDoCoMo) { - // Not sure about the reason, but previous implementation had - // used "VOICE" instead of "PAGER" - parameterList.add(VCardConstants.PARAM_TYPE_VOICE); - } else { - parameterList.add(VCardConstants.PARAM_TYPE_PAGER); - } - break; - } - case Phone.TYPE_OTHER: { - parameterList.add(VCardConstants.PARAM_TYPE_VOICE); - break; - } - case Phone.TYPE_CAR: { - parameterList.add(VCardConstants.PARAM_TYPE_CAR); - break; - } - case Phone.TYPE_COMPANY_MAIN: { - // There's no relevant field in vCard (at least 2.1). - parameterList.add(VCardConstants.PARAM_TYPE_WORK); - isPrimary = true; - break; - } - case Phone.TYPE_ISDN: { - parameterList.add(VCardConstants.PARAM_TYPE_ISDN); - break; - } - case Phone.TYPE_MAIN: { - isPrimary = true; - break; - } - case Phone.TYPE_OTHER_FAX: { - parameterList.add(VCardConstants.PARAM_TYPE_FAX); - break; - } - case Phone.TYPE_TELEX: { - parameterList.add(VCardConstants.PARAM_TYPE_TLX); - break; - } - case Phone.TYPE_WORK_MOBILE: { - parameterList.addAll( - Arrays.asList(VCardConstants.PARAM_TYPE_WORK, VCardConstants.PARAM_TYPE_CELL)); - break; - } - case Phone.TYPE_WORK_PAGER: { - parameterList.add(VCardConstants.PARAM_TYPE_WORK); - // See above. - if (mIsDoCoMo) { - parameterList.add(VCardConstants.PARAM_TYPE_VOICE); - } else { - parameterList.add(VCardConstants.PARAM_TYPE_PAGER); - } - break; - } - case Phone.TYPE_MMS: { - parameterList.add(VCardConstants.PARAM_TYPE_MSG); - break; - } - case Phone.TYPE_CUSTOM: { - if (TextUtils.isEmpty(label)) { - // Just ignore the custom type. - parameterList.add(VCardConstants.PARAM_TYPE_VOICE); - } else if (VCardUtils.isMobilePhoneLabel(label)) { - parameterList.add(VCardConstants.PARAM_TYPE_CELL); - } else { - final String upperLabel = label.toUpperCase(); - if (VCardUtils.isValidInV21ButUnknownToContactsPhoteType(upperLabel)) { - parameterList.add(upperLabel); - } else if (VCardUtils.containsOnlyAlphaDigitHyphen(label)) { - // Note: Strictly, vCard 2.1 does not allow "X-" parameter without - // "TYPE=" string. - parameterList.add("X-" + label); - } - } - break; - } - case Phone.TYPE_RADIO: - case Phone.TYPE_TTY_TDD: - default: { - break; - } - } - - if (isPrimary) { - parameterList.add(VCardConstants.PARAM_TYPE_PREF); - } - - if (parameterList.isEmpty()) { - appendUncommonPhoneType(mBuilder, type); - } else { - appendTypeParameters(parameterList); - } - - mBuilder.append(VCARD_DATA_SEPARATOR); - mBuilder.append(encodedValue); - mBuilder.append(VCARD_END_OF_LINE); - } - - /** - * Appends phone type string which may not be available in some devices. - */ - private void appendUncommonPhoneType(final StringBuilder builder, final Integer type) { - if (mIsDoCoMo) { - // The previous implementation for DoCoMo had been conservative - // about miscellaneous types. - builder.append(VCardConstants.PARAM_TYPE_VOICE); - } else { - String phoneType = VCardUtils.getPhoneTypeString(type); - if (phoneType != null) { - appendTypeParameter(phoneType); - } else { - Log.e(LOG_TAG, "Unknown or unsupported (by vCard) Phone type: " + type); - } - } - } - - /** - * @param encodedValue Must be encoded by BASE64 - * @param photoType - */ - public void appendPhotoLine(final String encodedValue, final String photoType) { - StringBuilder tmpBuilder = new StringBuilder(); - tmpBuilder.append(VCardConstants.PROPERTY_PHOTO); - tmpBuilder.append(VCARD_PARAM_SEPARATOR); - if (mIsV30) { - tmpBuilder.append(VCARD_PARAM_ENCODING_BASE64_V30); - } else { - tmpBuilder.append(VCARD_PARAM_ENCODING_BASE64_V21); - } - tmpBuilder.append(VCARD_PARAM_SEPARATOR); - appendTypeParameter(tmpBuilder, photoType); - tmpBuilder.append(VCARD_DATA_SEPARATOR); - tmpBuilder.append(encodedValue); - - final String tmpStr = tmpBuilder.toString(); - tmpBuilder = new StringBuilder(); - int lineCount = 0; - final int length = tmpStr.length(); - final int maxNumForFirstLine = VCardConstants.MAX_CHARACTER_NUMS_BASE64_V30 - - VCARD_END_OF_LINE.length(); - final int maxNumInGeneral = maxNumForFirstLine - VCARD_WS.length(); - int maxNum = maxNumForFirstLine; - for (int i = 0; i < length; i++) { - tmpBuilder.append(tmpStr.charAt(i)); - lineCount++; - if (lineCount > maxNum) { - tmpBuilder.append(VCARD_END_OF_LINE); - tmpBuilder.append(VCARD_WS); - maxNum = maxNumInGeneral; - lineCount = 0; - } - } - mBuilder.append(tmpBuilder.toString()); - mBuilder.append(VCARD_END_OF_LINE); - mBuilder.append(VCARD_END_OF_LINE); - } - - public void appendAndroidSpecificProperty( - final String mimeType, ContentValues contentValues) { - if (!sAllowedAndroidPropertySet.contains(mimeType)) { - return; - } - final List rawValueList = new ArrayList(); - for (int i = 1; i <= VCardConstants.MAX_DATA_COLUMN; i++) { - String value = contentValues.getAsString("data" + i); - if (value == null) { - value = ""; - } - rawValueList.add(value); - } - - boolean needCharset = - (mShouldAppendCharsetParam && - !VCardUtils.containsOnlyNonCrLfPrintableAscii(rawValueList)); - boolean reallyUseQuotedPrintable = - (mShouldUseQuotedPrintable && - !VCardUtils.containsOnlyNonCrLfPrintableAscii(rawValueList)); - mBuilder.append(VCardConstants.PROPERTY_X_ANDROID_CUSTOM); - if (needCharset) { - mBuilder.append(VCARD_PARAM_SEPARATOR); - mBuilder.append(mVCardCharsetParameter); - } - if (reallyUseQuotedPrintable) { - mBuilder.append(VCARD_PARAM_SEPARATOR); - mBuilder.append(VCARD_PARAM_ENCODING_QP); - } - mBuilder.append(VCARD_DATA_SEPARATOR); - mBuilder.append(mimeType); // Should not be encoded. - for (String rawValue : rawValueList) { - final String encodedValue; - if (reallyUseQuotedPrintable) { - encodedValue = encodeQuotedPrintable(rawValue); - } else { - // TODO: one line may be too huge, which may be invalid in vCard 3.0 - // (which says "When generating a content line, lines longer than - // 75 characters SHOULD be folded"), though several - // (even well-known) applications do not care this. - encodedValue = escapeCharacters(rawValue); - } - mBuilder.append(VCARD_ITEM_SEPARATOR); - mBuilder.append(encodedValue); - } - mBuilder.append(VCARD_END_OF_LINE); - } - - public void appendLineWithCharsetAndQPDetection(final String propertyName, - final String rawValue) { - appendLineWithCharsetAndQPDetection(propertyName, null, rawValue); - } - - public void appendLineWithCharsetAndQPDetection( - final String propertyName, final List rawValueList) { - appendLineWithCharsetAndQPDetection(propertyName, null, rawValueList); - } - - public void appendLineWithCharsetAndQPDetection(final String propertyName, - final List parameterList, final String rawValue) { - final boolean needCharset = - !VCardUtils.containsOnlyPrintableAscii(rawValue); - final boolean reallyUseQuotedPrintable = - (mShouldUseQuotedPrintable && - !VCardUtils.containsOnlyNonCrLfPrintableAscii(rawValue)); - appendLine(propertyName, parameterList, - rawValue, needCharset, reallyUseQuotedPrintable); - } - - public void appendLineWithCharsetAndQPDetection(final String propertyName, - final List parameterList, final List rawValueList) { - boolean needCharset = - (mShouldAppendCharsetParam && - !VCardUtils.containsOnlyNonCrLfPrintableAscii(rawValueList)); - boolean reallyUseQuotedPrintable = - (mShouldUseQuotedPrintable && - !VCardUtils.containsOnlyNonCrLfPrintableAscii(rawValueList)); - appendLine(propertyName, parameterList, rawValueList, - needCharset, reallyUseQuotedPrintable); - } - - /** - * Appends one line with a given property name and value. - */ - public void appendLine(final String propertyName, final String rawValue) { - appendLine(propertyName, rawValue, false, false); - } - - public void appendLine(final String propertyName, final List rawValueList) { - appendLine(propertyName, rawValueList, false, false); - } - - public void appendLine(final String propertyName, - final String rawValue, final boolean needCharset, - boolean reallyUseQuotedPrintable) { - appendLine(propertyName, null, rawValue, needCharset, reallyUseQuotedPrintable); - } - - public void appendLine(final String propertyName, final List parameterList, - final String rawValue) { - appendLine(propertyName, parameterList, rawValue, false, false); - } - - public void appendLine(final String propertyName, final List parameterList, - final String rawValue, final boolean needCharset, - boolean reallyUseQuotedPrintable) { - mBuilder.append(propertyName); - if (parameterList != null && parameterList.size() > 0) { - mBuilder.append(VCARD_PARAM_SEPARATOR); - appendTypeParameters(parameterList); - } - if (needCharset) { - mBuilder.append(VCARD_PARAM_SEPARATOR); - mBuilder.append(mVCardCharsetParameter); - } - - final String encodedValue; - if (reallyUseQuotedPrintable) { - mBuilder.append(VCARD_PARAM_SEPARATOR); - mBuilder.append(VCARD_PARAM_ENCODING_QP); - encodedValue = encodeQuotedPrintable(rawValue); - } else { - // TODO: one line may be too huge, which may be invalid in vCard spec, though - // several (even well-known) applications do not care that violation. - encodedValue = escapeCharacters(rawValue); - } - - mBuilder.append(VCARD_DATA_SEPARATOR); - mBuilder.append(encodedValue); - mBuilder.append(VCARD_END_OF_LINE); - } - - public void appendLine(final String propertyName, final List rawValueList, - final boolean needCharset, boolean needQuotedPrintable) { - appendLine(propertyName, null, rawValueList, needCharset, needQuotedPrintable); - } - - public void appendLine(final String propertyName, final List parameterList, - final List rawValueList, final boolean needCharset, - final boolean needQuotedPrintable) { - mBuilder.append(propertyName); - if (parameterList != null && parameterList.size() > 0) { - mBuilder.append(VCARD_PARAM_SEPARATOR); - appendTypeParameters(parameterList); - } - if (needCharset) { - mBuilder.append(VCARD_PARAM_SEPARATOR); - mBuilder.append(mVCardCharsetParameter); - } - if (needQuotedPrintable) { - mBuilder.append(VCARD_PARAM_SEPARATOR); - mBuilder.append(VCARD_PARAM_ENCODING_QP); - } - - mBuilder.append(VCARD_DATA_SEPARATOR); - boolean first = true; - for (String rawValue : rawValueList) { - final String encodedValue; - if (needQuotedPrintable) { - encodedValue = encodeQuotedPrintable(rawValue); - } else { - // TODO: one line may be too huge, which may be invalid in vCard 3.0 - // (which says "When generating a content line, lines longer than - // 75 characters SHOULD be folded"), though several - // (even well-known) applications do not care this. - encodedValue = escapeCharacters(rawValue); - } - - if (first) { - first = false; - } else { - mBuilder.append(VCARD_ITEM_SEPARATOR); - } - mBuilder.append(encodedValue); - } - mBuilder.append(VCARD_END_OF_LINE); - } - - /** - * VCARD_PARAM_SEPARATOR must be appended before this method being called. - */ - private void appendTypeParameters(final List types) { - // We may have to make this comma separated form like "TYPE=DOM,WORK" in the future, - // which would be recommended way in vcard 3.0 though not valid in vCard 2.1. - boolean first = true; - for (final String typeValue : types) { - // Note: vCard 3.0 specifies the different type of acceptable type Strings, but - // we don't emit that kind of vCard 3.0 specific type since there should be - // high probabilyty in which external importers cannot understand them. - // - // e.g. TYPE="\u578B\u306B\u3087" (vCard 3.0 allows non-Ascii characters if they - // are quoted.) - if (!VCardUtils.isV21Word(typeValue)) { - continue; - } - if (first) { - first = false; - } else { - mBuilder.append(VCARD_PARAM_SEPARATOR); - } - appendTypeParameter(typeValue); - } - } - - /** - * VCARD_PARAM_SEPARATOR must be appended before this method being called. - */ - private void appendTypeParameter(final String type) { - appendTypeParameter(mBuilder, type); - } - - private void appendTypeParameter(final StringBuilder builder, final String type) { - // Refrain from using appendType() so that "TYPE=" is not be appended when the - // device is DoCoMo's (just for safety). - // - // Note: In vCard 3.0, Type strings also can be like this: "TYPE=HOME,PREF" - if ((mIsV30 || mAppendTypeParamName) && !mIsDoCoMo) { - builder.append(VCardConstants.PARAM_TYPE).append(VCARD_PARAM_EQUAL); - } - builder.append(type); - } - - /** - * Returns true when the property line should contain charset parameter - * information. This method may return true even when vCard version is 3.0. - * - * Strictly, adding charset information is invalid in VCard 3.0. - * However we'll add the info only when charset we use is not UTF-8 - * in vCard 3.0 format, since parser side may be able to use the charset - * via this field, though we may encounter another problem by adding it. - * - * e.g. Japanese mobile phones use Shift_Jis while RFC 2426 - * recommends UTF-8. By adding this field, parsers may be able - * to know this text is NOT UTF-8 but Shift_Jis. - */ - private boolean shouldAppendCharsetParam(String...propertyValueList) { - if (!mShouldAppendCharsetParam) { - return false; - } - for (String propertyValue : propertyValueList) { - if (!VCardUtils.containsOnlyPrintableAscii(propertyValue)) { - return true; - } - } - return false; - } - - private String encodeQuotedPrintable(final String str) { - if (TextUtils.isEmpty(str)) { - return ""; - } - - final StringBuilder builder = new StringBuilder(); - int index = 0; - int lineCount = 0; - byte[] strArray = null; - - try { - strArray = str.getBytes(mCharset); - } catch (UnsupportedEncodingException e) { - Log.e(LOG_TAG, "Charset " + mCharset + " cannot be used. " - + "Try default charset"); - strArray = str.getBytes(); - } - while (index < strArray.length) { - builder.append(String.format("=%02X", strArray[index])); - index += 1; - lineCount += 3; - - if (lineCount >= 67) { - // Specification requires CRLF must be inserted before the - // length of the line - // becomes more than 76. - // Assuming that the next character is a multi-byte character, - // it will become - // 6 bytes. - // 76 - 6 - 3 = 67 - builder.append("=\r\n"); - lineCount = 0; - } - } - - return builder.toString(); - } - - /** - * Append '\' to the characters which should be escaped. The character set is different - * not only between vCard 2.1 and vCard 3.0 but also among each device. - * - * Note that Quoted-Printable string must not be input here. - */ - @SuppressWarnings("fallthrough") - private String escapeCharacters(final String unescaped) { - if (TextUtils.isEmpty(unescaped)) { - return ""; - } - - final StringBuilder tmpBuilder = new StringBuilder(); - final int length = unescaped.length(); - for (int i = 0; i < length; i++) { - final char ch = unescaped.charAt(i); - switch (ch) { - case ';': { - tmpBuilder.append('\\'); - tmpBuilder.append(';'); - break; - } - case '\r': { - if (i + 1 < length) { - char nextChar = unescaped.charAt(i); - if (nextChar == '\n') { - break; - } else { - // fall through - } - } else { - // fall through - } - } - case '\n': { - // In vCard 2.1, there's no specification about this, while - // vCard 3.0 explicitly requires this should be encoded to "\n". - tmpBuilder.append("\\n"); - break; - } - case '\\': { - if (mIsV30) { - tmpBuilder.append("\\\\"); - break; - } else { - // fall through - } - } - case '<': - case '>': { - if (mIsDoCoMo) { - tmpBuilder.append('\\'); - tmpBuilder.append(ch); - } else { - tmpBuilder.append(ch); - } - break; - } - case ',': { - if (mIsV30) { - tmpBuilder.append("\\,"); - } else { - tmpBuilder.append(ch); - } - break; - } - default: { - tmpBuilder.append(ch); - break; - } - } - } - return tmpBuilder.toString(); - } - - @Override - public String toString() { - if (!mEndAppended) { - if (mIsDoCoMo) { - appendLine(VCardConstants.PROPERTY_X_CLASS, VCARD_DATA_PUBLIC); - appendLine(VCardConstants.PROPERTY_X_REDUCTION, ""); - appendLine(VCardConstants.PROPERTY_X_NO, ""); - appendLine(VCardConstants.PROPERTY_X_DCM_HMN_MODE, ""); - } - appendLine(VCardConstants.PROPERTY_END, VCARD_DATA_VCARD); - mEndAppended = true; - } - return mBuilder.toString(); - } -} diff --git a/vcard/java/com/android/vcard/VCardComposer.java b/vcard/java/com/android/vcard/VCardComposer.java deleted file mode 100644 index 703895575..000000000 --- a/vcard/java/com/android/vcard/VCardComposer.java +++ /dev/null @@ -1,677 +0,0 @@ -/* - * 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.vcard; - -import android.content.ContentResolver; -import android.content.ContentValues; -import android.content.Context; -import android.content.Entity; -import android.content.EntityIterator; -import android.content.Entity.NamedContentValues; -import android.database.Cursor; -import android.database.sqlite.SQLiteException; -import android.net.Uri; -import android.provider.ContactsContract.Contacts; -import android.provider.ContactsContract.Data; -import android.provider.ContactsContract.RawContacts; -import android.provider.ContactsContract.RawContactsEntity; -import android.provider.ContactsContract.CommonDataKinds.Email; -import android.provider.ContactsContract.CommonDataKinds.Event; -import android.provider.ContactsContract.CommonDataKinds.Im; -import android.provider.ContactsContract.CommonDataKinds.Nickname; -import android.provider.ContactsContract.CommonDataKinds.Note; -import android.provider.ContactsContract.CommonDataKinds.Organization; -import android.provider.ContactsContract.CommonDataKinds.Phone; -import android.provider.ContactsContract.CommonDataKinds.Photo; -import android.provider.ContactsContract.CommonDataKinds.Relation; -import android.provider.ContactsContract.CommonDataKinds.StructuredName; -import android.provider.ContactsContract.CommonDataKinds.StructuredPostal; -import android.provider.ContactsContract.CommonDataKinds.Website; -import android.text.TextUtils; -import android.util.CharsetUtils; -import android.util.Log; - -import com.android.vcard.exception.VCardException; - -import java.io.BufferedWriter; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.io.UnsupportedEncodingException; -import java.io.Writer; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.nio.charset.UnsupportedCharsetException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - *

- * The class for composing vCard from Contacts information. - *

- *

- * Usually, this class should be used like this. - *

- *
VCardComposer composer = null;
- * try {
- *     composer = new VCardComposer(context);
- *     composer.addHandler(
- *             composer.new HandlerForOutputStream(outputStream));
- *     if (!composer.init()) {
- *         // Do something handling the situation.
- *         return;
- *     }
- *     while (!composer.isAfterLast()) {
- *         if (mCanceled) {
- *             // Assume a user may cancel this operation during the export.
- *             return;
- *         }
- *         if (!composer.createOneEntry()) {
- *             // Do something handling the error situation.
- *             return;
- *         }
- *     }
- * } finally {
- *     if (composer != null) {
- *         composer.terminate();
- *     }
- * }
- *

- * Users have to manually take care of memory efficiency. Even one vCard may contain - * image of non-trivial size for mobile devices. - *

- *

- * {@link VCardBuilder} is used to build each vCard. - *

- */ -public class VCardComposer { - private static final String LOG_TAG = "VCardComposer"; - - public static final String FAILURE_REASON_FAILED_TO_GET_DATABASE_INFO = - "Failed to get database information"; - - public static final String FAILURE_REASON_NO_ENTRY = - "There's no exportable in the database"; - - public static final String FAILURE_REASON_NOT_INITIALIZED = - "The vCard composer object is not correctly initialized"; - - /** Should be visible only from developers... (no need to translate, hopefully) */ - public static final String FAILURE_REASON_UNSUPPORTED_URI = - "The Uri vCard composer received is not supported by the composer."; - - public static final String NO_ERROR = "No error"; - - public static final String VCARD_TYPE_STRING_DOCOMO = "docomo"; - - // Strictly speaking, "Shift_JIS" is the most appropriate, but we use upper version here, - // since usual vCard devices for Japanese devices already use it. - private static final String SHIFT_JIS = "SHIFT_JIS"; - private static final String UTF_8 = "UTF-8"; - - /** - * Special URI for testing. - */ - public static final String VCARD_TEST_AUTHORITY = "com.android.unit_tests.vcard"; - public static final Uri VCARD_TEST_AUTHORITY_URI = - Uri.parse("content://" + VCARD_TEST_AUTHORITY); - public static final Uri CONTACTS_TEST_CONTENT_URI = - Uri.withAppendedPath(VCARD_TEST_AUTHORITY_URI, "contacts"); - - private static final Map sImMap; - - static { - sImMap = new HashMap(); - sImMap.put(Im.PROTOCOL_AIM, VCardConstants.PROPERTY_X_AIM); - sImMap.put(Im.PROTOCOL_MSN, VCardConstants.PROPERTY_X_MSN); - sImMap.put(Im.PROTOCOL_YAHOO, VCardConstants.PROPERTY_X_YAHOO); - sImMap.put(Im.PROTOCOL_ICQ, VCardConstants.PROPERTY_X_ICQ); - sImMap.put(Im.PROTOCOL_JABBER, VCardConstants.PROPERTY_X_JABBER); - sImMap.put(Im.PROTOCOL_SKYPE, VCardConstants.PROPERTY_X_SKYPE_USERNAME); - // We don't add Google talk here since it has to be handled separately. - } - - public static interface OneEntryHandler { - public boolean onInit(Context context); - public boolean onEntryCreated(String vcard); - public void onTerminate(); - } - - /** - *

- * An useful handler for emitting vCard String to an OutputStream object one by one. - *

- *

- * The input OutputStream object is closed() on {@link #onTerminate()}. - * Must not close the stream outside this class. - *

- */ - public final class HandlerForOutputStream implements OneEntryHandler { - @SuppressWarnings("hiding") - private static final String LOG_TAG = "VCardComposer.HandlerForOutputStream"; - - private boolean mOnTerminateIsCalled = false; - - private final OutputStream mOutputStream; // mWriter will close this. - private Writer mWriter; - - /** - * Input stream will be closed on the detruction of this object. - */ - public HandlerForOutputStream(final OutputStream outputStream) { - mOutputStream = outputStream; - } - - public boolean onInit(final Context context) { - try { - mWriter = new BufferedWriter(new OutputStreamWriter( - mOutputStream, mCharset)); - } catch (UnsupportedEncodingException e1) { - Log.e(LOG_TAG, "Unsupported charset: " + mCharset); - mErrorReason = "Encoding is not supported (usually this does not happen!): " - + mCharset; - return false; - } - - if (mIsDoCoMo) { - try { - // Create one empty entry. - mWriter.write(createOneEntryInternal("-1", null)); - } catch (VCardException e) { - Log.e(LOG_TAG, "VCardException has been thrown during on Init(): " + - e.getMessage()); - return false; - } catch (IOException e) { - Log.e(LOG_TAG, - "IOException occurred during exportOneContactData: " - + e.getMessage()); - mErrorReason = "IOException occurred: " + e.getMessage(); - return false; - } - } - return true; - } - - public boolean onEntryCreated(String vcard) { - try { - mWriter.write(vcard); - } catch (IOException e) { - Log.e(LOG_TAG, - "IOException occurred during exportOneContactData: " - + e.getMessage()); - mErrorReason = "IOException occurred: " + e.getMessage(); - return false; - } - return true; - } - - public void onTerminate() { - mOnTerminateIsCalled = true; - if (mWriter != null) { - try { - // Flush and sync the data so that a user is able to pull - // the SDCard just after - // the export. - mWriter.flush(); - if (mOutputStream != null - && mOutputStream instanceof FileOutputStream) { - ((FileOutputStream) mOutputStream).getFD().sync(); - } - } catch (IOException e) { - Log.d(LOG_TAG, - "IOException during closing the output stream: " - + e.getMessage()); - } finally { - closeOutputStream(); - } - } - } - - public void closeOutputStream() { - try { - mWriter.close(); - } catch (IOException e) { - Log.w(LOG_TAG, "IOException is thrown during close(). Ignoring."); - } - } - - @Override - public void finalize() { - if (!mOnTerminateIsCalled) { - onTerminate(); - } - } - } - - private final Context mContext; - private final int mVCardType; - private final boolean mCareHandlerErrors; - private final ContentResolver mContentResolver; - - private final boolean mIsDoCoMo; - private Cursor mCursor; - private int mIdColumn; - - private final String mCharset; - private boolean mTerminateIsCalled; - private final List mHandlerList; - - private String mErrorReason = NO_ERROR; - - private static final String[] sContactsProjection = new String[] { - Contacts._ID, - }; - - public VCardComposer(Context context) { - this(context, VCardConfig.VCARD_TYPE_DEFAULT, null, true); - } - - /** - * The variant which sets charset to null and sets careHandlerErrors to true. - */ - public VCardComposer(Context context, int vcardType) { - this(context, vcardType, null, true); - } - - public VCardComposer(Context context, int vcardType, String charset) { - this(context, vcardType, charset, true); - } - - /** - * The variant which sets charset to null. - */ - public VCardComposer(final Context context, final int vcardType, - final boolean careHandlerErrors) { - this(context, vcardType, null, careHandlerErrors); - } - - /** - * Construct for supporting call log entry vCard composing. - * - * @param context Context to be used during the composition. - * @param vcardType The type of vCard, typically available via {@link VCardConfig}. - * @param charset The charset to be used. Use null when you don't need the charset. - * @param careHandlerErrors If true, This object returns false everytime - * a Handler object given via {{@link #addHandler(OneEntryHandler)} returns false. - * If false, this ignores those errors. - */ - public VCardComposer(final Context context, final int vcardType, String charset, - final boolean careHandlerErrors) { - mContext = context; - mVCardType = vcardType; - mCareHandlerErrors = careHandlerErrors; - mContentResolver = context.getContentResolver(); - - mIsDoCoMo = VCardConfig.isDoCoMo(vcardType); - mHandlerList = new ArrayList(); - - charset = (TextUtils.isEmpty(charset) ? VCardConfig.DEFAULT_EXPORT_CHARSET : charset); - final boolean shouldAppendCharsetParam = !( - VCardConfig.isV30(vcardType) && UTF_8.equalsIgnoreCase(charset)); - - if (mIsDoCoMo || shouldAppendCharsetParam) { - if (SHIFT_JIS.equalsIgnoreCase(charset)) { - if (mIsDoCoMo) { - try { - charset = CharsetUtils.charsetForVendor(SHIFT_JIS, "docomo").name(); - } catch (UnsupportedCharsetException e) { - Log.e(LOG_TAG, - "DoCoMo-specific SHIFT_JIS was not found. " - + "Use SHIFT_JIS as is."); - charset = SHIFT_JIS; - } - } else { - try { - charset = CharsetUtils.charsetForVendor(SHIFT_JIS).name(); - } catch (UnsupportedCharsetException e) { - Log.e(LOG_TAG, - "Career-specific SHIFT_JIS was not found. " - + "Use SHIFT_JIS as is."); - charset = SHIFT_JIS; - } - } - mCharset = charset; - } else { - Log.w(LOG_TAG, - "The charset \"" + charset + "\" is used while " - + SHIFT_JIS + " is needed to be used."); - if (TextUtils.isEmpty(charset)) { - mCharset = SHIFT_JIS; - } else { - try { - charset = CharsetUtils.charsetForVendor(charset).name(); - } catch (UnsupportedCharsetException e) { - Log.i(LOG_TAG, - "Career-specific \"" + charset + "\" was not found (as usual). " - + "Use it as is."); - } - mCharset = charset; - } - } - } else { - if (TextUtils.isEmpty(charset)) { - mCharset = UTF_8; - } else { - try { - charset = CharsetUtils.charsetForVendor(charset).name(); - } catch (UnsupportedCharsetException e) { - Log.i(LOG_TAG, - "Career-specific \"" + charset + "\" was not found (as usual). " - + "Use it as is."); - } - mCharset = charset; - } - } - - Log.d(LOG_TAG, "Use the charset \"" + mCharset + "\""); - } - - /** - * Must be called before {@link #init()}. - */ - public void addHandler(OneEntryHandler handler) { - if (handler != null) { - mHandlerList.add(handler); - } - } - - /** - * @return Returns true when initialization is successful and all the other - * methods are available. Returns false otherwise. - */ - public boolean init() { - return init(null, null); - } - - public boolean init(final String selection, final String[] selectionArgs) { - return init(Contacts.CONTENT_URI, selection, selectionArgs, null); - } - - /** - * Note that this is unstable interface, may be deleted in the future. - */ - public boolean init(final Uri contentUri, final String selection, - final String[] selectionArgs, final String sortOrder) { - if (contentUri == null) { - return false; - } - - if (mCareHandlerErrors) { - final List finishedList = new ArrayList( - mHandlerList.size()); - for (OneEntryHandler handler : mHandlerList) { - if (!handler.onInit(mContext)) { - for (OneEntryHandler finished : finishedList) { - finished.onTerminate(); - } - return false; - } - } - } else { - // Just ignore the false returned from onInit(). - for (OneEntryHandler handler : mHandlerList) { - handler.onInit(mContext); - } - } - - final String[] projection; - if (Contacts.CONTENT_URI.equals(contentUri) || - CONTACTS_TEST_CONTENT_URI.equals(contentUri)) { - projection = sContactsProjection; - } else { - mErrorReason = FAILURE_REASON_UNSUPPORTED_URI; - return false; - } - mCursor = mContentResolver.query( - contentUri, projection, selection, selectionArgs, sortOrder); - - if (mCursor == null) { - mErrorReason = FAILURE_REASON_FAILED_TO_GET_DATABASE_INFO; - return false; - } - - if (getCount() == 0 || !mCursor.moveToFirst()) { - try { - mCursor.close(); - } catch (SQLiteException e) { - Log.e(LOG_TAG, "SQLiteException on Cursor#close(): " + e.getMessage()); - } finally { - mCursor = null; - mErrorReason = FAILURE_REASON_NO_ENTRY; - } - return false; - } - - mIdColumn = mCursor.getColumnIndex(Contacts._ID); - - return true; - } - - public boolean createOneEntry() { - return createOneEntry(null); - } - - /** - * @param getEntityIteratorMethod For Dependency Injection. - * @hide just for testing. - */ - public boolean createOneEntry(Method getEntityIteratorMethod) { - if (mCursor == null || mCursor.isAfterLast()) { - mErrorReason = FAILURE_REASON_NOT_INITIALIZED; - return false; - } - final String vcard; - try { - if (mIdColumn >= 0) { - vcard = createOneEntryInternal(mCursor.getString(mIdColumn), - getEntityIteratorMethod); - } else { - Log.e(LOG_TAG, "Incorrect mIdColumn: " + mIdColumn); - return true; - } - } catch (VCardException e) { - Log.e(LOG_TAG, "VCardException has been thrown: " + e.getMessage()); - return false; - } catch (OutOfMemoryError error) { - // Maybe some data (e.g. photo) is too big to have in memory. But it - // should be rare. - Log.e(LOG_TAG, "OutOfMemoryError occured. Ignore the entry."); - System.gc(); - // TODO: should tell users what happened? - return true; - } finally { - mCursor.moveToNext(); - } - - // This function does not care the OutOfMemoryError on the handler side :-P - if (mCareHandlerErrors) { - List finishedList = new ArrayList( - mHandlerList.size()); - for (OneEntryHandler handler : mHandlerList) { - if (!handler.onEntryCreated(vcard)) { - return false; - } - } - } else { - for (OneEntryHandler handler : mHandlerList) { - handler.onEntryCreated(vcard); - } - } - - return true; - } - - private String createOneEntryInternal(final String contactId, - final Method getEntityIteratorMethod) throws VCardException { - final Map> contentValuesListMap = - new HashMap>(); - // The resolver may return the entity iterator with no data. It is possible. - // e.g. If all the data in the contact of the given contact id are not exportable ones, - // they are hidden from the view of this method, though contact id itself exists. - EntityIterator entityIterator = null; - try { - final Uri uri = RawContactsEntity.CONTENT_URI.buildUpon() - // .appendQueryParameter("for_export_only", "1") - .appendQueryParameter(Data.FOR_EXPORT_ONLY, "1") - .build(); - final String selection = Data.CONTACT_ID + "=?"; - final String[] selectionArgs = new String[] {contactId}; - if (getEntityIteratorMethod != null) { - // Please note that this branch is executed by unit tests only - try { - entityIterator = (EntityIterator)getEntityIteratorMethod.invoke(null, - mContentResolver, uri, selection, selectionArgs, null); - } catch (IllegalArgumentException e) { - Log.e(LOG_TAG, "IllegalArgumentException has been thrown: " + - e.getMessage()); - } catch (IllegalAccessException e) { - Log.e(LOG_TAG, "IllegalAccessException has been thrown: " + - e.getMessage()); - } catch (InvocationTargetException e) { - Log.e(LOG_TAG, "InvocationTargetException has been thrown: "); - StackTraceElement[] stackTraceElements = e.getCause().getStackTrace(); - for (StackTraceElement element : stackTraceElements) { - Log.e(LOG_TAG, " at " + element.toString()); - } - throw new VCardException("InvocationTargetException has been thrown: " + - e.getCause().getMessage()); - } - } else { - entityIterator = RawContacts.newEntityIterator(mContentResolver.query( - uri, null, selection, selectionArgs, null)); - } - - if (entityIterator == null) { - Log.e(LOG_TAG, "EntityIterator is null"); - return ""; - } - - if (!entityIterator.hasNext()) { - Log.w(LOG_TAG, "Data does not exist. contactId: " + contactId); - return ""; - } - - while (entityIterator.hasNext()) { - Entity entity = entityIterator.next(); - for (NamedContentValues namedContentValues : entity.getSubValues()) { - ContentValues contentValues = namedContentValues.values; - String key = contentValues.getAsString(Data.MIMETYPE); - if (key != null) { - List contentValuesList = - contentValuesListMap.get(key); - if (contentValuesList == null) { - contentValuesList = new ArrayList(); - contentValuesListMap.put(key, contentValuesList); - } - contentValuesList.add(contentValues); - } - } - } - } finally { - if (entityIterator != null) { - entityIterator.close(); - } - } - - return buildVCard(contentValuesListMap); - } - - /** - * Builds and returns vCard using given map, whose key is CONTENT_ITEM_TYPE defined in - * {ContactsContract}. Developers can override this method to customize the output. - */ - public String buildVCard(final Map> contentValuesListMap) { - if (contentValuesListMap == null) { - Log.e(LOG_TAG, "The given map is null. Ignore and return empty String"); - return ""; - } else { - final VCardBuilder builder = new VCardBuilder(mVCardType, mCharset); - builder.appendNameProperties(contentValuesListMap.get(StructuredName.CONTENT_ITEM_TYPE)) - .appendNickNames(contentValuesListMap.get(Nickname.CONTENT_ITEM_TYPE)) - .appendPhones(contentValuesListMap.get(Phone.CONTENT_ITEM_TYPE)) - .appendEmails(contentValuesListMap.get(Email.CONTENT_ITEM_TYPE)) - .appendPostals(contentValuesListMap.get(StructuredPostal.CONTENT_ITEM_TYPE)) - .appendOrganizations(contentValuesListMap.get(Organization.CONTENT_ITEM_TYPE)) - .appendWebsites(contentValuesListMap.get(Website.CONTENT_ITEM_TYPE)) - .appendPhotos(contentValuesListMap.get(Photo.CONTENT_ITEM_TYPE)) - .appendNotes(contentValuesListMap.get(Note.CONTENT_ITEM_TYPE)) - .appendEvents(contentValuesListMap.get(Event.CONTENT_ITEM_TYPE)) - .appendIms(contentValuesListMap.get(Im.CONTENT_ITEM_TYPE)) - .appendRelation(contentValuesListMap.get(Relation.CONTENT_ITEM_TYPE)); - return builder.toString(); - } - } - - public void terminate() { - for (OneEntryHandler handler : mHandlerList) { - handler.onTerminate(); - } - - if (mCursor != null) { - try { - mCursor.close(); - } catch (SQLiteException e) { - Log.e(LOG_TAG, "SQLiteException on Cursor#close(): " + e.getMessage()); - } - mCursor = null; - } - - mTerminateIsCalled = true; - } - - @Override - public void finalize() { - if (!mTerminateIsCalled) { - Log.w(LOG_TAG, "terminate() is not called yet. We call it in finalize() step."); - terminate(); - } - } - - /** - * @return returns the number of available entities. The return value is undefined - * when this object is not ready yet (typically when {{@link #init()} is not called - * or when {@link #terminate()} is already called). - */ - public int getCount() { - if (mCursor == null) { - Log.w(LOG_TAG, "This object is not ready yet."); - return 0; - } - return mCursor.getCount(); - } - - /** - * @return true when there's no entity to be built. The return value is undefined - * when this object is not ready yet. - */ - public boolean isAfterLast() { - if (mCursor == null) { - Log.w(LOG_TAG, "This object is not ready yet."); - return false; - } - return mCursor.isAfterLast(); - } - - /** - * @return Returns the error reason. - */ - public String getErrorReason() { - return mErrorReason; - } -} diff --git a/vcard/java/com/android/vcard/VCardConfig.java b/vcard/java/com/android/vcard/VCardConfig.java deleted file mode 100644 index a011d8e27..000000000 --- a/vcard/java/com/android/vcard/VCardConfig.java +++ /dev/null @@ -1,483 +0,0 @@ -/* - * 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.vcard; - -import android.telephony.PhoneNumberUtils; -import android.util.Log; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -/** - * The class representing VCard related configurations. Useful static methods are not in this class - * but in VCardUtils. - */ -public class VCardConfig { - private static final String LOG_TAG = "VCardConfig"; - - /* package */ static final int LOG_LEVEL_NONE = 0; - /* package */ static final int LOG_LEVEL_PERFORMANCE_MEASUREMENT = 0x1; - /* package */ static final int LOG_LEVEL_SHOW_WARNING = 0x2; - /* package */ static final int LOG_LEVEL_VERBOSE = - LOG_LEVEL_PERFORMANCE_MEASUREMENT | LOG_LEVEL_SHOW_WARNING; - - /* package */ static final int LOG_LEVEL = LOG_LEVEL_NONE; - - /** - *

- * The charset used during import. - *

- *

- * We cannot determine which charset should be used to interpret lines in vCard, - * while Java requires us to specify it when InputStream is used. - * We need to rely on the mechanism due to some performance reason. - *

- *

- * In order to avoid "misinterpretation" of charset and lose any data in vCard, - * "ISO-8859-1" is first used for reading the stream. - * When a charset is specified in a property (with "CHARSET=..." parameter), - * the string is decoded to raw bytes and encoded into the specific charset, - *

- *

- * Unicode specification there's a one to one mapping between each byte in ISO-8859-1 - * and a codepoint, and Java specification requires runtime must have the charset. - * Thus, ISO-8859-1 is one effective mapping for intermediate mapping. - *

- */ - public static final String DEFAULT_INTERMEDIATE_CHARSET = "ISO-8859-1"; - - /** - * The charset used when there's no information affbout what charset should be used to - * encode the binary given from vCard. - */ - public static final String DEFAULT_IMPORT_CHARSET = "UTF-8"; - public static final String DEFAULT_EXPORT_CHARSET = "UTF-8"; - - public static final int FLAG_V21 = 0; - public static final int FLAG_V30 = 1; - - // 0x2 is reserved for the future use ... - - public static final int NAME_ORDER_DEFAULT = 0; - public static final int NAME_ORDER_EUROPE = 0x4; - public static final int NAME_ORDER_JAPANESE = 0x8; - private static final int NAME_ORDER_MASK = 0xC; - - // 0x10 is reserved for safety - - /** - *

- * The flag indicating the vCard composer will add some "X-" properties used only in Android - * when the formal vCard specification does not have appropriate fields for that data. - *

- *

- * For example, Android accepts nickname information while vCard 2.1 does not. - * When this flag is on, vCard composer emits alternative "X-" property (like "X-NICKNAME") - * instead of just dropping it. - *

- *

- * vCard parser code automatically parses the field emitted even when this flag is off. - *

- */ - private static final int FLAG_USE_ANDROID_PROPERTY = 0x80000000; - - /** - *

- * The flag indicating the vCard composer will add some "X-" properties seen in the - * vCard data emitted by the other softwares/devices when the formal vCard specification - * does not have appropriate field(s) for that data. - *

- *

- * One example is X-PHONETIC-FIRST-NAME/X-PHONETIC-MIDDLE-NAME/X-PHONETIC-LAST-NAME, which are - * for phonetic name (how the name is pronounced), seen in the vCard emitted by some other - * non-Android devices/softwares. We chose to enable the vCard composer to use those - * defact properties since they are also useful for Android devices. - *

- *

- * Note for developers: only "X-" properties should be added with this flag. vCard 2.1/3.0 - * allows any kind of "X-" properties but does not allow non-"X-" properties (except IANA tokens - * in vCard 3.0). Some external parsers may get confused with non-valid, non-"X-" properties. - *

- */ - private static final int FLAG_USE_DEFACT_PROPERTY = 0x40000000; - - /** - *

- * The flag indicating some specific dialect seen in vCard of DoCoMo (one of Japanese - * mobile careers) should be used. This flag does not include any other information like - * that "the vCard is for Japanese". So it is "possible" that "the vCard should have DoCoMo's - * dialect but the name order should be European", but it is not recommended. - *

- */ - private static final int FLAG_DOCOMO = 0x20000000; - - /** - *

- * The flag indicating the vCard composer does "NOT" use Quoted-Printable toward "primary" - * properties even though it is required by vCard 2.1 (QP is prohibited in vCard 3.0). - *

- *

- * We actually cannot define what is the "primary" property. Note that this is NOT defined - * in vCard specification either. Also be aware that it is NOT related to "primary" notion - * used in {@link android.provider.ContactsContract}. - * This notion is just for vCard composition in Android. - *

- *

- * We added this Android-specific notion since some (incomplete) vCard exporters for vCard 2.1 - * do NOT use Quoted-Printable encoding toward some properties related names like "N", "FN", etc. - * even when their values contain non-ascii or/and CR/LF, while they use the encoding in the - * other properties like "ADR", "ORG", etc. - *

- * We are afraid of the case where some vCard importer also forget handling QP presuming QP is - * not used in such fields. - *

- *

- * This flag is useful when some target importer you are going to focus on does not accept - * such properties with Quoted-Printable encoding. - *

- *

- * Again, we should not use this flag at all for complying vCard 2.1 spec. - *

- *

- * In vCard 3.0, Quoted-Printable is explicitly "prohibitted", so we don't need to care this - * kind of problem (hopefully). - *

- * @hide - */ - public static final int FLAG_REFRAIN_QP_TO_NAME_PROPERTIES = 0x10000000; - - /** - *

- * The flag indicating that phonetic name related fields must be converted to - * appropriate form. Note that "appropriate" is not defined in any vCard specification. - * This is Android-specific. - *

- *

- * One typical (and currently sole) example where we need this flag is the time when - * we need to emit Japanese phonetic names into vCard entries. The property values - * should be encoded into half-width katakana when the target importer is Japanese mobile - * phones', which are probably not able to parse full-width hiragana/katakana for - * historical reasons, while the vCard importers embedded to softwares for PC should be - * able to parse them as we expect. - *

- */ - public static final int FLAG_CONVERT_PHONETIC_NAME_STRINGS = 0x08000000; - - /** - *

- * The flag indicating the vCard composer "for 2.1" emits "TYPE=" string toward TYPE params - * every time possible. The default behavior does not emit it and is valid in the spec. - * In vCrad 3.0, this flag is unnecessary, since "TYPE=" is MUST in vCard 3.0 specification. - *

- *

- * Detail: - * How more than one TYPE fields are expressed is different between vCard 2.1 and vCard 3.0. - *

- *

- * e.g. - *

- *
    - *
  1. Probably valid in both vCard 2.1 and vCard 3.0: "ADR;TYPE=DOM;TYPE=HOME:..."
  2. - *
  3. Valid in vCard 2.1 but not in vCard 3.0: "ADR;DOM;HOME:..."
  4. - *
  5. Valid in vCard 3.0 but not in vCard 2.1: "ADR;TYPE=DOM,HOME:..."
  6. - *
- *

- * If you are targeting to the importer which cannot accept TYPE params without "TYPE=" - * strings (which should be rare though), please use this flag. - *

- *

- * Example usage: - *

int type = (VCARD_TYPE_V21_GENERIC | FLAG_APPEND_TYPE_PARAM);
- *

- */ - public static final int FLAG_APPEND_TYPE_PARAM = 0x04000000; - - /** - *

- * The flag indicating the vCard composer does touch nothing toward phone number Strings - * but leave it as is. - *

- *

- * The vCard specifications mention nothing toward phone numbers, while some devices - * do (wrongly, but with innevitable reasons). - * For example, there's a possibility Japanese mobile phones are expected to have - * just numbers, hypens, plus, etc. but not usual alphabets, while US mobile phones - * should get such characters. To make exported vCard simple for external parsers, - * we have used {@link PhoneNumberUtils#formatNumber(String)} during export, and - * removed unnecessary characters inside the number (e.g. "111-222-3333 (Miami)" - * becomes "111-222-3333"). - * Unfortunate side effect of that use was some control characters used in the other - * areas may be badly affected by the formatting. - *

- *

- * This flag disables that formatting, affecting both importer and exporter. - * If the user is aware of some side effects due to the implicit formatting, use this flag. - *

- */ - public static final int FLAG_REFRAIN_PHONE_NUMBER_FORMATTING = 0x02000000; - - /** - *

- * For importer only. Ignored in exporter. - *

- *

- * The flag indicating the parser should handle a nested vCard, in which vCard clause starts - * in another vCard clause. Here's a typical example. - *

- *
BEGIN:VCARD
-     * BEGIN:VCARD
-     * VERSION:2.1
-     * ...
-     * END:VCARD
-     * END:VCARD
- *

- * The vCard 2.1 specification allows the nest, but also let parsers ignore nested entries, - * while some mobile devices emit nested ones as primary data to be imported. - *

- *

- * This flag forces a vCard parser to torelate such a nest and understand its content. - *

- */ - public static final int FLAG_TORELATE_NEST = 0x01000000; - - //// The followings are VCard types available from importer/exporter. //// - - /** - *

- * The type indicating nothing. Used by {@link VCardSourceDetector} when it - * was not able to guess the exact vCard type. - *

- */ - public static final int VCARD_TYPE_UNKNOWN = 0; - - /** - *

- * Generic vCard format with the vCard 2.1. When composing a vCard entry, - * the US convension will be used toward formatting some values. - *

- *

- * e.g. The order of the display name would be "Prefix Given Middle Family Suffix", - * while it should be "Prefix Family Middle Given Suffix" in Japan for example. - *

- *

- * Uses UTF-8 for the charset as a charset for exporting. Note that old vCard importer - * outside Android cannot accept it since vCard 2.1 specifically does not allow - * that charset, while we need to use it to support various languages around the world. - *

- *

- * If you want to use alternative charset, you should notify the charset to the other - * compontent to be used. - *

- */ - public static final int VCARD_TYPE_V21_GENERIC = - (FLAG_V21 | NAME_ORDER_DEFAULT | FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY); - - /* package */ static String VCARD_TYPE_V21_GENERIC_STR = "v21_generic"; - - /** - *

- * General vCard format with the version 3.0. Uses UTF-8 for the charset. - *

- *

- * Not fully ready yet. Use with caution when you use this. - *

- */ - public static final int VCARD_TYPE_V30_GENERIC = - (FLAG_V30 | NAME_ORDER_DEFAULT | FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY); - - /* package */ static final String VCARD_TYPE_V30_GENERIC_STR = "v30_generic"; - - /** - *

- * General vCard format for the vCard 2.1 with some Europe convension. Uses Utf-8. - * Currently, only name order is considered ("Prefix Middle Given Family Suffix") - *

- */ - public static final int VCARD_TYPE_V21_EUROPE = - (FLAG_V21 | NAME_ORDER_EUROPE | FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY); - - /* package */ static final String VCARD_TYPE_V21_EUROPE_STR = "v21_europe"; - - /** - *

- * General vCard format with the version 3.0 with some Europe convension. Uses UTF-8. - *

- *

- * Not ready yet. Use with caution when you use this. - *

- */ - public static final int VCARD_TYPE_V30_EUROPE = - (FLAG_V30 | NAME_ORDER_EUROPE | FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY); - - /* package */ static final String VCARD_TYPE_V30_EUROPE_STR = "v30_europe"; - - /** - *

- * The vCard 2.1 format for miscellaneous Japanese devices, using UTF-8 as default charset. - *

- *

- * Not ready yet. Use with caution when you use this. - *

- */ - public static final int VCARD_TYPE_V21_JAPANESE = - (FLAG_V21 | NAME_ORDER_JAPANESE | FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY); - - /* package */ static final String VCARD_TYPE_V21_JAPANESE_STR = "v21_japanese_utf8"; - - /** - *

- * The vCard 3.0 format for miscellaneous Japanese devices, using UTF-8 as default charset. - *

- *

- * Not ready yet. Use with caution when you use this. - *

- */ - public static final int VCARD_TYPE_V30_JAPANESE = - (FLAG_V30 | NAME_ORDER_JAPANESE | FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY); - - /* package */ static final String VCARD_TYPE_V30_JAPANESE_STR = "v30_japanese_utf8"; - - /** - *

- * The vCard 2.1 based format which (partially) considers the convention in Japanese - * mobile phones, where phonetic names are translated to half-width katakana if - * possible, etc. It would be better to use Shift_JIS as a charset for maximum - * compatibility. - *

- * @hide Should not be available world wide. - */ - public static final int VCARD_TYPE_V21_JAPANESE_MOBILE = - (FLAG_V21 | NAME_ORDER_JAPANESE | - FLAG_CONVERT_PHONETIC_NAME_STRINGS | FLAG_REFRAIN_QP_TO_NAME_PROPERTIES); - - /* package */ static final String VCARD_TYPE_V21_JAPANESE_MOBILE_STR = "v21_japanese_mobile"; - - /** - *

- * The vCard format used in DoCoMo, which is one of Japanese mobile phone careers. - *

- *

- * Base version is vCard 2.1, but the data has several DoCoMo-specific convensions. - * No Android-specific property nor defact property is included. The "Primary" properties - * are NOT encoded to Quoted-Printable. - *

- * @hide Should not be available world wide. - */ - public static final int VCARD_TYPE_DOCOMO = - (VCARD_TYPE_V21_JAPANESE_MOBILE | FLAG_DOCOMO); - - /* package */ static final String VCARD_TYPE_DOCOMO_STR = "docomo"; - - public static int VCARD_TYPE_DEFAULT = VCARD_TYPE_V21_GENERIC; - - private static final Map sVCardTypeMap; - private static final Set sJapaneseMobileTypeSet; - - static { - sVCardTypeMap = new HashMap(); - sVCardTypeMap.put(VCARD_TYPE_V21_GENERIC_STR, VCARD_TYPE_V21_GENERIC); - sVCardTypeMap.put(VCARD_TYPE_V30_GENERIC_STR, VCARD_TYPE_V30_GENERIC); - sVCardTypeMap.put(VCARD_TYPE_V21_EUROPE_STR, VCARD_TYPE_V21_EUROPE); - sVCardTypeMap.put(VCARD_TYPE_V30_EUROPE_STR, VCARD_TYPE_V30_EUROPE); - sVCardTypeMap.put(VCARD_TYPE_V21_JAPANESE_STR, VCARD_TYPE_V21_JAPANESE); - sVCardTypeMap.put(VCARD_TYPE_V30_JAPANESE_STR, VCARD_TYPE_V30_JAPANESE); - sVCardTypeMap.put(VCARD_TYPE_V21_JAPANESE_MOBILE_STR, VCARD_TYPE_V21_JAPANESE_MOBILE); - sVCardTypeMap.put(VCARD_TYPE_DOCOMO_STR, VCARD_TYPE_DOCOMO); - - sJapaneseMobileTypeSet = new HashSet(); - sJapaneseMobileTypeSet.add(VCARD_TYPE_V21_JAPANESE); - sJapaneseMobileTypeSet.add(VCARD_TYPE_V30_JAPANESE); - sJapaneseMobileTypeSet.add(VCARD_TYPE_V21_JAPANESE_MOBILE); - sJapaneseMobileTypeSet.add(VCARD_TYPE_DOCOMO); - } - - public static int getVCardTypeFromString(final String vcardTypeString) { - final String loweredKey = vcardTypeString.toLowerCase(); - if (sVCardTypeMap.containsKey(loweredKey)) { - return sVCardTypeMap.get(loweredKey); - } else if ("default".equalsIgnoreCase(vcardTypeString)) { - return VCARD_TYPE_DEFAULT; - } else { - Log.e(LOG_TAG, "Unknown vCard type String: \"" + vcardTypeString + "\""); - return VCARD_TYPE_DEFAULT; - } - } - - public static boolean isV30(final int vcardType) { - return ((vcardType & FLAG_V30) != 0); - } - - public static boolean shouldUseQuotedPrintable(final int vcardType) { - return !isV30(vcardType); - } - - public static int getNameOrderType(final int vcardType) { - return vcardType & NAME_ORDER_MASK; - } - - public static boolean usesAndroidSpecificProperty(final int vcardType) { - return ((vcardType & FLAG_USE_ANDROID_PROPERTY) != 0); - } - - public static boolean usesDefactProperty(final int vcardType) { - return ((vcardType & FLAG_USE_DEFACT_PROPERTY) != 0); - } - - public static boolean showPerformanceLog() { - return (VCardConfig.LOG_LEVEL & VCardConfig.LOG_LEVEL_PERFORMANCE_MEASUREMENT) != 0; - } - - public static boolean shouldRefrainQPToNameProperties(final int vcardType) { - return (!shouldUseQuotedPrintable(vcardType) || - ((vcardType & FLAG_REFRAIN_QP_TO_NAME_PROPERTIES) != 0)); - } - - public static boolean appendTypeParamName(final int vcardType) { - return (isV30(vcardType) || ((vcardType & FLAG_APPEND_TYPE_PARAM) != 0)); - } - - /** - * @return true if the device is Japanese and some Japanese convension is - * applied to creating "formatted" something like FORMATTED_ADDRESS. - */ - public static boolean isJapaneseDevice(final int vcardType) { - // TODO: Some mask will be required so that this method wrongly interpret - // Japanese"-like" vCard type. - // e.g. VCARD_TYPE_V21_JAPANESE_SJIS | FLAG_APPEND_TYPE_PARAMS - return sJapaneseMobileTypeSet.contains(vcardType); - } - - /* package */ static boolean refrainPhoneNumberFormatting(final int vcardType) { - return ((vcardType & FLAG_REFRAIN_PHONE_NUMBER_FORMATTING) != 0); - } - - public static boolean needsToConvertPhoneticString(final int vcardType) { - return ((vcardType & FLAG_CONVERT_PHONETIC_NAME_STRINGS) != 0); - } - - public static boolean onlyOneNoteFieldIsAvailable(final int vcardType) { - return vcardType == VCARD_TYPE_DOCOMO; - } - - public static boolean isDoCoMo(final int vcardType) { - return ((vcardType & FLAG_DOCOMO) != 0); - } - - private VCardConfig() { - } -} \ No newline at end of file diff --git a/vcard/java/com/android/vcard/VCardConstants.java b/vcard/java/com/android/vcard/VCardConstants.java deleted file mode 100644 index 862c9edcb..000000000 --- a/vcard/java/com/android/vcard/VCardConstants.java +++ /dev/null @@ -1,160 +0,0 @@ -/* - * 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.vcard; - -/** - * Constants used in both exporter and importer code. - */ -public class VCardConstants { - public static final String VERSION_V21 = "2.1"; - public static final String VERSION_V30 = "3.0"; - - // The property names valid both in vCard 2.1 and 3.0. - public static final String PROPERTY_BEGIN = "BEGIN"; - public static final String PROPERTY_VERSION = "VERSION"; - public static final String PROPERTY_N = "N"; - public static final String PROPERTY_FN = "FN"; - public static final String PROPERTY_ADR = "ADR"; - public static final String PROPERTY_EMAIL = "EMAIL"; - public static final String PROPERTY_NOTE = "NOTE"; - public static final String PROPERTY_ORG = "ORG"; - public static final String PROPERTY_SOUND = "SOUND"; // Not fully supported. - public static final String PROPERTY_TEL = "TEL"; - public static final String PROPERTY_TITLE = "TITLE"; - public static final String PROPERTY_ROLE = "ROLE"; - public static final String PROPERTY_PHOTO = "PHOTO"; - public static final String PROPERTY_LOGO = "LOGO"; - public static final String PROPERTY_URL = "URL"; - public static final String PROPERTY_BDAY = "BDAY"; // Birthday - public static final String PROPERTY_END = "END"; - - // Valid property names not supported (not appropriately handled) by our vCard importer now. - public static final String PROPERTY_REV = "REV"; - public static final String PROPERTY_AGENT = "AGENT"; - - // Available in vCard 3.0. Shoud not use when composing vCard 2.1 file. - public static final String PROPERTY_NAME = "NAME"; - public static final String PROPERTY_NICKNAME = "NICKNAME"; - public static final String PROPERTY_SORT_STRING = "SORT-STRING"; - - // De-fact property values expressing phonetic names. - public static final String PROPERTY_X_PHONETIC_FIRST_NAME = "X-PHONETIC-FIRST-NAME"; - public static final String PROPERTY_X_PHONETIC_MIDDLE_NAME = "X-PHONETIC-MIDDLE-NAME"; - public static final String PROPERTY_X_PHONETIC_LAST_NAME = "X-PHONETIC-LAST-NAME"; - - // Properties both ContactsStruct in Eclair and de-fact vCard extensions - // shown in http://en.wikipedia.org/wiki/VCard support are defined here. - public static final String PROPERTY_X_AIM = "X-AIM"; - public static final String PROPERTY_X_MSN = "X-MSN"; - public static final String PROPERTY_X_YAHOO = "X-YAHOO"; - public static final String PROPERTY_X_ICQ = "X-ICQ"; - public static final String PROPERTY_X_JABBER = "X-JABBER"; - public static final String PROPERTY_X_GOOGLE_TALK = "X-GOOGLE-TALK"; - public static final String PROPERTY_X_SKYPE_USERNAME = "X-SKYPE-USERNAME"; - // Properties only ContactsStruct has. We alse use this. - public static final String PROPERTY_X_QQ = "X-QQ"; - public static final String PROPERTY_X_NETMEETING = "X-NETMEETING"; - - // Phone number for Skype, available as usual phone. - public static final String PROPERTY_X_SKYPE_PSTNNUMBER = "X-SKYPE-PSTNNUMBER"; - - // Property for Android-specific fields. - public static final String PROPERTY_X_ANDROID_CUSTOM = "X-ANDROID-CUSTOM"; - - // Properties for DoCoMo vCard. - public static final String PROPERTY_X_CLASS = "X-CLASS"; - public static final String PROPERTY_X_REDUCTION = "X-REDUCTION"; - public static final String PROPERTY_X_NO = "X-NO"; - public static final String PROPERTY_X_DCM_HMN_MODE = "X-DCM-HMN-MODE"; - - public static final String PARAM_TYPE = "TYPE"; - - public static final String PARAM_TYPE_HOME = "HOME"; - public static final String PARAM_TYPE_WORK = "WORK"; - public static final String PARAM_TYPE_FAX = "FAX"; - public static final String PARAM_TYPE_CELL = "CELL"; - public static final String PARAM_TYPE_VOICE = "VOICE"; - public static final String PARAM_TYPE_INTERNET = "INTERNET"; - - // Abbreviation of "prefered" according to vCard 2.1 specification. - // We interpret this value as "primary" property during import/export. - // - // Note: Both vCard specs does not mention anything about the requirement for this parameter, - // but there may be some vCard importer which will get confused with more than - // one "PREF"s in one property name, while Android accepts them. - public static final String PARAM_TYPE_PREF = "PREF"; - - // Phone type parameters valid in vCard and known to ContactsContract, but not so common. - public static final String PARAM_TYPE_CAR = "CAR"; - public static final String PARAM_TYPE_ISDN = "ISDN"; - public static final String PARAM_TYPE_PAGER = "PAGER"; - public static final String PARAM_TYPE_TLX = "TLX"; // Telex - - // Phone types existing in vCard 2.1 but not known to ContactsContract. - public static final String PARAM_TYPE_MODEM = "MODEM"; - public static final String PARAM_TYPE_MSG = "MSG"; - public static final String PARAM_TYPE_BBS = "BBS"; - public static final String PARAM_TYPE_VIDEO = "VIDEO"; - - public static final String PARAM_ENCODING_7BIT = "7BIT"; - public static final String PARAM_ENCODING_8BIT = "8BIT"; - public static final String PARAM_ENCODING_QP = "QUOTED-PRINTABLE"; - public static final String PARAM_ENCODING_BASE64 = "BASE64"; // Available in vCard 2.1 - public static final String PARAM_ENCODING_B = "B"; // Available in vCard 3.0 - - // TYPE parameters for Phones, which are not formally valid in vCard (at least 2.1). - // These types are basically encoded to "X-" parameters when composing vCard. - // Parser passes these when "X-" is added to the parameter or not. - public static final String PARAM_PHONE_EXTRA_TYPE_CALLBACK = "CALLBACK"; - public static final String PARAM_PHONE_EXTRA_TYPE_RADIO = "RADIO"; - public static final String PARAM_PHONE_EXTRA_TYPE_TTY_TDD = "TTY-TDD"; - public static final String PARAM_PHONE_EXTRA_TYPE_ASSISTANT = "ASSISTANT"; - // vCard composer translates this type to "WORK" + "PREF". Just for parsing. - public static final String PARAM_PHONE_EXTRA_TYPE_COMPANY_MAIN = "COMPANY-MAIN"; - // vCard composer translates this type to "VOICE" Just for parsing. - public static final String PARAM_PHONE_EXTRA_TYPE_OTHER = "OTHER"; - - // TYPE parameters for postal addresses. - public static final String PARAM_ADR_TYPE_PARCEL = "PARCEL"; - public static final String PARAM_ADR_TYPE_DOM = "DOM"; - public static final String PARAM_ADR_TYPE_INTL = "INTL"; - - // TYPE parameters not officially valid but used in some vCard exporter. - // Do not use in composer side. - public static final String PARAM_EXTRA_TYPE_COMPANY = "COMPANY"; - - public interface ImportOnly { - public static final String PROPERTY_X_NICKNAME = "X-NICKNAME"; - // Some device emits this "X-" parameter for expressing Google Talk, - // which is specifically invalid but should be always properly accepted, and emitted - // in some special case (for that device/application). - public static final String PROPERTY_X_GOOGLE_TALK_WITH_SPACE = "X-GOOGLE TALK"; - } - - //// Mainly for package constants. - - // DoCoMo specific type parameter. Used with "SOUND" property, which is alternate of - // SORT-STRING invCard 3.0. - /* package */ static final String PARAM_TYPE_X_IRMC_N = "X-IRMC-N"; - - /* package */ static final int MAX_DATA_COLUMN = 15; - - /* package */ static final int MAX_CHARACTER_NUMS_QP = 76; - static final int MAX_CHARACTER_NUMS_BASE64_V30 = 75; - - private VCardConstants() { - } -} \ No newline at end of file diff --git a/vcard/java/com/android/vcard/VCardEntry.java b/vcard/java/com/android/vcard/VCardEntry.java deleted file mode 100644 index 624407a35..000000000 --- a/vcard/java/com/android/vcard/VCardEntry.java +++ /dev/null @@ -1,1423 +0,0 @@ -/* - * 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.vcard; - -import android.accounts.Account; -import android.content.ContentProviderOperation; -import android.content.ContentProviderResult; -import android.content.ContentResolver; -import android.content.OperationApplicationException; -import android.net.Uri; -import android.os.RemoteException; -import android.provider.ContactsContract; -import android.provider.ContactsContract.Contacts; -import android.provider.ContactsContract.Data; -import android.provider.ContactsContract.RawContacts; -import android.provider.ContactsContract.CommonDataKinds.Email; -import android.provider.ContactsContract.CommonDataKinds.Event; -import android.provider.ContactsContract.CommonDataKinds.GroupMembership; -import android.provider.ContactsContract.CommonDataKinds.Im; -import android.provider.ContactsContract.CommonDataKinds.Nickname; -import android.provider.ContactsContract.CommonDataKinds.Note; -import android.provider.ContactsContract.CommonDataKinds.Organization; -import android.provider.ContactsContract.CommonDataKinds.Phone; -import android.provider.ContactsContract.CommonDataKinds.Photo; -import android.provider.ContactsContract.CommonDataKinds.StructuredName; -import android.provider.ContactsContract.CommonDataKinds.StructuredPostal; -import android.provider.ContactsContract.CommonDataKinds.Website; -import android.telephony.PhoneNumberUtils; -import android.text.TextUtils; -import android.util.Log; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; - -/** - * This class bridges between data structure of Contact app and VCard data. - */ -public class VCardEntry { - private static final String LOG_TAG = "VCardEntry"; - - private final static int DEFAULT_ORGANIZATION_TYPE = Organization.TYPE_WORK; - - private static final Map sImMap = new HashMap(); - - static { - sImMap.put(VCardConstants.PROPERTY_X_AIM, Im.PROTOCOL_AIM); - sImMap.put(VCardConstants.PROPERTY_X_MSN, Im.PROTOCOL_MSN); - sImMap.put(VCardConstants.PROPERTY_X_YAHOO, Im.PROTOCOL_YAHOO); - sImMap.put(VCardConstants.PROPERTY_X_ICQ, Im.PROTOCOL_ICQ); - sImMap.put(VCardConstants.PROPERTY_X_JABBER, Im.PROTOCOL_JABBER); - sImMap.put(VCardConstants.PROPERTY_X_SKYPE_USERNAME, Im.PROTOCOL_SKYPE); - sImMap.put(VCardConstants.PROPERTY_X_GOOGLE_TALK, Im.PROTOCOL_GOOGLE_TALK); - sImMap.put(VCardConstants.ImportOnly.PROPERTY_X_GOOGLE_TALK_WITH_SPACE, - Im.PROTOCOL_GOOGLE_TALK); - } - - public static class PhoneData { - public final int type; - public final String data; - public final String label; - // isPrimary is (not final but) changable, only when there's no appropriate one existing - // in the original VCard. - public boolean isPrimary; - public PhoneData(int type, String data, String label, boolean isPrimary) { - this.type = type; - this.data = data; - this.label = label; - this.isPrimary = isPrimary; - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof PhoneData)) { - return false; - } - PhoneData phoneData = (PhoneData)obj; - return (type == phoneData.type && data.equals(phoneData.data) && - label.equals(phoneData.label) && isPrimary == phoneData.isPrimary); - } - - @Override - public String toString() { - return String.format("type: %d, data: %s, label: %s, isPrimary: %s", - type, data, label, isPrimary); - } - } - - public static class EmailData { - public final int type; - public final String data; - // Used only when TYPE is TYPE_CUSTOM. - public final String label; - public boolean isPrimary; - public EmailData(int type, String data, String label, boolean isPrimary) { - this.type = type; - this.data = data; - this.label = label; - this.isPrimary = isPrimary; - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof EmailData)) { - return false; - } - EmailData emailData = (EmailData)obj; - return (type == emailData.type && data.equals(emailData.data) && - label.equals(emailData.label) && isPrimary == emailData.isPrimary); - } - - @Override - public String toString() { - return String.format("type: %d, data: %s, label: %s, isPrimary: %s", - type, data, label, isPrimary); - } - } - - public static class PostalData { - // Determined by vCard specification. - // - PO Box, Extended Addr, Street, Locality, Region, Postal Code, Country Name - public static final int ADDR_MAX_DATA_SIZE = 7; - private final String[] dataArray; - public final String pobox; - public final String extendedAddress; - public final String street; - public final String localty; - public final String region; - public final String postalCode; - public final String country; - public final int type; - public final String label; - public boolean isPrimary; - - public PostalData(final int type, final List propValueList, - final String label, boolean isPrimary) { - this.type = type; - dataArray = new String[ADDR_MAX_DATA_SIZE]; - - int size = propValueList.size(); - if (size > ADDR_MAX_DATA_SIZE) { - size = ADDR_MAX_DATA_SIZE; - } - - // adr-value = 0*6(text-value ";") text-value - // ; PO Box, Extended Address, Street, Locality, Region, Postal - // ; Code, Country Name - // - // Use Iterator assuming List may be LinkedList, though actually it is - // always ArrayList in the current implementation. - int i = 0; - for (String addressElement : propValueList) { - dataArray[i] = addressElement; - if (++i >= size) { - break; - } - } - while (i < ADDR_MAX_DATA_SIZE) { - dataArray[i++] = null; - } - - this.pobox = dataArray[0]; - this.extendedAddress = dataArray[1]; - this.street = dataArray[2]; - this.localty = dataArray[3]; - this.region = dataArray[4]; - this.postalCode = dataArray[5]; - this.country = dataArray[6]; - this.label = label; - this.isPrimary = isPrimary; - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof PostalData)) { - return false; - } - final PostalData postalData = (PostalData)obj; - return (Arrays.equals(dataArray, postalData.dataArray) && - (type == postalData.type && - (type == StructuredPostal.TYPE_CUSTOM ? - (label == postalData.label) : true)) && - (isPrimary == postalData.isPrimary)); - } - - public String getFormattedAddress(final int vcardType) { - StringBuilder builder = new StringBuilder(); - boolean empty = true; - if (VCardConfig.isJapaneseDevice(vcardType)) { - // In Japan, the order is reversed. - for (int i = ADDR_MAX_DATA_SIZE - 1; i >= 0; i--) { - String addressPart = dataArray[i]; - if (!TextUtils.isEmpty(addressPart)) { - if (!empty) { - builder.append(' '); - } else { - empty = false; - } - builder.append(addressPart); - } - } - } else { - for (int i = 0; i < ADDR_MAX_DATA_SIZE; i++) { - String addressPart = dataArray[i]; - if (!TextUtils.isEmpty(addressPart)) { - if (!empty) { - builder.append(' '); - } else { - empty = false; - } - builder.append(addressPart); - } - } - } - - return builder.toString().trim(); - } - - @Override - public String toString() { - return String.format("type: %d, label: %s, isPrimary: %s", - type, label, isPrimary); - } - } - - public static class OrganizationData { - public final int type; - // non-final is Intentional: we may change the values since this info is separated into - // two parts in vCard: "ORG" + "TITLE", and we have to cope with each field in - // different timing. - public String companyName; - public String departmentName; - public String titleName; - public boolean isPrimary; - - public OrganizationData(int type, - String companyName, - String departmentName, - String titleName, - boolean isPrimary) { - this.type = type; - this.companyName = companyName; - this.departmentName = departmentName; - this.titleName = titleName; - this.isPrimary = isPrimary; - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof OrganizationData)) { - return false; - } - OrganizationData organization = (OrganizationData)obj; - return (type == organization.type && - TextUtils.equals(companyName, organization.companyName) && - TextUtils.equals(departmentName, organization.departmentName) && - TextUtils.equals(titleName, organization.titleName) && - isPrimary == organization.isPrimary); - } - - public String getFormattedString() { - final StringBuilder builder = new StringBuilder(); - if (!TextUtils.isEmpty(companyName)) { - builder.append(companyName); - } - - if (!TextUtils.isEmpty(departmentName)) { - if (builder.length() > 0) { - builder.append(", "); - } - builder.append(departmentName); - } - - if (!TextUtils.isEmpty(titleName)) { - if (builder.length() > 0) { - builder.append(", "); - } - builder.append(titleName); - } - - return builder.toString(); - } - - @Override - public String toString() { - return String.format( - "type: %d, company: %s, department: %s, title: %s, isPrimary: %s", - type, companyName, departmentName, titleName, isPrimary); - } - } - - public static class ImData { - public final int protocol; - public final String customProtocol; - public final int type; - public final String data; - public final boolean isPrimary; - - public ImData(final int protocol, final String customProtocol, final int type, - final String data, final boolean isPrimary) { - this.protocol = protocol; - this.customProtocol = customProtocol; - this.type = type; - this.data = data; - this.isPrimary = isPrimary; - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof ImData)) { - return false; - } - ImData imData = (ImData)obj; - return (type == imData.type && protocol == imData.protocol - && (customProtocol != null ? customProtocol.equals(imData.customProtocol) : - (imData.customProtocol == null)) - && (data != null ? data.equals(imData.data) : (imData.data == null)) - && isPrimary == imData.isPrimary); - } - - @Override - public String toString() { - return String.format( - "type: %d, protocol: %d, custom_protcol: %s, data: %s, isPrimary: %s", - type, protocol, customProtocol, data, isPrimary); - } - } - - public static class PhotoData { - public static final String FORMAT_FLASH = "SWF"; - public final int type; - public final String formatName; // used when type is not defined in ContactsContract. - public final byte[] photoBytes; - public final boolean isPrimary; - - public PhotoData(int type, String formatName, byte[] photoBytes, boolean isPrimary) { - this.type = type; - this.formatName = formatName; - this.photoBytes = photoBytes; - this.isPrimary = isPrimary; - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof PhotoData)) { - return false; - } - PhotoData photoData = (PhotoData)obj; - return (type == photoData.type && - (formatName == null ? (photoData.formatName == null) : - formatName.equals(photoData.formatName)) && - (Arrays.equals(photoBytes, photoData.photoBytes)) && - (isPrimary == photoData.isPrimary)); - } - - @Override - public String toString() { - return String.format("type: %d, format: %s: size: %d, isPrimary: %s", - type, formatName, photoBytes.length, isPrimary); - } - } - - /* package */ static class Property { - private String mPropertyName; - private Map> mParameterMap = - new HashMap>(); - private List mPropertyValueList = new ArrayList(); - private byte[] mPropertyBytes; - - public void setPropertyName(final String propertyName) { - mPropertyName = propertyName; - } - - public void addParameter(final String paramName, final String paramValue) { - Collection values; - if (!mParameterMap.containsKey(paramName)) { - if (paramName.equals("TYPE")) { - values = new HashSet(); - } else { - values = new ArrayList(); - } - mParameterMap.put(paramName, values); - } else { - values = mParameterMap.get(paramName); - } - values.add(paramValue); - } - - public void addToPropertyValueList(final String propertyValue) { - mPropertyValueList.add(propertyValue); - } - - public void setPropertyBytes(final byte[] propertyBytes) { - mPropertyBytes = propertyBytes; - } - - public final Collection getParameters(String type) { - return mParameterMap.get(type); - } - - public final List getPropertyValueList() { - return mPropertyValueList; - } - - public void clear() { - mPropertyName = null; - mParameterMap.clear(); - mPropertyValueList.clear(); - mPropertyBytes = null; - } - } - - private String mFamilyName; - private String mGivenName; - private String mMiddleName; - private String mPrefix; - private String mSuffix; - - // Used only when no family nor given name is found. - private String mFormattedName; - - private String mPhoneticFamilyName; - private String mPhoneticGivenName; - private String mPhoneticMiddleName; - - private String mPhoneticFullName; - - private List mNickNameList; - - private String mDisplayName; - - private String mBirthday; - - private List mNoteList; - private List mPhoneList; - private List mEmailList; - private List mPostalList; - private List mOrganizationList; - private List mImList; - private List mPhotoList; - private List mWebsiteList; - private List> mAndroidCustomPropertyList; - - private final int mVCardType; - private final Account mAccount; - - public VCardEntry() { - this(VCardConfig.VCARD_TYPE_V21_GENERIC); - } - - public VCardEntry(int vcardType) { - this(vcardType, null); - } - - public VCardEntry(int vcardType, Account account) { - mVCardType = vcardType; - mAccount = account; - } - - private void addPhone(int type, String data, String label, boolean isPrimary) { - if (mPhoneList == null) { - mPhoneList = new ArrayList(); - } - final StringBuilder builder = new StringBuilder(); - final String trimed = data.trim(); - final String formattedNumber; - if (type == Phone.TYPE_PAGER || VCardConfig.refrainPhoneNumberFormatting(mVCardType)) { - formattedNumber = trimed; - } else { - final int length = trimed.length(); - for (int i = 0; i < length; i++) { - char ch = trimed.charAt(i); - if (('0' <= ch && ch <= '9') || (i == 0 && ch == '+')) { - builder.append(ch); - } - } - - final int formattingType = VCardUtils.getPhoneNumberFormat(mVCardType); - formattedNumber = PhoneNumberUtils.formatNumber(builder.toString(), formattingType); - } - PhoneData phoneData = new PhoneData(type, formattedNumber, label, isPrimary); - mPhoneList.add(phoneData); - } - - private void addNickName(final String nickName) { - if (mNickNameList == null) { - mNickNameList = new ArrayList(); - } - mNickNameList.add(nickName); - } - - private void addEmail(int type, String data, String label, boolean isPrimary){ - if (mEmailList == null) { - mEmailList = new ArrayList(); - } - mEmailList.add(new EmailData(type, data, label, isPrimary)); - } - - private void addPostal(int type, List propValueList, String label, boolean isPrimary){ - if (mPostalList == null) { - mPostalList = new ArrayList(0); - } - mPostalList.add(new PostalData(type, propValueList, label, isPrimary)); - } - - /** - * Should be called via {@link #handleOrgValue(int, List, boolean)} or - * {@link #handleTitleValue(String)}. - */ - private void addNewOrganization(int type, final String companyName, - final String departmentName, - final String titleName, boolean isPrimary) { - if (mOrganizationList == null) { - mOrganizationList = new ArrayList(); - } - mOrganizationList.add(new OrganizationData(type, companyName, - departmentName, titleName, isPrimary)); - } - - private static final List sEmptyList = - Collections.unmodifiableList(new ArrayList(0)); - - /** - * Set "ORG" related values to the appropriate data. If there's more than one - * {@link OrganizationData} objects, this input data are attached to the last one which - * does not have valid values (not including empty but only null). If there's no - * {@link OrganizationData} object, a new {@link OrganizationData} is created, - * whose title is set to null. - */ - private void handleOrgValue(final int type, List orgList, boolean isPrimary) { - if (orgList == null) { - orgList = sEmptyList; - } - final String companyName; - final String departmentName; - final int size = orgList.size(); - switch (size) { - case 0: { - companyName = ""; - departmentName = null; - break; - } - case 1: { - companyName = orgList.get(0); - departmentName = null; - break; - } - default: { // More than 1. - companyName = orgList.get(0); - // We're not sure which is the correct string for department. - // In order to keep all the data, concatinate the rest of elements. - StringBuilder builder = new StringBuilder(); - for (int i = 1; i < size; i++) { - if (i > 1) { - builder.append(' '); - } - builder.append(orgList.get(i)); - } - departmentName = builder.toString(); - } - } - if (mOrganizationList == null) { - // Create new first organization entry, with "null" title which may be - // added via handleTitleValue(). - addNewOrganization(type, companyName, departmentName, null, isPrimary); - return; - } - for (OrganizationData organizationData : mOrganizationList) { - // Not use TextUtils.isEmpty() since ORG was set but the elements might be empty. - // e.g. "ORG;PREF:;" -> Both companyName and departmentName become empty but not null. - if (organizationData.companyName == null && - organizationData.departmentName == null) { - // Probably the "TITLE" property comes before the "ORG" property via - // handleTitleLine(). - organizationData.companyName = companyName; - organizationData.departmentName = departmentName; - organizationData.isPrimary = isPrimary; - return; - } - } - // No OrganizatioData is available. Create another one, with "null" title, which may be - // added via handleTitleValue(). - addNewOrganization(type, companyName, departmentName, null, isPrimary); - } - - /** - * Set "title" value to the appropriate data. If there's more than one - * OrganizationData objects, this input is attached to the last one which does not - * have valid title value (not including empty but only null). If there's no - * OrganizationData object, a new OrganizationData is created, whose company name is - * set to null. - */ - private void handleTitleValue(final String title) { - if (mOrganizationList == null) { - // Create new first organization entry, with "null" other info, which may be - // added via handleOrgValue(). - addNewOrganization(DEFAULT_ORGANIZATION_TYPE, null, null, title, false); - return; - } - for (OrganizationData organizationData : mOrganizationList) { - if (organizationData.titleName == null) { - organizationData.titleName = title; - return; - } - } - // No Organization is available. Create another one, with "null" other info, which may be - // added via handleOrgValue(). - addNewOrganization(DEFAULT_ORGANIZATION_TYPE, null, null, title, false); - } - - private void addIm(int protocol, String customProtocol, int type, - String propValue, boolean isPrimary) { - if (mImList == null) { - mImList = new ArrayList(); - } - mImList.add(new ImData(protocol, customProtocol, type, propValue, isPrimary)); - } - - private void addNote(final String note) { - if (mNoteList == null) { - mNoteList = new ArrayList(1); - } - mNoteList.add(note); - } - - private void addPhotoBytes(String formatName, byte[] photoBytes, boolean isPrimary) { - if (mPhotoList == null) { - mPhotoList = new ArrayList(1); - } - final PhotoData photoData = new PhotoData(0, null, photoBytes, isPrimary); - mPhotoList.add(photoData); - } - - @SuppressWarnings("fallthrough") - private void handleNProperty(List elems) { - // Family, Given, Middle, Prefix, Suffix. (1 - 5) - int size; - if (elems == null || (size = elems.size()) < 1) { - return; - } - if (size > 5) { - size = 5; - } - - switch (size) { - // fallthrough - case 5: mSuffix = elems.get(4); - case 4: mPrefix = elems.get(3); - case 3: mMiddleName = elems.get(2); - case 2: mGivenName = elems.get(1); - default: mFamilyName = elems.get(0); - } - } - - /** - * Note: Some Japanese mobile phones use this field for phonetic name, - * since vCard 2.1 does not have "SORT-STRING" type. - * Also, in some cases, the field has some ';'s in it. - * Assume the ';' means the same meaning in N property - */ - @SuppressWarnings("fallthrough") - private void handlePhoneticNameFromSound(List elems) { - if (!(TextUtils.isEmpty(mPhoneticFamilyName) && - TextUtils.isEmpty(mPhoneticMiddleName) && - TextUtils.isEmpty(mPhoneticGivenName))) { - // This means the other properties like "X-PHONETIC-FIRST-NAME" was already found. - // Ignore "SOUND;X-IRMC-N". - return; - } - - int size; - if (elems == null || (size = elems.size()) < 1) { - return; - } - - // Assume that the order is "Family, Given, Middle". - // This is not from specification but mere assumption. Some Japanese phones use this order. - if (size > 3) { - size = 3; - } - - if (elems.get(0).length() > 0) { - boolean onlyFirstElemIsNonEmpty = true; - for (int i = 1; i < size; i++) { - if (elems.get(i).length() > 0) { - onlyFirstElemIsNonEmpty = false; - break; - } - } - if (onlyFirstElemIsNonEmpty) { - final String[] namesArray = elems.get(0).split(" "); - final int nameArrayLength = namesArray.length; - if (nameArrayLength == 3) { - // Assume the string is "Family Middle Given". - mPhoneticFamilyName = namesArray[0]; - mPhoneticMiddleName = namesArray[1]; - mPhoneticGivenName = namesArray[2]; - } else if (nameArrayLength == 2) { - // Assume the string is "Family Given" based on the Japanese mobile - // phones' preference. - mPhoneticFamilyName = namesArray[0]; - mPhoneticGivenName = namesArray[1]; - } else { - mPhoneticFullName = elems.get(0); - } - return; - } - } - - switch (size) { - // fallthrough - case 3: mPhoneticMiddleName = elems.get(2); - case 2: mPhoneticGivenName = elems.get(1); - default: mPhoneticFamilyName = elems.get(0); - } - } - - public void addProperty(final Property property) { - final String propName = property.mPropertyName; - final Map> paramMap = property.mParameterMap; - final List propValueList = property.mPropertyValueList; - byte[] propBytes = property.mPropertyBytes; - - if (propValueList.size() == 0) { - return; - } - final String propValue = listToString(propValueList).trim(); - - if (propName.equals(VCardConstants.PROPERTY_VERSION)) { - // vCard version. Ignore this. - } else if (propName.equals(VCardConstants.PROPERTY_FN)) { - mFormattedName = propValue; - } else if (propName.equals(VCardConstants.PROPERTY_NAME) && mFormattedName == null) { - // Only in vCard 3.0. Use this if FN, which must exist in vCard 3.0 but may not - // actually exist in the real vCard data, does not exist. - mFormattedName = propValue; - } else if (propName.equals(VCardConstants.PROPERTY_N)) { - handleNProperty(propValueList); - } else if (propName.equals(VCardConstants.PROPERTY_SORT_STRING)) { - mPhoneticFullName = propValue; - } else if (propName.equals(VCardConstants.PROPERTY_NICKNAME) || - propName.equals(VCardConstants.ImportOnly.PROPERTY_X_NICKNAME)) { - addNickName(propValue); - } else if (propName.equals(VCardConstants.PROPERTY_SOUND)) { - Collection typeCollection = paramMap.get(VCardConstants.PARAM_TYPE); - if (typeCollection != null - && typeCollection.contains(VCardConstants.PARAM_TYPE_X_IRMC_N)) { - // As of 2009-10-08, Parser side does not split a property value into separated - // values using ';' (in other words, propValueList.size() == 1), - // which is correct behavior from the view of vCard 2.1. - // But we want it to be separated, so do the separation here. - final List phoneticNameList = - VCardUtils.constructListFromValue(propValue, - VCardConfig.isV30(mVCardType)); - handlePhoneticNameFromSound(phoneticNameList); - } else { - // Ignore this field since Android cannot understand what it is. - } - } else if (propName.equals(VCardConstants.PROPERTY_ADR)) { - boolean valuesAreAllEmpty = true; - for (String value : propValueList) { - if (value.length() > 0) { - valuesAreAllEmpty = false; - break; - } - } - if (valuesAreAllEmpty) { - return; - } - - int type = -1; - String label = ""; - boolean isPrimary = false; - Collection typeCollection = paramMap.get(VCardConstants.PARAM_TYPE); - if (typeCollection != null) { - for (String typeString : typeCollection) { - typeString = typeString.toUpperCase(); - if (typeString.equals(VCardConstants.PARAM_TYPE_PREF)) { - isPrimary = true; - } else if (typeString.equals(VCardConstants.PARAM_TYPE_HOME)) { - type = StructuredPostal.TYPE_HOME; - label = ""; - } else if (typeString.equals(VCardConstants.PARAM_TYPE_WORK) || - typeString.equalsIgnoreCase(VCardConstants.PARAM_EXTRA_TYPE_COMPANY)) { - // "COMPANY" seems emitted by Windows Mobile, which is not - // specifically supported by vCard 2.1. We assume this is same - // as "WORK". - type = StructuredPostal.TYPE_WORK; - label = ""; - } else if (typeString.equals(VCardConstants.PARAM_ADR_TYPE_PARCEL) || - typeString.equals(VCardConstants.PARAM_ADR_TYPE_DOM) || - typeString.equals(VCardConstants.PARAM_ADR_TYPE_INTL)) { - // We do not have any appropriate way to store this information. - } else { - if (typeString.startsWith("X-") && type < 0) { - typeString = typeString.substring(2); - } - // vCard 3.0 allows iana-token. Also some vCard 2.1 exporters - // emit non-standard types. We do not handle their values now. - type = StructuredPostal.TYPE_CUSTOM; - label = typeString; - } - } - } - // We use "HOME" as default - if (type < 0) { - type = StructuredPostal.TYPE_HOME; - } - - addPostal(type, propValueList, label, isPrimary); - } else if (propName.equals(VCardConstants.PROPERTY_EMAIL)) { - int type = -1; - String label = null; - boolean isPrimary = false; - Collection typeCollection = paramMap.get(VCardConstants.PARAM_TYPE); - if (typeCollection != null) { - for (String typeString : typeCollection) { - typeString = typeString.toUpperCase(); - if (typeString.equals(VCardConstants.PARAM_TYPE_PREF)) { - isPrimary = true; - } else if (typeString.equals(VCardConstants.PARAM_TYPE_HOME)) { - type = Email.TYPE_HOME; - } else if (typeString.equals(VCardConstants.PARAM_TYPE_WORK)) { - type = Email.TYPE_WORK; - } else if (typeString.equals(VCardConstants.PARAM_TYPE_CELL)) { - type = Email.TYPE_MOBILE; - } else { - if (typeString.startsWith("X-") && type < 0) { - typeString = typeString.substring(2); - } - // vCard 3.0 allows iana-token. - // We may have INTERNET (specified in vCard spec), - // SCHOOL, etc. - type = Email.TYPE_CUSTOM; - label = typeString; - } - } - } - if (type < 0) { - type = Email.TYPE_OTHER; - } - addEmail(type, propValue, label, isPrimary); - } else if (propName.equals(VCardConstants.PROPERTY_ORG)) { - // vCard specification does not specify other types. - final int type = Organization.TYPE_WORK; - boolean isPrimary = false; - Collection typeCollection = paramMap.get(VCardConstants.PARAM_TYPE); - if (typeCollection != null) { - for (String typeString : typeCollection) { - if (typeString.equals(VCardConstants.PARAM_TYPE_PREF)) { - isPrimary = true; - } - } - } - handleOrgValue(type, propValueList, isPrimary); - } else if (propName.equals(VCardConstants.PROPERTY_TITLE)) { - handleTitleValue(propValue); - } else if (propName.equals(VCardConstants.PROPERTY_ROLE)) { - // This conflicts with TITLE. Ignore for now... - // handleTitleValue(propValue); - } else if (propName.equals(VCardConstants.PROPERTY_PHOTO) || - propName.equals(VCardConstants.PROPERTY_LOGO)) { - Collection paramMapValue = paramMap.get("VALUE"); - if (paramMapValue != null && paramMapValue.contains("URL")) { - // Currently we do not have appropriate example for testing this case. - } else { - final Collection typeCollection = paramMap.get("TYPE"); - String formatName = null; - boolean isPrimary = false; - if (typeCollection != null) { - for (String typeValue : typeCollection) { - if (VCardConstants.PARAM_TYPE_PREF.equals(typeValue)) { - isPrimary = true; - } else if (formatName == null){ - formatName = typeValue; - } - } - } - addPhotoBytes(formatName, propBytes, isPrimary); - } - } else if (propName.equals(VCardConstants.PROPERTY_TEL)) { - final Collection typeCollection = paramMap.get(VCardConstants.PARAM_TYPE); - final Object typeObject = - VCardUtils.getPhoneTypeFromStrings(typeCollection, propValue); - final int type; - final String label; - if (typeObject instanceof Integer) { - type = (Integer)typeObject; - label = null; - } else { - type = Phone.TYPE_CUSTOM; - label = typeObject.toString(); - } - - final boolean isPrimary; - if (typeCollection != null && typeCollection.contains(VCardConstants.PARAM_TYPE_PREF)) { - isPrimary = true; - } else { - isPrimary = false; - } - addPhone(type, propValue, label, isPrimary); - } else if (propName.equals(VCardConstants.PROPERTY_X_SKYPE_PSTNNUMBER)) { - // The phone number available via Skype. - Collection typeCollection = paramMap.get(VCardConstants.PARAM_TYPE); - final int type = Phone.TYPE_OTHER; - final boolean isPrimary; - if (typeCollection != null && typeCollection.contains(VCardConstants.PARAM_TYPE_PREF)) { - isPrimary = true; - } else { - isPrimary = false; - } - addPhone(type, propValue, null, isPrimary); - } else if (sImMap.containsKey(propName)) { - final int protocol = sImMap.get(propName); - boolean isPrimary = false; - int type = -1; - final Collection typeCollection = paramMap.get(VCardConstants.PARAM_TYPE); - if (typeCollection != null) { - for (String typeString : typeCollection) { - if (typeString.equals(VCardConstants.PARAM_TYPE_PREF)) { - isPrimary = true; - } else if (type < 0) { - if (typeString.equalsIgnoreCase(VCardConstants.PARAM_TYPE_HOME)) { - type = Im.TYPE_HOME; - } else if (typeString.equalsIgnoreCase(VCardConstants.PARAM_TYPE_WORK)) { - type = Im.TYPE_WORK; - } - } - } - } - if (type < 0) { - type = Phone.TYPE_HOME; - } - addIm(protocol, null, type, propValue, isPrimary); - } else if (propName.equals(VCardConstants.PROPERTY_NOTE)) { - addNote(propValue); - } else if (propName.equals(VCardConstants.PROPERTY_URL)) { - if (mWebsiteList == null) { - mWebsiteList = new ArrayList(1); - } - mWebsiteList.add(propValue); - } else if (propName.equals(VCardConstants.PROPERTY_BDAY)) { - mBirthday = propValue; - } else if (propName.equals(VCardConstants.PROPERTY_X_PHONETIC_FIRST_NAME)) { - mPhoneticGivenName = propValue; - } else if (propName.equals(VCardConstants.PROPERTY_X_PHONETIC_MIDDLE_NAME)) { - mPhoneticMiddleName = propValue; - } else if (propName.equals(VCardConstants.PROPERTY_X_PHONETIC_LAST_NAME)) { - mPhoneticFamilyName = propValue; - } else if (propName.equals(VCardConstants.PROPERTY_X_ANDROID_CUSTOM)) { - final List customPropertyList = - VCardUtils.constructListFromValue(propValue, - VCardConfig.isV30(mVCardType)); - handleAndroidCustomProperty(customPropertyList); - /*} else if (propName.equals("REV")) { - // Revision of this VCard entry. I think we can ignore this. - } else if (propName.equals("UID")) { - } else if (propName.equals("KEY")) { - // Type is X509 or PGP? I don't know how to handle this... - } else if (propName.equals("MAILER")) { - } else if (propName.equals("TZ")) { - } else if (propName.equals("GEO")) { - } else if (propName.equals("CLASS")) { - // vCard 3.0 only. - // e.g. CLASS:CONFIDENTIAL - } else if (propName.equals("PROFILE")) { - // VCard 3.0 only. Must be "VCARD". I think we can ignore this. - } else if (propName.equals("CATEGORIES")) { - // VCard 3.0 only. - // e.g. CATEGORIES:INTERNET,IETF,INDUSTRY,INFORMATION TECHNOLOGY - } else if (propName.equals("SOURCE")) { - // VCard 3.0 only. - } else if (propName.equals("PRODID")) { - // VCard 3.0 only. - // To specify the identifier for the product that created - // the vCard object.*/ - } else { - // Unknown X- words and IANA token. - } - } - - private void handleAndroidCustomProperty(final List customPropertyList) { - if (mAndroidCustomPropertyList == null) { - mAndroidCustomPropertyList = new ArrayList>(); - } - mAndroidCustomPropertyList.add(customPropertyList); - } - - /** - * Construct the display name. The constructed data must not be null. - */ - private void constructDisplayName() { - // FullName (created via "FN" or "NAME" field) is prefered. - if (!TextUtils.isEmpty(mFormattedName)) { - mDisplayName = mFormattedName; - } else if (!(TextUtils.isEmpty(mFamilyName) && TextUtils.isEmpty(mGivenName))) { - mDisplayName = VCardUtils.constructNameFromElements(mVCardType, - mFamilyName, mMiddleName, mGivenName, mPrefix, mSuffix); - } else if (!(TextUtils.isEmpty(mPhoneticFamilyName) && - TextUtils.isEmpty(mPhoneticGivenName))) { - mDisplayName = VCardUtils.constructNameFromElements(mVCardType, - mPhoneticFamilyName, mPhoneticMiddleName, mPhoneticGivenName); - } else if (mEmailList != null && mEmailList.size() > 0) { - mDisplayName = mEmailList.get(0).data; - } else if (mPhoneList != null && mPhoneList.size() > 0) { - mDisplayName = mPhoneList.get(0).data; - } else if (mPostalList != null && mPostalList.size() > 0) { - mDisplayName = mPostalList.get(0).getFormattedAddress(mVCardType); - } else if (mOrganizationList != null && mOrganizationList.size() > 0) { - mDisplayName = mOrganizationList.get(0).getFormattedString(); - } - - if (mDisplayName == null) { - mDisplayName = ""; - } - } - - /** - * Consolidate several fielsds (like mName) using name candidates, - */ - public void consolidateFields() { - constructDisplayName(); - - if (mPhoneticFullName != null) { - mPhoneticFullName = mPhoneticFullName.trim(); - } - } - - public Uri pushIntoContentResolver(ContentResolver resolver) { - ArrayList operationList = - new ArrayList(); - // After applying the batch the first result's Uri is returned so it is important that - // the RawContact is the first operation that gets inserted into the list - ContentProviderOperation.Builder builder = - ContentProviderOperation.newInsert(RawContacts.CONTENT_URI); - String myGroupsId = null; - if (mAccount != null) { - builder.withValue(RawContacts.ACCOUNT_NAME, mAccount.name); - builder.withValue(RawContacts.ACCOUNT_TYPE, mAccount.type); - } else { - builder.withValue(RawContacts.ACCOUNT_NAME, null); - builder.withValue(RawContacts.ACCOUNT_TYPE, null); - } - operationList.add(builder.build()); - - if (!nameFieldsAreEmpty()) { - builder = ContentProviderOperation.newInsert(Data.CONTENT_URI); - builder.withValueBackReference(StructuredName.RAW_CONTACT_ID, 0); - builder.withValue(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE); - - builder.withValue(StructuredName.GIVEN_NAME, mGivenName); - builder.withValue(StructuredName.FAMILY_NAME, mFamilyName); - builder.withValue(StructuredName.MIDDLE_NAME, mMiddleName); - builder.withValue(StructuredName.PREFIX, mPrefix); - builder.withValue(StructuredName.SUFFIX, mSuffix); - - if (!(TextUtils.isEmpty(mPhoneticGivenName) - && TextUtils.isEmpty(mPhoneticFamilyName) - && TextUtils.isEmpty(mPhoneticMiddleName))) { - builder.withValue(StructuredName.PHONETIC_GIVEN_NAME, mPhoneticGivenName); - builder.withValue(StructuredName.PHONETIC_FAMILY_NAME, mPhoneticFamilyName); - builder.withValue(StructuredName.PHONETIC_MIDDLE_NAME, mPhoneticMiddleName); - } else if (!TextUtils.isEmpty(mPhoneticFullName)) { - builder.withValue(StructuredName.PHONETIC_GIVEN_NAME, mPhoneticFullName); - } - - builder.withValue(StructuredName.DISPLAY_NAME, getDisplayName()); - operationList.add(builder.build()); - } - - if (mNickNameList != null && mNickNameList.size() > 0) { - for (String nickName : mNickNameList) { - builder = ContentProviderOperation.newInsert(Data.CONTENT_URI); - builder.withValueBackReference(Nickname.RAW_CONTACT_ID, 0); - builder.withValue(Data.MIMETYPE, Nickname.CONTENT_ITEM_TYPE); - builder.withValue(Nickname.TYPE, Nickname.TYPE_DEFAULT); - builder.withValue(Nickname.NAME, nickName); - operationList.add(builder.build()); - } - } - - if (mPhoneList != null) { - for (PhoneData phoneData : mPhoneList) { - builder = ContentProviderOperation.newInsert(Data.CONTENT_URI); - builder.withValueBackReference(Phone.RAW_CONTACT_ID, 0); - builder.withValue(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE); - - builder.withValue(Phone.TYPE, phoneData.type); - if (phoneData.type == Phone.TYPE_CUSTOM) { - builder.withValue(Phone.LABEL, phoneData.label); - } - builder.withValue(Phone.NUMBER, phoneData.data); - if (phoneData.isPrimary) { - builder.withValue(Phone.IS_PRIMARY, 1); - } - operationList.add(builder.build()); - } - } - - if (mOrganizationList != null) { - for (OrganizationData organizationData : mOrganizationList) { - builder = ContentProviderOperation.newInsert(Data.CONTENT_URI); - builder.withValueBackReference(Organization.RAW_CONTACT_ID, 0); - builder.withValue(Data.MIMETYPE, Organization.CONTENT_ITEM_TYPE); - builder.withValue(Organization.TYPE, organizationData.type); - if (organizationData.companyName != null) { - builder.withValue(Organization.COMPANY, organizationData.companyName); - } - if (organizationData.departmentName != null) { - builder.withValue(Organization.DEPARTMENT, organizationData.departmentName); - } - if (organizationData.titleName != null) { - builder.withValue(Organization.TITLE, organizationData.titleName); - } - if (organizationData.isPrimary) { - builder.withValue(Organization.IS_PRIMARY, 1); - } - operationList.add(builder.build()); - } - } - - if (mEmailList != null) { - for (EmailData emailData : mEmailList) { - builder = ContentProviderOperation.newInsert(Data.CONTENT_URI); - builder.withValueBackReference(Email.RAW_CONTACT_ID, 0); - builder.withValue(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE); - - builder.withValue(Email.TYPE, emailData.type); - if (emailData.type == Email.TYPE_CUSTOM) { - builder.withValue(Email.LABEL, emailData.label); - } - builder.withValue(Email.DATA, emailData.data); - if (emailData.isPrimary) { - builder.withValue(Data.IS_PRIMARY, 1); - } - operationList.add(builder.build()); - } - } - - if (mPostalList != null) { - for (PostalData postalData : mPostalList) { - builder = ContentProviderOperation.newInsert(Data.CONTENT_URI); - VCardUtils.insertStructuredPostalDataUsingContactsStruct( - mVCardType, builder, postalData); - operationList.add(builder.build()); - } - } - - if (mImList != null) { - for (ImData imData : mImList) { - builder = ContentProviderOperation.newInsert(Data.CONTENT_URI); - builder.withValueBackReference(Im.RAW_CONTACT_ID, 0); - builder.withValue(Data.MIMETYPE, Im.CONTENT_ITEM_TYPE); - builder.withValue(Im.TYPE, imData.type); - builder.withValue(Im.PROTOCOL, imData.protocol); - if (imData.protocol == Im.PROTOCOL_CUSTOM) { - builder.withValue(Im.CUSTOM_PROTOCOL, imData.customProtocol); - } - if (imData.isPrimary) { - builder.withValue(Data.IS_PRIMARY, 1); - } - } - } - - if (mNoteList != null) { - for (String note : mNoteList) { - builder = ContentProviderOperation.newInsert(Data.CONTENT_URI); - builder.withValueBackReference(Note.RAW_CONTACT_ID, 0); - builder.withValue(Data.MIMETYPE, Note.CONTENT_ITEM_TYPE); - builder.withValue(Note.NOTE, note); - operationList.add(builder.build()); - } - } - - if (mPhotoList != null) { - for (PhotoData photoData : mPhotoList) { - builder = ContentProviderOperation.newInsert(Data.CONTENT_URI); - builder.withValueBackReference(Photo.RAW_CONTACT_ID, 0); - builder.withValue(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE); - builder.withValue(Photo.PHOTO, photoData.photoBytes); - if (photoData.isPrimary) { - builder.withValue(Photo.IS_PRIMARY, 1); - } - operationList.add(builder.build()); - } - } - - if (mWebsiteList != null) { - for (String website : mWebsiteList) { - builder = ContentProviderOperation.newInsert(Data.CONTENT_URI); - builder.withValueBackReference(Website.RAW_CONTACT_ID, 0); - builder.withValue(Data.MIMETYPE, Website.CONTENT_ITEM_TYPE); - builder.withValue(Website.URL, website); - // There's no information about the type of URL in vCard. - // We use TYPE_HOMEPAGE for safety. - builder.withValue(Website.TYPE, Website.TYPE_HOMEPAGE); - operationList.add(builder.build()); - } - } - - if (!TextUtils.isEmpty(mBirthday)) { - builder = ContentProviderOperation.newInsert(Data.CONTENT_URI); - builder.withValueBackReference(Event.RAW_CONTACT_ID, 0); - builder.withValue(Data.MIMETYPE, Event.CONTENT_ITEM_TYPE); - builder.withValue(Event.START_DATE, mBirthday); - builder.withValue(Event.TYPE, Event.TYPE_BIRTHDAY); - operationList.add(builder.build()); - } - - if (mAndroidCustomPropertyList != null) { - for (List customPropertyList : mAndroidCustomPropertyList) { - int size = customPropertyList.size(); - if (size < 2 || TextUtils.isEmpty(customPropertyList.get(0))) { - continue; - } else if (size > VCardConstants.MAX_DATA_COLUMN + 1) { - size = VCardConstants.MAX_DATA_COLUMN + 1; - customPropertyList = - customPropertyList.subList(0, VCardConstants.MAX_DATA_COLUMN + 2); - } - - int i = 0; - for (final String customPropertyValue : customPropertyList) { - if (i == 0) { - final String mimeType = customPropertyValue; - builder = ContentProviderOperation.newInsert(Data.CONTENT_URI); - builder.withValueBackReference(GroupMembership.RAW_CONTACT_ID, 0); - builder.withValue(Data.MIMETYPE, mimeType); - } else { // 1 <= i && i <= MAX_DATA_COLUMNS - if (!TextUtils.isEmpty(customPropertyValue)) { - builder.withValue("data" + i, customPropertyValue); - } - } - - i++; - } - operationList.add(builder.build()); - } - } - - if (myGroupsId != null) { - builder = ContentProviderOperation.newInsert(Data.CONTENT_URI); - builder.withValueBackReference(GroupMembership.RAW_CONTACT_ID, 0); - builder.withValue(Data.MIMETYPE, GroupMembership.CONTENT_ITEM_TYPE); - builder.withValue(GroupMembership.GROUP_SOURCE_ID, myGroupsId); - operationList.add(builder.build()); - } - - try { - ContentProviderResult[] results = resolver.applyBatch( - ContactsContract.AUTHORITY, operationList); - // the first result is always the raw_contact. return it's uri so - // that it can be found later. do null checking for badly behaving - // ContentResolvers - return (results == null || results.length == 0 || results[0] == null) - ? null - : results[0].uri; - } catch (RemoteException e) { - Log.e(LOG_TAG, String.format("%s: %s", e.toString(), e.getMessage())); - return null; - } catch (OperationApplicationException e) { - Log.e(LOG_TAG, String.format("%s: %s", e.toString(), e.getMessage())); - return null; - } - } - - public static VCardEntry buildFromResolver(ContentResolver resolver) { - return buildFromResolver(resolver, Contacts.CONTENT_URI); - } - - public static VCardEntry buildFromResolver(ContentResolver resolver, Uri uri) { - - return null; - } - - private boolean nameFieldsAreEmpty() { - return (TextUtils.isEmpty(mFamilyName) - && TextUtils.isEmpty(mMiddleName) - && TextUtils.isEmpty(mGivenName) - && TextUtils.isEmpty(mPrefix) - && TextUtils.isEmpty(mSuffix) - && TextUtils.isEmpty(mFormattedName) - && TextUtils.isEmpty(mPhoneticFamilyName) - && TextUtils.isEmpty(mPhoneticMiddleName) - && TextUtils.isEmpty(mPhoneticGivenName) - && TextUtils.isEmpty(mPhoneticFullName)); - } - - public boolean isIgnorable() { - return getDisplayName().length() == 0; - } - - private String listToString(List list){ - final int size = list.size(); - if (size > 1) { - StringBuilder builder = new StringBuilder(); - int i = 0; - for (String type : list) { - builder.append(type); - if (i < size - 1) { - builder.append(";"); - } - } - return builder.toString(); - } else if (size == 1) { - return list.get(0); - } else { - return ""; - } - } - - // All getter methods should be used carefully, since they may change - // in the future as of 2009-10-05, on which I cannot be sure this structure - // is completely consolidated. - // - // Also note that these getter methods should be used only after - // all properties being pushed into this object. If not, incorrect - // value will "be stored in the local cache and" be returned to you. - - public String getFamilyName() { - return mFamilyName; - } - - public String getGivenName() { - return mGivenName; - } - - public String getMiddleName() { - return mMiddleName; - } - - public String getPrefix() { - return mPrefix; - } - - public String getSuffix() { - return mSuffix; - } - - public String getFullName() { - return mFormattedName; - } - - public String getPhoneticFamilyName() { - return mPhoneticFamilyName; - } - - public String getPhoneticGivenName() { - return mPhoneticGivenName; - } - - public String getPhoneticMiddleName() { - return mPhoneticMiddleName; - } - - public String getPhoneticFullName() { - return mPhoneticFullName; - } - - public final List getNickNameList() { - return mNickNameList; - } - - public String getBirthday() { - return mBirthday; - } - - public final List getNotes() { - return mNoteList; - } - - public final List getPhoneList() { - return mPhoneList; - } - - public final List getEmailList() { - return mEmailList; - } - - public final List getPostalList() { - return mPostalList; - } - - public final List getOrganizationList() { - return mOrganizationList; - } - - public final List getImList() { - return mImList; - } - - public final List getPhotoList() { - return mPhotoList; - } - - public final List getWebsiteList() { - return mWebsiteList; - } - - public String getDisplayName() { - if (mDisplayName == null) { - constructDisplayName(); - } - return mDisplayName; - } -} diff --git a/vcard/java/com/android/vcard/VCardEntryCommitter.java b/vcard/java/com/android/vcard/VCardEntryCommitter.java deleted file mode 100644 index 7bd314eb3..000000000 --- a/vcard/java/com/android/vcard/VCardEntryCommitter.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * 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.vcard; - -import android.content.ContentResolver; -import android.net.Uri; -import android.util.Log; - -import java.util.ArrayList; - -/** - *

- * {@link VCardEntryHandler} implementation which commits the entry to ContentResolver. - *

- *

- * Note:
- * Each vCard may contain big photo images encoded by BASE64, - * If we store all vCard entries in memory, OutOfMemoryError may be thrown. - * Thus, this class push each VCard entry into ContentResolver immediately. - *

- */ -public class VCardEntryCommitter implements VCardEntryHandler { - public static String LOG_TAG = "VCardEntryComitter"; - - private final ContentResolver mContentResolver; - private long mTimeToCommit; - private ArrayList mCreatedUris = new ArrayList(); - - public VCardEntryCommitter(ContentResolver resolver) { - mContentResolver = resolver; - } - - public void onStart() { - } - - public void onEnd() { - if (VCardConfig.showPerformanceLog()) { - Log.d(LOG_TAG, String.format("time to commit entries: %d ms", mTimeToCommit)); - } - } - - public void onEntryCreated(final VCardEntry vcardEntry) { - long start = System.currentTimeMillis(); - mCreatedUris.add(vcardEntry.pushIntoContentResolver(mContentResolver)); - mTimeToCommit += System.currentTimeMillis() - start; - } - - /** - * Returns the list of created Uris. This list should not be modified by the caller as it is - * not a clone. - */ - public ArrayList getCreatedUris() { - return mCreatedUris; - } -} \ No newline at end of file diff --git a/vcard/java/com/android/vcard/VCardEntryConstructor.java b/vcard/java/com/android/vcard/VCardEntryConstructor.java deleted file mode 100644 index 6cee0704b..000000000 --- a/vcard/java/com/android/vcard/VCardEntryConstructor.java +++ /dev/null @@ -1,240 +0,0 @@ -/* - * 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.vcard; - -import android.accounts.Account; -import android.text.TextUtils; -import android.util.Base64; -import android.util.CharsetUtils; -import android.util.Log; - -import java.io.UnsupportedEncodingException; -import java.nio.ByteBuffer; -import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -/** - *

- * The {@link VCardInterpreter} implementation which enables {@link VCardEntryHandler} objects - * to easily handle each vCard entry. - *

- *

- * This class understand details inside vCard and translates it to {@link VCardEntry}. - * Then the class throw it to {@link VCardEntryHandler} registered via - * {@link #addEntryHandler(VCardEntryHandler)}, so that all those registered objects - * are able to handle the {@link VCardEntry} object. - *

- *

- * If you want to know the detail inside vCard, it would be better to implement - * {@link VCardInterpreter} directly, instead of relying on this class and - * {@link VCardEntry} created by the object. - *

- */ -public class VCardEntryConstructor implements VCardInterpreter { - private static String LOG_TAG = "VCardEntryConstructor"; - - private VCardEntry.Property mCurrentProperty = new VCardEntry.Property(); - private VCardEntry mCurrentVCardEntry; - private String mParamType; - - // The charset using which {@link VCardInterpreter} parses the text. - // Each String is first decoded into binary stream with this charset, and encoded back - // to "target charset", which may be explicitly specified by the vCard with "CHARSET" - // property or implicitly mentioned by its version (e.g. vCard 3.0 recommends UTF-8). - private final String mSourceCharset; - - private final boolean mStrictLineBreaking; - private final int mVCardType; - private final Account mAccount; - - // For measuring performance. - private long mTimePushIntoContentResolver; - - private final List mEntryHandlers = new ArrayList(); - - public VCardEntryConstructor() { - this(VCardConfig.VCARD_TYPE_V21_GENERIC, null); - } - - public VCardEntryConstructor(final int vcardType) { - this(vcardType, null, null, false); - } - - public VCardEntryConstructor(final int vcardType, final Account account) { - this(vcardType, account, null, false); - } - - public VCardEntryConstructor(final int vcardType, final Account account, - final String inputCharset) { - this(vcardType, account, inputCharset, false); - } - - /** - * @hide Just for testing. - */ - public VCardEntryConstructor(final int vcardType, final Account account, - final String inputCharset, final boolean strictLineBreakParsing) { - if (inputCharset != null) { - mSourceCharset = inputCharset; - } else { - mSourceCharset = VCardConfig.DEFAULT_INTERMEDIATE_CHARSET; - } - mStrictLineBreaking = strictLineBreakParsing; - mVCardType = vcardType; - mAccount = account; - } - - public void addEntryHandler(VCardEntryHandler entryHandler) { - mEntryHandlers.add(entryHandler); - } - - public void start() { - for (VCardEntryHandler entryHandler : mEntryHandlers) { - entryHandler.onStart(); - } - } - - public void end() { - for (VCardEntryHandler entryHandler : mEntryHandlers) { - entryHandler.onEnd(); - } - } - - public void clear() { - mCurrentVCardEntry = null; - mCurrentProperty = new VCardEntry.Property(); - } - - public void startEntry() { - if (mCurrentVCardEntry != null) { - Log.e(LOG_TAG, "Nested VCard code is not supported now."); - } - mCurrentVCardEntry = new VCardEntry(mVCardType, mAccount); - } - - public void endEntry() { - mCurrentVCardEntry.consolidateFields(); - for (VCardEntryHandler entryHandler : mEntryHandlers) { - entryHandler.onEntryCreated(mCurrentVCardEntry); - } - mCurrentVCardEntry = null; - } - - public void startProperty() { - mCurrentProperty.clear(); - } - - public void endProperty() { - mCurrentVCardEntry.addProperty(mCurrentProperty); - } - - public void propertyName(String name) { - mCurrentProperty.setPropertyName(name); - } - - public void propertyGroup(String group) { - } - - public void propertyParamType(String type) { - if (mParamType != null) { - Log.e(LOG_TAG, "propertyParamType() is called more than once " + - "before propertyParamValue() is called"); - } - mParamType = type; - } - - public void propertyParamValue(String value) { - if (mParamType == null) { - // From vCard 2.1 specification. vCard 3.0 formally does not allow this case. - mParamType = "TYPE"; - } - mCurrentProperty.addParameter(mParamType, value); - mParamType = null; - } - - private static String encodeToSystemCharset(String originalString, - String sourceCharset, String targetCharset) { - if (sourceCharset.equalsIgnoreCase(targetCharset)) { - return originalString; - } - final Charset charset = Charset.forName(sourceCharset); - final ByteBuffer byteBuffer = charset.encode(originalString); - // byteBuffer.array() "may" return byte array which is larger than - // byteBuffer.remaining(). Here, we keep on the safe side. - final byte[] bytes = new byte[byteBuffer.remaining()]; - byteBuffer.get(bytes); - try { - String ret = new String(bytes, targetCharset); - return ret; - } catch (UnsupportedEncodingException e) { - Log.e(LOG_TAG, "Failed to encode: charset=" + targetCharset); - return null; - } - } - - private String handleOneValue(String value, - String sourceCharset, String targetCharset, String encoding) { - if (value == null) { - Log.w(LOG_TAG, "Null is given."); - value = ""; - } - - if (encoding != null) { - if (encoding.equals("BASE64") || encoding.equals("B")) { - mCurrentProperty.setPropertyBytes(Base64.decode(value.getBytes(), Base64.DEFAULT)); - return value; - } else if (encoding.equals("QUOTED-PRINTABLE")) { - return VCardUtils.parseQuotedPrintable( - value, mStrictLineBreaking, sourceCharset, targetCharset); - } - Log.w(LOG_TAG, "Unknown encoding. Fall back to default."); - } - - // Just translate the charset of a given String from inputCharset to a system one. - return encodeToSystemCharset(value, sourceCharset, targetCharset); - } - - public void propertyValues(List values) { - if (values == null || values.isEmpty()) { - return; - } - - final Collection charsetCollection = mCurrentProperty.getParameters("CHARSET"); - final Collection encodingCollection = mCurrentProperty.getParameters("ENCODING"); - final String encoding = - ((encodingCollection != null) ? encodingCollection.iterator().next() : null); - String targetCharset = CharsetUtils.nameForDefaultVendor( - ((charsetCollection != null) ? charsetCollection.iterator().next() : null)); - if (TextUtils.isEmpty(targetCharset)) { - targetCharset = VCardConfig.DEFAULT_IMPORT_CHARSET; - } - - for (final String value : values) { - mCurrentProperty.addToPropertyValueList( - handleOneValue(value, mSourceCharset, targetCharset, encoding)); - } - } - - /** - * @hide - */ - public void showPerformanceInfo() { - Log.d(LOG_TAG, "time for insert ContactStruct to database: " + - mTimePushIntoContentResolver + " ms"); - } -} diff --git a/vcard/java/com/android/vcard/VCardEntryCounter.java b/vcard/java/com/android/vcard/VCardEntryCounter.java deleted file mode 100644 index 7bfe9773f..000000000 --- a/vcard/java/com/android/vcard/VCardEntryCounter.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * 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.vcard; - -import java.util.List; - -/** - * The class which just counts the number of vCard entries in the specified input. - */ -public class VCardEntryCounter implements VCardInterpreter { - private int mCount; - - public int getCount() { - return mCount; - } - - public void start() { - } - - public void end() { - } - - public void startEntry() { - } - - public void endEntry() { - mCount++; - } - - public void startProperty() { - } - - public void endProperty() { - } - - public void propertyGroup(String group) { - } - - public void propertyName(String name) { - } - - public void propertyParamType(String type) { - } - - public void propertyParamValue(String value) { - } - - public void propertyValues(List values) { - } -} diff --git a/vcard/java/com/android/vcard/VCardEntryHandler.java b/vcard/java/com/android/vcard/VCardEntryHandler.java deleted file mode 100644 index ef35a20a2..000000000 --- a/vcard/java/com/android/vcard/VCardEntryHandler.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * 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.vcard; - -/** - *

- * The interface called by {@link VCardEntryConstructor}. - *

- *

- * This class is useful when you don't want to know vCard data in detail. If you want to know - * it, it would be better to consider using {@link VCardInterpreter}. - *

- */ -public interface VCardEntryHandler { - /** - * Called when the parsing started. - */ - public void onStart(); - - /** - * The method called when one VCard entry is successfully created - */ - public void onEntryCreated(final VCardEntry entry); - - /** - * Called when the parsing ended. - * Able to be use this method for showing performance log, etc. - */ - public void onEnd(); -} diff --git a/vcard/java/com/android/vcard/VCardInterpreter.java b/vcard/java/com/android/vcard/VCardInterpreter.java deleted file mode 100644 index 2d987644f..000000000 --- a/vcard/java/com/android/vcard/VCardInterpreter.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * 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.vcard; - -import java.util.List; - -/** - *

- * The interface which should be implemented by the classes which have to analyze each - * vCard entry minutely. - *

- *

- * Here, there are several terms specific to vCard (and this library). - *

- *

- * The term "entry" is one vCard representation in the input, which should start with "BEGIN:VCARD" - * and end with "END:VCARD". - *

- *

- * The term "property" is one line in vCard entry, which consists of "group", "property name", - * "parameter(param) names and values", and "property values". - *

- *

- * e.g. group1.propName;paramName1=paramValue1;paramName2=paramValue2;propertyValue1;propertyValue2... - *

- */ -public interface VCardInterpreter { - /** - * Called when vCard interpretation started. - */ - void start(); - - /** - * Called when vCard interpretation finished. - */ - void end(); - - /** - * Called when parsing one vCard entry started. - * More specifically, this method is called when "BEGIN:VCARD" is read. - */ - void startEntry(); - - /** - * Called when parsing one vCard entry ended. - * More specifically, this method is called when "END:VCARD" is read. - * Note that {@link #startEntry()} may be called since - * vCard (especially 2.1) allows nested vCard. - */ - void endEntry(); - - /** - * Called when reading one property started. - */ - void startProperty(); - - /** - * Called when reading one property ended. - */ - void endProperty(); - - /** - * @param group A group name. This method may be called more than once or may not be - * called at all, depending on how many gruoups are appended to the property. - */ - void propertyGroup(String group); - - /** - * @param name A property name like "N", "FN", "ADR", etc. - */ - void propertyName(String name); - - /** - * @param type A parameter name like "ENCODING", "CHARSET", etc. - */ - void propertyParamType(String type); - - /** - * @param value A parameter value. This method may be called without - * {@link #propertyParamType(String)} being called (when the vCard is vCard 2.1). - */ - void propertyParamValue(String value); - - /** - * @param values List of property values. The size of values would be 1 unless - * coressponding property name is "N", "ADR", or "ORG". - */ - void propertyValues(List values); -} diff --git a/vcard/java/com/android/vcard/VCardInterpreterCollection.java b/vcard/java/com/android/vcard/VCardInterpreterCollection.java deleted file mode 100644 index 4a40d9312..000000000 --- a/vcard/java/com/android/vcard/VCardInterpreterCollection.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * 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.vcard; - -import java.util.Collection; -import java.util.List; - -/** - * The {@link VCardInterpreter} implementation which aggregates more than one - * {@link VCardInterpreter} objects and make a user object treat them as one - * {@link VCardInterpreter} object. - */ -public final class VCardInterpreterCollection implements VCardInterpreter { - private final Collection mInterpreterCollection; - - public VCardInterpreterCollection(Collection interpreterCollection) { - mInterpreterCollection = interpreterCollection; - } - - public Collection getCollection() { - return mInterpreterCollection; - } - - public void start() { - for (VCardInterpreter builder : mInterpreterCollection) { - builder.start(); - } - } - - public void end() { - for (VCardInterpreter builder : mInterpreterCollection) { - builder.end(); - } - } - - public void startEntry() { - for (VCardInterpreter builder : mInterpreterCollection) { - builder.startEntry(); - } - } - - public void endEntry() { - for (VCardInterpreter builder : mInterpreterCollection) { - builder.endEntry(); - } - } - - public void startProperty() { - for (VCardInterpreter builder : mInterpreterCollection) { - builder.startProperty(); - } - } - - public void endProperty() { - for (VCardInterpreter builder : mInterpreterCollection) { - builder.endProperty(); - } - } - - public void propertyGroup(String group) { - for (VCardInterpreter builder : mInterpreterCollection) { - builder.propertyGroup(group); - } - } - - public void propertyName(String name) { - for (VCardInterpreter builder : mInterpreterCollection) { - builder.propertyName(name); - } - } - - public void propertyParamType(String type) { - for (VCardInterpreter builder : mInterpreterCollection) { - builder.propertyParamType(type); - } - } - - public void propertyParamValue(String value) { - for (VCardInterpreter builder : mInterpreterCollection) { - builder.propertyParamValue(value); - } - } - - public void propertyValues(List values) { - for (VCardInterpreter builder : mInterpreterCollection) { - builder.propertyValues(values); - } - } -} diff --git a/vcard/java/com/android/vcard/VCardParser.java b/vcard/java/com/android/vcard/VCardParser.java deleted file mode 100644 index b7b8291dd..000000000 --- a/vcard/java/com/android/vcard/VCardParser.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * 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.vcard; - -import com.android.vcard.exception.VCardException; - -import java.io.IOException; -import java.io.InputStream; - -public interface VCardParser { - /** - *

- * Parses the given stream and send the vCard data into VCardBuilderBase object. - *

. - *

- * Note that vCard 2.1 specification allows "CHARSET" parameter, and some career sets - * local encoding to it. For example, Japanese phone career uses Shift_JIS, which is - * formally allowed in vCard 2.1, but not allowed in vCard 3.0. In vCard 2.1, - * In some exreme case, it is allowed for vCard to have different charsets in one vCard. - *

- *

- * We recommend you use {@link VCardSourceDetector} and detect which kind of source the - * vCard comes from and explicitly specify a charset using the result. - *

- * - * @param is The source to parse. - * @param interepreter A {@link VCardInterpreter} object which used to construct data. - * @throws IOException, VCardException - */ - public void parse(InputStream is, VCardInterpreter interepreter) - throws IOException, VCardException; - - /** - *

- * Cancel parsing vCard. Useful when you want to stop the parse in the other threads. - *

- *

- * Actual cancel is done after parsing the current vcard. - *

- */ - public abstract void cancel(); -} diff --git a/vcard/java/com/android/vcard/VCardParserImpl_V21.java b/vcard/java/com/android/vcard/VCardParserImpl_V21.java deleted file mode 100644 index b8343ae8e..000000000 --- a/vcard/java/com/android/vcard/VCardParserImpl_V21.java +++ /dev/null @@ -1,962 +0,0 @@ -/* - * Copyright (C) 2010 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.vcard; - -import android.util.Log; - -import com.android.vcard.exception.VCardAgentNotSupportedException; -import com.android.vcard.exception.VCardException; -import com.android.vcard.exception.VCardInvalidCommentLineException; -import com.android.vcard.exception.VCardInvalidLineException; -import com.android.vcard.exception.VCardNestedException; -import com.android.vcard.exception.VCardVersionException; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.Reader; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.Set; - -/** - *

- * Basic implementation achieving vCard parsing. Based on vCard 2.1, - *

- * @hide - */ -/* package */ class VCardParserImpl_V21 { - private static final String LOG_TAG = "VCardParserImpl_V21"; - - private static final class CustomBufferedReader extends BufferedReader { - private long mTime; - - public CustomBufferedReader(Reader in) { - super(in); - } - - @Override - public String readLine() throws IOException { - long start = System.currentTimeMillis(); - String ret = super.readLine(); - long end = System.currentTimeMillis(); - mTime += end - start; - return ret; - } - - public long getTotalmillisecond() { - return mTime; - } - } - - private static final String DEFAULT_ENCODING = "8BIT"; - - protected boolean mCanceled; - protected VCardInterpreter mInterpreter; - - protected final String mIntermediateCharset; - - /** - *

- * The encoding type for deconding byte streams. This member variable is - * reset to a default encoding every time when a new item comes. - *

- *

- * "Encoding" in vCard is different from "Charset". It is mainly used for - * addresses, notes, images. "7BIT", "8BIT", "BASE64", and - * "QUOTED-PRINTABLE" are known examples. - *

- */ - protected String mCurrentEncoding; - - /** - *

- * The reader object to be used internally. - *

- *

- * Developers should not directly read a line from this object. Use - * getLine() unless there some reason. - *

- */ - protected BufferedReader mReader; - - /** - *

- * Set for storing unkonwn TYPE attributes, which is not acceptable in vCard - * specification, but happens to be seen in real world vCard. - *

- */ - protected final Set mUnknownTypeSet = new HashSet(); - - /** - *

- * Set for storing unkonwn VALUE attributes, which is not acceptable in - * vCard specification, but happens to be seen in real world vCard. - *

- */ - protected final Set mUnknownValueSet = new HashSet(); - - - // In some cases, vCard is nested. Currently, we only consider the most - // interior vCard data. - // See v21_foma_1.vcf in test directory for more information. - // TODO: Don't ignore by using count, but read all of information outside vCard. - private int mNestCount; - - // Used only for parsing END:VCARD. - private String mPreviousLine; - - // For measuring performance. - private long mTimeTotal; - private long mTimeReadStartRecord; - private long mTimeReadEndRecord; - private long mTimeStartProperty; - private long mTimeEndProperty; - private long mTimeParseItems; - private long mTimeParseLineAndHandleGroup; - private long mTimeParsePropertyValues; - private long mTimeParseAdrOrgN; - private long mTimeHandleMiscPropertyValue; - private long mTimeHandleQuotedPrintable; - private long mTimeHandleBase64; - - public VCardParserImpl_V21() { - this(VCardConfig.VCARD_TYPE_DEFAULT); - } - - public VCardParserImpl_V21(int vcardType) { - if ((vcardType & VCardConfig.FLAG_TORELATE_NEST) != 0) { - mNestCount = 1; - } - - mIntermediateCharset = VCardConfig.DEFAULT_INTERMEDIATE_CHARSET; - } - - /** - *

- * Parses the file at the given position. - *

- */ - //
vcard_file = [wsls] vcard [wsls]
- protected void parseVCardFile() throws IOException, VCardException { - boolean readingFirstFile = true; - while (true) { - if (mCanceled) { - break; - } - if (!parseOneVCard(readingFirstFile)) { - break; - } - readingFirstFile = false; - } - - if (mNestCount > 0) { - boolean useCache = true; - for (int i = 0; i < mNestCount; i++) { - readEndVCard(useCache, true); - useCache = false; - } - } - } - - /** - * @return true when a given property name is a valid property name. - */ - protected boolean isValidPropertyName(final String propertyName) { - if (!(getKnownPropertyNameSet().contains(propertyName.toUpperCase()) || - propertyName.startsWith("X-")) - && !mUnknownTypeSet.contains(propertyName)) { - mUnknownTypeSet.add(propertyName); - Log.w(LOG_TAG, "Property name unsupported by vCard 2.1: " + propertyName); - } - return true; - } - - /** - * @return String. It may be null, or its length may be 0 - * @throws IOException - */ - protected String getLine() throws IOException { - return mReader.readLine(); - } - - /** - * @return String with it's length > 0 - * @throws IOException - * @throws VCardException when the stream reached end of line - */ - protected String getNonEmptyLine() throws IOException, VCardException { - String line; - while (true) { - line = getLine(); - if (line == null) { - throw new VCardException("Reached end of buffer."); - } else if (line.trim().length() > 0) { - return line; - } - } - } - - /* - * vcard = "BEGIN" [ws] ":" [ws] "VCARD" [ws] 1*CRLF - * items *CRLF - * "END" [ws] ":" [ws] "VCARD" - */ - private boolean parseOneVCard(boolean firstRead) throws IOException, VCardException { - boolean allowGarbage = false; - if (firstRead) { - if (mNestCount > 0) { - for (int i = 0; i < mNestCount; i++) { - if (!readBeginVCard(allowGarbage)) { - return false; - } - allowGarbage = true; - } - } - } - - if (!readBeginVCard(allowGarbage)) { - return false; - } - long start; - if (mInterpreter != null) { - start = System.currentTimeMillis(); - mInterpreter.startEntry(); - mTimeReadStartRecord += System.currentTimeMillis() - start; - } - start = System.currentTimeMillis(); - parseItems(); - mTimeParseItems += System.currentTimeMillis() - start; - readEndVCard(true, false); - if (mInterpreter != null) { - start = System.currentTimeMillis(); - mInterpreter.endEntry(); - mTimeReadEndRecord += System.currentTimeMillis() - start; - } - return true; - } - - /** - * @return True when successful. False when reaching the end of line - * @throws IOException - * @throws VCardException - */ - protected boolean readBeginVCard(boolean allowGarbage) throws IOException, VCardException { - String line; - do { - while (true) { - line = getLine(); - if (line == null) { - return false; - } else if (line.trim().length() > 0) { - break; - } - } - String[] strArray = line.split(":", 2); - int length = strArray.length; - - // Though vCard 2.1/3.0 specification does not allow lower cases, - // vCard file emitted by some external vCard expoter have such - // invalid Strings. - // So we allow it. - // e.g. BEGIN:vCard - if (length == 2 && strArray[0].trim().equalsIgnoreCase("BEGIN") - && strArray[1].trim().equalsIgnoreCase("VCARD")) { - return true; - } else if (!allowGarbage) { - if (mNestCount > 0) { - mPreviousLine = line; - return false; - } else { - throw new VCardException("Expected String \"BEGIN:VCARD\" did not come " - + "(Instead, \"" + line + "\" came)"); - } - } - } while (allowGarbage); - - throw new VCardException("Reached where must not be reached."); - } - - /** - *

- * The arguments useCache and allowGarbase are usually true and false - * accordingly when this function is called outside this function itself. - *

- * - * @param useCache When true, line is obtained from mPreviousline. - * Otherwise, getLine() is used. - * @param allowGarbage When true, ignore non "END:VCARD" line. - * @throws IOException - * @throws VCardException - */ - protected void readEndVCard(boolean useCache, boolean allowGarbage) throws IOException, - VCardException { - String line; - do { - if (useCache) { - // Though vCard specification does not allow lower cases, - // some data may have them, so we allow it. - line = mPreviousLine; - } else { - while (true) { - line = getLine(); - if (line == null) { - throw new VCardException("Expected END:VCARD was not found."); - } else if (line.trim().length() > 0) { - break; - } - } - } - - String[] strArray = line.split(":", 2); - if (strArray.length == 2 && strArray[0].trim().equalsIgnoreCase("END") - && strArray[1].trim().equalsIgnoreCase("VCARD")) { - return; - } else if (!allowGarbage) { - throw new VCardException("END:VCARD != \"" + mPreviousLine + "\""); - } - useCache = false; - } while (allowGarbage); - } - - /* - * items = *CRLF item / item - */ - protected void parseItems() throws IOException, VCardException { - boolean ended = false; - - if (mInterpreter != null) { - long start = System.currentTimeMillis(); - mInterpreter.startProperty(); - mTimeStartProperty += System.currentTimeMillis() - start; - } - ended = parseItem(); - if (mInterpreter != null && !ended) { - long start = System.currentTimeMillis(); - mInterpreter.endProperty(); - mTimeEndProperty += System.currentTimeMillis() - start; - } - - while (!ended) { - // follow VCARD ,it wont reach endProperty - if (mInterpreter != null) { - long start = System.currentTimeMillis(); - mInterpreter.startProperty(); - mTimeStartProperty += System.currentTimeMillis() - start; - } - try { - ended = parseItem(); - } catch (VCardInvalidCommentLineException e) { - Log.e(LOG_TAG, "Invalid line which looks like some comment was found. Ignored."); - ended = false; - } - if (mInterpreter != null && !ended) { - long start = System.currentTimeMillis(); - mInterpreter.endProperty(); - mTimeEndProperty += System.currentTimeMillis() - start; - } - } - } - - /* - * item = [groups "."] name [params] ":" value CRLF / [groups "."] "ADR" - * [params] ":" addressparts CRLF / [groups "."] "ORG" [params] ":" orgparts - * CRLF / [groups "."] "N" [params] ":" nameparts CRLF / [groups "."] - * "AGENT" [params] ":" vcard CRLF - */ - protected boolean parseItem() throws IOException, VCardException { - mCurrentEncoding = DEFAULT_ENCODING; - - final String line = getNonEmptyLine(); - long start = System.currentTimeMillis(); - - String[] propertyNameAndValue = separateLineAndHandleGroup(line); - if (propertyNameAndValue == null) { - return true; - } - if (propertyNameAndValue.length != 2) { - throw new VCardInvalidLineException("Invalid line \"" + line + "\""); - } - String propertyName = propertyNameAndValue[0].toUpperCase(); - String propertyValue = propertyNameAndValue[1]; - - mTimeParseLineAndHandleGroup += System.currentTimeMillis() - start; - - if (propertyName.equals("ADR") || propertyName.equals("ORG") || propertyName.equals("N")) { - start = System.currentTimeMillis(); - handleMultiplePropertyValue(propertyName, propertyValue); - mTimeParseAdrOrgN += System.currentTimeMillis() - start; - return false; - } else if (propertyName.equals("AGENT")) { - handleAgent(propertyValue); - return false; - } else if (isValidPropertyName(propertyName)) { - if (propertyName.equals("BEGIN")) { - if (propertyValue.equals("VCARD")) { - throw new VCardNestedException("This vCard has nested vCard data in it."); - } else { - throw new VCardException("Unknown BEGIN type: " + propertyValue); - } - } else if (propertyName.equals("VERSION") && !propertyValue.equals(getVersionString())) { - throw new VCardVersionException("Incompatible version: " + propertyValue + " != " - + getVersionString()); - } - start = System.currentTimeMillis(); - handlePropertyValue(propertyName, propertyValue); - mTimeParsePropertyValues += System.currentTimeMillis() - start; - return false; - } - - throw new VCardException("Unknown property name: \"" + propertyName + "\""); - } - - // For performance reason, the states for group and property name are merged into one. - static private final int STATE_GROUP_OR_PROPERTY_NAME = 0; - static private final int STATE_PARAMS = 1; - // vCard 3.0 specification allows double-quoted parameters, while vCard 2.1 does not. - static private final int STATE_PARAMS_IN_DQUOTE = 2; - - protected String[] separateLineAndHandleGroup(String line) throws VCardException { - final String[] propertyNameAndValue = new String[2]; - final int length = line.length(); - if (length > 0 && line.charAt(0) == '#') { - throw new VCardInvalidCommentLineException(); - } - - int state = STATE_GROUP_OR_PROPERTY_NAME; - int nameIndex = 0; - - // This loop is developed so that we don't have to take care of bottle neck here. - // Refactor carefully when you need to do so. - for (int i = 0; i < length; i++) { - final char ch = line.charAt(i); - switch (state) { - case STATE_GROUP_OR_PROPERTY_NAME: { - if (ch == ':') { // End of a property name. - final String propertyName = line.substring(nameIndex, i); - if (propertyName.equalsIgnoreCase("END")) { - mPreviousLine = line; - return null; - } - if (mInterpreter != null) { - mInterpreter.propertyName(propertyName); - } - propertyNameAndValue[0] = propertyName; - if (i < length - 1) { - propertyNameAndValue[1] = line.substring(i + 1); - } else { - propertyNameAndValue[1] = ""; - } - return propertyNameAndValue; - } else if (ch == '.') { // Each group is followed by the dot. - final String groupName = line.substring(nameIndex, i); - if (groupName.length() == 0) { - Log.w(LOG_TAG, "Empty group found. Ignoring."); - } else if (mInterpreter != null) { - mInterpreter.propertyGroup(groupName); - } - nameIndex = i + 1; // Next should be another group or a property name. - } else if (ch == ';') { // End of property name and beginneng of parameters. - final String propertyName = line.substring(nameIndex, i); - if (propertyName.equalsIgnoreCase("END")) { - mPreviousLine = line; - return null; - } - if (mInterpreter != null) { - mInterpreter.propertyName(propertyName); - } - propertyNameAndValue[0] = propertyName; - nameIndex = i + 1; - state = STATE_PARAMS; // Start parameter parsing. - } - break; - } - case STATE_PARAMS: { - if (ch == '"') { - if (VCardConstants.VERSION_V21.equalsIgnoreCase(getVersionString())) { - Log.w(LOG_TAG, "Double-quoted params found in vCard 2.1. " + - "Silently allow it"); - } - state = STATE_PARAMS_IN_DQUOTE; - } else if (ch == ';') { // Starts another param. - handleParams(line.substring(nameIndex, i)); - nameIndex = i + 1; - } else if (ch == ':') { // End of param and beginenning of values. - handleParams(line.substring(nameIndex, i)); - if (i < length - 1) { - propertyNameAndValue[1] = line.substring(i + 1); - } else { - propertyNameAndValue[1] = ""; - } - return propertyNameAndValue; - } - break; - } - case STATE_PARAMS_IN_DQUOTE: { - if (ch == '"') { - if (VCardConstants.VERSION_V21.equalsIgnoreCase(getVersionString())) { - Log.w(LOG_TAG, "Double-quoted params found in vCard 2.1. " + - "Silently allow it"); - } - state = STATE_PARAMS; - } - break; - } - } - } - - throw new VCardInvalidLineException("Invalid line: \"" + line + "\""); - } - - /* - * params = ";" [ws] paramlist paramlist = paramlist [ws] ";" [ws] param / - * param param = "TYPE" [ws] "=" [ws] ptypeval / "VALUE" [ws] "=" [ws] - * pvalueval / "ENCODING" [ws] "=" [ws] pencodingval / "CHARSET" [ws] "=" - * [ws] charsetval / "LANGUAGE" [ws] "=" [ws] langval / "X-" word [ws] "=" - * [ws] word / knowntype - */ - protected void handleParams(String params) throws VCardException { - final String[] strArray = params.split("=", 2); - if (strArray.length == 2) { - final String paramName = strArray[0].trim().toUpperCase(); - String paramValue = strArray[1].trim(); - if (paramName.equals("TYPE")) { - handleType(paramValue); - } else if (paramName.equals("VALUE")) { - handleValue(paramValue); - } else if (paramName.equals("ENCODING")) { - handleEncoding(paramValue); - } else if (paramName.equals("CHARSET")) { - handleCharset(paramValue); - } else if (paramName.equals("LANGUAGE")) { - handleLanguage(paramValue); - } else if (paramName.startsWith("X-")) { - handleAnyParam(paramName, paramValue); - } else { - throw new VCardException("Unknown type \"" + paramName + "\""); - } - } else { - handleParamWithoutName(strArray[0]); - } - } - - /** - * vCard 3.0 parser implementation may throw VCardException. - */ - @SuppressWarnings("unused") - protected void handleParamWithoutName(final String paramValue) throws VCardException { - handleType(paramValue); - } - - /* - * ptypeval = knowntype / "X-" word - */ - protected void handleType(final String ptypeval) { - if (!(getKnownTypeSet().contains(ptypeval.toUpperCase()) - || ptypeval.startsWith("X-")) - && !mUnknownTypeSet.contains(ptypeval)) { - mUnknownTypeSet.add(ptypeval); - Log.w(LOG_TAG, String.format("TYPE unsupported by %s: ", getVersion(), ptypeval)); - } - if (mInterpreter != null) { - mInterpreter.propertyParamType("TYPE"); - mInterpreter.propertyParamValue(ptypeval); - } - } - - /* - * pvalueval = "INLINE" / "URL" / "CONTENT-ID" / "CID" / "X-" word - */ - protected void handleValue(final String pvalueval) { - if (!(getKnownValueSet().contains(pvalueval.toUpperCase()) - || pvalueval.startsWith("X-") - || mUnknownValueSet.contains(pvalueval))) { - mUnknownValueSet.add(pvalueval); - Log.w(LOG_TAG, String.format( - "The value unsupported by TYPE of %s: ", getVersion(), pvalueval)); - } - if (mInterpreter != null) { - mInterpreter.propertyParamType("VALUE"); - mInterpreter.propertyParamValue(pvalueval); - } - } - - /* - * pencodingval = "7BIT" / "8BIT" / "QUOTED-PRINTABLE" / "BASE64" / "X-" word - */ - protected void handleEncoding(String pencodingval) throws VCardException { - if (getAvailableEncodingSet().contains(pencodingval) || - pencodingval.startsWith("X-")) { - if (mInterpreter != null) { - mInterpreter.propertyParamType("ENCODING"); - mInterpreter.propertyParamValue(pencodingval); - } - mCurrentEncoding = pencodingval; - } else { - throw new VCardException("Unknown encoding \"" + pencodingval + "\""); - } - } - - /** - *

- * vCard 2.1 specification only allows us-ascii and iso-8859-xxx (See RFC 1521), - * but recent vCard files often contain other charset like UTF-8, SHIFT_JIS, etc. - * We allow any charset. - *

- */ - protected void handleCharset(String charsetval) { - if (mInterpreter != null) { - mInterpreter.propertyParamType("CHARSET"); - mInterpreter.propertyParamValue(charsetval); - } - } - - /** - * See also Section 7.1 of RFC 1521 - */ - protected void handleLanguage(String langval) throws VCardException { - String[] strArray = langval.split("-"); - if (strArray.length != 2) { - throw new VCardException("Invalid Language: \"" + langval + "\""); - } - String tmp = strArray[0]; - int length = tmp.length(); - for (int i = 0; i < length; i++) { - if (!isAsciiLetter(tmp.charAt(i))) { - throw new VCardException("Invalid Language: \"" + langval + "\""); - } - } - tmp = strArray[1]; - length = tmp.length(); - for (int i = 0; i < length; i++) { - if (!isAsciiLetter(tmp.charAt(i))) { - throw new VCardException("Invalid Language: \"" + langval + "\""); - } - } - if (mInterpreter != null) { - mInterpreter.propertyParamType("LANGUAGE"); - mInterpreter.propertyParamValue(langval); - } - } - - private boolean isAsciiLetter(char ch) { - if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) { - return true; - } - return false; - } - - /** - * Mainly for "X-" type. This accepts any kind of type without check. - */ - protected void handleAnyParam(String paramName, String paramValue) { - if (mInterpreter != null) { - mInterpreter.propertyParamType(paramName); - mInterpreter.propertyParamValue(paramValue); - } - } - - protected void handlePropertyValue(String propertyName, String propertyValue) - throws IOException, VCardException { - final String upperEncoding = mCurrentEncoding.toUpperCase(); - if (upperEncoding.equals(VCardConstants.PARAM_ENCODING_QP)) { - final long start = System.currentTimeMillis(); - final String result = getQuotedPrintable(propertyValue); - if (mInterpreter != null) { - ArrayList v = new ArrayList(); - v.add(result); - mInterpreter.propertyValues(v); - } - mTimeHandleQuotedPrintable += System.currentTimeMillis() - start; - } else if (upperEncoding.equals(VCardConstants.PARAM_ENCODING_BASE64) - || upperEncoding.equals(VCardConstants.PARAM_ENCODING_B)) { - final long start = System.currentTimeMillis(); - // It is very rare, but some BASE64 data may be so big that - // OutOfMemoryError occurs. To ignore such cases, use try-catch. - try { - final String result = getBase64(propertyValue); - if (mInterpreter != null) { - ArrayList arrayList = new ArrayList(); - arrayList.add(result); - mInterpreter.propertyValues(arrayList); - } - } catch (OutOfMemoryError error) { - Log.e(LOG_TAG, "OutOfMemoryError happened during parsing BASE64 data!"); - if (mInterpreter != null) { - mInterpreter.propertyValues(null); - } - } - mTimeHandleBase64 += System.currentTimeMillis() - start; - } else { - if (!(upperEncoding.equals("7BIT") || upperEncoding.equals("8BIT") || - upperEncoding.startsWith("X-"))) { - Log.w(LOG_TAG, - String.format("The encoding \"%s\" is unsupported by vCard %s", - mCurrentEncoding, getVersionString())); - } - - final long start = System.currentTimeMillis(); - if (mInterpreter != null) { - ArrayList v = new ArrayList(); - v.add(maybeUnescapeText(propertyValue)); - mInterpreter.propertyValues(v); - } - mTimeHandleMiscPropertyValue += System.currentTimeMillis() - start; - } - } - - /** - *

- * Parses and returns Quoted-Printable. - *

- * - * @param firstString The string following a parameter name and attributes. - * Example: "string" in - * "ADR:ENCODING=QUOTED-PRINTABLE:string\n\r". - * @return whole Quoted-Printable string, including a given argument and - * following lines. Excludes the last empty line following to Quoted - * Printable lines. - * @throws IOException - * @throws VCardException - */ - private String getQuotedPrintable(String firstString) throws IOException, VCardException { - // Specifically, there may be some padding between = and CRLF. - // See the following: - // - // qp-line := *(qp-segment transport-padding CRLF) - // qp-part transport-padding - // qp-segment := qp-section *(SPACE / TAB) "=" - // ; Maximum length of 76 characters - // - // e.g. (from RFC 2045) - // Now's the time = - // for all folk to come= - // to the aid of their country. - if (firstString.trim().endsWith("=")) { - // remove "transport-padding" - int pos = firstString.length() - 1; - while (firstString.charAt(pos) != '=') { - } - StringBuilder builder = new StringBuilder(); - builder.append(firstString.substring(0, pos + 1)); - builder.append("\r\n"); - String line; - while (true) { - line = getLine(); - if (line == null) { - throw new VCardException("File ended during parsing a Quoted-Printable String"); - } - if (line.trim().endsWith("=")) { - // remove "transport-padding" - pos = line.length() - 1; - while (line.charAt(pos) != '=') { - } - builder.append(line.substring(0, pos + 1)); - builder.append("\r\n"); - } else { - builder.append(line); - break; - } - } - return builder.toString(); - } else { - return firstString; - } - } - - protected String getBase64(String firstString) throws IOException, VCardException { - StringBuilder builder = new StringBuilder(); - builder.append(firstString); - - while (true) { - String line = getLine(); - if (line == null) { - throw new VCardException("File ended during parsing BASE64 binary"); - } - if (line.length() == 0) { - break; - } - builder.append(line); - } - - return builder.toString(); - } - - /** - *

- * Mainly for "ADR", "ORG", and "N" - *

- */ - /* - * addressparts = 0*6(strnosemi ";") strnosemi ; PO Box, Extended Addr, - * Street, Locality, Region, Postal Code, Country Name orgparts = - * *(strnosemi ";") strnosemi ; First is Organization Name, remainder are - * Organization Units. nameparts = 0*4(strnosemi ";") strnosemi ; Family, - * Given, Middle, Prefix, Suffix. ; Example:Public;John;Q.;Reverend Dr.;III, - * Esq. strnosemi = *(*nonsemi ("\;" / "\" CRLF)) *nonsemi ; To include a - * semicolon in this string, it must be escaped ; with a "\" character. We - * do not care the number of "strnosemi" here. We are not sure whether we - * should add "\" CRLF to each value. We exclude them for now. - */ - protected void handleMultiplePropertyValue(String propertyName, String propertyValue) - throws IOException, VCardException { - // vCard 2.1 does not allow QUOTED-PRINTABLE here, but some - // softwares/devices - // emit such data. - if (mCurrentEncoding.equalsIgnoreCase("QUOTED-PRINTABLE")) { - propertyValue = getQuotedPrintable(propertyValue); - } - - if (mInterpreter != null) { - mInterpreter.propertyValues(VCardUtils.constructListFromValue(propertyValue, - (getVersion() == VCardConfig.FLAG_V30))); - } - } - - /* - * vCard 2.1 specifies AGENT allows one vcard entry. Currently we emit an - * error toward the AGENT property. - * // TODO: Support AGENT property. - * item = - * ... / [groups "."] "AGENT" [params] ":" vcard CRLF vcard = "BEGIN" [ws] - * ":" [ws] "VCARD" [ws] 1*CRLF items *CRLF "END" [ws] ":" [ws] "VCARD" - */ - protected void handleAgent(final String propertyValue) throws VCardException { - if (!propertyValue.toUpperCase().contains("BEGIN:VCARD")) { - // Apparently invalid line seen in Windows Mobile 6.5. Ignore them. - return; - } else { - throw new VCardAgentNotSupportedException("AGENT Property is not supported now."); - } - } - - /** - * For vCard 3.0. - */ - protected String maybeUnescapeText(final String text) { - return text; - } - - /** - * Returns unescaped String if the character should be unescaped. Return - * null otherwise. e.g. In vCard 2.1, "\;" should be unescaped into ";" - * while "\x" should not be. - */ - protected String maybeUnescapeCharacter(final char ch) { - return unescapeCharacter(ch); - } - - /* package */ static String unescapeCharacter(final char ch) { - // Original vCard 2.1 specification does not allow transformation - // "\:" -> ":", "\," -> ",", and "\\" -> "\", but previous - // implementation of - // this class allowed them, so keep it as is. - if (ch == '\\' || ch == ';' || ch == ':' || ch == ',') { - return String.valueOf(ch); - } else { - return null; - } - } - - private void showPerformanceInfo() { - Log.d(LOG_TAG, "Total parsing time: " + mTimeTotal + " ms"); - if (mReader instanceof CustomBufferedReader) { - Log.d(LOG_TAG, "Total readLine time: " - + ((CustomBufferedReader) mReader).getTotalmillisecond() + " ms"); - } - Log.d(LOG_TAG, "Time for handling the beggining of the record: " + mTimeReadStartRecord - + " ms"); - Log.d(LOG_TAG, "Time for handling the end of the record: " + mTimeReadEndRecord + " ms"); - Log.d(LOG_TAG, "Time for parsing line, and handling group: " + mTimeParseLineAndHandleGroup - + " ms"); - Log.d(LOG_TAG, "Time for parsing ADR, ORG, and N fields:" + mTimeParseAdrOrgN + " ms"); - Log.d(LOG_TAG, "Time for parsing property values: " + mTimeParsePropertyValues + " ms"); - Log.d(LOG_TAG, "Time for handling normal property values: " + mTimeHandleMiscPropertyValue - + " ms"); - Log.d(LOG_TAG, "Time for handling Quoted-Printable: " + mTimeHandleQuotedPrintable + " ms"); - Log.d(LOG_TAG, "Time for handling Base64: " + mTimeHandleBase64 + " ms"); - } - - /** - * @return {@link VCardConfig#FLAG_V21} - */ - protected int getVersion() { - return VCardConfig.FLAG_V21; - } - - /** - * @return {@link VCardConfig#FLAG_V30} - */ - protected String getVersionString() { - return VCardConstants.VERSION_V21; - } - - protected Set getKnownPropertyNameSet() { - return VCardParser_V21.sKnownPropertyNameSet; - } - - protected Set getKnownTypeSet() { - return VCardParser_V21.sKnownTypeSet; - } - - protected Set getKnownValueSet() { - return VCardParser_V21.sKnownValueSet; - } - - protected Set getAvailableEncodingSet() { - return VCardParser_V21.sAvailableEncoding; - } - - protected String getDefaultEncoding() { - return DEFAULT_ENCODING; - } - - - public void parse(InputStream is, VCardInterpreter interpreter) - throws IOException, VCardException { - if (is == null) { - throw new NullPointerException("InputStream must not be null."); - } - - final InputStreamReader tmpReader = new InputStreamReader(is, mIntermediateCharset); - if (VCardConfig.showPerformanceLog()) { - mReader = new CustomBufferedReader(tmpReader); - } else { - mReader = new BufferedReader(tmpReader); - } - - mInterpreter = interpreter; - - final long start = System.currentTimeMillis(); - if (mInterpreter != null) { - mInterpreter.start(); - } - parseVCardFile(); - if (mInterpreter != null) { - mInterpreter.end(); - } - mTimeTotal += System.currentTimeMillis() - start; - - if (VCardConfig.showPerformanceLog()) { - showPerformanceInfo(); - } - } - - public final void cancel() { - mCanceled = true; - } -} diff --git a/vcard/java/com/android/vcard/VCardParserImpl_V30.java b/vcard/java/com/android/vcard/VCardParserImpl_V30.java deleted file mode 100644 index def149563..000000000 --- a/vcard/java/com/android/vcard/VCardParserImpl_V30.java +++ /dev/null @@ -1,313 +0,0 @@ -/* - * Copyright (C) 2010 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.vcard; - -import android.util.Log; - -import com.android.vcard.exception.VCardException; - -import java.io.IOException; -import java.util.Set; - -/** - *

- * Basic implementation achieving vCard 3.0 parsing. - *

- *

- * This class inherits vCard 2.1 implementation since technically they are similar, - * while specifically there's logical no relevance between them. - * So that developers are not confused with the inheritance, - * {@link VCardParser_V30} does not inherit {@link VCardParser_V21}, while - * {@link VCardParserImpl_V30} inherits {@link VCardParserImpl_V21}. - *

- * @hide - */ -/* package */ class VCardParserImpl_V30 extends VCardParserImpl_V21 { - private static final String LOG_TAG = "VCardParserImpl_V30"; - - private String mPreviousLine; - private boolean mEmittedAgentWarning = false; - - public VCardParserImpl_V30() { - super(); - } - - public VCardParserImpl_V30(int vcardType) { - super(vcardType); - } - - @Override - protected int getVersion() { - return VCardConfig.FLAG_V30; - } - - @Override - protected String getVersionString() { - return VCardConstants.VERSION_V30; - } - - @Override - protected String getLine() throws IOException { - if (mPreviousLine != null) { - String ret = mPreviousLine; - mPreviousLine = null; - return ret; - } else { - return mReader.readLine(); - } - } - - /** - * vCard 3.0 requires that the line with space at the beginning of the line - * must be combined with previous line. - */ - @Override - protected String getNonEmptyLine() throws IOException, VCardException { - String line; - StringBuilder builder = null; - while (true) { - line = mReader.readLine(); - if (line == null) { - if (builder != null) { - return builder.toString(); - } else if (mPreviousLine != null) { - String ret = mPreviousLine; - mPreviousLine = null; - return ret; - } - throw new VCardException("Reached end of buffer."); - } else if (line.length() == 0) { - if (builder != null) { - return builder.toString(); - } else if (mPreviousLine != null) { - String ret = mPreviousLine; - mPreviousLine = null; - return ret; - } - } else if (line.charAt(0) == ' ' || line.charAt(0) == '\t') { - if (builder != null) { - // See Section 5.8.1 of RFC 2425 (MIME-DIR document). - // Following is the excerpts from it. - // - // DESCRIPTION:This is a long description that exists on a long line. - // - // Can be represented as: - // - // DESCRIPTION:This is a long description - // that exists on a long line. - // - // It could also be represented as: - // - // DESCRIPTION:This is a long descrip - // tion that exists o - // n a long line. - builder.append(line.substring(1)); - } else if (mPreviousLine != null) { - builder = new StringBuilder(); - builder.append(mPreviousLine); - mPreviousLine = null; - builder.append(line.substring(1)); - } else { - throw new VCardException("Space exists at the beginning of the line"); - } - } else { - if (mPreviousLine == null) { - mPreviousLine = line; - if (builder != null) { - return builder.toString(); - } - } else { - String ret = mPreviousLine; - mPreviousLine = line; - return ret; - } - } - } - } - - /* - * vcard = [group "."] "BEGIN" ":" "VCARD" 1 * CRLF - * 1 * (contentline) - * ;A vCard object MUST include the VERSION, FN and N types. - * [group "."] "END" ":" "VCARD" 1 * CRLF - */ - @Override - protected boolean readBeginVCard(boolean allowGarbage) throws IOException, VCardException { - // TODO: vCard 3.0 supports group. - return super.readBeginVCard(allowGarbage); - } - - @Override - protected void readEndVCard(boolean useCache, boolean allowGarbage) - throws IOException, VCardException { - // TODO: vCard 3.0 supports group. - super.readEndVCard(useCache, allowGarbage); - } - - /** - * vCard 3.0 allows iana-token as paramType, while vCard 2.1 does not. - */ - @Override - protected void handleParams(final String params) throws VCardException { - try { - super.handleParams(params); - } catch (VCardException e) { - // maybe IANA type - String[] strArray = params.split("=", 2); - if (strArray.length == 2) { - handleAnyParam(strArray[0], strArray[1]); - } else { - // Must not come here in the current implementation. - throw new VCardException( - "Unknown params value: " + params); - } - } - } - - @Override - protected void handleAnyParam(final String paramName, final String paramValue) { - super.handleAnyParam(paramName, paramValue); - } - - @Override - protected void handleParamWithoutName(final String paramValue) throws VCardException { - super.handleParamWithoutName(paramValue); - } - - /* - * vCard 3.0 defines - * - * param = param-name "=" param-value *("," param-value) - * param-name = iana-token / x-name - * param-value = ptext / quoted-string - * quoted-string = DQUOTE QSAFE-CHAR DQUOTE - */ - @Override - protected void handleType(final String ptypevalues) { - String[] ptypeArray = ptypevalues.split(","); - mInterpreter.propertyParamType("TYPE"); - for (String value : ptypeArray) { - int length = value.length(); - if (length >= 2 && value.startsWith("\"") && value.endsWith("\"")) { - mInterpreter.propertyParamValue(value.substring(1, value.length() - 1)); - } else { - mInterpreter.propertyParamValue(value); - } - } - } - - @Override - protected void handleAgent(final String propertyValue) { - // The way how vCard 3.0 supports "AGENT" is completely different from vCard 2.1. - // - // e.g. - // AGENT:BEGIN:VCARD\nFN:Joe Friday\nTEL:+1-919-555-7878\n - // TITLE:Area Administrator\, Assistant\n EMAIL\;TYPE=INTERN\n - // ET:jfriday@host.com\nEND:VCARD\n - // - // TODO: fix this. - // - // issue: - // vCard 3.0 also allows this as an example. - // - // AGENT;VALUE=uri: - // CID:JQPUBLIC.part3.960129T083020.xyzMail@host3.com - // - // This is not vCard. Should we support this? - // - // Just ignore the line for now, since we cannot know how to handle it... - if (!mEmittedAgentWarning) { - Log.w(LOG_TAG, "AGENT in vCard 3.0 is not supported yet. Ignore it"); - mEmittedAgentWarning = true; - } - } - - /** - * vCard 3.0 does not require two CRLF at the last of BASE64 data. - * It only requires that data should be MIME-encoded. - */ - @Override - protected String getBase64(final String firstString) - throws IOException, VCardException { - final StringBuilder builder = new StringBuilder(); - builder.append(firstString); - - while (true) { - final String line = getLine(); - if (line == null) { - throw new VCardException("File ended during parsing BASE64 binary"); - } - if (line.length() == 0) { - break; - } else if (!line.startsWith(" ") && !line.startsWith("\t")) { - mPreviousLine = line; - break; - } - builder.append(line); - } - - return builder.toString(); - } - - /** - * ESCAPED-CHAR = "\\" / "\;" / "\," / "\n" / "\N") - * ; \\ encodes \, \n or \N encodes newline - * ; \; encodes ;, \, encodes , - * - * Note: Apple escapes ':' into '\:' while does not escape '\' - */ - @Override - protected String maybeUnescapeText(final String text) { - return unescapeText(text); - } - - public static String unescapeText(final String text) { - StringBuilder builder = new StringBuilder(); - final int length = text.length(); - for (int i = 0; i < length; i++) { - char ch = text.charAt(i); - if (ch == '\\' && i < length - 1) { - final char next_ch = text.charAt(++i); - if (next_ch == 'n' || next_ch == 'N') { - builder.append("\n"); - } else { - builder.append(next_ch); - } - } else { - builder.append(ch); - } - } - return builder.toString(); - } - - @Override - protected String maybeUnescapeCharacter(final char ch) { - return unescapeCharacter(ch); - } - - public static String unescapeCharacter(final char ch) { - if (ch == 'n' || ch == 'N') { - return "\n"; - } else { - return String.valueOf(ch); - } - } - - @Override - protected Set getKnownPropertyNameSet() { - return VCardParser_V30.sKnownPropertyNameSet; - } -} diff --git a/vcard/java/com/android/vcard/VCardParser_V21.java b/vcard/java/com/android/vcard/VCardParser_V21.java deleted file mode 100644 index 7aa7a822e..000000000 --- a/vcard/java/com/android/vcard/VCardParser_V21.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * 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.vcard; - -import com.android.vcard.exception.VCardException; - -import java.io.IOException; -import java.io.InputStream; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; - -/** - *

- * vCard parser for vCard 2.1. See the specification for more detail about the spec itself. - *

- *

- * The spec is written in 1996, and currently various types of "vCard 2.1" exist. - * To handle real the world vCard formats appropriately and effectively, this class does not - * obey with strict vCard 2.1. - * In stead, not only vCard spec but also real world vCard is considered. - *

- * e.g. A lot of devices and softwares let vCard importer/exporter to use - * the PNG format to determine the type of image, while it is not allowed in - * the original specification. As of 2010, we can see even the FLV format - * (possible in Japanese mobile phones). - *

- */ -public final class VCardParser_V21 implements VCardParser { - /** - * A unmodifiable Set storing the property names available in the vCard 2.1 specification. - */ - /* package */ static final Set sKnownPropertyNameSet = - Collections.unmodifiableSet(new HashSet( - Arrays.asList("BEGIN", "LOGO", "PHOTO", "LABEL", "FN", "TITLE", "SOUND", - "VERSION", "TEL", "EMAIL", "TZ", "GEO", "NOTE", "URL", - "BDAY", "ROLE", "REV", "UID", "KEY", "MAILER"))); - - /** - * A unmodifiable Set storing the types known in vCard 2.1. - */ - /* package */ static final Set sKnownTypeSet = - Collections.unmodifiableSet(new HashSet( - Arrays.asList("DOM", "INTL", "POSTAL", "PARCEL", "HOME", "WORK", - "PREF", "VOICE", "FAX", "MSG", "CELL", "PAGER", "BBS", - "MODEM", "CAR", "ISDN", "VIDEO", "AOL", "APPLELINK", - "ATTMAIL", "CIS", "EWORLD", "INTERNET", "IBMMAIL", - "MCIMAIL", "POWERSHARE", "PRODIGY", "TLX", "X400", "GIF", - "CGM", "WMF", "BMP", "MET", "PMB", "DIB", "PICT", "TIFF", - "PDF", "PS", "JPEG", "QTIME", "MPEG", "MPEG2", "AVI", - "WAVE", "AIFF", "PCM", "X509", "PGP"))); - - /** - * A unmodifiable Set storing the values for the type "VALUE", available in the vCard 2.1. - */ - /* package */ static final Set sKnownValueSet = - Collections.unmodifiableSet(new HashSet( - Arrays.asList("INLINE", "URL", "CONTENT-ID", "CID"))); - - /** - *

- * A unmodifiable Set storing the values for the type "ENCODING", available in the vCard 2.1. - *

- *

- * Though vCard 2.1 specification does not allow "B" encoding, some data may have it. - * We allow it for safety. - *

- */ - /* package */ static final Set sAvailableEncoding = - Collections.unmodifiableSet(new HashSet( - Arrays.asList(VCardConstants.PARAM_ENCODING_7BIT, - VCardConstants.PARAM_ENCODING_8BIT, - VCardConstants.PARAM_ENCODING_QP, - VCardConstants.PARAM_ENCODING_BASE64, - VCardConstants.PARAM_ENCODING_B))); - - private final VCardParserImpl_V21 mVCardParserImpl; - - public VCardParser_V21() { - mVCardParserImpl = new VCardParserImpl_V21(); - } - - public VCardParser_V21(int vcardType) { - mVCardParserImpl = new VCardParserImpl_V21(vcardType); - } - - public void parse(InputStream is, VCardInterpreter interepreter) - throws IOException, VCardException { - mVCardParserImpl.parse(is, interepreter); - } - - public void cancel() { - mVCardParserImpl.cancel(); - } -} diff --git a/vcard/java/com/android/vcard/VCardParser_V30.java b/vcard/java/com/android/vcard/VCardParser_V30.java deleted file mode 100644 index 475534c42..000000000 --- a/vcard/java/com/android/vcard/VCardParser_V30.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * 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.vcard; - -import com.android.vcard.exception.VCardException; - -import java.io.IOException; -import java.io.InputStream; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; - -/** - *

- * vCard parser for vCard 3.0. See RFC 2426 for more detail. - *

- *

- * This parser allows vCard format which is not allowed in the RFC, since - * we have seen several vCard 3.0 files which don't comply with it. - *

- *

- * e.g. vCard 3.0 does not allow "CHARSET" attribute, but some actual files - * have it and they uses non UTF-8 charsets. UTF-8 is recommended in RFC 2426, - * but it is not a must. We silently allow "CHARSET". - *

- */ -public class VCardParser_V30 implements VCardParser { - /* package */ static final Set sKnownPropertyNameSet = - Collections.unmodifiableSet(new HashSet(Arrays.asList( - "BEGIN", "LOGO", "PHOTO", "LABEL", "FN", "TITLE", "SOUND", - "VERSION", "TEL", "EMAIL", "TZ", "GEO", "NOTE", "URL", - "BDAY", "ROLE", "REV", "UID", "KEY", "MAILER", // 2.1 - "NAME", "PROFILE", "SOURCE", "NICKNAME", "CLASS", - "SORT-STRING", "CATEGORIES", "PRODID"))); // 3.0 - - /** - *

- * A unmodifiable Set storing the values for the type "ENCODING", available in the vCard 3.0. - *

- *

- * Though vCard 2.1 specification does not allow "7BIT" or "BASE64", we allow them for safety. - *

- *

- * "QUOTED-PRINTABLE" is not allowed in vCard 3.0 and not in this parser either, - * because the encoding ambiguates how the vCard file to be parsed. - *

- */ - /* package */ static final Set sAcceptableEncoding = - Collections.unmodifiableSet(new HashSet(Arrays.asList( - VCardConstants.PARAM_ENCODING_7BIT, - VCardConstants.PARAM_ENCODING_8BIT, - VCardConstants.PARAM_ENCODING_BASE64, - VCardConstants.PARAM_ENCODING_B))); - - private final VCardParserImpl_V30 mVCardParserImpl; - - public VCardParser_V30() { - mVCardParserImpl = new VCardParserImpl_V30(); - } - - public VCardParser_V30(int vcardType) { - mVCardParserImpl = new VCardParserImpl_V30(vcardType); - } - - public void parse(InputStream is, VCardInterpreter interepreter) - throws IOException, VCardException { - mVCardParserImpl.parse(is, interepreter); - } - - public void cancel() { - mVCardParserImpl.cancel(); - } -} diff --git a/vcard/java/com/android/vcard/VCardSourceDetector.java b/vcard/java/com/android/vcard/VCardSourceDetector.java deleted file mode 100644 index e70d4961c..000000000 --- a/vcard/java/com/android/vcard/VCardSourceDetector.java +++ /dev/null @@ -1,172 +0,0 @@ -/* - * 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.vcard; - -import android.text.TextUtils; - -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -/** - *

- * The class which tries to detects the source of a vCard file from its contents. - *

- *

- * The specification of vCard (including both 2.1 and 3.0) is not so strict as to - * guess its format just by reading beginning few lines (usually we can, but in - * some most pessimistic case, we cannot until at almost the end of the file). - * Also we cannot store all vCard entries in memory, while there's no specification - * how big the vCard entry would become after the parse. - *

- *

- * This class is usually used for the "first scan", in which we can understand which vCard - * version is used (and how many entries exist in a file). - *

- */ -public class VCardSourceDetector implements VCardInterpreter { - private static Set APPLE_SIGNS = new HashSet(Arrays.asList( - "X-PHONETIC-FIRST-NAME", "X-PHONETIC-MIDDLE-NAME", "X-PHONETIC-LAST-NAME", - "X-ABADR", "X-ABUID")); - - private static Set JAPANESE_MOBILE_PHONE_SIGNS = new HashSet(Arrays.asList( - "X-GNO", "X-GN", "X-REDUCTION")); - - private static Set WINDOWS_MOBILE_PHONE_SIGNS = new HashSet(Arrays.asList( - "X-MICROSOFT-ASST_TEL", "X-MICROSOFT-ASSISTANT", "X-MICROSOFT-OFFICELOC")); - - // Note: these signes appears before the signs of the other type (e.g. "X-GN"). - // In other words, Japanese FOMA mobile phones are detected as FOMA, not JAPANESE_MOBILE_PHONES. - private static Set FOMA_SIGNS = new HashSet(Arrays.asList( - "X-SD-VERN", "X-SD-FORMAT_VER", "X-SD-CATEGORIES", "X-SD-CLASS", "X-SD-DCREATED", - "X-SD-DESCRIPTION")); - private static String TYPE_FOMA_CHARSET_SIGN = "X-SD-CHAR_CODE"; - - - // TODO: Should replace this with types in VCardConfig - private static final int PARSE_TYPE_UNKNOWN = 0; - // For Apple's software, which does not mean this type is effective for all its products. - // We confirmed they usually use UTF-8, but not sure about vCard type. - private static final int PARSE_TYPE_APPLE = 1; - // For Japanese mobile phones, which are usually using Shift_JIS as a charset. - private static final int PARSE_TYPE_MOBILE_PHONE_JP = 2; - // For some of mobile phones released from DoCoMo, which use nested vCard. - private static final int PARSE_TYPE_DOCOMO_TORELATE_NEST = 3; - // For Japanese Windows Mobel phones. It's version is supposed to be 6.5. - private static final int PARSE_TYPE_WINDOWS_MOBILE_V65_JP = 4; - - private int mParseType = 0; // Not sure. - - // Some mobile phones (like FOMA) tells us the charset of the data. - private boolean mNeedParseSpecifiedCharset; - private String mSpecifiedCharset; - - public void start() { - } - - public void end() { - } - - public void startEntry() { - } - - public void startProperty() { - mNeedParseSpecifiedCharset = false; - } - - public void endProperty() { - } - - public void endEntry() { - } - - public void propertyGroup(String group) { - } - - public void propertyName(String name) { - if (name.equalsIgnoreCase(TYPE_FOMA_CHARSET_SIGN)) { - mParseType = PARSE_TYPE_DOCOMO_TORELATE_NEST; - // Probably Shift_JIS is used, but we should double confirm. - mNeedParseSpecifiedCharset = true; - return; - } - if (mParseType != PARSE_TYPE_UNKNOWN) { - return; - } - if (WINDOWS_MOBILE_PHONE_SIGNS.contains(name)) { - mParseType = PARSE_TYPE_WINDOWS_MOBILE_V65_JP; - } else if (FOMA_SIGNS.contains(name)) { - mParseType = PARSE_TYPE_DOCOMO_TORELATE_NEST; - } else if (JAPANESE_MOBILE_PHONE_SIGNS.contains(name)) { - mParseType = PARSE_TYPE_MOBILE_PHONE_JP; - } else if (APPLE_SIGNS.contains(name)) { - mParseType = PARSE_TYPE_APPLE; - } - } - - public void propertyParamType(String type) { - } - - public void propertyParamValue(String value) { - } - - public void propertyValues(List values) { - if (mNeedParseSpecifiedCharset && values.size() > 0) { - mSpecifiedCharset = values.get(0); - } - } - - /** - * @return The available type can be used with vCard parser. You probably need to - * use {{@link #getEstimatedCharset()} to understand the charset to be used. - */ - public int getEstimatedType() { - switch (mParseType) { - case PARSE_TYPE_DOCOMO_TORELATE_NEST: - return VCardConfig.VCARD_TYPE_DOCOMO | VCardConfig.FLAG_TORELATE_NEST; - case PARSE_TYPE_MOBILE_PHONE_JP: - return VCardConfig.VCARD_TYPE_V21_JAPANESE_MOBILE; - case PARSE_TYPE_APPLE: - case PARSE_TYPE_WINDOWS_MOBILE_V65_JP: - default: - return VCardConfig.VCARD_TYPE_UNKNOWN; - } - } - - /** - *

- * Returns charset String guessed from the source's properties. - * This method must be called after parsing target file(s). - *

- * @return Charset String. Null is returned if guessing the source fails. - */ - public String getEstimatedCharset() { - if (TextUtils.isEmpty(mSpecifiedCharset)) { - return mSpecifiedCharset; - } - switch (mParseType) { - case PARSE_TYPE_WINDOWS_MOBILE_V65_JP: - case PARSE_TYPE_DOCOMO_TORELATE_NEST: - case PARSE_TYPE_MOBILE_PHONE_JP: - return "SHIFT_JIS"; - case PARSE_TYPE_APPLE: - return "UTF-8"; - default: - return null; - } - } -} diff --git a/vcard/java/com/android/vcard/VCardUtils.java b/vcard/java/com/android/vcard/VCardUtils.java deleted file mode 100644 index fb0c2e7b6..000000000 --- a/vcard/java/com/android/vcard/VCardUtils.java +++ /dev/null @@ -1,658 +0,0 @@ -/* - * 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.vcard; - -import org.apache.commons.codec.DecoderException; -import org.apache.commons.codec.net.QuotedPrintableCodec; - -import android.content.ContentProviderOperation; -import android.provider.ContactsContract.Data; -import android.provider.ContactsContract.CommonDataKinds.Im; -import android.provider.ContactsContract.CommonDataKinds.Phone; -import android.provider.ContactsContract.CommonDataKinds.StructuredPostal; -import android.telephony.PhoneNumberUtils; -import android.text.TextUtils; -import android.util.Log; - -import java.io.UnsupportedEncodingException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * Utilities for VCard handling codes. - */ -public class VCardUtils { - private static final String LOG_TAG = "VCardUtils"; - - // Note that not all types are included in this map/set, since, for example, TYPE_HOME_FAX is - // converted to two parameter Strings. These only contain some minor fields valid in both - // vCard and current (as of 2009-08-07) Contacts structure. - private static final Map sKnownPhoneTypesMap_ItoS; - private static final Set sPhoneTypesUnknownToContactsSet; - private static final Map sKnownPhoneTypeMap_StoI; - private static final Map sKnownImPropNameMap_ItoS; - private static final Set sMobilePhoneLabelSet; - - static { - sKnownPhoneTypesMap_ItoS = new HashMap(); - sKnownPhoneTypeMap_StoI = new HashMap(); - - sKnownPhoneTypesMap_ItoS.put(Phone.TYPE_CAR, VCardConstants.PARAM_TYPE_CAR); - sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_TYPE_CAR, Phone.TYPE_CAR); - sKnownPhoneTypesMap_ItoS.put(Phone.TYPE_PAGER, VCardConstants.PARAM_TYPE_PAGER); - sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_TYPE_PAGER, Phone.TYPE_PAGER); - sKnownPhoneTypesMap_ItoS.put(Phone.TYPE_ISDN, VCardConstants.PARAM_TYPE_ISDN); - sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_TYPE_ISDN, Phone.TYPE_ISDN); - - sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_TYPE_HOME, Phone.TYPE_HOME); - sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_TYPE_WORK, Phone.TYPE_WORK); - sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_TYPE_CELL, Phone.TYPE_MOBILE); - - sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_PHONE_EXTRA_TYPE_OTHER, Phone.TYPE_OTHER); - sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_PHONE_EXTRA_TYPE_CALLBACK, - Phone.TYPE_CALLBACK); - sKnownPhoneTypeMap_StoI.put( - VCardConstants.PARAM_PHONE_EXTRA_TYPE_COMPANY_MAIN, Phone.TYPE_COMPANY_MAIN); - sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_PHONE_EXTRA_TYPE_RADIO, Phone.TYPE_RADIO); - sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_PHONE_EXTRA_TYPE_TTY_TDD, - Phone.TYPE_TTY_TDD); - sKnownPhoneTypeMap_StoI.put(VCardConstants.PARAM_PHONE_EXTRA_TYPE_ASSISTANT, - Phone.TYPE_ASSISTANT); - - sPhoneTypesUnknownToContactsSet = new HashSet(); - sPhoneTypesUnknownToContactsSet.add(VCardConstants.PARAM_TYPE_MODEM); - sPhoneTypesUnknownToContactsSet.add(VCardConstants.PARAM_TYPE_MSG); - sPhoneTypesUnknownToContactsSet.add(VCardConstants.PARAM_TYPE_BBS); - sPhoneTypesUnknownToContactsSet.add(VCardConstants.PARAM_TYPE_VIDEO); - - sKnownImPropNameMap_ItoS = new HashMap(); - sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_AIM, VCardConstants.PROPERTY_X_AIM); - sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_MSN, VCardConstants.PROPERTY_X_MSN); - sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_YAHOO, VCardConstants.PROPERTY_X_YAHOO); - sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_SKYPE, VCardConstants.PROPERTY_X_SKYPE_USERNAME); - sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_GOOGLE_TALK, - VCardConstants.PROPERTY_X_GOOGLE_TALK); - sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_ICQ, VCardConstants.PROPERTY_X_ICQ); - sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_JABBER, VCardConstants.PROPERTY_X_JABBER); - sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_QQ, VCardConstants.PROPERTY_X_QQ); - sKnownImPropNameMap_ItoS.put(Im.PROTOCOL_NETMEETING, VCardConstants.PROPERTY_X_NETMEETING); - - // \u643A\u5E2F\u96FB\u8A71 = Full-width Hiragana "Keitai-Denwa" (mobile phone) - // \u643A\u5E2F = Full-width Hiragana "Keitai" (mobile phone) - // \u30B1\u30A4\u30BF\u30A4 = Full-width Katakana "Keitai" (mobile phone) - // \uFF79\uFF72\uFF80\uFF72 = Half-width Katakana "Keitai" (mobile phone) - sMobilePhoneLabelSet = new HashSet(Arrays.asList( - "MOBILE", "\u643A\u5E2F\u96FB\u8A71", "\u643A\u5E2F", "\u30B1\u30A4\u30BF\u30A4", - "\uFF79\uFF72\uFF80\uFF72")); - } - - public static String getPhoneTypeString(Integer type) { - return sKnownPhoneTypesMap_ItoS.get(type); - } - - /** - * Returns Interger when the given types can be parsed as known type. Returns String object - * when not, which should be set to label. - */ - public static Object getPhoneTypeFromStrings(Collection types, - String number) { - if (number == null) { - number = ""; - } - int type = -1; - String label = null; - boolean isFax = false; - boolean hasPref = false; - - if (types != null) { - for (String typeString : types) { - if (typeString == null) { - continue; - } - typeString = typeString.toUpperCase(); - if (typeString.equals(VCardConstants.PARAM_TYPE_PREF)) { - hasPref = true; - } else if (typeString.equals(VCardConstants.PARAM_TYPE_FAX)) { - isFax = true; - } else { - if (typeString.startsWith("X-") && type < 0) { - typeString = typeString.substring(2); - } - if (typeString.length() == 0) { - continue; - } - final Integer tmp = sKnownPhoneTypeMap_StoI.get(typeString); - if (tmp != null) { - final int typeCandidate = tmp; - // TYPE_PAGER is prefered when the number contains @ surronded by - // a pager number and a domain name. - // e.g. - // o 1111@domain.com - // x @domain.com - // x 1111@ - final int indexOfAt = number.indexOf("@"); - if ((typeCandidate == Phone.TYPE_PAGER - && 0 < indexOfAt && indexOfAt < number.length() - 1) - || type < 0 - || type == Phone.TYPE_CUSTOM) { - type = tmp; - } - } else if (type < 0) { - type = Phone.TYPE_CUSTOM; - label = typeString; - } - } - } - } - if (type < 0) { - if (hasPref) { - type = Phone.TYPE_MAIN; - } else { - // default to TYPE_HOME - type = Phone.TYPE_HOME; - } - } - if (isFax) { - if (type == Phone.TYPE_HOME) { - type = Phone.TYPE_FAX_HOME; - } else if (type == Phone.TYPE_WORK) { - type = Phone.TYPE_FAX_WORK; - } else if (type == Phone.TYPE_OTHER) { - type = Phone.TYPE_OTHER_FAX; - } - } - if (type == Phone.TYPE_CUSTOM) { - return label; - } else { - return type; - } - } - - @SuppressWarnings("deprecation") - public static boolean isMobilePhoneLabel(final String label) { - // For backward compatibility. - // Detail: Until Donut, there isn't TYPE_MOBILE for email while there is now. - // To support mobile type at that time, this custom label had been used. - return ("_AUTO_CELL".equals(label) || sMobilePhoneLabelSet.contains(label)); - } - - public static boolean isValidInV21ButUnknownToContactsPhoteType(final String label) { - return sPhoneTypesUnknownToContactsSet.contains(label); - } - - public static String getPropertyNameForIm(final int protocol) { - return sKnownImPropNameMap_ItoS.get(protocol); - } - - public static String[] sortNameElements(final int vcardType, - final String familyName, final String middleName, final String givenName) { - final String[] list = new String[3]; - final int nameOrderType = VCardConfig.getNameOrderType(vcardType); - switch (nameOrderType) { - case VCardConfig.NAME_ORDER_JAPANESE: { - if (containsOnlyPrintableAscii(familyName) && - containsOnlyPrintableAscii(givenName)) { - list[0] = givenName; - list[1] = middleName; - list[2] = familyName; - } else { - list[0] = familyName; - list[1] = middleName; - list[2] = givenName; - } - break; - } - case VCardConfig.NAME_ORDER_EUROPE: { - list[0] = middleName; - list[1] = givenName; - list[2] = familyName; - break; - } - default: { - list[0] = givenName; - list[1] = middleName; - list[2] = familyName; - break; - } - } - return list; - } - - public static int getPhoneNumberFormat(final int vcardType) { - if (VCardConfig.isJapaneseDevice(vcardType)) { - return PhoneNumberUtils.FORMAT_JAPAN; - } else { - return PhoneNumberUtils.FORMAT_NANP; - } - } - - /** - *

- * Inserts postal data into the builder object. - *

- *

- * Note that the data structure of ContactsContract is different from that defined in vCard. - * So some conversion may be performed in this method. - *

- */ - public static void insertStructuredPostalDataUsingContactsStruct(int vcardType, - final ContentProviderOperation.Builder builder, - final VCardEntry.PostalData postalData) { - builder.withValueBackReference(StructuredPostal.RAW_CONTACT_ID, 0); - builder.withValue(Data.MIMETYPE, StructuredPostal.CONTENT_ITEM_TYPE); - - builder.withValue(StructuredPostal.TYPE, postalData.type); - if (postalData.type == StructuredPostal.TYPE_CUSTOM) { - builder.withValue(StructuredPostal.LABEL, postalData.label); - } - - final String streetString; - if (TextUtils.isEmpty(postalData.street)) { - if (TextUtils.isEmpty(postalData.extendedAddress)) { - streetString = null; - } else { - streetString = postalData.extendedAddress; - } - } else { - if (TextUtils.isEmpty(postalData.extendedAddress)) { - streetString = postalData.street; - } else { - streetString = postalData.street + " " + postalData.extendedAddress; - } - } - builder.withValue(StructuredPostal.POBOX, postalData.pobox); - builder.withValue(StructuredPostal.STREET, streetString); - builder.withValue(StructuredPostal.CITY, postalData.localty); - builder.withValue(StructuredPostal.REGION, postalData.region); - builder.withValue(StructuredPostal.POSTCODE, postalData.postalCode); - builder.withValue(StructuredPostal.COUNTRY, postalData.country); - - builder.withValue(StructuredPostal.FORMATTED_ADDRESS, - postalData.getFormattedAddress(vcardType)); - if (postalData.isPrimary) { - builder.withValue(Data.IS_PRIMARY, 1); - } - } - - public static String constructNameFromElements(final int vcardType, - final String familyName, final String middleName, final String givenName) { - return constructNameFromElements(vcardType, familyName, middleName, givenName, - null, null); - } - - public static String constructNameFromElements(final int vcardType, - final String familyName, final String middleName, final String givenName, - final String prefix, final String suffix) { - final StringBuilder builder = new StringBuilder(); - final String[] nameList = sortNameElements(vcardType, familyName, middleName, givenName); - boolean first = true; - if (!TextUtils.isEmpty(prefix)) { - first = false; - builder.append(prefix); - } - for (final String namePart : nameList) { - if (!TextUtils.isEmpty(namePart)) { - if (first) { - first = false; - } else { - builder.append(' '); - } - builder.append(namePart); - } - } - if (!TextUtils.isEmpty(suffix)) { - if (!first) { - builder.append(' '); - } - builder.append(suffix); - } - return builder.toString(); - } - - public static List constructListFromValue(final String value, - final boolean isV30) { - final List list = new ArrayList(); - StringBuilder builder = new StringBuilder(); - int length = value.length(); - for (int i = 0; i < length; i++) { - char ch = value.charAt(i); - if (ch == '\\' && i < length - 1) { - char nextCh = value.charAt(i + 1); - final String unescapedString = - (isV30 ? VCardParserImpl_V30.unescapeCharacter(nextCh) : - VCardParserImpl_V21.unescapeCharacter(nextCh)); - if (unescapedString != null) { - builder.append(unescapedString); - i++; - } else { - builder.append(ch); - } - } else if (ch == ';') { - list.add(builder.toString()); - builder = new StringBuilder(); - } else { - builder.append(ch); - } - } - list.add(builder.toString()); - return list; - } - - public static boolean containsOnlyPrintableAscii(final String...values) { - if (values == null) { - return true; - } - return containsOnlyPrintableAscii(Arrays.asList(values)); - } - - public static boolean containsOnlyPrintableAscii(final Collection values) { - if (values == null) { - return true; - } - for (final String value : values) { - if (TextUtils.isEmpty(value)) { - continue; - } - if (!TextUtils.isPrintableAsciiOnly(value)) { - return false; - } - } - return true; - } - - /** - *

- * This is useful when checking the string should be encoded into quoted-printable - * or not, which is required by vCard 2.1. - *

- *

- * See the definition of "7bit" in vCard 2.1 spec for more information. - *

- */ - public static boolean containsOnlyNonCrLfPrintableAscii(final String...values) { - if (values == null) { - return true; - } - return containsOnlyNonCrLfPrintableAscii(Arrays.asList(values)); - } - - public static boolean containsOnlyNonCrLfPrintableAscii(final Collection values) { - if (values == null) { - return true; - } - final int asciiFirst = 0x20; - final int asciiLast = 0x7E; // included - for (final String value : values) { - if (TextUtils.isEmpty(value)) { - continue; - } - final int length = value.length(); - for (int i = 0; i < length; i = value.offsetByCodePoints(i, 1)) { - final int c = value.codePointAt(i); - if (!(asciiFirst <= c && c <= asciiLast)) { - return false; - } - } - } - return true; - } - - private static final Set sUnAcceptableAsciiInV21WordSet = - new HashSet(Arrays.asList('[', ']', '=', ':', '.', ',', ' ')); - - /** - *

- * This is useful since vCard 3.0 often requires the ("X-") properties and groups - * should contain only alphabets, digits, and hyphen. - *

- *

- * Note: It is already known some devices (wrongly) outputs properties with characters - * which should not be in the field. One example is "X-GOOGLE TALK". We accept - * such kind of input but must never output it unless the target is very specific - * to the device which is able to parse the malformed input. - *

- */ - public static boolean containsOnlyAlphaDigitHyphen(final String...values) { - if (values == null) { - return true; - } - return containsOnlyAlphaDigitHyphen(Arrays.asList(values)); - } - - public static boolean containsOnlyAlphaDigitHyphen(final Collection values) { - if (values == null) { - return true; - } - final int upperAlphabetFirst = 0x41; // A - final int upperAlphabetAfterLast = 0x5b; // [ - final int lowerAlphabetFirst = 0x61; // a - final int lowerAlphabetAfterLast = 0x7b; // { - final int digitFirst = 0x30; // 0 - final int digitAfterLast = 0x3A; // : - final int hyphen = '-'; - for (final String str : values) { - if (TextUtils.isEmpty(str)) { - continue; - } - final int length = str.length(); - for (int i = 0; i < length; i = str.offsetByCodePoints(i, 1)) { - int codepoint = str.codePointAt(i); - if (!((lowerAlphabetFirst <= codepoint && codepoint < lowerAlphabetAfterLast) || - (upperAlphabetFirst <= codepoint && codepoint < upperAlphabetAfterLast) || - (digitFirst <= codepoint && codepoint < digitAfterLast) || - (codepoint == hyphen))) { - return false; - } - } - } - return true; - } - - /** - *

- * Returns true when the given String is categorized as "word" specified in vCard spec 2.1. - *

- *

- * vCard 2.1 specifies:
- * word = <any printable 7bit us-ascii except []=:., > - *

- */ - public static boolean isV21Word(final String value) { - if (TextUtils.isEmpty(value)) { - return true; - } - final int asciiFirst = 0x20; - final int asciiLast = 0x7E; // included - final int length = value.length(); - for (int i = 0; i < length; i = value.offsetByCodePoints(i, 1)) { - final int c = value.codePointAt(i); - if (!(asciiFirst <= c && c <= asciiLast) || - sUnAcceptableAsciiInV21WordSet.contains((char)c)) { - return false; - } - } - return true; - } - - public static String toHalfWidthString(final String orgString) { - if (TextUtils.isEmpty(orgString)) { - return null; - } - final StringBuilder builder = new StringBuilder(); - final int length = orgString.length(); - for (int i = 0; i < length; i = orgString.offsetByCodePoints(i, 1)) { - // All Japanese character is able to be expressed by char. - // Do not need to use String#codepPointAt(). - final char ch = orgString.charAt(i); - final String halfWidthText = JapaneseUtils.tryGetHalfWidthText(ch); - if (halfWidthText != null) { - builder.append(halfWidthText); - } else { - builder.append(ch); - } - } - return builder.toString(); - } - - /** - * Guesses the format of input image. Currently just the first few bytes are used. - * The type "GIF", "PNG", or "JPEG" is returned when possible. Returns null when - * the guess failed. - * @param input Image as byte array. - * @return The image type or null when the type cannot be determined. - */ - public static String guessImageType(final byte[] input) { - if (input == null) { - return null; - } - if (input.length >= 3 && input[0] == 'G' && input[1] == 'I' && input[2] == 'F') { - return "GIF"; - } else if (input.length >= 4 && input[0] == (byte) 0x89 - && input[1] == 'P' && input[2] == 'N' && input[3] == 'G') { - // Note: vCard 2.1 officially does not support PNG, but we may have it and - // using X- word like "X-PNG" may not let importers know it is PNG. - // So we use the String "PNG" as is... - return "PNG"; - } else if (input.length >= 2 && input[0] == (byte) 0xff - && input[1] == (byte) 0xd8) { - return "JPEG"; - } else { - return null; - } - } - - /** - * @return True when all the given values are null or empty Strings. - */ - public static boolean areAllEmpty(final String...values) { - if (values == null) { - return true; - } - - for (final String value : values) { - if (!TextUtils.isEmpty(value)) { - return false; - } - } - return true; - } - - //// The methods bellow may be used by unit test. - - /** - * @hide - */ - public static String parseQuotedPrintable(String value, boolean strictLineBreaking, - String sourceCharset, String targetCharset) { - // "= " -> " ", "=\t" -> "\t". - // Previous code had done this replacement. Keep on the safe side. - final String quotedPrintable; - { - final StringBuilder builder = new StringBuilder(); - final int length = value.length(); - for (int i = 0; i < length; i++) { - char ch = value.charAt(i); - if (ch == '=' && i < length - 1) { - char nextCh = value.charAt(i + 1); - if (nextCh == ' ' || nextCh == '\t') { - builder.append(nextCh); - i++; - continue; - } - } - builder.append(ch); - } - quotedPrintable = builder.toString(); - } - - String[] lines; - if (strictLineBreaking) { - lines = quotedPrintable.split("\r\n"); - } else { - StringBuilder builder = new StringBuilder(); - final int length = quotedPrintable.length(); - ArrayList list = new ArrayList(); - for (int i = 0; i < length; i++) { - char ch = quotedPrintable.charAt(i); - if (ch == '\n') { - list.add(builder.toString()); - builder = new StringBuilder(); - } else if (ch == '\r') { - list.add(builder.toString()); - builder = new StringBuilder(); - if (i < length - 1) { - char nextCh = quotedPrintable.charAt(i + 1); - if (nextCh == '\n') { - i++; - } - } - } else { - builder.append(ch); - } - } - final String lastLine = builder.toString(); - if (lastLine.length() > 0) { - list.add(lastLine); - } - lines = list.toArray(new String[0]); - } - - final StringBuilder builder = new StringBuilder(); - for (String line : lines) { - if (line.endsWith("=")) { - line = line.substring(0, line.length() - 1); - } - builder.append(line); - } - - final String rawString = builder.toString(); - if (TextUtils.isEmpty(rawString)) { - Log.w(LOG_TAG, "Given raw string is empty."); - } - - byte[] rawBytes = null; - try { - rawBytes = rawString.getBytes(sourceCharset); - } catch (UnsupportedEncodingException e) { - Log.w(LOG_TAG, "Failed to decode: " + sourceCharset); - rawBytes = rawString.getBytes(); - } - - byte[] decodedBytes = null; - try { - decodedBytes = QuotedPrintableCodec.decodeQuotedPrintable(rawBytes); - } catch (DecoderException e) { - Log.e(LOG_TAG, "DecoderException is thrown."); - decodedBytes = rawBytes; - } - - try { - return new String(decodedBytes, targetCharset); - } catch (UnsupportedEncodingException e) { - Log.e(LOG_TAG, "Failed to encode: charset=" + targetCharset); - return new String(decodedBytes); - } - } - - private VCardUtils() { - } -} diff --git a/vcard/java/com/android/vcard/exception/VCardAgentNotSupportedException.java b/vcard/java/com/android/vcard/exception/VCardAgentNotSupportedException.java deleted file mode 100644 index c408716e5..000000000 --- a/vcard/java/com/android/vcard/exception/VCardAgentNotSupportedException.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * 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.vcard.exception; - -public class VCardAgentNotSupportedException extends VCardNotSupportedException { - public VCardAgentNotSupportedException() { - super(); - } - - public VCardAgentNotSupportedException(String message) { - super(message); - } - -} \ No newline at end of file diff --git a/vcard/java/com/android/vcard/exception/VCardException.java b/vcard/java/com/android/vcard/exception/VCardException.java deleted file mode 100644 index 3ad7fd38b..000000000 --- a/vcard/java/com/android/vcard/exception/VCardException.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * 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.vcard.exception; - -public class VCardException extends java.lang.Exception { - /** - * Constructs a VCardException object - */ - public VCardException() { - super(); - } - - /** - * Constructs a VCardException object - * - * @param message the error message - */ - public VCardException(String message) { - super(message); - } - -} diff --git a/vcard/java/com/android/vcard/exception/VCardInvalidCommentLineException.java b/vcard/java/com/android/vcard/exception/VCardInvalidCommentLineException.java deleted file mode 100644 index 342769ef5..000000000 --- a/vcard/java/com/android/vcard/exception/VCardInvalidCommentLineException.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * 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.vcard.exception; - -/** - * Thrown when the vCard has some line starting with '#'. In the specification, - * both vCard 2.1 and vCard 3.0 does not allow such line, but some actual exporter emit - * such lines. - */ -public class VCardInvalidCommentLineException extends VCardInvalidLineException { - public VCardInvalidCommentLineException() { - super(); - } - - public VCardInvalidCommentLineException(final String message) { - super(message); - } -} diff --git a/vcard/java/com/android/vcard/exception/VCardInvalidLineException.java b/vcard/java/com/android/vcard/exception/VCardInvalidLineException.java deleted file mode 100644 index 5c2250fc3..000000000 --- a/vcard/java/com/android/vcard/exception/VCardInvalidLineException.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * 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.vcard.exception; - -/** - * Thrown when the vCard has some line starting with '#'. In the specification, - * both vCard 2.1 and vCard 3.0 does not allow such line, but some actual exporter emit - * such lines. - */ -public class VCardInvalidLineException extends VCardException { - public VCardInvalidLineException() { - super(); - } - - public VCardInvalidLineException(final String message) { - super(message); - } -} diff --git a/vcard/java/com/android/vcard/exception/VCardNestedException.java b/vcard/java/com/android/vcard/exception/VCardNestedException.java deleted file mode 100644 index 2b9b1acf5..000000000 --- a/vcard/java/com/android/vcard/exception/VCardNestedException.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * 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.vcard.exception; - -/** - * VCardException thrown when VCard is nested without VCardParser's being notified. - */ -public class VCardNestedException extends VCardNotSupportedException { - public VCardNestedException() { - super(); - } - public VCardNestedException(String message) { - super(message); - } -} diff --git a/vcard/java/com/android/vcard/exception/VCardNotSupportedException.java b/vcard/java/com/android/vcard/exception/VCardNotSupportedException.java deleted file mode 100644 index 61ff752c9..000000000 --- a/vcard/java/com/android/vcard/exception/VCardNotSupportedException.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * 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.vcard.exception; - -/** - * The exception which tells that the input VCard is probably valid from the view of - * specification but not supported in the current framework for now. - * - * This is a kind of a good news from the view of development. - * It may be good to ask users to send a report with the VCard example - * for the future development. - */ -public class VCardNotSupportedException extends VCardException { - public VCardNotSupportedException() { - super(); - } - public VCardNotSupportedException(String message) { - super(message); - } -} \ No newline at end of file diff --git a/vcard/java/com/android/vcard/exception/VCardVersionException.java b/vcard/java/com/android/vcard/exception/VCardVersionException.java deleted file mode 100644 index 047c58053..000000000 --- a/vcard/java/com/android/vcard/exception/VCardVersionException.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * 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.vcard.exception; - -/** - * VCardException used only when the version of the vCard is different. - */ -public class VCardVersionException extends VCardException { - public VCardVersionException() { - super(); - } - public VCardVersionException(String message) { - super(message); - } -} diff --git a/vcard/tests/Android.mk b/vcard/tests/Android.mk deleted file mode 100644 index c34d3265b..000000000 --- a/vcard/tests/Android.mk +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright (C) 2010 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. - -LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_CERTIFICATE := platform -LOCAL_MODULE_TAGS := tests -LOCAL_PACKAGE_NAME := AndroidVCardTests -LOCAL_SRC_FILES := $(call all-java-files-under, src) -LOCAL_JAVA_LIBRARIES := android.test.runner -LOCAL_STATIC_JAVA_LIBRARIES := com.android.vcard - -include $(BUILD_PACKAGE) diff --git a/vcard/tests/AndroidManifest.xml b/vcard/tests/AndroidManifest.xml deleted file mode 100644 index fcbf76721..000000000 --- a/vcard/tests/AndroidManifest.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - diff --git a/vcard/tests/res/raw/v21_backslash.vcf b/vcard/tests/res/raw/v21_backslash.vcf deleted file mode 100644 index bd3002b32..000000000 --- a/vcard/tests/res/raw/v21_backslash.vcf +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN:VCARD -VERSION:2.1 -N:;A\;B\\;C\\\;;D;\:E;\\\\; -FN:A;B\C\;D:E\\ -END:VCARD diff --git a/vcard/tests/res/raw/v21_complicated.vcf b/vcard/tests/res/raw/v21_complicated.vcf deleted file mode 100644 index de34e1668..000000000 --- a/vcard/tests/res/raw/v21_complicated.vcf +++ /dev/null @@ -1,106 +0,0 @@ -BEGIN:VCARD -VERSION:2.1 -N:Gump;Forrest;Hoge;Pos;Tao -FN:Joe Due -ORG:Gump Shrimp Co.;Sales Dept.\;Manager;Fish keeper -ROLE:Fish Cake Keeper! -X-CLASS:PUBLIC -TITLE:Shrimp Man -TEL;WORK;VOICE:(111) 555-1212 -TEL;HOME;VOICE:(404) 555-1212 -TEL;CELL:0311111111 -TEL;VIDEO:0322222222 -TEL;VOICE:0333333333 -ADR;WORK:;;100 Waters Edge;Baytown;LA;30314;United States of America -LABEL;WORK;ENCODING=QUOTED-PRINTABLE:100 Waters Edge=0D=0ABaytown, LA 30314=0D=0AUnited States of America -ADR;HOME:;;42 Plantation St.;Baytown;LA;30314;United States of America -LABEL;HOME;ENCODING=QUOTED-PRINTABLE:42 Plantation St.=0D=0A= -Baytown, LA 30314=0D=0A= -United States of America -EMAIL;PREF;INTERNET:forrestgump@walladalla.com -EMAIL;CELL:cell@example.com -NOTE:The following note is the example from RFC 2045. -NOTE;ENCODING=QUOTED-PRINTABLE:Now's the time = -for all folk to come= - to the aid of their country. - -PHOTO;ENCODING=BASE64;TYPE=JPEG: - /9j/4QoPRXhpZgAATU0AKgAAAAgADQEOAAIAAAAPAAAAqgEPAAIAAAAHAAAAugEQAAIAAAAG - AAAAwgESAAMAAAABAAEAAAEaAAUAAAABAAAAyAEbAAUAAAABAAAA0AEoAAMAAAABAAIAAAEx - AAIAAAAOAAAA2AEyAAIAAAAUAAAA5gITAAMAAAABAAEAAIKYAAIAAAAOAAAA+odpAAQAAAAB - AAABhMSlAAcAAAB8AAABCAAABB4yMDA4MTAyOTEzNTUzMQAARG9Db01vAABEOTA1aQAAAABI - AAAAAQAAAEgAAAABRDkwNWkgVmVyMS4wMAAyMDA4OjEwOjI5IDEzOjU1OjQ3ACAgICAgICAg - ICAgICAAUHJpbnRJTQAwMzAwAAAABgABABQAFAACAQAAAAADAAAANAEABQAAAAEBAQAAAAEQ - gAAAAAAAEQkAACcQAAAPCwAAJxAAAAWXAAAnEAAACLAAACcQAAAcAQAAJxAAAAJeAAAnEAAA - AIsAACcQAAADywAAJxAAABvlAAAnEAAogpoABQAAAAEAAANqgp0ABQAAAAEAAANyiCIAAwAA - AAEAAgAAkAAABwAAAAQwMjIwkAMAAgAAABQAAAN6kAQAAgAAABQAAAOOkQEABwAAAAQBAgMA - kQIABQAAAAEAAAOikgEACgAAAAEAAAOqkgIABQAAAAEAAAOykgQACgAAAAEAAAO6kgUABQAA - AAEAAAPCkgcAAwAAAAEAAgAAkggAAwAAAAEAAAAAkgkAAwAAAAEAAAAAkgoABQAAAAEAAAPK - knwABwAAAAEAAAAAkoYABwAAABYAAAPSoAAABwAAAAQwMTAwoAEAAwAAAAEAAQAAoAIAAwAA - AAEAYAAAoAMAAwAAAAEASAAAoAUABAAAAAEAAAQAog4ABQAAAAEAAAPoog8ABQAAAAEAAAPw - ohAAAwAAAAEAAgAAohcAAwAAAAEAAgAAowAABwAAAAEDAAAAowEABwAAAAEBAAAApAEAAwAA - AAEAAAAApAIAAwAAAAEAAAAApAMAAwAAAAEAAAAApAQABQAAAAEAAAP4pAUAAwAAAAEAHQAA - pAYAAwAAAAEAAAAApAcAAwAAAAEAAAAApAgAAwAAAAEAAAAApAkAAwAAAAEAAAAApAoAAwAA - AAEAAAAApAwAAwAAAAEAAgAAAAAAAAAAAFMAACcQAAABXgAAAGQyMDA4OjEwOjI5IDEzOjU1 - OjMxADIwMDg6MTA6MjkgMTM6NTU6NDcAAAApiAAAGwAAAAKyAAAAZAAAAV4AAABkAAAAAAAA - AGQAAAAlAAAACgAADpIAAAPoAAAAAAAAAAAyMDA4MTAyOTEzNTUzMQAAICoAAAAKAAAq4gAA - AAoAAAAAAAAAAQACAAEAAgAAAARSOTgAAAIABwAAAAQwMTAwAAAAAAAGAQMAAwAAAAEABgAA - ARoABQAAAAEAAARsARsABQAAAAEAAAR0ASgAAwAAAAEAAgAAAgEABAAAAAEAAAR8AgIABAAA - AAEAAAWLAAAAAAAAAEgAAAABAAAASAAAAAH/2P/bAIQAIBYYHBgUIBwaHCQiICYwUDQwLCww - YkZKOlB0Znp4cmZwboCQuJyAiK6KbnCg2qKuvsTO0M58muLy4MjwuMrOxgEiJCQwKjBeNDRe - xoRwhMbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbG - /8AAEQgAeACgAwEhAAIRAQMRAf/EAaIAAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKCxAA - AgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkK - FhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWG - h4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl - 5ufo6erx8vP09fb3+Pn6AQADAQEBAQEBAQEBAAAAAAAAAQIDBAUGBwgJCgsRAAIBAgQEAwQH - BQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEXGBka - JicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWGh4iJipKT - lJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uLj5OXm5+jp6vLz - 9PX29/j5+v/aAAwDAQACEQMRAD8AFFSqKkZIoqRVpgSKKeBTEOApwFADsUYpgIRSEUANIppF - ICNhUTCgCMio2FICJhULCgC0oqVaAJFFSqKBkgFOApiHCnCgB2KMUCENJQA0imEUDGMKiYUA - RtUbUgIWqJhQBZSpFoAlWpVoGPFPFMQ7tSK2ODQA4yKO9HmKe9FxAzDHFIOlAAaYaAGNUTUD - ImqNqQETVE1AE6VKKAJFNSqaAHg08GmANIFFQM5Y5qJMBuT60ZNQIcrkVYSQMKuLGKaaasQx - qiagZE1RtSAjaomoAkQ1KpoAlU1IpoAkU07OBTArO+5qkV12Y71lfUBmaKkCRSuznrTFba2a - oCwGyM0E1qIjY1GxoGRNUZNICNqiagByGplNAEimpFNMB4YDvSucpxSYEIU04KazsAu1qArU - WELtPpTSposBNETt5pxNaoCNjUbGgCNjUZoGRtUTUgFU1KpoAkBqQHigCFnO7rUqOdlZp6gA - c+tODn1pXAXzD60eYfWncQvmNSGQ07gOMhCVEJGz1ptgS5yKYxqwGE1GxoAiamGkMapqVTQB - Kpp+eKAICfmqWM/Kaz6gANOBqQFzRmmAuaTNACsfkqMHmm9wJs8U0mtRDGNRsaAI2phpDI1N - SqaAJFNSA8UCISfmqSM/Kaz6jAHmnA1ICg0uaAFzSZpgKx+SmDrTe4E2eKaTWoiMmmMaAIzT - DSGRKakU0ASKaeDTERseakjPyms+oxAacDUgOBpc0gFzSZpgOY/KKYv3qrqIlpprQBjGoyaA - GGmmkMgU1IppgPBqQGgQu0Gn4wvFKwEQpwNZDHZpc0ALmigRKBleaQKBWtgA001QDGqM0gGm - mGkMrqakBoAepp4NMRIDTwaAE2A008GokgHxjd1pzKFpW0uAg5NSBBTirgOpDWgDTTTQAw0w - 0gGGmmgZWBp4pASKaeDTEOBp4NADwajbrUyBEkXWnSUdAGr1qeiAMSkNWAhphoAaaYaQDDTT - SGVRTwaYDxTwaBDwaeDQA4GlK5oauIeo20pGaLaAKqgU6hKwBSGmAhphoAaaYaQxhpppDKgN - PFMB4p4oEPFOBpgPBp4NAhwpwoAWloAKSgBDTTQMYaYaQDTTTSGA/9n/2wCEAAoHBwgHBgoI - CAgLCgoLDhgQDg0NDh0VFhEYIx8lJCIfIiEmKzcvJik0KSEiMEExNDk7Pj4+JS5ESUM8SDc9 - PjsBCgsLDg0OHBAQHDsoIig7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7 - Ozs7Ozs7Ozs7Ozs7O//AABEIAEgAYAMBIQACEQEDEQH/xAGiAAABBQEBAQEBAQAAAAAAAAAA - AQIDBAUGBwgJCgsQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNC - scEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hp - anN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS - 09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+gEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcI - CQoLEQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVi - ctEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4 - eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY - 2dri4+Tl5ufo6ery8/T19vf4+fr/2gAMAwEAAhEDEQA/AJ7SLgcVr20I4rNFGvbQAAHFaEUX - SrQi5HCMdKk8oY6VSJYx4hjpVaWMelAFC4iGDxWPdR8mkxmRdxjBrEvI+tZjN20xtHNbNqAc - UIDXg6Cr0WKtCY8XKQOyzOB3FKNWsyceZ+lS6sY6NkjvPSdwImBHUmmy4q076oCjOODWPdgc - 0MpGPdAYNYl4o5rNjNKzkyorZtXxihAa1vIDip7m9Frb7/4jwKcnyxbEzN3ieJppZsyZ4U1H - urzZau4mWVlNrGk0UuWPVa1YroXEIkHfrXZh5W90RWncAHmsi6bJNdQ0ZNw3BrGuiMGs2Mks - puBzWzbzdOaEBeOpR2oUtkk9hTru7iuo4m8wgemKyqTi04sBsfkEf68j8KlUQZz9o/SuZRj3 - JYriAji4/Sp7W6htbV2aXcu70ramoxle4gN7HcIXjbis+4k5NdaaauhmVcv1rHuW61DGiG1m - 6c1s20/TmgAv5vmj57VKk3+ixnPc1xVV70h9CVJuOtSrL71hFgxzScUkkn+iY/2q1i9xDrGT - 9y31pJ5Otd1L+GhMy7mTrWXO2SapjRn28vTmta3nxjmgGOvJd2w1Kkv+ipz/ABGuOoveYdCe - ObjrU6y5rlsA8ycUksn+ij/eNaw6iJLNsW59zTJn6816FP4EJmbO+Saz5m602UjIgk4HNadv - LwKaBl+MpIMOMipp490SCJeF7CoqQvF2JuRqWQ4YEGrSiQJuKnHrXByMpki73GFBNXIoh9n2 - SrnnOK6MPTbd3sSwIVF2qMCqkzHmuy1lYRnTHrVGWpZaMKB+BWlbycYoQM0IZDxzV+GU8c1a - IYy5Y+dnHatAsfsAHfArmS1mPoh1gT8x9qtk1rQX7tCe5DIapzGtGBQm71SlqGWjnIH6Vowt - zmhAy/E3vV6F6tEMuxlWIyAfrVxCCAO1VZEEyYA4AApxNGwyJ+lVJRUsaKMw61SlFQzRAP/Z - -X-ATTRIBUTE:Some String -BDAY:19800101 -GEO:35.6563854,139.6994233 -URL:http://www.example.com/ -REV:20080424T195243Z -END:VCARD \ No newline at end of file diff --git a/vcard/tests/res/raw/v21_invalid_comment_line.vcf b/vcard/tests/res/raw/v21_invalid_comment_line.vcf deleted file mode 100644 index f910710af..000000000 --- a/vcard/tests/res/raw/v21_invalid_comment_line.vcf +++ /dev/null @@ -1,10 +0,0 @@ -BEGIN:vCard -VERSION:2.1 -UID:357 -N:;Conference Call -FN:Conference Call -# This line must be ignored. -NOTE;ENCODING=QUOTED-PRINTABLE:This is an (sharp ->= -#<- sharp) example. This message must NOT be ignored. -# This line must be ignored too. -END:vCard diff --git a/vcard/tests/res/raw/v21_japanese_1.vcf b/vcard/tests/res/raw/v21_japanese_1.vcf deleted file mode 100644 index d05e2fffb..000000000 --- a/vcard/tests/res/raw/v21_japanese_1.vcf +++ /dev/null @@ -1,6 +0,0 @@ -BEGIN:VCARD -VERSION:2.1 -N;CHARSET=SHIFT_JIS:ˆÀ“¡ƒƒCƒh;;;; -SOUND;X-IRMC-N;CHARSET=SHIFT_JIS:±ÝÄÞ³Û²ÄÞ;;;; -TEL;PREF;VOICE:0300000000 -END:VCARD diff --git a/vcard/tests/res/raw/v21_japanese_2.vcf b/vcard/tests/res/raw/v21_japanese_2.vcf deleted file mode 100644 index fa54acbc8..000000000 --- a/vcard/tests/res/raw/v21_japanese_2.vcf +++ /dev/null @@ -1,10 +0,0 @@ -BEGIN:VCARD -VERSION:2.1 -FN;CHARSET=SHIFT_JIS:ˆÀ“¡ ƒƒCƒh 1 -N;CHARSET=SHIFT_JIS:ˆÀ“¡;ƒƒCƒh1;;; -SOUND;X-IRMC-N;CHARSET=SHIFT_JIS:±ÝÄÞ³;Û²ÄÞ1;;; -ADR;HOME;CHARSET=SHIFT_JIS;ENCODING=QUOTED-PRINTABLE:;=93=8C=8B=9E=93=73= -=8F=61=92=4A=8B=E6=8D=F7=8B=75=92=AC26-1=83=5A=83=8B=83=8A=83=41=83=93= -=83=5E=83=8F=81=5B6=8A=4B;;;;150-8512; -NOTE;CHARSET=SHIFT_JIS;ENCODING=QUOTED-PRINTABLE:=83=81=83=82 -END:VCARD diff --git a/vcard/tests/res/raw/v21_multiple_entry.vcf b/vcard/tests/res/raw/v21_multiple_entry.vcf deleted file mode 100644 index ebbb19a4b..000000000 --- a/vcard/tests/res/raw/v21_multiple_entry.vcf +++ /dev/null @@ -1,33 +0,0 @@ -BEGIN:VCARD -VERSION:2.1 -N;CHARSET=SHIFT_JIS:ˆÀ“¡ƒƒCƒh3;;;; -SOUND;X-IRMC-N;CHARSET=SHIFT_JIS:±ÝÄÞ³Û²ÄÞ3;;;; -TEL;X-NEC-SECRET:9 -TEL;X-NEC-HOTEL:10 -TEL;X-NEC-SCHOOL:11 -TEL;HOME;FAX:12 -END:VCARD - - -BEGIN:VCARD -VERSION:2.1 -N;CHARSET=SHIFT_JIS:ˆÀ“¡ƒƒCƒh4;;;; -SOUND;X-IRMC-N;CHARSET=SHIFT_JIS:±ÝÄÞ³Û²ÄÞ4;;;; -TEL;MODEM:13 -TEL;PAGER:14 -TEL;X-NEC-FAMILY:15 -TEL;X-NEC-GIRL:16 -END:VCARD - - -BEGIN:VCARD -VERSION:2.1 -N;CHARSET=SHIFT_JIS:ˆÀ“¡ƒƒCƒh5;;;; -SOUND;X-IRMC-N;CHARSET=SHIFT_JIS:±ÝÄÞ³Û²ÄÞ5;;;; -TEL;X-NEC-BOY:17 -TEL;X-NEC-FRIEND:18 -TEL;X-NEC-PHS:19 -TEL;X-NEC-RESTAURANT:20 -END:VCARD - - diff --git a/vcard/tests/res/raw/v21_org_before_title.vcf b/vcard/tests/res/raw/v21_org_before_title.vcf deleted file mode 100644 index 8ff1190f1..000000000 --- a/vcard/tests/res/raw/v21_org_before_title.vcf +++ /dev/null @@ -1,6 +0,0 @@ -BEGIN:VCARD -VERSION:2.1 -FN:Normal Guy -ORG:Company;Organization;Devision;Room;Sheet No. -TITLE:Excellent Janitor -END:VCARD diff --git a/vcard/tests/res/raw/v21_pref_handling.vcf b/vcard/tests/res/raw/v21_pref_handling.vcf deleted file mode 100644 index 51053101a..000000000 --- a/vcard/tests/res/raw/v21_pref_handling.vcf +++ /dev/null @@ -1,15 +0,0 @@ -BEGIN:VCARD -VERSION:2.1 -FN:Smith -TEL;HOME:1 -TEL;WORK;PREF:2 -TEL;ISDN:3 -EMAIL;PREF;HOME:test@example.com -EMAIL;CELL;PREF:test2@examination.com -ORG:Company -TITLE:Engineer -ORG:Mystery -TITLE:Blogger -ORG:Poetry -TITLE:Poet -END:VCARD diff --git a/vcard/tests/res/raw/v21_simple_1.vcf b/vcard/tests/res/raw/v21_simple_1.vcf deleted file mode 100644 index 6aabb4c02..000000000 --- a/vcard/tests/res/raw/v21_simple_1.vcf +++ /dev/null @@ -1,3 +0,0 @@ -BEGIN:VCARD -N:Ando;Roid; -END:VCARD diff --git a/vcard/tests/res/raw/v21_simple_2.vcf b/vcard/tests/res/raw/v21_simple_2.vcf deleted file mode 100644 index f0d5ab506..000000000 --- a/vcard/tests/res/raw/v21_simple_2.vcf +++ /dev/null @@ -1,3 +0,0 @@ -BEGIN:VCARD -FN:Ando Roid -END:VCARD diff --git a/vcard/tests/res/raw/v21_simple_3.vcf b/vcard/tests/res/raw/v21_simple_3.vcf deleted file mode 100644 index beddabb96..000000000 --- a/vcard/tests/res/raw/v21_simple_3.vcf +++ /dev/null @@ -1,4 +0,0 @@ -BEGIN:VCARD -N:Ando;Roid; -FN:Ando Roid -END:VCARD diff --git a/vcard/tests/res/raw/v21_title_before_org.vcf b/vcard/tests/res/raw/v21_title_before_org.vcf deleted file mode 100644 index 9fdc7389c..000000000 --- a/vcard/tests/res/raw/v21_title_before_org.vcf +++ /dev/null @@ -1,6 +0,0 @@ -BEGIN:VCARD -VERSION:2.1 -FN:Nice Guy -TITLE:Cool Title -ORG:Marverous;Perfect;Great;Good;Bad;Poor -END:VCARD diff --git a/vcard/tests/res/raw/v21_winmo_65.vcf b/vcard/tests/res/raw/v21_winmo_65.vcf deleted file mode 100644 index f380d0d5c..000000000 --- a/vcard/tests/res/raw/v21_winmo_65.vcf +++ /dev/null @@ -1,10 +0,0 @@ -BEGIN:VCARD -VERSION:2.1 -N:Example;;;; -FN:Example -ANNIVERSARY;VALUE=DATE:20091010 -AGENT:Invalid line which must be handled correctly. -X-CLASS:PUBLIC -X-REDUCTION: -X-NO: -END:VCARD diff --git a/vcard/tests/res/raw/v30_comma_separated.vcf b/vcard/tests/res/raw/v30_comma_separated.vcf deleted file mode 100644 index 98a7f2058..000000000 --- a/vcard/tests/res/raw/v30_comma_separated.vcf +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN:VCARD -VERSION:3.0 -N:F;G;M;; -TEL;TYPE=PAGER,WORK,MSG:6101231234@pagersample.com -END:VCARD diff --git a/vcard/tests/res/raw/v30_simple.vcf b/vcard/tests/res/raw/v30_simple.vcf deleted file mode 100644 index 418661f74..000000000 --- a/vcard/tests/res/raw/v30_simple.vcf +++ /dev/null @@ -1,13 +0,0 @@ -BEGIN:VCARD -VERSION:3.0 -FN:And Roid -N:And;Roid;;; -ORG:Open;Handset; Alliance -SORT-STRING:android -TEL;TYPE=PREF;TYPE=VOICE:0300000000 -CLASS:PUBLIC -X-GNO:0 -X-GN:group0 -X-REDUCTION:0 -REV:20081031T065854Z -END:VCARD diff --git a/vcard/tests/src/com/android/vcard/tests/VCardExporterTests.java b/vcard/tests/src/com/android/vcard/tests/VCardExporterTests.java deleted file mode 100644 index b6419c36f..000000000 --- a/vcard/tests/src/com/android/vcard/tests/VCardExporterTests.java +++ /dev/null @@ -1,971 +0,0 @@ -/* - * 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.vcard.tests; - -import android.content.ContentValues; -import android.provider.ContactsContract.CommonDataKinds.Email; -import android.provider.ContactsContract.CommonDataKinds.Event; -import android.provider.ContactsContract.CommonDataKinds.Im; -import android.provider.ContactsContract.CommonDataKinds.Nickname; -import android.provider.ContactsContract.CommonDataKinds.Note; -import android.provider.ContactsContract.CommonDataKinds.Organization; -import android.provider.ContactsContract.CommonDataKinds.Phone; -import android.provider.ContactsContract.CommonDataKinds.Photo; -import android.provider.ContactsContract.CommonDataKinds.Relation; -import android.provider.ContactsContract.CommonDataKinds.StructuredName; -import android.provider.ContactsContract.CommonDataKinds.StructuredPostal; -import android.provider.ContactsContract.CommonDataKinds.Website; - -import com.android.vcard.VCardConfig; -import com.android.vcard.tests.test_utils.ContactEntry; -import com.android.vcard.tests.test_utils.PropertyNodesVerifierElem; -import com.android.vcard.tests.test_utils.PropertyNodesVerifierElem.TypeSet; - -import java.util.Arrays; - -/** - * Tests for the code related to vCard exporter, inculding vCard composer. - * This test class depends on vCard importer code, so if tests for vCard importer fail, - * the result of this class will not be reliable. - */ -public class VCardExporterTests extends VCardTestsBase { - private static final byte[] sPhotoByteArray = - VCardImporterTests.sPhotoByteArrayForComplicatedCase; - - public void testSimpleV21() { - mVerifier.initForExportTest(V21); - mVerifier.addInputEntry().addContentValues(StructuredName.CONTENT_ITEM_TYPE) - .put(StructuredName.FAMILY_NAME, "Ando") - .put(StructuredName.GIVEN_NAME, "Roid"); - mVerifier.addPropertyNodesVerifierElem() - .addExpectedNode("FN", "Roid Ando") - .addExpectedNode("N", "Ando;Roid;;;", - Arrays.asList("Ando", "Roid", "", "", "")); - } - - private void testStructuredNameBasic(int vcardType) { - final boolean isV30 = VCardConfig.isV30(vcardType); - mVerifier.initForExportTest(vcardType); - mVerifier.addInputEntry().addContentValues(StructuredName.CONTENT_ITEM_TYPE) - .put(StructuredName.FAMILY_NAME, "AppropriateFamilyName") - .put(StructuredName.GIVEN_NAME, "AppropriateGivenName") - .put(StructuredName.MIDDLE_NAME, "AppropriateMiddleName") - .put(StructuredName.PREFIX, "AppropriatePrefix") - .put(StructuredName.SUFFIX, "AppropriateSuffix") - .put(StructuredName.PHONETIC_FAMILY_NAME, "AppropriatePhoneticFamily") - .put(StructuredName.PHONETIC_GIVEN_NAME, "AppropriatePhoneticGiven") - .put(StructuredName.PHONETIC_MIDDLE_NAME, "AppropriatePhoneticMiddle"); - - PropertyNodesVerifierElem elem = mVerifier.addPropertyNodesVerifierElem() - .addExpectedNodeWithOrder("N", - "AppropriateFamilyName;AppropriateGivenName;AppropriateMiddleName;" - + "AppropriatePrefix;AppropriateSuffix", - Arrays.asList("AppropriateFamilyName", "AppropriateGivenName", - "AppropriateMiddleName", "AppropriatePrefix", "AppropriateSuffix")) - .addExpectedNodeWithOrder("FN", - "AppropriatePrefix AppropriateGivenName " - + "AppropriateMiddleName AppropriateFamilyName AppropriateSuffix") - .addExpectedNode("X-PHONETIC-FIRST-NAME", "AppropriatePhoneticGiven") - .addExpectedNode("X-PHONETIC-MIDDLE-NAME", "AppropriatePhoneticMiddle") - .addExpectedNode("X-PHONETIC-LAST-NAME", "AppropriatePhoneticFamily"); - - if (isV30) { - elem.addExpectedNode("SORT-STRING", - "AppropriatePhoneticGiven AppropriatePhoneticMiddle " - + "AppropriatePhoneticFamily"); - } - } - - public void testStructuredNameBasicV21() { - testStructuredNameBasic(V21); - } - - public void testStructuredNameBasicV30() { - testStructuredNameBasic(V30); - } - - /** - * Test that only "primary" StructuredName is emitted, so that our vCard file - * will not confuse the external importer, assuming there may be some importer - * which presume that there's only one property toward each of "N", "FN", etc. - * Note that more than one "N", "FN", etc. properties are acceptable in vCard spec. - */ - private void testStructuredNameUsePrimaryCommon(int vcardType) { - final boolean isV30 = (vcardType == V30); - mVerifier.initForExportTest(vcardType); - ContactEntry entry = mVerifier.addInputEntry(); - entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE) - .put(StructuredName.FAMILY_NAME, "DoNotEmitFamilyName1") - .put(StructuredName.GIVEN_NAME, "DoNotEmitGivenName1") - .put(StructuredName.MIDDLE_NAME, "DoNotEmitMiddleName1") - .put(StructuredName.PREFIX, "DoNotEmitPrefix1") - .put(StructuredName.SUFFIX, "DoNotEmitSuffix1") - .put(StructuredName.PHONETIC_FAMILY_NAME, "DoNotEmitPhoneticFamily1") - .put(StructuredName.PHONETIC_GIVEN_NAME, "DoNotEmitPhoneticGiven1") - .put(StructuredName.PHONETIC_MIDDLE_NAME, "DoNotEmitPhoneticMiddle1"); - - // With "IS_PRIMARY=1". This is what we should use. - entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE) - .put(StructuredName.FAMILY_NAME, "AppropriateFamilyName") - .put(StructuredName.GIVEN_NAME, "AppropriateGivenName") - .put(StructuredName.MIDDLE_NAME, "AppropriateMiddleName") - .put(StructuredName.PREFIX, "AppropriatePrefix") - .put(StructuredName.SUFFIX, "AppropriateSuffix") - .put(StructuredName.PHONETIC_FAMILY_NAME, "AppropriatePhoneticFamily") - .put(StructuredName.PHONETIC_GIVEN_NAME, "AppropriatePhoneticGiven") - .put(StructuredName.PHONETIC_MIDDLE_NAME, "AppropriatePhoneticMiddle") - .put(StructuredName.IS_PRIMARY, 1); - - // With "IS_PRIMARY=1", but we should ignore this time, since this is second, not first. - entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE) - .put(StructuredName.FAMILY_NAME, "DoNotEmitFamilyName2") - .put(StructuredName.GIVEN_NAME, "DoNotEmitGivenName2") - .put(StructuredName.MIDDLE_NAME, "DoNotEmitMiddleName2") - .put(StructuredName.PREFIX, "DoNotEmitPrefix2") - .put(StructuredName.SUFFIX, "DoNotEmitSuffix2") - .put(StructuredName.PHONETIC_FAMILY_NAME, "DoNotEmitPhoneticFamily2") - .put(StructuredName.PHONETIC_GIVEN_NAME, "DoNotEmitPhoneticGiven2") - .put(StructuredName.PHONETIC_MIDDLE_NAME, "DoNotEmitPhoneticMiddle2") - .put(StructuredName.IS_PRIMARY, 1); - - PropertyNodesVerifierElem elem = mVerifier.addPropertyNodesVerifierElem() - .addExpectedNodeWithOrder("N", - "AppropriateFamilyName;AppropriateGivenName;AppropriateMiddleName;" - + "AppropriatePrefix;AppropriateSuffix", - Arrays.asList("AppropriateFamilyName", "AppropriateGivenName", - "AppropriateMiddleName", "AppropriatePrefix", "AppropriateSuffix")) - .addExpectedNodeWithOrder("FN", - "AppropriatePrefix AppropriateGivenName " - + "AppropriateMiddleName AppropriateFamilyName AppropriateSuffix") - .addExpectedNode("X-PHONETIC-FIRST-NAME", "AppropriatePhoneticGiven") - .addExpectedNode("X-PHONETIC-MIDDLE-NAME", "AppropriatePhoneticMiddle") - .addExpectedNode("X-PHONETIC-LAST-NAME", "AppropriatePhoneticFamily"); - - if (isV30) { - elem.addExpectedNode("SORT-STRING", - "AppropriatePhoneticGiven AppropriatePhoneticMiddle " - + "AppropriatePhoneticFamily"); - } - } - - public void testStructuredNameUsePrimaryV21() { - testStructuredNameUsePrimaryCommon(V21); - } - - public void testStructuredNameUsePrimaryV30() { - testStructuredNameUsePrimaryCommon(V30); - } - - /** - * Tests that only "super primary" StructuredName is emitted. - * See also the comment in {@link #testStructuredNameUsePrimaryCommon(int)}. - */ - private void testStructuredNameUseSuperPrimaryCommon(int vcardType) { - final boolean isV30 = (vcardType == V30); - mVerifier.initForExportTest(vcardType); - ContactEntry entry = mVerifier.addInputEntry(); - entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE) - .put(StructuredName.FAMILY_NAME, "DoNotEmitFamilyName1") - .put(StructuredName.GIVEN_NAME, "DoNotEmitGivenName1") - .put(StructuredName.MIDDLE_NAME, "DoNotEmitMiddleName1") - .put(StructuredName.PREFIX, "DoNotEmitPrefix1") - .put(StructuredName.SUFFIX, "DoNotEmitSuffix1") - .put(StructuredName.PHONETIC_FAMILY_NAME, "DoNotEmitPhoneticFamily1") - .put(StructuredName.PHONETIC_GIVEN_NAME, "DoNotEmitPhoneticGiven1") - .put(StructuredName.PHONETIC_MIDDLE_NAME, "DoNotEmitPhoneticMiddle1"); - - // With "IS_PRIMARY=1", but we should ignore this time. - entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE) - .put(StructuredName.FAMILY_NAME, "DoNotEmitFamilyName2") - .put(StructuredName.GIVEN_NAME, "DoNotEmitGivenName2") - .put(StructuredName.MIDDLE_NAME, "DoNotEmitMiddleName2") - .put(StructuredName.PREFIX, "DoNotEmitPrefix2") - .put(StructuredName.SUFFIX, "DoNotEmitSuffix2") - .put(StructuredName.PHONETIC_FAMILY_NAME, "DoNotEmitPhoneticFamily2") - .put(StructuredName.PHONETIC_GIVEN_NAME, "DoNotEmitPhoneticGiven2") - .put(StructuredName.PHONETIC_MIDDLE_NAME, "DoNotEmitPhoneticMiddle2") - .put(StructuredName.IS_PRIMARY, 1); - - // With "IS_SUPER_PRIMARY=1". This is what we should use. - entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE) - .put(StructuredName.FAMILY_NAME, "AppropriateFamilyName") - .put(StructuredName.GIVEN_NAME, "AppropriateGivenName") - .put(StructuredName.MIDDLE_NAME, "AppropriateMiddleName") - .put(StructuredName.PREFIX, "AppropriatePrefix") - .put(StructuredName.SUFFIX, "AppropriateSuffix") - .put(StructuredName.PHONETIC_FAMILY_NAME, "AppropriatePhoneticFamily") - .put(StructuredName.PHONETIC_GIVEN_NAME, "AppropriatePhoneticGiven") - .put(StructuredName.PHONETIC_MIDDLE_NAME, "AppropriatePhoneticMiddle") - .put(StructuredName.IS_SUPER_PRIMARY, 1); - - entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE) - .put(StructuredName.FAMILY_NAME, "DoNotEmitFamilyName3") - .put(StructuredName.GIVEN_NAME, "DoNotEmitGivenName3") - .put(StructuredName.MIDDLE_NAME, "DoNotEmitMiddleName3") - .put(StructuredName.PREFIX, "DoNotEmitPrefix3") - .put(StructuredName.SUFFIX, "DoNotEmitSuffix3") - .put(StructuredName.PHONETIC_FAMILY_NAME, "DoNotEmitPhoneticFamily3") - .put(StructuredName.PHONETIC_GIVEN_NAME, "DoNotEmitPhoneticGiven3") - .put(StructuredName.PHONETIC_MIDDLE_NAME, "DoNotEmitPhoneticMiddle3") - .put(StructuredName.IS_PRIMARY, 1); - - PropertyNodesVerifierElem elem = mVerifier.addPropertyNodesVerifierElem() - .addExpectedNodeWithOrder("N", - "AppropriateFamilyName;AppropriateGivenName;AppropriateMiddleName;" - + "AppropriatePrefix;AppropriateSuffix", - Arrays.asList("AppropriateFamilyName", "AppropriateGivenName", - "AppropriateMiddleName", "AppropriatePrefix", "AppropriateSuffix")) - .addExpectedNodeWithOrder("FN", - "AppropriatePrefix AppropriateGivenName " - + "AppropriateMiddleName AppropriateFamilyName AppropriateSuffix") - .addExpectedNode("X-PHONETIC-FIRST-NAME", "AppropriatePhoneticGiven") - .addExpectedNode("X-PHONETIC-MIDDLE-NAME", "AppropriatePhoneticMiddle") - .addExpectedNode("X-PHONETIC-LAST-NAME", "AppropriatePhoneticFamily"); - - if (isV30) { - elem.addExpectedNode("SORT-STRING", - "AppropriatePhoneticGiven AppropriatePhoneticMiddle" - + " AppropriatePhoneticFamily"); - } - } - - public void testStructuredNameUseSuperPrimaryV21() { - testStructuredNameUseSuperPrimaryCommon(V21); - } - - public void testStructuredNameUseSuperPrimaryV30() { - testStructuredNameUseSuperPrimaryCommon(V30); - } - - public void testNickNameV30() { - mVerifier.initForExportTest(V30); - mVerifier.addInputEntry().addContentValues(Nickname.CONTENT_ITEM_TYPE) - .put(Nickname.NAME, "Nicky"); - - mVerifier.addPropertyNodesVerifierElemWithEmptyName() - .addExpectedNodeWithOrder("NICKNAME", "Nicky"); - } - - private void testPhoneBasicCommon(int vcardType) { - mVerifier.initForExportTest(vcardType); - mVerifier.addInputEntry().addContentValues(Phone.CONTENT_ITEM_TYPE) - .put(Phone.NUMBER, "1") - .put(Phone.TYPE, Phone.TYPE_HOME); - mVerifier.addPropertyNodesVerifierElemWithEmptyName() - .addExpectedNode("TEL", "1", new TypeSet("HOME")); - } - - public void testPhoneBasicV21() { - testPhoneBasicCommon(V21); - } - - public void testPhoneBasicV30() { - testPhoneBasicCommon(V30); - } - - public void testPhoneRefrainFormatting() { - mVerifier.initForExportTest(V21 | VCardConfig.FLAG_REFRAIN_PHONE_NUMBER_FORMATTING); - mVerifier.addInputEntry().addContentValues(Phone.CONTENT_ITEM_TYPE) - .put(Phone.NUMBER, "1234567890(abcdefghijklmnopqrstuvwxyz)") - .put(Phone.TYPE, Phone.TYPE_HOME); - mVerifier.addPropertyNodesVerifierElemWithEmptyName() - .addExpectedNode("TEL", "1234567890(abcdefghijklmnopqrstuvwxyz)", - new TypeSet("HOME")); - } - - /** - * Tests that vCard composer emits corresponding type param which we expect. - */ - private void testPhoneVariousTypeSupport(int vcardType) { - mVerifier.initForExportTest(vcardType); - ContactEntry entry = mVerifier.addInputEntry(); - entry.addContentValues(Phone.CONTENT_ITEM_TYPE) - .put(Phone.NUMBER, "10") - .put(Phone.TYPE, Phone.TYPE_HOME); - entry.addContentValues(Phone.CONTENT_ITEM_TYPE) - .put(Phone.NUMBER, "20") - .put(Phone.TYPE, Phone.TYPE_WORK); - entry.addContentValues(Phone.CONTENT_ITEM_TYPE) - .put(Phone.NUMBER, "30") - .put(Phone.TYPE, Phone.TYPE_FAX_HOME); - entry.addContentValues(Phone.CONTENT_ITEM_TYPE) - .put(Phone.NUMBER, "40") - .put(Phone.TYPE, Phone.TYPE_FAX_WORK); - entry.addContentValues(Phone.CONTENT_ITEM_TYPE) - .put(Phone.NUMBER, "50") - .put(Phone.TYPE, Phone.TYPE_MOBILE); - entry.addContentValues(Phone.CONTENT_ITEM_TYPE) - .put(Phone.NUMBER, "60") - .put(Phone.TYPE, Phone.TYPE_PAGER); - entry.addContentValues(Phone.CONTENT_ITEM_TYPE) - .put(Phone.NUMBER, "70") - .put(Phone.TYPE, Phone.TYPE_OTHER); - entry.addContentValues(Phone.CONTENT_ITEM_TYPE) - .put(Phone.NUMBER, "80") - .put(Phone.TYPE, Phone.TYPE_CAR); - entry.addContentValues(Phone.CONTENT_ITEM_TYPE) - .put(Phone.NUMBER, "90") - .put(Phone.TYPE, Phone.TYPE_COMPANY_MAIN); - entry.addContentValues(Phone.CONTENT_ITEM_TYPE) - .put(Phone.NUMBER, "100") - .put(Phone.TYPE, Phone.TYPE_ISDN); - entry.addContentValues(Phone.CONTENT_ITEM_TYPE) - .put(Phone.NUMBER, "110") - .put(Phone.TYPE, Phone.TYPE_MAIN); - entry.addContentValues(Phone.CONTENT_ITEM_TYPE) - .put(Phone.NUMBER, "120") - .put(Phone.TYPE, Phone.TYPE_OTHER_FAX); - entry.addContentValues(Phone.CONTENT_ITEM_TYPE) - .put(Phone.NUMBER, "130") - .put(Phone.TYPE, Phone.TYPE_TELEX); - entry.addContentValues(Phone.CONTENT_ITEM_TYPE) - .put(Phone.NUMBER, "140") - .put(Phone.TYPE, Phone.TYPE_WORK_MOBILE); - entry.addContentValues(Phone.CONTENT_ITEM_TYPE) - .put(Phone.NUMBER, "150") - .put(Phone.TYPE, Phone.TYPE_WORK_PAGER); - entry.addContentValues(Phone.CONTENT_ITEM_TYPE) - .put(Phone.NUMBER, "160") - .put(Phone.TYPE, Phone.TYPE_MMS); - - mVerifier.addPropertyNodesVerifierElemWithEmptyName() - .addExpectedNode("TEL", "10", new TypeSet("HOME")) - .addExpectedNode("TEL", "20", new TypeSet("WORK")) - .addExpectedNode("TEL", "30", new TypeSet("HOME", "FAX")) - .addExpectedNode("TEL", "40", new TypeSet("WORK", "FAX")) - .addExpectedNode("TEL", "50", new TypeSet("CELL")) - .addExpectedNode("TEL", "60", new TypeSet("PAGER")) - .addExpectedNode("TEL", "70", new TypeSet("VOICE")) - .addExpectedNode("TEL", "80", new TypeSet("CAR")) - .addExpectedNode("TEL", "90", new TypeSet("WORK", "PREF")) - .addExpectedNode("TEL", "100", new TypeSet("ISDN")) - .addExpectedNode("TEL", "110", new TypeSet("PREF")) - .addExpectedNode("TEL", "120", new TypeSet("FAX")) - .addExpectedNode("TEL", "130", new TypeSet("TLX")) - .addExpectedNode("TEL", "140", new TypeSet("WORK", "CELL")) - .addExpectedNode("TEL", "150", new TypeSet("WORK", "PAGER")) - .addExpectedNode("TEL", "160", new TypeSet("MSG")); - } - - public void testPhoneVariousTypeSupportV21() { - testPhoneVariousTypeSupport(V21); - } - - public void testPhoneVariousTypeSupportV30() { - testPhoneVariousTypeSupport(V30); - } - - /** - * Tests that "PREF"s are emitted appropriately. - */ - private void testPhonePrefHandlingCommon(int vcardType) { - mVerifier.initForExportTest(vcardType); - ContactEntry entry = mVerifier.addInputEntry(); - entry.addContentValues(Phone.CONTENT_ITEM_TYPE) - .put(Phone.NUMBER, "1") - .put(Phone.TYPE, Phone.TYPE_HOME); - entry.addContentValues(Phone.CONTENT_ITEM_TYPE) - .put(Phone.NUMBER, "2") - .put(Phone.TYPE, Phone.TYPE_WORK) - .put(Phone.IS_PRIMARY, 1); - entry.addContentValues(Phone.CONTENT_ITEM_TYPE) - .put(Phone.NUMBER, "3") - .put(Phone.TYPE, Phone.TYPE_FAX_HOME) - .put(Phone.IS_PRIMARY, 1); - entry.addContentValues(Phone.CONTENT_ITEM_TYPE) - .put(Phone.NUMBER, "4") - .put(Phone.TYPE, Phone.TYPE_FAX_WORK); - - mVerifier.addPropertyNodesVerifierElemWithEmptyName() - .addExpectedNode("TEL", "4", new TypeSet("WORK", "FAX")) - .addExpectedNode("TEL", "3", new TypeSet("HOME", "FAX", "PREF")) - .addExpectedNode("TEL", "2", new TypeSet("WORK", "PREF")) - .addExpectedNode("TEL", "1", new TypeSet("HOME")); - } - - public void testPhonePrefHandlingV21() { - testPhonePrefHandlingCommon(V21); - } - - public void testPhonePrefHandlingV30() { - testPhonePrefHandlingCommon(V30); - } - - private void testMiscPhoneTypeHandling(int vcardType) { - mVerifier.initForExportTest(vcardType); - ContactEntry entry = mVerifier.addInputEntry(); - entry.addContentValues(Phone.CONTENT_ITEM_TYPE) - .put(Phone.NUMBER, "1") - .put(Phone.TYPE, Phone.TYPE_CUSTOM) - .put(Phone.LABEL, "Modem"); - entry.addContentValues(Phone.CONTENT_ITEM_TYPE) - .put(Phone.NUMBER, "2") - .put(Phone.TYPE, Phone.TYPE_CUSTOM) - .put(Phone.LABEL, "MSG"); - entry.addContentValues(Phone.CONTENT_ITEM_TYPE) - .put(Phone.NUMBER, "3") - .put(Phone.TYPE, Phone.TYPE_CUSTOM) - .put(Phone.LABEL, "BBS"); - entry.addContentValues(Phone.CONTENT_ITEM_TYPE) - .put(Phone.NUMBER, "4") - .put(Phone.TYPE, Phone.TYPE_CUSTOM) - .put(Phone.LABEL, "VIDEO"); - entry.addContentValues(Phone.CONTENT_ITEM_TYPE) - .put(Phone.NUMBER, "5") - .put(Phone.TYPE, Phone.TYPE_CUSTOM); - entry.addContentValues(Phone.CONTENT_ITEM_TYPE) - .put(Phone.NUMBER, "6") - .put(Phone.TYPE, Phone.TYPE_CUSTOM) - .put(Phone.LABEL, "_AUTO_CELL"); // The old indicator for the type mobile. - entry.addContentValues(Phone.CONTENT_ITEM_TYPE) - .put(Phone.NUMBER, "7") - .put(Phone.TYPE, Phone.TYPE_CUSTOM) - .put(Phone.LABEL, "\u643A\u5E2F"); // Mobile phone in Japanese Kanji - entry.addContentValues(Phone.CONTENT_ITEM_TYPE) - .put(Phone.NUMBER, "8") - .put(Phone.TYPE, Phone.TYPE_CUSTOM) - .put(Phone.LABEL, "invalid"); - PropertyNodesVerifierElem elem = mVerifier.addPropertyNodesVerifierElemWithEmptyName(); - elem.addExpectedNode("TEL", "1", new TypeSet("MODEM")) - .addExpectedNode("TEL", "2", new TypeSet("MSG")) - .addExpectedNode("TEL", "3", new TypeSet("BBS")) - .addExpectedNode("TEL", "4", new TypeSet("VIDEO")) - .addExpectedNode("TEL", "5", new TypeSet("VOICE")) - .addExpectedNode("TEL", "6", new TypeSet("CELL")) - .addExpectedNode("TEL", "7", new TypeSet("CELL")) - .addExpectedNode("TEL", "8", new TypeSet("X-invalid")); - } - - public void testPhoneTypeHandlingV21() { - testMiscPhoneTypeHandling(V21); - } - - public void testPhoneTypeHandlingV30() { - testMiscPhoneTypeHandling(V30); - } - - private void testEmailBasicCommon(int vcardType) { - mVerifier.initForExportTest(vcardType); - mVerifier.addInputEntry().addContentValues(Email.CONTENT_ITEM_TYPE) - .put(Email.DATA, "sample@example.com"); - mVerifier.addPropertyNodesVerifierElemWithEmptyName() - .addExpectedNode("EMAIL", "sample@example.com"); - } - - public void testEmailBasicV21() { - testEmailBasicCommon(V21); - } - - public void testEmailBasicV30() { - testEmailBasicCommon(V30); - } - - private void testEmailVariousTypeSupportCommon(int vcardType) { - mVerifier.initForExportTest(vcardType); - ContactEntry entry = mVerifier.addInputEntry(); - entry.addContentValues(Email.CONTENT_ITEM_TYPE) - .put(Email.DATA, "type_home@example.com") - .put(Email.TYPE, Email.TYPE_HOME); - entry.addContentValues(Email.CONTENT_ITEM_TYPE) - .put(Email.DATA, "type_work@example.com") - .put(Email.TYPE, Email.TYPE_WORK); - entry.addContentValues(Email.CONTENT_ITEM_TYPE) - .put(Email.DATA, "type_mobile@example.com") - .put(Email.TYPE, Email.TYPE_MOBILE); - entry.addContentValues(Email.CONTENT_ITEM_TYPE) - .put(Email.DATA, "type_other@example.com") - .put(Email.TYPE, Email.TYPE_OTHER); - mVerifier.addPropertyNodesVerifierElemWithEmptyName() - .addExpectedNode("EMAIL", "type_home@example.com", new TypeSet("HOME")) - .addExpectedNode("EMAIL", "type_work@example.com", new TypeSet("WORK")) - .addExpectedNode("EMAIL", "type_mobile@example.com", new TypeSet("CELL")) - .addExpectedNode("EMAIL", "type_other@example.com"); - } - - public void testEmailVariousTypeSupportV21() { - testEmailVariousTypeSupportCommon(V21); - } - - public void testEmailVariousTypeSupportV30() { - testEmailVariousTypeSupportCommon(V30); - } - - private void testEmailPrefHandlingCommon(int vcardType) { - mVerifier.initForExportTest(vcardType); - ContactEntry entry = mVerifier.addInputEntry(); - entry.addContentValues(Email.CONTENT_ITEM_TYPE) - .put(Email.DATA, "type_home@example.com") - .put(Email.TYPE, Email.TYPE_HOME) - .put(Email.IS_PRIMARY, 1); - entry.addContentValues(Email.CONTENT_ITEM_TYPE) - .put(Email.DATA, "type_notype@example.com") - .put(Email.IS_PRIMARY, 1); - - mVerifier.addPropertyNodesVerifierElemWithEmptyName() - .addExpectedNode("EMAIL", "type_notype@example.com", new TypeSet("PREF")) - .addExpectedNode("EMAIL", "type_home@example.com", new TypeSet("HOME", "PREF")); - } - - public void testEmailPrefHandlingV21() { - testEmailPrefHandlingCommon(V21); - } - - public void testEmailPrefHandlingV30() { - testEmailPrefHandlingCommon(V30); - } - - private void testPostalAddressCommon(int vcardType) { - mVerifier.initForExportTest(vcardType); - mVerifier.addInputEntry().addContentValues(StructuredPostal.CONTENT_ITEM_TYPE) - .put(StructuredPostal.POBOX, "Pobox") - .put(StructuredPostal.NEIGHBORHOOD, "Neighborhood") - .put(StructuredPostal.STREET, "Street") - .put(StructuredPostal.CITY, "City") - .put(StructuredPostal.REGION, "Region") - .put(StructuredPostal.POSTCODE, "100") - .put(StructuredPostal.COUNTRY, "Country") - .put(StructuredPostal.FORMATTED_ADDRESS, "Formatted Address") - .put(StructuredPostal.TYPE, StructuredPostal.TYPE_WORK); - // adr-value = 0*6(text-value ";") text-value - // ; PO Box, Extended Address, Street, Locality, Region, Postal Code, - // ; Country Name - // - // The NEIGHBORHOOD field is appended after the CITY field. - mVerifier.addPropertyNodesVerifierElemWithEmptyName() - .addExpectedNode("ADR", - Arrays.asList("Pobox", "", "Street", "City Neighborhood", - "Region", "100", "Country"), new TypeSet("WORK")); - } - - public void testPostalAddressV21() { - testPostalAddressCommon(V21); - } - - public void testPostalAddressV30() { - testPostalAddressCommon(V30); - } - - private void testPostalAddressNonNeighborhood(int vcardType) { - mVerifier.initForExportTest(vcardType); - mVerifier.addInputEntry().addContentValues(StructuredPostal.CONTENT_ITEM_TYPE) - .put(StructuredPostal.CITY, "City"); - mVerifier.addPropertyNodesVerifierElemWithEmptyName() - .addExpectedNode("ADR", - Arrays.asList("", "", "", "City", "", "", ""), new TypeSet("HOME")); - } - - public void testPostalAddressNonNeighborhoodV21() { - testPostalAddressNonNeighborhood(V21); - } - - public void testPostalAddressNonNeighborhoodV30() { - testPostalAddressNonNeighborhood(V30); - } - - private void testPostalAddressNonCity(int vcardType) { - mVerifier.initForExportTest(vcardType); - mVerifier.addInputEntry().addContentValues(StructuredPostal.CONTENT_ITEM_TYPE) - .put(StructuredPostal.NEIGHBORHOOD, "Neighborhood"); - mVerifier.addPropertyNodesVerifierElemWithEmptyName() - .addExpectedNode("ADR", - Arrays.asList("", "", "", "Neighborhood", "", "", ""), new TypeSet("HOME")); - } - - public void testPostalAddressNonCityV21() { - testPostalAddressNonCity(V21); - } - - public void testPostalAddressNonCityV30() { - testPostalAddressNonCity(V30); - } - - private void testPostalOnlyWithFormattedAddressCommon(int vcardType) { - mVerifier.initForExportTest(vcardType); - mVerifier.addInputEntry().addContentValues(StructuredPostal.CONTENT_ITEM_TYPE) - .put(StructuredPostal.REGION, "") // Must be ignored. - .put(StructuredPostal.FORMATTED_ADDRESS, - "Formatted address CA 123-334 United Statue"); - mVerifier.addPropertyNodesVerifierElemWithEmptyName() - .addExpectedNodeWithOrder("ADR", ";Formatted address CA 123-334 United Statue;;;;;", - Arrays.asList("", "Formatted address CA 123-334 United Statue", - "", "", "", "", ""), new TypeSet("HOME")); - } - - public void testPostalOnlyWithFormattedAddressV21() { - testPostalOnlyWithFormattedAddressCommon(V21); - } - - public void testPostalOnlyWithFormattedAddressV30() { - testPostalOnlyWithFormattedAddressCommon(V30); - } - - /** - * Tests that the vCard composer honors formatted data when it is available - * even when it is partial. - */ - private void testPostalWithBothStructuredAndFormattedCommon(int vcardType) { - mVerifier.initForExportTest(vcardType); - mVerifier.addInputEntry().addContentValues(StructuredPostal.CONTENT_ITEM_TYPE) - .put(StructuredPostal.POBOX, "Pobox") - .put(StructuredPostal.COUNTRY, "Country") - .put(StructuredPostal.FORMATTED_ADDRESS, - "Formatted address CA 123-334 United Statue"); // Should be ignored - mVerifier.addPropertyNodesVerifierElemWithEmptyName() - .addExpectedNode("ADR", "Pobox;;;;;;Country", - Arrays.asList("Pobox", "", "", "", "", "", "Country"), - new TypeSet("HOME")); - } - - public void testPostalWithBothStructuredAndFormattedV21() { - testPostalWithBothStructuredAndFormattedCommon(V21); - } - - public void testPostalWithBothStructuredAndFormattedV30() { - testPostalWithBothStructuredAndFormattedCommon(V30); - } - - private void testOrganizationCommon(int vcardType) { - mVerifier.initForExportTest(vcardType); - ContactEntry entry = mVerifier.addInputEntry(); - entry.addContentValues(Organization.CONTENT_ITEM_TYPE) - .put(Organization.COMPANY, "CompanyX") - .put(Organization.DEPARTMENT, "DepartmentY") - .put(Organization.TITLE, "TitleZ") - .put(Organization.JOB_DESCRIPTION, "Description Rambda") // Ignored. - .put(Organization.OFFICE_LOCATION, "Mountain View") // Ignored. - .put(Organization.PHONETIC_NAME, "PhoneticName!") // Ignored - .put(Organization.SYMBOL, "(^o^)/~~"); // Ignore him (her). - entry.addContentValues(Organization.CONTENT_ITEM_TYPE) - .putNull(Organization.COMPANY) - .put(Organization.DEPARTMENT, "DepartmentXX") - .putNull(Organization.TITLE); - entry.addContentValues(Organization.CONTENT_ITEM_TYPE) - .put(Organization.COMPANY, "CompanyXYZ") - .putNull(Organization.DEPARTMENT) - .put(Organization.TITLE, "TitleXYZYX"); - // Currently we do not use group but depend on the order. - mVerifier.addPropertyNodesVerifierElemWithEmptyName() - .addExpectedNodeWithOrder("ORG", "CompanyX;DepartmentY", - Arrays.asList("CompanyX", "DepartmentY")) - .addExpectedNodeWithOrder("TITLE", "TitleZ") - .addExpectedNodeWithOrder("ORG", "DepartmentXX") - .addExpectedNodeWithOrder("ORG", "CompanyXYZ") - .addExpectedNodeWithOrder("TITLE", "TitleXYZYX"); - } - - public void testOrganizationV21() { - testOrganizationCommon(V21); - } - - public void testOrganizationV30() { - testOrganizationCommon(V30); - } - - private void testImVariousTypeSupportCommon(int vcardType) { - mVerifier.initForExportTest(vcardType); - ContactEntry entry = mVerifier.addInputEntry(); - entry.addContentValues(Im.CONTENT_ITEM_TYPE) - .put(Im.PROTOCOL, Im.PROTOCOL_AIM) - .put(Im.DATA, "aim"); - entry.addContentValues(Im.CONTENT_ITEM_TYPE) - .put(Im.PROTOCOL, Im.PROTOCOL_MSN) - .put(Im.DATA, "msn"); - entry.addContentValues(Im.CONTENT_ITEM_TYPE) - .put(Im.PROTOCOL, Im.PROTOCOL_YAHOO) - .put(Im.DATA, "yahoo"); - entry.addContentValues(Im.CONTENT_ITEM_TYPE) - .put(Im.PROTOCOL, Im.PROTOCOL_SKYPE) - .put(Im.DATA, "skype"); - entry.addContentValues(Im.CONTENT_ITEM_TYPE) - .put(Im.PROTOCOL, Im.PROTOCOL_QQ) - .put(Im.DATA, "qq"); - entry.addContentValues(Im.CONTENT_ITEM_TYPE) - .put(Im.PROTOCOL, Im.PROTOCOL_GOOGLE_TALK) - .put(Im.DATA, "google talk"); - entry.addContentValues(Im.CONTENT_ITEM_TYPE) - .put(Im.PROTOCOL, Im.PROTOCOL_ICQ) - .put(Im.DATA, "icq"); - entry.addContentValues(Im.CONTENT_ITEM_TYPE) - .put(Im.PROTOCOL, Im.PROTOCOL_JABBER) - .put(Im.DATA, "jabber"); - entry.addContentValues(Im.CONTENT_ITEM_TYPE) - .put(Im.PROTOCOL, Im.PROTOCOL_NETMEETING) - .put(Im.DATA, "netmeeting"); - - // No determined way to express unknown type... - mVerifier.addPropertyNodesVerifierElemWithEmptyName() - .addExpectedNode("X-JABBER", "jabber") - .addExpectedNode("X-ICQ", "icq") - .addExpectedNode("X-GOOGLE-TALK", "google talk") - .addExpectedNode("X-QQ", "qq") - .addExpectedNode("X-SKYPE-USERNAME", "skype") - .addExpectedNode("X-YAHOO", "yahoo") - .addExpectedNode("X-MSN", "msn") - .addExpectedNode("X-NETMEETING", "netmeeting") - .addExpectedNode("X-AIM", "aim"); - } - - public void testImBasiV21() { - testImVariousTypeSupportCommon(V21); - } - - public void testImBasicV30() { - testImVariousTypeSupportCommon(V30); - } - - private void testImPrefHandlingCommon(int vcardType) { - mVerifier.initForExportTest(vcardType); - ContactEntry entry = mVerifier.addInputEntry(); - entry.addContentValues(Im.CONTENT_ITEM_TYPE) - .put(Im.PROTOCOL, Im.PROTOCOL_AIM) - .put(Im.DATA, "aim1"); - entry.addContentValues(Im.CONTENT_ITEM_TYPE) - .put(Im.PROTOCOL, Im.PROTOCOL_AIM) - .put(Im.DATA, "aim2") - .put(Im.TYPE, Im.TYPE_HOME) - .put(Im.IS_PRIMARY, 1); - - mVerifier.addPropertyNodesVerifierElemWithEmptyName() - .addExpectedNode("X-AIM", "aim1") - .addExpectedNode("X-AIM", "aim2", new TypeSet("HOME", "PREF")); - } - - public void testImPrefHandlingV21() { - testImPrefHandlingCommon(V21); - } - - public void testImPrefHandlingV30() { - testImPrefHandlingCommon(V30); - } - - private void testWebsiteCommon(int vcardType) { - mVerifier.initForExportTest(vcardType); - ContactEntry entry = mVerifier.addInputEntry(); - entry.addContentValues(Website.CONTENT_ITEM_TYPE) - .put(Website.URL, "http://website.example.android.com/index.html") - .put(Website.TYPE, Website.TYPE_BLOG); - entry.addContentValues(Website.CONTENT_ITEM_TYPE) - .put(Website.URL, "ftp://ftp.example.android.com/index.html") - .put(Website.TYPE, Website.TYPE_FTP); - - // We drop TYPE information since vCard (especially 3.0) does not allow us to emit it. - mVerifier.addPropertyNodesVerifierElemWithEmptyName() - .addExpectedNode("URL", "ftp://ftp.example.android.com/index.html") - .addExpectedNode("URL", "http://website.example.android.com/index.html"); - } - - public void testWebsiteV21() { - testWebsiteCommon(V21); - } - - public void testWebsiteV30() { - testWebsiteCommon(V30); - } - - private String getAndroidPropValue(final String mimeType, String value, Integer type) { - return getAndroidPropValue(mimeType, value, type, null); - } - - private String getAndroidPropValue(final String mimeType, String value, - Integer type, String label) { - return (mimeType + ";" + value + ";" - + (type != null ? type : "") + ";" - + (label != null ? label : "") + ";;;;;;;;;;;;"); - } - - private void testEventCommon(int vcardType) { - mVerifier.initForExportTest(vcardType); - ContactEntry entry = mVerifier.addInputEntry(); - entry.addContentValues(Event.CONTENT_ITEM_TYPE) - .put(Event.TYPE, Event.TYPE_ANNIVERSARY) - .put(Event.START_DATE, "1982-06-16"); - entry.addContentValues(Event.CONTENT_ITEM_TYPE) - .put(Event.TYPE, Event.TYPE_BIRTHDAY) - .put(Event.START_DATE, "2008-10-22"); - entry.addContentValues(Event.CONTENT_ITEM_TYPE) - .put(Event.TYPE, Event.TYPE_OTHER) - .put(Event.START_DATE, "2018-03-12"); - entry.addContentValues(Event.CONTENT_ITEM_TYPE) - .put(Event.TYPE, Event.TYPE_CUSTOM) - .put(Event.LABEL, "The last day") - .put(Event.START_DATE, "When the Tower of Hanoi with 64 rings is completed."); - entry.addContentValues(Event.CONTENT_ITEM_TYPE) - .put(Event.TYPE, Event.TYPE_BIRTHDAY) - .put(Event.START_DATE, "2009-05-19"); // Should be ignored. - mVerifier.addPropertyNodesVerifierElemWithEmptyName() - .addExpectedNode("BDAY", "2008-10-22") - .addExpectedNode("X-ANDROID-CUSTOM", - getAndroidPropValue( - Event.CONTENT_ITEM_TYPE, "1982-06-16", Event.TYPE_ANNIVERSARY)) - .addExpectedNode("X-ANDROID-CUSTOM", - getAndroidPropValue( - Event.CONTENT_ITEM_TYPE, "2018-03-12", Event.TYPE_OTHER)) - .addExpectedNode("X-ANDROID-CUSTOM", - getAndroidPropValue( - Event.CONTENT_ITEM_TYPE, - "When the Tower of Hanoi with 64 rings is completed.", - Event.TYPE_CUSTOM, "The last day")); - } - - public void testEventV21() { - testEventCommon(V21); - } - - public void testEventV30() { - testEventCommon(V30); - } - - private void testNoteCommon(int vcardType) { - mVerifier.initForExportTest(vcardType); - ContactEntry entry = mVerifier.addInputEntry(); - entry.addContentValues(Note.CONTENT_ITEM_TYPE) - .put(Note.NOTE, "note1"); - entry.addContentValues(Note.CONTENT_ITEM_TYPE) - .put(Note.NOTE, "note2") - .put(Note.IS_PRIMARY, 1); // Just ignored. - mVerifier.addPropertyNodesVerifierElemWithEmptyName() - .addExpectedNodeWithOrder("NOTE", "note1") - .addExpectedNodeWithOrder("NOTE", "note2"); - } - - public void testNoteV21() { - testNoteCommon(V21); - } - - public void testNoteV30() { - testNoteCommon(V30); - } - - private void testPhotoCommon(int vcardType) { - final boolean isV30 = vcardType == V30; - mVerifier.initForExportTest(vcardType); - ContactEntry entry = mVerifier.addInputEntry(); - entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE) - .put(StructuredName.FAMILY_NAME, "PhotoTest"); - entry.addContentValues(Photo.CONTENT_ITEM_TYPE) - .put(Photo.PHOTO, sPhotoByteArray); - - ContentValues contentValuesForPhoto = new ContentValues(); - contentValuesForPhoto.put("ENCODING", (isV30 ? "b" : "BASE64")); - mVerifier.addPropertyNodesVerifierElem() - .addExpectedNode("FN", "PhotoTest") - .addExpectedNode("N", "PhotoTest;;;;", - Arrays.asList("PhotoTest", "", "", "", "")) - .addExpectedNodeWithOrder("PHOTO", null, null, sPhotoByteArray, - contentValuesForPhoto, new TypeSet("JPEG"), null); - } - - public void testPhotoV21() { - testPhotoCommon(V21); - } - - public void testPhotoV30() { - testPhotoCommon(V30); - } - - private void testRelationCommon(int vcardType) { - mVerifier.initForExportTest(vcardType); - mVerifier.addInputEntry().addContentValues(Relation.CONTENT_ITEM_TYPE) - .put(Relation.TYPE, Relation.TYPE_MOTHER) - .put(Relation.NAME, "Ms. Mother"); - mVerifier.addContentValuesVerifierElem().addExpected(Relation.CONTENT_ITEM_TYPE) - .put(Relation.TYPE, Relation.TYPE_MOTHER) - .put(Relation.NAME, "Ms. Mother"); - } - - public void testRelationV21() { - testRelationCommon(V21); - } - - public void testRelationV30() { - testRelationCommon(V30); - } - - public void testV30HandleEscape() { - mVerifier.initForExportTest(V30); - mVerifier.addInputEntry().addContentValues(StructuredName.CONTENT_ITEM_TYPE) - .put(StructuredName.FAMILY_NAME, "\\") - .put(StructuredName.GIVEN_NAME, ";") - .put(StructuredName.MIDDLE_NAME, ",") - .put(StructuredName.PREFIX, "\n") - .put(StructuredName.DISPLAY_NAME, "[<{Unescaped:Asciis}>]"); - // Verifies the vCard String correctly escapes each character which must be escaped. - mVerifier.addLineVerifierElem() - .addExpected("N:\\\\;\\;;\\,;\\n;") - .addExpected("FN:[<{Unescaped:Asciis}>]"); - mVerifier.addPropertyNodesVerifierElem() - .addExpectedNode("FN", "[<{Unescaped:Asciis}>]") - .addExpectedNode("N", Arrays.asList("\\", ";", ",", "\n", "")); - } - - /** - * There's no "NICKNAME" property in vCard 2.1, while there is in vCard 3.0. - * We use Android-specific "X-ANDROID-CUSTOM" property. - * This test verifies the functionality. - */ - public void testNickNameV21() { - mVerifier.initForExportTest(V21); - mVerifier.addInputEntry().addContentValues(Nickname.CONTENT_ITEM_TYPE) - .put(Nickname.NAME, "Nicky"); - mVerifier.addPropertyNodesVerifierElemWithEmptyName() - .addExpectedNode("X-ANDROID-CUSTOM", - Nickname.CONTENT_ITEM_TYPE + ";Nicky;;;;;;;;;;;;;;"); - mVerifier.addContentValuesVerifierElem().addExpected(Nickname.CONTENT_ITEM_TYPE) - .put(Nickname.NAME, "Nicky"); - } - - public void testTolerateBrokenPhoneNumberEntryV21() { - mVerifier.initForExportTest(V21); - ContactEntry entry = mVerifier.addInputEntry(); - entry.addContentValues(Phone.CONTENT_ITEM_TYPE) - .put(Phone.TYPE, Phone.TYPE_HOME) - .put(Phone.NUMBER, "111-222-3333 (Miami)\n444-5555-666 (Tokyo);" - + "777-888-9999 (Chicago);111-222-3333 (Miami)"); - mVerifier.addPropertyNodesVerifierElemWithEmptyName() - .addExpectedNode("TEL", "111-222-3333", new TypeSet("HOME")) - .addExpectedNode("TEL", "444-555-5666", new TypeSet("HOME")) - .addExpectedNode("TEL", "777-888-9999", new TypeSet("HOME")); - } - - private void testPickUpNonEmptyContentValuesCommon(int vcardType) { - mVerifier.initForExportTest(vcardType); - ContactEntry entry = mVerifier.addInputEntry(); - entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE) - .put(StructuredName.IS_PRIMARY, 1); // Empty name. Should be ignored. - entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE) - .put(StructuredName.FAMILY_NAME, "family1"); // Not primary. Should be ignored. - entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE) - .put(StructuredName.IS_PRIMARY, 1) - .put(StructuredName.FAMILY_NAME, "family2"); // This entry is what we want. - entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE) - .put(StructuredName.IS_PRIMARY, 1) - .put(StructuredName.FAMILY_NAME, "family3"); - entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE) - .put(StructuredName.FAMILY_NAME, "family4"); - mVerifier.addPropertyNodesVerifierElem() - .addExpectedNode("N", Arrays.asList("family2", "", "", "", "")) - .addExpectedNode("FN", "family2"); - } - - public void testPickUpNonEmptyContentValuesV21() { - testPickUpNonEmptyContentValuesCommon(V21); - } - - public void testPickUpNonEmptyContentValuesV30() { - testPickUpNonEmptyContentValuesCommon(V30); - } -} diff --git a/vcard/tests/src/com/android/vcard/tests/VCardImporterTests.java b/vcard/tests/src/com/android/vcard/tests/VCardImporterTests.java deleted file mode 100644 index 045c0d94e..000000000 --- a/vcard/tests/src/com/android/vcard/tests/VCardImporterTests.java +++ /dev/null @@ -1,1008 +0,0 @@ -/* - * 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.vcard.tests; - -import android.content.ContentValues; -import android.provider.ContactsContract.Data; -import android.provider.ContactsContract.CommonDataKinds.Email; -import android.provider.ContactsContract.CommonDataKinds.Event; -import android.provider.ContactsContract.CommonDataKinds.Note; -import android.provider.ContactsContract.CommonDataKinds.Organization; -import android.provider.ContactsContract.CommonDataKinds.Phone; -import android.provider.ContactsContract.CommonDataKinds.Photo; -import android.provider.ContactsContract.CommonDataKinds.StructuredName; -import android.provider.ContactsContract.CommonDataKinds.StructuredPostal; -import android.provider.ContactsContract.CommonDataKinds.Website; - -import com.android.vcard.VCardConfig; -import com.android.vcard.tests.test_utils.ContentValuesVerifier; -import com.android.vcard.tests.test_utils.ContentValuesVerifierElem; -import com.android.vcard.tests.test_utils.PropertyNodesVerifierElem.TypeSet; - -import java.util.Arrays; - -public class VCardImporterTests extends VCardTestsBase { - // Push data into int array at first since values like 0x80 are - // interpreted as int by the compiler and casting all of them is - // cumbersome... - private static final int[] sPhotoIntArrayForComplicatedCase = { - 0xff, 0xd8, 0xff, 0xe1, 0x0a, 0x0f, 0x45, 0x78, 0x69, 0x66, 0x00, - 0x00, 0x4d, 0x4d, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0d, - 0x01, 0x0e, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, - 0xaa, 0x01, 0x0f, 0x00, 0x02, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, - 0x00, 0xba, 0x01, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x06, 0x00, - 0x00, 0x00, 0xc2, 0x01, 0x12, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x00, 0x01, 0x1a, 0x00, 0x05, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0xc8, 0x01, 0x1b, 0x00, 0x05, 0x00, 0x00, - 0x00, 0x01, 0x00, 0x00, 0x00, 0xd0, 0x01, 0x28, 0x00, 0x03, 0x00, - 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x01, 0x31, 0x00, 0x02, - 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0xd8, 0x01, 0x32, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0xe6, 0x02, 0x13, - 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x82, - 0x98, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0xfa, - 0x87, 0x69, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, - 0x84, 0xc4, 0xa5, 0x00, 0x07, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, - 0x01, 0x08, 0x00, 0x00, 0x04, 0x1e, 0x32, 0x30, 0x30, 0x38, 0x31, - 0x30, 0x32, 0x39, 0x31, 0x33, 0x35, 0x35, 0x33, 0x31, 0x00, 0x00, - 0x44, 0x6f, 0x43, 0x6f, 0x4d, 0x6f, 0x00, 0x00, 0x44, 0x39, 0x30, - 0x35, 0x69, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x01, 0x44, 0x39, 0x30, - 0x35, 0x69, 0x20, 0x56, 0x65, 0x72, 0x31, 0x2e, 0x30, 0x30, 0x00, - 0x32, 0x30, 0x30, 0x38, 0x3a, 0x31, 0x30, 0x3a, 0x32, 0x39, 0x20, - 0x31, 0x33, 0x3a, 0x35, 0x35, 0x3a, 0x34, 0x37, 0x00, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x00, 0x50, 0x72, 0x69, 0x6e, 0x74, 0x49, 0x4d, 0x00, 0x30, 0x33, - 0x30, 0x30, 0x00, 0x00, 0x00, 0x06, 0x00, 0x01, 0x00, 0x14, 0x00, - 0x14, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, - 0x00, 0x34, 0x01, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, - 0x00, 0x00, 0x00, 0x01, 0x10, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x11, 0x09, 0x00, 0x00, 0x27, 0x10, 0x00, 0x00, 0x0f, 0x0b, 0x00, - 0x00, 0x27, 0x10, 0x00, 0x00, 0x05, 0x97, 0x00, 0x00, 0x27, 0x10, - 0x00, 0x00, 0x08, 0xb0, 0x00, 0x00, 0x27, 0x10, 0x00, 0x00, 0x1c, - 0x01, 0x00, 0x00, 0x27, 0x10, 0x00, 0x00, 0x02, 0x5e, 0x00, 0x00, - 0x27, 0x10, 0x00, 0x00, 0x00, 0x8b, 0x00, 0x00, 0x27, 0x10, 0x00, - 0x00, 0x03, 0xcb, 0x00, 0x00, 0x27, 0x10, 0x00, 0x00, 0x1b, 0xe5, - 0x00, 0x00, 0x27, 0x10, 0x00, 0x28, 0x82, 0x9a, 0x00, 0x05, 0x00, - 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0x6a, 0x82, 0x9d, 0x00, 0x05, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0x72, 0x88, 0x22, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x90, 0x00, - 0x00, 0x07, 0x00, 0x00, 0x00, 0x04, 0x30, 0x32, 0x32, 0x30, 0x90, - 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x03, 0x7a, - 0x90, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x03, - 0x8e, 0x91, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x04, 0x01, 0x02, - 0x03, 0x00, 0x91, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, - 0x00, 0x03, 0xa2, 0x92, 0x01, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x03, 0xaa, 0x92, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x03, 0xb2, 0x92, 0x04, 0x00, 0x0a, 0x00, 0x00, - 0x00, 0x01, 0x00, 0x00, 0x03, 0xba, 0x92, 0x05, 0x00, 0x05, 0x00, - 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0xc2, 0x92, 0x07, 0x00, 0x03, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x92, 0x08, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x92, 0x09, - 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x92, - 0x0a, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0xca, - 0x92, 0x7c, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x92, 0x86, 0x00, 0x07, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, - 0x03, 0xd2, 0xa0, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x04, 0x30, - 0x31, 0x30, 0x30, 0xa0, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x00, 0xa0, 0x02, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x60, 0x00, 0x00, 0xa0, 0x03, 0x00, 0x03, 0x00, 0x00, - 0x00, 0x01, 0x00, 0x48, 0x00, 0x00, 0xa0, 0x05, 0x00, 0x04, 0x00, - 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x00, 0xa2, 0x0e, 0x00, 0x05, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0xe8, 0xa2, 0x0f, 0x00, - 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0xf0, 0xa2, 0x10, - 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0xa2, - 0x17, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, - 0xa3, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, - 0x00, 0xa3, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, - 0x00, 0x00, 0xa4, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, - 0x00, 0x00, 0x00, 0xa4, 0x02, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x00, 0xa4, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x04, 0x00, 0x05, 0x00, 0x00, - 0x00, 0x01, 0x00, 0x00, 0x03, 0xf8, 0xa4, 0x05, 0x00, 0x03, 0x00, - 0x00, 0x00, 0x01, 0x00, 0x1d, 0x00, 0x00, 0xa4, 0x06, 0x00, 0x03, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x07, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x08, - 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xa4, - 0x09, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, - 0xa4, 0x0a, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x00, 0xa4, 0x0c, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, 0x00, - 0x00, 0x27, 0x10, 0x00, 0x00, 0x01, 0x5e, 0x00, 0x00, 0x00, 0x64, - 0x32, 0x30, 0x30, 0x38, 0x3a, 0x31, 0x30, 0x3a, 0x32, 0x39, 0x20, - 0x31, 0x33, 0x3a, 0x35, 0x35, 0x3a, 0x33, 0x31, 0x00, 0x32, 0x30, - 0x30, 0x38, 0x3a, 0x31, 0x30, 0x3a, 0x32, 0x39, 0x20, 0x31, 0x33, - 0x3a, 0x35, 0x35, 0x3a, 0x34, 0x37, 0x00, 0x00, 0x00, 0x29, 0x88, - 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x02, 0xb2, 0x00, 0x00, 0x00, - 0x64, 0x00, 0x00, 0x01, 0x5e, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x25, 0x00, - 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0e, 0x92, 0x00, 0x00, 0x03, 0xe8, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x30, 0x30, - 0x38, 0x31, 0x30, 0x32, 0x39, 0x31, 0x33, 0x35, 0x35, 0x33, 0x31, - 0x00, 0x00, 0x20, 0x2a, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x2a, - 0xe2, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x04, 0x52, 0x39, 0x38, 0x00, 0x00, 0x02, 0x00, 0x07, 0x00, 0x00, - 0x00, 0x04, 0x30, 0x31, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x06, 0x01, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x06, - 0x00, 0x00, 0x01, 0x1a, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, - 0x00, 0x04, 0x6c, 0x01, 0x1b, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x04, 0x74, 0x01, 0x28, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x02, 0x00, 0x00, 0x02, 0x01, 0x00, 0x04, 0x00, 0x00, - 0x00, 0x01, 0x00, 0x00, 0x04, 0x7c, 0x02, 0x02, 0x00, 0x04, 0x00, - 0x00, 0x00, 0x01, 0x00, 0x00, 0x05, 0x8b, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x48, 0x00, 0x00, 0x00, 0x01, 0xff, 0xd8, 0xff, 0xdb, 0x00, 0x84, - 0x00, 0x20, 0x16, 0x18, 0x1c, 0x18, 0x14, 0x20, 0x1c, 0x1a, 0x1c, - 0x24, 0x22, 0x20, 0x26, 0x30, 0x50, 0x34, 0x30, 0x2c, 0x2c, 0x30, - 0x62, 0x46, 0x4a, 0x3a, 0x50, 0x74, 0x66, 0x7a, 0x78, 0x72, 0x66, - 0x70, 0x6e, 0x80, 0x90, 0xb8, 0x9c, 0x80, 0x88, 0xae, 0x8a, 0x6e, - 0x70, 0xa0, 0xda, 0xa2, 0xae, 0xbe, 0xc4, 0xce, 0xd0, 0xce, 0x7c, - 0x9a, 0xe2, 0xf2, 0xe0, 0xc8, 0xf0, 0xb8, 0xca, 0xce, 0xc6, 0x01, - 0x22, 0x24, 0x24, 0x30, 0x2a, 0x30, 0x5e, 0x34, 0x34, 0x5e, 0xc6, - 0x84, 0x70, 0x84, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, - 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, - 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, - 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, - 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xff, 0xc0, - 0x00, 0x11, 0x08, 0x00, 0x78, 0x00, 0xa0, 0x03, 0x01, 0x21, 0x00, - 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, 0xff, 0xc4, 0x01, 0xa2, 0x00, - 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, - 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x10, 0x00, 0x02, 0x01, 0x03, - 0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, - 0x7d, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, - 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, - 0x91, 0xa1, 0x08, 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, - 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, - 0x1a, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, - 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, - 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, - 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, - 0x79, 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, - 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, - 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, - 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, - 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, - 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1, - 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0x01, 0x00, - 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, - 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x11, 0x00, 0x02, 0x01, 0x02, 0x04, - 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77, - 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, - 0x41, 0x51, 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, - 0x42, 0x91, 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15, - 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, - 0x18, 0x19, 0x1a, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, - 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, - 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, - 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, - 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, - 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, - 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, - 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, - 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, - 0xda, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2, - 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xff, 0xda, 0x00, - 0x0c, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00, - 0x14, 0x54, 0xaa, 0x2a, 0x46, 0x48, 0xa2, 0xa4, 0x55, 0xa6, 0x04, - 0x8a, 0x29, 0xe0, 0x53, 0x10, 0xe0, 0x29, 0xc0, 0x50, 0x03, 0xb1, - 0x46, 0x29, 0x80, 0x84, 0x52, 0x11, 0x40, 0x0d, 0x22, 0x9a, 0x45, - 0x20, 0x23, 0x61, 0x51, 0x30, 0xa0, 0x08, 0xc8, 0xa8, 0xd8, 0x52, - 0x02, 0x26, 0x15, 0x0b, 0x0a, 0x00, 0xb4, 0xa2, 0xa5, 0x5a, 0x00, - 0x91, 0x45, 0x4a, 0xa2, 0x81, 0x92, 0x01, 0x4e, 0x02, 0x98, 0x87, - 0x0a, 0x70, 0xa0, 0x07, 0x62, 0x8c, 0x50, 0x21, 0x0d, 0x25, 0x00, - 0x34, 0x8a, 0x61, 0x14, 0x0c, 0x63, 0x0a, 0x89, 0x85, 0x00, 0x46, - 0xd5, 0x1b, 0x52, 0x02, 0x16, 0xa8, 0x98, 0x50, 0x05, 0x94, 0xa9, - 0x16, 0x80, 0x25, 0x5a, 0x95, 0x68, 0x18, 0xf1, 0x4f, 0x14, 0xc4, - 0x3b, 0xb5, 0x22, 0xb6, 0x38, 0x34, 0x00, 0xe3, 0x22, 0x8e, 0xf4, - 0x79, 0x8a, 0x7b, 0xd1, 0x71, 0x03, 0x30, 0xc7, 0x14, 0x83, 0xa5, - 0x00, 0x06, 0x98, 0x68, 0x01, 0x8d, 0x51, 0x35, 0x03, 0x22, 0x6a, - 0x8d, 0xa9, 0x01, 0x13, 0x54, 0x4d, 0x40, 0x13, 0xa5, 0x4a, 0x28, - 0x02, 0x45, 0x35, 0x2a, 0x9a, 0x00, 0x78, 0x34, 0xf0, 0x69, 0x80, - 0x34, 0x81, 0x45, 0x40, 0xce, 0x58, 0xe6, 0xa2, 0x4c, 0x06, 0xe4, - 0xfa, 0xd1, 0x93, 0x50, 0x21, 0xca, 0xe4, 0x55, 0x84, 0x90, 0x30, - 0xab, 0x8b, 0x18, 0xa6, 0x9a, 0x6a, 0xc4, 0x31, 0xaa, 0x26, 0xa0, - 0x64, 0x4d, 0x51, 0xb5, 0x20, 0x23, 0x6a, 0x89, 0xa8, 0x02, 0x44, - 0x35, 0x2a, 0x9a, 0x00, 0x95, 0x4d, 0x48, 0xa6, 0x80, 0x24, 0x53, - 0x4e, 0xce, 0x05, 0x30, 0x2b, 0x3b, 0xee, 0x6a, 0x91, 0x5d, 0x76, - 0x63, 0xbd, 0x65, 0x7d, 0x40, 0x66, 0x68, 0xa9, 0x02, 0x45, 0x2b, - 0xb3, 0x9e, 0xb4, 0xc5, 0x6d, 0xad, 0x9a, 0xa0, 0x2c, 0x06, 0xc8, - 0xcd, 0x04, 0xd6, 0xa2, 0x23, 0x63, 0x51, 0xb1, 0xa0, 0x64, 0x4d, - 0x51, 0x93, 0x48, 0x08, 0xda, 0xa2, 0x6a, 0x00, 0x72, 0x1a, 0x99, - 0x4d, 0x00, 0x48, 0xa6, 0xa4, 0x53, 0x4c, 0x07, 0x86, 0x03, 0xbd, - 0x2b, 0x9c, 0xa7, 0x14, 0x98, 0x10, 0x85, 0x34, 0xe0, 0xa6, 0xb3, - 0xb0, 0x0b, 0xb5, 0xa8, 0x0a, 0xd4, 0x58, 0x42, 0xed, 0x3e, 0x94, - 0xd2, 0xa6, 0x8b, 0x01, 0x34, 0x44, 0xed, 0xe6, 0x9c, 0x4d, 0x6a, - 0x80, 0x8d, 0x8d, 0x46, 0xc6, 0x80, 0x23, 0x63, 0x51, 0x9a, 0x06, - 0x46, 0xd5, 0x13, 0x52, 0x01, 0x54, 0xd4, 0xaa, 0x68, 0x02, 0x40, - 0x6a, 0x40, 0x78, 0xa0, 0x08, 0x59, 0xce, 0xee, 0xb5, 0x2a, 0x39, - 0xd9, 0x59, 0xa7, 0xa8, 0x00, 0x73, 0xeb, 0x4e, 0x0e, 0x7d, 0x69, - 0x5c, 0x05, 0xf3, 0x0f, 0xad, 0x1e, 0x61, 0xf5, 0xa7, 0x71, 0x0b, - 0xe6, 0x35, 0x21, 0x90, 0xd3, 0xb8, 0x0e, 0x32, 0x10, 0x95, 0x10, - 0x91, 0xb3, 0xd6, 0x9b, 0x60, 0x4b, 0x9c, 0x8a, 0x63, 0x1a, 0xb0, - 0x18, 0x4d, 0x46, 0xc6, 0x80, 0x22, 0x6a, 0x61, 0xa4, 0x31, 0xaa, - 0x6a, 0x55, 0x34, 0x01, 0x2a, 0x9a, 0x7e, 0x78, 0xa0, 0x08, 0x09, - 0xf9, 0xaa, 0x58, 0xcf, 0xca, 0x6b, 0x3e, 0xa0, 0x00, 0xd3, 0x81, - 0xa9, 0x01, 0x73, 0x46, 0x69, 0x80, 0xb9, 0xa4, 0xcd, 0x00, 0x2b, - 0x1f, 0x92, 0xa3, 0x07, 0x9a, 0x6f, 0x70, 0x26, 0xcf, 0x14, 0xd2, - 0x6b, 0x51, 0x0c, 0x63, 0x51, 0xb1, 0xa0, 0x08, 0xda, 0x98, 0x69, - 0x0c, 0x8d, 0x4d, 0x4a, 0xa6, 0x80, 0x24, 0x53, 0x52, 0x03, 0xc5, - 0x02, 0x21, 0x27, 0xe6, 0xa9, 0x23, 0x3f, 0x29, 0xac, 0xfa, 0x8c, - 0x01, 0xe6, 0x9c, 0x0d, 0x48, 0x0a, 0x0d, 0x2e, 0x68, 0x01, 0x73, - 0x49, 0x9a, 0x60, 0x2b, 0x1f, 0x92, 0x98, 0x3a, 0xd3, 0x7b, 0x81, - 0x36, 0x78, 0xa6, 0x93, 0x5a, 0x88, 0x8c, 0x9a, 0x63, 0x1a, 0x00, - 0x8c, 0xd3, 0x0d, 0x21, 0x91, 0x29, 0xa9, 0x14, 0xd0, 0x04, 0x8a, - 0x69, 0xe0, 0xd3, 0x11, 0x1b, 0x1e, 0x6a, 0x48, 0xcf, 0xca, 0x6b, - 0x3e, 0xa3, 0x10, 0x1a, 0x70, 0x35, 0x20, 0x38, 0x1a, 0x5c, 0xd2, - 0x01, 0x73, 0x49, 0x9a, 0x60, 0x39, 0x8f, 0xca, 0x29, 0x8b, 0xf7, - 0xaa, 0xba, 0x88, 0x96, 0x9a, 0x6b, 0x40, 0x18, 0xc6, 0xa3, 0x26, - 0x80, 0x18, 0x69, 0xa6, 0x90, 0xc8, 0x14, 0xd4, 0x8a, 0x69, 0x80, - 0xf0, 0x6a, 0x40, 0x68, 0x10, 0xbb, 0x41, 0xa7, 0xe3, 0x0b, 0xc5, - 0x2b, 0x01, 0x10, 0xa7, 0x03, 0x59, 0x0c, 0x76, 0x69, 0x73, 0x40, - 0x0b, 0x9a, 0x28, 0x11, 0x28, 0x19, 0x5e, 0x69, 0x02, 0x81, 0x5a, - 0xd8, 0x00, 0xd3, 0x4d, 0x50, 0x0c, 0x6a, 0x8c, 0xd2, 0x01, 0xa6, - 0x98, 0x69, 0x0c, 0xae, 0xa6, 0xa4, 0x06, 0x80, 0x1e, 0xa6, 0x9e, - 0x0d, 0x31, 0x12, 0x03, 0x4f, 0x06, 0x80, 0x13, 0x60, 0x34, 0xd3, - 0xc1, 0xa8, 0x92, 0x01, 0xf1, 0x8d, 0xdd, 0x69, 0xcc, 0xa1, 0x69, - 0x5b, 0x4b, 0x80, 0x83, 0x93, 0x52, 0x04, 0x14, 0xe2, 0xae, 0x03, - 0xa9, 0x0d, 0x68, 0x03, 0x4d, 0x34, 0xd0, 0x03, 0x0d, 0x30, 0xd2, - 0x01, 0x86, 0x9a, 0x68, 0x19, 0x58, 0x1a, 0x78, 0xa4, 0x04, 0x8a, - 0x69, 0xe0, 0xd3, 0x10, 0xe0, 0x69, 0xe0, 0xd0, 0x03, 0xc1, 0xa8, - 0xdb, 0xad, 0x4c, 0x81, 0x12, 0x45, 0xd6, 0x9d, 0x25, 0x1d, 0x00, - 0x6a, 0xf5, 0xa9, 0xe8, 0x80, 0x31, 0x29, 0x0d, 0x58, 0x08, 0x69, - 0x86, 0x80, 0x1a, 0x69, 0x86, 0x90, 0x0c, 0x34, 0xd3, 0x48, 0x65, - 0x51, 0x4f, 0x06, 0x98, 0x0f, 0x14, 0xf0, 0x68, 0x10, 0xf0, 0x69, - 0xe0, 0xd0, 0x03, 0x81, 0xa5, 0x2b, 0x9a, 0x1a, 0xb8, 0x87, 0xa8, - 0xdb, 0x4a, 0x46, 0x68, 0xb6, 0x80, 0x2a, 0xa8, 0x14, 0xea, 0x12, - 0xb0, 0x05, 0x21, 0xa6, 0x02, 0x1a, 0x61, 0xa0, 0x06, 0x9a, 0x61, - 0xa4, 0x31, 0x86, 0x9a, 0x69, 0x0c, 0xa8, 0x0d, 0x3c, 0x53, 0x01, - 0xe2, 0x9e, 0x28, 0x10, 0xf1, 0x4e, 0x06, 0x98, 0x0f, 0x06, 0x9e, - 0x0d, 0x02, 0x1c, 0x29, 0xc2, 0x80, 0x16, 0x96, 0x80, 0x0a, 0x4a, - 0x00, 0x43, 0x4d, 0x34, 0x0c, 0x61, 0xa6, 0x1a, 0x40, 0x34, 0xd3, - 0x4d, 0x21, 0x80, 0xff, 0xd9, 0xff, 0xdb, 0x00, 0x84, 0x00, 0x0a, - 0x07, 0x07, 0x08, 0x07, 0x06, 0x0a, 0x08, 0x08, 0x08, 0x0b, 0x0a, - 0x0a, 0x0b, 0x0e, 0x18, 0x10, 0x0e, 0x0d, 0x0d, 0x0e, 0x1d, 0x15, - 0x16, 0x11, 0x18, 0x23, 0x1f, 0x25, 0x24, 0x22, 0x1f, 0x22, 0x21, - 0x26, 0x2b, 0x37, 0x2f, 0x26, 0x29, 0x34, 0x29, 0x21, 0x22, 0x30, - 0x41, 0x31, 0x34, 0x39, 0x3b, 0x3e, 0x3e, 0x3e, 0x25, 0x2e, 0x44, - 0x49, 0x43, 0x3c, 0x48, 0x37, 0x3d, 0x3e, 0x3b, 0x01, 0x0a, 0x0b, - 0x0b, 0x0e, 0x0d, 0x0e, 0x1c, 0x10, 0x10, 0x1c, 0x3b, 0x28, 0x22, - 0x28, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, - 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, - 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, - 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, - 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0xff, 0xc0, 0x00, 0x11, - 0x08, 0x00, 0x48, 0x00, 0x60, 0x03, 0x01, 0x21, 0x00, 0x02, 0x11, - 0x01, 0x03, 0x11, 0x01, 0xff, 0xc4, 0x01, 0xa2, 0x00, 0x00, 0x01, - 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0a, 0x0b, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, - 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7d, 0x01, - 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, - 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, - 0x08, 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x24, 0x33, - 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25, - 0x26, 0x27, 0x28, 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, - 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, - 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, - 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, - 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, - 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, - 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, - 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, - 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, - 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1, 0xf2, 0xf3, - 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0x01, 0x00, 0x03, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, - 0x09, 0x0a, 0x0b, 0x11, 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, - 0x04, 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77, 0x00, 0x01, - 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, - 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, - 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15, 0x62, 0x72, - 0xd1, 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, - 0x1a, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, 0x39, - 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, - 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, - 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, - 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, - 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, - 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, - 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, - 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe2, - 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4, - 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xff, 0xda, 0x00, 0x0c, 0x03, - 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00, 0x9e, 0xd2, - 0x2e, 0x07, 0x15, 0xaf, 0x6d, 0x08, 0xe2, 0xb3, 0x45, 0x1a, 0xf6, - 0xd0, 0x00, 0x01, 0xc5, 0x68, 0x45, 0x17, 0x4a, 0xb4, 0x22, 0xe4, - 0x70, 0x8c, 0x74, 0xa9, 0x3c, 0xa1, 0x8e, 0x95, 0x48, 0x96, 0x31, - 0xe2, 0x18, 0xe9, 0x55, 0xa5, 0x8c, 0x7a, 0x50, 0x05, 0x0b, 0x88, - 0x86, 0x0f, 0x15, 0x8f, 0x75, 0x1f, 0x26, 0x93, 0x19, 0x91, 0x77, - 0x18, 0xc1, 0xac, 0x4b, 0xc8, 0xfa, 0xd6, 0x63, 0x37, 0x6d, 0x31, - 0xb4, 0x73, 0x5b, 0x36, 0xa0, 0x1c, 0x50, 0x80, 0xd7, 0x83, 0xa0, - 0xab, 0xd1, 0x62, 0xad, 0x09, 0x8f, 0x17, 0x29, 0x03, 0xb2, 0xcc, - 0xe0, 0x77, 0x14, 0xa3, 0x56, 0xb3, 0x27, 0x1e, 0x67, 0xe9, 0x52, - 0xea, 0xc6, 0x3a, 0x36, 0x48, 0xef, 0x3d, 0x27, 0x70, 0x22, 0x60, - 0x47, 0x52, 0x69, 0xb2, 0xe2, 0xad, 0x3b, 0xea, 0x80, 0xa3, 0x38, - 0xe0, 0xd6, 0x3d, 0xd8, 0x1c, 0xd0, 0xca, 0x46, 0x3d, 0xd0, 0x18, - 0x35, 0x89, 0x78, 0xa3, 0x9a, 0xcd, 0x8c, 0xd2, 0xb3, 0x93, 0x2a, - 0x2b, 0x66, 0xd5, 0xf1, 0x8a, 0x10, 0x1a, 0xd6, 0xf2, 0x03, 0x8a, - 0x9e, 0xe6, 0xf4, 0x5a, 0xdb, 0xef, 0xfe, 0x23, 0xc0, 0xa7, 0x27, - 0xcb, 0x16, 0xc4, 0xcc, 0xdd, 0xe2, 0x78, 0x9a, 0x69, 0x66, 0xcc, - 0x99, 0xe1, 0x4d, 0x47, 0xba, 0xbc, 0xd9, 0x6a, 0xee, 0x26, 0x59, - 0x59, 0x4d, 0xac, 0x69, 0x34, 0x52, 0xe5, 0x8f, 0x55, 0xad, 0x58, - 0xae, 0x85, 0xc4, 0x22, 0x41, 0xdf, 0xad, 0x76, 0x61, 0xe5, 0x6f, - 0x74, 0x45, 0x69, 0xdc, 0x00, 0x79, 0xac, 0x8b, 0xa6, 0xc9, 0x35, - 0xd4, 0x34, 0x64, 0xdc, 0x37, 0x06, 0xb1, 0xae, 0x88, 0xc1, 0xac, - 0xd8, 0xc9, 0x2c, 0xa6, 0xe0, 0x73, 0x5b, 0x36, 0xf3, 0x74, 0xe6, - 0x84, 0x05, 0xe3, 0xa9, 0x47, 0x6a, 0x14, 0xb6, 0x49, 0x3d, 0x85, - 0x3a, 0xee, 0xee, 0x2b, 0xa8, 0xe2, 0x6f, 0x30, 0x81, 0xe9, 0x8a, - 0xca, 0xa4, 0xe2, 0xd3, 0x8b, 0x01, 0xb1, 0xf9, 0x04, 0x7f, 0xaf, - 0x23, 0xf0, 0xa9, 0x54, 0x41, 0x9c, 0xfd, 0xa3, 0xf4, 0xae, 0x65, - 0x18, 0xf7, 0x25, 0x8a, 0xe2, 0x02, 0x38, 0xb8, 0xfd, 0x2a, 0x7b, - 0x5b, 0xa8, 0x6d, 0x6d, 0x5d, 0x9a, 0x5d, 0xcb, 0xbb, 0xd2, 0xb6, - 0xa6, 0xa3, 0x19, 0x5e, 0xe2, 0x03, 0x7b, 0x1d, 0xc2, 0x17, 0x8d, - 0xb8, 0xac, 0xfb, 0x89, 0x39, 0x35, 0xd6, 0x9a, 0x6a, 0xe8, 0x66, - 0x55, 0xcb, 0xf5, 0xac, 0x7b, 0x96, 0xeb, 0x50, 0xc6, 0x88, 0x6d, - 0x66, 0xe9, 0xcd, 0x6c, 0xdb, 0x4f, 0xd3, 0x9a, 0x00, 0x2f, 0xe6, - 0xf9, 0xa3, 0xe7, 0xb5, 0x4a, 0x93, 0x7f, 0xa2, 0xc6, 0x73, 0xdc, - 0xd7, 0x15, 0x55, 0xef, 0x48, 0x7d, 0x09, 0x52, 0x6e, 0x3a, 0xd4, - 0xab, 0x2f, 0xbd, 0x61, 0x16, 0x0c, 0x73, 0x49, 0xc5, 0x24, 0x92, - 0x7f, 0xa2, 0x63, 0xfd, 0xaa, 0xd6, 0x2f, 0x71, 0x0e, 0xb1, 0x93, - 0xf7, 0x2d, 0xf5, 0xa4, 0x9e, 0x4e, 0xb5, 0xdd, 0x4b, 0xf8, 0x68, - 0x4c, 0xcb, 0xb9, 0x93, 0xad, 0x65, 0xce, 0xd9, 0x26, 0xa9, 0x8d, - 0x19, 0xf6, 0xf2, 0xf4, 0xe6, 0xb5, 0xad, 0xe7, 0xc6, 0x39, 0xa0, - 0x18, 0xeb, 0xc9, 0x77, 0x6c, 0x35, 0x2a, 0x4b, 0xfe, 0x8a, 0x9c, - 0xff, 0x00, 0x11, 0xae, 0x3a, 0x8b, 0xde, 0x61, 0xd0, 0x9e, 0x39, - 0xb8, 0xeb, 0x53, 0xac, 0xb9, 0xae, 0x5b, 0x00, 0xf3, 0x27, 0x14, - 0x92, 0xc9, 0xfe, 0x8a, 0x3f, 0xde, 0x35, 0xac, 0x3a, 0x88, 0x92, - 0xcd, 0xb1, 0x6e, 0x7d, 0xcd, 0x32, 0x67, 0xeb, 0xcd, 0x7a, 0x14, - 0xfe, 0x04, 0x26, 0x66, 0xce, 0xf9, 0x26, 0xb3, 0xe6, 0x6e, 0xb4, - 0xd9, 0x48, 0xc8, 0x82, 0x4e, 0x07, 0x35, 0xa7, 0x6f, 0x2f, 0x02, - 0x9a, 0x06, 0x5f, 0x8c, 0xa4, 0x83, 0x0e, 0x32, 0x2a, 0x69, 0xe3, - 0xdd, 0x12, 0x08, 0x97, 0x85, 0xec, 0x2a, 0x2a, 0x42, 0xf1, 0x76, - 0x26, 0xe4, 0x6a, 0x59, 0x0e, 0x18, 0x10, 0x6a, 0xd2, 0x89, 0x02, - 0x6e, 0x2a, 0x71, 0xeb, 0x5c, 0x1c, 0x8c, 0xa6, 0x48, 0xbb, 0xdc, - 0x61, 0x41, 0x35, 0x72, 0x28, 0x87, 0xd9, 0xf6, 0x4a, 0xb9, 0xe7, - 0x38, 0xae, 0x8c, 0x3d, 0x36, 0xdd, 0xde, 0xc4, 0xb0, 0x21, 0x51, - 0x76, 0xa8, 0xc0, 0xaa, 0x93, 0x31, 0xe6, 0xbb, 0x2d, 0x65, 0x61, - 0x19, 0xd3, 0x1e, 0xb5, 0x46, 0x5a, 0x96, 0x5a, 0x30, 0xa0, 0x7e, - 0x05, 0x69, 0x5b, 0xc9, 0xc6, 0x28, 0x40, 0xcd, 0x08, 0x64, 0x3c, - 0x73, 0x57, 0xe1, 0x94, 0xf1, 0xcd, 0x5a, 0x21, 0x8c, 0xb9, 0x63, - 0xe7, 0x67, 0x1d, 0xab, 0x40, 0xb1, 0xfb, 0x00, 0x1d, 0xf0, 0x2b, - 0x99, 0x2d, 0x66, 0x3e, 0x88, 0x75, 0x81, 0x3f, 0x31, 0xf6, 0xab, - 0x64, 0xd6, 0xb4, 0x17, 0xee, 0xd0, 0x9e, 0xe4, 0x32, 0x1a, 0xa7, - 0x31, 0xad, 0x18, 0x14, 0x26, 0xef, 0x54, 0xa5, 0xa8, 0x65, 0xa3, - 0x9c, 0x81, 0xfa, 0x56, 0x8c, 0x2d, 0xce, 0x68, 0x40, 0xcb, 0xf1, - 0x37, 0xbd, 0x5e, 0x85, 0xea, 0xd1, 0x0c, 0xbb, 0x19, 0x56, 0x23, - 0x20, 0x1f, 0xad, 0x5c, 0x42, 0x08, 0x03, 0xb5, 0x55, 0x91, 0x04, - 0xc9, 0x80, 0x38, 0x00, 0x0a, 0x71, 0x34, 0x6c, 0x32, 0x27, 0xe9, - 0x55, 0x25, 0x15, 0x2c, 0x68, 0xa3, 0x30, 0xeb, 0x54, 0xa5, 0x15, - 0x0c, 0xd1, 0x00, 0xff, 0xd9}; - - /* package */ static final byte[] sPhotoByteArrayForComplicatedCase; - - static { - final int length = sPhotoIntArrayForComplicatedCase.length; - sPhotoByteArrayForComplicatedCase = new byte[length]; - for (int i = 0; i < length; i++) { - sPhotoByteArrayForComplicatedCase[i] = (byte)sPhotoIntArrayForComplicatedCase[i]; - } - } - - public void testV21SimpleCase1_Parsing() { - mVerifier.initForImportTest(V21, R.raw.v21_simple_1); - mVerifier.addPropertyNodesVerifierElem() - .addExpectedNodeWithOrder("N", "Ando;Roid;", Arrays.asList("Ando", "Roid", "")); - } - - public void testV21SimpleCase1_Type_Generic() { - mVerifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_GENERIC, R.raw.v21_simple_1); - mVerifier.addContentValuesVerifierElem() - .addExpected(StructuredName.CONTENT_ITEM_TYPE) - .put(StructuredName.FAMILY_NAME, "Ando") - .put(StructuredName.GIVEN_NAME, "Roid") - .put(StructuredName.DISPLAY_NAME, "Roid Ando"); - } - - public void testV21SimpleCase1_Type_Japanese() { - mVerifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE, R.raw.v21_simple_1); - mVerifier.addContentValuesVerifierElem() - .addExpected(StructuredName.CONTENT_ITEM_TYPE) - .put(StructuredName.FAMILY_NAME, "Ando") - .put(StructuredName.GIVEN_NAME, "Roid") - // If name-related strings only contains printable Ascii, - // the order is remained to be US's: - // "Prefix Given Middle Family Suffix" - .put(StructuredName.DISPLAY_NAME, "Roid Ando"); - } - - public void testV21SimpleCase2() { - mVerifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE, R.raw.v21_simple_2); - mVerifier.addContentValuesVerifierElem() - .addExpected(StructuredName.CONTENT_ITEM_TYPE) - .put(StructuredName.DISPLAY_NAME, "Ando Roid"); - } - - public void testV21SimpleCase3() { - mVerifier.initForImportTest(V21, R.raw.v21_simple_3); - mVerifier.addContentValuesVerifierElem() - .addExpected(StructuredName.CONTENT_ITEM_TYPE) - .put(StructuredName.FAMILY_NAME, "Ando") - .put(StructuredName.GIVEN_NAME, "Roid") - // "FN" field should be prefered since it should contain the original - // order intended by the author of the file. - .put(StructuredName.DISPLAY_NAME, "Ando Roid"); - } - - /** - * Tests ';' is properly handled by VCardParser implementation. - */ - public void testV21BackslashCase_Parsing() { - mVerifier.initForImportTest(V21, R.raw.v21_backslash); - mVerifier.addPropertyNodesVerifierElem() - .addExpectedNodeWithOrder("VERSION", "2.1") - .addExpectedNodeWithOrder("N", ";A;B\\;C\\;;D;:E;\\\\;", - Arrays.asList("", "A;B\\", "C\\;", "D", ":E", "\\\\", "")) - .addExpectedNodeWithOrder("FN", "A;B\\C\\;D:E\\\\"); - - } - - /** - * Tests ContactStruct correctly ignores redundant fields in "N" property values and - * inserts name related data. - */ - public void testV21BackslashCase() { - mVerifier.initForImportTest(V21, R.raw.v21_backslash); - mVerifier.addContentValuesVerifierElem() - .addExpected(StructuredName.CONTENT_ITEM_TYPE) - // FAMILY_NAME is empty and removed in this test... - .put(StructuredName.GIVEN_NAME, "A;B\\") - .put(StructuredName.MIDDLE_NAME, "C\\;") - .put(StructuredName.PREFIX, "D") - .put(StructuredName.SUFFIX, ":E") - .put(StructuredName.DISPLAY_NAME, "A;B\\C\\;D:E\\\\"); - } - - public void testOrgBeforTitle() { - mVerifier.initForImportTest(V21, R.raw.v21_org_before_title); - ContentValuesVerifierElem elem = mVerifier.addContentValuesVerifierElem(); - elem.addExpected(StructuredName.CONTENT_ITEM_TYPE) - .put(StructuredName.DISPLAY_NAME, "Normal Guy"); - elem.addExpected(Organization.CONTENT_ITEM_TYPE) - .put(Organization.COMPANY, "Company") - .put(Organization.DEPARTMENT, "Organization Devision Room Sheet No.") - .put(Organization.TITLE, "Excellent Janitor") - .put(Organization.TYPE, Organization.TYPE_WORK); - } - - public void testTitleBeforOrg() { - mVerifier.initForImportTest(V21, R.raw.v21_title_before_org); - ContentValuesVerifierElem elem = mVerifier.addContentValuesVerifierElem(); - elem.addExpected(StructuredName.CONTENT_ITEM_TYPE) - .put(StructuredName.DISPLAY_NAME, "Nice Guy"); - elem.addExpected(Organization.CONTENT_ITEM_TYPE) - .put(Organization.COMPANY, "Marverous") - .put(Organization.DEPARTMENT, "Perfect Great Good Bad Poor") - .put(Organization.TITLE, "Cool Title") - .put(Organization.TYPE, Organization.TYPE_WORK); - } - - /** - * Verifies that vCard importer correctly interpret "PREF" attribute to IS_PRIMARY. - * The data contain three cases: one "PREF", no "PREF" and multiple "PREF", in each type. - */ - public void testV21PrefToIsPrimary() { - mVerifier.initForImportTest(V21, R.raw.v21_pref_handling); - ContentValuesVerifierElem elem = mVerifier.addContentValuesVerifierElem(); - elem.addExpected(StructuredName.CONTENT_ITEM_TYPE) - .put(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE) - .put(StructuredName.DISPLAY_NAME, "Smith"); - elem.addExpected(Phone.CONTENT_ITEM_TYPE) - .put(Phone.NUMBER, "1") - .put(Phone.TYPE, Phone.TYPE_HOME); - elem.addExpected(Phone.CONTENT_ITEM_TYPE) - .put(Phone.NUMBER, "2") - .put(Phone.TYPE, Phone.TYPE_WORK) - .put(Phone.IS_PRIMARY, 1); - elem.addExpected(Phone.CONTENT_ITEM_TYPE) - .put(Phone.NUMBER, "3") - .put(Phone.TYPE, Phone.TYPE_ISDN); - elem.addExpected(Email.CONTENT_ITEM_TYPE) - .put(Email.DATA, "test@example.com") - .put(Email.TYPE, Email.TYPE_HOME) - .put(Email.IS_PRIMARY, 1); - elem.addExpected(Email.CONTENT_ITEM_TYPE) - .put(Email.DATA, "test2@examination.com") - .put(Email.TYPE, Email.TYPE_MOBILE) - .put(Email.IS_PRIMARY, 1); - elem.addExpected(Organization.CONTENT_ITEM_TYPE) - .put(Organization.COMPANY, "Company") - .put(Organization.TITLE, "Engineer") - .put(Organization.TYPE, Organization.TYPE_WORK); - elem.addExpected(Organization.CONTENT_ITEM_TYPE) - .put(Organization.COMPANY, "Mystery") - .put(Organization.TITLE, "Blogger") - .put(Organization.TYPE, Organization.TYPE_WORK); - elem.addExpected(Organization.CONTENT_ITEM_TYPE) - .put(Organization.COMPANY, "Poetry") - .put(Organization.TITLE, "Poet") - .put(Organization.TYPE, Organization.TYPE_WORK); - } - - /** - * Tests all the properties in a complicated vCard are correctly parsed by the VCardParser. - */ - public void testV21ComplicatedCase_Parsing() { - mVerifier.initForImportTest(V21, R.raw.v21_complicated); - mVerifier.addPropertyNodesVerifierElem() - .addExpectedNodeWithOrder("VERSION", "2.1") - .addExpectedNodeWithOrder("N", "Gump;Forrest;Hoge;Pos;Tao", - Arrays.asList("Gump", "Forrest", "Hoge", "Pos", "Tao")) - .addExpectedNodeWithOrder("FN", "Joe Due") - .addExpectedNodeWithOrder("ORG", "Gump Shrimp Co.;Sales Dept.;Manager;Fish keeper", - Arrays.asList("Gump Shrimp Co.", "Sales Dept.;Manager", "Fish keeper")) - .addExpectedNodeWithOrder("ROLE", "Fish Cake Keeper!") - .addExpectedNodeWithOrder("TITLE", "Shrimp Man") - .addExpectedNodeWithOrder("X-CLASS", "PUBLIC") - .addExpectedNodeWithOrder("TEL", "(111) 555-1212", new TypeSet("WORK", "VOICE")) - .addExpectedNodeWithOrder("TEL", "(404) 555-1212", new TypeSet("HOME", "VOICE")) - .addExpectedNodeWithOrder("TEL", "0311111111", new TypeSet("CELL")) - .addExpectedNodeWithOrder("TEL", "0322222222", new TypeSet("VIDEO")) - .addExpectedNodeWithOrder("TEL", "0333333333", new TypeSet("VOICE")) - .addExpectedNodeWithOrder("ADR", - ";;100 Waters Edge;Baytown;LA;30314;United States of America", - Arrays.asList("", "", "100 Waters Edge", "Baytown", - "LA", "30314", "United States of America"), - null, null, new TypeSet("WORK"), null) - .addExpectedNodeWithOrder("LABEL", - "100 Waters Edge\r\nBaytown, LA 30314\r\nUnited States of America", - null, null, mContentValuesForQP, new TypeSet("WORK"), null) - .addExpectedNodeWithOrder("ADR", - ";;42 Plantation St.;Baytown;LA;30314;United States of America", - Arrays.asList("", "", "42 Plantation St.", "Baytown", - "LA", "30314", "United States of America"), null, null, - new TypeSet("HOME"), null) - .addExpectedNodeWithOrder("LABEL", - "42 Plantation St.\r\nBaytown, LA 30314\r\nUnited States of America", - null, null, mContentValuesForQP, - new TypeSet("HOME"), null) - .addExpectedNodeWithOrder("EMAIL", "forrestgump@walladalla.com", - new TypeSet("PREF", "INTERNET")) - .addExpectedNodeWithOrder("EMAIL", "cell@example.com", new TypeSet("CELL")) - .addExpectedNodeWithOrder("NOTE", "The following note is the example from RFC 2045.") - .addExpectedNodeWithOrder("NOTE", - "Now's the time for all folk to come to the aid of their country.", - null, null, mContentValuesForQP, null, null) - .addExpectedNodeWithOrder("PHOTO", null, - null, sPhotoByteArrayForComplicatedCase, mContentValuesForBase64V21, - new TypeSet("JPEG"), null) - .addExpectedNodeWithOrder("X-ATTRIBUTE", "Some String") - .addExpectedNodeWithOrder("BDAY", "19800101") - .addExpectedNodeWithOrder("GEO", "35.6563854,139.6994233") - .addExpectedNodeWithOrder("URL", "http://www.example.com/") - .addExpectedNodeWithOrder("REV", "20080424T195243Z"); - } - - /** - * Checks ContactStruct correctly inserts values in a complicated vCard - * into ContentResolver. - */ - public void testV21ComplicatedCase() { - mVerifier.initForImportTest(V21, R.raw.v21_complicated); - ContentValuesVerifierElem elem = mVerifier.addContentValuesVerifierElem(); - elem.addExpected(StructuredName.CONTENT_ITEM_TYPE) - .put(StructuredName.FAMILY_NAME, "Gump") - .put(StructuredName.GIVEN_NAME, "Forrest") - .put(StructuredName.MIDDLE_NAME, "Hoge") - .put(StructuredName.PREFIX, "Pos") - .put(StructuredName.SUFFIX, "Tao") - .put(StructuredName.DISPLAY_NAME, "Joe Due"); - elem.addExpected(Organization.CONTENT_ITEM_TYPE) - .put(Organization.TYPE, Organization.TYPE_WORK) - .put(Organization.COMPANY, "Gump Shrimp Co.") - .put(Organization.DEPARTMENT, "Sales Dept.;Manager Fish keeper") - .put(Organization.TITLE, "Shrimp Man"); - elem.addExpected(Phone.CONTENT_ITEM_TYPE) - .put(Phone.TYPE, Phone.TYPE_WORK) - // Phone number is expected to be formated with NAMP format in default. - .put(Phone.NUMBER, "111-555-1212"); - elem.addExpected(Phone.CONTENT_ITEM_TYPE) - .put(Phone.TYPE, Phone.TYPE_HOME) - .put(Phone.NUMBER, "404-555-1212"); - elem.addExpected(Phone.CONTENT_ITEM_TYPE) - .put(Phone.TYPE, Phone.TYPE_MOBILE) - .put(Phone.NUMBER, "031-111-1111"); - elem.addExpected(Phone.CONTENT_ITEM_TYPE) - .put(Phone.TYPE, Phone.TYPE_CUSTOM) - .put(Phone.LABEL, "VIDEO") - .put(Phone.NUMBER, "032-222-2222"); - elem.addExpected(Phone.CONTENT_ITEM_TYPE) - .put(Phone.TYPE, Phone.TYPE_CUSTOM) - .put(Phone.LABEL, "VOICE") - .put(Phone.NUMBER, "033-333-3333"); - elem.addExpected(StructuredPostal.CONTENT_ITEM_TYPE) - .put(StructuredPostal.TYPE, StructuredPostal.TYPE_WORK) - .put(StructuredPostal.COUNTRY, "United States of America") - .put(StructuredPostal.POSTCODE, "30314") - .put(StructuredPostal.REGION, "LA") - .put(StructuredPostal.CITY, "Baytown") - .put(StructuredPostal.STREET, "100 Waters Edge") - .put(StructuredPostal.FORMATTED_ADDRESS, - "100 Waters Edge Baytown LA 30314 United States of America"); - elem.addExpected(StructuredPostal.CONTENT_ITEM_TYPE) - .put(StructuredPostal.TYPE, StructuredPostal.TYPE_HOME) - .put(StructuredPostal.COUNTRY, "United States of America") - .put(StructuredPostal.POSTCODE, "30314") - .put(StructuredPostal.REGION, "LA") - .put(StructuredPostal.CITY, "Baytown") - .put(StructuredPostal.STREET, "42 Plantation St.") - .put(StructuredPostal.FORMATTED_ADDRESS, - "42 Plantation St. Baytown LA 30314 United States of America"); - elem.addExpected(Email.CONTENT_ITEM_TYPE) - // "TYPE=INTERNET" -> TYPE_CUSTOM + the label "INTERNET" - .put(Email.TYPE, Email.TYPE_CUSTOM) - .put(Email.LABEL, "INTERNET") - .put(Email.DATA, "forrestgump@walladalla.com") - .put(Email.IS_PRIMARY, 1); - elem.addExpected(Email.CONTENT_ITEM_TYPE) - .put(Email.TYPE, Email.TYPE_MOBILE) - .put(Email.DATA, "cell@example.com"); - elem.addExpected(Note.CONTENT_ITEM_TYPE) - .put(Note.NOTE, "The following note is the example from RFC 2045."); - elem.addExpected(Note.CONTENT_ITEM_TYPE) - .put(Note.NOTE, - "Now's the time for all folk to come to the aid of their country."); - elem.addExpected(Photo.CONTENT_ITEM_TYPE) - // No information about its image format can be inserted. - .put(Photo.PHOTO, sPhotoByteArrayForComplicatedCase); - elem.addExpected(Event.CONTENT_ITEM_TYPE) - .put(Event.START_DATE, "19800101") - .put(Event.TYPE, Event.TYPE_BIRTHDAY); - elem.addExpected(Website.CONTENT_ITEM_TYPE) - .put(Website.URL, "http://www.example.com/") - .put(Website.TYPE, Website.TYPE_HOMEPAGE); - } - - public void testV30Simple_Parsing() { - mVerifier.initForImportTest(V30, R.raw.v30_simple); - mVerifier.addPropertyNodesVerifierElem() - .addExpectedNodeWithOrder("VERSION", "3.0") - .addExpectedNodeWithOrder("FN", "And Roid") - .addExpectedNodeWithOrder("N", "And;Roid;;;", Arrays.asList("And", "Roid", "", "", "")) - .addExpectedNodeWithOrder("ORG", "Open;Handset; Alliance", - Arrays.asList("Open", "Handset", " Alliance")) - .addExpectedNodeWithOrder("SORT-STRING", "android") - .addExpectedNodeWithOrder("TEL", "0300000000", new TypeSet("PREF", "VOICE")) - .addExpectedNodeWithOrder("CLASS", "PUBLIC") - .addExpectedNodeWithOrder("X-GNO", "0") - .addExpectedNodeWithOrder("X-GN", "group0") - .addExpectedNodeWithOrder("X-REDUCTION", "0") - .addExpectedNodeWithOrder("REV", "20081031T065854Z"); - } - - public void testV30Simple() { - mVerifier.initForImportTest(V30, R.raw.v30_simple); - ContentValuesVerifierElem elem = mVerifier.addContentValuesVerifierElem(); - elem.addExpected(StructuredName.CONTENT_ITEM_TYPE) - .put(StructuredName.FAMILY_NAME, "And") - .put(StructuredName.GIVEN_NAME, "Roid") - .put(StructuredName.DISPLAY_NAME, "And Roid") - .put(StructuredName.PHONETIC_GIVEN_NAME, "android"); - elem.addExpected(Organization.CONTENT_ITEM_TYPE) - .put(Organization.COMPANY, "Open") - .put(Organization.DEPARTMENT, "Handset Alliance") - .put(Organization.TYPE, Organization.TYPE_WORK); - elem.addExpected(Phone.CONTENT_ITEM_TYPE) - .put(Phone.TYPE, Phone.TYPE_CUSTOM) - .put(Phone.LABEL, "VOICE") - .put(Phone.NUMBER, "030-000-0000") - .put(Phone.IS_PRIMARY, 1); - } - - public void testV21Japanese1_Parsing() { - // Though Japanese careers append ";;;;" at the end of the value of "SOUND", - // vCard 2.1/3.0 specification does not allow multiple values. - // Do not need to handle it as multiple values. - mVerifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE, R.raw.v21_japanese_1); - mVerifier.addPropertyNodesVerifierElem() - .addExpectedNodeWithOrder("VERSION", "2.1", null, null, null, null, null) - .addExpectedNodeWithOrder("N", "\u5B89\u85E4\u30ED\u30A4\u30C9;;;;", - Arrays.asList("\u5B89\u85E4\u30ED\u30A4\u30C9", "", "", "", ""), - null, mContentValuesForSJis, null, null) - .addExpectedNodeWithOrder("SOUND", - "\uFF71\uFF9D\uFF84\uFF9E\uFF73\uFF9B\uFF72\uFF84\uFF9E;;;;", - null, null, mContentValuesForSJis, - new TypeSet("X-IRMC-N"), null) - .addExpectedNodeWithOrder("TEL", "0300000000", null, null, null, - new TypeSet("VOICE", "PREF"), null); - } - - private void testV21Japanese1Common(int resId, int vcardType, boolean japanese) { - mVerifier.initForImportTest(vcardType, resId); - ContentValuesVerifierElem elem = mVerifier.addContentValuesVerifierElem(); - elem.addExpected(StructuredName.CONTENT_ITEM_TYPE) - .put(StructuredName.FAMILY_NAME, "\u5B89\u85E4\u30ED\u30A4\u30C9") - .put(StructuredName.DISPLAY_NAME, "\u5B89\u85E4\u30ED\u30A4\u30C9") - // While vCard parser does not split "SOUND" property values, - // ContactStruct care it. - .put(StructuredName.PHONETIC_GIVEN_NAME, - "\uFF71\uFF9D\uFF84\uFF9E\uFF73\uFF9B\uFF72\uFF84\uFF9E"); - elem.addExpected(Phone.CONTENT_ITEM_TYPE) - // Phone number formatting is different. - .put(Phone.NUMBER, (japanese ? "03-0000-0000" : "030-000-0000")) - .put(Phone.TYPE, Phone.TYPE_CUSTOM) - .put(Phone.LABEL, "VOICE") - .put(Phone.IS_PRIMARY, 1); - } - - /** - * Verifies vCard with Japanese can be parsed correctly with - * {@link android.pim.vcard.VCardConfig#VCARD_TYPE_V21_GENERIC}. - */ - public void testV21Japanese1_Type_Generic_Utf8() { - testV21Japanese1Common( - R.raw.v21_japanese_1, VCardConfig.VCARD_TYPE_V21_GENERIC, false); - } - - /** - * Verifies vCard with Japanese can be parsed correctly with - * {@link android.pim.vcard.VCardConfig#VCARD_TYPE_V21_JAPANESE}. - */ - public void testV21Japanese1_Type_Japanese_Sjis() { - testV21Japanese1Common( - R.raw.v21_japanese_1, VCardConfig.VCARD_TYPE_V21_JAPANESE, true); - } - - /** - * Verifies vCard with Japanese can be parsed correctly with - * {@link android.pim.vcard.VCardConfig#VCARD_TYPE_V21_JAPANESE}. - * since vCard 2.1 specifies the charset of each line if it contains non-Ascii. - */ - public void testV21Japanese1_Type_Japanese_Utf8() { - testV21Japanese1Common( - R.raw.v21_japanese_1, VCardConfig.VCARD_TYPE_V21_JAPANESE, true); - } - - public void testV21Japanese2_Parsing() { - mVerifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE, R.raw.v21_japanese_2); - mVerifier.addPropertyNodesVerifierElem() - .addExpectedNodeWithOrder("VERSION", "2.1") - .addExpectedNodeWithOrder("N", "\u5B89\u85E4;\u30ED\u30A4\u30C9\u0031;;;", - Arrays.asList("\u5B89\u85E4", "\u30ED\u30A4\u30C9\u0031", - "", "", ""), - null, mContentValuesForSJis, null, null) - .addExpectedNodeWithOrder("FN", "\u5B89\u85E4\u0020\u30ED\u30A4\u30C9\u0020\u0031", - null, null, mContentValuesForSJis, null, null) - .addExpectedNodeWithOrder("SOUND", - "\uFF71\uFF9D\uFF84\uFF9E\uFF73;\uFF9B\uFF72\uFF84\uFF9E\u0031;;;", - null, null, mContentValuesForSJis, - new TypeSet("X-IRMC-N"), null) - .addExpectedNodeWithOrder("ADR", - ";\u6771\u4EAC\u90FD\u6E0B\u8C37\u533A\u685C" + - "\u4E18\u753A\u0032\u0036\u002D\u0031\u30BB" + - "\u30EB\u30EA\u30A2\u30F3\u30BF\u30EF\u30FC\u0036" + - "\u968E;;;;150-8512;", - Arrays.asList("", - "\u6771\u4EAC\u90FD\u6E0B\u8C37\u533A\u685C" + - "\u4E18\u753A\u0032\u0036\u002D\u0031\u30BB" + - "\u30EB\u30EA\u30A2\u30F3\u30BF\u30EF\u30FC" + - "\u0036\u968E", "", "", "", "150-8512", ""), - null, mContentValuesForQPAndSJis, new TypeSet("HOME"), null) - .addExpectedNodeWithOrder("NOTE", "\u30E1\u30E2", null, null, - mContentValuesForQPAndSJis, null, null); - } - - public void testV21Japanese2_Type_Generic_Utf8() { - mVerifier.initForImportTest(V21, R.raw.v21_japanese_2); - ContentValuesVerifierElem elem = mVerifier.addContentValuesVerifierElem(); - elem.addExpected(StructuredName.CONTENT_ITEM_TYPE) - .put(StructuredName.FAMILY_NAME, "\u5B89\u85E4") - .put(StructuredName.GIVEN_NAME, "\u30ED\u30A4\u30C9\u0031") - .put(StructuredName.DISPLAY_NAME, - "\u5B89\u85E4\u0020\u30ED\u30A4\u30C9\u0020\u0031") - // ContactStruct should correctly split "SOUND" property into several elements, - // even though VCardParser side does not care it. - .put(StructuredName.PHONETIC_FAMILY_NAME, "\uFF71\uFF9D\uFF84\uFF9E\uFF73") - .put(StructuredName.PHONETIC_GIVEN_NAME, "\uFF9B\uFF72\uFF84\uFF9E\u0031"); - elem.addExpected(StructuredPostal.CONTENT_ITEM_TYPE) - .put(StructuredPostal.POSTCODE, "150-8512") - .put(StructuredPostal.STREET, - "\u6771\u4EAC\u90FD\u6E0B\u8C37\u533A\u685C" + - "\u4E18\u753A\u0032\u0036\u002D\u0031\u30BB" + - "\u30EB\u30EA\u30A2\u30F3\u30BF\u30EF\u30FC" + - "\u0036\u968E") - .put(StructuredPostal.FORMATTED_ADDRESS, - "\u6771\u4EAC\u90FD\u6E0B\u8C37\u533A\u685C" + - "\u4E18\u753A\u0032\u0036\u002D\u0031\u30BB" + - "\u30EB\u30EA\u30A2\u30F3\u30BF\u30EF\u30FC" + - "\u0036\u968E 150-8512") - .put(StructuredPostal.TYPE, StructuredPostal.TYPE_HOME); - elem.addExpected(Note.CONTENT_ITEM_TYPE) - .put(Note.NOTE, "\u30E1\u30E2"); - } - - public void testV21MultipleEntryCase_Parse() { - mVerifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE, R.raw.v21_multiple_entry); - mVerifier.addPropertyNodesVerifierElem() - .addExpectedNodeWithOrder("VERSION", "2.1") - .addExpectedNodeWithOrder("N", "\u5B89\u85E4\u30ED\u30A4\u30C9\u0033;;;;", - Arrays.asList("\u5B89\u85E4\u30ED\u30A4\u30C9\u0033", "", "", "", ""), - null, mContentValuesForSJis, null, null) - .addExpectedNodeWithOrder("SOUND", - "\uFF71\uFF9D\uFF84\uFF9E\uFF73\uFF9B\uFF72\uFF84\uFF9E\u0033;;;;", - null, null, mContentValuesForSJis, - new TypeSet("X-IRMC-N"), null) - .addExpectedNodeWithOrder("TEL", "9", new TypeSet("X-NEC-SECRET")) - .addExpectedNodeWithOrder("TEL", "10", new TypeSet("X-NEC-HOTEL")) - .addExpectedNodeWithOrder("TEL", "11", new TypeSet("X-NEC-SCHOOL")) - .addExpectedNodeWithOrder("TEL", "12", new TypeSet("FAX", "HOME")); - mVerifier.addPropertyNodesVerifierElem() - .addExpectedNodeWithOrder("VERSION", "2.1") - .addExpectedNodeWithOrder("N", "\u5B89\u85E4\u30ED\u30A4\u30C9\u0034;;;;", - Arrays.asList("\u5B89\u85E4\u30ED\u30A4\u30C9\u0034", "", "", "", ""), - null, mContentValuesForSJis, null, null) - .addExpectedNodeWithOrder("SOUND", - "\uFF71\uFF9D\uFF84\uFF9E\uFF73\uFF9B\uFF72\uFF84\uFF9E\u0034;;;;", - null, null, mContentValuesForSJis, - new TypeSet("X-IRMC-N"), null) - .addExpectedNodeWithOrder("TEL", "13", new TypeSet("MODEM")) - .addExpectedNodeWithOrder("TEL", "14", new TypeSet("PAGER")) - .addExpectedNodeWithOrder("TEL", "15", new TypeSet("X-NEC-FAMILY")) - .addExpectedNodeWithOrder("TEL", "16", new TypeSet("X-NEC-GIRL")); - mVerifier.addPropertyNodesVerifierElem() - .addExpectedNodeWithOrder("VERSION", "2.1") - .addExpectedNodeWithOrder("N", "\u5B89\u85E4\u30ED\u30A4\u30C9\u0035;;;;", - Arrays.asList("\u5B89\u85E4\u30ED\u30A4\u30C9\u0035", "", "", "", ""), - null, mContentValuesForSJis, null, null) - .addExpectedNodeWithOrder("SOUND", - "\uFF71\uFF9D\uFF84\uFF9E\uFF73\uFF9B\uFF72\uFF84\uFF9E\u0035;;;;", - null, null, mContentValuesForSJis, - new TypeSet("X-IRMC-N"), null) - .addExpectedNodeWithOrder("TEL", "17", new TypeSet("X-NEC-BOY")) - .addExpectedNodeWithOrder("TEL", "18", new TypeSet("X-NEC-FRIEND")) - .addExpectedNodeWithOrder("TEL", "19", new TypeSet("X-NEC-PHS")) - .addExpectedNodeWithOrder("TEL", "20", new TypeSet("X-NEC-RESTAURANT")); - } - - public void testV21MultipleEntryCase() { - mVerifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE, R.raw.v21_multiple_entry); - ContentValuesVerifierElem elem = mVerifier.addContentValuesVerifierElem(); - elem.addExpected(StructuredName.CONTENT_ITEM_TYPE) - .put(StructuredName.FAMILY_NAME, "\u5B89\u85E4\u30ED\u30A4\u30C9\u0033") - .put(StructuredName.DISPLAY_NAME, "\u5B89\u85E4\u30ED\u30A4\u30C9\u0033") - .put(StructuredName.PHONETIC_GIVEN_NAME, - "\uFF71\uFF9D\uFF84\uFF9E\uFF73\uFF9B\uFF72\uFF84\uFF9E\u0033"); - elem.addExpected(Phone.CONTENT_ITEM_TYPE) - .put(Phone.TYPE, Phone.TYPE_CUSTOM) - .put(Phone.LABEL, "NEC-SECRET") - .put(Phone.NUMBER, "9"); - elem.addExpected(Phone.CONTENT_ITEM_TYPE) - .put(Phone.TYPE, Phone.TYPE_CUSTOM) - .put(Phone.LABEL, "NEC-HOTEL") - .put(Phone.NUMBER, "10"); - elem.addExpected(Phone.CONTENT_ITEM_TYPE) - .put(Phone.TYPE, Phone.TYPE_CUSTOM) - .put(Phone.LABEL, "NEC-SCHOOL") - .put(Phone.NUMBER, "11"); - elem.addExpected(Phone.CONTENT_ITEM_TYPE) - .put(Phone.TYPE, Phone.TYPE_FAX_HOME) - .put(Phone.NUMBER, "12"); - - elem = mVerifier.addContentValuesVerifierElem(); - elem.addExpected(StructuredName.CONTENT_ITEM_TYPE) - .put(StructuredName.FAMILY_NAME, "\u5B89\u85E4\u30ED\u30A4\u30C9\u0034") - .put(StructuredName.DISPLAY_NAME, "\u5B89\u85E4\u30ED\u30A4\u30C9\u0034") - .put(StructuredName.PHONETIC_GIVEN_NAME, - "\uFF71\uFF9D\uFF84\uFF9E\uFF73\uFF9B\uFF72\uFF84\uFF9E\u0034"); - elem.addExpected(Phone.CONTENT_ITEM_TYPE) - .put(Phone.TYPE, Phone.TYPE_CUSTOM) - .put(Phone.LABEL, "MODEM") - .put(Phone.NUMBER, "13"); - elem.addExpected(Phone.CONTENT_ITEM_TYPE) - .put(Phone.TYPE, Phone.TYPE_PAGER) - .put(Phone.NUMBER, "14"); - elem.addExpected(Phone.CONTENT_ITEM_TYPE) - .put(Phone.TYPE, Phone.TYPE_CUSTOM) - .put(Phone.LABEL, "NEC-FAMILY") - .put(Phone.NUMBER, "15"); - elem.addExpected(Phone.CONTENT_ITEM_TYPE) - .put(Phone.TYPE, Phone.TYPE_CUSTOM) - .put(Phone.LABEL, "NEC-GIRL") - .put(Phone.NUMBER, "16"); - - elem = mVerifier.addContentValuesVerifierElem(); - elem.addExpected(StructuredName.CONTENT_ITEM_TYPE) - .put(StructuredName.FAMILY_NAME, "\u5B89\u85E4\u30ED\u30A4\u30C9\u0035") - .put(StructuredName.DISPLAY_NAME, "\u5B89\u85E4\u30ED\u30A4\u30C9\u0035") - .put(StructuredName.PHONETIC_GIVEN_NAME, - "\uFF71\uFF9D\uFF84\uFF9E\uFF73\uFF9B\uFF72\uFF84\uFF9E\u0035"); - elem.addExpected(Phone.CONTENT_ITEM_TYPE) - .put(Phone.TYPE, Phone.TYPE_CUSTOM) - .put(Phone.LABEL, "NEC-BOY") - .put(Phone.NUMBER, "17"); - elem.addExpected(Phone.CONTENT_ITEM_TYPE) - .put(Phone.TYPE, Phone.TYPE_CUSTOM) - .put(Phone.LABEL, "NEC-FRIEND") - .put(Phone.NUMBER, "18"); - elem.addExpected(Phone.CONTENT_ITEM_TYPE) - .put(Phone.TYPE, Phone.TYPE_CUSTOM) - .put(Phone.LABEL, "NEC-PHS") - .put(Phone.NUMBER, "19"); - elem.addExpected(Phone.CONTENT_ITEM_TYPE) - .put(Phone.TYPE, Phone.TYPE_CUSTOM) - .put(Phone.LABEL, "NEC-RESTAURANT") - .put(Phone.NUMBER, "20"); - } - - public void testIgnoreAgentV21_Parse() { - mVerifier.initForImportTest(V21, R.raw.v21_winmo_65); - ContentValues contentValuesForValue = new ContentValues(); - contentValuesForValue.put("VALUE", "DATE"); - mVerifier.addPropertyNodesVerifierElem() - .addExpectedNodeWithOrder("VERSION", "2.1") - .addExpectedNodeWithOrder("N", Arrays.asList("Example", "", "", "", "")) - .addExpectedNodeWithOrder("FN", "Example") - .addExpectedNodeWithOrder("ANNIVERSARY", "20091010", contentValuesForValue) - .addExpectedNodeWithOrder("AGENT", "") - .addExpectedNodeWithOrder("X-CLASS", "PUBLIC") - .addExpectedNodeWithOrder("X-REDUCTION", "") - .addExpectedNodeWithOrder("X-NO", ""); - } - - public void testIgnoreAgentV21() { - mVerifier.initForImportTest(V21, R.raw.v21_winmo_65); - ContentValuesVerifier verifier = new ContentValuesVerifier(); - ContentValuesVerifierElem elem = mVerifier.addContentValuesVerifierElem(); - elem.addExpected(StructuredName.CONTENT_ITEM_TYPE) - .put(StructuredName.FAMILY_NAME, "Example") - .put(StructuredName.DISPLAY_NAME, "Example"); - } - - public void testTolerateInvalidCommentLikeLineV21() { - mVerifier.initForImportTest(V21, R.raw.v21_invalid_comment_line); - ContentValuesVerifierElem elem = mVerifier.addContentValuesVerifierElem(); - elem.addExpected(StructuredName.CONTENT_ITEM_TYPE) - .put(StructuredName.GIVEN_NAME, "Conference Call") - .put(StructuredName.DISPLAY_NAME, "Conference Call"); - elem.addExpected(Note.CONTENT_ITEM_TYPE) - .put(Note.NOTE, "This is an (sharp ->#<- sharp) example. " - + "This message must NOT be ignored."); - } - - public void testPagerV30_Parse() { - mVerifier.initForImportTest(V30, R.raw.v30_comma_separated); - mVerifier.addPropertyNodesVerifierElem() - .addExpectedNodeWithOrder("VERSION", "3.0") - .addExpectedNodeWithOrder("N", Arrays.asList("F", "G", "M", "", "")) - .addExpectedNodeWithOrder("TEL", "6101231234@pagersample.com", - new TypeSet("WORK", "MSG", "PAGER")); - } - - public void testPagerV30() { - mVerifier.initForImportTest(V30, R.raw.v30_comma_separated); - ContentValuesVerifierElem elem = mVerifier.addContentValuesVerifierElem(); - elem.addExpected(StructuredName.CONTENT_ITEM_TYPE) - .put(StructuredName.FAMILY_NAME, "F") - .put(StructuredName.MIDDLE_NAME, "M") - .put(StructuredName.GIVEN_NAME, "G") - .put(StructuredName.DISPLAY_NAME, "G M F"); - elem.addExpected(Phone.CONTENT_ITEM_TYPE) - .put(Phone.TYPE, Phone.TYPE_PAGER) - .put(Phone.NUMBER, "6101231234@pagersample.com"); - } -} diff --git a/vcard/tests/src/com/android/vcard/tests/VCardJapanizationTests.java b/vcard/tests/src/com/android/vcard/tests/VCardJapanizationTests.java deleted file mode 100644 index 0d0b9f10a..000000000 --- a/vcard/tests/src/com/android/vcard/tests/VCardJapanizationTests.java +++ /dev/null @@ -1,436 +0,0 @@ -/* - * 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.vcard.tests; - -import android.content.ContentValues; -import android.provider.ContactsContract.CommonDataKinds.Nickname; -import android.provider.ContactsContract.CommonDataKinds.Note; -import android.provider.ContactsContract.CommonDataKinds.Phone; -import android.provider.ContactsContract.CommonDataKinds.StructuredName; -import android.provider.ContactsContract.CommonDataKinds.StructuredPostal; - -import com.android.vcard.VCardConfig; -import com.android.vcard.tests.test_utils.ContactEntry; -import com.android.vcard.tests.test_utils.ContentValuesBuilder; -import com.android.vcard.tests.test_utils.PropertyNodesVerifierElem; -import com.android.vcard.tests.test_utils.PropertyNodesVerifierElem.TypeSet; - -import java.util.Arrays; - -public class VCardJapanizationTests extends VCardTestsBase { - private void testNameUtf8Common(int vcardType) { - mVerifier.initForExportTest(vcardType); - ContactEntry entry = mVerifier.addInputEntry(); - entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE) - .put(StructuredName.FAMILY_NAME, "\u3075\u308B\u3069") - .put(StructuredName.GIVEN_NAME, "\u3091\u308A\u304B") - .put(StructuredName.MIDDLE_NAME, "B") - .put(StructuredName.PREFIX, "Dr.") - .put(StructuredName.SUFFIX, "Ph.D"); - ContentValues contentValues = - (VCardConfig.isV30(vcardType) ? null : mContentValuesForQPAndUtf8); - mVerifier.addPropertyNodesVerifierElem() - .addExpectedNode("FN", "Dr. \u3075\u308B\u3069 B \u3091\u308A\u304B Ph.D", - contentValues) - .addExpectedNode("N", "\u3075\u308B\u3069;\u3091\u308A\u304B;B;Dr.;Ph.D", - Arrays.asList( - "\u3075\u308B\u3069", "\u3091\u308A\u304B", "B", "Dr.", "Ph.D"), - null, contentValues, null, null); - } - - public void testNameUtf8V21() { - testNameUtf8Common(VCardConfig.VCARD_TYPE_V21_JAPANESE); - } - - public void testNameUtf8V30() { - testNameUtf8Common(VCardConfig.VCARD_TYPE_V30_JAPANESE); - } - - public void testNameShiftJis() { - mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_V30_JAPANESE, "Shift_JIS"); - ContactEntry entry = mVerifier.addInputEntry(); - entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE) - .put(StructuredName.FAMILY_NAME, "\u3075\u308B\u3069") - .put(StructuredName.GIVEN_NAME, "\u3091\u308A\u304B") - .put(StructuredName.MIDDLE_NAME, "B") - .put(StructuredName.PREFIX, "Dr.") - .put(StructuredName.SUFFIX, "Ph.D"); - - mVerifier.addPropertyNodesVerifierElem() - .addExpectedNode("FN", "Dr. \u3075\u308B\u3069 B \u3091\u308A\u304B Ph.D", - mContentValuesForSJis) - .addExpectedNode("N", "\u3075\u308B\u3069;\u3091\u308A\u304B;B;Dr.;Ph.D", - Arrays.asList( - "\u3075\u308B\u3069", "\u3091\u308A\u304B", "B", "Dr.", "Ph.D"), - null, mContentValuesForSJis, null, null); - } - - /** - * DoCoMo phones require all name elements should be in "family name" field. - */ - public void testNameDoCoMo() { - mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO, "Shift_JIS"); - ContactEntry entry = mVerifier.addInputEntry(); - entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE) - .put(StructuredName.FAMILY_NAME, "\u3075\u308B\u3069") - .put(StructuredName.GIVEN_NAME, "\u3091\u308A\u304B") - .put(StructuredName.MIDDLE_NAME, "B") - .put(StructuredName.PREFIX, "Dr.") - .put(StructuredName.SUFFIX, "Ph.D"); - - final String fullName = "Dr. \u3075\u308B\u3069 B \u3091\u308A\u304B Ph.D"; - mVerifier.addPropertyNodesVerifierElem() - .addExpectedNode("N", fullName + ";;;;", - Arrays.asList(fullName, "", "", "", ""), - null, mContentValuesForSJis, null, null) - .addExpectedNode("FN", fullName, mContentValuesForSJis) - .addExpectedNode("SOUND", ";;;;", new TypeSet("X-IRMC-N")) - .addExpectedNode("TEL", "", new TypeSet("HOME")) - .addExpectedNode("EMAIL", "", new TypeSet("HOME")) - .addExpectedNode("ADR", "", new TypeSet("HOME")) - .addExpectedNode("X-CLASS", "PUBLIC") - .addExpectedNode("X-REDUCTION", "") - .addExpectedNode("X-NO", "") - .addExpectedNode("X-DCM-HMN-MODE", ""); - } - - private void testPhoneticNameCommon(int vcardType, String charset) { - mVerifier.initForExportTest(vcardType, charset); - ContactEntry entry = mVerifier.addInputEntry(); - entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE) - .put(StructuredName.PHONETIC_FAMILY_NAME, "\u3084\u307E\u3060") - .put(StructuredName.PHONETIC_MIDDLE_NAME, "\u30DF\u30C9\u30EB\u30CD\u30FC\u30E0") - .put(StructuredName.PHONETIC_GIVEN_NAME, "\u305F\u308D\u3046"); - - final ContentValues contentValues = - ("SHIFT_JIS".equalsIgnoreCase(charset) ? - (VCardConfig.isV30(vcardType) ? mContentValuesForSJis : - mContentValuesForQPAndSJis) : - (VCardConfig.isV30(vcardType) ? null : mContentValuesForQPAndUtf8)); - PropertyNodesVerifierElem elem = mVerifier.addPropertyNodesVerifierElemWithEmptyName(); - elem.addExpectedNode("X-PHONETIC-LAST-NAME", "\u3084\u307E\u3060", - contentValues) - .addExpectedNode("X-PHONETIC-MIDDLE-NAME", - "\u30DF\u30C9\u30EB\u30CD\u30FC\u30E0", - contentValues) - .addExpectedNode("X-PHONETIC-FIRST-NAME", "\u305F\u308D\u3046", - contentValues); - if (VCardConfig.isV30(vcardType)) { - elem.addExpectedNode("SORT-STRING", - "\u3084\u307E\u3060 \u30DF\u30C9\u30EB\u30CD\u30FC\u30E0 \u305F\u308D\u3046", - contentValues); - } - ContentValuesBuilder builder = mVerifier.addContentValuesVerifierElem() - .addExpected(StructuredName.CONTENT_ITEM_TYPE); - builder.put(StructuredName.PHONETIC_FAMILY_NAME, "\u3084\u307E\u3060") - .put(StructuredName.PHONETIC_MIDDLE_NAME, "\u30DF\u30C9\u30EB\u30CD\u30FC\u30E0") - .put(StructuredName.PHONETIC_GIVEN_NAME, "\u305F\u308D\u3046") - .put(StructuredName.DISPLAY_NAME, - "\u3084\u307E\u3060 \u30DF\u30C9\u30EB\u30CD\u30FC\u30E0 " + - "\u305F\u308D\u3046"); - } - - public void testPhoneticNameForJapaneseV21Utf8() { - testPhoneticNameCommon(VCardConfig.VCARD_TYPE_V21_JAPANESE, null); - } - - public void testPhoneticNameForJapaneseV21Sjis() { - testPhoneticNameCommon(VCardConfig.VCARD_TYPE_V21_JAPANESE, "Shift_JIS"); - } - - public void testPhoneticNameForJapaneseV30Utf8() { - testPhoneticNameCommon(VCardConfig.VCARD_TYPE_V30_JAPANESE, null); - } - - public void testPhoneticNameForJapaneseV30SJis() { - testPhoneticNameCommon(VCardConfig.VCARD_TYPE_V30_JAPANESE, "Shift_JIS"); - } - - public void testPhoneticNameForMobileV21_1() { - mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE_MOBILE, "Shift_JIS"); - ContactEntry entry = mVerifier.addInputEntry(); - entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE) - .put(StructuredName.PHONETIC_FAMILY_NAME, "\u3084\u307E\u3060") - .put(StructuredName.PHONETIC_MIDDLE_NAME, "\u30DF\u30C9\u30EB\u30CD\u30FC\u30E0") - .put(StructuredName.PHONETIC_GIVEN_NAME, "\u305F\u308D\u3046"); - - mVerifier.addPropertyNodesVerifierElem() - .addExpectedNode("SOUND", - "\uFF94\uFF8F\uFF80\uFF9E \uFF90\uFF84\uFF9E\uFF99\uFF88\uFF70\uFF91 " + - "\uFF80\uFF9B\uFF73;;;;", - mContentValuesForSJis, new TypeSet("X-IRMC-N")); - ContentValuesBuilder builder = mVerifier.addContentValuesVerifierElem() - .addExpected(StructuredName.CONTENT_ITEM_TYPE); - builder.put(StructuredName.PHONETIC_FAMILY_NAME, "\uFF94\uFF8F\uFF80\uFF9E") - .put(StructuredName.PHONETIC_MIDDLE_NAME, - "\uFF90\uFF84\uFF9E\uFF99\uFF88\uFF70\uFF91") - .put(StructuredName.PHONETIC_GIVEN_NAME, "\uFF80\uFF9B\uFF73") - .put(StructuredName.DISPLAY_NAME, - "\uFF94\uFF8F\uFF80\uFF9E \uFF90\uFF84\uFF9E\uFF99\uFF88\uFF70\uFF91 " + - "\uFF80\uFF9B\uFF73"); - } - - public void testPhoneticNameForMobileV21_2() { - mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE_MOBILE, "Shift_JIS"); - ContactEntry entry = mVerifier.addInputEntry(); - entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE) - .put(StructuredName.PHONETIC_FAMILY_NAME, "\u3084\u307E\u3060") - .put(StructuredName.PHONETIC_GIVEN_NAME, "\u305F\u308D\u3046"); - - mVerifier.addPropertyNodesVerifierElem() - .addExpectedNode("SOUND", "\uFF94\uFF8F\uFF80\uFF9E \uFF80\uFF9B\uFF73;;;;", - mContentValuesForSJis, new TypeSet("X-IRMC-N")); - ContentValuesBuilder builder = mVerifier.addContentValuesVerifierElem() - .addExpected(StructuredName.CONTENT_ITEM_TYPE); - builder.put(StructuredName.PHONETIC_FAMILY_NAME, "\uFF94\uFF8F\uFF80\uFF9E") - .put(StructuredName.PHONETIC_GIVEN_NAME, "\uFF80\uFF9B\uFF73") - .put(StructuredName.DISPLAY_NAME, "\uFF94\uFF8F\uFF80\uFF9E \uFF80\uFF9B\uFF73"); - } - - private void testPostalAddressWithJapaneseCommon(int vcardType, String charset) { - mVerifier.initForExportTest(vcardType, charset); - ContactEntry entry = mVerifier.addInputEntry(); - entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE) - .put(StructuredPostal.POBOX, "\u79C1\u66F8\u7BB107") - .put(StructuredPostal.STREET, "\u96DB\u898B\u6CA2\u6751") - .put(StructuredPostal.CITY, "\u9E7F\u9AA8\u5E02") - .put(StructuredPostal.REGION, "\u00D7\u00D7\u770C") - .put(StructuredPostal.POSTCODE, "494-1313") - .put(StructuredPostal.COUNTRY, "\u65E5\u672C") - .put(StructuredPostal.FORMATTED_ADDRESS, - "\u3053\u3093\u306A\u3068\u3053\u308D\u3092\u898B" - + "\u308B\u306A\u3093\u3066\u6687\u4EBA\u3067\u3059\u304B\uFF1F") - .put(StructuredPostal.TYPE, StructuredPostal.TYPE_CUSTOM) - .put(StructuredPostal.LABEL, "\u304A\u3082\u3061\u304B\u3048\u308A"); - - ContentValues contentValues = ("UTF-8".equalsIgnoreCase(charset) ? - (VCardConfig.isV30(vcardType) ? mContentValuesForSJis : - mContentValuesForQPAndSJis) : - (VCardConfig.isV30(vcardType) ? mContentValuesForUtf8 : - mContentValuesForQPAndUtf8)); - - PropertyNodesVerifierElem elem = mVerifier.addPropertyNodesVerifierElemWithEmptyName(); - // LABEL must be ignored in vCard 2.1. As for vCard 3.0, the current behavior is - // same as that in vCard 3.0, which can be changed in the future. - elem.addExpectedNode("ADR", Arrays.asList("\u79C1\u66F8\u7BB107", - "", "\u96DB\u898B\u6CA2\u6751", "\u9E7F\u9AA8\u5E02", "\u00D7\u00D7\u770C", - "494-1313", "\u65E5\u672C"), - contentValues); - mVerifier.addContentValuesVerifierElem().addExpected(StructuredPostal.CONTENT_ITEM_TYPE) - .put(StructuredPostal.POBOX, "\u79C1\u66F8\u7BB107") - .put(StructuredPostal.STREET, "\u96DB\u898B\u6CA2\u6751") - .put(StructuredPostal.CITY, "\u9E7F\u9AA8\u5E02") - .put(StructuredPostal.REGION, "\u00D7\u00D7\u770C") - .put(StructuredPostal.POSTCODE, "494-1313") - .put(StructuredPostal.COUNTRY, "\u65E5\u672C") - .put(StructuredPostal.FORMATTED_ADDRESS, - "\u65E5\u672C 494-1313 \u00D7\u00D7\u770C \u9E7F\u9AA8\u5E02 " + - "\u96DB\u898B\u6CA2\u6751 " + "\u79C1\u66F8\u7BB107") - .put(StructuredPostal.TYPE, StructuredPostal.TYPE_HOME); - } - public void testPostalAddresswithJapaneseV21() { - testPostalAddressWithJapaneseCommon(VCardConfig.VCARD_TYPE_V21_JAPANESE, "Shift_JIS"); - } - - /** - * Verifies that only one address field is emitted toward DoCoMo phones. - * Prefered type must (should?) be: HOME > WORK > OTHER > CUSTOM - */ - public void testPostalAdrressForDoCoMo_1() { - mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO, "Shift_JIS"); - ContactEntry entry = mVerifier.addInputEntry(); - entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE) - .put(StructuredPostal.TYPE, StructuredPostal.TYPE_WORK) - .put(StructuredPostal.POBOX, "1"); - entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE) - .put(StructuredPostal.TYPE, StructuredPostal.TYPE_OTHER) - .put(StructuredPostal.POBOX, "2"); - entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE) - .put(StructuredPostal.TYPE, StructuredPostal.TYPE_HOME) - .put(StructuredPostal.POBOX, "3"); - entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE) - .put(StructuredPostal.TYPE, StructuredPostal.TYPE_CUSTOM) - .put(StructuredPostal.LABEL, "custom") - .put(StructuredPostal.POBOX, "4"); - - mVerifier.addPropertyNodesVerifierElemWithEmptyName() - .addExpectedNode("TEL", "", new TypeSet("HOME")) - .addExpectedNode("EMAIL", "", new TypeSet("HOME")) - .addExpectedNode("X-CLASS", "PUBLIC") - .addExpectedNode("X-REDUCTION", "") - .addExpectedNode("X-NO", "") - .addExpectedNode("X-DCM-HMN-MODE", "") - .addExpectedNode("ADR", - Arrays.asList("3", "", "", "", "", "", ""), new TypeSet("HOME")); - } - - public void testPostalAdrressForDoCoMo_2() { - mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO, "Shift_JIS"); - ContactEntry entry = mVerifier.addInputEntry(); - entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE) - .put(StructuredPostal.TYPE, StructuredPostal.TYPE_OTHER) - .put(StructuredPostal.POBOX, "1"); - entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE) - .put(StructuredPostal.TYPE, StructuredPostal.TYPE_WORK) - .put(StructuredPostal.POBOX, "2"); - entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE) - .put(StructuredPostal.TYPE, StructuredPostal.TYPE_CUSTOM) - .put(StructuredPostal.LABEL, "custom") - .put(StructuredPostal.POBOX, "3"); - - mVerifier.addPropertyNodesVerifierElemWithEmptyName() - .addExpectedNode("TEL", "", new TypeSet("HOME")) - .addExpectedNode("EMAIL", "", new TypeSet("HOME")) - .addExpectedNode("X-CLASS", "PUBLIC") - .addExpectedNode("X-REDUCTION", "") - .addExpectedNode("X-NO", "") - .addExpectedNode("X-DCM-HMN-MODE", "") - .addExpectedNode("ADR", - Arrays.asList("2", "", "", "", "", "", ""), new TypeSet("WORK")); - } - - public void testPostalAdrressForDoCoMo_3() { - mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO, "Shift_JIS"); - ContactEntry entry = mVerifier.addInputEntry(); - entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE) - .put(StructuredPostal.TYPE, StructuredPostal.TYPE_CUSTOM) - .put(StructuredPostal.LABEL, "custom1") - .put(StructuredPostal.POBOX, "1"); - entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE) - .put(StructuredPostal.TYPE, StructuredPostal.TYPE_OTHER) - .put(StructuredPostal.POBOX, "2"); - entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE) - .put(StructuredPostal.TYPE, StructuredPostal.TYPE_CUSTOM) - .put(StructuredPostal.LABEL, "custom2") - .put(StructuredPostal.POBOX, "3"); - - mVerifier.addPropertyNodesVerifierElemWithEmptyName() - .addExpectedNode("TEL", "", new TypeSet("HOME")) - .addExpectedNode("EMAIL", "", new TypeSet("HOME")) - .addExpectedNode("X-CLASS", "PUBLIC") - .addExpectedNode("X-REDUCTION", "") - .addExpectedNode("X-NO", "") - .addExpectedNode("X-DCM-HMN-MODE", "") - .addExpectedNode("ADR", Arrays.asList("2", "", "", "", "", "", "")); - } - - /** - * Verifies the vCard exporter tolerates null TYPE. - */ - public void testPostalAdrressForDoCoMo_4() { - mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO, "Shift_JIS"); - ContactEntry entry = mVerifier.addInputEntry(); - entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE) - .put(StructuredPostal.POBOX, "1"); - entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE) - .put(StructuredPostal.TYPE, StructuredPostal.TYPE_OTHER) - .put(StructuredPostal.POBOX, "2"); - entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE) - .put(StructuredPostal.TYPE, StructuredPostal.TYPE_HOME) - .put(StructuredPostal.POBOX, "3"); - entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE) - .put(StructuredPostal.TYPE, StructuredPostal.TYPE_WORK) - .put(StructuredPostal.POBOX, "4"); - entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE) - .put(StructuredPostal.POBOX, "5"); - - mVerifier.addPropertyNodesVerifierElemWithEmptyName() - .addExpectedNode("TEL", "", new TypeSet("HOME")) - .addExpectedNode("EMAIL", "", new TypeSet("HOME")) - .addExpectedNode("X-CLASS", "PUBLIC") - .addExpectedNode("X-REDUCTION", "") - .addExpectedNode("X-NO", "") - .addExpectedNode("X-DCM-HMN-MODE", "") - .addExpectedNode("ADR", - Arrays.asList("3", "", "", "", "", "", ""), new TypeSet("HOME")); - } - - private void testJapanesePhoneNumberCommon(int vcardType) { - mVerifier.initForExportTest(vcardType); - ContactEntry entry = mVerifier.addInputEntry(); - entry.addContentValues(Phone.CONTENT_ITEM_TYPE) - .put(Phone.NUMBER, "0312341234") - .put(Phone.TYPE, Phone.TYPE_HOME); - entry.addContentValues(Phone.CONTENT_ITEM_TYPE) - .put(Phone.NUMBER, "09012341234") - .put(Phone.TYPE, Phone.TYPE_MOBILE); - mVerifier.addPropertyNodesVerifierElemWithEmptyName() - .addExpectedNode("TEL", "03-1234-1234", new TypeSet("HOME")) - .addExpectedNode("TEL", "090-1234-1234", new TypeSet("CELL")); - } - - public void testJapanesePhoneNumberV21_1() { - testJapanesePhoneNumberCommon(VCardConfig.VCARD_TYPE_V21_JAPANESE); - } - - public void testJapanesePhoneNumberV30() { - testJapanesePhoneNumberCommon(VCardConfig.VCARD_TYPE_V30_JAPANESE); - } - - public void testJapanesePhoneNumberDoCoMo() { - mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO, "Shift_JIS"); - ContactEntry entry = mVerifier.addInputEntry(); - entry.addContentValues(Phone.CONTENT_ITEM_TYPE) - .put(Phone.NUMBER, "0312341234") - .put(Phone.TYPE, Phone.TYPE_HOME); - entry.addContentValues(Phone.CONTENT_ITEM_TYPE) - .put(Phone.NUMBER, "09012341234") - .put(Phone.TYPE, Phone.TYPE_MOBILE); - mVerifier.addPropertyNodesVerifierElemWithEmptyName() - .addExpectedNode("EMAIL", "", new TypeSet("HOME")) - .addExpectedNode("X-CLASS", "PUBLIC") - .addExpectedNode("X-REDUCTION", "") - .addExpectedNode("X-NO", "") - .addExpectedNode("X-DCM-HMN-MODE", "") - .addExpectedNode("ADR", "", new TypeSet("HOME")) - .addExpectedNode("TEL", "03-1234-1234", new TypeSet("HOME")) - .addExpectedNode("TEL", "090-1234-1234", new TypeSet("CELL")); - } - - public void testNoteDoCoMo() { - mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO, "Shift_JIS"); - ContactEntry entry = mVerifier.addInputEntry(); - entry.addContentValues(Note.CONTENT_ITEM_TYPE) - .put(Note.NOTE, "note1"); - entry.addContentValues(Note.CONTENT_ITEM_TYPE) - .put(Note.NOTE, "note2"); - entry.addContentValues(Note.CONTENT_ITEM_TYPE) - .put(Note.NOTE, "note3"); - - // More than one note fields must be aggregated into one note. - mVerifier.addPropertyNodesVerifierElemWithEmptyName() - .addExpectedNode("TEL", "", new TypeSet("HOME")) - .addExpectedNode("EMAIL", "", new TypeSet("HOME")) - .addExpectedNode("X-CLASS", "PUBLIC") - .addExpectedNode("X-REDUCTION", "") - .addExpectedNode("X-NO", "") - .addExpectedNode("X-DCM-HMN-MODE", "") - .addExpectedNode("ADR", "", new TypeSet("HOME")) - .addExpectedNode("NOTE", "note1\nnote2\nnote3", mContentValuesForQP); - } - - public void testAndroidCustomV21() { - mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_V21_GENERIC); - mVerifier.addInputEntry().addContentValues(Nickname.CONTENT_ITEM_TYPE) - .put(Nickname.NAME, "\u304D\u3083\u30FC\u30A8\u30C3\u30C1\u30FC"); - mVerifier.addPropertyNodesVerifierElemWithEmptyName() - .addExpectedNode("X-ANDROID-CUSTOM", - Arrays.asList(Nickname.CONTENT_ITEM_TYPE, - "\u304D\u3083\u30FC\u30A8\u30C3\u30C1\u30FC", - "", "", "", "", "", "", "", "", "", "", "", "", "", ""), - mContentValuesForQPAndUtf8); - } -} diff --git a/vcard/tests/src/com/android/vcard/tests/VCardTestsBase.java b/vcard/tests/src/com/android/vcard/tests/VCardTestsBase.java deleted file mode 100644 index 8998b3caf..000000000 --- a/vcard/tests/src/com/android/vcard/tests/VCardTestsBase.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * 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.vcard.tests; - -import android.content.ContentValues; -import android.test.AndroidTestCase; - -import com.android.vcard.VCardConfig; -import com.android.vcard.tests.test_utils.VCardVerifier; - -/** - * BaseClass for vCard unit tests with utility classes. - * Please do not add each unit test here. - */ -/* package */ class VCardTestsBase extends AndroidTestCase { - public static final int V21 = VCardConfig.VCARD_TYPE_V21_GENERIC; - public static final int V30 = VCardConfig.VCARD_TYPE_V30_GENERIC; - - // Do not modify these during tests. - protected final ContentValues mContentValuesForQP; - protected final ContentValues mContentValuesForSJis; - protected final ContentValues mContentValuesForUtf8; - protected final ContentValues mContentValuesForQPAndSJis; - protected final ContentValues mContentValuesForQPAndUtf8; - protected final ContentValues mContentValuesForBase64V21; - protected final ContentValues mContentValuesForBase64V30; - - protected VCardVerifier mVerifier; - private boolean mSkipVerification; - - public VCardTestsBase() { - super(); - // Not using constants in vCard code since it may be wrong. - mContentValuesForQP = new ContentValues(); - mContentValuesForQP.put("ENCODING", "QUOTED-PRINTABLE"); - mContentValuesForSJis = new ContentValues(); - mContentValuesForSJis.put("CHARSET", "SHIFT_JIS"); - mContentValuesForUtf8 = new ContentValues(); - mContentValuesForUtf8.put("CHARSET", "UTF-8"); - mContentValuesForQPAndSJis = new ContentValues(); - mContentValuesForQPAndSJis.put("ENCODING", "QUOTED-PRINTABLE"); - mContentValuesForQPAndSJis.put("CHARSET", "SHIFT_JIS"); - mContentValuesForQPAndUtf8 = new ContentValues(); - mContentValuesForQPAndUtf8.put("ENCODING", "QUOTED-PRINTABLE"); - mContentValuesForQPAndUtf8.put("CHARSET", "UTF-8"); - mContentValuesForBase64V21 = new ContentValues(); - mContentValuesForBase64V21.put("ENCODING", "BASE64"); - mContentValuesForBase64V30 = new ContentValues(); - mContentValuesForBase64V30.put("ENCODING", "b"); - } - - @Override - public void testAndroidTestCaseSetupProperly() { - super.testAndroidTestCaseSetupProperly(); - mSkipVerification = true; - } - - @Override - public void setUp() throws Exception{ - super.setUp(); - mVerifier = new VCardVerifier(this); - mSkipVerification = false; - } - - @Override - public void tearDown() throws Exception { - super.tearDown(); - if (!mSkipVerification) { - mVerifier.verify(); - } - } -} diff --git a/vcard/tests/src/com/android/vcard/tests/VCardUtilsTests.java b/vcard/tests/src/com/android/vcard/tests/VCardUtilsTests.java deleted file mode 100644 index 732009a36..000000000 --- a/vcard/tests/src/com/android/vcard/tests/VCardUtilsTests.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * 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.vcard.tests; - -import com.android.vcard.VCardUtils; - -import junit.framework.TestCase; - -import java.util.List; - -public class VCardUtilsTests extends TestCase { - public void testContainsOnlyPrintableAscii() { - assertTrue(VCardUtils.containsOnlyPrintableAscii((String)null)); - assertTrue(VCardUtils.containsOnlyPrintableAscii((String[])null)); - assertTrue(VCardUtils.containsOnlyPrintableAscii((List)null)); - assertTrue(VCardUtils.containsOnlyPrintableAscii("")); - assertTrue(VCardUtils.containsOnlyPrintableAscii("abcdefghijklmnopqrstuvwxyz")); - assertTrue(VCardUtils.containsOnlyPrintableAscii("ABCDEFGHIJKLMNOPQRSTUVWXYZ")); - StringBuilder builder = new StringBuilder(); - for (int i = 0x20; i < 0x7F; i++) { - builder.append((char)i); - } - assertTrue(VCardUtils.containsOnlyPrintableAscii(builder.toString())); - assertTrue(VCardUtils.containsOnlyPrintableAscii("\r\n")); - assertFalse(VCardUtils.containsOnlyPrintableAscii("\u0019")); - assertFalse(VCardUtils.containsOnlyPrintableAscii("\u007F")); - } - - public void testContainsOnlyNonCrLfPrintableAscii() { - assertTrue(VCardUtils.containsOnlyNonCrLfPrintableAscii((String)null)); - assertTrue(VCardUtils.containsOnlyNonCrLfPrintableAscii((String[])null)); - assertTrue(VCardUtils.containsOnlyNonCrLfPrintableAscii((List)null)); - assertTrue(VCardUtils.containsOnlyNonCrLfPrintableAscii("")); - assertTrue(VCardUtils.containsOnlyNonCrLfPrintableAscii("abcdefghijklmnopqrstuvwxyz")); - assertTrue(VCardUtils.containsOnlyNonCrLfPrintableAscii("ABCDEFGHIJKLMNOPQRSTUVWXYZ")); - StringBuilder builder = new StringBuilder(); - for (int i = 0x20; i < 0x7F; i++) { - builder.append((char)i); - } - assertTrue(VCardUtils.containsOnlyNonCrLfPrintableAscii(builder.toString())); - assertFalse(VCardUtils.containsOnlyNonCrLfPrintableAscii("\u0019")); - assertFalse(VCardUtils.containsOnlyNonCrLfPrintableAscii("\u007F")); - assertFalse(VCardUtils.containsOnlyNonCrLfPrintableAscii("\r")); - assertFalse(VCardUtils.containsOnlyNonCrLfPrintableAscii("\n")); - } - - public void testContainsOnlyAlphaDigitHyphen() { - assertTrue(VCardUtils.containsOnlyAlphaDigitHyphen((String)null)); - assertTrue(VCardUtils.containsOnlyAlphaDigitHyphen((String[])null)); - assertTrue(VCardUtils.containsOnlyAlphaDigitHyphen((List)null)); - assertTrue(VCardUtils.containsOnlyAlphaDigitHyphen("")); - assertTrue(VCardUtils.containsOnlyNonCrLfPrintableAscii("abcdefghijklmnopqrstuvwxyz")); - assertTrue(VCardUtils.containsOnlyNonCrLfPrintableAscii("ABCDEFGHIJKLMNOPQRSTUVWXYZ")); - assertTrue(VCardUtils.containsOnlyNonCrLfPrintableAscii("0123456789-")); - for (int i = 0; i < 0x30; i++) { - if (i == 0x2D) { // - - continue; - } - assertFalse(VCardUtils.containsOnlyAlphaDigitHyphen(String.valueOf((char)i))); - } - for (int i = 0x3A; i < 0x41; i++) { - assertFalse(VCardUtils.containsOnlyAlphaDigitHyphen(String.valueOf((char)i))); - } - for (int i = 0x5B; i < 0x61; i++) { - assertFalse(VCardUtils.containsOnlyAlphaDigitHyphen(String.valueOf((char)i))); - } - for (int i = 0x7B; i < 0x100; i++) { - assertFalse(VCardUtils.containsOnlyAlphaDigitHyphen(String.valueOf((char)i))); - } - } -} diff --git a/vcard/tests/src/com/android/vcard/tests/test_utils/ContactEntry.java b/vcard/tests/src/com/android/vcard/tests/test_utils/ContactEntry.java deleted file mode 100644 index dff1f0510..000000000 --- a/vcard/tests/src/com/android/vcard/tests/test_utils/ContactEntry.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2010 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.vcard.tests.test_utils; - -import android.content.ContentValues; -import android.provider.ContactsContract.Data; - -import java.util.ArrayList; -import java.util.List; - -/** - *

- * The class representing one contact, which should contain multiple ContentValues like - * StructuredName, Email, etc. - *

- */ -public final class ContactEntry { - private final List mContentValuesList = new ArrayList(); - - public ContentValuesBuilder addContentValues(String mimeType) { - ContentValues contentValues = new ContentValues(); - contentValues.put(Data.MIMETYPE, mimeType); - mContentValuesList.add(contentValues); - return new ContentValuesBuilder(contentValues); - } - - public List getList() { - return mContentValuesList; - } -} \ No newline at end of file diff --git a/vcard/tests/src/com/android/vcard/tests/test_utils/ContentValuesBuilder.java b/vcard/tests/src/com/android/vcard/tests/test_utils/ContentValuesBuilder.java deleted file mode 100644 index fb53b8f9a..000000000 --- a/vcard/tests/src/com/android/vcard/tests/test_utils/ContentValuesBuilder.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * 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.vcard.tests.test_utils; - -import android.content.ContentValues; - -/** - * ContentValues-like class which enables users to chain put() methods and restricts - * the other methods. - */ -public class ContentValuesBuilder { - private final ContentValues mContentValues; - - public ContentValuesBuilder(final ContentValues contentValues) { - mContentValues = contentValues; - } - - public ContentValuesBuilder put(String key, String value) { - mContentValues.put(key, value); - return this; - } - - public ContentValuesBuilder put(String key, Byte value) { - mContentValues.put(key, value); - return this; - } - - public ContentValuesBuilder put(String key, Short value) { - mContentValues.put(key, value); - return this; - } - - public ContentValuesBuilder put(String key, Integer value) { - mContentValues.put(key, value); - return this; - } - - public ContentValuesBuilder put(String key, Long value) { - mContentValues.put(key, value); - return this; - } - - public ContentValuesBuilder put(String key, Float value) { - mContentValues.put(key, value); - return this; - } - - public ContentValuesBuilder put(String key, Double value) { - mContentValues.put(key, value); - return this; - } - - public ContentValuesBuilder put(String key, Boolean value) { - mContentValues.put(key, value); - return this; - } - - public ContentValuesBuilder put(String key, byte[] value) { - mContentValues.put(key, value); - return this; - } - - public ContentValuesBuilder putNull(String key) { - mContentValues.putNull(key); - return this; - } -} diff --git a/vcard/tests/src/com/android/vcard/tests/test_utils/ContentValuesVerifier.java b/vcard/tests/src/com/android/vcard/tests/test_utils/ContentValuesVerifier.java deleted file mode 100644 index 2b3e3ab4a..000000000 --- a/vcard/tests/src/com/android/vcard/tests/test_utils/ContentValuesVerifier.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * 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.vcard.tests.test_utils; - -import android.test.AndroidTestCase; - -import com.android.vcard.VCardConfig; -import com.android.vcard.VCardEntry; -import com.android.vcard.VCardEntryConstructor; -import com.android.vcard.VCardEntryHandler; -import com.android.vcard.VCardParser; -import com.android.vcard.VCardParser_V21; -import com.android.vcard.VCardParser_V30; -import com.android.vcard.exception.VCardException; - -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.List; - -public class ContentValuesVerifier implements VCardEntryHandler { - private AndroidTestCase mTestCase; - private List mContentValuesVerifierElemList = - new ArrayList(); - private int mIndex; - - public ContentValuesVerifierElem addElem(AndroidTestCase androidTestCase) { - mTestCase = androidTestCase; - ContentValuesVerifierElem importVerifier = new ContentValuesVerifierElem(androidTestCase); - mContentValuesVerifierElemList.add(importVerifier); - return importVerifier; - } - - public void verify(int resId, int vCardType) throws IOException, VCardException { - verify(mTestCase.getContext().getResources().openRawResource(resId), vCardType); - } - - public void verify(int resId, int vCardType, final VCardParser vCardParser) - throws IOException, VCardException { - verify(mTestCase.getContext().getResources().openRawResource(resId), - vCardType, vCardParser); - } - - public void verify(InputStream is, int vCardType) throws IOException, VCardException { - final VCardParser vCardParser; - if (VCardConfig.isV30(vCardType)) { - vCardParser = new VCardParser_V30(); - } else { - vCardParser = new VCardParser_V21(); - } - verify(is, vCardType, vCardParser); - } - - public void verify(InputStream is, int vCardType, final VCardParser vCardParser) - throws IOException, VCardException { - VCardEntryConstructor builder = new VCardEntryConstructor(vCardType, null); - builder.addEntryHandler(this); - try { - vCardParser.parse(is, builder); - } finally { - if (is != null) { - try { - is.close(); - } catch (IOException e) { - } - } - } - } - - public void onStart() { - for (ContentValuesVerifierElem elem : mContentValuesVerifierElemList) { - elem.onParsingStart(); - } - } - - public void onEntryCreated(VCardEntry entry) { - mTestCase.assertTrue(mIndex < mContentValuesVerifierElemList.size()); - mContentValuesVerifierElemList.get(mIndex).onEntryCreated(entry); - mIndex++; - } - - public void onEnd() { - for (ContentValuesVerifierElem elem : mContentValuesVerifierElemList) { - elem.onParsingEnd(); - elem.verifyResolver(); - } - } -} diff --git a/vcard/tests/src/com/android/vcard/tests/test_utils/ContentValuesVerifierElem.java b/vcard/tests/src/com/android/vcard/tests/test_utils/ContentValuesVerifierElem.java deleted file mode 100644 index 6c09693b1..000000000 --- a/vcard/tests/src/com/android/vcard/tests/test_utils/ContentValuesVerifierElem.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * 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.vcard.tests.test_utils; - -import android.content.ContentValues; -import android.provider.ContactsContract.Data; -import android.test.AndroidTestCase; - -import com.android.vcard.VCardConfig; -import com.android.vcard.VCardEntry; -import com.android.vcard.VCardEntryCommitter; -import com.android.vcard.VCardEntryConstructor; -import com.android.vcard.VCardEntryHandler; -import com.android.vcard.VCardParser; -import com.android.vcard.VCardParser_V21; -import com.android.vcard.VCardParser_V30; -import com.android.vcard.exception.VCardException; - -import java.io.IOException; -import java.io.InputStream; - -public class ContentValuesVerifierElem { - private final AndroidTestCase mTestCase; - private final ImportTestResolver mResolver; - private final VCardEntryHandler mHandler; - - public ContentValuesVerifierElem(AndroidTestCase androidTestCase) { - mTestCase = androidTestCase; - mResolver = new ImportTestResolver(androidTestCase); - mHandler = new VCardEntryCommitter(mResolver); - } - - public ContentValuesBuilder addExpected(String mimeType) { - ContentValues contentValues = new ContentValues(); - contentValues.put(Data.MIMETYPE, mimeType); - mResolver.addExpectedContentValues(contentValues); - return new ContentValuesBuilder(contentValues); - } - - public void verify(int resId, int vCardType) - throws IOException, VCardException { - verify(mTestCase.getContext().getResources().openRawResource(resId), vCardType); - } - - public void verify(InputStream is, int vCardType) throws IOException, VCardException { - final VCardParser vCardParser; - if (VCardConfig.isV30(vCardType)) { - vCardParser = new VCardParser_V30(); - } else { - vCardParser = new VCardParser_V21(); - } - final VCardEntryConstructor builder = new VCardEntryConstructor(vCardType, null); - builder.addEntryHandler(mHandler); - try { - vCardParser.parse(is, builder); - } finally { - if (is != null) { - try { - is.close(); - } catch (IOException e) { - } - } - } - verifyResolver(); - } - - public void verifyResolver() { - mResolver.verify(); - } - - public void onParsingStart() { - mHandler.onStart(); - } - - public void onEntryCreated(VCardEntry entry) { - mHandler.onEntryCreated(entry); - } - - public void onParsingEnd() { - mHandler.onEnd(); - } -} diff --git a/vcard/tests/src/com/android/vcard/tests/test_utils/ExportTestProvider.java b/vcard/tests/src/com/android/vcard/tests/test_utils/ExportTestProvider.java deleted file mode 100644 index caedf9dca..000000000 --- a/vcard/tests/src/com/android/vcard/tests/test_utils/ExportTestProvider.java +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright (C) 2010 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.vcard.tests.test_utils; - -import android.content.ContentResolver; -import android.content.ContentValues; -import android.content.Entity; -import android.content.EntityIterator; -import android.database.Cursor; -import android.net.Uri; -import android.provider.ContactsContract.Contacts; -import android.provider.ContactsContract.Data; -import android.provider.ContactsContract.RawContacts; -import android.test.mock.MockContentProvider; -import android.test.mock.MockCursor; - -import com.android.vcard.VCardComposer; - -import junit.framework.TestCase; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -/* package */ class ExportTestProvider extends MockContentProvider { - final private TestCase mTestCase; - final private ArrayList mContactEntryList = new ArrayList(); - - private static class MockEntityIterator implements EntityIterator { - List mEntityList; - Iterator mIterator; - - public MockEntityIterator(List contentValuesList) { - mEntityList = new ArrayList(); - Entity entity = new Entity(new ContentValues()); - for (ContentValues contentValues : contentValuesList) { - entity.addSubValue(Data.CONTENT_URI, contentValues); - } - mEntityList.add(entity); - mIterator = mEntityList.iterator(); - } - - public boolean hasNext() { - return mIterator.hasNext(); - } - - public Entity next() { - return mIterator.next(); - } - - public void remove() { - throw new UnsupportedOperationException("remove not supported"); - } - - public void reset() { - mIterator = mEntityList.iterator(); - } - - public void close() { - } - } - - public ExportTestProvider(TestCase testCase) { - mTestCase = testCase; - } - - public ContactEntry buildInputEntry() { - ContactEntry contactEntry = new ContactEntry(); - mContactEntryList.add(contactEntry); - return contactEntry; - } - - /** - *

- * An old method which had existed but was removed from ContentResolver. - *

- *

- * We still keep using this method since we don't have a propeer way to know - * which value in the ContentValue corresponds to the entry in Contacts database. - *

- */ - public EntityIterator queryEntities(Uri uri, - String selection, String[] selectionArgs, String sortOrder) { - mTestCase.assertTrue(uri != null); - mTestCase.assertTrue(ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())); - final String authority = uri.getAuthority(); - mTestCase.assertTrue(RawContacts.CONTENT_URI.getAuthority().equals(authority)); - mTestCase.assertTrue((Data.CONTACT_ID + "=?").equals(selection)); - mTestCase.assertEquals(1, selectionArgs.length); - final int id = Integer.parseInt(selectionArgs[0]); - mTestCase.assertTrue(id >= 0 && id < mContactEntryList.size()); - - return new MockEntityIterator(mContactEntryList.get(id).getList()); - } - - @Override - public Cursor query(Uri uri,String[] projection, - String selection, String[] selectionArgs, String sortOrder) { - mTestCase.assertTrue(VCardComposer.CONTACTS_TEST_CONTENT_URI.equals(uri)); - // In this test, following arguments are not supported. - mTestCase.assertNull(selection); - mTestCase.assertNull(selectionArgs); - mTestCase.assertNull(sortOrder); - - return new MockCursor() { - int mCurrentPosition = -1; - - @Override - public int getCount() { - return mContactEntryList.size(); - } - - @Override - public boolean moveToFirst() { - mCurrentPosition = 0; - return true; - } - - @Override - public boolean moveToNext() { - if (mCurrentPosition < mContactEntryList.size()) { - mCurrentPosition++; - return true; - } else { - return false; - } - } - - @Override - public boolean isBeforeFirst() { - return mCurrentPosition < 0; - } - - @Override - public boolean isAfterLast() { - return mCurrentPosition >= mContactEntryList.size(); - } - - @Override - public int getColumnIndex(String columnName) { - mTestCase.assertEquals(Contacts._ID, columnName); - return 0; - } - - @Override - public int getInt(int columnIndex) { - mTestCase.assertEquals(0, columnIndex); - mTestCase.assertTrue(mCurrentPosition >= 0 - && mCurrentPosition < mContactEntryList.size()); - return mCurrentPosition; - } - - @Override - public String getString(int columnIndex) { - return String.valueOf(getInt(columnIndex)); - } - - @Override - public void close() { - } - }; - } -} \ No newline at end of file diff --git a/vcard/tests/src/com/android/vcard/tests/test_utils/ExportTestResolver.java b/vcard/tests/src/com/android/vcard/tests/test_utils/ExportTestResolver.java deleted file mode 100644 index 3cd014ce4..000000000 --- a/vcard/tests/src/com/android/vcard/tests/test_utils/ExportTestResolver.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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.vcard.tests.test_utils; - -import android.provider.ContactsContract.RawContacts; -import android.test.mock.MockContentResolver; - -import com.android.vcard.VCardComposer; - -import junit.framework.TestCase; - -/* package */ class ExportTestResolver extends MockContentResolver { - private final ExportTestProvider mProvider; - public ExportTestResolver(TestCase testCase) { - mProvider = new ExportTestProvider(testCase); - addProvider(VCardComposer.VCARD_TEST_AUTHORITY, mProvider); - addProvider(RawContacts.CONTENT_URI.getAuthority(), mProvider); - } - - public ContactEntry addInputContactEntry() { - return mProvider.buildInputEntry(); - } - - public ExportTestProvider getProvider() { - return mProvider; - } -} diff --git a/vcard/tests/src/com/android/vcard/tests/test_utils/ImportTestProvider.java b/vcard/tests/src/com/android/vcard/tests/test_utils/ImportTestProvider.java deleted file mode 100644 index 3d7cb60a0..000000000 --- a/vcard/tests/src/com/android/vcard/tests/test_utils/ImportTestProvider.java +++ /dev/null @@ -1,274 +0,0 @@ -/* - * Copyright (C) 2010 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.vcard.tests.test_utils; - -import android.content.ContentProviderOperation; -import android.content.ContentProviderResult; -import android.content.ContentValues; -import android.net.Uri; -import android.provider.ContactsContract.Data; -import android.provider.ContactsContract.RawContacts; -import android.provider.ContactsContract.CommonDataKinds.Email; -import android.provider.ContactsContract.CommonDataKinds.Event; -import android.provider.ContactsContract.CommonDataKinds.GroupMembership; -import android.provider.ContactsContract.CommonDataKinds.Im; -import android.provider.ContactsContract.CommonDataKinds.Nickname; -import android.provider.ContactsContract.CommonDataKinds.Note; -import android.provider.ContactsContract.CommonDataKinds.Organization; -import android.provider.ContactsContract.CommonDataKinds.Phone; -import android.provider.ContactsContract.CommonDataKinds.Photo; -import android.provider.ContactsContract.CommonDataKinds.Relation; -import android.provider.ContactsContract.CommonDataKinds.StructuredName; -import android.provider.ContactsContract.CommonDataKinds.StructuredPostal; -import android.provider.ContactsContract.CommonDataKinds.Website; -import android.test.mock.MockContentProvider; -import android.text.TextUtils; -import android.util.Log; - -import junit.framework.TestCase; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.SortedMap; -import java.util.TreeMap; -import java.util.Map.Entry; - -/* package */ class ImportTestProvider extends MockContentProvider { - private static final Set sKnownMimeTypeSet = - new HashSet(Arrays.asList(StructuredName.CONTENT_ITEM_TYPE, - Nickname.CONTENT_ITEM_TYPE, Phone.CONTENT_ITEM_TYPE, - Email.CONTENT_ITEM_TYPE, StructuredPostal.CONTENT_ITEM_TYPE, - Im.CONTENT_ITEM_TYPE, Organization.CONTENT_ITEM_TYPE, - Event.CONTENT_ITEM_TYPE, Photo.CONTENT_ITEM_TYPE, - Note.CONTENT_ITEM_TYPE, Website.CONTENT_ITEM_TYPE, - Relation.CONTENT_ITEM_TYPE, Event.CONTENT_ITEM_TYPE, - GroupMembership.CONTENT_ITEM_TYPE)); - - final Map> mMimeTypeToExpectedContentValues; - - private final TestCase mTestCase; - - public ImportTestProvider(TestCase testCase) { - mTestCase = testCase; - mMimeTypeToExpectedContentValues = - new HashMap>(); - for (String acceptanbleMimeType : sKnownMimeTypeSet) { - // Do not use HashSet since the current implementation changes the content of - // ContentValues after the insertion, which make the result of hashCode() - // changes... - mMimeTypeToExpectedContentValues.put( - acceptanbleMimeType, new ArrayList()); - } - } - - public void addExpectedContentValues(ContentValues expectedContentValues) { - final String mimeType = expectedContentValues.getAsString(Data.MIMETYPE); - if (!sKnownMimeTypeSet.contains(mimeType)) { - mTestCase.fail(String.format( - "Unknow MimeType %s in the test code. Test code should be broken.", - mimeType)); - } - - final Collection contentValuesCollection = - mMimeTypeToExpectedContentValues.get(mimeType); - contentValuesCollection.add(expectedContentValues); - } - - @Override - public ContentProviderResult[] applyBatch( - ArrayList operations) { - if (operations == null) { - mTestCase.fail("There is no operation."); - } - - final int size = operations.size(); - ContentProviderResult[] fakeResultArray = new ContentProviderResult[size]; - for (int i = 0; i < size; i++) { - Uri uri = Uri.withAppendedPath(RawContacts.CONTENT_URI, String.valueOf(i)); - fakeResultArray[i] = new ContentProviderResult(uri); - } - - for (int i = 0; i < size; i++) { - ContentProviderOperation operation = operations.get(i); - ContentValues contentValues = operation.resolveValueBackReferences( - fakeResultArray, i); - } - for (int i = 0; i < size; i++) { - ContentProviderOperation operation = operations.get(i); - ContentValues actualContentValues = operation.resolveValueBackReferences( - fakeResultArray, i); - final Uri uri = operation.getUri(); - if (uri.equals(RawContacts.CONTENT_URI)) { - mTestCase.assertNull(actualContentValues.get(RawContacts.ACCOUNT_NAME)); - mTestCase.assertNull(actualContentValues.get(RawContacts.ACCOUNT_TYPE)); - } else if (uri.equals(Data.CONTENT_URI)) { - final String mimeType = actualContentValues.getAsString(Data.MIMETYPE); - if (!sKnownMimeTypeSet.contains(mimeType)) { - mTestCase.fail(String.format( - "Unknown MimeType %s. Probably added after developing this test", - mimeType)); - } - // Remove data meaningless in this unit tests. - // Specifically, Data.DATA1 - DATA7 are set to null or empty String - // regardless of the input, but it may change depending on how - // resolver-related code handles it. - // Here, we ignore these implementation-dependent specs and - // just check whether vCard importer correctly inserts rellevent data. - Set keyToBeRemoved = new HashSet(); - for (Entry entry : actualContentValues.valueSet()) { - Object value = entry.getValue(); - if (value == null || TextUtils.isEmpty(value.toString())) { - keyToBeRemoved.add(entry.getKey()); - } - } - for (String key: keyToBeRemoved) { - actualContentValues.remove(key); - } - /* for testing - Log.d("@@@", - String.format("MimeType: %s, data: %s", - mimeType, actualContentValues.toString())); */ - // Remove RAW_CONTACT_ID entry just for safety, since we do not care - // how resolver-related code handles the entry in this unit test, - if (actualContentValues.containsKey(Data.RAW_CONTACT_ID)) { - actualContentValues.remove(Data.RAW_CONTACT_ID); - } - final Collection contentValuesCollection = - mMimeTypeToExpectedContentValues.get(mimeType); - if (contentValuesCollection.isEmpty()) { - mTestCase.fail("ContentValues for MimeType " + mimeType - + " is not expected at all (" + actualContentValues + ")"); - } - boolean checked = false; - for (ContentValues expectedContentValues : contentValuesCollection) { - /*for testing - Log.d("@@@", "expected: " - + convertToEasilyReadableString(expectedContentValues)); - Log.d("@@@", "actual : " - + convertToEasilyReadableString(actualContentValues));*/ - if (equalsForContentValues(expectedContentValues, - actualContentValues)) { - mTestCase.assertTrue(contentValuesCollection.remove(expectedContentValues)); - checked = true; - break; - } - } - if (!checked) { - final StringBuilder builder = new StringBuilder(); - builder.append("Unexpected: "); - builder.append(convertToEasilyReadableString(actualContentValues)); - builder.append("\nExpected: "); - for (ContentValues expectedContentValues : contentValuesCollection) { - builder.append(convertToEasilyReadableString(expectedContentValues)); - } - mTestCase.fail(builder.toString()); - } - } else { - mTestCase.fail("Unexpected Uri has come: " + uri); - } - } // for (int i = 0; i < size; i++) { - return fakeResultArray; - } - - public void verify() { - StringBuilder builder = new StringBuilder(); - for (Collection contentValuesCollection : - mMimeTypeToExpectedContentValues.values()) { - for (ContentValues expectedContentValues: contentValuesCollection) { - builder.append(convertToEasilyReadableString(expectedContentValues)); - builder.append("\n"); - } - } - if (builder.length() > 0) { - final String failMsg = - "There is(are) remaining expected ContentValues instance(s): \n" - + builder.toString(); - mTestCase.fail(failMsg); - } - } - - /** - * Utility method to print ContentValues whose content is printed with sorted keys. - */ - private String convertToEasilyReadableString(ContentValues contentValues) { - if (contentValues == null) { - return "null"; - } - String mimeTypeValue = ""; - SortedMap sortedMap = new TreeMap(); - for (Entry entry : contentValues.valueSet()) { - final String key = entry.getKey(); - final Object value = entry.getValue(); - final String valueString = (value != null ? value.toString() : null); - if (Data.MIMETYPE.equals(key)) { - mimeTypeValue = valueString; - } else { - mTestCase.assertNotNull(key); - sortedMap.put(key, valueString); - } - } - StringBuilder builder = new StringBuilder(); - builder.append(Data.MIMETYPE); - builder.append('='); - builder.append(mimeTypeValue); - for (Entry entry : sortedMap.entrySet()) { - final String key = entry.getKey(); - final String value = entry.getValue(); - builder.append(' '); - builder.append(key); - builder.append("=\""); - builder.append(value); - builder.append('"'); - } - return builder.toString(); - } - - private static boolean equalsForContentValues( - ContentValues expected, ContentValues actual) { - if (expected == actual) { - return true; - } else if (expected == null || actual == null || expected.size() != actual.size()) { - return false; - } - - for (Entry entry : expected.valueSet()) { - final String key = entry.getKey(); - final Object value = entry.getValue(); - if (!actual.containsKey(key)) { - return false; - } - if (value instanceof byte[]) { - Object actualValue = actual.get(key); - if (!Arrays.equals((byte[])value, (byte[])actualValue)) { - byte[] e = (byte[])value; - byte[] a = (byte[])actualValue; - Log.d("@@@", "expected (len: " + e.length + "): " + Arrays.toString(e)); - Log.d("@@@", "actual (len: " + a.length + "): " + Arrays.toString(a)); - return false; - } - } else if (!value.equals(actual.get(key))) { - Log.d("@@@", "different."); - return false; - } - } - return true; - } -} \ No newline at end of file diff --git a/vcard/tests/src/com/android/vcard/tests/test_utils/ImportTestResolver.java b/vcard/tests/src/com/android/vcard/tests/test_utils/ImportTestResolver.java deleted file mode 100644 index 645e9dbea..000000000 --- a/vcard/tests/src/com/android/vcard/tests/test_utils/ImportTestResolver.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * 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.vcard.tests.test_utils; - -import android.content.ContentProviderOperation; -import android.content.ContentProviderResult; -import android.content.ContentValues; -import android.provider.ContactsContract.RawContacts; -import android.test.mock.MockContentResolver; - -import junit.framework.TestCase; - -import java.util.ArrayList; - -/* package */ class ImportTestResolver extends MockContentResolver { - private final ImportTestProvider mProvider; - - public ImportTestResolver(TestCase testCase) { - mProvider = new ImportTestProvider(testCase); - } - - @Override - public ContentProviderResult[] applyBatch(String authority, - ArrayList operations) { - equalsString(authority, RawContacts.CONTENT_URI.toString()); - return mProvider.applyBatch(operations); - } - - public void addExpectedContentValues(ContentValues expectedContentValues) { - mProvider.addExpectedContentValues(expectedContentValues); - } - - public void verify() { - mProvider.verify(); - } - - private static boolean equalsString(String a, String b) { - if (a == null || a.length() == 0) { - return b == null || b.length() == 0; - } else { - return a.equals(b); - } - } -} diff --git a/vcard/tests/src/com/android/vcard/tests/test_utils/LineVerifier.java b/vcard/tests/src/com/android/vcard/tests/test_utils/LineVerifier.java deleted file mode 100644 index d8cfe5b48..000000000 --- a/vcard/tests/src/com/android/vcard/tests/test_utils/LineVerifier.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * 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.vcard.tests.test_utils; - -import com.android.vcard.VCardComposer; - -import android.content.Context; - -import junit.framework.TestCase; - -import java.util.ArrayList; - -public class LineVerifier implements VCardComposer.OneEntryHandler { - private final TestCase mTestCase; - private final ArrayList mLineVerifierElemList; - private int mVCardType; - private int index; - - public LineVerifier(TestCase testCase, int vcardType) { - mTestCase = testCase; - mLineVerifierElemList = new ArrayList(); - mVCardType = vcardType; - } - - public LineVerifierElem addLineVerifierElem() { - LineVerifierElem lineVerifier = new LineVerifierElem(mTestCase, mVCardType); - mLineVerifierElemList.add(lineVerifier); - return lineVerifier; - } - - public void verify(String vcard) { - if (index >= mLineVerifierElemList.size()) { - mTestCase.fail("Insufficient number of LineVerifier (" + index + ")"); - } - - LineVerifierElem lineVerifier = mLineVerifierElemList.get(index); - lineVerifier.verify(vcard); - - index++; - } - - public boolean onEntryCreated(String vcard) { - verify(vcard); - return true; - } - - public boolean onInit(Context context) { - return true; - } - - public void onTerminate() { - } -} diff --git a/vcard/tests/src/com/android/vcard/tests/test_utils/LineVerifierElem.java b/vcard/tests/src/com/android/vcard/tests/test_utils/LineVerifierElem.java deleted file mode 100644 index 3ec6ba39b..000000000 --- a/vcard/tests/src/com/android/vcard/tests/test_utils/LineVerifierElem.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * 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.vcard.tests.test_utils; - -import android.text.TextUtils; - -import com.android.vcard.VCardConfig; - -import junit.framework.TestCase; - -import java.util.ArrayList; -import java.util.List; - -public class LineVerifierElem { - private final TestCase mTestCase; - private final List mExpectedLineList = new ArrayList(); - private final boolean mIsV30; - - public LineVerifierElem(TestCase testCase, int vcardType) { - mTestCase = testCase; - mIsV30 = VCardConfig.isV30(vcardType); - } - - public LineVerifierElem addExpected(final String line) { - if (!TextUtils.isEmpty(line)) { - mExpectedLineList.add(line); - } - return this; - } - - public void verify(final String vcard) { - final String[] lineArray = vcard.split("\\r?\\n"); - final int length = lineArray.length; - boolean beginExists = false; - boolean endExists = false; - boolean versionExists = false; - - for (int i = 0; i < length; i++) { - final String line = lineArray[i]; - if (TextUtils.isEmpty(line)) { - continue; - } - - if ("BEGIN:VCARD".equalsIgnoreCase(line)) { - if (beginExists) { - mTestCase.fail("Multiple \"BEGIN:VCARD\" line found"); - } else { - beginExists = true; - continue; - } - } else if ("END:VCARD".equalsIgnoreCase(line)) { - if (endExists) { - mTestCase.fail("Multiple \"END:VCARD\" line found"); - } else { - endExists = true; - continue; - } - } else if ((mIsV30 ? "VERSION:3.0" : "VERSION:2.1").equalsIgnoreCase(line)) { - if (versionExists) { - mTestCase.fail("Multiple VERSION line + found"); - } else { - versionExists = true; - continue; - } - } - - if (!beginExists) { - mTestCase.fail("Property other than BEGIN came before BEGIN property: " - + line); - } else if (endExists) { - mTestCase.fail("Property other than END came after END property: " - + line); - } - - final int index = mExpectedLineList.indexOf(line); - if (index >= 0) { - mExpectedLineList.remove(index); - } else { - mTestCase.fail("Unexpected line: " + line); - } - } - - if (!mExpectedLineList.isEmpty()) { - StringBuffer buffer = new StringBuffer(); - for (String expectedLine : mExpectedLineList) { - buffer.append(expectedLine); - buffer.append("\n"); - } - - mTestCase.fail("Expected line(s) not found:" + buffer.toString()); - } - } -} diff --git a/vcard/tests/src/com/android/vcard/tests/test_utils/PropertyNode.java b/vcard/tests/src/com/android/vcard/tests/test_utils/PropertyNode.java deleted file mode 100644 index 14c8d6cb2..000000000 --- a/vcard/tests/src/com/android/vcard/tests/test_utils/PropertyNode.java +++ /dev/null @@ -1,205 +0,0 @@ -/* - * 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.vcard.tests.test_utils; - -import android.content.ContentValues; - -import com.android.vcard.VCardEntry; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -/** - *

- * The class representing one property (e.g. "N;ENCODING=UTF-8:family:given:middle:prefix:suffix"). - *

- *

- * Previously used in main vCard handling code but now exists only for testing. - *

- *

- * Especially useful for testing parser code (VCardParser), since all properties can be - * checked via this class unlike {@link VCardEntry}, which only emits the result of - * interpretation of the content of each vCard. We cannot know whether vCard parser or - * {@link VCardEntry} is wrong without this class. - *

- */ -public class PropertyNode { - public String propName; - public String propValue; - public List propValue_vector; - - /** Store value as byte[],after decode. - * Used when propValue is encoded by something like BASE64, QUOTED-PRINTABLE, etc. - */ - public byte[] propValue_bytes; - - /** - * param store: key=paramType, value=paramValue - * Note that currently PropertyNode class does not support multiple param-values - * defined in vCard 3.0 (See also RFC 2426). multiple-values are stored as - * one String value like "A,B", not ["A", "B"]... - * TODO: fix this. - */ - public ContentValues paramMap; - - /** Only for TYPE=??? param store. */ - public Set paramMap_TYPE; - - /** Store group values. Used only in VCard. */ - public Set propGroupSet; - - public PropertyNode() { - propName = ""; - propValue = ""; - propValue_vector = new ArrayList(); - paramMap = new ContentValues(); - paramMap_TYPE = new HashSet(); - propGroupSet = new HashSet(); - } - - public PropertyNode( - String propName, String propValue, List propValue_vector, - byte[] propValue_bytes, ContentValues paramMap, Set paramMap_TYPE, - Set propGroupSet) { - if (propName != null) { - this.propName = propName; - } else { - this.propName = ""; - } - if (propValue != null) { - this.propValue = propValue; - } else { - this.propValue = ""; - } - if (propValue_vector != null) { - this.propValue_vector = propValue_vector; - } else { - this.propValue_vector = new ArrayList(); - } - this.propValue_bytes = propValue_bytes; - if (paramMap != null) { - this.paramMap = paramMap; - } else { - this.paramMap = new ContentValues(); - } - if (paramMap_TYPE != null) { - this.paramMap_TYPE = paramMap_TYPE; - } else { - this.paramMap_TYPE = new HashSet(); - } - if (propGroupSet != null) { - this.propGroupSet = propGroupSet; - } else { - this.propGroupSet = new HashSet(); - } - } - - @Override - public int hashCode() { - // vCard may contain more than one same line in one entry, while HashSet or any other - // library which utilize hashCode() does not honor that, so intentionally throw an - // Exception. - throw new UnsupportedOperationException( - "PropertyNode does not provide hashCode() implementation intentionally."); - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof PropertyNode)) { - return false; - } - - PropertyNode node = (PropertyNode)obj; - - if (propName == null || !propName.equals(node.propName)) { - return false; - } else if (!paramMap_TYPE.equals(node.paramMap_TYPE)) { - return false; - } else if (!paramMap_TYPE.equals(node.paramMap_TYPE)) { - return false; - } else if (!propGroupSet.equals(node.propGroupSet)) { - return false; - } - - if (propValue_bytes != null && Arrays.equals(propValue_bytes, node.propValue_bytes)) { - return true; - } else { - if (!propValue.equals(node.propValue)) { - return false; - } - - // The value in propValue_vector is not decoded even if it should be - // decoded by BASE64 or QUOTED-PRINTABLE. When the size of propValue_vector - // is 1, the encoded value is stored in propValue, so we do not have to - // check it. - return (propValue_vector.equals(node.propValue_vector) || - propValue_vector.size() == 1 || - node.propValue_vector.size() == 1); - } - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append("propName: "); - builder.append(propName); - builder.append(", paramMap: "); - builder.append(paramMap.toString()); - builder.append(", paramMap_TYPE: ["); - boolean first = true; - for (String elem : paramMap_TYPE) { - if (first) { - first = false; - } else { - builder.append(", "); - } - builder.append('"'); - builder.append(elem); - builder.append('"'); - } - builder.append("]"); - if (!propGroupSet.isEmpty()) { - builder.append(", propGroupSet: ["); - first = true; - for (String elem : propGroupSet) { - if (first) { - first = false; - } else { - builder.append(", "); - } - builder.append('"'); - builder.append(elem); - builder.append('"'); - } - builder.append("]"); - } - if (propValue_vector != null && propValue_vector.size() > 1) { - builder.append(", propValue_vector size: "); - builder.append(propValue_vector.size()); - } - if (propValue_bytes != null) { - builder.append(", propValue_bytes size: "); - builder.append(propValue_bytes.length); - } - builder.append(", propValue: \""); - builder.append(propValue); - builder.append("\""); - return builder.toString(); - } -} diff --git a/vcard/tests/src/com/android/vcard/tests/test_utils/PropertyNodesVerifier.java b/vcard/tests/src/com/android/vcard/tests/test_utils/PropertyNodesVerifier.java deleted file mode 100644 index de33a36a4..000000000 --- a/vcard/tests/src/com/android/vcard/tests/test_utils/PropertyNodesVerifier.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * 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.vcard.tests.test_utils; - -import android.test.AndroidTestCase; - -import com.android.vcard.VCardConfig; -import com.android.vcard.VCardParser; -import com.android.vcard.VCardParser_V21; -import com.android.vcard.VCardParser_V30; -import com.android.vcard.exception.VCardException; - -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.List; - -public class PropertyNodesVerifier extends VNodeBuilder { - private final List mPropertyNodesVerifierElemList; - private final AndroidTestCase mAndroidTestCase; - private int mIndex; - - public PropertyNodesVerifier(AndroidTestCase testCase) { - super(); - mPropertyNodesVerifierElemList = new ArrayList(); - mAndroidTestCase = testCase; - } - - public PropertyNodesVerifierElem addPropertyNodesVerifierElem() { - PropertyNodesVerifierElem elem = new PropertyNodesVerifierElem(mAndroidTestCase); - mPropertyNodesVerifierElemList.add(elem); - return elem; - } - - public void verify(int resId, int vCardType) - throws IOException, VCardException { - verify(mAndroidTestCase.getContext().getResources().openRawResource(resId), vCardType); - } - - public void verify(int resId, int vCardType, final VCardParser vCardParser) - throws IOException, VCardException { - verify(mAndroidTestCase.getContext().getResources().openRawResource(resId), - vCardType, vCardParser); - } - - public void verify(InputStream is, int vCardType) throws IOException, VCardException { - final VCardParser vCardParser; - if (VCardConfig.isV30(vCardType)) { - vCardParser = new VCardParser_V30(); - } else { - vCardParser = new VCardParser_V21(); - } - verify(is, vCardType, vCardParser); - } - - public void verify(InputStream is, int vCardType, final VCardParser vCardParser) - throws IOException, VCardException { - try { - vCardParser.parse(is, this); - } finally { - if (is != null) { - try { - is.close(); - } catch (IOException e) { - } - } - } - } - - @Override - public void endEntry() { - super.endEntry(); - mAndroidTestCase.assertTrue(mIndex < mPropertyNodesVerifierElemList.size()); - mAndroidTestCase.assertTrue(mIndex < vNodeList.size()); - mPropertyNodesVerifierElemList.get(mIndex).verify(vNodeList.get(mIndex)); - mIndex++; - } -} diff --git a/vcard/tests/src/com/android/vcard/tests/test_utils/PropertyNodesVerifierElem.java b/vcard/tests/src/com/android/vcard/tests/test_utils/PropertyNodesVerifierElem.java deleted file mode 100644 index 6eb84983e..000000000 --- a/vcard/tests/src/com/android/vcard/tests/test_utils/PropertyNodesVerifierElem.java +++ /dev/null @@ -1,317 +0,0 @@ -/* - * Copyright (C) 2010 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.vcard.tests.test_utils; - -import android.content.ContentValues; - -import junit.framework.TestCase; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; - -/** - * Utility class which verifies input VNode. - * - * This class first checks whether each propertyNode in the VNode is in the - * "ordered expected property list". - * If the node does not exist in the "ordered list", the class refers to - * "unorderd expected property set" and checks the node is expected somewhere. - */ -public class PropertyNodesVerifierElem { - public static class TypeSet extends HashSet { - public TypeSet(String ... array) { - super(Arrays.asList(array)); - } - } - - public static class GroupSet extends HashSet { - public GroupSet(String ... array) { - super(Arrays.asList(array)); - } - } - - private final HashMap> mOrderedNodeMap; - // Intentionally use ArrayList instead of Set, assuming there may be more than one - // exactly same objects. - private final ArrayList mUnorderedNodeList; - private final TestCase mTestCase; - - public PropertyNodesVerifierElem(TestCase testCase) { - mOrderedNodeMap = new HashMap>(); - mUnorderedNodeList = new ArrayList(); - mTestCase = testCase; - } - - // WithOrder - - public PropertyNodesVerifierElem addExpectedNodeWithOrder(String propName, String propValue) { - return addExpectedNodeWithOrder(propName, propValue, null, null, null, null, null); - } - - public PropertyNodesVerifierElem addExpectedNodeWithOrder( - String propName, String propValue, ContentValues contentValues) { - return addExpectedNodeWithOrder(propName, propValue, null, - null, contentValues, null, null); - } - - public PropertyNodesVerifierElem addExpectedNodeWithOrder( - String propName, List propValueList, ContentValues contentValues) { - return addExpectedNodeWithOrder(propName, null, propValueList, - null, contentValues, null, null); - } - - public PropertyNodesVerifierElem addExpectedNodeWithOrder( - String propName, String propValue, List propValueList) { - return addExpectedNodeWithOrder(propName, propValue, propValueList, null, - null, null, null); - } - - public PropertyNodesVerifierElem addExpectedNodeWithOrder( - String propName, List propValueList) { - final String propValue = concatinateListWithSemiColon(propValueList); - return addExpectedNodeWithOrder(propName, propValue.toString(), propValueList, - null, null, null, null); - } - - public PropertyNodesVerifierElem addExpectedNodeWithOrder(String propName, String propValue, - TypeSet paramMap_TYPE) { - return addExpectedNodeWithOrder(propName, propValue, null, - null, null, paramMap_TYPE, null); - } - - public PropertyNodesVerifierElem addExpectedNodeWithOrder(String propName, - List propValueList, TypeSet paramMap_TYPE) { - return addExpectedNodeWithOrder(propName, null, propValueList, null, null, - paramMap_TYPE, null); - } - - public PropertyNodesVerifierElem addExpectedNodeWithOrder(String propName, String propValue, - ContentValues paramMap, TypeSet paramMap_TYPE) { - return addExpectedNodeWithOrder(propName, propValue, null, null, - paramMap, paramMap_TYPE, null); - } - - public PropertyNodesVerifierElem addExpectedNodeWithOrder(String propName, String propValue, - List propValueList, TypeSet paramMap_TYPE) { - return addExpectedNodeWithOrder(propName, propValue, propValueList, null, null, - paramMap_TYPE, null); - } - - public PropertyNodesVerifierElem addExpectedNodeWithOrder(String propName, String propValue, - List propValueList, byte[] propValue_bytes, - ContentValues paramMap, TypeSet paramMap_TYPE, GroupSet propGroupSet) { - if (propValue == null && propValueList != null) { - propValue = concatinateListWithSemiColon(propValueList); - } - PropertyNode propertyNode = new PropertyNode(propName, - propValue, propValueList, propValue_bytes, - paramMap, paramMap_TYPE, propGroupSet); - List expectedNodeList = mOrderedNodeMap.get(propName); - if (expectedNodeList == null) { - expectedNodeList = new ArrayList(); - mOrderedNodeMap.put(propName, expectedNodeList); - } - expectedNodeList.add(propertyNode); - return this; - } - - // WithoutOrder - - public PropertyNodesVerifierElem addExpectedNode(String propName, String propValue) { - return addExpectedNode(propName, propValue, null, null, null, null, null); - } - - public PropertyNodesVerifierElem addExpectedNode(String propName, String propValue, - ContentValues contentValues) { - return addExpectedNode(propName, propValue, null, null, contentValues, null, null); - } - - public PropertyNodesVerifierElem addExpectedNode(String propName, - List propValueList, ContentValues contentValues) { - return addExpectedNode(propName, null, - propValueList, null, contentValues, null, null); - } - - public PropertyNodesVerifierElem addExpectedNode(String propName, String propValue, - List propValueList) { - return addExpectedNode(propName, propValue, propValueList, null, null, null, null); - } - - public PropertyNodesVerifierElem addExpectedNode(String propName, - List propValueList) { - return addExpectedNode(propName, null, propValueList, - null, null, null, null); - } - - public PropertyNodesVerifierElem addExpectedNode(String propName, String propValue, - TypeSet paramMap_TYPE) { - return addExpectedNode(propName, propValue, null, null, null, paramMap_TYPE, null); - } - - public PropertyNodesVerifierElem addExpectedNode(String propName, - List propValueList, TypeSet paramMap_TYPE) { - final String propValue = concatinateListWithSemiColon(propValueList); - return addExpectedNode(propName, propValue, propValueList, null, null, - paramMap_TYPE, null); - } - - public PropertyNodesVerifierElem addExpectedNode(String propName, String propValue, - List propValueList, TypeSet paramMap_TYPE) { - return addExpectedNode(propName, propValue, propValueList, null, null, - paramMap_TYPE, null); - } - - public PropertyNodesVerifierElem addExpectedNode(String propName, String propValue, - ContentValues paramMap, TypeSet paramMap_TYPE) { - return addExpectedNode(propName, propValue, null, null, - paramMap, paramMap_TYPE, null); - } - - public PropertyNodesVerifierElem addExpectedNode(String propName, String propValue, - List propValueList, byte[] propValue_bytes, - ContentValues paramMap, TypeSet paramMap_TYPE, GroupSet propGroupSet) { - if (propValue == null && propValueList != null) { - propValue = concatinateListWithSemiColon(propValueList); - } - mUnorderedNodeList.add(new PropertyNode(propName, propValue, - propValueList, propValue_bytes, paramMap, paramMap_TYPE, propGroupSet)); - return this; - } - - public void verify(VNode vnode) { - for (PropertyNode actualNode : vnode.propList) { - verifyNode(actualNode.propName, actualNode); - } - if (!mOrderedNodeMap.isEmpty() || !mUnorderedNodeList.isEmpty()) { - List expectedProps = new ArrayList(); - for (List nodes : mOrderedNodeMap.values()) { - for (PropertyNode node : nodes) { - if (!expectedProps.contains(node.propName)) { - expectedProps.add(node.propName); - } - } - } - for (PropertyNode node : mUnorderedNodeList) { - if (!expectedProps.contains(node.propName)) { - expectedProps.add(node.propName); - } - } - mTestCase.fail("Expected property " + Arrays.toString(expectedProps.toArray()) - + " was not found."); - } - } - - private void verifyNode(final String propName, final PropertyNode actualNode) { - List expectedNodeList = mOrderedNodeMap.get(propName); - final int size = (expectedNodeList != null ? expectedNodeList.size() : 0); - if (size > 0) { - for (int i = 0; i < size; i++) { - PropertyNode expectedNode = expectedNodeList.get(i); - List expectedButDifferentValueList = new ArrayList(); - if (expectedNode.propName.equals(propName)) { - if (expectedNode.equals(actualNode)) { - expectedNodeList.remove(i); - if (expectedNodeList.size() == 0) { - mOrderedNodeMap.remove(propName); - } - return; - } else { - expectedButDifferentValueList.add(expectedNode); - } - } - - // "actualNode" is not in ordered expected list. - // Try looking over unordered expected list. - if (tryFoundExpectedNodeFromUnorderedList(actualNode, - expectedButDifferentValueList)) { - return; - } - - if (!expectedButDifferentValueList.isEmpty()) { - // Same propName exists but with different value(s). - failWithExpectedNodeList(propName, actualNode, - expectedButDifferentValueList); - } else { - // There's no expected node with same propName. - mTestCase.fail("Unexpected property \"" + propName + "\" exists."); - } - } - } else { - List expectedButDifferentValueList = - new ArrayList(); - if (tryFoundExpectedNodeFromUnorderedList(actualNode, expectedButDifferentValueList)) { - return; - } else { - if (!expectedButDifferentValueList.isEmpty()) { - // Same propName exists but with different value(s). - failWithExpectedNodeList(propName, actualNode, - expectedButDifferentValueList); - } else { - // There's no expected node with same propName. - mTestCase.fail("Unexpected property \"" + propName + "\" exists."); - } - } - } - } - - private String concatinateListWithSemiColon(List array) { - StringBuffer buffer = new StringBuffer(); - boolean first = true; - for (String propValueElem : array) { - if (first) { - first = false; - } else { - buffer.append(';'); - } - buffer.append(propValueElem); - } - - return buffer.toString(); - } - - private boolean tryFoundExpectedNodeFromUnorderedList(PropertyNode actualNode, - List expectedButDifferentValueList) { - final String propName = actualNode.propName; - int unorderedListSize = mUnorderedNodeList.size(); - for (int i = 0; i < unorderedListSize; i++) { - PropertyNode unorderedExpectedNode = mUnorderedNodeList.get(i); - if (unorderedExpectedNode.propName.equals(propName)) { - if (unorderedExpectedNode.equals(actualNode)) { - mUnorderedNodeList.remove(i); - return true; - } - expectedButDifferentValueList.add(unorderedExpectedNode); - } - } - return false; - } - - private void failWithExpectedNodeList(String propName, PropertyNode actualNode, - List expectedNodeList) { - StringBuilder builder = new StringBuilder(); - for (PropertyNode expectedNode : expectedNodeList) { - builder.append("expected: "); - builder.append(expectedNode.toString()); - builder.append("\n"); - } - mTestCase.fail("Property \"" + propName + "\" has wrong value.\n" - + builder.toString() - + " actual: " + actualNode.toString()); - } -} diff --git a/vcard/tests/src/com/android/vcard/tests/test_utils/VCardVerifier.java b/vcard/tests/src/com/android/vcard/tests/test_utils/VCardVerifier.java deleted file mode 100644 index 87d82d202..000000000 --- a/vcard/tests/src/com/android/vcard/tests/test_utils/VCardVerifier.java +++ /dev/null @@ -1,339 +0,0 @@ -/* - * 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.vcard.tests.test_utils; - -import android.content.ContentResolver; -import android.content.Context; -import android.content.EntityIterator; -import android.net.Uri; -import android.test.AndroidTestCase; -import android.test.mock.MockContext; -import android.text.TextUtils; -import android.util.Log; - -import com.android.vcard.VCardComposer; -import com.android.vcard.VCardConfig; -import com.android.vcard.VCardEntryConstructor; -import com.android.vcard.VCardInterpreter; -import com.android.vcard.VCardInterpreterCollection; -import com.android.vcard.VCardParser; -import com.android.vcard.VCardParser_V21; -import com.android.vcard.VCardParser_V30; -import com.android.vcard.exception.VCardException; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.lang.reflect.Method; -import java.util.Arrays; - -/** - *

- * The class lets users checks that given expected vCard data are same as given actual vCard data. - * Able to verify both vCard importer/exporter. - *

- *

- * First a user has to initialize the object by calling either - * {@link #initForImportTest(int, int)} or {@link #initForExportTest(int)}. - * "Round trip test" (import -> export -> import, or export -> import -> export) is not supported. - *

- */ -public class VCardVerifier { - private static final String LOG_TAG = "VCardVerifier"; - - private static class CustomMockContext extends MockContext { - final ContentResolver mResolver; - public CustomMockContext(ContentResolver resolver) { - mResolver = resolver; - } - - @Override - public ContentResolver getContentResolver() { - return mResolver; - } - } - - private class VCardVerifierInternal implements VCardComposer.OneEntryHandler { - public boolean onInit(Context context) { - return true; - } - public boolean onEntryCreated(String vcard) { - verifyOneVCard(vcard); - return true; - } - public void onTerminate() { - } - } - - private final AndroidTestCase mTestCase; - private final VCardVerifierInternal mVCardVerifierInternal; - private int mVCardType; - private boolean mIsV30; - private boolean mIsDoCoMo; - - // Only one of them must be non-empty. - private ExportTestResolver mExportTestResolver; - private InputStream mInputStream; - - // To allow duplication, use list instead of set. - // When null, we don't need to do the verification. - private PropertyNodesVerifier mPropertyNodesVerifier; - private LineVerifier mLineVerifier; - private ContentValuesVerifier mContentValuesVerifier; - private boolean mInitialized; - private boolean mVerified = false; - private String mCharset; - - // Called by VCardTestsBase - public VCardVerifier(AndroidTestCase testCase) { - mTestCase = testCase; - mVCardVerifierInternal = new VCardVerifierInternal(); - mExportTestResolver = null; - mInputStream = null; - mInitialized = false; - mVerified = false; - } - - // Should be called at the beginning of each import test. - public void initForImportTest(int vcardType, int resId) { - if (mInitialized) { - mTestCase.fail("Already initialized"); - } - mVCardType = vcardType; - mIsV30 = VCardConfig.isV30(vcardType); - mIsDoCoMo = VCardConfig.isDoCoMo(vcardType); - setInputResourceId(resId); - mInitialized = true; - } - - // Should be called at the beginning of each export test. - public void initForExportTest(int vcardType) { - initForExportTest(vcardType, "UTF-8"); - } - - public void initForExportTest(int vcardType, String charset) { - if (mInitialized) { - mTestCase.fail("Already initialized"); - } - mExportTestResolver = new ExportTestResolver(mTestCase); - mVCardType = vcardType; - mIsV30 = VCardConfig.isV30(vcardType); - mIsDoCoMo = VCardConfig.isDoCoMo(vcardType); - mInitialized = true; - if (TextUtils.isEmpty(charset)) { - mCharset = "UTF-8"; - } else { - mCharset = charset; - } - } - - private void setInputResourceId(int resId) { - InputStream inputStream = mTestCase.getContext().getResources().openRawResource(resId); - if (inputStream == null) { - mTestCase.fail("Wrong resId: " + resId); - } - setInputStream(inputStream); - } - - private void setInputStream(InputStream inputStream) { - if (mExportTestResolver != null) { - mTestCase.fail("addInputEntry() is called."); - } else if (mInputStream != null) { - mTestCase.fail("InputStream is already set"); - } - mInputStream = inputStream; - } - - public ContactEntry addInputEntry() { - if (!mInitialized) { - mTestCase.fail("Not initialized"); - } - if (mInputStream != null) { - mTestCase.fail("setInputStream is called"); - } - return mExportTestResolver.addInputContactEntry(); - } - - public PropertyNodesVerifierElem addPropertyNodesVerifierElem() { - if (!mInitialized) { - mTestCase.fail("Not initialized"); - } - if (mPropertyNodesVerifier == null) { - mPropertyNodesVerifier = new PropertyNodesVerifier(mTestCase); - } - PropertyNodesVerifierElem elem = - mPropertyNodesVerifier.addPropertyNodesVerifierElem(); - elem.addExpectedNodeWithOrder("VERSION", (mIsV30 ? "3.0" : "2.1")); - - return elem; - } - - public PropertyNodesVerifierElem addPropertyNodesVerifierElemWithEmptyName() { - if (!mInitialized) { - mTestCase.fail("Not initialized"); - } - PropertyNodesVerifierElem elem = addPropertyNodesVerifierElem(); - if (mIsV30) { - elem.addExpectedNodeWithOrder("N", "").addExpectedNodeWithOrder("FN", ""); - } else if (mIsDoCoMo) { - elem.addExpectedNodeWithOrder("N", ""); - } - return elem; - } - - public LineVerifierElem addLineVerifierElem() { - if (!mInitialized) { - mTestCase.fail("Not initialized"); - } - if (mLineVerifier == null) { - mLineVerifier = new LineVerifier(mTestCase, mVCardType); - } - return mLineVerifier.addLineVerifierElem(); - } - - public ContentValuesVerifierElem addContentValuesVerifierElem() { - if (!mInitialized) { - mTestCase.fail("Not initialized"); - } - if (mContentValuesVerifier == null) { - mContentValuesVerifier = new ContentValuesVerifier(); - } - - return mContentValuesVerifier.addElem(mTestCase); - } - - private void verifyOneVCard(final String vcard) { - Log.d(LOG_TAG, vcard); - final VCardInterpreter builder; - if (mContentValuesVerifier != null) { - final VNodeBuilder vnodeBuilder = mPropertyNodesVerifier; - final VCardEntryConstructor vcardDataBuilder = - new VCardEntryConstructor(mVCardType); - vcardDataBuilder.addEntryHandler(mContentValuesVerifier); - if (mPropertyNodesVerifier != null) { - builder = new VCardInterpreterCollection(Arrays.asList( - mPropertyNodesVerifier, vcardDataBuilder)); - } else { - builder = vnodeBuilder; - } - } else { - if (mPropertyNodesVerifier != null) { - builder = mPropertyNodesVerifier; - } else { - return; - } - } - - InputStream is = null; - try { - // Note: we must not specify charset toward vCard parsers. This code checks whether - // those parsers are able to encode given binary without any extra information for - // charset. - final VCardParser parser = (mIsV30 ? - new VCardParser_V30(mVCardType) : new VCardParser_V21(mVCardType)); - is = new ByteArrayInputStream(vcard.getBytes(mCharset)); - parser.parse(is, builder); - } catch (IOException e) { - mTestCase.fail("Unexpected IOException: " + e.getMessage()); - } catch (VCardException e) { - mTestCase.fail("Unexpected VCardException: " + e.getMessage()); - } finally { - if (is != null) { - try { - is.close(); - } catch (IOException e) { - mTestCase.fail("Unexpected IOException: " + e.getMessage()); - } - } - } - } - - public void verify() { - if (!mInitialized) { - mTestCase.fail("Not initialized."); - } - if (mVerified) { - mTestCase.fail("verify() was called twice."); - } - if (mInputStream != null) { - try { - verifyForImportTest(); - } catch (IOException e) { - mTestCase.fail("IOException was thrown: " + e.getMessage()); - } catch (VCardException e) { - mTestCase.fail("VCardException was thrown: " + e.getMessage()); - } - } else if (mExportTestResolver != null){ - verifyForExportTest(); - } else { - mTestCase.fail("No input is determined"); - } - mVerified = true; - } - - private void verifyForImportTest() throws IOException, VCardException { - if (mLineVerifier != null) { - mTestCase.fail("Not supported now."); - } - if (mContentValuesVerifier != null) { - mContentValuesVerifier.verify(mInputStream, mVCardType); - } - } - - public static EntityIterator mockGetEntityIteratorMethod( - final ContentResolver resolver, - final Uri uri, final String selection, - final String[] selectionArgs, final String sortOrder) { - if (ExportTestResolver.class.equals(resolver.getClass())) { - return ((ExportTestResolver)resolver).getProvider().queryEntities( - uri, selection, selectionArgs, sortOrder); - } - - Log.e(LOG_TAG, "Unexpected provider given."); - return null; - } - - private Method getMockGetEntityIteratorMethod() - throws SecurityException, NoSuchMethodException { - return this.getClass().getMethod("mockGetEntityIteratorMethod", - ContentResolver.class, Uri.class, String.class, String[].class, String.class); - } - - private void verifyForExportTest() { - final VCardComposer composer = - new VCardComposer(new CustomMockContext(mExportTestResolver), mVCardType, mCharset); - composer.addHandler(mLineVerifier); - composer.addHandler(mVCardVerifierInternal); - if (!composer.init(VCardComposer.CONTACTS_TEST_CONTENT_URI, null, null, null)) { - mTestCase.fail("init() failed. Reason: " + composer.getErrorReason()); - } - mTestCase.assertFalse(composer.isAfterLast()); - try { - while (!composer.isAfterLast()) { - try { - final Method mockGetEntityIteratorMethod = getMockGetEntityIteratorMethod(); - mTestCase.assertNotNull(mockGetEntityIteratorMethod); - mTestCase.assertTrue(composer.createOneEntry(mockGetEntityIteratorMethod)); - } catch (Exception e) { - e.printStackTrace(); - mTestCase.fail(); - } - } - } finally { - composer.terminate(); - } - } -} diff --git a/vcard/tests/src/com/android/vcard/tests/test_utils/VNode.java b/vcard/tests/src/com/android/vcard/tests/test_utils/VNode.java deleted file mode 100644 index 2ca762b0a..000000000 --- a/vcard/tests/src/com/android/vcard/tests/test_utils/VNode.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * 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.vcard.tests.test_utils; - -import java.util.ArrayList; - -/** - * Previously used in main vCard handling code but now exists only for testing. - */ -public class VNode { - public String VName; - - public ArrayList propList = new ArrayList(); - - /** 0:parse over. 1:parsing. */ - public int parseStatus = 1; -} diff --git a/vcard/tests/src/com/android/vcard/tests/test_utils/VNodeBuilder.java b/vcard/tests/src/com/android/vcard/tests/test_utils/VNodeBuilder.java deleted file mode 100644 index 77a28ad2a..000000000 --- a/vcard/tests/src/com/android/vcard/tests/test_utils/VNodeBuilder.java +++ /dev/null @@ -1,235 +0,0 @@ -/* - * 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.vcard.tests.test_utils; - -import android.content.ContentValues; -import android.util.Base64; -import android.util.CharsetUtils; -import android.util.Log; - -import com.android.vcard.VCardConfig; -import com.android.vcard.VCardInterpreter; -import com.android.vcard.VCardUtils; - -import java.io.UnsupportedEncodingException; -import java.nio.ByteBuffer; -import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.List; - -/** - *

- * The class storing the parse result to custom datastruct: - * {@link VNode}, and {@link PropertyNode}. - * Maybe several vcard instance, so use vNodeList to store. - *

- *

- * This is called VNode, not VCardNode, since it was used for expressing vCalendar (iCal). - *

- */ -/* package */ class VNodeBuilder implements VCardInterpreter { - static private String LOG_TAG = "VNodeBuilder"; - - public List vNodeList = new ArrayList(); - private int mNodeListPos = 0; - private VNode mCurrentVNode; - private PropertyNode mCurrentPropNode; - private String mCurrentParamType; - - /** - * The charset using which VParser parses the text. - */ - private String mSourceCharset; - - /** - * The charset with which byte array is encoded to String. - */ - private String mTargetCharset; - - private boolean mStrictLineBreakParsing; - - public VNodeBuilder() { - this(VCardConfig.DEFAULT_IMPORT_CHARSET, false); - } - - public VNodeBuilder(String targetCharset, boolean strictLineBreakParsing) { - mSourceCharset = VCardConfig.DEFAULT_INTERMEDIATE_CHARSET; - if (targetCharset != null) { - mTargetCharset = targetCharset; - } else { - mTargetCharset = VCardConfig.DEFAULT_IMPORT_CHARSET; - } - mStrictLineBreakParsing = strictLineBreakParsing; - } - - public void start() { - } - - public void end() { - } - - // Note: I guess that this code assumes the Record may nest like this: - // START:VPOS - // ... - // START:VPOS2 - // ... - // END:VPOS2 - // ... - // END:VPOS - // - // However the following code has a bug. - // When error occurs after calling startRecord(), the entry which is probably - // the cause of the error remains to be in vNodeList, while endRecord() is not called. - // - // I leave this code as is since I'm not familiar with vcalendar specification. - // But I believe we should refactor this code in the future. - // Until this, the last entry has to be removed when some error occurs. - public void startEntry() { - VNode vnode = new VNode(); - vnode.parseStatus = 1; - vnode.VName = "VCARD"; - // I feel this should be done in endRecord(), but it cannot be done because of - // the reason above. - vNodeList.add(vnode); - mNodeListPos = vNodeList.size() - 1; - mCurrentVNode = vNodeList.get(mNodeListPos); - } - - public void endEntry() { - VNode endNode = vNodeList.get(mNodeListPos); - endNode.parseStatus = 0; - while(mNodeListPos > 0){ - mNodeListPos--; - if((vNodeList.get(mNodeListPos)).parseStatus == 1) - break; - } - mCurrentVNode = vNodeList.get(mNodeListPos); - } - - public void startProperty() { - mCurrentPropNode = new PropertyNode(); - } - - public void endProperty() { - mCurrentVNode.propList.add(mCurrentPropNode); - } - - public void propertyName(String name) { - mCurrentPropNode.propName = name; - } - - public void propertyGroup(String group) { - mCurrentPropNode.propGroupSet.add(group); - } - - public void propertyParamType(String type) { - mCurrentParamType = type; - } - - public void propertyParamValue(String value) { - if (mCurrentParamType == null || - mCurrentParamType.equalsIgnoreCase("TYPE")) { - mCurrentPropNode.paramMap_TYPE.add(value); - } else { - mCurrentPropNode.paramMap.put(mCurrentParamType, value); - } - - mCurrentParamType = null; - } - - private String encodeString(String originalString, String targetCharset) { - if (mSourceCharset.equalsIgnoreCase(targetCharset)) { - return originalString; - } - Charset charset = Charset.forName(mSourceCharset); - ByteBuffer byteBuffer = charset.encode(originalString); - // byteBuffer.array() "may" return byte array which is larger than - // byteBuffer.remaining(). Here, we keep on the safe side. - byte[] bytes = new byte[byteBuffer.remaining()]; - byteBuffer.get(bytes); - try { - return new String(bytes, targetCharset); - } catch (UnsupportedEncodingException e) { - Log.e(LOG_TAG, "Failed to encode: charset=" + targetCharset); - return null; - } - } - - private String handleOneValue(String value, String targetCharset, String encoding) { - if (encoding != null) { - encoding = encoding.toUpperCase(); - if (encoding.equals("BASE64") || encoding.equals("B")) { - // Assume BASE64 is used only when the number of values is 1. - mCurrentPropNode.propValue_bytes = Base64.decode(value.getBytes(), Base64.NO_WRAP); - return value; - } else if (encoding.equals("QUOTED-PRINTABLE")) { - return VCardUtils.parseQuotedPrintable( - value, mStrictLineBreakParsing, mSourceCharset, targetCharset); - } - // Unknown encoding. Fall back to default. - } - return encodeString(value, targetCharset); - } - - public void propertyValues(List values) { - if (values == null || values.size() == 0) { - mCurrentPropNode.propValue_bytes = null; - mCurrentPropNode.propValue_vector.clear(); - mCurrentPropNode.propValue_vector.add(""); - mCurrentPropNode.propValue = ""; - return; - } - - ContentValues paramMap = mCurrentPropNode.paramMap; - - String targetCharset = CharsetUtils.nameForDefaultVendor(paramMap.getAsString("CHARSET")); - String encoding = paramMap.getAsString("ENCODING"); - - if (targetCharset == null || targetCharset.length() == 0) { - targetCharset = mTargetCharset; - } - - for (String value : values) { - mCurrentPropNode.propValue_vector.add( - handleOneValue(value, targetCharset, encoding)); - } - - mCurrentPropNode.propValue = listToString(mCurrentPropNode.propValue_vector); - } - - private String listToString(List list){ - int size = list.size(); - if (size > 1) { - StringBuilder typeListB = new StringBuilder(); - for (String type : list) { - typeListB.append(type).append(";"); - } - int len = typeListB.length(); - if (len > 0 && typeListB.charAt(len - 1) == ';') { - return typeListB.substring(0, len - 1); - } - return typeListB.toString(); - } else if (size == 1) { - return list.get(0); - } else { - return ""; - } - } - - public String getResult(){ - throw new RuntimeException("Not supported"); - } -} From 7517432a5589d4098d435ba82bb28c363fec0467 Mon Sep 17 00:00:00 2001 From: Konstantin Lopyrev Date: Fri, 4 Jun 2010 14:36:49 -0700 Subject: [PATCH 212/541] Fix 1684100: Making sure that empty package, type and resource ids are not accepted. Change-Id: I4c94877e4312e9a5b4d22c8625069ca056c62e40 --- libs/utils/ResourceTypes.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp index a1401addf..b5355cd76 100644 --- a/libs/utils/ResourceTypes.cpp +++ b/libs/utils/ResourceTypes.cpp @@ -2609,6 +2609,24 @@ bool ResTable::expandResourceRef(const uint16_t* refStr, size_t refLen, *outType = *defType; } *outName = String16(p, end-p); + if(**outPackage == 0) { + if(outErrorMsg) { + *outErrorMsg = "Resource package cannot be an empty string"; + } + return false; + } + if(**outType == 0) { + if(outErrorMsg) { + *outErrorMsg = "Resource type cannot be an empty string"; + } + return false; + } + if(**outName == 0) { + if(outErrorMsg) { + *outErrorMsg = "Resource id cannot be an empty string"; + } + return false; + } return true; } From 076618172d3c6910d09b063fd57f3eb97c91ee40 Mon Sep 17 00:00:00 2001 From: Kenny Root Date: Tue, 8 Jun 2010 10:16:48 -0700 Subject: [PATCH 213/541] Improve checking in aapt dump The aapt dump reading had less error checking than the actual parsing, so this change brings it more into parity so that bad APKs don't crash "aapt dump" Change-Id: Ib30e63e41be5c652645c4aa0de580a87b184529d --- libs/utils/ResourceTypes.cpp | 42 +++++++++++++++++++++++------------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp index b5355cd76..8a645d912 100644 --- a/libs/utils/ResourceTypes.cpp +++ b/libs/utils/ResourceTypes.cpp @@ -1878,6 +1878,12 @@ bool ResTable::getResourceName(uint32_t resID, resource_name* outName) const outName->type = grp->basePackage->typeStrings.stringAt(t, &outName->typeLen); outName->name = grp->basePackage->keyStrings.stringAt( dtohl(entry->key.index), &outName->nameLen); + + // If we have a bad index for some reason, we should abort. + if (outName->type == NULL || outName->name == NULL) { + return false; + } + return true; } @@ -4145,13 +4151,16 @@ void ResTable::print(bool inclValues) const | (0x00ff0000 & ((typeIndex+1)<<16)) | (0x0000ffff & (entryIndex)); resource_name resName; - this->getResourceName(resID, &resName); - printf(" spec resource 0x%08x %s:%s/%s: flags=0x%08x\n", - resID, - CHAR16_TO_CSTR(resName.package, resName.packageLen), - CHAR16_TO_CSTR(resName.type, resName.typeLen), - CHAR16_TO_CSTR(resName.name, resName.nameLen), - dtohl(typeConfigs->typeSpecFlags[entryIndex])); + if (this->getResourceName(resID, &resName)) { + printf(" spec resource 0x%08x %s:%s/%s: flags=0x%08x\n", + resID, + CHAR16_TO_CSTR(resName.package, resName.packageLen), + CHAR16_TO_CSTR(resName.type, resName.typeLen), + CHAR16_TO_CSTR(resName.name, resName.nameLen), + dtohl(typeConfigs->typeSpecFlags[entryIndex])); + } else { + printf(" INVALID TYPE CONFIG FOR RESOURCE 0x%08x\n", resID); + } } } for (size_t configIndex=0; configIndexgetResourceName(resID, &resName); - printf(" resource 0x%08x %s:%s/%s: ", resID, - CHAR16_TO_CSTR(resName.package, resName.packageLen), - CHAR16_TO_CSTR(resName.type, resName.typeLen), - CHAR16_TO_CSTR(resName.name, resName.nameLen)); + if (this->getResourceName(resID, &resName)) { + printf(" resource 0x%08x %s:%s/%s: ", resID, + CHAR16_TO_CSTR(resName.package, resName.packageLen), + CHAR16_TO_CSTR(resName.type, resName.typeLen), + CHAR16_TO_CSTR(resName.name, resName.nameLen)); + } else { + printf(" INVALID RESOURCE 0x%08x: ", resID); + } if ((thisOffset&0x3) != 0) { printf("NON-INTEGER OFFSET: %p\n", (void*)thisOffset); continue; @@ -4424,14 +4436,14 @@ void ResTable::print(bool inclValues) const (((const uint8_t*)ent) + esize); printf(" Parent=0x%08x, Count=%d\n", dtohl(bagPtr->parent.ident), N); - for (int i=0; iname.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)); + thisOffset += size + sizeof(*mapPtr)-sizeof(mapPtr->value); + mapPtr = (ResTable_map*)(((const uint8_t*)mapPtr)+thisOffset); } } } From 65d3c95a4d9a5fb4f61de952af0abd0db5380c77 Mon Sep 17 00:00:00 2001 From: Kenny Root Date: Tue, 8 Jun 2010 12:45:31 -0700 Subject: [PATCH 214/541] Range checking for ResTable_map Correct previous offset change and get ready for safe-iop. Change-Id: Ib276c726f32b0711a72f47d3263eb21640e5a800 --- libs/utils/ResourceTypes.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp index 8a645d912..954255b76 100644 --- a/libs/utils/ResourceTypes.cpp +++ b/libs/utils/ResourceTypes.cpp @@ -4432,18 +4432,19 @@ void ResTable::print(bool inclValues) const 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); + const uint8_t* baseMapPtr = (const uint8_t*)ent; + size_t mapOffset = esize; + const ResTable_map* mapPtr = (ResTable_map*)(baseMapPtr+mapOffset); printf(" Parent=0x%08x, Count=%d\n", dtohl(bagPtr->parent.ident), N); - for (int i=0; iname.ident)); value.copyFrom_dtoh(mapPtr->value); print_value(pkg, value); const size_t size = dtohs(mapPtr->value.size); - thisOffset += size + sizeof(*mapPtr)-sizeof(mapPtr->value); - mapPtr = (ResTable_map*)(((const uint8_t*)mapPtr)+thisOffset); + mapOffset += size + sizeof(*mapPtr)-sizeof(mapPtr->value); + mapPtr = (ResTable_map*)(baseMapPtr+mapOffset); } } } From ad4f2e59af9fe207056099dc002eb80cacbc0f4f Mon Sep 17 00:00:00 2001 From: Kenny Root Date: Tue, 8 Jun 2010 12:34:43 -0700 Subject: [PATCH 215/541] Add invariant check for stylesString size It was possible for stylesStrings to claim to start past the end of the data area thereby making mStringPoolSize larger than the data area. Change-Id: Ibc4d5b429e3a388516135801c8abc3681daae291 --- libs/utils/ResourceTypes.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp index 954255b76..4362d14c3 100644 --- a/libs/utils/ResourceTypes.cpp +++ b/libs/utils/ResourceTypes.cpp @@ -317,6 +317,12 @@ status_t ResStringPool::setTo(const void* data, size_t size, bool copyData) mStringPoolSize = (mHeader->header.size-mHeader->stringsStart)/charSize; } else { + // check invariant: styles starts before end of data + if (mHeader->stylesStart >= (mHeader->header.size-sizeof(uint16_t))) { + LOGW("Bad style block: style block starts at %d past data size of %d\n", + (int)mHeader->stylesStart, (int)mHeader->header.size); + return (mError=BAD_TYPE); + } // check invariant: styles follow the strings if (mHeader->stylesStart <= mHeader->stringsStart) { LOGW("Bad style block: style block starts at %d, before strings at %d\n", From 10e5da58e4c22f2262f7ec7f82745d9673bed14b Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Thu, 10 Jun 2010 11:14:26 -0700 Subject: [PATCH 216/541] Fix String8 to free its memory only after assignment operations based on pointers are finished in case that pointer referred to the string's original contents. Change-Id: I6961f3cf10ba3b728579ea63262db750a4cf8577 --- libs/utils/String8.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/libs/utils/String8.cpp b/libs/utils/String8.cpp index 636cd8342..82776f47a 100644 --- a/libs/utils/String8.cpp +++ b/libs/utils/String8.cpp @@ -301,8 +301,9 @@ void String8::setTo(const String8& other) status_t String8::setTo(const char* other) { + const char *newString = allocFromUTF8(other, strlen(other)); SharedBuffer::bufferFromData(mString)->release(); - mString = allocFromUTF8(other, strlen(other)); + mString = newString; if (mString) return NO_ERROR; mString = getEmptyString(); @@ -311,8 +312,9 @@ status_t String8::setTo(const char* other) status_t String8::setTo(const char* other, size_t len) { + const char *newString = allocFromUTF8(other, len); SharedBuffer::bufferFromData(mString)->release(); - mString = allocFromUTF8(other, len); + mString = newString; if (mString) return NO_ERROR; mString = getEmptyString(); @@ -321,8 +323,9 @@ status_t String8::setTo(const char* other, size_t len) status_t String8::setTo(const char16_t* other, size_t len) { + const char *newString = allocFromUTF16(other, len); SharedBuffer::bufferFromData(mString)->release(); - mString = allocFromUTF16(other, len); + mString = newString; if (mString) return NO_ERROR; mString = getEmptyString(); @@ -331,8 +334,9 @@ status_t String8::setTo(const char16_t* other, size_t len) status_t String8::setTo(const char32_t* other, size_t len) { + const char *newString = allocFromUTF32(other, len); SharedBuffer::bufferFromData(mString)->release(); - mString = allocFromUTF32(other, len); + mString = newString; if (mString) return NO_ERROR; mString = getEmptyString(); From 66db68948c83f1940fa66d76d28208b49bed7815 Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Thu, 22 Apr 2010 18:58:52 -0700 Subject: [PATCH 217/541] Native input dispatch rewrite work in progress. The old dispatch mechanism has been left in place and continues to be used by default for now. To enable native input dispatch, edit the ENABLE_NATIVE_DISPATCH constant in WindowManagerPolicy. Includes part of the new input event NDK API. Some details TBD. To wire up input dispatch, as the ViewRoot adds a window to the window session it receives an InputChannel object as an output argument. The InputChannel encapsulates the file descriptors for a shared memory region and two pipe end-points. The ViewRoot then provides the InputChannel to the InputQueue. Behind the scenes, InputQueue simply attaches handlers to the native PollLoop object that underlies the MessageQueue. This way MessageQueue doesn't need to know anything about input dispatch per-se, it just exposes (in native code) a PollLoop that other components can use to monitor file descriptor state changes. There can be zero or more targets for any given input event. Each input target is specified by its input channel and some parameters including flags, an X/Y coordinate offset, and the dispatch timeout. An input target can request either synchronous dispatch (for foreground apps) or asynchronous dispatch (fire-and-forget for wallpapers and "outside" targets). Currently, finding the appropriate input targets for an event requires a call back into the WindowManagerServer from native code. In the future this will be refactored to avoid most of these callbacks except as required to handle pending focus transitions. End-to-end event dispatch mostly works! To do: event injection, rate limiting, ANRs, testing, optimization, etc. Change-Id: I8c36b2b9e0a2d27392040ecda0f51b636456de25 --- include/utils/BitSet.h | 67 +++++ include/utils/Buffer.h | 107 -------- include/utils/PollLoop.h | 137 ++++++++++ include/utils/Pool.h | 71 +++++ include/utils/StopWatch.h | 2 + include/utils/Vector.h | 16 ++ include/utils/VectorImpl.h | 4 +- libs/utils/Android.mk | 2 + libs/utils/PollLoop.cpp | 267 +++++++++++++++++++ libs/utils/Pool.cpp | 37 +++ libs/utils/StopWatch.cpp | 11 +- libs/utils/VectorImpl.cpp | 12 +- libs/utils/tests/Android.mk | 33 +++ libs/utils/tests/PollLoop_test.cpp | 398 +++++++++++++++++++++++++++++ libs/utils/tests/TestHelpers.h | 44 ++++ 15 files changed, 1088 insertions(+), 120 deletions(-) create mode 100644 include/utils/BitSet.h delete mode 100644 include/utils/Buffer.h create mode 100644 include/utils/PollLoop.h create mode 100644 include/utils/Pool.h create mode 100644 libs/utils/PollLoop.cpp create mode 100644 libs/utils/Pool.cpp create mode 100644 libs/utils/tests/Android.mk create mode 100644 libs/utils/tests/PollLoop_test.cpp create mode 100644 libs/utils/tests/TestHelpers.h diff --git a/include/utils/BitSet.h b/include/utils/BitSet.h new file mode 100644 index 000000000..19c8bf093 --- /dev/null +++ b/include/utils/BitSet.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2010 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 UTILS_BITSET_H +#define UTILS_BITSET_H + +#include + +/* + * Contains some bit manipulation helpers. + */ + +namespace android { + +// A simple set of 32 bits that can be individually marked or cleared. +struct BitSet32 { + uint32_t value; + + inline BitSet32() : value(0) { } + explicit inline BitSet32(uint32_t value) : value(value) { } + + // Gets the value associated with a particular bit index. + static inline uint32_t valueForBit(uint32_t n) { return 0x80000000 >> n; } + + // Clears the bit set. + inline void clear() { value = 0; } + + // Returns true if the bit set does not contain any marked bits. + inline bool isEmpty() const { return ! value; } + + // Returns true if the specified bit is marked. + inline bool hasBit(uint32_t n) const { return value & valueForBit(n); } + + // Marks the specified bit. + inline void markBit(uint32_t n) { value |= valueForBit(n); } + + // Clears the specified bit. + inline void clearBit(uint32_t n) { value &= ~ valueForBit(n); } + + // Finds the first marked bit in the set. + // Result is undefined if all bits are unmarked. + inline uint32_t firstMarkedBit() const { return __builtin_clz(value); } + + // Finds the first unmarked bit in the set. + // Result is undefined if all bits are marked. + inline uint32_t firstUnmarkedBit() const { return __builtin_clz(~ value); } + + inline bool operator== (const BitSet32& other) const { return value == other.value; } + inline bool operator!= (const BitSet32& other) const { return value != other.value; } +}; + +} // namespace android + +#endif // UTILS_BITSET_H diff --git a/include/utils/Buffer.h b/include/utils/Buffer.h deleted file mode 100644 index 8e22b0f21..000000000 --- a/include/utils/Buffer.h +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __UTILS_BUFFER_H__ -#define __UTILS_BUFFER_H__ 1 - -#include -#include -#include - -namespace android { - -class Buffer -{ -private: - char *buf; - int bufsiz; - int used; - void ensureCapacity(int len); - - void - makeRoomFor(int len) - { - if (len + used >= bufsiz) { - bufsiz = (len + used) * 3/2 + 2; - char *blah = new char[bufsiz]; - - memcpy(blah, buf, used); - delete[] buf; - buf = blah; - } - } - -public: - Buffer() - { - bufsiz = 16; - buf = new char[bufsiz]; - clear(); - } - - ~Buffer() - { - delete[] buf; - } - - void - clear() - { - buf[0] = '\0'; - used = 0; - } - - int - length() - { - return used; - } - - void - append(const char c) - { - makeRoomFor(1); - buf[used] = c; - used++; - buf[used] = '\0'; - } - - void - append(const char *s, int len) - { - makeRoomFor(len); - - memcpy(buf + used, s, len); - used += len; - buf[used] = '\0'; - } - - void - append(const char *s) - { - append(s, strlen(s)); - } - - char * - getBytes() - { - return buf; - } -}; - -}; // namespace android - -#endif diff --git a/include/utils/PollLoop.h b/include/utils/PollLoop.h new file mode 100644 index 000000000..2ec39fe50 --- /dev/null +++ b/include/utils/PollLoop.h @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2010 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 UTILS_POLL_LOOP_H +#define UTILS_POLL_LOOP_H + +#include +#include + +#include + +namespace android { + +/** + * A basic file descriptor polling loop based on poll() with callbacks. + */ +class PollLoop : public RefBase { +protected: + virtual ~PollLoop(); + +public: + PollLoop(); + + /** + * A callback that it to be invoked when an event occurs on a file descriptor. + * Specifies the events that were triggered and the user data provided when the + * callback was set. + * + * Returns true if the callback should be kept, false if it should be removed automatically + * after the callback returns. + */ + typedef bool (*Callback)(int fd, int events, void* data); + + /** + * Performs a single call to poll() with optional timeout in milliseconds. + * Invokes callbacks for all file descriptors on which an event occurred. + * + * If the timeout is zero, returns immediately without blocking. + * If the timeout is negative, waits indefinitely until awoken. + * + * Returns true if a callback was invoked or if the loop was awoken by wake(). + * Returns false if a timeout or error occurred. + * + * This method must only be called on the main thread. + * This method blocks until either a file descriptor is signalled, a timeout occurs, + * or wake() is called. + * This method does not return until it has finished invoking the appropriate callbacks + * for all file descriptors that were signalled. + */ + bool pollOnce(int timeoutMillis); + + /** + * Wakes the loop asynchronously. + * + * This method can be called on any thread. + * This method returns immediately. + */ + void wake(); + + /** + * Sets the callback for a file descriptor, replacing the existing one, if any. + * It is an error to call this method with events == 0 or callback == NULL. + * + * Note that a callback can be invoked with the POLLERR, POLLHUP or POLLNVAL events + * even if it is not explicitly requested when registered. + * + * This method can be called on any thread. + * This method may block briefly if it needs to wake the poll loop. + */ + void setCallback(int fd, int events, Callback callback, void* data = NULL); + + /** + * Removes the callback for a file descriptor, if one exists. + * + * When this method returns, it is safe to close the file descriptor since the poll loop + * will no longer have a reference to it. However, it is possible for the callback to + * already be running or for it to run one last time if the file descriptor was already + * signalled. Calling code is responsible for ensuring that this case is safely handled. + * For example, if the callback takes care of removing itself during its own execution either + * by returning false or calling this method, then it can be guaranteed to not be invoked + * again at any later time unless registered anew. + * + * This method can be called on any thread. + * This method may block briefly if it needs to wake the poll loop. + * + * Returns true if a callback was actually removed, false if none was registered. + */ + bool removeCallback(int fd); + +private: + struct RequestedCallback { + Callback callback; + void* data; + }; + + struct PendingCallback { + int fd; + int events; + Callback callback; + void* data; + }; + + Mutex mLock; + Condition mAwake; + bool mPolling; + + int mWakeReadPipeFd; + int mWakeWritePipeFd; + + Vector mRequestedFds; + Vector mRequestedCallbacks; + + Vector mPendingCallbacks; // used privately by pollOnce + + void openWakePipe(); + void closeWakePipe(); + + ssize_t getRequestIndexLocked(int fd); + void wakeAndLock(); +}; + +} // namespace android + +#endif // UTILS_POLL_LOOP_H diff --git a/include/utils/Pool.h b/include/utils/Pool.h new file mode 100644 index 000000000..2ee768eef --- /dev/null +++ b/include/utils/Pool.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2010 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 UTILS_POOL_H +#define UTILS_POOL_H + +#include + +namespace android { + +class PoolImpl { +public: + PoolImpl(size_t objSize); + ~PoolImpl(); + + void* allocImpl(); + void freeImpl(void* obj); + +private: + size_t mObjSize; +}; + +/* + * A homogeneous typed memory pool for fixed size objects. + * Not intended to be thread-safe. + */ +template +class Pool : private PoolImpl { +public: + /* Creates an initially empty pool. */ + Pool() : PoolImpl(sizeof(T)) { } + + /* Destroys the pool. + * Assumes that the pool is empty. */ + ~Pool() { } + + /* Allocates an object from the pool, growing the pool if needed. */ + inline T* alloc() { + void* mem = allocImpl(); + if (! traits::has_trivial_ctor) { + return new (mem) T(); + } else { + return static_cast(mem); + } + } + + /* Frees an object from the pool. */ + inline void free(T* obj) { + if (! traits::has_trivial_dtor) { + obj->~T(); + } + freeImpl(obj); + } +}; + +} // namespace android + +#endif // UTILS_POOL_H diff --git a/include/utils/StopWatch.h b/include/utils/StopWatch.h index cc0bebc40..693dd3ccf 100644 --- a/include/utils/StopWatch.h +++ b/include/utils/StopWatch.h @@ -37,6 +37,8 @@ public: const char* name() const; nsecs_t lap(); nsecs_t elapsedTime() const; + + void reset(); private: const char* mName; diff --git a/include/utils/Vector.h b/include/utils/Vector.h index ad59fd63e..d40ae1664 100644 --- a/include/utils/Vector.h +++ b/include/utils/Vector.h @@ -114,6 +114,12 @@ public: ssize_t appendVector(const Vector& vector); + //! insert an array at a given index + ssize_t insertArrayAt(const TYPE* array, size_t index, size_t numItems); + + //! append an array at the end of this vector + ssize_t appendArray(const TYPE* array, size_t numItems); + /*! * add/insert/replace items */ @@ -258,6 +264,16 @@ ssize_t Vector::appendVector(const Vector& vector) { return VectorImpl::appendVector(reinterpret_cast(vector)); } +template inline +ssize_t Vector::insertArrayAt(const TYPE* array, size_t index, size_t numItems) { + return VectorImpl::insertAt(array, index, numItems); +} + +template inline +ssize_t Vector::appendArray(const TYPE* array, size_t numItems) { + return VectorImpl::add(array, numItems); +} + template inline ssize_t Vector::insertAt(const TYPE& item, size_t index, size_t numItems) { return VectorImpl::insertAt(&item, index, numItems); diff --git a/include/utils/VectorImpl.h b/include/utils/VectorImpl.h index 49b03f10b..46a7bc265 100644 --- a/include/utils/VectorImpl.h +++ b/include/utils/VectorImpl.h @@ -76,7 +76,7 @@ public: void push(); void push(const void* item); ssize_t add(); - ssize_t add(const void* item); + ssize_t add(const void* item, size_t numItems = 1); ssize_t replaceAt(size_t index); ssize_t replaceAt(const void* item, size_t index); @@ -184,6 +184,8 @@ private: void push(const void* item); ssize_t insertVectorAt(const VectorImpl& vector, size_t index); ssize_t appendVector(const VectorImpl& vector); + ssize_t insertArrayAt(const void* array, size_t index, size_t numItems); + ssize_t appendArray(const void* array, size_t numItems); ssize_t insertAt(size_t where, size_t numItems = 1); ssize_t insertAt(const void* item, size_t where, size_t numItems = 1); ssize_t replaceAt(size_t index); diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk index afecdcb0c..945b03958 100644 --- a/libs/utils/Android.mk +++ b/libs/utils/Android.mk @@ -26,6 +26,8 @@ commonSources:= \ Debug.cpp \ FileMap.cpp \ Flattenable.cpp \ + PollLoop.cpp \ + Pool.cpp \ RefBase.cpp \ ResourceTypes.cpp \ SharedBuffer.cpp \ diff --git a/libs/utils/PollLoop.cpp b/libs/utils/PollLoop.cpp new file mode 100644 index 000000000..90a3e8b35 --- /dev/null +++ b/libs/utils/PollLoop.cpp @@ -0,0 +1,267 @@ +// +// Copyright 2010 The Android Open Source Project +// +// A select loop implementation. +// +#define LOG_TAG "PollLoop" + +//#define LOG_NDEBUG 0 + +// Debugs poll and wake interactions. +#define DEBUG_POLL_AND_WAKE 0 + +// Debugs callback registration and invocation. +#define DEBUG_CALLBACKS 1 + +#include +#include + +#include +#include + +namespace android { + +PollLoop::PollLoop() : + mPolling(false) { + openWakePipe(); +} + +PollLoop::~PollLoop() { + closeWakePipe(); +} + +void PollLoop::openWakePipe() { + int wakeFds[2]; + int result = pipe(wakeFds); + LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe. errno=%d", errno); + + mWakeReadPipeFd = wakeFds[0]; + mWakeWritePipeFd = wakeFds[1]; + + result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK); + LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake read pipe non-blocking. errno=%d", + errno); + + result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK); + LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake write pipe non-blocking. errno=%d", + errno); + + // Add the wake pipe to the head of the request list with a null callback. + struct pollfd requestedFd; + requestedFd.fd = mWakeReadPipeFd; + requestedFd.events = POLLIN; + mRequestedFds.insertAt(requestedFd, 0); + + RequestedCallback requestedCallback; + requestedCallback.callback = NULL; + requestedCallback.data = NULL; + mRequestedCallbacks.insertAt(requestedCallback, 0); +} + +void PollLoop::closeWakePipe() { + close(mWakeReadPipeFd); + close(mWakeWritePipeFd); + + // Note: We don't need to remove the poll structure or callback entry because this + // method is currently only called by the destructor. +} + +bool PollLoop::pollOnce(int timeoutMillis) { + mLock.lock(); + mPolling = true; + mLock.unlock(); + + bool result; + size_t requestedCount = mRequestedFds.size(); + +#if DEBUG_POLL_AND_WAKE + LOGD("%p ~ pollOnce - waiting on %d fds", this, requestedCount); + for (size_t i = 0; i < requestedCount; i++) { + LOGD(" fd %d - events %d", mRequestedFds[i].fd, mRequestedFds[i].events); + } +#endif + + int respondedCount = poll(mRequestedFds.editArray(), requestedCount, timeoutMillis); + + if (respondedCount == 0) { + // Timeout +#if DEBUG_POLL_AND_WAKE + LOGD("%p ~ pollOnce - timeout", this); +#endif + result = false; + goto Done; + } + + if (respondedCount < 0) { + // Error +#if DEBUG_POLL_AND_WAKE + LOGD("%p ~ pollOnce - error, errno=%d", this, errno); +#endif + if (errno != EINTR) { + LOGW("Poll failed with an unexpected error, errno=%d", errno); + } + result = false; + goto Done; + } + +#if DEBUG_POLL_AND_WAKE + LOGD("%p ~ pollOnce - handling responses from %d fds", this, respondedCount); + for (size_t i = 0; i < requestedCount; i++) { + LOGD(" fd %d - events %d, revents %d", mRequestedFds[i].fd, mRequestedFds[i].events, + mRequestedFds[i].revents); + } +#endif + + mPendingCallbacks.clear(); + for (size_t i = 0; i < requestedCount; i++) { + const struct pollfd& requestedFd = mRequestedFds.itemAt(i); + + short revents = requestedFd.revents; + if (revents) { + const RequestedCallback& requestedCallback = mRequestedCallbacks.itemAt(i); + Callback callback = requestedCallback.callback; + + if (callback) { + PendingCallback pendingCallback; + pendingCallback.fd = requestedFd.fd; + pendingCallback.events = requestedFd.revents; + pendingCallback.callback = callback; + pendingCallback.data = requestedCallback.data; + mPendingCallbacks.push(pendingCallback); + } else { + if (requestedFd.fd == mWakeReadPipeFd) { +#if DEBUG_POLL_AND_WAKE + LOGD("%p ~ pollOnce - awoken", this); +#endif + char buffer[16]; + ssize_t nRead; + do { + nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer)); + } while (nRead == sizeof(buffer)); + } else { +#if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS + LOGD("%p ~ pollOnce - fd %d has no callback!", this, requestedFd.fd); +#endif + } + } + + respondedCount -= 1; + if (respondedCount == 0) { + break; + } + } + } + result = true; + +Done: + mLock.lock(); + mPolling = false; + mAwake.broadcast(); + mLock.unlock(); + + if (result) { + size_t pendingCount = mPendingCallbacks.size(); + for (size_t i = 0; i < pendingCount; i++) { + const PendingCallback& pendingCallback = mPendingCallbacks.itemAt(i); +#if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS + LOGD("%p ~ pollOnce - invoking callback for fd %d", this, pendingCallback.fd); +#endif + + bool keep = pendingCallback.callback(pendingCallback.fd, pendingCallback.events, + pendingCallback.data); + if (! keep) { + removeCallback(pendingCallback.fd); + } + } + } + +#if DEBUG_POLL_AND_WAKE + LOGD("%p ~ pollOnce - done", this); +#endif + return result; +} + +void PollLoop::wake() { +#if DEBUG_POLL_AND_WAKE + LOGD("%p ~ wake", this); +#endif + + ssize_t nWrite = write(mWakeWritePipeFd, "W", 1); + if (nWrite != 1) { + if (errno != EAGAIN) { + LOGW("Could not write wake signal, errno=%d", errno); + } + } +} + +void PollLoop::setCallback(int fd, int events, Callback callback, void* data) { +#if DEBUG_CALLBACKS + LOGD("%p ~ setCallback - fd=%d, events=%d", this, fd, events); +#endif + + if (! events || ! callback) { + LOGE("Invalid attempt to set a callback with no selected poll events or no callback."); + removeCallback(fd); + return; + } + + wakeAndLock(); + + struct pollfd requestedFd; + requestedFd.fd = fd; + requestedFd.events = events; + + RequestedCallback requestedCallback; + requestedCallback.callback = callback; + requestedCallback.data = data; + + ssize_t index = getRequestIndexLocked(fd); + if (index < 0) { + mRequestedFds.push(requestedFd); + mRequestedCallbacks.push(requestedCallback); + } else { + mRequestedFds.replaceAt(requestedFd, size_t(index)); + mRequestedCallbacks.replaceAt(requestedCallback, size_t(index)); + } + + mLock.unlock(); +} + +bool PollLoop::removeCallback(int fd) { +#if DEBUG_CALLBACKS + LOGD("%p ~ removeCallback - fd=%d", this, fd); +#endif + + wakeAndLock(); + + ssize_t index = getRequestIndexLocked(fd); + if (index >= 0) { + mRequestedFds.removeAt(size_t(index)); + mRequestedCallbacks.removeAt(size_t(index)); + } + + mLock.unlock(); + return index >= 0; +} + +ssize_t PollLoop::getRequestIndexLocked(int fd) { + size_t requestCount = mRequestedFds.size(); + + for (size_t i = 0; i < requestCount; i++) { + if (mRequestedFds.itemAt(i).fd == fd) { + return i; + } + } + + return -1; +} + +void PollLoop::wakeAndLock() { + mLock.lock(); + while (mPolling) { + wake(); + mAwake.wait(mLock); + } +} + +} // namespace android diff --git a/libs/utils/Pool.cpp b/libs/utils/Pool.cpp new file mode 100644 index 000000000..8f18cb913 --- /dev/null +++ b/libs/utils/Pool.cpp @@ -0,0 +1,37 @@ +// +// Copyright 2010 The Android Open Source Project +// +// A simple memory pool. +// +#define LOG_TAG "Pool" + +//#define LOG_NDEBUG 0 + +#include +#include + +#include + +namespace android { + +// TODO Provide a real implementation of a pool. This is just a stub for initial development. + +PoolImpl::PoolImpl(size_t objSize) : + mObjSize(objSize) { +} + +PoolImpl::~PoolImpl() { +} + +void* PoolImpl::allocImpl() { + void* ptr = malloc(mObjSize); + LOG_ALWAYS_FATAL_IF(ptr == NULL, "Cannot allocate new pool object."); + return ptr; +} + +void PoolImpl::freeImpl(void* obj) { + LOG_ALWAYS_FATAL_IF(obj == NULL, "Caller attempted to free NULL pool object."); + return free(obj); +} + +} // namespace android diff --git a/libs/utils/StopWatch.cpp b/libs/utils/StopWatch.cpp index 68a1c5217..b5dda2fac 100644 --- a/libs/utils/StopWatch.cpp +++ b/libs/utils/StopWatch.cpp @@ -30,10 +30,9 @@ namespace android { StopWatch::StopWatch(const char *name, int clock, uint32_t flags) - : mName(name), mClock(clock), mFlags(flags), - mStartTime(0), mNumLaps(0) + : mName(name), mClock(clock), mFlags(flags) { - mStartTime = systemTime(mClock); + reset(); } StopWatch::~StopWatch() @@ -72,6 +71,12 @@ nsecs_t StopWatch::elapsedTime() const return systemTime(mClock) - mStartTime; } +void StopWatch::reset() +{ + mNumLaps = 0; + mStartTime = systemTime(mClock); +} + /*****************************************************************************/ diff --git a/libs/utils/VectorImpl.cpp b/libs/utils/VectorImpl.cpp index 0322af7da..b09c6cae7 100644 --- a/libs/utils/VectorImpl.cpp +++ b/libs/utils/VectorImpl.cpp @@ -108,13 +108,7 @@ size_t VectorImpl::capacity() const ssize_t VectorImpl::insertVectorAt(const VectorImpl& vector, size_t index) { - if (index > size()) - return BAD_INDEX; - void* where = _grow(index, vector.size()); - if (where) { - _do_copy(where, vector.arrayImpl(), vector.size()); - } - return where ? index : (ssize_t)NO_MEMORY; + return insertAt(vector.arrayImpl(), index, vector.size()); } ssize_t VectorImpl::appendVector(const VectorImpl& vector) @@ -226,9 +220,9 @@ ssize_t VectorImpl::add() return add(0); } -ssize_t VectorImpl::add(const void* item) +ssize_t VectorImpl::add(const void* item, size_t numItems) { - return insertAt(item, size()); + return insertAt(item, size(), numItems); } ssize_t VectorImpl::replaceAt(size_t index) diff --git a/libs/utils/tests/Android.mk b/libs/utils/tests/Android.mk new file mode 100644 index 000000000..45e8061af --- /dev/null +++ b/libs/utils/tests/Android.mk @@ -0,0 +1,33 @@ +# Build the unit tests. +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +test_src_files := \ + PollLoop_test.cpp + +LOCAL_SHARED_LIBRARIES := \ + libz \ + liblog \ + libcutils \ + libutils \ + libstlport + +LOCAL_STATIC_LIBRARIES := \ + libgtest \ + libgtest_main + +LOCAL_C_INCLUDES := \ + external/zlib \ + external/icu4c/common \ + bionic \ + bionic/libstdc++/include \ + external/gtest/include \ + external/stlport/stlport + +LOCAL_MODULE_TAGS := eng tests + +$(foreach file,$(test_src_files), \ + $(eval LOCAL_SRC_FILES := $(file)) \ + $(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \ + $(eval include $(BUILD_EXECUTABLE)) \ +) diff --git a/libs/utils/tests/PollLoop_test.cpp b/libs/utils/tests/PollLoop_test.cpp new file mode 100644 index 000000000..6c719c85c --- /dev/null +++ b/libs/utils/tests/PollLoop_test.cpp @@ -0,0 +1,398 @@ +// +// Copyright 2010 The Android Open Source Project +// + +#include +#include +#include +#include +#include +#include + +#include "TestHelpers.h" + +// # of milliseconds to fudge stopwatch measurements +#define TIMING_TOLERANCE_MS 25 + +namespace android { + +class Pipe { +public: + int sendFd; + int receiveFd; + + Pipe() { + int fds[2]; + ::pipe(fds); + + receiveFd = fds[0]; + sendFd = fds[1]; + } + + ~Pipe() { + ::close(sendFd); + ::close(receiveFd); + } + + bool writeSignal() { + return ::write(sendFd, "*", 1) == 1; + } + + bool readSignal() { + char buf[1]; + return ::read(receiveFd, buf, 1) == 1; + } +}; + +class DelayedWake : public DelayedTask { + sp mPollLoop; + +public: + DelayedWake(int delayMillis, const sp pollLoop) : + DelayedTask(delayMillis), mPollLoop(pollLoop) { + } + +protected: + virtual void doTask() { + mPollLoop->wake(); + } +}; + +class DelayedWriteSignal : public DelayedTask { + Pipe* mPipe; + +public: + DelayedWriteSignal(int delayMillis, Pipe* pipe) : + DelayedTask(delayMillis), mPipe(pipe) { + } + +protected: + virtual void doTask() { + mPipe->writeSignal(); + } +}; + +class CallbackHandler { +public: + void setCallback(const sp& pollLoop, int fd, int events) { + pollLoop->setCallback(fd, events, staticHandler, this); + } + +protected: + virtual ~CallbackHandler() { } + + virtual bool handler(int fd, int events) = 0; + +private: + static bool staticHandler(int fd, int events, void* data) { + return static_cast(data)->handler(fd, events); + } +}; + +class StubCallbackHandler : public CallbackHandler { +public: + bool nextResult; + int callbackCount; + + int fd; + int events; + + StubCallbackHandler(bool nextResult) : nextResult(nextResult), + callbackCount(0), fd(-1), events(-1) { + } + +protected: + virtual bool handler(int fd, int events) { + callbackCount += 1; + this->fd = fd; + this->events = events; + return nextResult; + } +}; + +class PollLoopTest : public testing::Test { +protected: + sp mPollLoop; + + virtual void SetUp() { + mPollLoop = new PollLoop(); + } + + virtual void TearDown() { + mPollLoop.clear(); + } +}; + + +TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndNotAwoken_WaitsForTimeoutAndReturnsFalse) { + StopWatch stopWatch("pollOnce"); + bool result = mPollLoop->pollOnce(100); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. equal timeout"; + EXPECT_FALSE(result) + << "pollOnce result should be false because timeout occurred"; +} + +TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndAwokenBeforeWaiting_ImmediatelyReturnsTrue) { + mPollLoop->wake(); + + StopWatch stopWatch("pollOnce"); + bool result = mPollLoop->pollOnce(1000); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. zero because wake() was called before waiting"; + EXPECT_TRUE(result) + << "pollOnce result should be true because loop was awoken"; +} + +TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndAwokenWhileWaiting_PromptlyReturnsTrue) { + sp delayedWake = new DelayedWake(100, mPollLoop); + delayedWake->run(); + + StopWatch stopWatch("pollOnce"); + bool result = mPollLoop->pollOnce(1000); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. equal wake delay"; + EXPECT_TRUE(result) + << "pollOnce result should be true because loop was awoken"; +} + +TEST_F(PollLoopTest, PollOnce_WhenZeroTimeoutAndNoRegisteredFDs_ImmediatelyReturnsFalse) { + StopWatch stopWatch("pollOnce"); + bool result = mPollLoop->pollOnce(0); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should be approx. zero"; + EXPECT_FALSE(result) + << "pollOnce result should be false because timeout occurred"; +} + +TEST_F(PollLoopTest, PollOnce_WhenZeroTimeoutAndNoSignalledFDs_ImmediatelyReturnsFalse) { + Pipe pipe; + StubCallbackHandler handler(true); + + handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN); + + StopWatch stopWatch("pollOnce"); + bool result = mPollLoop->pollOnce(0); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should be approx. zero"; + EXPECT_FALSE(result) + << "pollOnce result should be false because timeout occurred"; + EXPECT_EQ(0, handler.callbackCount) + << "callback should not have been invoked because FD was not signalled"; +} + +TEST_F(PollLoopTest, PollOnce_WhenZeroTimeoutAndSignalledFD_ImmediatelyInvokesCallbackAndReturnsTrue) { + Pipe pipe; + StubCallbackHandler handler(true); + + ASSERT_TRUE(pipe.writeSignal()); + handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN); + + StopWatch stopWatch("pollOnce"); + bool result = mPollLoop->pollOnce(0); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should be approx. zero"; + EXPECT_TRUE(result) + << "pollOnce result should be true because FD was signalled"; + EXPECT_EQ(1, handler.callbackCount) + << "callback should be invoked exactly once"; + EXPECT_EQ(pipe.receiveFd, handler.fd) + << "callback should have received pipe fd as parameter"; + EXPECT_EQ(POLL_IN, handler.events) + << "callback should have received POLL_IN as events"; +} + +TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndNoSignalledFDs_WaitsForTimeoutAndReturnsFalse) { + Pipe pipe; + StubCallbackHandler handler(true); + + handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN); + + StopWatch stopWatch("pollOnce"); + bool result = mPollLoop->pollOnce(100); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. equal timeout"; + EXPECT_FALSE(result) + << "pollOnce result should be false because timeout occurred"; + EXPECT_EQ(0, handler.callbackCount) + << "callback should not have been invoked because FD was not signalled"; +} + +TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndSignalledFDBeforeWaiting_ImmediatelyInvokesCallbackAndReturnsTrue) { + Pipe pipe; + StubCallbackHandler handler(true); + + pipe.writeSignal(); + handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN); + + StopWatch stopWatch("pollOnce"); + bool result = mPollLoop->pollOnce(100); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + ASSERT_TRUE(pipe.readSignal()) + << "signal should actually have been written"; + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should be approx. zero"; + EXPECT_TRUE(result) + << "pollOnce result should be true because FD was signalled"; + EXPECT_EQ(1, handler.callbackCount) + << "callback should be invoked exactly once"; + EXPECT_EQ(pipe.receiveFd, handler.fd) + << "callback should have received pipe fd as parameter"; + EXPECT_EQ(POLL_IN, handler.events) + << "callback should have received POLL_IN as events"; +} + +TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndSignalledFDWhileWaiting_PromptlyInvokesCallbackAndReturnsTrue) { + Pipe pipe; + StubCallbackHandler handler(true); + sp delayedWriteSignal = new DelayedWriteSignal(100, & pipe); + + handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN); + delayedWriteSignal->run(); + + StopWatch stopWatch("pollOnce"); + bool result = mPollLoop->pollOnce(1000); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + ASSERT_TRUE(pipe.readSignal()) + << "signal should actually have been written"; + EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. equal signal delay"; + EXPECT_TRUE(result) + << "pollOnce result should be true because FD was signalled"; + EXPECT_EQ(1, handler.callbackCount) + << "callback should be invoked exactly once"; + EXPECT_EQ(pipe.receiveFd, handler.fd) + << "callback should have received pipe fd as parameter"; + EXPECT_EQ(POLL_IN, handler.events) + << "callback should have received POLL_IN as events"; +} + +TEST_F(PollLoopTest, PollOnce_WhenCallbackAddedThenRemoved_CallbackShouldNotBeInvoked) { + Pipe pipe; + StubCallbackHandler handler(true); + + handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN); + pipe.writeSignal(); // would cause FD to be considered signalled + mPollLoop->removeCallback(pipe.receiveFd); + + StopWatch stopWatch("pollOnce"); + bool result = mPollLoop->pollOnce(100); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + ASSERT_TRUE(pipe.readSignal()) + << "signal should actually have been written"; + EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. equal timeout because FD was no longer registered"; + EXPECT_FALSE(result) + << "pollOnce result should be false because timeout occurred"; + EXPECT_EQ(0, handler.callbackCount) + << "callback should not be invoked"; +} + +TEST_F(PollLoopTest, PollOnce_WhenCallbackReturnsFalse_CallbackShouldNotBeInvokedAgainLater) { + Pipe pipe; + StubCallbackHandler handler(false); + + handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN); + + // First loop: Callback is registered and FD is signalled. + pipe.writeSignal(); + + StopWatch stopWatch("pollOnce"); + bool result = mPollLoop->pollOnce(0); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + ASSERT_TRUE(pipe.readSignal()) + << "signal should actually have been written"; + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. equal zero because FD was already signalled"; + EXPECT_TRUE(result) + << "pollOnce result should be true because FD was signalled"; + EXPECT_EQ(1, handler.callbackCount) + << "callback should be invoked"; + + // Second loop: Callback is no longer registered and FD is signalled. + pipe.writeSignal(); + + stopWatch.reset(); + result = mPollLoop->pollOnce(0); + elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + ASSERT_TRUE(pipe.readSignal()) + << "signal should actually have been written"; + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. equal zero because timeout was zero"; + EXPECT_FALSE(result) + << "pollOnce result should be false because timeout occurred"; + EXPECT_EQ(1, handler.callbackCount) + << "callback should not be invoked this time"; +} + +TEST_F(PollLoopTest, RemoveCallback_WhenCallbackNotAdded_ReturnsFalse) { + bool result = mPollLoop->removeCallback(1); + + EXPECT_FALSE(result) + << "removeCallback should return false because FD not registered"; +} + +TEST_F(PollLoopTest, RemoveCallback_WhenCallbackAddedThenRemovedTwice_ReturnsTrueFirstTimeAndReturnsFalseSecondTime) { + Pipe pipe; + StubCallbackHandler handler(false); + handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN); + + // First time. + bool result = mPollLoop->removeCallback(pipe.receiveFd); + + EXPECT_TRUE(result) + << "removeCallback should return true first time because FD was registered"; + + // Second time. + result = mPollLoop->removeCallback(pipe.receiveFd); + + EXPECT_FALSE(result) + << "removeCallback should return false second time because FD was no longer registered"; +} + +TEST_F(PollLoopTest, PollOnce_WhenCallbackAddedTwice_OnlySecondCallbackShouldBeInvoked) { + Pipe pipe; + StubCallbackHandler handler1(true); + StubCallbackHandler handler2(true); + + handler1.setCallback(mPollLoop, pipe.receiveFd, POLL_IN); + handler2.setCallback(mPollLoop, pipe.receiveFd, POLL_IN); // replace it + pipe.writeSignal(); // would cause FD to be considered signalled + + StopWatch stopWatch("pollOnce"); + bool result = mPollLoop->pollOnce(100); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + ASSERT_TRUE(pipe.readSignal()) + << "signal should actually have been written"; + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. zero because FD was already signalled"; + EXPECT_TRUE(result) + << "pollOnce result should be true because FD was signalled"; + EXPECT_EQ(0, handler1.callbackCount) + << "original handler callback should not be invoked because it was replaced"; + EXPECT_EQ(1, handler2.callbackCount) + << "replacement handler callback should be invoked"; +} + + +} // namespace android diff --git a/libs/utils/tests/TestHelpers.h b/libs/utils/tests/TestHelpers.h new file mode 100644 index 000000000..e55af3ccd --- /dev/null +++ b/libs/utils/tests/TestHelpers.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2010 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 TESTHELPERS_H +#define TESTHELPERS_H + +#include + +namespace android { + +class DelayedTask : public Thread { + int mDelayMillis; + +public: + DelayedTask(int delayMillis) : mDelayMillis(delayMillis) { } + +protected: + virtual ~DelayedTask() { } + + virtual void doTask() = 0; + + virtual bool threadLoop() { + usleep(mDelayMillis * 1000); + doTask(); + return false; + } +}; + +} // namespace android + +#endif // TESTHELPERS_H From 4d643d6fdb469a603e0645e7a8fecad334341141 Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Sun, 13 Jun 2010 19:35:19 -0700 Subject: [PATCH 218/541] Fix include paths. Change-Id: Ifda45688f9f02710a74d5d7a7d902bacf1441e2e --- include/utils/PollLoop.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/utils/PollLoop.h b/include/utils/PollLoop.h index 2ec39fe50..c9d951f18 100644 --- a/include/utils/PollLoop.h +++ b/include/utils/PollLoop.h @@ -20,7 +20,7 @@ #include #include -#include +#include namespace android { From ea5c46b7c180f741762f1a66203e3c99ffccc752 Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Sun, 13 Jun 2010 20:21:19 -0700 Subject: [PATCH 219/541] Remove PollLoop from host build. We can't build (and we don't need) PollLoop in the SDK on all platforms. Change-Id: I3b0f327bbb8bc58b9b8630b0161cf360403fea68 --- libs/utils/Android.mk | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk index 945b03958..7d4524aa5 100644 --- a/libs/utils/Android.mk +++ b/libs/utils/Android.mk @@ -26,7 +26,6 @@ commonSources:= \ Debug.cpp \ FileMap.cpp \ Flattenable.cpp \ - PollLoop.cpp \ Pool.cpp \ RefBase.cpp \ ResourceTypes.cpp \ @@ -41,7 +40,7 @@ commonSources:= \ Threads.cpp \ Timers.cpp \ VectorImpl.cpp \ - ZipFileCRO.cpp \ + ZipFileCRO.cpp \ ZipFileRO.cpp \ ZipUtils.cpp \ misc.cpp @@ -78,8 +77,9 @@ include $(CLEAR_VARS) # we have the common sources, plus some device-specific stuff LOCAL_SRC_FILES:= \ $(commonSources) \ - BackupData.cpp \ - BackupHelpers.cpp + BackupData.cpp \ + BackupHelpers.cpp \ + PollLoop.cpp ifeq ($(TARGET_OS),linux) LOCAL_LDLIBS += -lrt -ldl From 80d9c1cfb0642e680c559a854d09514c2fc72edc Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Mon, 14 Jun 2010 15:44:41 -0700 Subject: [PATCH 220/541] Support for multiple tests The build system enforces some invariants that were being ignored before. Change-Id: Ie7675042af7f961a507386c13b2a0b592b591af8 --- libs/utils/tests/Android.mk | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/libs/utils/tests/Android.mk b/libs/utils/tests/Android.mk index 45e8061af..92ebfd7c3 100644 --- a/libs/utils/tests/Android.mk +++ b/libs/utils/tests/Android.mk @@ -5,18 +5,18 @@ include $(CLEAR_VARS) test_src_files := \ PollLoop_test.cpp -LOCAL_SHARED_LIBRARIES := \ +shared_libraries := \ libz \ liblog \ libcutils \ libutils \ libstlport -LOCAL_STATIC_LIBRARIES := \ +static_libraries := \ libgtest \ libgtest_main -LOCAL_C_INCLUDES := \ +c_includes := \ external/zlib \ external/icu4c/common \ bionic \ @@ -24,10 +24,15 @@ LOCAL_C_INCLUDES := \ external/gtest/include \ external/stlport/stlport -LOCAL_MODULE_TAGS := eng tests +module_tags := eng tests $(foreach file,$(test_src_files), \ + $(eval include $(CLEAR_VARS)) \ + $(eval LOCAL_SHARED_LIBRARIES := $(shared_libraries)) \ + $(eval LOCAL_STATIC_LIBRARIES := $(static_libraries)) \ + $(eval LOCAL_C_INCLUDES := $(c_includes)) \ $(eval LOCAL_SRC_FILES := $(file)) \ $(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \ + $(eval LOCAL_MODULE_TAGS := $(module_tags)) \ $(eval include $(BUILD_EXECUTABLE)) \ ) From 9efaaa43595307786dbf06760823c25cb16b1925 Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Wed, 16 Jun 2010 01:53:36 -0700 Subject: [PATCH 221/541] Even more native input dispatch work in progress. Added more tests. Fixed a regression in Vector. Fixed bugs in pointer tracking. Fixed a starvation issue in PollLoop when setting or removing callbacks. Fixed a couple of policy nits. Modified the internal representation of MotionEvent to be more efficient and more consistent. Added code to skip/cancel virtual key processing when there are multiple pointers down. This helps to better disambiguate virtual key presses from stray touches (such as cheek presses). Change-Id: I2a7d2cce0195afb9125b23378baa94fd2fc6671c --- include/utils/PollLoop.h | 4 ++- include/utils/Vector.h | 14 +++++----- include/utils/VectorImpl.h | 10 ++++--- libs/utils/PollLoop.cpp | 16 +++++++++--- libs/utils/VectorImpl.cpp | 22 +++++++++++++--- libs/utils/tests/PollLoop_test.cpp | 42 +++++------------------------- libs/utils/tests/TestHelpers.h | 35 +++++++++++++++++++++++++ 7 files changed, 90 insertions(+), 53 deletions(-) diff --git a/include/utils/PollLoop.h b/include/utils/PollLoop.h index c9d951f18..a95fb171c 100644 --- a/include/utils/PollLoop.h +++ b/include/utils/PollLoop.h @@ -114,8 +114,10 @@ private: }; Mutex mLock; - Condition mAwake; bool mPolling; + uint32_t mWaiters; + Condition mAwake; + Condition mResume; int mWakeReadPipeFd; int mWakeWritePipeFd; diff --git a/include/utils/Vector.h b/include/utils/Vector.h index d40ae1664..ec851bd0b 100644 --- a/include/utils/Vector.h +++ b/include/utils/Vector.h @@ -115,10 +115,10 @@ public: //! insert an array at a given index - ssize_t insertArrayAt(const TYPE* array, size_t index, size_t numItems); + ssize_t insertArrayAt(const TYPE* array, size_t index, size_t length); //! append an array at the end of this vector - ssize_t appendArray(const TYPE* array, size_t numItems); + ssize_t appendArray(const TYPE* array, size_t length); /*! * add/insert/replace items @@ -126,7 +126,7 @@ public: //! insert one or several items initialized with their default constructor inline ssize_t insertAt(size_t index, size_t numItems = 1); - //! insert on onr several items initialized from a prototype item + //! insert one or several items initialized from a prototype item ssize_t insertAt(const TYPE& prototype_item, size_t index, size_t numItems = 1); //! pop the top of the stack (removes the last element). No-op if the stack's empty inline void pop(); @@ -265,13 +265,13 @@ ssize_t Vector::appendVector(const Vector& vector) { } template inline -ssize_t Vector::insertArrayAt(const TYPE* array, size_t index, size_t numItems) { - return VectorImpl::insertAt(array, index, numItems); +ssize_t Vector::insertArrayAt(const TYPE* array, size_t index, size_t length) { + return VectorImpl::insertArrayAt(array, index, length); } template inline -ssize_t Vector::appendArray(const TYPE* array, size_t numItems) { - return VectorImpl::add(array, numItems); +ssize_t Vector::appendArray(const TYPE* array, size_t length) { + return VectorImpl::appendArray(array, length); } template inline diff --git a/include/utils/VectorImpl.h b/include/utils/VectorImpl.h index 46a7bc265..c4ec2ff97 100644 --- a/include/utils/VectorImpl.h +++ b/include/utils/VectorImpl.h @@ -65,9 +65,11 @@ public: size_t capacity() const; ssize_t setCapacity(size_t size); - /*! append/insert another vector */ + /*! append/insert another vector or array */ ssize_t insertVectorAt(const VectorImpl& vector, size_t index); ssize_t appendVector(const VectorImpl& vector); + ssize_t insertArrayAt(const void* array, size_t index, size_t length); + ssize_t appendArray(const void* array, size_t length); /*! add/insert/replace items */ ssize_t insertAt(size_t where, size_t numItems = 1); @@ -76,7 +78,7 @@ public: void push(); void push(const void* item); ssize_t add(); - ssize_t add(const void* item, size_t numItems = 1); + ssize_t add(const void* item); ssize_t replaceAt(size_t index); ssize_t replaceAt(const void* item, size_t index); @@ -184,8 +186,8 @@ private: void push(const void* item); ssize_t insertVectorAt(const VectorImpl& vector, size_t index); ssize_t appendVector(const VectorImpl& vector); - ssize_t insertArrayAt(const void* array, size_t index, size_t numItems); - ssize_t appendArray(const void* array, size_t numItems); + ssize_t insertArrayAt(const void* array, size_t index, size_t length); + ssize_t appendArray(const void* array, size_t length); ssize_t insertAt(size_t where, size_t numItems = 1); ssize_t insertAt(const void* item, size_t where, size_t numItems = 1); ssize_t replaceAt(size_t index); diff --git a/libs/utils/PollLoop.cpp b/libs/utils/PollLoop.cpp index 90a3e8b35..20a4d1385 100644 --- a/libs/utils/PollLoop.cpp +++ b/libs/utils/PollLoop.cpp @@ -11,7 +11,7 @@ #define DEBUG_POLL_AND_WAKE 0 // Debugs callback registration and invocation. -#define DEBUG_CALLBACKS 1 +#define DEBUG_CALLBACKS 0 #include #include @@ -22,7 +22,7 @@ namespace android { PollLoop::PollLoop() : - mPolling(false) { + mPolling(false), mWaiters(0) { openWakePipe(); } @@ -68,6 +68,9 @@ void PollLoop::closeWakePipe() { bool PollLoop::pollOnce(int timeoutMillis) { mLock.lock(); + while (mWaiters != 0) { + mResume.wait(mLock); + } mPolling = true; mLock.unlock(); @@ -156,7 +159,9 @@ bool PollLoop::pollOnce(int timeoutMillis) { Done: mLock.lock(); mPolling = false; - mAwake.broadcast(); + if (mWaiters != 0) { + mAwake.broadcast(); + } mLock.unlock(); if (result) { @@ -258,10 +263,15 @@ ssize_t PollLoop::getRequestIndexLocked(int fd) { void PollLoop::wakeAndLock() { mLock.lock(); + mWaiters += 1; while (mPolling) { wake(); mAwake.wait(mLock); } + mWaiters -= 1; + if (mWaiters == 0) { + mResume.signal(); + } } } // namespace android diff --git a/libs/utils/VectorImpl.cpp b/libs/utils/VectorImpl.cpp index b09c6cae7..289c826d3 100644 --- a/libs/utils/VectorImpl.cpp +++ b/libs/utils/VectorImpl.cpp @@ -108,7 +108,7 @@ size_t VectorImpl::capacity() const ssize_t VectorImpl::insertVectorAt(const VectorImpl& vector, size_t index) { - return insertAt(vector.arrayImpl(), index, vector.size()); + return insertArrayAt(vector.arrayImpl(), index, vector.size()); } ssize_t VectorImpl::appendVector(const VectorImpl& vector) @@ -116,6 +116,22 @@ ssize_t VectorImpl::appendVector(const VectorImpl& vector) return insertVectorAt(vector, size()); } +ssize_t VectorImpl::insertArrayAt(const void* array, size_t index, size_t length) +{ + if (index > size()) + return BAD_INDEX; + void* where = _grow(index, length); + if (where) { + _do_copy(where, array, length); + } + return where ? index : (ssize_t)NO_MEMORY; +} + +ssize_t VectorImpl::appendArray(const void* array, size_t length) +{ + return insertArrayAt(array, size(), length); +} + ssize_t VectorImpl::insertAt(size_t index, size_t numItems) { return insertAt(0, index, numItems); @@ -220,9 +236,9 @@ ssize_t VectorImpl::add() return add(0); } -ssize_t VectorImpl::add(const void* item, size_t numItems) +ssize_t VectorImpl::add(const void* item) { - return insertAt(item, size(), numItems); + return insertAt(item, size()); } ssize_t VectorImpl::replaceAt(size_t index) diff --git a/libs/utils/tests/PollLoop_test.cpp b/libs/utils/tests/PollLoop_test.cpp index 6c719c85c..4848c0fa6 100644 --- a/libs/utils/tests/PollLoop_test.cpp +++ b/libs/utils/tests/PollLoop_test.cpp @@ -16,34 +16,6 @@ namespace android { -class Pipe { -public: - int sendFd; - int receiveFd; - - Pipe() { - int fds[2]; - ::pipe(fds); - - receiveFd = fds[0]; - sendFd = fds[1]; - } - - ~Pipe() { - ::close(sendFd); - ::close(receiveFd); - } - - bool writeSignal() { - return ::write(sendFd, "*", 1) == 1; - } - - bool readSignal() { - char buf[1]; - return ::read(receiveFd, buf, 1) == 1; - } -}; - class DelayedWake : public DelayedTask { sp mPollLoop; @@ -195,7 +167,7 @@ TEST_F(PollLoopTest, PollOnce_WhenZeroTimeoutAndSignalledFD_ImmediatelyInvokesCa Pipe pipe; StubCallbackHandler handler(true); - ASSERT_TRUE(pipe.writeSignal()); + ASSERT_EQ(OK, pipe.writeSignal()); handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN); StopWatch stopWatch("pollOnce"); @@ -243,7 +215,7 @@ TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndSignalledFDBeforeWaiting_Imme bool result = mPollLoop->pollOnce(100); int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); - ASSERT_TRUE(pipe.readSignal()) + ASSERT_EQ(OK, pipe.readSignal()) << "signal should actually have been written"; EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) << "elapsed time should be approx. zero"; @@ -269,7 +241,7 @@ TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndSignalledFDWhileWaiting_Promp bool result = mPollLoop->pollOnce(1000); int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); - ASSERT_TRUE(pipe.readSignal()) + ASSERT_EQ(OK, pipe.readSignal()) << "signal should actually have been written"; EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) << "elapsed time should approx. equal signal delay"; @@ -295,7 +267,7 @@ TEST_F(PollLoopTest, PollOnce_WhenCallbackAddedThenRemoved_CallbackShouldNotBeIn bool result = mPollLoop->pollOnce(100); int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); - ASSERT_TRUE(pipe.readSignal()) + ASSERT_EQ(OK, pipe.readSignal()) << "signal should actually have been written"; EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) << "elapsed time should approx. equal timeout because FD was no longer registered"; @@ -318,7 +290,7 @@ TEST_F(PollLoopTest, PollOnce_WhenCallbackReturnsFalse_CallbackShouldNotBeInvoke bool result = mPollLoop->pollOnce(0); int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); - ASSERT_TRUE(pipe.readSignal()) + ASSERT_EQ(OK, pipe.readSignal()) << "signal should actually have been written"; EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) << "elapsed time should approx. equal zero because FD was already signalled"; @@ -334,7 +306,7 @@ TEST_F(PollLoopTest, PollOnce_WhenCallbackReturnsFalse_CallbackShouldNotBeInvoke result = mPollLoop->pollOnce(0); elapsedMillis = ns2ms(stopWatch.elapsedTime()); - ASSERT_TRUE(pipe.readSignal()) + ASSERT_EQ(OK, pipe.readSignal()) << "signal should actually have been written"; EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) << "elapsed time should approx. equal zero because timeout was zero"; @@ -382,7 +354,7 @@ TEST_F(PollLoopTest, PollOnce_WhenCallbackAddedTwice_OnlySecondCallbackShouldBeI bool result = mPollLoop->pollOnce(100); int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); - ASSERT_TRUE(pipe.readSignal()) + ASSERT_EQ(OK, pipe.readSignal()) << "signal should actually have been written"; EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) << "elapsed time should approx. zero because FD was already signalled"; diff --git a/libs/utils/tests/TestHelpers.h b/libs/utils/tests/TestHelpers.h index e55af3ccd..d8e985e9c 100644 --- a/libs/utils/tests/TestHelpers.h +++ b/libs/utils/tests/TestHelpers.h @@ -21,6 +21,41 @@ namespace android { +class Pipe { +public: + int sendFd; + int receiveFd; + + Pipe() { + int fds[2]; + ::pipe(fds); + + receiveFd = fds[0]; + sendFd = fds[1]; + } + + ~Pipe() { + if (sendFd != -1) { + ::close(sendFd); + } + + if (receiveFd != -1) { + ::close(receiveFd); + } + } + + status_t writeSignal() { + ssize_t nWritten = ::write(sendFd, "*", 1); + return nWritten == 1 ? 0 : -errno; + } + + status_t readSignal() { + char buf[1]; + ssize_t nRead = ::read(receiveFd, buf, 1); + return nRead == 1 ? 0 : nRead == 0 ? -EPIPE : -errno; + } +}; + class DelayedTask : public Thread { int mDelayMillis; From 7b1516795e6174fea6b12b3c4c3177e64e329ba6 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Thu, 24 Jun 2010 21:49:02 -0700 Subject: [PATCH 222/541] Fix a bug in sp<> and wp<> which could cause memory corruptions when assigning a smart pointer to another one, we need to make sure to read all the data we need from the right-hand-side reference (the assignee) before we decRef the assigned. This bug would cause linked-list of smart-pointers to fail miserably. Change-Id: Ibb554c15fddf909f7737c632b7c80322e80ea93f --- include/utils/RefBase.h | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/include/utils/RefBase.h b/include/utils/RefBase.h index bd7f28cae..9c64ac044 100644 --- a/include/utils/RefBase.h +++ b/include/utils/RefBase.h @@ -333,9 +333,10 @@ sp::~sp() template sp& sp::operator = (const sp& other) { - if (other.m_ptr) other.m_ptr->incStrong(this); + T* otherPtr(other.m_ptr); + if (otherPtr) otherPtr->incStrong(this); if (m_ptr) m_ptr->decStrong(this); - m_ptr = other.m_ptr; + m_ptr = otherPtr; return *this; } @@ -351,9 +352,10 @@ sp& sp::operator = (T* other) template template sp& sp::operator = (const sp& other) { - if (other.m_ptr) other.m_ptr->incStrong(this); + U* otherPtr(other.m_ptr); + if (otherPtr) otherPtr->incStrong(this); if (m_ptr) m_ptr->decStrong(this); - m_ptr = other.m_ptr; + m_ptr = otherPtr; return *this; } @@ -466,10 +468,12 @@ wp& wp::operator = (T* other) template wp& wp::operator = (const wp& other) { - if (other.m_ptr) other.m_refs->incWeak(this); + weakref_type* otherRefs(other.m_refs); + T* otherPtr(other.m_ptr); + if (otherPtr) otherRefs->incWeak(this); if (m_ptr) m_refs->decWeak(this); - m_ptr = other.m_ptr; - m_refs = other.m_refs; + m_ptr = otherPtr; + m_refs = otherRefs; return *this; } @@ -478,8 +482,9 @@ wp& wp::operator = (const sp& other) { weakref_type* newRefs = other != NULL ? other->createWeak(this) : 0; + T* otherPtr(other.m_ptr); if (m_ptr) m_refs->decWeak(this); - m_ptr = other.get(); + m_ptr = otherPtr; m_refs = newRefs; return *this; } @@ -498,10 +503,12 @@ wp& wp::operator = (U* other) template template wp& wp::operator = (const wp& other) { - if (other.m_ptr) other.m_refs->incWeak(this); + weakref_type* otherRefs(other.m_refs); + U* otherPtr(other.m_ptr); + if (otherPtr) otherRefs->incWeak(this); if (m_ptr) m_refs->decWeak(this); - m_ptr = other.m_ptr; - m_refs = other.m_refs; + m_ptr = otherPtr; + m_refs = otherRefs; return *this; } @@ -510,8 +517,9 @@ wp& wp::operator = (const sp& other) { weakref_type* newRefs = other != NULL ? other->createWeak(this) : 0; + U* otherPtr(other.m_ptr); if (m_ptr) m_refs->decWeak(this); - m_ptr = other.get(); + m_ptr = otherPtr; m_refs = newRefs; return *this; } From 26fc52b1d46d14880913f6b3a8d853d0bcd117f2 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Fri, 2 Jul 2010 18:52:01 -0700 Subject: [PATCH 223/541] Add new native Looper API. This allows us to avoid exposing the file descriptor of the event queue; instead, you attach an event queue to a looper. This will also should allow native apps to be written without the need for a separate thread, by attaching the event queue to the main thread's looper and scheduling their own messages there. Change-Id: I38489282635895ae2cbfacb88599c1b1cad9b239 --- include/utils/PollLoop.h | 34 +++++++++++++++++++- libs/utils/PollLoop.cpp | 68 +++++++++++++++++++++++++++++++++++++--- 2 files changed, 97 insertions(+), 5 deletions(-) diff --git a/include/utils/PollLoop.h b/include/utils/PollLoop.h index a95fb171c..b3651caea 100644 --- a/include/utils/PollLoop.h +++ b/include/utils/PollLoop.h @@ -22,12 +22,22 @@ #include +#include + +struct ALooper : public android::RefBase { +protected: + virtual ~ALooper() { } + +public: + ALooper() { } +}; + namespace android { /** * A basic file descriptor polling loop based on poll() with callbacks. */ -class PollLoop : public RefBase { +class PollLoop : public ALooper { protected: virtual ~PollLoop(); @@ -82,6 +92,11 @@ public: */ void setCallback(int fd, int events, Callback callback, void* data = NULL); + /** + * Like setCallback(), but for the NDK callback function. + */ + void setLooperCallback(int fd, int events, ALooper_callbackFunc* callback, void* data); + /** * Removes the callback for a file descriptor, if one exists. * @@ -100,9 +115,22 @@ public: */ bool removeCallback(int fd); + /** + * Set the given PollLoop to be associated with the + * calling thread. There must be a 1:1 relationship between + * PollLoop and thread. + */ + static void setForThread(const sp& pollLoop); + + /** + * Return the PollLoop associated with the calling thread. + */ + static sp getForThread(); + private: struct RequestedCallback { Callback callback; + ALooper_callbackFunc* looperCallback; void* data; }; @@ -110,6 +138,7 @@ private: int fd; int events; Callback callback; + ALooper_callbackFunc* looperCallback; void* data; }; @@ -130,8 +159,11 @@ private: void openWakePipe(); void closeWakePipe(); + void setCallbackCommon(int fd, int events, Callback callback, + ALooper_callbackFunc* looperCallback, void* data); ssize_t getRequestIndexLocked(int fd); void wakeAndLock(); + static void threadDestructor(void *st); }; } // namespace android diff --git a/libs/utils/PollLoop.cpp b/libs/utils/PollLoop.cpp index 20a4d1385..58fe1411d 100644 --- a/libs/utils/PollLoop.cpp +++ b/libs/utils/PollLoop.cpp @@ -21,6 +21,10 @@ namespace android { +static pthread_mutex_t gTLSMutex = PTHREAD_MUTEX_INITIALIZER; +static bool gHaveTLS = false; +static pthread_key_t gTLS = 0; + PollLoop::PollLoop() : mPolling(false), mWaiters(0) { openWakePipe(); @@ -30,6 +34,41 @@ PollLoop::~PollLoop() { closeWakePipe(); } +void PollLoop::threadDestructor(void *st) { + PollLoop* const self = static_cast(st); + if (self != NULL) { + self->decStrong((void*)threadDestructor); + } +} + +void PollLoop::setForThread(const sp& pollLoop) { + sp old = getForThread(); + + if (pollLoop != NULL) { + pollLoop->incStrong((void*)threadDestructor); + } + + pthread_setspecific(gTLS, pollLoop.get()); + + if (old != NULL) { + old->decStrong((void*)threadDestructor); + } +} + +sp PollLoop::getForThread() { + if (!gHaveTLS) { + pthread_mutex_lock(&gTLSMutex); + if (pthread_key_create(&gTLS, threadDestructor) != 0) { + pthread_mutex_unlock(&gTLSMutex); + return NULL; + } + gHaveTLS = true; + pthread_mutex_unlock(&gTLSMutex); + } + + return (PollLoop*)pthread_getspecific(gTLS); +} + void PollLoop::openWakePipe() { int wakeFds[2]; int result = pipe(wakeFds); @@ -54,6 +93,7 @@ void PollLoop::openWakePipe() { RequestedCallback requestedCallback; requestedCallback.callback = NULL; + requestedCallback.looperCallback = NULL; requestedCallback.data = NULL; mRequestedCallbacks.insertAt(requestedCallback, 0); } @@ -123,12 +163,14 @@ bool PollLoop::pollOnce(int timeoutMillis) { if (revents) { const RequestedCallback& requestedCallback = mRequestedCallbacks.itemAt(i); Callback callback = requestedCallback.callback; + ALooper_callbackFunc* looperCallback = requestedCallback.looperCallback; - if (callback) { + if (callback || looperCallback) { PendingCallback pendingCallback; pendingCallback.fd = requestedFd.fd; pendingCallback.events = requestedFd.revents; pendingCallback.callback = callback; + pendingCallback.looperCallback = looperCallback; pendingCallback.data = requestedCallback.data; mPendingCallbacks.push(pendingCallback); } else { @@ -172,8 +214,14 @@ Done: LOGD("%p ~ pollOnce - invoking callback for fd %d", this, pendingCallback.fd); #endif - bool keep = pendingCallback.callback(pendingCallback.fd, pendingCallback.events, - pendingCallback.data); + bool keep = true; + if (pendingCallback.callback != NULL) { + keep = pendingCallback.callback(pendingCallback.fd, pendingCallback.events, + pendingCallback.data); + } else { + keep = pendingCallback.looperCallback(pendingCallback.fd, pendingCallback.events, + pendingCallback.data) != 0; + } if (! keep) { removeCallback(pendingCallback.fd); } @@ -200,11 +248,22 @@ void PollLoop::wake() { } void PollLoop::setCallback(int fd, int events, Callback callback, void* data) { + setCallbackCommon(fd, events, callback, NULL, data); +} + +void PollLoop::setLooperCallback(int fd, int events, ALooper_callbackFunc* callback, + void* data) { + setCallbackCommon(fd, events, NULL, callback, data); +} + +void PollLoop::setCallbackCommon(int fd, int events, Callback callback, + ALooper_callbackFunc* looperCallback, void* data) { + #if DEBUG_CALLBACKS LOGD("%p ~ setCallback - fd=%d, events=%d", this, fd, events); #endif - if (! events || ! callback) { + if (! events || (! callback && ! looperCallback)) { LOGE("Invalid attempt to set a callback with no selected poll events or no callback."); removeCallback(fd); return; @@ -218,6 +277,7 @@ void PollLoop::setCallback(int fd, int events, Callback callback, void* data) { RequestedCallback requestedCallback; requestedCallback.callback = callback; + requestedCallback.looperCallback = looperCallback; requestedCallback.data = data; ssize_t index = getRequestIndexLocked(fd); From 1d2aeb673f9e757521ea0fd3717c7d4277f30b9f Mon Sep 17 00:00:00 2001 From: Kenny Root Date: Tue, 1 Jun 2010 10:34:29 -0700 Subject: [PATCH 224/541] Add OBB file helper class ObbFile is a binary blob that will be used in packaging large files with smaller APKs. Change-Id: Ib1594346cfa2f49113de6565af77c24efbd89d63 --- include/utils/ObbFile.h | 87 +++++++++ libs/utils/Android.mk | 6 + libs/utils/ObbFile.cpp | 284 ++++++++++++++++++++++++++++++ libs/utils/tests/Android.mk | 1 + libs/utils/tests/ObbFile_test.cpp | 75 ++++++++ 5 files changed, 453 insertions(+) create mode 100644 include/utils/ObbFile.h create mode 100644 libs/utils/ObbFile.cpp create mode 100644 libs/utils/tests/ObbFile_test.cpp diff --git a/include/utils/ObbFile.h b/include/utils/ObbFile.h new file mode 100644 index 000000000..075927cdf --- /dev/null +++ b/include/utils/ObbFile.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2010 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 OBBFILE_H_ +#define OBBFILE_H_ + +#include + +#include +#include + +namespace android { + +class ObbFile : public RefBase { +protected: + virtual ~ObbFile(); + +public: + ObbFile(); + + bool readFrom(const char* filename); + bool readFrom(int fd); + bool writeTo(const char* filename); + bool writeTo(int fd); + + const char* getFileName() const { + return mFileName; + } + + const String8 getPackageName() const { + return mPackageName; + } + + int32_t getVersion() const { + return mVersion; + } + + void setPackageName(String8 packageName) { + mPackageName = packageName; + } + + void setVersion(int32_t version) { + mVersion = version; + } + + static inline uint32_t get4LE(const unsigned char* buf) { + return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); + } + + static inline void put4LE(unsigned char* buf, uint32_t val) { + buf[0] = val & 0xFF; + buf[1] = (val >> 8) & 0xFF; + buf[2] = (val >> 16) & 0xFF; + buf[3] = (val >> 24) & 0xFF; + } + +private: + /* Package name this ObbFile is associated with */ + String8 mPackageName; + + /* Package version this ObbFile is associated with */ + int32_t mVersion; + + const char* mFileName; + + size_t mFileSize; + + unsigned char* mReadBuf; + + bool parseObbFile(int fd); +}; + +} +#endif /* OBBFILE_H_ */ diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk index 7d4524aa5..2bb42ab68 100644 --- a/libs/utils/Android.mk +++ b/libs/utils/Android.mk @@ -26,6 +26,7 @@ commonSources:= \ Debug.cpp \ FileMap.cpp \ Flattenable.cpp \ + ObbFile.cpp \ Pool.cpp \ RefBase.cpp \ ResourceTypes.cpp \ @@ -65,6 +66,11 @@ LOCAL_CFLAGS += -DMB_CUR_MAX=1 endif endif +ifeq ($(HOST_OS),darwin) +# MacOS doesn't have lseek64. However, off_t is 64-bit anyway. +LOCAL_CFLAGS += -DOFF_T_IS_64_BIT +endif + include $(BUILD_HOST_STATIC_LIBRARY) diff --git a/libs/utils/ObbFile.cpp b/libs/utils/ObbFile.cpp new file mode 100644 index 000000000..3a4a03a77 --- /dev/null +++ b/libs/utils/ObbFile.cpp @@ -0,0 +1,284 @@ +/* + * Copyright (C) 2010 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 +#include +#include +#include +#include +#include + +#define LOG_TAG "ObbFile" +#include +#include + +//#define DEBUG 1 + +#define kFooterTagSize 8 /* last two 32-bit integers */ + +#define kFooterMinSize 21 /* 32-bit signature version + * 32-bit package version + * 32-bit package name size + * 1-character package name + * 32-bit footer size + * 32-bit footer marker + */ + +#define kMaxBufSize 32768 /* Maximum file read buffer */ + +#define kSignature 0x01059983U /* ObbFile signature */ + +#define kSigVersion 1 /* We only know about signature version 1 */ + +/* offsets in version 1 of the header */ +#define kPackageVersionOffset 4 +#define kPackageNameLenOffset 8 +#define kPackageNameOffset 12 + +/* + * TEMP_FAILURE_RETRY is defined by some, but not all, versions of + * . (Alas, it is not as standard as we'd hoped!) So, if it's + * not already defined, then define it here. + */ +#ifndef TEMP_FAILURE_RETRY +/* Used to retry syscalls that can return EINTR. */ +#define TEMP_FAILURE_RETRY(exp) ({ \ + typeof (exp) _rc; \ + do { \ + _rc = (exp); \ + } while (_rc == -1 && errno == EINTR); \ + _rc; }) +#endif + +/* + * Work around situations where off_t is 64-bit and use off64_t in + * situations where it's 32-bit. + */ +#ifdef OFF_T_IS_64_BIT +#define my_lseek64 lseek +typedef off_t my_off64_t; +#else +#define my_lseek64 lseek64 +typedef off64_t my_off64_t; +#endif + +namespace android { + +ObbFile::ObbFile() : + mVersion(-1) { +} + +ObbFile::~ObbFile() { +} + +bool ObbFile::readFrom(const char* filename) +{ + int fd; + bool success = false; + + fd = ::open(filename, O_RDONLY); + if (fd < 0) { + goto out; + } + success = readFrom(fd); + close(fd); + +out: + if (!success) { + LOGW("failed to read from %s\n", filename); + } + return success; +} + +bool ObbFile::readFrom(int fd) +{ + if (fd < 0) { + LOGW("failed to read file\n"); + return false; + } + + return parseObbFile(fd); +} + +bool ObbFile::parseObbFile(int fd) +{ + my_off64_t fileLength = my_lseek64(fd, 0, SEEK_END); + + if (fileLength < kFooterMinSize) { + if (fileLength < 0) { + LOGW("error seeking in ObbFile: %s\n", strerror(errno)); + } else { + LOGW("file is only %lld (less than %d minimum)\n", fileLength, kFooterMinSize); + } + return false; + } + + ssize_t actual; + size_t footerSize; + + { + my_lseek64(fd, fileLength - kFooterTagSize, SEEK_SET); + + char *footer = new char[kFooterTagSize]; + actual = TEMP_FAILURE_RETRY(read(fd, footer, kFooterTagSize)); + if (actual != kFooterTagSize) { + LOGW("couldn't read footer signature: %s\n", strerror(errno)); + return false; + } + + unsigned int fileSig = get4LE((unsigned char*)footer + sizeof(int32_t)); + if (fileSig != kSignature) { + LOGW("footer didn't match magic string (expected 0x%08x; got 0x%08x)\n", + kSignature, fileSig); + return false; + } + + footerSize = get4LE((unsigned char*)footer); + if (footerSize > (size_t)fileLength - kFooterTagSize + || footerSize > kMaxBufSize) { + LOGW("claimed footer size is too large (0x%08lx; file size is 0x%08llx)\n", + footerSize, fileLength); + return false; + } + } + + my_off64_t fileOffset = fileLength - footerSize - kFooterTagSize; + if (my_lseek64(fd, fileOffset, SEEK_SET) != fileOffset) { + LOGW("seek %lld failed: %s\n", fileOffset, strerror(errno)); + return false; + } + + size_t readAmount = kMaxBufSize; + if (readAmount > footerSize) + readAmount = footerSize; + + char* scanBuf = (char*)malloc(readAmount); + if (scanBuf == NULL) { + LOGW("couldn't allocate scanBuf: %s\n", strerror(errno)); + return false; + } + + actual = TEMP_FAILURE_RETRY(read(fd, scanBuf, readAmount)); + // readAmount is guaranteed to be less than kMaxBufSize + if (actual != (ssize_t)readAmount) { + LOGI("couldn't read ObbFile footer: %s\n", strerror(errno)); + free(scanBuf); + return false; + } + +#ifdef DEBUG + for (int i = 0; i < readAmount; ++i) { + LOGI("char: 0x%02x", scanBuf[i]); + } +#endif + + uint32_t sigVersion = get4LE((unsigned char*)scanBuf); + if (sigVersion != kSigVersion) { + LOGW("Unsupported ObbFile version %d\n", sigVersion); + free(scanBuf); + return false; + } + + mVersion = (int32_t) get4LE((unsigned char*)scanBuf + kPackageVersionOffset); + + uint32_t packageNameLen = get4LE((unsigned char*)scanBuf + kPackageNameLenOffset); + if (packageNameLen <= 0 + || packageNameLen > (footerSize - kPackageNameOffset)) { + LOGW("bad ObbFile package name length (0x%08x)\n", packageNameLen); + free(scanBuf); + return false; + } + + char* packageName = reinterpret_cast(scanBuf + kPackageNameOffset); + mPackageName = String8(const_cast(packageName), packageNameLen); + + free(scanBuf); + return true; +} + +bool ObbFile::writeTo(const char* filename) +{ + int fd; + bool success = false; + + fd = ::open(filename, O_WRONLY); + if (fd < 0) { + goto out; + } + success = writeTo(fd); + close(fd); + +out: + if (!success) { + LOGW("failed to write to %s: %s\n", filename, strerror(errno)); + } + return success; +} + +bool ObbFile::writeTo(int fd) +{ + if (fd < 0) { + return false; + } + + if (mPackageName.size() == 0 || mVersion == -1) { + LOGW("tried to write uninitialized ObbFile data"); + return false; + } + + unsigned char intBuf[sizeof(uint32_t)+1]; + memset(&intBuf, 0, sizeof(intBuf)); + + put4LE(intBuf, kSigVersion); + if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) { + LOGW("couldn't write signature version: %s", strerror(errno)); + return false; + } + + put4LE(intBuf, mVersion); + if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) { + LOGW("couldn't write package version"); + return false; + } + + size_t packageNameLen = mPackageName.size(); + put4LE(intBuf, packageNameLen); + if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) { + LOGW("couldn't write package name length: %s", strerror(errno)); + return false; + } + + if (write(fd, mPackageName.string(), packageNameLen) != (ssize_t)packageNameLen) { + LOGW("couldn't write package name: %s", strerror(errno)); + return false; + } + + put4LE(intBuf, 3*sizeof(uint32_t) + packageNameLen); + if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) { + LOGW("couldn't write footer size: %s", strerror(errno)); + return false; + } + + put4LE(intBuf, kSignature); + if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) { + LOGW("couldn't write footer magic signature: %s", strerror(errno)); + return false; + } + + return true; +} + +} diff --git a/libs/utils/tests/Android.mk b/libs/utils/tests/Android.mk index 92ebfd7c3..f1b8cd56e 100644 --- a/libs/utils/tests/Android.mk +++ b/libs/utils/tests/Android.mk @@ -3,6 +3,7 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) test_src_files := \ + ObbFile_test.cpp \ PollLoop_test.cpp shared_libraries := \ diff --git a/libs/utils/tests/ObbFile_test.cpp b/libs/utils/tests/ObbFile_test.cpp new file mode 100644 index 000000000..05aaf0870 --- /dev/null +++ b/libs/utils/tests/ObbFile_test.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2010 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 "ObbFile_test" +#include +#include +#include +#include + +#include + +namespace android { + +#define TEST_FILENAME "/test.obb" + +class ObbFileTest : public testing::Test { +protected: + sp mObbFile; + char* mExternalStorage; + char* mFileName; + + virtual void SetUp() { + mObbFile = new ObbFile(); + mExternalStorage = getenv("EXTERNAL_STORAGE"); + + const int totalLen = strlen(mExternalStorage) + strlen(TEST_FILENAME) + 1; + mFileName = new char[totalLen]; + snprintf(mFileName, totalLen, "%s%s", mExternalStorage, TEST_FILENAME); + } + + virtual void TearDown() { + } +}; + +TEST_F(ObbFileTest, ReadFailure) { + EXPECT_FALSE(mObbFile->readFrom(-1)) + << "No failure on invalid file descriptor"; +} + +TEST_F(ObbFileTest, WriteThenRead) { + const char* packageName = "com.example.obbfile"; + const int32_t versionNum = 1; + + mObbFile->setPackageName(String8(packageName)); + mObbFile->setVersion(versionNum); + + EXPECT_TRUE(mObbFile->writeTo(mFileName)) + << "couldn't write to fake .obb file"; + + mObbFile = new ObbFile(); + + EXPECT_TRUE(mObbFile->readFrom(mFileName)) + << "couldn't read from fake .obb file"; + + EXPECT_EQ(versionNum, mObbFile->getVersion()) + << "version didn't come out the same as it went in"; + const char* currentPackageName = mObbFile->getPackageName().string(); + EXPECT_STREQ(packageName, currentPackageName) + << "package name didn't come out the same as it went in"; +} + +} From 245708a1bec5f50581b0093aed573bbe7ad6c708 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Wed, 7 Jul 2010 14:27:31 -0700 Subject: [PATCH 225/541] Add new glue code for writing native apps. This factors out the boiler-plate code from the sample app to a common glue code that can be used for everyone writing this style of app: a dedicated app thread that takes care of waiting for events and processing them. As part of doing this, ALooper has a new facility to allow registration of fds that cause ALooper_pollOnce() to return the fd that has data, allowing the app to drive the loop without callbacks. Hopefully this makes some people feel better. :) Also do some other cleanup of the ALooper API, plus some actual documentation. Change-Id: Ic53bd56bdf627e3ba28a3c093faa06a92be522b8 --- include/utils/PollLoop.h | 42 +++++++++++--- libs/utils/PollLoop.cpp | 93 +++++++++++++++++++----------- libs/utils/tests/PollLoop_test.cpp | 78 ++++++++++++------------- 3 files changed, 133 insertions(+), 80 deletions(-) diff --git a/include/utils/PollLoop.h b/include/utils/PollLoop.h index b3651caea..81230e84d 100644 --- a/include/utils/PollLoop.h +++ b/include/utils/PollLoop.h @@ -42,7 +42,7 @@ protected: virtual ~PollLoop(); public: - PollLoop(); + PollLoop(bool allowNonCallbacks); /** * A callback that it to be invoked when an event occurs on a file descriptor. @@ -54,6 +54,12 @@ public: */ typedef bool (*Callback)(int fd, int events, void* data); + enum { + POLL_CALLBACK = ALOOPER_POLL_CALLBACK, + POLL_TIMEOUT = ALOOPER_POLL_TIMEOUT, + POLL_ERROR = ALOOPER_POLL_ERROR, + }; + /** * Performs a single call to poll() with optional timeout in milliseconds. * Invokes callbacks for all file descriptors on which an event occurred. @@ -61,16 +67,25 @@ public: * If the timeout is zero, returns immediately without blocking. * If the timeout is negative, waits indefinitely until awoken. * - * Returns true if a callback was invoked or if the loop was awoken by wake(). - * Returns false if a timeout or error occurred. + * Returns ALOOPER_POLL_CALLBACK if a callback was invoked. * - * This method must only be called on the main thread. + * Returns ALOOPER_POLL_TIMEOUT if there was no data before the given + * timeout expired. + * + * Returns ALOPER_POLL_ERROR if an error occurred. + * + * Returns a value >= 0 containing a file descriptor if it has data + * and it has no callback function (requiring the caller here to handle it). + * In this (and only this) case outEvents and outData will contain the poll + * events and data associated with the fd. + * + * This method must only be called on the thread owning the PollLoop. * This method blocks until either a file descriptor is signalled, a timeout occurs, * or wake() is called. * This method does not return until it has finished invoking the appropriate callbacks * for all file descriptors that were signalled. */ - bool pollOnce(int timeoutMillis); + int32_t pollOnce(int timeoutMillis, int* outEvents = NULL, void** outData = NULL); /** * Wakes the loop asynchronously. @@ -80,6 +95,12 @@ public: */ void wake(); + /** + * Control whether this PollLoop instance allows using IDs instead + * of callbacks. + */ + bool getAllowNonCallbacks() const; + /** * Sets the callback for a file descriptor, replacing the existing one, if any. * It is an error to call this method with events == 0 or callback == NULL. @@ -95,7 +116,8 @@ public: /** * Like setCallback(), but for the NDK callback function. */ - void setLooperCallback(int fd, int events, ALooper_callbackFunc* callback, void* data); + void setLooperCallback(int fd, int events, ALooper_callbackFunc* callback, + void* data); /** * Removes the callback for a file descriptor, if one exists. @@ -141,7 +163,9 @@ private: ALooper_callbackFunc* looperCallback; void* data; }; - + + const bool mAllowNonCallbacks; + Mutex mLock; bool mPolling; uint32_t mWaiters; @@ -155,7 +179,9 @@ private: Vector mRequestedCallbacks; Vector mPendingCallbacks; // used privately by pollOnce - + Vector mPendingFds; // used privately by pollOnce + size_t mPendingFdsPos; + void openWakePipe(); void closeWakePipe(); diff --git a/libs/utils/PollLoop.cpp b/libs/utils/PollLoop.cpp index 58fe1411d..f740fa0d5 100644 --- a/libs/utils/PollLoop.cpp +++ b/libs/utils/PollLoop.cpp @@ -25,8 +25,9 @@ static pthread_mutex_t gTLSMutex = PTHREAD_MUTEX_INITIALIZER; static bool gHaveTLS = false; static pthread_key_t gTLS = 0; -PollLoop::PollLoop() : - mPolling(false), mWaiters(0) { +PollLoop::PollLoop(bool allowNonCallbacks) : + mAllowNonCallbacks(allowNonCallbacks), mPolling(false), + mWaiters(0), mPendingFdsPos(0) { openWakePipe(); } @@ -106,7 +107,18 @@ void PollLoop::closeWakePipe() { // method is currently only called by the destructor. } -bool PollLoop::pollOnce(int timeoutMillis) { +int32_t PollLoop::pollOnce(int timeoutMillis, int* outEvents, void** outData) { + // If there are still pending fds from the last call, dispatch those + // first, to avoid an earlier fd from starving later ones. + const size_t pendingFdsCount = mPendingFds.size(); + if (mPendingFdsPos < pendingFdsCount) { + const PendingCallback& pending = mPendingFds.itemAt(mPendingFdsPos); + mPendingFdsPos++; + if (outEvents != NULL) *outEvents = pending.events; + if (outData != NULL) *outData = pending.data; + return pending.fd; + } + mLock.lock(); while (mWaiters != 0) { mResume.wait(mLock); @@ -114,7 +126,7 @@ bool PollLoop::pollOnce(int timeoutMillis) { mPolling = true; mLock.unlock(); - bool result; + int32_t result; size_t requestedCount = mRequestedFds.size(); #if DEBUG_POLL_AND_WAKE @@ -131,7 +143,7 @@ bool PollLoop::pollOnce(int timeoutMillis) { #if DEBUG_POLL_AND_WAKE LOGD("%p ~ pollOnce - timeout", this); #endif - result = false; + result = POLL_TIMEOUT; goto Done; } @@ -143,7 +155,7 @@ bool PollLoop::pollOnce(int timeoutMillis) { if (errno != EINTR) { LOGW("Poll failed with an unexpected error, errno=%d", errno); } - result = false; + result = POLL_ERROR; goto Done; } @@ -156,38 +168,44 @@ bool PollLoop::pollOnce(int timeoutMillis) { #endif mPendingCallbacks.clear(); + mPendingFds.clear(); + mPendingFdsPos = 0; + if (outEvents != NULL) *outEvents = 0; + if (outData != NULL) *outData = NULL; + + result = POLL_CALLBACK; for (size_t i = 0; i < requestedCount; i++) { const struct pollfd& requestedFd = mRequestedFds.itemAt(i); short revents = requestedFd.revents; if (revents) { const RequestedCallback& requestedCallback = mRequestedCallbacks.itemAt(i); - Callback callback = requestedCallback.callback; - ALooper_callbackFunc* looperCallback = requestedCallback.looperCallback; + PendingCallback pending; + pending.fd = requestedFd.fd; + pending.events = revents; + pending.callback = requestedCallback.callback; + pending.looperCallback = requestedCallback.looperCallback; + pending.data = requestedCallback.data; - if (callback || looperCallback) { - PendingCallback pendingCallback; - pendingCallback.fd = requestedFd.fd; - pendingCallback.events = requestedFd.revents; - pendingCallback.callback = callback; - pendingCallback.looperCallback = looperCallback; - pendingCallback.data = requestedCallback.data; - mPendingCallbacks.push(pendingCallback); - } else { - if (requestedFd.fd == mWakeReadPipeFd) { -#if DEBUG_POLL_AND_WAKE - LOGD("%p ~ pollOnce - awoken", this); -#endif - char buffer[16]; - ssize_t nRead; - do { - nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer)); - } while (nRead == sizeof(buffer)); + if (pending.callback || pending.looperCallback) { + mPendingCallbacks.push(pending); + } else if (pending.fd != mWakeReadPipeFd) { + if (result == POLL_CALLBACK) { + result = pending.fd; + if (outEvents != NULL) *outEvents = pending.events; + if (outData != NULL) *outData = pending.data; } else { -#if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS - LOGD("%p ~ pollOnce - fd %d has no callback!", this, requestedFd.fd); -#endif + mPendingFds.push(pending); } + } else { +#if DEBUG_POLL_AND_WAKE + LOGD("%p ~ pollOnce - awoken", this); +#endif + char buffer[16]; + ssize_t nRead; + do { + nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer)); + } while (nRead == sizeof(buffer)); } respondedCount -= 1; @@ -196,7 +214,6 @@ bool PollLoop::pollOnce(int timeoutMillis) { } } } - result = true; Done: mLock.lock(); @@ -206,7 +223,7 @@ Done: } mLock.unlock(); - if (result) { + if (result == POLL_CALLBACK || result >= 0) { size_t pendingCount = mPendingCallbacks.size(); for (size_t i = 0; i < pendingCount; i++) { const PendingCallback& pendingCallback = mPendingCallbacks.itemAt(i); @@ -247,6 +264,10 @@ void PollLoop::wake() { } } +bool PollLoop::getAllowNonCallbacks() const { + return mAllowNonCallbacks; +} + void PollLoop::setCallback(int fd, int events, Callback callback, void* data) { setCallbackCommon(fd, events, callback, NULL, data); } @@ -263,12 +284,18 @@ void PollLoop::setCallbackCommon(int fd, int events, Callback callback, LOGD("%p ~ setCallback - fd=%d, events=%d", this, fd, events); #endif - if (! events || (! callback && ! looperCallback)) { - LOGE("Invalid attempt to set a callback with no selected poll events or no callback."); + if (! events) { + LOGE("Invalid attempt to set a callback with no selected poll events."); removeCallback(fd); return; } + if (! callback && ! looperCallback && ! mAllowNonCallbacks) { + LOGE("Invalid attempt to set NULL callback but not allowed."); + removeCallback(fd); + return; + } + wakeAndLock(); struct pollfd requestedFd; diff --git a/libs/utils/tests/PollLoop_test.cpp b/libs/utils/tests/PollLoop_test.cpp index 4848c0fa6..02f180893 100644 --- a/libs/utils/tests/PollLoop_test.cpp +++ b/libs/utils/tests/PollLoop_test.cpp @@ -87,7 +87,7 @@ protected: sp mPollLoop; virtual void SetUp() { - mPollLoop = new PollLoop(); + mPollLoop = new PollLoop(false); } virtual void TearDown() { @@ -98,26 +98,26 @@ protected: TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndNotAwoken_WaitsForTimeoutAndReturnsFalse) { StopWatch stopWatch("pollOnce"); - bool result = mPollLoop->pollOnce(100); + int32_t result = mPollLoop->pollOnce(100); int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) << "elapsed time should approx. equal timeout"; - EXPECT_FALSE(result) - << "pollOnce result should be false because timeout occurred"; + EXPECT_EQ(result, PollLoop::POLL_TIMEOUT) + << "pollOnce result should be POLL_TIMEOUT"; } TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndAwokenBeforeWaiting_ImmediatelyReturnsTrue) { mPollLoop->wake(); StopWatch stopWatch("pollOnce"); - bool result = mPollLoop->pollOnce(1000); + int32_t result = mPollLoop->pollOnce(1000); int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) << "elapsed time should approx. zero because wake() was called before waiting"; - EXPECT_TRUE(result) - << "pollOnce result should be true because loop was awoken"; + EXPECT_EQ(result, PollLoop::POLL_CALLBACK) + << "pollOnce result should be POLL_CALLBACK because loop was awoken"; } TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndAwokenWhileWaiting_PromptlyReturnsTrue) { @@ -125,24 +125,24 @@ TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndAwokenWhileWaiting_PromptlyRe delayedWake->run(); StopWatch stopWatch("pollOnce"); - bool result = mPollLoop->pollOnce(1000); + int32_t result = mPollLoop->pollOnce(1000); int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) << "elapsed time should approx. equal wake delay"; - EXPECT_TRUE(result) - << "pollOnce result should be true because loop was awoken"; + EXPECT_EQ(result, PollLoop::POLL_CALLBACK) + << "pollOnce result should be POLL_CALLBACK because loop was awoken"; } TEST_F(PollLoopTest, PollOnce_WhenZeroTimeoutAndNoRegisteredFDs_ImmediatelyReturnsFalse) { StopWatch stopWatch("pollOnce"); - bool result = mPollLoop->pollOnce(0); + int32_t result = mPollLoop->pollOnce(0); int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) << "elapsed time should be approx. zero"; - EXPECT_FALSE(result) - << "pollOnce result should be false because timeout occurred"; + EXPECT_EQ(result, PollLoop::POLL_TIMEOUT) + << "pollOnce result should be POLL_TIMEOUT"; } TEST_F(PollLoopTest, PollOnce_WhenZeroTimeoutAndNoSignalledFDs_ImmediatelyReturnsFalse) { @@ -152,13 +152,13 @@ TEST_F(PollLoopTest, PollOnce_WhenZeroTimeoutAndNoSignalledFDs_ImmediatelyReturn handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN); StopWatch stopWatch("pollOnce"); - bool result = mPollLoop->pollOnce(0); + int32_t result = mPollLoop->pollOnce(0); int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) << "elapsed time should be approx. zero"; - EXPECT_FALSE(result) - << "pollOnce result should be false because timeout occurred"; + EXPECT_EQ(result, PollLoop::POLL_TIMEOUT) + << "pollOnce result should be POLL_TIMEOUT"; EXPECT_EQ(0, handler.callbackCount) << "callback should not have been invoked because FD was not signalled"; } @@ -171,13 +171,13 @@ TEST_F(PollLoopTest, PollOnce_WhenZeroTimeoutAndSignalledFD_ImmediatelyInvokesCa handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN); StopWatch stopWatch("pollOnce"); - bool result = mPollLoop->pollOnce(0); + int32_t result = mPollLoop->pollOnce(0); int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) << "elapsed time should be approx. zero"; - EXPECT_TRUE(result) - << "pollOnce result should be true because FD was signalled"; + EXPECT_EQ(result, PollLoop::POLL_CALLBACK) + << "pollOnce result should be POLL_CALLBACK because FD was signalled"; EXPECT_EQ(1, handler.callbackCount) << "callback should be invoked exactly once"; EXPECT_EQ(pipe.receiveFd, handler.fd) @@ -193,13 +193,13 @@ TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndNoSignalledFDs_WaitsForTimeou handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN); StopWatch stopWatch("pollOnce"); - bool result = mPollLoop->pollOnce(100); + int32_t result = mPollLoop->pollOnce(100); int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) << "elapsed time should approx. equal timeout"; - EXPECT_FALSE(result) - << "pollOnce result should be false because timeout occurred"; + EXPECT_EQ(result, PollLoop::POLL_TIMEOUT) + << "pollOnce result should be POLL_TIMEOUT"; EXPECT_EQ(0, handler.callbackCount) << "callback should not have been invoked because FD was not signalled"; } @@ -212,15 +212,15 @@ TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndSignalledFDBeforeWaiting_Imme handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN); StopWatch stopWatch("pollOnce"); - bool result = mPollLoop->pollOnce(100); + int32_t result = mPollLoop->pollOnce(100); int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); ASSERT_EQ(OK, pipe.readSignal()) << "signal should actually have been written"; EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) << "elapsed time should be approx. zero"; - EXPECT_TRUE(result) - << "pollOnce result should be true because FD was signalled"; + EXPECT_EQ(result, PollLoop::POLL_CALLBACK) + << "pollOnce result should be POLL_CALLBACK because FD was signalled"; EXPECT_EQ(1, handler.callbackCount) << "callback should be invoked exactly once"; EXPECT_EQ(pipe.receiveFd, handler.fd) @@ -238,15 +238,15 @@ TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndSignalledFDWhileWaiting_Promp delayedWriteSignal->run(); StopWatch stopWatch("pollOnce"); - bool result = mPollLoop->pollOnce(1000); + int32_t result = mPollLoop->pollOnce(1000); int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); ASSERT_EQ(OK, pipe.readSignal()) << "signal should actually have been written"; EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) << "elapsed time should approx. equal signal delay"; - EXPECT_TRUE(result) - << "pollOnce result should be true because FD was signalled"; + EXPECT_EQ(result, PollLoop::POLL_CALLBACK) + << "pollOnce result should be POLL_CALLBACK because FD was signalled"; EXPECT_EQ(1, handler.callbackCount) << "callback should be invoked exactly once"; EXPECT_EQ(pipe.receiveFd, handler.fd) @@ -264,15 +264,15 @@ TEST_F(PollLoopTest, PollOnce_WhenCallbackAddedThenRemoved_CallbackShouldNotBeIn mPollLoop->removeCallback(pipe.receiveFd); StopWatch stopWatch("pollOnce"); - bool result = mPollLoop->pollOnce(100); + int32_t result = mPollLoop->pollOnce(100); int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); ASSERT_EQ(OK, pipe.readSignal()) << "signal should actually have been written"; EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) << "elapsed time should approx. equal timeout because FD was no longer registered"; - EXPECT_FALSE(result) - << "pollOnce result should be false because timeout occurred"; + EXPECT_EQ(result, PollLoop::POLL_TIMEOUT) + << "pollOnce result should be POLL_TIMEOUT"; EXPECT_EQ(0, handler.callbackCount) << "callback should not be invoked"; } @@ -287,15 +287,15 @@ TEST_F(PollLoopTest, PollOnce_WhenCallbackReturnsFalse_CallbackShouldNotBeInvoke pipe.writeSignal(); StopWatch stopWatch("pollOnce"); - bool result = mPollLoop->pollOnce(0); + int32_t result = mPollLoop->pollOnce(0); int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); ASSERT_EQ(OK, pipe.readSignal()) << "signal should actually have been written"; EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) << "elapsed time should approx. equal zero because FD was already signalled"; - EXPECT_TRUE(result) - << "pollOnce result should be true because FD was signalled"; + EXPECT_EQ(result, PollLoop::POLL_CALLBACK) + << "pollOnce result should be POLL_CALLBACK because FD was signalled"; EXPECT_EQ(1, handler.callbackCount) << "callback should be invoked"; @@ -310,8 +310,8 @@ TEST_F(PollLoopTest, PollOnce_WhenCallbackReturnsFalse_CallbackShouldNotBeInvoke << "signal should actually have been written"; EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) << "elapsed time should approx. equal zero because timeout was zero"; - EXPECT_FALSE(result) - << "pollOnce result should be false because timeout occurred"; + EXPECT_EQ(result, PollLoop::POLL_TIMEOUT) + << "pollOnce result should be POLL_TIMEOUT"; EXPECT_EQ(1, handler.callbackCount) << "callback should not be invoked this time"; } @@ -351,15 +351,15 @@ TEST_F(PollLoopTest, PollOnce_WhenCallbackAddedTwice_OnlySecondCallbackShouldBeI pipe.writeSignal(); // would cause FD to be considered signalled StopWatch stopWatch("pollOnce"); - bool result = mPollLoop->pollOnce(100); + int32_t result = mPollLoop->pollOnce(100); int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); ASSERT_EQ(OK, pipe.readSignal()) << "signal should actually have been written"; EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) << "elapsed time should approx. zero because FD was already signalled"; - EXPECT_TRUE(result) - << "pollOnce result should be true because FD was signalled"; + EXPECT_EQ(result, PollLoop::POLL_CALLBACK) + << "pollOnce result should be POLL_CALLBACK because FD was signalled"; EXPECT_EQ(0, handler1.callbackCount) << "original handler callback should not be invoked because it was replaced"; EXPECT_EQ(1, handler2.callbackCount) From 51e2fb7086548af8610308a54d8f7c241c8b0932 Mon Sep 17 00:00:00 2001 From: Kenny Root Date: Mon, 12 Jul 2010 09:02:18 -0700 Subject: [PATCH 226/541] Tweak ObbFile class * Move error messages around to clarify the errors. * Add extra error check when reading a file. * Seek to the end of a file when writing the signature so the users of the API don't have to remember to do it. Change-Id: I2337051b9f9fa8147c5900237deec790dcd92436 --- libs/utils/ObbFile.cpp | 38 ++++++++++++++++++++----------- libs/utils/tests/ObbFile_test.cpp | 15 ++++++++---- 2 files changed, 36 insertions(+), 17 deletions(-) diff --git a/libs/utils/ObbFile.cpp b/libs/utils/ObbFile.cpp index 3a4a03a77..fe49300eb 100644 --- a/libs/utils/ObbFile.cpp +++ b/libs/utils/ObbFile.cpp @@ -91,22 +91,24 @@ bool ObbFile::readFrom(const char* filename) fd = ::open(filename, O_RDONLY); if (fd < 0) { + LOGW("couldn't open file %s: %s", filename, strerror(errno)); goto out; } success = readFrom(fd); close(fd); -out: if (!success) { - LOGW("failed to read from %s\n", filename); + LOGW("failed to read from %s (fd=%d)\n", filename, fd); } + +out: return success; } bool ObbFile::readFrom(int fd) { if (fd < 0) { - LOGW("failed to read file\n"); + LOGW("attempt to read from invalid fd\n"); return false; } @@ -149,10 +151,16 @@ bool ObbFile::parseObbFile(int fd) footerSize = get4LE((unsigned char*)footer); if (footerSize > (size_t)fileLength - kFooterTagSize || footerSize > kMaxBufSize) { - LOGW("claimed footer size is too large (0x%08lx; file size is 0x%08llx)\n", + LOGW("claimed footer size is too large (0x%08zx; file size is 0x%08llx)\n", footerSize, fileLength); return false; } + + if (footerSize < kFooterMinSize) { + LOGW("claimed footer size is too small (%08zx; minimum size is 0x%x)\n", + footerSize, kFooterMinSize); + return false; + } } my_off64_t fileOffset = fileLength - footerSize - kFooterTagSize; @@ -161,26 +169,22 @@ bool ObbFile::parseObbFile(int fd) return false; } - size_t readAmount = kMaxBufSize; - if (readAmount > footerSize) - readAmount = footerSize; - - char* scanBuf = (char*)malloc(readAmount); + char* scanBuf = (char*)malloc(footerSize); if (scanBuf == NULL) { LOGW("couldn't allocate scanBuf: %s\n", strerror(errno)); return false; } - actual = TEMP_FAILURE_RETRY(read(fd, scanBuf, readAmount)); + actual = TEMP_FAILURE_RETRY(read(fd, scanBuf, footerSize)); // readAmount is guaranteed to be less than kMaxBufSize - if (actual != (ssize_t)readAmount) { + if (actual != (ssize_t)footerSize) { LOGI("couldn't read ObbFile footer: %s\n", strerror(errno)); free(scanBuf); return false; } #ifdef DEBUG - for (int i = 0; i < readAmount; ++i) { + for (int i = 0; i < footerSize; ++i) { LOGI("char: 0x%02x", scanBuf[i]); } #endif @@ -197,7 +201,8 @@ bool ObbFile::parseObbFile(int fd) uint32_t packageNameLen = get4LE((unsigned char*)scanBuf + kPackageNameLenOffset); if (packageNameLen <= 0 || packageNameLen > (footerSize - kPackageNameOffset)) { - LOGW("bad ObbFile package name length (0x%08x)\n", packageNameLen); + LOGW("bad ObbFile package name length (0x%04x; 0x%04x possible)\n", + packageNameLen, footerSize - kPackageNameOffset); free(scanBuf); return false; } @@ -206,6 +211,11 @@ bool ObbFile::parseObbFile(int fd) mPackageName = String8(const_cast(packageName), packageNameLen); free(scanBuf); + +#ifdef DEBUG + LOGI("Obb scan succeeded: packageName=%s, version=%d\n", mPackageName.string(), mVersion); +#endif + return true; } @@ -234,6 +244,8 @@ bool ObbFile::writeTo(int fd) return false; } + my_lseek64(fd, 0, SEEK_END); + if (mPackageName.size() == 0 || mVersion == -1) { LOGW("tried to write uninitialized ObbFile data"); return false; diff --git a/libs/utils/tests/ObbFile_test.cpp b/libs/utils/tests/ObbFile_test.cpp index 05aaf0870..29bb70a66 100644 --- a/libs/utils/tests/ObbFile_test.cpp +++ b/libs/utils/tests/ObbFile_test.cpp @@ -22,6 +22,8 @@ #include +#include + namespace android { #define TEST_FILENAME "/test.obb" @@ -39,6 +41,11 @@ protected: const int totalLen = strlen(mExternalStorage) + strlen(TEST_FILENAME) + 1; mFileName = new char[totalLen]; snprintf(mFileName, totalLen, "%s%s", mExternalStorage, TEST_FILENAME); + + int fd = ::open(mFileName, O_CREAT | O_TRUNC); + if (fd < 0) { + FAIL() << "Couldn't create " << mFileName << " for tests"; + } } virtual void TearDown() { @@ -46,8 +53,8 @@ protected: }; TEST_F(ObbFileTest, ReadFailure) { - EXPECT_FALSE(mObbFile->readFrom(-1)) - << "No failure on invalid file descriptor"; + EXPECT_FALSE(mObbFile->readFrom(-1)) + << "No failure on invalid file descriptor"; } TEST_F(ObbFileTest, WriteThenRead) { @@ -66,10 +73,10 @@ TEST_F(ObbFileTest, WriteThenRead) { << "couldn't read from fake .obb file"; EXPECT_EQ(versionNum, mObbFile->getVersion()) - << "version didn't come out the same as it went in"; + << "version didn't come out the same as it went in"; const char* currentPackageName = mObbFile->getPackageName().string(); EXPECT_STREQ(packageName, currentPackageName) - << "package name didn't come out the same as it went in"; + << "package name didn't come out the same as it went in"; } } From 7aba23129b339f6f6ea4c378aa5f3a78d7f677dd Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Wed, 30 Jun 2010 16:10:35 -0700 Subject: [PATCH 227/541] Add initial gamepad support. Change-Id: I0439648f6eb5405f200e4223c915eb3a418b32b9 --- libs/utils/Android.mk | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk index 2bb42ab68..8bd582353 100644 --- a/libs/utils/Android.mk +++ b/libs/utils/Android.mk @@ -122,3 +122,13 @@ LOCAL_SRC_FILES := $(commonSources) BackupData.cpp BackupHelpers.cpp include $(BUILD_STATIC_LIBRARY) endif endif + + +# Include subdirectory makefiles +# ============================================================ + +# If we're building with ONE_SHOT_MAKEFILE (mm, mmm), then what the framework +# team really wants is to build the stuff defined by this makefile. +ifeq (,$(ONE_SHOT_MAKEFILE)) +include $(call first-makefiles-under,$(LOCAL_PATH)) +endif \ No newline at end of file From 3f607c1c84631e61ddc11234466620e38fabfc1f Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Wed, 14 Jul 2010 22:40:08 -0700 Subject: [PATCH 228/541] Don't build framework tests for simulator target. Change-Id: I70f29c7eb307e4f3ec5702f4eb9d97b4342e2f36 --- libs/utils/tests/Android.mk | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libs/utils/tests/Android.mk b/libs/utils/tests/Android.mk index f1b8cd56e..b9f206a9d 100644 --- a/libs/utils/tests/Android.mk +++ b/libs/utils/tests/Android.mk @@ -2,6 +2,9 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) +ifneq ($(TARGET_SIMULATOR),true) + +# Build the unit tests. test_src_files := \ ObbFile_test.cpp \ PollLoop_test.cpp @@ -37,3 +40,5 @@ $(foreach file,$(test_src_files), \ $(eval LOCAL_MODULE_TAGS := $(module_tags)) \ $(eval include $(BUILD_EXECUTABLE)) \ ) + +endif \ No newline at end of file From 9f43945b7cba2698500c1f4adb17492c5e70ee86 Mon Sep 17 00:00:00 2001 From: Christopher Tate Date: Mon, 12 Jul 2010 18:21:36 -0700 Subject: [PATCH 229/541] Add native C APIs for working with the Asset Manager Change-Id: I493b142c4b35e5cc1a1e85283bb5dfb306a6d261 --- include/utils/AssetManager.h | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/include/utils/AssetManager.h b/include/utils/AssetManager.h index d8994e05a..97694ff0a 100644 --- a/include/utils/AssetManager.h +++ b/include/utils/AssetManager.h @@ -29,6 +29,24 @@ #include #include +/* + * Native-app access is via the opaque typedef struct AAssetManager in the C namespace. + */ +#ifdef __cplusplus +extern "C" { +#endif + +struct AAssetManager { }; + +#ifdef __cplusplus +}; +#endif + + +/* + * Now the proper C++ android-namespace definitions + */ + namespace android { class Asset; // fwd decl for things that include Asset.h first @@ -48,7 +66,7 @@ struct ResTable_config; * The asset hierarchy may be examined like a filesystem, using * AssetDir objects to peruse a single directory. */ -class AssetManager { +class AssetManager : public AAssetManager { public: typedef enum CacheMode { CACHE_UNKNOWN = 0, From 35a154e57f2f59722de580395ccd5172e44842cb Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Thu, 15 Jul 2010 23:54:05 -0700 Subject: [PATCH 230/541] Fix bug with phantom input windows. Add dumpsys integration for the native input dispatcher. Add some InputDevice API stubs. Add an appendFormat helper method to String8 for printf style string formatting mainly for debugging purposes. Use generic ArrayList everywhere in WindowManagerService to eliminate unnecessary casts all over. Change-Id: I9d1e3bd90eb7222d10620200477f11b7bfd25e44 --- include/utils/String8.h | 2 ++ libs/utils/String8.cpp | 36 +++++++++++++++++++++++++++++------- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/include/utils/String8.h b/include/utils/String8.h index c4b18a4ca..0b18fe3d2 100644 --- a/include/utils/String8.h +++ b/include/utils/String8.h @@ -171,6 +171,8 @@ public: status_t append(const char* other); status_t append(const char* other, size_t numChars); + status_t appendFormat(const char* fmt, ...); + // Note that this function takes O(N) time to calculate the value. // No cache value is stored. size_t getUtf32Length() const; diff --git a/libs/utils/String8.cpp b/libs/utils/String8.cpp index 82776f47a..1c4f80c1f 100644 --- a/libs/utils/String8.cpp +++ b/libs/utils/String8.cpp @@ -372,6 +372,27 @@ status_t String8::append(const char* other, size_t otherLen) return real_append(other, otherLen); } +status_t String8::appendFormat(const char* fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + + int result = NO_ERROR; + int n = vsnprintf(NULL, 0, fmt, ap); + if (n != 0) { + size_t oldLength = length(); + char* buf = lockBuffer(oldLength + n); + if (buf) { + vsnprintf(buf + oldLength, n + 1, fmt, ap); + } else { + result = NO_MEMORY; + } + } + + va_end(ap); + return result; +} + status_t String8::real_append(const char* other, size_t otherLen) { const size_t myLen = bytes(); @@ -411,15 +432,16 @@ status_t String8::unlockBuffer(size_t size) if (size != this->size()) { SharedBuffer* buf = SharedBuffer::bufferFromData(mString) ->editResize(size+1); - if (buf) { - char* str = (char*)buf->data(); - str[size] = 0; - mString = str; - return NO_ERROR; + if (! buf) { + return NO_MEMORY; } + + char* str = (char*)buf->data(); + str[size] = 0; + mString = str; } - - return NO_MEMORY; + + return NO_ERROR; } ssize_t String8::find(const char* other, size_t start) const From c28f214f6c65c56936b11fff68564d9631cfae70 Mon Sep 17 00:00:00 2001 From: Christopher Tate Date: Mon, 26 Jul 2010 11:24:18 -0700 Subject: [PATCH 231/541] Support streaming of compressed assets > 1 megabyte Compressed assets larger than one megabyte are now decompressed on demand rather than being decompressed in their entirety and held in memory. Reading the data in order is relatively efficient, as is seeking forward in the stream. Seeking backwards is supported, but requires reprocessing the compressed data from the beginning, so is very inefficient. In addition, the size limit on compressed assets has been eliminated. Change-Id: I6e68247957e6c53e7e8ba70d12764695f1723bad --- include/utils/Asset.h | 11 +- include/utils/StreamingZipInflater.h | 82 ++++++++++ libs/utils/Android.mk | 3 +- libs/utils/Asset.cpp | 76 +++++---- libs/utils/StreamingZipInflater.cpp | 224 +++++++++++++++++++++++++++ 5 files changed, 356 insertions(+), 40 deletions(-) create mode 100644 include/utils/StreamingZipInflater.h create mode 100644 libs/utils/StreamingZipInflater.cpp diff --git a/include/utils/Asset.h b/include/utils/Asset.h index 5908bcced..2a09095bd 100644 --- a/include/utils/Asset.h +++ b/include/utils/Asset.h @@ -61,15 +61,6 @@ public: ACCESS_BUFFER, } AccessMode; - enum { - /* data larger than this does not get uncompressed into a buffer */ -#ifdef HAVE_ANDROID_OS - UNCOMPRESS_DATA_MAX = 1 * 1024 * 1024 -#else - UNCOMPRESS_DATA_MAX = 2 * 1024 * 1024 -#endif - }; - /* * Read data from the current offset. Returns the actual number of * bytes read, 0 on EOF, or -1 on error. @@ -317,6 +308,8 @@ private: FileMap* mMap; // for memory-mapped input int mFd; // for file input + class StreamingZipInflater* mZipInflater; // for streaming large compressed assets + unsigned char* mBuf; // for getBuffer() }; diff --git a/include/utils/StreamingZipInflater.h b/include/utils/StreamingZipInflater.h new file mode 100644 index 000000000..16867d8db --- /dev/null +++ b/include/utils/StreamingZipInflater.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2010 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 __LIBS_STREAMINGZIPINFLATER_H +#define __LIBS_STREAMINGZIPINFLATER_H + +#include +#include +#include + +namespace android { + +class StreamingZipInflater { +public: + static const size_t INPUT_CHUNK_SIZE = 64 * 1024; + static const size_t OUTPUT_CHUNK_SIZE = 64 * 1024; + + // Flavor that pages in the compressed data from a fd + StreamingZipInflater(int fd, off_t compDataStart, size_t uncompSize, size_t compSize); + + // Flavor that gets the compressed data from an in-memory buffer + StreamingZipInflater(class FileMap* dataMap, size_t uncompSize); + + ~StreamingZipInflater(); + + // read 'count' bytes of uncompressed data from the current position. outBuf may + // be NULL, in which case the data is consumed and discarded. + ssize_t read(void* outBuf, size_t count); + + // seeking backwards requires uncompressing fom the beginning, so is very + // expensive. seeking forwards only requires uncompressing from the current + // position to the destination. + off_t seekAbsolute(off_t absoluteInputPosition); + +private: + void initInflateState(); + int readNextChunk(); + + // where to find the uncompressed data + int mFd; + off_t mInFileStart; // where the compressed data lives in the file + class FileMap* mDataMap; + + z_stream mInflateState; + bool mStreamNeedsInit; + + // output invariants for this asset + uint8_t* mOutBuf; // output buf for decompressed bytes + size_t mOutBufSize; // allocated size of mOutBuf + size_t mOutTotalSize; // total uncompressed size of the blob + + // current output state bookkeeping + off_t mOutCurPosition; // current position in total offset + size_t mOutLastDecoded; // last decoded byte + 1 in mOutbuf + size_t mOutDeliverable; // next undelivered byte of decoded output in mOutBuf + + // input invariants + uint8_t* mInBuf; + size_t mInBufSize; // allocated size of mInBuf; + size_t mInTotalSize; // total size of compressed data for this blob + + // input state bookkeeping + size_t mInNextChunkOffset; // offset from start of blob at which the next input chunk lies + // the z_stream contains state about input block consumption +}; + +} + +#endif diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk index 8bd582353..2e20268b3 100644 --- a/libs/utils/Android.mk +++ b/libs/utils/Android.mk @@ -33,6 +33,7 @@ commonSources:= \ SharedBuffer.cpp \ Static.cpp \ StopWatch.cpp \ + StreamingZipInflater.cpp \ String8.cpp \ String16.cpp \ StringArray.cpp \ @@ -131,4 +132,4 @@ endif # team really wants is to build the stuff defined by this makefile. ifeq (,$(ONE_SHOT_MAKEFILE)) include $(call first-makefiles-under,$(LOCAL_PATH)) -endif \ No newline at end of file +endif diff --git a/libs/utils/Asset.cpp b/libs/utils/Asset.cpp index 42951237d..cef7db492 100644 --- a/libs/utils/Asset.cpp +++ b/libs/utils/Asset.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -659,7 +660,7 @@ const void* _FileAsset::ensureAlignment(FileMap* map) */ _CompressedAsset::_CompressedAsset(void) : mStart(0), mCompressedLen(0), mUncompressedLen(0), mOffset(0), - mMap(NULL), mFd(-1), mBuf(NULL) + mMap(NULL), mFd(-1), mZipInflater(NULL), mBuf(NULL) { } @@ -698,6 +699,10 @@ status_t _CompressedAsset::openChunk(int fd, off_t offset, mFd = fd; assert(mBuf == NULL); + if (uncompressedLen > StreamingZipInflater::OUTPUT_CHUNK_SIZE) { + mZipInflater = new StreamingZipInflater(mFd, offset, uncompressedLen, compressedLen); + } + return NO_ERROR; } @@ -724,6 +729,9 @@ status_t _CompressedAsset::openChunk(FileMap* dataMap, int compressionMethod, mUncompressedLen = uncompressedLen; assert(mOffset == 0); + if (uncompressedLen > StreamingZipInflater::OUTPUT_CHUNK_SIZE) { + mZipInflater = new StreamingZipInflater(dataMap, uncompressedLen); + } return NO_ERROR; } @@ -739,26 +747,29 @@ ssize_t _CompressedAsset::read(void* buf, size_t count) assert(mOffset >= 0 && mOffset <= mUncompressedLen); - // TODO: if mAccessMode == ACCESS_STREAMING, use zlib more cleverly + /* If we're relying on a streaming inflater, go through that */ + if (mZipInflater) { + actual = mZipInflater->read(buf, count); + } else { + if (mBuf == NULL) { + if (getBuffer(false) == NULL) + return -1; + } + assert(mBuf != NULL); - if (mBuf == NULL) { - if (getBuffer(false) == NULL) - return -1; + /* adjust count if we're near EOF */ + maxLen = mUncompressedLen - mOffset; + if (count > maxLen) + count = maxLen; + + if (!count) + return 0; + + /* copy from buffer */ + //printf("comp buf read\n"); + memcpy(buf, (char*)mBuf + mOffset, count); + actual = count; } - assert(mBuf != NULL); - - /* adjust count if we're near EOF */ - maxLen = mUncompressedLen - mOffset; - if (count > maxLen) - count = maxLen; - - if (!count) - return 0; - - /* copy from buffer */ - //printf("comp buf read\n"); - memcpy(buf, (char*)mBuf + mOffset, count); - actual = count; mOffset += actual; return actual; @@ -780,6 +791,9 @@ off_t _CompressedAsset::seek(off_t offset, int whence) if (newPosn == (off_t) -1) return newPosn; + if (mZipInflater) { + mZipInflater->seekAbsolute(newPosn); + } mOffset = newPosn; return mOffset; } @@ -793,10 +807,12 @@ void _CompressedAsset::close(void) mMap->release(); mMap = NULL; } - if (mBuf != NULL) { - delete[] mBuf; - mBuf = NULL; - } + + delete[] mBuf; + mBuf = NULL; + + delete mZipInflater; + mZipInflater = NULL; if (mFd > 0) { ::close(mFd); @@ -817,12 +833,6 @@ const void* _CompressedAsset::getBuffer(bool wordAligned) if (mBuf != NULL) return mBuf; - if (mUncompressedLen > UNCOMPRESS_DATA_MAX) { - LOGD("Data exceeds UNCOMPRESS_DATA_MAX (%ld vs %d)\n", - (long) mUncompressedLen, UNCOMPRESS_DATA_MAX); - goto bail; - } - /* * Allocate a buffer and read the file into it. */ @@ -853,7 +863,13 @@ const void* _CompressedAsset::getBuffer(bool wordAligned) goto bail; } - /* success! */ + /* + * Success - now that we have the full asset in RAM we + * no longer need the streaming inflater + */ + delete mZipInflater; + mZipInflater = NULL; + mBuf = buf; buf = NULL; diff --git a/libs/utils/StreamingZipInflater.cpp b/libs/utils/StreamingZipInflater.cpp new file mode 100644 index 000000000..2ebec937b --- /dev/null +++ b/libs/utils/StreamingZipInflater.cpp @@ -0,0 +1,224 @@ +/* + * Copyright (C) 2010 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 "szipinf" +#include + +#include +#include +#include +#include + +static inline size_t min(size_t a, size_t b) { return (a < b) ? a : b; } + +using namespace android; + +/* + * Streaming access to compressed asset data in an open fd + */ +StreamingZipInflater::StreamingZipInflater(int fd, off_t compDataStart, + size_t uncompSize, size_t compSize) { + mFd = fd; + mDataMap = NULL; + mInFileStart = compDataStart; + mOutTotalSize = uncompSize; + mInTotalSize = compSize; + + mInBufSize = StreamingZipInflater::INPUT_CHUNK_SIZE; + mInBuf = new uint8_t[mInBufSize]; + + mOutBufSize = StreamingZipInflater::OUTPUT_CHUNK_SIZE; + mOutBuf = new uint8_t[mOutBufSize]; + + initInflateState(); +} + +/* + * Streaming access to compressed data held in an mmapped region of memory + */ +StreamingZipInflater::StreamingZipInflater(FileMap* dataMap, size_t uncompSize) { + mFd = -1; + mDataMap = dataMap; + mOutTotalSize = uncompSize; + mInTotalSize = dataMap->getDataLength(); + + mInBuf = (uint8_t*) dataMap->getDataPtr(); + mInBufSize = mInTotalSize; + + mOutBufSize = StreamingZipInflater::OUTPUT_CHUNK_SIZE; + mOutBuf = new uint8_t[mOutBufSize]; + + initInflateState(); +} + +StreamingZipInflater::~StreamingZipInflater() { + // tear down the in-flight zip state just in case + ::inflateEnd(&mInflateState); + + if (mDataMap == NULL) { + delete [] mInBuf; + } + delete [] mOutBuf; +} + +void StreamingZipInflater::initInflateState() { + LOGD("Initializing inflate state"); + + memset(&mInflateState, 0, sizeof(mInflateState)); + mInflateState.zalloc = Z_NULL; + mInflateState.zfree = Z_NULL; + mInflateState.opaque = Z_NULL; + mInflateState.next_in = (Bytef*)mInBuf; + mInflateState.next_out = (Bytef*) mOutBuf; + mInflateState.avail_out = mOutBufSize; + mInflateState.data_type = Z_UNKNOWN; + + mOutLastDecoded = mOutDeliverable = mOutCurPosition = 0; + mInNextChunkOffset = 0; + mStreamNeedsInit = true; + + if (mDataMap == NULL) { + ::lseek(mFd, mInFileStart, SEEK_SET); + mInflateState.avail_in = 0; // set when a chunk is read in + } else { + mInflateState.avail_in = mInBufSize; + } +} + +/* + * Basic approach: + * + * 1. If we have undelivered uncompressed data, send it. At this point + * either we've satisfied the request, or we've exhausted the available + * output data in mOutBuf. + * + * 2. While we haven't sent enough data to satisfy the request: + * 0. if the request is for more data than exists, bail. + * a. if there is no input data to decode, read some into the input buffer + * and readjust the z_stream input pointers + * b. point the output to the start of the output buffer and decode what we can + * c. deliver whatever output data we can + */ +ssize_t StreamingZipInflater::read(void* outBuf, size_t count) { + uint8_t* dest = (uint8_t*) outBuf; + size_t bytesRead = 0; + size_t toRead = min(count, size_t(mOutTotalSize - mOutCurPosition)); + while (toRead > 0) { + // First, write from whatever we already have decoded and ready to go + size_t deliverable = min(toRead, mOutLastDecoded - mOutDeliverable); + if (deliverable > 0) { + if (outBuf != NULL) memcpy(dest, mOutBuf + mOutDeliverable, deliverable); + mOutDeliverable += deliverable; + mOutCurPosition += deliverable; + dest += deliverable; + bytesRead += deliverable; + toRead -= deliverable; + } + + // need more data? time to decode some. + if (toRead > 0) { + // if we don't have any data to decode, read some in. If we're working + // from mmapped data this won't happen, because the clipping to total size + // will prevent reading off the end of the mapped input chunk. + if (mInflateState.avail_in == 0) { + int err = readNextChunk(); + if (err < 0) { + LOGE("Unable to access asset data: %d", err); + if (!mStreamNeedsInit) { + ::inflateEnd(&mInflateState); + initInflateState(); + } + return -1; + } + } + // we know we've drained whatever is in the out buffer now, so just + // start from scratch there, reading all the input we have at present. + mInflateState.next_out = (Bytef*) mOutBuf; + mInflateState.avail_out = mOutBufSize; + + /* + LOGD("Inflating to outbuf: avail_in=%u avail_out=%u next_in=%p next_out=%p", + mInflateState.avail_in, mInflateState.avail_out, + mInflateState.next_in, mInflateState.next_out); + */ + int result = Z_OK; + if (mStreamNeedsInit) { + LOGI("Initializing zlib to inflate"); + result = inflateInit2(&mInflateState, -MAX_WBITS); + mStreamNeedsInit = false; + } + if (result == Z_OK) result = ::inflate(&mInflateState, Z_SYNC_FLUSH); + if (result < 0) { + // Whoops, inflation failed + LOGE("Error inflating asset: %d", result); + ::inflateEnd(&mInflateState); + initInflateState(); + return -1; + } else { + if (result == Z_STREAM_END) { + // we know we have to have reached the target size here and will + // not try to read any further, so just wind things up. + ::inflateEnd(&mInflateState); + } + + // Note how much data we got, and off we go + mOutDeliverable = 0; + mOutLastDecoded = mOutBufSize - mInflateState.avail_out; + } + } + } + return bytesRead; +} + +int StreamingZipInflater::readNextChunk() { + assert(mDataMap == NULL); + + if (mInNextChunkOffset < mInTotalSize) { + size_t toRead = min(mInBufSize, mInTotalSize - mInNextChunkOffset); + if (toRead > 0) { + ssize_t didRead = ::read(mFd, mInBuf, toRead); + //LOGD("Reading input chunk, size %08x didread %08x", toRead, didRead); + if (didRead < 0) { + // TODO: error + LOGE("Error reading asset data"); + return didRead; + } else { + mInNextChunkOffset += didRead; + mInflateState.next_in = (Bytef*) mInBuf; + mInflateState.avail_in = didRead; + } + } + } + return 0; +} + +// seeking backwards requires uncompressing fom the beginning, so is very +// expensive. seeking forwards only requires uncompressing from the current +// position to the destination. +off_t StreamingZipInflater::seekAbsolute(off_t absoluteInputPosition) { + if (absoluteInputPosition < mOutCurPosition) { + // rewind and reprocess the data from the beginning + if (!mStreamNeedsInit) { + ::inflateEnd(&mInflateState); + } + initInflateState(); + read(NULL, absoluteInputPosition); + } else if (absoluteInputPosition > mOutCurPosition) { + read(NULL, absoluteInputPosition - mOutCurPosition); + } + // else if the target position *is* our current position, do nothing + return absoluteInputPosition; +} From b9590bde9a93b7f86c7386747045e025f3bd4fbc Mon Sep 17 00:00:00 2001 From: Kenny Root Date: Wed, 28 Jul 2010 16:31:24 -0700 Subject: [PATCH 232/541] Add unistd.h for size_t typedef to fix sim-eng Change-Id: Ida4f1742547f39539b601f13182c722aa7cf863e --- libs/utils/StreamingZipInflater.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libs/utils/StreamingZipInflater.cpp b/libs/utils/StreamingZipInflater.cpp index 2ebec937b..c13760a30 100644 --- a/libs/utils/StreamingZipInflater.cpp +++ b/libs/utils/StreamingZipInflater.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include static inline size_t min(size_t a, size_t b) { return (a < b) ? a : b; } From f2729494e83a7d44445b397e60d9f395890c34c2 Mon Sep 17 00:00:00 2001 From: Kenny Root Date: Wed, 28 Jul 2010 16:46:12 -0700 Subject: [PATCH 233/541] Change unistd.h to stddef.h to be correct size_t should be defined through inclusion of stddef.h instead of unistd.h Change-Id: Ieaadacfca1e1c44c2533ea95f73fc060c1519b52 --- libs/utils/StreamingZipInflater.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/utils/StreamingZipInflater.cpp b/libs/utils/StreamingZipInflater.cpp index c13760a30..4e0591441 100644 --- a/libs/utils/StreamingZipInflater.cpp +++ b/libs/utils/StreamingZipInflater.cpp @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include static inline size_t min(size_t a, size_t b) { return (a < b) ? a : b; } From 0374b429afdca0feba7e95548c1c2da6c4cfac43 Mon Sep 17 00:00:00 2001 From: Christopher Tate Date: Thu, 29 Jul 2010 13:16:38 -0700 Subject: [PATCH 234/541] Attempt to fix the SDK build On the assumption that the local min() function declaration is in conflict with some 'min' #define floating around, rename the local function to min_of(). Change-Id: I62aa27f213c6093cc78805de611cf4aa75f0eef2 --- libs/utils/StreamingZipInflater.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libs/utils/StreamingZipInflater.cpp b/libs/utils/StreamingZipInflater.cpp index 4e0591441..7ebde78cd 100644 --- a/libs/utils/StreamingZipInflater.cpp +++ b/libs/utils/StreamingZipInflater.cpp @@ -23,7 +23,7 @@ #include #include -static inline size_t min(size_t a, size_t b) { return (a < b) ? a : b; } +static inline size_t min_of(size_t a, size_t b) { return (a < b) ? a : b; } using namespace android; @@ -116,10 +116,10 @@ void StreamingZipInflater::initInflateState() { ssize_t StreamingZipInflater::read(void* outBuf, size_t count) { uint8_t* dest = (uint8_t*) outBuf; size_t bytesRead = 0; - size_t toRead = min(count, size_t(mOutTotalSize - mOutCurPosition)); + size_t toRead = min_of(count, size_t(mOutTotalSize - mOutCurPosition)); while (toRead > 0) { // First, write from whatever we already have decoded and ready to go - size_t deliverable = min(toRead, mOutLastDecoded - mOutDeliverable); + size_t deliverable = min_of(toRead, mOutLastDecoded - mOutDeliverable); if (deliverable > 0) { if (outBuf != NULL) memcpy(dest, mOutBuf + mOutDeliverable, deliverable); mOutDeliverable += deliverable; @@ -188,7 +188,7 @@ int StreamingZipInflater::readNextChunk() { assert(mDataMap == NULL); if (mInNextChunkOffset < mInTotalSize) { - size_t toRead = min(mInBufSize, mInTotalSize - mInNextChunkOffset); + size_t toRead = min_of(mInBufSize, mInTotalSize - mInNextChunkOffset); if (toRead > 0) { ssize_t didRead = ::read(mFd, mInBuf, toRead); //LOGD("Reading input chunk, size %08x didread %08x", toRead, didRead); From 3da482eec2a6d893f80eb56c1670b0bc59b13c30 Mon Sep 17 00:00:00 2001 From: Kenny Root Date: Wed, 4 Aug 2010 16:30:40 -0700 Subject: [PATCH 235/541] Free scanBuf in ZipFileRO In the success case, the 65kB scanBuf was not freed! Also, get rid of annoying complaints about ssize_t from printf in error cases. Change-Id: If154ac19bf47637f898b4ec8c8e27c9a073a7b81 --- libs/utils/ZipFileRO.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/libs/utils/ZipFileRO.cpp b/libs/utils/ZipFileRO.cpp index 28dc512bb..a4c3500dc 100644 --- a/libs/utils/ZipFileRO.cpp +++ b/libs/utils/ZipFileRO.cpp @@ -253,21 +253,21 @@ bool ZipFileRO::mapCentralDirectory(void) /* * Grab the CD offset and size, and the number of entries in the - * archive. Verify that they look reasonable. + * archive. After that, we can release our EOCD hunt buffer. */ unsigned int numEntries = get2LE(eocdPtr + kEOCDNumEntries); unsigned int dirSize = get4LE(eocdPtr + kEOCDSize); unsigned int dirOffset = get4LE(eocdPtr + kEOCDFileOffset); + free(scanBuf); + // Verify that they look reasonable. if ((long long) dirOffset + (long long) dirSize > (long long) eocdOffset) { LOGW("bad offsets (dir %ld, size %u, eocd %ld)\n", (long) dirOffset, dirSize, (long) eocdOffset); - free(scanBuf); return false; } if (numEntries == 0) { LOGW("empty archive?\n"); - free(scanBuf); return false; } @@ -277,14 +277,12 @@ bool ZipFileRO::mapCentralDirectory(void) mDirectoryMap = new FileMap(); if (mDirectoryMap == NULL) { LOGW("Unable to create directory map: %s", strerror(errno)); - free(scanBuf); return false; } if (!mDirectoryMap->create(mFileName, mFd, dirOffset, dirSize, true)) { LOGW("Unable to map '%s' (%zd to %zd): %s\n", mFileName, dirOffset, dirOffset + dirSize, strerror(errno)); - free(scanBuf); return false; } @@ -683,7 +681,7 @@ bool ZipFileRO::uncompressEntry(ZipEntryRO entry, int fd) const goto bail; } else if ((size_t) actual != uncompLen) { LOGE("Partial write during uncompress (%zd of %zd)\n", - actual, uncompLen); + (size_t)actual, (size_t)uncompLen); goto bail; } else { LOGI("+++ successful write\n"); From 23b4a0936f1ee11e587b7be9dc3bcae5b55d31cf Mon Sep 17 00:00:00 2001 From: Kenny Root Date: Thu, 5 Aug 2010 16:21:23 -0700 Subject: [PATCH 236/541] Fix String8::operator+ The LHS was ignored when using: String8 + String8 String8 + (const char*) Add unit tests for above. Bug: 2898473 Change-Id: Ic8fe7be668b665c36aaaa3fc3c3ffdfff0fbba25 --- include/utils/String8.h | 4 +- libs/utils/tests/Android.mk | 5 ++- libs/utils/tests/String8_test.cpp | 75 +++++++++++++++++++++++++++++++ 3 files changed, 80 insertions(+), 4 deletions(-) create mode 100644 libs/utils/tests/String8_test.cpp diff --git a/include/utils/String8.h b/include/utils/String8.h index 0b18fe3d2..4e41410f4 100644 --- a/include/utils/String8.h +++ b/include/utils/String8.h @@ -374,7 +374,7 @@ inline String8& String8::operator+=(const String8& other) inline String8 String8::operator+(const String8& other) const { - String8 tmp; + String8 tmp(*this); tmp += other; return tmp; } @@ -387,7 +387,7 @@ inline String8& String8::operator+=(const char* other) inline String8 String8::operator+(const char* other) const { - String8 tmp; + String8 tmp(*this); tmp += other; return tmp; } diff --git a/libs/utils/tests/Android.mk b/libs/utils/tests/Android.mk index b9f206a9d..725de9c5b 100644 --- a/libs/utils/tests/Android.mk +++ b/libs/utils/tests/Android.mk @@ -7,7 +7,8 @@ ifneq ($(TARGET_SIMULATOR),true) # Build the unit tests. test_src_files := \ ObbFile_test.cpp \ - PollLoop_test.cpp + PollLoop_test.cpp \ + String8_test.cpp shared_libraries := \ libz \ @@ -41,4 +42,4 @@ $(foreach file,$(test_src_files), \ $(eval include $(BUILD_EXECUTABLE)) \ ) -endif \ No newline at end of file +endif diff --git a/libs/utils/tests/String8_test.cpp b/libs/utils/tests/String8_test.cpp new file mode 100644 index 000000000..c42c68dce --- /dev/null +++ b/libs/utils/tests/String8_test.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2010 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 "String8_test" +#include +#include + +#include + +namespace android { + +class String8Test : public testing::Test { +protected: + virtual void SetUp() { + } + + virtual void TearDown() { + } +}; + +TEST_F(String8Test, Cstr) { + String8 tmp("Hello, world!"); + + EXPECT_STREQ(tmp.string(), "Hello, world!"); +} + +TEST_F(String8Test, OperatorPlus) { + String8 src1("Hello, "); + + // Test adding String8 + const char* + const char* ccsrc2 = "world!"; + String8 dst1 = src1 + ccsrc2; + EXPECT_STREQ(dst1.string(), "Hello, world!"); + EXPECT_STREQ(src1.string(), "Hello, "); + EXPECT_STREQ(ccsrc2, "world!"); + + // Test adding String8 + String8 + String8 ssrc2("world!"); + String8 dst2 = src1 + ssrc2; + EXPECT_STREQ(dst2.string(), "Hello, world!"); + EXPECT_STREQ(src1.string(), "Hello, "); + EXPECT_STREQ(ssrc2.string(), "world!"); +} + +TEST_F(String8Test, OperatorPlusEquals) { + String8 src1("My voice"); + + // Testing String8 += String8 + String8 src2(" is my passport."); + src1 += src2; + EXPECT_STREQ(src1.string(), "My voice is my passport."); + EXPECT_STREQ(src2.string(), " is my passport."); + + // Adding const char* to the previous string. + const char* src3 = " Verify me."; + src1 += src3; + EXPECT_STREQ(src1.string(), "My voice is my passport. Verify me."); + EXPECT_STREQ(src2.string(), " is my passport."); + EXPECT_STREQ(src3, " Verify me."); +} + +} From 0537db078bf7cd57dd8887fda548049d20594ca1 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Wed, 4 Aug 2010 11:12:40 -0700 Subject: [PATCH 237/541] More native work. Implement save/restore of state, and add native APIs for configuration information. Change-Id: I2a3ddc2ba605db58d7c8b2b31b9215fb323f90b5 --- include/utils/AssetManager.h | 2 + include/utils/ResourceTypes.h | 119 ++++++++++++++++++---------------- libs/utils/AssetManager.cpp | 6 ++ 3 files changed, 70 insertions(+), 57 deletions(-) diff --git a/include/utils/AssetManager.h b/include/utils/AssetManager.h index 97694ff0a..9e2bf37e8 100644 --- a/include/utils/AssetManager.h +++ b/include/utils/AssetManager.h @@ -129,6 +129,8 @@ public: */ void setConfiguration(const ResTable_config& config, const char* locale = NULL); + void getConfiguration(ResTable_config* outConfig) const; + typedef Asset::AccessMode AccessMode; // typing shortcut /* diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h index c7d9ff1dd..da86da410 100644 --- a/include/utils/ResourceTypes.h +++ b/include/utils/ResourceTypes.h @@ -31,6 +31,8 @@ #include #include +#include + namespace android { /** ******************************************************************** @@ -822,25 +824,25 @@ struct ResTable_config }; enum { - ORIENTATION_ANY = 0x0000, - ORIENTATION_PORT = 0x0001, - ORIENTATION_LAND = 0x0002, - ORIENTATION_SQUARE = 0x0003, + ORIENTATION_ANY = ACONFIGURATION_ORIENTATION_ANY, + ORIENTATION_PORT = ACONFIGURATION_ORIENTATION_PORT, + ORIENTATION_LAND = ACONFIGURATION_ORIENTATION_LAND, + ORIENTATION_SQUARE = ACONFIGURATION_ORIENTATION_SQUARE, }; enum { - TOUCHSCREEN_ANY = 0x0000, - TOUCHSCREEN_NOTOUCH = 0x0001, - TOUCHSCREEN_STYLUS = 0x0002, - TOUCHSCREEN_FINGER = 0x0003, + TOUCHSCREEN_ANY = ACONFIGURATION_TOUCHSCREEN_ANY, + TOUCHSCREEN_NOTOUCH = ACONFIGURATION_TOUCHSCREEN_NOTOUCH, + TOUCHSCREEN_STYLUS = ACONFIGURATION_TOUCHSCREEN_STYLUS, + TOUCHSCREEN_FINGER = ACONFIGURATION_TOUCHSCREEN_FINGER, }; enum { - DENSITY_DEFAULT = 0, - DENSITY_LOW = 120, - DENSITY_MEDIUM = 160, - DENSITY_HIGH = 240, - DENSITY_NONE = 0xffff + DENSITY_DEFAULT = ACONFIGURATION_DENSITY_DEFAULT, + DENSITY_LOW = ACONFIGURATION_DENSITY_LOW, + DENSITY_MEDIUM = ACONFIGURATION_DENSITY_MEDIUM, + DENSITY_HIGH = ACONFIGURATION_DENSITY_HIGH, + DENSITY_NONE = ACONFIGURATION_DENSITY_NONE }; union { @@ -853,33 +855,34 @@ struct ResTable_config }; enum { - KEYBOARD_ANY = 0x0000, - KEYBOARD_NOKEYS = 0x0001, - KEYBOARD_QWERTY = 0x0002, - KEYBOARD_12KEY = 0x0003, + KEYBOARD_ANY = ACONFIGURATION_KEYBOARD_ANY, + KEYBOARD_NOKEYS = ACONFIGURATION_KEYBOARD_NOKEYS, + KEYBOARD_QWERTY = ACONFIGURATION_KEYBOARD_QWERTY, + KEYBOARD_12KEY = ACONFIGURATION_KEYBOARD_12KEY, }; enum { - NAVIGATION_ANY = 0x0000, - NAVIGATION_NONAV = 0x0001, - NAVIGATION_DPAD = 0x0002, - NAVIGATION_TRACKBALL = 0x0003, - NAVIGATION_WHEEL = 0x0004, + NAVIGATION_ANY = ACONFIGURATION_NAVIGATION_ANY, + NAVIGATION_NONAV = ACONFIGURATION_NAVIGATION_NONAV, + NAVIGATION_DPAD = ACONFIGURATION_NAVIGATION_DPAD, + NAVIGATION_TRACKBALL = ACONFIGURATION_NAVIGATION_TRACKBALL, + NAVIGATION_WHEEL = ACONFIGURATION_NAVIGATION_WHEEL, }; enum { MASK_KEYSHIDDEN = 0x0003, - KEYSHIDDEN_ANY = 0x0000, - KEYSHIDDEN_NO = 0x0001, - KEYSHIDDEN_YES = 0x0002, - KEYSHIDDEN_SOFT = 0x0003, + KEYSHIDDEN_ANY = ACONFIGURATION_KEYSHIDDEN_ANY, + KEYSHIDDEN_NO = ACONFIGURATION_KEYSHIDDEN_NO, + KEYSHIDDEN_YES = ACONFIGURATION_KEYSHIDDEN_YES, + KEYSHIDDEN_SOFT = ACONFIGURATION_KEYSHIDDEN_SOFT, }; enum { MASK_NAVHIDDEN = 0x000c, - NAVHIDDEN_ANY = 0x0000, - NAVHIDDEN_NO = 0x0004, - NAVHIDDEN_YES = 0x0008, + SHIFT_NAVHIDDEN = 2, + NAVHIDDEN_ANY = ACONFIGURATION_NAVHIDDEN_ANY << SHIFT_NAVHIDDEN, + NAVHIDDEN_NO = ACONFIGURATION_NAVHIDDEN_NO << SHIFT_NAVHIDDEN, + NAVHIDDEN_YES = ACONFIGURATION_NAVHIDDEN_YES << SHIFT_NAVHIDDEN, }; union { @@ -929,32 +932,34 @@ struct ResTable_config enum { // screenLayout bits for screen size class. MASK_SCREENSIZE = 0x0f, - SCREENSIZE_ANY = 0x00, - SCREENSIZE_SMALL = 0x01, - SCREENSIZE_NORMAL = 0x02, - SCREENSIZE_LARGE = 0x03, - SCREENSIZE_XLARGE = 0x04, + SCREENSIZE_ANY = ACONFIGURATION_SCREENSIZE_ANY, + SCREENSIZE_SMALL = ACONFIGURATION_SCREENSIZE_SMALL, + SCREENSIZE_NORMAL = ACONFIGURATION_SCREENSIZE_NORMAL, + SCREENSIZE_LARGE = ACONFIGURATION_SCREENSIZE_LARGE, + SCREENSIZE_XLARGE = ACONFIGURATION_SCREENSIZE_XLARGE, // screenLayout bits for wide/long screen variation. MASK_SCREENLONG = 0x30, - SCREENLONG_ANY = 0x00, - SCREENLONG_NO = 0x10, - SCREENLONG_YES = 0x20, + SHIFT_SCREENLONG = 4, + SCREENLONG_ANY = ACONFIGURATION_SCREENLONG_ANY << SHIFT_SCREENLONG, + SCREENLONG_NO = ACONFIGURATION_SCREENLONG_NO << SHIFT_SCREENLONG, + SCREENLONG_YES = ACONFIGURATION_SCREENLONG_YES << SHIFT_SCREENLONG, }; enum { // uiMode bits for the mode type. MASK_UI_MODE_TYPE = 0x0f, - UI_MODE_TYPE_ANY = 0x00, - UI_MODE_TYPE_NORMAL = 0x01, - UI_MODE_TYPE_DESK = 0x02, - UI_MODE_TYPE_CAR = 0x03, + UI_MODE_TYPE_ANY = ACONFIGURATION_UI_MODE_TYPE_ANY, + UI_MODE_TYPE_NORMAL = ACONFIGURATION_UI_MODE_TYPE_NORMAL, + UI_MODE_TYPE_DESK = ACONFIGURATION_UI_MODE_TYPE_DESK, + UI_MODE_TYPE_CAR = ACONFIGURATION_UI_MODE_TYPE_CAR, // uiMode bits for the night switch. MASK_UI_MODE_NIGHT = 0x30, - UI_MODE_NIGHT_ANY = 0x00, - UI_MODE_NIGHT_NO = 0x10, - UI_MODE_NIGHT_YES = 0x20, + SHIFT_UI_MODE_NIGHT = 4, + UI_MODE_NIGHT_ANY = ACONFIGURATION_UI_MODE_NIGHT_ANY << SHIFT_UI_MODE_NIGHT, + UI_MODE_NIGHT_NO = ACONFIGURATION_UI_MODE_NIGHT_NO << SHIFT_UI_MODE_NIGHT, + UI_MODE_NIGHT_YES = ACONFIGURATION_UI_MODE_NIGHT_YES << SHIFT_UI_MODE_NIGHT, }; union { @@ -1023,19 +1028,19 @@ struct ResTable_config // match the corresponding ones in android.content.pm.ActivityInfo and // attrs_manifest.xml. enum { - CONFIG_MCC = 0x0001, - CONFIG_MNC = 0x0002, - CONFIG_LOCALE = 0x0004, - CONFIG_TOUCHSCREEN = 0x0008, - CONFIG_KEYBOARD = 0x0010, - CONFIG_KEYBOARD_HIDDEN = 0x0020, - CONFIG_NAVIGATION = 0x0040, - CONFIG_ORIENTATION = 0x0080, - CONFIG_DENSITY = 0x0100, - CONFIG_SCREEN_SIZE = 0x0200, - CONFIG_VERSION = 0x0400, - CONFIG_SCREEN_LAYOUT = 0x0800, - CONFIG_UI_MODE = 0x1000 + CONFIG_MCC = ACONFIGURATION_MCC, + CONFIG_MNC = ACONFIGURATION_MCC, + CONFIG_LOCALE = ACONFIGURATION_LOCALE, + CONFIG_TOUCHSCREEN = ACONFIGURATION_TOUCHSCREEN, + CONFIG_KEYBOARD = ACONFIGURATION_KEYBOARD, + CONFIG_KEYBOARD_HIDDEN = ACONFIGURATION_KEYBOARD_HIDDEN, + CONFIG_NAVIGATION = ACONFIGURATION_NAVIGATION, + CONFIG_ORIENTATION = ACONFIGURATION_ORIENTATION, + CONFIG_DENSITY = ACONFIGURATION_DENSITY, + CONFIG_SCREEN_SIZE = ACONFIGURATION_SCREEN_SIZE, + CONFIG_VERSION = ACONFIGURATION_VERSION, + CONFIG_SCREEN_LAYOUT = ACONFIGURATION_SCREEN_LAYOUT, + CONFIG_UI_MODE = ACONFIGURATION_UI_MODE }; // Compare two configuration, returning CONFIG_* flags set for each value diff --git a/libs/utils/AssetManager.cpp b/libs/utils/AssetManager.cpp index 60a0d82e2..e09e75586 100644 --- a/libs/utils/AssetManager.cpp +++ b/libs/utils/AssetManager.cpp @@ -232,6 +232,12 @@ void AssetManager::setConfiguration(const ResTable_config& config, const char* l } } +void AssetManager::getConfiguration(ResTable_config* outConfig) const +{ + AutoMutex _l(mLock); + *outConfig = *mConfig; +} + /* * Open an asset. * From 4e248ec5c632d4261e50de4f5b34344c22889935 Mon Sep 17 00:00:00 2001 From: Kenny Root Date: Mon, 19 Jul 2010 10:31:34 -0700 Subject: [PATCH 238/541] Initial tool for OBB manipulation Add "obbtool" host command for adding, removing, and querying Opaque Binary Blob (OBB) information from a file. Change-Id: Id2ac41e687ad2a500c362616d6738a8ae7e8f5c3 --- include/utils/ObbFile.h | 4 ++++ libs/utils/ObbFile.cpp | 42 ++++++++++++++++++++++++++++++++++++++--- 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/include/utils/ObbFile.h b/include/utils/ObbFile.h index 075927cdf..d2ca82eb5 100644 --- a/include/utils/ObbFile.h +++ b/include/utils/ObbFile.h @@ -35,6 +35,8 @@ public: bool readFrom(int fd); bool writeTo(const char* filename); bool writeTo(int fd); + bool removeFrom(const char* filename); + bool removeFrom(int fd); const char* getFileName() const { return mFileName; @@ -78,6 +80,8 @@ private: size_t mFileSize; + size_t mFooterStart; + unsigned char* mReadBuf; bool parseObbFile(int fd); diff --git a/libs/utils/ObbFile.cpp b/libs/utils/ObbFile.cpp index fe49300eb..adedf0c5d 100644 --- a/libs/utils/ObbFile.cpp +++ b/libs/utils/ObbFile.cpp @@ -156,9 +156,9 @@ bool ObbFile::parseObbFile(int fd) return false; } - if (footerSize < kFooterMinSize) { - LOGW("claimed footer size is too small (%08zx; minimum size is 0x%x)\n", - footerSize, kFooterMinSize); + if (footerSize < (kFooterMinSize - kFooterTagSize)) { + LOGW("claimed footer size is too small (0x%zx; minimum size is 0x%x)\n", + footerSize, kFooterMinSize - kFooterTagSize); return false; } } @@ -169,6 +169,8 @@ bool ObbFile::parseObbFile(int fd) return false; } + mFooterStart = fileOffset; + char* scanBuf = (char*)malloc(footerSize); if (scanBuf == NULL) { LOGW("couldn't allocate scanBuf: %s\n", strerror(errno)); @@ -293,4 +295,38 @@ bool ObbFile::writeTo(int fd) return true; } +bool ObbFile::removeFrom(const char* filename) +{ + int fd; + bool success = false; + + fd = ::open(filename, O_RDWR); + if (fd < 0) { + goto out; + } + success = removeFrom(fd); + close(fd); + +out: + if (!success) { + LOGW("failed to remove signature from %s: %s\n", filename, strerror(errno)); + } + return success; +} + +bool ObbFile::removeFrom(int fd) +{ + if (fd < 0) { + return false; + } + + if (!readFrom(fd)) { + return false; + } + + ftruncate(fd, mFooterStart); + + return true; +} + } From 38fb25b9d040ac16dc838027da2889301a38fc7d Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Wed, 11 Aug 2010 14:46:32 -0700 Subject: [PATCH 239/541] Add support for the PointerLocation overlay. This change involves adding a new method to IWindowManager, monitorInput() that returns an InputChannel to receive a copy of all input that is dispatched to applications. The caller must have the READ_INPUT_STATE permission to make this request (similar to other window manager methods such as getKeycodeState). Change-Id: Icd14d810174a5b2928671ef16de73af88302aea0 --- include/utils/String8.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/utils/String8.h b/include/utils/String8.h index 4e41410f4..ef0b51a44 100644 --- a/include/utils/String8.h +++ b/include/utils/String8.h @@ -171,7 +171,8 @@ public: status_t append(const char* other); status_t append(const char* other, size_t numChars); - status_t appendFormat(const char* fmt, ...); + status_t appendFormat(const char* fmt, ...) + __attribute__((format (printf, 2, 3))); // Note that this function takes O(N) time to calculate the value. // No cache value is stored. From 6fab9b21caa35ba0dfb12757e5cf1e94109758b1 Mon Sep 17 00:00:00 2001 From: Kenny Root Date: Thu, 12 Aug 2010 07:36:02 -0700 Subject: [PATCH 240/541] Add OBB flags to support overlays * Add flags field in OBB footer to support overlays. * Remove unused 'crypto' and 'filesystem' fields in obbtool (could later be supported in the "flags" field of the OBB footer). * Add notes to document OBB classes before shipping. Change-Id: I386b43c32c5edef55210acb5d3322639c08010ba --- include/utils/ObbFile.h | 35 +++++++++++++++++++++++++++++++---- libs/utils/ObbFile.cpp | 32 ++++++++++++++++++++++---------- 2 files changed, 53 insertions(+), 14 deletions(-) diff --git a/include/utils/ObbFile.h b/include/utils/ObbFile.h index d2ca82eb5..5243f5005 100644 --- a/include/utils/ObbFile.h +++ b/include/utils/ObbFile.h @@ -18,12 +18,16 @@ #define OBBFILE_H_ #include +#include #include #include namespace android { +// OBB flags (bit 0) +#define OBB_OVERLAY (1 << 0) + class ObbFile : public RefBase { protected: virtual ~ObbFile(); @@ -46,18 +50,38 @@ public: return mPackageName; } - int32_t getVersion() const { - return mVersion; - } - void setPackageName(String8 packageName) { mPackageName = packageName; } + int32_t getVersion() const { + return mVersion; + } + void setVersion(int32_t version) { mVersion = version; } + int32_t getFlags() const { + return mFlags; + } + + void setFlags(int32_t flags) { + mFlags = flags; + } + + bool isOverlay() { + return (mFlags & OBB_OVERLAY) == OBB_OVERLAY; + } + + void setOverlay(bool overlay) { + if (overlay) { + mFlags |= OBB_OVERLAY; + } else { + mFlags &= ~OBB_OVERLAY; + } + } + static inline uint32_t get4LE(const unsigned char* buf) { return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); } @@ -76,6 +100,9 @@ private: /* Package version this ObbFile is associated with */ int32_t mVersion; + /* Flags for this OBB type. */ + int32_t mFlags; + const char* mFileName; size_t mFileSize; diff --git a/libs/utils/ObbFile.cpp b/libs/utils/ObbFile.cpp index adedf0c5d..e170ab88c 100644 --- a/libs/utils/ObbFile.cpp +++ b/libs/utils/ObbFile.cpp @@ -29,12 +29,13 @@ #define kFooterTagSize 8 /* last two 32-bit integers */ -#define kFooterMinSize 21 /* 32-bit signature version - * 32-bit package version - * 32-bit package name size - * 1-character package name - * 32-bit footer size - * 32-bit footer marker +#define kFooterMinSize 25 /* 32-bit signature version (4 bytes) + * 32-bit package version (4 bytes) + * 32-bit flags (4 bytes) + * 32-bit package name size (4-bytes) + * >=1-character package name (1 byte) + * 32-bit footer size (4 bytes) + * 32-bit footer marker (4 bytes) */ #define kMaxBufSize 32768 /* Maximum file read buffer */ @@ -45,8 +46,9 @@ /* offsets in version 1 of the header */ #define kPackageVersionOffset 4 -#define kPackageNameLenOffset 8 -#define kPackageNameOffset 12 +#define kFlagsOffset 8 +#define kPackageNameLenOffset 12 +#define kPackageNameOffset 16 /* * TEMP_FAILURE_RETRY is defined by some, but not all, versions of @@ -78,7 +80,10 @@ typedef off64_t my_off64_t; namespace android { ObbFile::ObbFile() : - mVersion(-1) { + mPackageName(""), + mVersion(-1), + mFlags(0) +{ } ObbFile::~ObbFile() { @@ -199,6 +204,7 @@ bool ObbFile::parseObbFile(int fd) } mVersion = (int32_t) get4LE((unsigned char*)scanBuf + kPackageVersionOffset); + mFlags = (int32_t) get4LE((unsigned char*)scanBuf + kFlagsOffset); uint32_t packageNameLen = get4LE((unsigned char*)scanBuf + kPackageNameLenOffset); if (packageNameLen <= 0 @@ -268,6 +274,12 @@ bool ObbFile::writeTo(int fd) return false; } + put4LE(intBuf, mFlags); + if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) { + LOGW("couldn't write package version"); + return false; + } + size_t packageNameLen = mPackageName.size(); put4LE(intBuf, packageNameLen); if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) { @@ -280,7 +292,7 @@ bool ObbFile::writeTo(int fd) return false; } - put4LE(intBuf, 3*sizeof(uint32_t) + packageNameLen); + put4LE(intBuf, kPackageNameOffset + packageNameLen); if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) { LOGW("couldn't write footer size: %s", strerror(errno)); return false; From 95f09be2917fa45b8d3e70e72a88869fa4019566 Mon Sep 17 00:00:00 2001 From: Kenny Root Date: Thu, 19 Aug 2010 18:39:58 -0700 Subject: [PATCH 241/541] Free created FileMap when uncompressing files Change-Id: Ice22c4ecb7c129b74bf60cd66ae79e110b017a4a --- libs/utils/ZipFileRO.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/libs/utils/ZipFileRO.cpp b/libs/utils/ZipFileRO.cpp index a4c3500dc..604f55876 100644 --- a/libs/utils/ZipFileRO.cpp +++ b/libs/utils/ZipFileRO.cpp @@ -636,7 +636,7 @@ bool ZipFileRO::uncompressEntry(ZipEntryRO entry, void* buffer) const memcpy(buffer, ptr, uncompLen); } else { if (!inflateBuffer(buffer, ptr, uncompLen, compLen)) - goto bail; + goto unmap; } if (compLen > kSequentialMin) @@ -644,6 +644,8 @@ bool ZipFileRO::uncompressEntry(ZipEntryRO entry, void* buffer) const result = true; +unmap: + file->release(); bail: return result; } @@ -667,7 +669,7 @@ bool ZipFileRO::uncompressEntry(ZipEntryRO entry, int fd) const getEntryInfo(entry, &method, &uncompLen, &compLen, &offset, NULL, NULL); - const FileMap* file = createEntryFileMap(entry); + FileMap* file = createEntryFileMap(entry); if (file == NULL) { goto bail; } @@ -678,21 +680,23 @@ bool ZipFileRO::uncompressEntry(ZipEntryRO entry, int fd) const ssize_t actual = write(fd, ptr, uncompLen); if (actual < 0) { LOGE("Write failed: %s\n", strerror(errno)); - goto bail; + goto unmap; } else if ((size_t) actual != uncompLen) { LOGE("Partial write during uncompress (%zd of %zd)\n", (size_t)actual, (size_t)uncompLen); - goto bail; + goto unmap; } else { LOGI("+++ successful write\n"); } } else { if (!inflateBuffer(fd, ptr, uncompLen, compLen)) - goto bail; + goto unmap; } result = true; +unmap: + file->release(); bail: return result; } From 2349ee0996dea0168ff3ff9b504b328e7c2be105 Mon Sep 17 00:00:00 2001 From: Kenny Root Date: Fri, 27 Aug 2010 12:47:32 -0700 Subject: [PATCH 242/541] Add debugging to ZipFileRO Some assets are failing to read from ZIP files. This adds a bit more debugging to figure out what's going on. Change-Id: Id90e10546c54c85069a34478fbc28bc2523bda5d --- libs/utils/ZipFileRO.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/utils/ZipFileRO.cpp b/libs/utils/ZipFileRO.cpp index 604f55876..a0e01c693 100644 --- a/libs/utils/ZipFileRO.cpp +++ b/libs/utils/ZipFileRO.cpp @@ -508,8 +508,8 @@ bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, size_t* pUncompLen, } if (get4LE(lfhBuf) != kLFHSignature) { - LOGW("didn't find signature at start of lfh, offset=%ld\n", - localHdrOffset); + LOGW("didn't find signature at start of lfh, offset=%ld (got 0x%08lx, expected 0x%08x)\n", + localHdrOffset, get4LE(lfhBuf), kLFHSignature); return false; } From 6bdd470cf1a74d136c911294f8c380bc13e9f3a2 Mon Sep 17 00:00:00 2001 From: Kenny Root Date: Thu, 2 Sep 2010 14:58:47 -0700 Subject: [PATCH 243/541] Add better debug output for ResourceType errors An error code is returned as -ESOMETHING, but the debug code was printing out 0x%08x for these codes making -74 print out as 0xffffffb5 which is kind of silly. Change-Id: I7d77fb3da2e146845949f121404f662b47288e61 --- libs/utils/ResourceTypes.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp index 4362d14c3..91e7df3ef 100644 --- a/libs/utils/ResourceTypes.cpp +++ b/libs/utils/ResourceTypes.cpp @@ -1946,8 +1946,8 @@ ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag ssize_t offset = getEntry(package, t, e, &mParams, &type, &entry, &typeClass); if (offset <= 0) { if (offset < 0) { - LOGW("Failure getting entry for 0x%08x (t=%d e=%d) in package %d: 0x%08x\n", - resID, t, e, (int)ip, (int)offset); + LOGW("Failure getting entry for 0x%08x (t=%d e=%d) in package %zd (error %d)\n", + resID, t, e, ip, (int)offset); return offset; } continue; From 16d217efbbeb1fde5e3925ad4340856730ad0c98 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Fri, 3 Sep 2010 17:07:07 -0700 Subject: [PATCH 244/541] Add system property to turn off scheduling groups. Do this: adb shell setprop debug.sys.noschedgroups 1 Change-Id: I6e06a74205fd45ee1526ce71fe33944465d39984 --- libs/utils/Threads.cpp | 37 +++++++++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp index 2b1f49044..c8de1f51e 100644 --- a/libs/utils/Threads.cpp +++ b/libs/utils/Threads.cpp @@ -21,6 +21,7 @@ #include #include +#include #include #include @@ -57,7 +58,7 @@ using namespace android; // ---------------------------------------------------------------------------- /* - * Create and run a new thead. + * Create and run a new thread. * * We create it "detached", so it cleans up after itself. */ @@ -280,6 +281,20 @@ pid_t androidGetTid() #endif } +static pthread_once_t gDoSchedulingGroupOnce = PTHREAD_ONCE_INIT; +static bool gDoSchedulingGroup = true; + +static void checkDoSchedulingGroup(void) { + char buf[PROPERTY_VALUE_MAX]; + int len = property_get("debug.sys.noschedgroups", buf, ""); + if (len > 0) { + int temp; + if (sscanf(buf, "%d", &temp) == 1) { + gDoSchedulingGroup = temp == 0; + } + } +} + int androidSetThreadSchedulingGroup(pid_t tid, int grp) { if (grp > ANDROID_TGROUP_MAX || grp < 0) { @@ -287,9 +302,12 @@ int androidSetThreadSchedulingGroup(pid_t tid, int grp) } #if defined(HAVE_PTHREADS) - if (set_sched_policy(tid, (grp == ANDROID_TGROUP_BG_NONINTERACT) ? - SP_BACKGROUND : SP_FOREGROUND)) { - return PERMISSION_DENIED; + pthread_once(&gDoSchedulingGroupOnce, checkDoSchedulingGroup); + if (gDoSchedulingGroup) { + if (set_sched_policy(tid, (grp == ANDROID_TGROUP_BG_NONINTERACT) ? + SP_BACKGROUND : SP_FOREGROUND)) { + return PERMISSION_DENIED; + } } #endif @@ -303,10 +321,13 @@ int androidSetThreadPriority(pid_t tid, int pri) #if defined(HAVE_PTHREADS) int lasterr = 0; - if (pri >= ANDROID_PRIORITY_BACKGROUND) { - rc = set_sched_policy(tid, SP_BACKGROUND); - } else if (getpriority(PRIO_PROCESS, tid) >= ANDROID_PRIORITY_BACKGROUND) { - rc = set_sched_policy(tid, SP_FOREGROUND); + pthread_once(&gDoSchedulingGroupOnce, checkDoSchedulingGroup); + if (gDoSchedulingGroup) { + if (pri >= ANDROID_PRIORITY_BACKGROUND) { + rc = set_sched_policy(tid, SP_BACKGROUND); + } else if (getpriority(PRIO_PROCESS, tid) >= ANDROID_PRIORITY_BACKGROUND) { + rc = set_sched_policy(tid, SP_FOREGROUND); + } } if (rc) { From 193c18ca8a61116b74496e9bd0954ee864337dc3 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Tue, 7 Sep 2010 12:32:19 -0700 Subject: [PATCH 245/541] Hopefully fix the build. Change-Id: Id8cd92c0895c9939e1386ef488bd1309a3be3568 --- libs/utils/Threads.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp index c8de1f51e..e5ece8e8a 100644 --- a/libs/utils/Threads.cpp +++ b/libs/utils/Threads.cpp @@ -281,6 +281,7 @@ pid_t androidGetTid() #endif } +#if defined(HAVE_PTHREADS) static pthread_once_t gDoSchedulingGroupOnce = PTHREAD_ONCE_INIT; static bool gDoSchedulingGroup = true; @@ -294,6 +295,7 @@ static void checkDoSchedulingGroup(void) { } } } +#endif int androidSetThreadSchedulingGroup(pid_t tid, int grp) { From aeaad5f5e96af04d8d547ae6b40d2e8820dc9465 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Tue, 7 Sep 2010 15:28:30 -0700 Subject: [PATCH 246/541] Modify native ALooper to take an explicit ident. The ALooper API now uses an explicit "identifier" for the integer that is returned rather than implicitly using the fd. This allows the APIs that had the fd to be a little more sane. Change-Id: I8507f535ad484c0bdc4a1bd016d87bb09acd7ff0 --- include/utils/PollLoop.h | 14 +++++++++++--- libs/utils/PollLoop.cpp | 19 +++++++++++++------ 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/include/utils/PollLoop.h b/include/utils/PollLoop.h index 81230e84d..bc616ebc7 100644 --- a/include/utils/PollLoop.h +++ b/include/utils/PollLoop.h @@ -111,12 +111,18 @@ public: * This method can be called on any thread. * This method may block briefly if it needs to wake the poll loop. */ - void setCallback(int fd, int events, Callback callback, void* data = NULL); + void setCallback(int fd, int ident, int events, Callback callback, void* data = NULL); + /** + * Convenience for above setCallback when ident is not used. In this case + * the ident is set to POLL_CALLBACK. + */ + void setCallback(int fd, int events, Callback callback, void* data = NULL); + /** * Like setCallback(), but for the NDK callback function. */ - void setLooperCallback(int fd, int events, ALooper_callbackFunc* callback, + void setLooperCallback(int fd, int ident, int events, ALooper_callbackFunc* callback, void* data); /** @@ -153,11 +159,13 @@ private: struct RequestedCallback { Callback callback; ALooper_callbackFunc* looperCallback; + int ident; void* data; }; struct PendingCallback { int fd; + int ident; int events; Callback callback; ALooper_callbackFunc* looperCallback; @@ -185,7 +193,7 @@ private: void openWakePipe(); void closeWakePipe(); - void setCallbackCommon(int fd, int events, Callback callback, + void setCallbackCommon(int fd, int ident, int events, Callback callback, ALooper_callbackFunc* looperCallback, void* data); ssize_t getRequestIndexLocked(int fd); void wakeAndLock(); diff --git a/libs/utils/PollLoop.cpp b/libs/utils/PollLoop.cpp index f740fa0d5..6d3eeee15 100644 --- a/libs/utils/PollLoop.cpp +++ b/libs/utils/PollLoop.cpp @@ -95,6 +95,7 @@ void PollLoop::openWakePipe() { RequestedCallback requestedCallback; requestedCallback.callback = NULL; requestedCallback.looperCallback = NULL; + requestedCallback.ident = 0; requestedCallback.data = NULL; mRequestedCallbacks.insertAt(requestedCallback, 0); } @@ -116,7 +117,7 @@ int32_t PollLoop::pollOnce(int timeoutMillis, int* outEvents, void** outData) { mPendingFdsPos++; if (outEvents != NULL) *outEvents = pending.events; if (outData != NULL) *outData = pending.data; - return pending.fd; + return pending.ident; } mLock.lock(); @@ -182,6 +183,7 @@ int32_t PollLoop::pollOnce(int timeoutMillis, int* outEvents, void** outData) { const RequestedCallback& requestedCallback = mRequestedCallbacks.itemAt(i); PendingCallback pending; pending.fd = requestedFd.fd; + pending.ident = requestedCallback.ident; pending.events = revents; pending.callback = requestedCallback.callback; pending.looperCallback = requestedCallback.looperCallback; @@ -191,7 +193,7 @@ int32_t PollLoop::pollOnce(int timeoutMillis, int* outEvents, void** outData) { mPendingCallbacks.push(pending); } else if (pending.fd != mWakeReadPipeFd) { if (result == POLL_CALLBACK) { - result = pending.fd; + result = pending.ident; if (outEvents != NULL) *outEvents = pending.events; if (outData != NULL) *outData = pending.data; } else { @@ -268,16 +270,20 @@ bool PollLoop::getAllowNonCallbacks() const { return mAllowNonCallbacks; } +void PollLoop::setCallback(int fd, int ident, int events, Callback callback, void* data) { + setCallbackCommon(fd, ident, events, callback, NULL, data); +} + void PollLoop::setCallback(int fd, int events, Callback callback, void* data) { - setCallbackCommon(fd, events, callback, NULL, data); + setCallbackCommon(fd, POLL_CALLBACK, events, callback, NULL, data); } -void PollLoop::setLooperCallback(int fd, int events, ALooper_callbackFunc* callback, +void PollLoop::setLooperCallback(int fd, int ident, int events, ALooper_callbackFunc* callback, void* data) { - setCallbackCommon(fd, events, NULL, callback, data); + setCallbackCommon(fd, ident, events, NULL, callback, data); } -void PollLoop::setCallbackCommon(int fd, int events, Callback callback, +void PollLoop::setCallbackCommon(int fd, int ident, int events, Callback callback, ALooper_callbackFunc* looperCallback, void* data) { #if DEBUG_CALLBACKS @@ -305,6 +311,7 @@ void PollLoop::setCallbackCommon(int fd, int events, Callback callback, RequestedCallback requestedCallback; requestedCallback.callback = callback; requestedCallback.looperCallback = looperCallback; + requestedCallback.ident = ident; requestedCallback.data = data; ssize_t index = getRequestIndexLocked(fd); From b03e56d704af1a66b4552fb606418f3882e56a80 Mon Sep 17 00:00:00 2001 From: Kenny Root Date: Tue, 7 Sep 2010 19:30:22 -0700 Subject: [PATCH 247/541] Revert "Free created FileMap when uncompressing files" This reverts commit 52b8235238fb4d8cf141020cc1ae33880929dc3f. munmap() on a region that overlaps something else (e.g, the ZIP Central Directory in this case) unmaps those other regions as well making the ZipFileRO class unhappy. Revert this for now until we can have FileMap deal with this craziness. --- libs/utils/ZipFileRO.cpp | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/libs/utils/ZipFileRO.cpp b/libs/utils/ZipFileRO.cpp index a0e01c693..2d53136c1 100644 --- a/libs/utils/ZipFileRO.cpp +++ b/libs/utils/ZipFileRO.cpp @@ -636,7 +636,7 @@ bool ZipFileRO::uncompressEntry(ZipEntryRO entry, void* buffer) const memcpy(buffer, ptr, uncompLen); } else { if (!inflateBuffer(buffer, ptr, uncompLen, compLen)) - goto unmap; + goto bail; } if (compLen > kSequentialMin) @@ -644,8 +644,6 @@ bool ZipFileRO::uncompressEntry(ZipEntryRO entry, void* buffer) const result = true; -unmap: - file->release(); bail: return result; } @@ -669,7 +667,7 @@ bool ZipFileRO::uncompressEntry(ZipEntryRO entry, int fd) const getEntryInfo(entry, &method, &uncompLen, &compLen, &offset, NULL, NULL); - FileMap* file = createEntryFileMap(entry); + const FileMap* file = createEntryFileMap(entry); if (file == NULL) { goto bail; } @@ -680,23 +678,21 @@ bool ZipFileRO::uncompressEntry(ZipEntryRO entry, int fd) const ssize_t actual = write(fd, ptr, uncompLen); if (actual < 0) { LOGE("Write failed: %s\n", strerror(errno)); - goto unmap; + goto bail; } else if ((size_t) actual != uncompLen) { LOGE("Partial write during uncompress (%zd of %zd)\n", (size_t)actual, (size_t)uncompLen); - goto unmap; + goto bail; } else { LOGI("+++ successful write\n"); } } else { if (!inflateBuffer(fd, ptr, uncompLen, compLen)) - goto unmap; + goto bail; } result = true; -unmap: - file->release(); bail: return result; } From 4e30623bdabfe392d5ef902666aad9be291cce73 Mon Sep 17 00:00:00 2001 From: Romain Guy Date: Wed, 8 Sep 2010 18:04:33 -0700 Subject: [PATCH 248/541] Purge Skia objects from GL caches as needed. Change-Id: I754c671cf790ad5ae8bf047ad328034217da4ecc --- include/utils/Singleton.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/utils/Singleton.h b/include/utils/Singleton.h index 3b975b4c4..e1ee8eb06 100644 --- a/include/utils/Singleton.h +++ b/include/utils/Singleton.h @@ -37,6 +37,11 @@ public: } return *instance; } + + static bool hasInstance() { + Mutex::Autolock _l(sLock); + return sInstance != 0; + } protected: ~Singleton() { }; From a78bab0359fb60f46f89978dbb4e135fb11ea68e Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Thu, 9 Sep 2010 15:50:18 -0700 Subject: [PATCH 249/541] Always set the scheduling group when starting a new thread. Change-Id: Ia33acf13fc3752707f3819928c36315e223fa1bd --- libs/utils/Threads.cpp | 39 +++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp index e5ece8e8a..f6c55e4d8 100644 --- a/libs/utils/Threads.cpp +++ b/libs/utils/Threads.cpp @@ -65,6 +65,20 @@ using namespace android; typedef void* (*android_pthread_entry)(void*); +static pthread_once_t gDoSchedulingGroupOnce = PTHREAD_ONCE_INIT; +static bool gDoSchedulingGroup = true; + +static void checkDoSchedulingGroup(void) { + char buf[PROPERTY_VALUE_MAX]; + int len = property_get("debug.sys.noschedgroups", buf, ""); + if (len > 0) { + int temp; + if (sscanf(buf, "%d", &temp) == 1) { + gDoSchedulingGroup = temp == 0; + } + } +} + struct thread_data_t { thread_func_t entryFunction; void* userData; @@ -80,6 +94,15 @@ struct thread_data_t { char * name = t->threadName; delete t; setpriority(PRIO_PROCESS, 0, prio); + pthread_once(&gDoSchedulingGroupOnce, checkDoSchedulingGroup); + if (gDoSchedulingGroup) { + if (prio >= ANDROID_PRIORITY_BACKGROUND) { + set_sched_policy(androidGetTid(), SP_BACKGROUND); + } else { + set_sched_policy(androidGetTid(), SP_FOREGROUND); + } + } + if (name) { #if defined(HAVE_PRCTL) // Mac OS doesn't have this, and we build libutil for the host too @@ -281,22 +304,6 @@ pid_t androidGetTid() #endif } -#if defined(HAVE_PTHREADS) -static pthread_once_t gDoSchedulingGroupOnce = PTHREAD_ONCE_INIT; -static bool gDoSchedulingGroup = true; - -static void checkDoSchedulingGroup(void) { - char buf[PROPERTY_VALUE_MAX]; - int len = property_get("debug.sys.noschedgroups", buf, ""); - if (len > 0) { - int temp; - if (sscanf(buf, "%d", &temp) == 1) { - gDoSchedulingGroup = temp == 0; - } - } -} -#endif - int androidSetThreadSchedulingGroup(pid_t tid, int grp) { if (grp > ANDROID_TGROUP_MAX || grp < 0) { From ac84a0a25d27b8dc537f1dff47581d3eff321014 Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Wed, 8 Sep 2010 11:49:43 -0700 Subject: [PATCH 250/541] Input dispatcher ANR handling enhancements. This change is essentially a rewrite of the main input dispatcher loop with the target identification folded in. Since the input dispatcher now has all of the window state, it can make better decisions about when to ANR. Added a .5 second deadline for processing app switch keys. This behavior predates Gingerbread but had not previously been ported. Fixed some timing inaccuracies in the ANR accounting that could cause applications to ANR sooner than they should have. Added a mechanism for tracking key and motion events that have been dispatched to a window so that appropriate cancelation events can be synthesized when recovering from ANR. This change helps to keep applications in sync so they don't end up with stuck buttons upon recovery from ANRs. Added more comments to describe the tricky parts of PollLoop. Change-Id: I13dffca27acb436fc383980db536abc4d8b9e6f1 --- include/utils/PollLoop.h | 36 +++++++++++++++++++++++++----------- libs/utils/PollLoop.cpp | 8 +++++++- 2 files changed, 32 insertions(+), 12 deletions(-) diff --git a/include/utils/PollLoop.h b/include/utils/PollLoop.h index bc616ebc7..c2dfe5da6 100644 --- a/include/utils/PollLoop.h +++ b/include/utils/PollLoop.h @@ -172,22 +172,36 @@ private: void* data; }; - const bool mAllowNonCallbacks; - + const bool mAllowNonCallbacks; // immutable + + int mWakeReadPipeFd; // immutable + int mWakeWritePipeFd; // immutable + + // The lock guards state used to track whether there is a poll() in progress and whether + // there are any other threads waiting in wakeAndLock(). The condition variables + // are used to transfer control among these threads such that all waiters are + // serviced before a new poll can begin. + // The wakeAndLock() method increments mWaiters, wakes the poll, blocks on mAwake + // until mPolling becomes false, then decrements mWaiters again. + // The poll() method blocks on mResume until mWaiters becomes 0, then sets + // mPolling to true, blocks until the poll completes, then resets mPolling to false + // and signals mResume if there are waiters. Mutex mLock; - bool mPolling; - uint32_t mWaiters; - Condition mAwake; - Condition mResume; - - int mWakeReadPipeFd; - int mWakeWritePipeFd; + bool mPolling; // guarded by mLock + uint32_t mWaiters; // guarded by mLock + Condition mAwake; // guarded by mLock + Condition mResume; // guarded by mLock + // The next two vectors are only mutated when mPolling is false since they must + // not be changed while the poll() system call is in progress. To mutate these + // vectors, the poll() must first be awoken then the lock acquired. Vector mRequestedFds; Vector mRequestedCallbacks; - Vector mPendingCallbacks; // used privately by pollOnce - Vector mPendingFds; // used privately by pollOnce + // This state is only used privately by pollOnce and does not require a lock since + // it runs on a single thread. + Vector mPendingCallbacks; + Vector mPendingFds; size_t mPendingFdsPos; void openWakePipe(); diff --git a/libs/utils/PollLoop.cpp b/libs/utils/PollLoop.cpp index 6d3eeee15..fe76cd08c 100644 --- a/libs/utils/PollLoop.cpp +++ b/libs/utils/PollLoop.cpp @@ -119,7 +119,8 @@ int32_t PollLoop::pollOnce(int timeoutMillis, int* outEvents, void** outData) { if (outData != NULL) *outData = pending.data; return pending.ident; } - + + // Wait for wakeAndLock() waiters to run then set mPolling to true. mLock.lock(); while (mWaiters != 0) { mResume.wait(mLock); @@ -127,6 +128,7 @@ int32_t PollLoop::pollOnce(int timeoutMillis, int* outEvents, void** outData) { mPolling = true; mLock.unlock(); + // Poll. int32_t result; size_t requestedCount = mRequestedFds.size(); @@ -168,6 +170,7 @@ int32_t PollLoop::pollOnce(int timeoutMillis, int* outEvents, void** outData) { } #endif + // Process the poll results. mPendingCallbacks.clear(); mPendingFds.clear(); mPendingFdsPos = 0; @@ -218,6 +221,7 @@ int32_t PollLoop::pollOnce(int timeoutMillis, int* outEvents, void** outData) { } Done: + // Set mPolling to false and wake up the wakeAndLock() waiters. mLock.lock(); mPolling = false; if (mWaiters != 0) { @@ -357,11 +361,13 @@ ssize_t PollLoop::getRequestIndexLocked(int fd) { void PollLoop::wakeAndLock() { mLock.lock(); + mWaiters += 1; while (mPolling) { wake(); mAwake.wait(mLock); } + mWaiters -= 1; if (mWaiters == 0) { mResume.signal(); From 7901eb25c60b1df00050d6c3772505d8dcfcdab9 Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Mon, 13 Sep 2010 23:17:30 -0700 Subject: [PATCH 251/541] Replace epoll() with poll() and rename PollLoop to Looper. As part of this change, consolidated and cleaned up the Looper API so that there are fewer distinctions between the NDK and non-NDK declarations (no need for two callback types, etc.). Removed the dependence on specific constants from sys/poll.h such as POLLIN. Instead looper.h defines events like LOOPER_EVENT_INPUT for the events that it supports. That should help make any future under-the-hood implementation changes easier. Fixed a couple of compiler warnings along the way. Change-Id: I449a7ec780bf061bdd325452f823673e2b39b6ae --- include/utils/Looper.h | 210 ++++++++++++++ include/utils/PollLoop.h | 219 --------------- libs/utils/Android.mk | 2 +- libs/utils/Looper.cpp | 368 ++++++++++++++++++++++++ libs/utils/PollLoop.cpp | 377 ------------------------- libs/utils/tests/Android.mk | 2 +- libs/utils/tests/Looper_test.cpp | 433 +++++++++++++++++++++++++++++ libs/utils/tests/PollLoop_test.cpp | 370 ------------------------ 8 files changed, 1013 insertions(+), 968 deletions(-) create mode 100644 include/utils/Looper.h delete mode 100644 include/utils/PollLoop.h create mode 100644 libs/utils/Looper.cpp delete mode 100644 libs/utils/PollLoop.cpp create mode 100644 libs/utils/tests/Looper_test.cpp delete mode 100644 libs/utils/tests/PollLoop_test.cpp diff --git a/include/utils/Looper.h b/include/utils/Looper.h new file mode 100644 index 000000000..92e4b0ae9 --- /dev/null +++ b/include/utils/Looper.h @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2010 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 UTILS_LOOPER_H +#define UTILS_LOOPER_H + +#include +#include +#include + +#include + +/* + * Declare a concrete type for the NDK's looper forward declaration. + */ +struct ALooper { +}; + +namespace android { + +/** + * A polling loop that supports monitoring file descriptor events, optionally + * using callbacks. The implementation uses epoll() internally. + * + * A looper can be associated with a thread although there is no requirement that it must be. + */ +class Looper : public ALooper, public RefBase { +protected: + virtual ~Looper(); + +public: + /** + * Creates a looper. + * + * If allowNonCallbaks is true, the looper will allow file descriptors to be + * registered without associated callbacks. This assumes that the caller of + * pollOnce() is prepared to handle callback-less events itself. + */ + Looper(bool allowNonCallbacks); + + /** + * Returns whether this looper instance allows the registration of file descriptors + * using identifiers instead of callbacks. + */ + bool getAllowNonCallbacks() const; + + /** + * Waits for events to be available, with optional timeout in milliseconds. + * Invokes callbacks for all file descriptors on which an event occurred. + * + * If the timeout is zero, returns immediately without blocking. + * If the timeout is negative, waits indefinitely until an event appears. + * + * Returns ALOOPER_POLL_WAKE if the poll was awoken using wake() before + * the timeout expired and no callbacks were invoked and no other file + * descriptors were ready. + * + * Returns ALOOPER_POLL_CALLBACK if one or more callbacks were invoked. + * + * Returns ALOOPER_POLL_TIMEOUT if there was no data before the given + * timeout expired. + * + * Returns ALOOPER_POLL_ERROR if an error occurred. + * + * Returns a value >= 0 containing an identifier if its file descriptor has data + * and it has no callback function (requiring the caller here to handle it). + * In this (and only this) case outFd, outEvents and outData will contain the poll + * events and data associated with the fd, otherwise they will be set to NULL. + * + * This method does not return until it has finished invoking the appropriate callbacks + * for all file descriptors that were signalled. + */ + int pollOnce(int timeoutMillis, + int* outFd = NULL, int* outEvents = NULL, void** outData = NULL); + + /** + * Like pollOnce(), but performs all pending callbacks until all + * data has been consumed or a file descriptor is available with no callback. + * This function will never return ALOOPER_POLL_CALLBACK. + */ + int pollAll(int timeoutMillis, + int* outFd = NULL, int* outEvents = NULL, void** outData = NULL); + + /** + * Wakes the poll asynchronously. + * + * This method can be called on any thread. + * This method returns immediately. + */ + void wake(); + + /** + * Adds a new file descriptor to be polled by the looper. + * If the same file descriptor was previously added, it is replaced. + * + * "fd" is the file descriptor to be added. + * "ident" is an identifier for this event, which is returned from ALooper_pollOnce(). + * The identifier must be >= 0, or ALOOPER_POLL_CALLBACK if providing a non-NULL callback. + * "events" are the poll events to wake up on. Typically this is ALOOPER_EVENT_INPUT. + * "callback" is the function to call when there is an event on the file descriptor. + * "data" is a private data pointer to supply to the callback. + * + * There are two main uses of this function: + * + * (1) If "callback" is non-NULL, then this function will be called when there is + * data on the file descriptor. It should execute any events it has pending, + * appropriately reading from the file descriptor. The 'ident' is ignored in this case. + * + * (2) If "callback" is NULL, the 'ident' will be returned by ALooper_pollOnce + * when its file descriptor has data available, requiring the caller to take + * care of processing it. + * + * Returns 1 if the file descriptor was added, 0 if the arguments were invalid. + * + * This method can be called on any thread. + * This method may block briefly if it needs to wake the poll. + */ + int addFd(int fd, int ident, + int events, ALooper_callbackFunc callback, void* data = NULL); + + /** + * Removes a previously added file descriptor from the looper. + * + * When this method returns, it is safe to close the file descriptor since the looper + * will no longer have a reference to it. However, it is possible for the callback to + * already be running or for it to run one last time if the file descriptor was already + * signalled. Calling code is responsible for ensuring that this case is safely handled. + * For example, if the callback takes care of removing itself during its own execution either + * by returning 0 or by calling this method, then it can be guaranteed to not be invoked + * again at any later time unless registered anew. + * + * Returns 1 if the file descriptor was removed, 0 if none was previously registered. + * + * This method can be called on any thread. + * This method may block briefly if it needs to wake the poll. + */ + int removeFd(int fd); + + /** + * Prepares a looper associated with the calling thread, and returns it. + * If the thread already has a looper, it is returned. Otherwise, a new + * one is created, associated with the thread, and returned. + * + * The opts may be ALOOPER_PREPARE_ALLOW_NON_CALLBACKS or 0. + */ + static sp prepare(int opts); + + /** + * Sets the given looper to be associated with the calling thread. + * If another looper is already associated with the thread, it is replaced. + * + * If "looper" is NULL, removes the currently associated looper. + */ + static void setForThread(const sp& looper); + + /** + * Returns the looper associated with the calling thread, or NULL if + * there is not one. + */ + static sp getForThread(); + +private: + struct Request { + int fd; + int ident; + ALooper_callbackFunc callback; + void* data; + }; + + struct Response { + int events; + Request request; + }; + + const bool mAllowNonCallbacks; // immutable + + int mEpollFd; // immutable + int mWakeReadPipeFd; // immutable + int mWakeWritePipeFd; // immutable + + // Locked list of file descriptor monitoring requests. + Mutex mLock; + KeyedVector mRequests; + + // This state is only used privately by pollOnce and does not require a lock since + // it runs on a single thread. + Vector mResponses; + size_t mResponseIndex; + + int pollInner(int timeoutMillis); + + static void threadDestructor(void *st); +}; + +} // namespace android + +#endif // UTILS_LOOPER_H diff --git a/include/utils/PollLoop.h b/include/utils/PollLoop.h deleted file mode 100644 index c2dfe5da6..000000000 --- a/include/utils/PollLoop.h +++ /dev/null @@ -1,219 +0,0 @@ -/* - * Copyright (C) 2010 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 UTILS_POLL_LOOP_H -#define UTILS_POLL_LOOP_H - -#include -#include - -#include - -#include - -struct ALooper : public android::RefBase { -protected: - virtual ~ALooper() { } - -public: - ALooper() { } -}; - -namespace android { - -/** - * A basic file descriptor polling loop based on poll() with callbacks. - */ -class PollLoop : public ALooper { -protected: - virtual ~PollLoop(); - -public: - PollLoop(bool allowNonCallbacks); - - /** - * A callback that it to be invoked when an event occurs on a file descriptor. - * Specifies the events that were triggered and the user data provided when the - * callback was set. - * - * Returns true if the callback should be kept, false if it should be removed automatically - * after the callback returns. - */ - typedef bool (*Callback)(int fd, int events, void* data); - - enum { - POLL_CALLBACK = ALOOPER_POLL_CALLBACK, - POLL_TIMEOUT = ALOOPER_POLL_TIMEOUT, - POLL_ERROR = ALOOPER_POLL_ERROR, - }; - - /** - * Performs a single call to poll() with optional timeout in milliseconds. - * Invokes callbacks for all file descriptors on which an event occurred. - * - * If the timeout is zero, returns immediately without blocking. - * If the timeout is negative, waits indefinitely until awoken. - * - * Returns ALOOPER_POLL_CALLBACK if a callback was invoked. - * - * Returns ALOOPER_POLL_TIMEOUT if there was no data before the given - * timeout expired. - * - * Returns ALOPER_POLL_ERROR if an error occurred. - * - * Returns a value >= 0 containing a file descriptor if it has data - * and it has no callback function (requiring the caller here to handle it). - * In this (and only this) case outEvents and outData will contain the poll - * events and data associated with the fd. - * - * This method must only be called on the thread owning the PollLoop. - * This method blocks until either a file descriptor is signalled, a timeout occurs, - * or wake() is called. - * This method does not return until it has finished invoking the appropriate callbacks - * for all file descriptors that were signalled. - */ - int32_t pollOnce(int timeoutMillis, int* outEvents = NULL, void** outData = NULL); - - /** - * Wakes the loop asynchronously. - * - * This method can be called on any thread. - * This method returns immediately. - */ - void wake(); - - /** - * Control whether this PollLoop instance allows using IDs instead - * of callbacks. - */ - bool getAllowNonCallbacks() const; - - /** - * Sets the callback for a file descriptor, replacing the existing one, if any. - * It is an error to call this method with events == 0 or callback == NULL. - * - * Note that a callback can be invoked with the POLLERR, POLLHUP or POLLNVAL events - * even if it is not explicitly requested when registered. - * - * This method can be called on any thread. - * This method may block briefly if it needs to wake the poll loop. - */ - void setCallback(int fd, int ident, int events, Callback callback, void* data = NULL); - - /** - * Convenience for above setCallback when ident is not used. In this case - * the ident is set to POLL_CALLBACK. - */ - void setCallback(int fd, int events, Callback callback, void* data = NULL); - - /** - * Like setCallback(), but for the NDK callback function. - */ - void setLooperCallback(int fd, int ident, int events, ALooper_callbackFunc* callback, - void* data); - - /** - * Removes the callback for a file descriptor, if one exists. - * - * When this method returns, it is safe to close the file descriptor since the poll loop - * will no longer have a reference to it. However, it is possible for the callback to - * already be running or for it to run one last time if the file descriptor was already - * signalled. Calling code is responsible for ensuring that this case is safely handled. - * For example, if the callback takes care of removing itself during its own execution either - * by returning false or calling this method, then it can be guaranteed to not be invoked - * again at any later time unless registered anew. - * - * This method can be called on any thread. - * This method may block briefly if it needs to wake the poll loop. - * - * Returns true if a callback was actually removed, false if none was registered. - */ - bool removeCallback(int fd); - - /** - * Set the given PollLoop to be associated with the - * calling thread. There must be a 1:1 relationship between - * PollLoop and thread. - */ - static void setForThread(const sp& pollLoop); - - /** - * Return the PollLoop associated with the calling thread. - */ - static sp getForThread(); - -private: - struct RequestedCallback { - Callback callback; - ALooper_callbackFunc* looperCallback; - int ident; - void* data; - }; - - struct PendingCallback { - int fd; - int ident; - int events; - Callback callback; - ALooper_callbackFunc* looperCallback; - void* data; - }; - - const bool mAllowNonCallbacks; // immutable - - int mWakeReadPipeFd; // immutable - int mWakeWritePipeFd; // immutable - - // The lock guards state used to track whether there is a poll() in progress and whether - // there are any other threads waiting in wakeAndLock(). The condition variables - // are used to transfer control among these threads such that all waiters are - // serviced before a new poll can begin. - // The wakeAndLock() method increments mWaiters, wakes the poll, blocks on mAwake - // until mPolling becomes false, then decrements mWaiters again. - // The poll() method blocks on mResume until mWaiters becomes 0, then sets - // mPolling to true, blocks until the poll completes, then resets mPolling to false - // and signals mResume if there are waiters. - Mutex mLock; - bool mPolling; // guarded by mLock - uint32_t mWaiters; // guarded by mLock - Condition mAwake; // guarded by mLock - Condition mResume; // guarded by mLock - - // The next two vectors are only mutated when mPolling is false since they must - // not be changed while the poll() system call is in progress. To mutate these - // vectors, the poll() must first be awoken then the lock acquired. - Vector mRequestedFds; - Vector mRequestedCallbacks; - - // This state is only used privately by pollOnce and does not require a lock since - // it runs on a single thread. - Vector mPendingCallbacks; - Vector mPendingFds; - size_t mPendingFdsPos; - - void openWakePipe(); - void closeWakePipe(); - - void setCallbackCommon(int fd, int ident, int events, Callback callback, - ALooper_callbackFunc* looperCallback, void* data); - ssize_t getRequestIndexLocked(int fd); - void wakeAndLock(); - static void threadDestructor(void *st); -}; - -} // namespace android - -#endif // UTILS_POLL_LOOP_H diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk index 2e20268b3..eb75ed894 100644 --- a/libs/utils/Android.mk +++ b/libs/utils/Android.mk @@ -86,7 +86,7 @@ LOCAL_SRC_FILES:= \ $(commonSources) \ BackupData.cpp \ BackupHelpers.cpp \ - PollLoop.cpp + Looper.cpp ifeq ($(TARGET_OS),linux) LOCAL_LDLIBS += -lrt -ldl diff --git a/libs/utils/Looper.cpp b/libs/utils/Looper.cpp new file mode 100644 index 000000000..fd287da51 --- /dev/null +++ b/libs/utils/Looper.cpp @@ -0,0 +1,368 @@ +// +// Copyright 2010 The Android Open Source Project +// +// A looper implementation based on epoll(). +// +#define LOG_TAG "Looper" + +//#define LOG_NDEBUG 0 + +// Debugs poll and wake interactions. +#define DEBUG_POLL_AND_WAKE 0 + +// Debugs callback registration and invocation. +#define DEBUG_CALLBACKS 0 + +#include +#include +#include + +#include +#include +#include + + +namespace android { + +static pthread_mutex_t gTLSMutex = PTHREAD_MUTEX_INITIALIZER; +static bool gHaveTLS = false; +static pthread_key_t gTLS = 0; + +// Hint for number of file descriptors to be associated with the epoll instance. +static const int EPOLL_SIZE_HINT = 8; + +// Maximum number of file descriptors for which to retrieve poll events each iteration. +static const int EPOLL_MAX_EVENTS = 16; + +Looper::Looper(bool allowNonCallbacks) : + mAllowNonCallbacks(allowNonCallbacks), + mResponseIndex(0) { + mEpollFd = epoll_create(EPOLL_SIZE_HINT); + LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance. errno=%d", errno); + + int wakeFds[2]; + int result = pipe(wakeFds); + LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe. errno=%d", errno); + + mWakeReadPipeFd = wakeFds[0]; + mWakeWritePipeFd = wakeFds[1]; + + result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK); + LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake read pipe non-blocking. errno=%d", + errno); + + result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK); + LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake write pipe non-blocking. errno=%d", + errno); + + struct epoll_event eventItem; + eventItem.events = EPOLLIN; + eventItem.data.fd = mWakeReadPipeFd; + result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, & eventItem); + LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake read pipe to epoll instance. errno=%d", + errno); +} + +Looper::~Looper() { + close(mWakeReadPipeFd); + close(mWakeWritePipeFd); + close(mEpollFd); +} + +void Looper::threadDestructor(void *st) { + Looper* const self = static_cast(st); + if (self != NULL) { + self->decStrong((void*)threadDestructor); + } +} + +void Looper::setForThread(const sp& looper) { + sp old = getForThread(); // also has side-effect of initializing TLS + + if (looper != NULL) { + looper->incStrong((void*)threadDestructor); + } + + pthread_setspecific(gTLS, looper.get()); + + if (old != NULL) { + old->decStrong((void*)threadDestructor); + } +} + +sp Looper::getForThread() { + if (!gHaveTLS) { + pthread_mutex_lock(&gTLSMutex); + if (pthread_key_create(&gTLS, threadDestructor) != 0) { + pthread_mutex_unlock(&gTLSMutex); + return NULL; + } + gHaveTLS = true; + pthread_mutex_unlock(&gTLSMutex); + } + + return (Looper*)pthread_getspecific(gTLS); +} + +sp Looper::prepare(int opts) { + bool allowNonCallbacks = opts & ALOOPER_PREPARE_ALLOW_NON_CALLBACKS; + sp looper = Looper::getForThread(); + if (looper == NULL) { + looper = new Looper(allowNonCallbacks); + Looper::setForThread(looper); + } + if (looper->getAllowNonCallbacks() != allowNonCallbacks) { + LOGW("Looper already prepared for this thread with a different value for the " + "ALOOPER_PREPARE_ALLOW_NON_CALLBACKS option."); + } + return looper; +} + +bool Looper::getAllowNonCallbacks() const { + return mAllowNonCallbacks; +} + +int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) { + int result = 0; + for (;;) { + while (mResponseIndex < mResponses.size()) { + const Response& response = mResponses.itemAt(mResponseIndex++); + if (! response.request.callback) { +#if DEBUG_POLL_AND_WAKE + LOGD("%p ~ pollOnce - returning signalled identifier %d: " + "fd=%d, events=0x%x, data=%p", this, + response.request.ident, response.request.fd, + response.events, response.request.data); +#endif + if (outFd != NULL) *outFd = response.request.fd; + if (outEvents != NULL) *outEvents = response.events; + if (outData != NULL) *outData = response.request.data; + return response.request.ident; + } + } + + if (result != 0) { +#if DEBUG_POLL_AND_WAKE + LOGD("%p ~ pollOnce - returning result %d", this, result); +#endif + if (outFd != NULL) *outFd = 0; + if (outEvents != NULL) *outEvents = NULL; + if (outData != NULL) *outData = NULL; + return result; + } + + result = pollInner(timeoutMillis); + } +} + +int Looper::pollInner(int timeoutMillis) { +#if DEBUG_POLL_AND_WAKE + LOGD("%p ~ pollOnce - waiting: timeoutMillis=%d", this, timeoutMillis); +#endif + struct epoll_event eventItems[EPOLL_MAX_EVENTS]; + int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis); + if (eventCount < 0) { + if (errno != EINTR) { + LOGW("Poll failed with an unexpected error, errno=%d", errno); + } + return ALOOPER_POLL_ERROR; + } + + if (eventCount == 0) { +#if DEBUG_POLL_AND_WAKE + LOGD("%p ~ pollOnce - timeout", this); +#endif + return ALOOPER_POLL_TIMEOUT; + } + + int result = ALOOPER_POLL_WAKE; + mResponses.clear(); + mResponseIndex = 0; + +#if DEBUG_POLL_AND_WAKE + LOGD("%p ~ pollOnce - handling events from %d fds", this, eventCount); +#endif + { // acquire lock + AutoMutex _l(mLock); + for (int i = 0; i < eventCount; i++) { + int fd = eventItems[i].data.fd; + uint32_t epollEvents = eventItems[i].events; + if (fd == mWakeReadPipeFd) { + if (epollEvents & EPOLLIN) { +#if DEBUG_POLL_AND_WAKE + LOGD("%p ~ pollOnce - awoken", this); +#endif + char buffer[16]; + ssize_t nRead; + do { + nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer)); + } while (nRead == sizeof(buffer)); + } else { + LOGW("Ignoring unexpected epoll events 0x%x on wake read pipe.", epollEvents); + } + } else { + ssize_t requestIndex = mRequests.indexOfKey(fd); + if (requestIndex >= 0) { + int events = 0; + if (epollEvents & EPOLLIN) events |= ALOOPER_EVENT_INPUT; + if (epollEvents & EPOLLOUT) events |= ALOOPER_EVENT_OUTPUT; + if (epollEvents & EPOLLERR) events |= ALOOPER_EVENT_ERROR; + if (epollEvents & EPOLLHUP) events |= ALOOPER_EVENT_HANGUP; + + Response response; + response.events = events; + response.request = mRequests.valueAt(requestIndex); + mResponses.push(response); + } else { + LOGW("Ignoring unexpected epoll events 0x%x on fd %d that is " + "no longer registered.", epollEvents, fd); + } + } + } + } + + for (size_t i = 0; i < mResponses.size(); i++) { + const Response& response = mResponses.itemAt(i); + if (response.request.callback) { +#if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS + LOGD("%p ~ pollOnce - invoking callback: fd=%d, events=0x%x, data=%p", this, + response.request.fd, response.events, response.request.data); +#endif + int callbackResult = response.request.callback( + response.request.fd, response.events, response.request.data); + if (callbackResult == 0) { + removeFd(response.request.fd); + } + + result = ALOOPER_POLL_CALLBACK; + } + } + return result; +} + +int Looper::pollAll(int timeoutMillis, int* outFd, int* outEvents, void** outData) { + if (timeoutMillis <= 0) { + int result; + do { + result = pollOnce(timeoutMillis, outFd, outEvents, outData); + } while (result == ALOOPER_POLL_CALLBACK); + return result; + } else { + nsecs_t endTime = systemTime(SYSTEM_TIME_MONOTONIC) + + milliseconds_to_nanoseconds(timeoutMillis); + + for (;;) { + int result = pollOnce(timeoutMillis, outFd, outEvents, outData); + if (result != ALOOPER_POLL_CALLBACK) { + return result; + } + + nsecs_t timeoutNanos = endTime - systemTime(SYSTEM_TIME_MONOTONIC); + if (timeoutNanos <= 0) { + return ALOOPER_POLL_TIMEOUT; + } + + timeoutMillis = int(nanoseconds_to_milliseconds(timeoutNanos + 999999LL)); + } + } +} + +void Looper::wake() { +#if DEBUG_POLL_AND_WAKE + LOGD("%p ~ wake", this); +#endif + + ssize_t nWrite = write(mWakeWritePipeFd, "W", 1); + if (nWrite != 1) { + if (errno != EAGAIN) { + LOGW("Could not write wake signal, errno=%d", errno); + } + } +} + +int Looper::addFd(int fd, int ident, int events, ALooper_callbackFunc callback, void* data) { +#if DEBUG_CALLBACKS + LOGD("%p ~ addFd - fd=%d, ident=%d, events=0x%x, callback=%p, data=%p", this, fd, ident, + events, callback, data); +#endif + + int epollEvents = 0; + if (events & ALOOPER_EVENT_INPUT) epollEvents |= EPOLLIN; + if (events & ALOOPER_EVENT_OUTPUT) epollEvents |= EPOLLOUT; + if (events & ALOOPER_EVENT_ERROR) epollEvents |= EPOLLERR; + if (events & ALOOPER_EVENT_HANGUP) epollEvents |= EPOLLHUP; + + if (epollEvents == 0) { + LOGE("Invalid attempt to set a callback with no selected poll events."); + return -1; + } + + if (! callback) { + if (! mAllowNonCallbacks) { + LOGE("Invalid attempt to set NULL callback but not allowed for this looper."); + return -1; + } + + if (ident < 0) { + LOGE("Invalid attempt to set NULL callback with ident <= 0."); + return -1; + } + } + + { // acquire lock + AutoMutex _l(mLock); + + Request request; + request.fd = fd; + request.ident = ident; + request.callback = callback; + request.data = data; + + struct epoll_event eventItem; + eventItem.events = epollEvents; + eventItem.data.fd = fd; + + ssize_t requestIndex = mRequests.indexOfKey(fd); + if (requestIndex < 0) { + int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, & eventItem); + if (epollResult < 0) { + LOGE("Error adding epoll events for fd %d, errno=%d", fd, errno); + return -1; + } + mRequests.add(fd, request); + } else { + int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_MOD, fd, & eventItem); + if (epollResult < 0) { + LOGE("Error modifying epoll events for fd %d, errno=%d", fd, errno); + return -1; + } + mRequests.replaceValueAt(requestIndex, request); + } + } // release lock + return 1; +} + +int Looper::removeFd(int fd) { +#if DEBUG_CALLBACKS + LOGD("%p ~ removeFd - fd=%d", this, fd); +#endif + + { // acquire lock + AutoMutex _l(mLock); + ssize_t requestIndex = mRequests.indexOfKey(fd); + if (requestIndex < 0) { + return 0; + } + + int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_DEL, fd, NULL); + if (epollResult < 0) { + LOGE("Error removing epoll events for fd %d, errno=%d", fd, errno); + return -1; + } + + mRequests.removeItemsAt(requestIndex); + } // request lock + return 1; +} + +} // namespace android diff --git a/libs/utils/PollLoop.cpp b/libs/utils/PollLoop.cpp deleted file mode 100644 index fe76cd08c..000000000 --- a/libs/utils/PollLoop.cpp +++ /dev/null @@ -1,377 +0,0 @@ -// -// Copyright 2010 The Android Open Source Project -// -// A select loop implementation. -// -#define LOG_TAG "PollLoop" - -//#define LOG_NDEBUG 0 - -// Debugs poll and wake interactions. -#define DEBUG_POLL_AND_WAKE 0 - -// Debugs callback registration and invocation. -#define DEBUG_CALLBACKS 0 - -#include -#include - -#include -#include - -namespace android { - -static pthread_mutex_t gTLSMutex = PTHREAD_MUTEX_INITIALIZER; -static bool gHaveTLS = false; -static pthread_key_t gTLS = 0; - -PollLoop::PollLoop(bool allowNonCallbacks) : - mAllowNonCallbacks(allowNonCallbacks), mPolling(false), - mWaiters(0), mPendingFdsPos(0) { - openWakePipe(); -} - -PollLoop::~PollLoop() { - closeWakePipe(); -} - -void PollLoop::threadDestructor(void *st) { - PollLoop* const self = static_cast(st); - if (self != NULL) { - self->decStrong((void*)threadDestructor); - } -} - -void PollLoop::setForThread(const sp& pollLoop) { - sp old = getForThread(); - - if (pollLoop != NULL) { - pollLoop->incStrong((void*)threadDestructor); - } - - pthread_setspecific(gTLS, pollLoop.get()); - - if (old != NULL) { - old->decStrong((void*)threadDestructor); - } -} - -sp PollLoop::getForThread() { - if (!gHaveTLS) { - pthread_mutex_lock(&gTLSMutex); - if (pthread_key_create(&gTLS, threadDestructor) != 0) { - pthread_mutex_unlock(&gTLSMutex); - return NULL; - } - gHaveTLS = true; - pthread_mutex_unlock(&gTLSMutex); - } - - return (PollLoop*)pthread_getspecific(gTLS); -} - -void PollLoop::openWakePipe() { - int wakeFds[2]; - int result = pipe(wakeFds); - LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe. errno=%d", errno); - - mWakeReadPipeFd = wakeFds[0]; - mWakeWritePipeFd = wakeFds[1]; - - result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK); - LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake read pipe non-blocking. errno=%d", - errno); - - result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK); - LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake write pipe non-blocking. errno=%d", - errno); - - // Add the wake pipe to the head of the request list with a null callback. - struct pollfd requestedFd; - requestedFd.fd = mWakeReadPipeFd; - requestedFd.events = POLLIN; - mRequestedFds.insertAt(requestedFd, 0); - - RequestedCallback requestedCallback; - requestedCallback.callback = NULL; - requestedCallback.looperCallback = NULL; - requestedCallback.ident = 0; - requestedCallback.data = NULL; - mRequestedCallbacks.insertAt(requestedCallback, 0); -} - -void PollLoop::closeWakePipe() { - close(mWakeReadPipeFd); - close(mWakeWritePipeFd); - - // Note: We don't need to remove the poll structure or callback entry because this - // method is currently only called by the destructor. -} - -int32_t PollLoop::pollOnce(int timeoutMillis, int* outEvents, void** outData) { - // If there are still pending fds from the last call, dispatch those - // first, to avoid an earlier fd from starving later ones. - const size_t pendingFdsCount = mPendingFds.size(); - if (mPendingFdsPos < pendingFdsCount) { - const PendingCallback& pending = mPendingFds.itemAt(mPendingFdsPos); - mPendingFdsPos++; - if (outEvents != NULL) *outEvents = pending.events; - if (outData != NULL) *outData = pending.data; - return pending.ident; - } - - // Wait for wakeAndLock() waiters to run then set mPolling to true. - mLock.lock(); - while (mWaiters != 0) { - mResume.wait(mLock); - } - mPolling = true; - mLock.unlock(); - - // Poll. - int32_t result; - size_t requestedCount = mRequestedFds.size(); - -#if DEBUG_POLL_AND_WAKE - LOGD("%p ~ pollOnce - waiting on %d fds", this, requestedCount); - for (size_t i = 0; i < requestedCount; i++) { - LOGD(" fd %d - events %d", mRequestedFds[i].fd, mRequestedFds[i].events); - } -#endif - - int respondedCount = poll(mRequestedFds.editArray(), requestedCount, timeoutMillis); - - if (respondedCount == 0) { - // Timeout -#if DEBUG_POLL_AND_WAKE - LOGD("%p ~ pollOnce - timeout", this); -#endif - result = POLL_TIMEOUT; - goto Done; - } - - if (respondedCount < 0) { - // Error -#if DEBUG_POLL_AND_WAKE - LOGD("%p ~ pollOnce - error, errno=%d", this, errno); -#endif - if (errno != EINTR) { - LOGW("Poll failed with an unexpected error, errno=%d", errno); - } - result = POLL_ERROR; - goto Done; - } - -#if DEBUG_POLL_AND_WAKE - LOGD("%p ~ pollOnce - handling responses from %d fds", this, respondedCount); - for (size_t i = 0; i < requestedCount; i++) { - LOGD(" fd %d - events %d, revents %d", mRequestedFds[i].fd, mRequestedFds[i].events, - mRequestedFds[i].revents); - } -#endif - - // Process the poll results. - mPendingCallbacks.clear(); - mPendingFds.clear(); - mPendingFdsPos = 0; - if (outEvents != NULL) *outEvents = 0; - if (outData != NULL) *outData = NULL; - - result = POLL_CALLBACK; - for (size_t i = 0; i < requestedCount; i++) { - const struct pollfd& requestedFd = mRequestedFds.itemAt(i); - - short revents = requestedFd.revents; - if (revents) { - const RequestedCallback& requestedCallback = mRequestedCallbacks.itemAt(i); - PendingCallback pending; - pending.fd = requestedFd.fd; - pending.ident = requestedCallback.ident; - pending.events = revents; - pending.callback = requestedCallback.callback; - pending.looperCallback = requestedCallback.looperCallback; - pending.data = requestedCallback.data; - - if (pending.callback || pending.looperCallback) { - mPendingCallbacks.push(pending); - } else if (pending.fd != mWakeReadPipeFd) { - if (result == POLL_CALLBACK) { - result = pending.ident; - if (outEvents != NULL) *outEvents = pending.events; - if (outData != NULL) *outData = pending.data; - } else { - mPendingFds.push(pending); - } - } else { -#if DEBUG_POLL_AND_WAKE - LOGD("%p ~ pollOnce - awoken", this); -#endif - char buffer[16]; - ssize_t nRead; - do { - nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer)); - } while (nRead == sizeof(buffer)); - } - - respondedCount -= 1; - if (respondedCount == 0) { - break; - } - } - } - -Done: - // Set mPolling to false and wake up the wakeAndLock() waiters. - mLock.lock(); - mPolling = false; - if (mWaiters != 0) { - mAwake.broadcast(); - } - mLock.unlock(); - - if (result == POLL_CALLBACK || result >= 0) { - size_t pendingCount = mPendingCallbacks.size(); - for (size_t i = 0; i < pendingCount; i++) { - const PendingCallback& pendingCallback = mPendingCallbacks.itemAt(i); -#if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS - LOGD("%p ~ pollOnce - invoking callback for fd %d", this, pendingCallback.fd); -#endif - - bool keep = true; - if (pendingCallback.callback != NULL) { - keep = pendingCallback.callback(pendingCallback.fd, pendingCallback.events, - pendingCallback.data); - } else { - keep = pendingCallback.looperCallback(pendingCallback.fd, pendingCallback.events, - pendingCallback.data) != 0; - } - if (! keep) { - removeCallback(pendingCallback.fd); - } - } - } - -#if DEBUG_POLL_AND_WAKE - LOGD("%p ~ pollOnce - done", this); -#endif - return result; -} - -void PollLoop::wake() { -#if DEBUG_POLL_AND_WAKE - LOGD("%p ~ wake", this); -#endif - - ssize_t nWrite = write(mWakeWritePipeFd, "W", 1); - if (nWrite != 1) { - if (errno != EAGAIN) { - LOGW("Could not write wake signal, errno=%d", errno); - } - } -} - -bool PollLoop::getAllowNonCallbacks() const { - return mAllowNonCallbacks; -} - -void PollLoop::setCallback(int fd, int ident, int events, Callback callback, void* data) { - setCallbackCommon(fd, ident, events, callback, NULL, data); -} - -void PollLoop::setCallback(int fd, int events, Callback callback, void* data) { - setCallbackCommon(fd, POLL_CALLBACK, events, callback, NULL, data); -} - -void PollLoop::setLooperCallback(int fd, int ident, int events, ALooper_callbackFunc* callback, - void* data) { - setCallbackCommon(fd, ident, events, NULL, callback, data); -} - -void PollLoop::setCallbackCommon(int fd, int ident, int events, Callback callback, - ALooper_callbackFunc* looperCallback, void* data) { - -#if DEBUG_CALLBACKS - LOGD("%p ~ setCallback - fd=%d, events=%d", this, fd, events); -#endif - - if (! events) { - LOGE("Invalid attempt to set a callback with no selected poll events."); - removeCallback(fd); - return; - } - - if (! callback && ! looperCallback && ! mAllowNonCallbacks) { - LOGE("Invalid attempt to set NULL callback but not allowed."); - removeCallback(fd); - return; - } - - wakeAndLock(); - - struct pollfd requestedFd; - requestedFd.fd = fd; - requestedFd.events = events; - - RequestedCallback requestedCallback; - requestedCallback.callback = callback; - requestedCallback.looperCallback = looperCallback; - requestedCallback.ident = ident; - requestedCallback.data = data; - - ssize_t index = getRequestIndexLocked(fd); - if (index < 0) { - mRequestedFds.push(requestedFd); - mRequestedCallbacks.push(requestedCallback); - } else { - mRequestedFds.replaceAt(requestedFd, size_t(index)); - mRequestedCallbacks.replaceAt(requestedCallback, size_t(index)); - } - - mLock.unlock(); -} - -bool PollLoop::removeCallback(int fd) { -#if DEBUG_CALLBACKS - LOGD("%p ~ removeCallback - fd=%d", this, fd); -#endif - - wakeAndLock(); - - ssize_t index = getRequestIndexLocked(fd); - if (index >= 0) { - mRequestedFds.removeAt(size_t(index)); - mRequestedCallbacks.removeAt(size_t(index)); - } - - mLock.unlock(); - return index >= 0; -} - -ssize_t PollLoop::getRequestIndexLocked(int fd) { - size_t requestCount = mRequestedFds.size(); - - for (size_t i = 0; i < requestCount; i++) { - if (mRequestedFds.itemAt(i).fd == fd) { - return i; - } - } - - return -1; -} - -void PollLoop::wakeAndLock() { - mLock.lock(); - - mWaiters += 1; - while (mPolling) { - wake(); - mAwake.wait(mLock); - } - - mWaiters -= 1; - if (mWaiters == 0) { - mResume.signal(); - } -} - -} // namespace android diff --git a/libs/utils/tests/Android.mk b/libs/utils/tests/Android.mk index 725de9c5b..00077eecf 100644 --- a/libs/utils/tests/Android.mk +++ b/libs/utils/tests/Android.mk @@ -7,7 +7,7 @@ ifneq ($(TARGET_SIMULATOR),true) # Build the unit tests. test_src_files := \ ObbFile_test.cpp \ - PollLoop_test.cpp \ + Looper_test.cpp \ String8_test.cpp shared_libraries := \ diff --git a/libs/utils/tests/Looper_test.cpp b/libs/utils/tests/Looper_test.cpp new file mode 100644 index 000000000..afc92f8cd --- /dev/null +++ b/libs/utils/tests/Looper_test.cpp @@ -0,0 +1,433 @@ +// +// Copyright 2010 The Android Open Source Project +// + +#include +#include +#include +#include +#include +#include + +#include "TestHelpers.h" + +// # of milliseconds to fudge stopwatch measurements +#define TIMING_TOLERANCE_MS 25 + +namespace android { + +class DelayedWake : public DelayedTask { + sp mLooper; + +public: + DelayedWake(int delayMillis, const sp looper) : + DelayedTask(delayMillis), mLooper(looper) { + } + +protected: + virtual void doTask() { + mLooper->wake(); + } +}; + +class DelayedWriteSignal : public DelayedTask { + Pipe* mPipe; + +public: + DelayedWriteSignal(int delayMillis, Pipe* pipe) : + DelayedTask(delayMillis), mPipe(pipe) { + } + +protected: + virtual void doTask() { + mPipe->writeSignal(); + } +}; + +class CallbackHandler { +public: + void setCallback(const sp& looper, int fd, int events) { + looper->addFd(fd, 0, events, staticHandler, this); + } + +protected: + virtual ~CallbackHandler() { } + + virtual int handler(int fd, int events) = 0; + +private: + static int staticHandler(int fd, int events, void* data) { + return static_cast(data)->handler(fd, events); + } +}; + +class StubCallbackHandler : public CallbackHandler { +public: + int nextResult; + int callbackCount; + + int fd; + int events; + + StubCallbackHandler(int nextResult) : nextResult(nextResult), + callbackCount(0), fd(-1), events(-1) { + } + +protected: + virtual int handler(int fd, int events) { + callbackCount += 1; + this->fd = fd; + this->events = events; + return nextResult; + } +}; + +class LooperTest : public testing::Test { +protected: + sp mLooper; + + virtual void SetUp() { + mLooper = new Looper(true); + } + + virtual void TearDown() { + mLooper.clear(); + } +}; + + +TEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndNotAwoken_WaitsForTimeout) { + StopWatch stopWatch("pollOnce"); + int result = mLooper->pollOnce(100); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. equal timeout"; + EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result) + << "pollOnce result should be ALOOPER_POLL_TIMEOUT"; +} + +TEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndAwokenBeforeWaiting_ImmediatelyReturns) { + mLooper->wake(); + + StopWatch stopWatch("pollOnce"); + int result = mLooper->pollOnce(1000); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. zero because wake() was called before waiting"; + EXPECT_EQ(ALOOPER_POLL_WAKE, result) + << "pollOnce result should be ALOOPER_POLL_CALLBACK because loop was awoken"; +} + +TEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndAwokenWhileWaiting_PromptlyReturns) { + sp delayedWake = new DelayedWake(100, mLooper); + delayedWake->run(); + + StopWatch stopWatch("pollOnce"); + int result = mLooper->pollOnce(1000); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. equal wake delay"; + EXPECT_EQ(ALOOPER_POLL_WAKE, result) + << "pollOnce result should be ALOOPER_POLL_CALLBACK because loop was awoken"; +} + +TEST_F(LooperTest, PollOnce_WhenZeroTimeoutAndNoRegisteredFDs_ImmediatelyReturns) { + StopWatch stopWatch("pollOnce"); + int result = mLooper->pollOnce(0); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should be approx. zero"; + EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result) + << "pollOnce result should be ALOOPER_POLL_TIMEOUT"; +} + +TEST_F(LooperTest, PollOnce_WhenZeroTimeoutAndNoSignalledFDs_ImmediatelyReturns) { + Pipe pipe; + StubCallbackHandler handler(true); + + handler.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT); + + StopWatch stopWatch("pollOnce"); + int result = mLooper->pollOnce(0); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should be approx. zero"; + EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result) + << "pollOnce result should be ALOOPER_POLL_TIMEOUT"; + EXPECT_EQ(0, handler.callbackCount) + << "callback should not have been invoked because FD was not signalled"; +} + +TEST_F(LooperTest, PollOnce_WhenZeroTimeoutAndSignalledFD_ImmediatelyInvokesCallbackAndReturns) { + Pipe pipe; + StubCallbackHandler handler(true); + + ASSERT_EQ(OK, pipe.writeSignal()); + handler.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT); + + StopWatch stopWatch("pollOnce"); + int result = mLooper->pollOnce(0); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should be approx. zero"; + EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) + << "pollOnce result should be ALOOPER_POLL_CALLBACK because FD was signalled"; + EXPECT_EQ(1, handler.callbackCount) + << "callback should be invoked exactly once"; + EXPECT_EQ(pipe.receiveFd, handler.fd) + << "callback should have received pipe fd as parameter"; + EXPECT_EQ(ALOOPER_EVENT_INPUT, handler.events) + << "callback should have received ALOOPER_EVENT_INPUT as events"; +} + +TEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndNoSignalledFDs_WaitsForTimeoutAndReturns) { + Pipe pipe; + StubCallbackHandler handler(true); + + handler.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT); + + StopWatch stopWatch("pollOnce"); + int result = mLooper->pollOnce(100); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. equal timeout"; + EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result) + << "pollOnce result should be ALOOPER_POLL_TIMEOUT"; + EXPECT_EQ(0, handler.callbackCount) + << "callback should not have been invoked because FD was not signalled"; +} + +TEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndSignalledFDBeforeWaiting_ImmediatelyInvokesCallbackAndReturns) { + Pipe pipe; + StubCallbackHandler handler(true); + + pipe.writeSignal(); + handler.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT); + + StopWatch stopWatch("pollOnce"); + int result = mLooper->pollOnce(100); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + ASSERT_EQ(OK, pipe.readSignal()) + << "signal should actually have been written"; + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should be approx. zero"; + EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) + << "pollOnce result should be ALOOPER_POLL_CALLBACK because FD was signalled"; + EXPECT_EQ(1, handler.callbackCount) + << "callback should be invoked exactly once"; + EXPECT_EQ(pipe.receiveFd, handler.fd) + << "callback should have received pipe fd as parameter"; + EXPECT_EQ(ALOOPER_EVENT_INPUT, handler.events) + << "callback should have received ALOOPER_EVENT_INPUT as events"; +} + +TEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndSignalledFDWhileWaiting_PromptlyInvokesCallbackAndReturns) { + Pipe pipe; + StubCallbackHandler handler(true); + sp delayedWriteSignal = new DelayedWriteSignal(100, & pipe); + + handler.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT); + delayedWriteSignal->run(); + + StopWatch stopWatch("pollOnce"); + int result = mLooper->pollOnce(1000); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + ASSERT_EQ(OK, pipe.readSignal()) + << "signal should actually have been written"; + EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. equal signal delay"; + EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) + << "pollOnce result should be ALOOPER_POLL_CALLBACK because FD was signalled"; + EXPECT_EQ(1, handler.callbackCount) + << "callback should be invoked exactly once"; + EXPECT_EQ(pipe.receiveFd, handler.fd) + << "callback should have received pipe fd as parameter"; + EXPECT_EQ(ALOOPER_EVENT_INPUT, handler.events) + << "callback should have received ALOOPER_EVENT_INPUT as events"; +} + +TEST_F(LooperTest, PollOnce_WhenCallbackAddedThenRemoved_CallbackShouldNotBeInvoked) { + Pipe pipe; + StubCallbackHandler handler(true); + + handler.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT); + pipe.writeSignal(); // would cause FD to be considered signalled + mLooper->removeFd(pipe.receiveFd); + + StopWatch stopWatch("pollOnce"); + int result = mLooper->pollOnce(100); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + ASSERT_EQ(OK, pipe.readSignal()) + << "signal should actually have been written"; + EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. equal timeout because FD was no longer registered"; + EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result) + << "pollOnce result should be ALOOPER_POLL_TIMEOUT"; + EXPECT_EQ(0, handler.callbackCount) + << "callback should not be invoked"; +} + +TEST_F(LooperTest, PollOnce_WhenCallbackReturnsFalse_CallbackShouldNotBeInvokedAgainLater) { + Pipe pipe; + StubCallbackHandler handler(false); + + handler.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT); + + // First loop: Callback is registered and FD is signalled. + pipe.writeSignal(); + + StopWatch stopWatch("pollOnce"); + int result = mLooper->pollOnce(0); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + ASSERT_EQ(OK, pipe.readSignal()) + << "signal should actually have been written"; + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. equal zero because FD was already signalled"; + EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) + << "pollOnce result should be ALOOPER_POLL_CALLBACK because FD was signalled"; + EXPECT_EQ(1, handler.callbackCount) + << "callback should be invoked"; + + // Second loop: Callback is no longer registered and FD is signalled. + pipe.writeSignal(); + + stopWatch.reset(); + result = mLooper->pollOnce(0); + elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + ASSERT_EQ(OK, pipe.readSignal()) + << "signal should actually have been written"; + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. equal zero because timeout was zero"; + EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result) + << "pollOnce result should be ALOOPER_POLL_TIMEOUT"; + EXPECT_EQ(1, handler.callbackCount) + << "callback should not be invoked this time"; +} + +TEST_F(LooperTest, PollOnce_WhenNonCallbackFdIsSignalled_ReturnsIdent) { + const int expectedIdent = 5; + void* expectedData = this; + + Pipe pipe; + + pipe.writeSignal(); + mLooper->addFd(pipe.receiveFd, expectedIdent, ALOOPER_EVENT_INPUT, NULL, expectedData); + + StopWatch stopWatch("pollOnce"); + int fd; + int events; + void* data; + int result = mLooper->pollOnce(100, &fd, &events, &data); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + ASSERT_EQ(OK, pipe.readSignal()) + << "signal should actually have been written"; + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should be approx. zero"; + EXPECT_EQ(expectedIdent, result) + << "pollOnce result should be the ident of the FD that was signalled"; + EXPECT_EQ(pipe.receiveFd, fd) + << "pollOnce should have returned the received pipe fd"; + EXPECT_EQ(ALOOPER_EVENT_INPUT, events) + << "pollOnce should have returned ALOOPER_EVENT_INPUT as events"; + EXPECT_EQ(expectedData, data) + << "pollOnce should have returned the data"; +} + +TEST_F(LooperTest, AddFd_WhenCallbackAdded_ReturnsOne) { + Pipe pipe; + int result = mLooper->addFd(pipe.receiveFd, 0, ALOOPER_EVENT_INPUT, NULL, NULL); + + EXPECT_EQ(1, result) + << "addFd should return 1 because FD was added"; +} + +TEST_F(LooperTest, AddFd_WhenEventsIsZero_ReturnsError) { + Pipe pipe; + int result = mLooper->addFd(pipe.receiveFd, 0, 0, NULL, NULL); + + EXPECT_EQ(-1, result) + << "addFd should return -1 because arguments were invalid"; +} + +TEST_F(LooperTest, AddFd_WhenIdentIsNegativeAndCallbackIsNull_ReturnsError) { + Pipe pipe; + int result = mLooper->addFd(pipe.receiveFd, -1, ALOOPER_EVENT_INPUT, NULL, NULL); + + EXPECT_EQ(-1, result) + << "addFd should return -1 because arguments were invalid"; +} + +TEST_F(LooperTest, AddFd_WhenNoCallbackAndAllowNonCallbacksIsFalse_ReturnsError) { + Pipe pipe; + sp looper = new Looper(false /*allowNonCallbacks*/); + int result = looper->addFd(pipe.receiveFd, 0, 0, NULL, NULL); + + EXPECT_EQ(-1, result) + << "addFd should return -1 because arguments were invalid"; +} + +TEST_F(LooperTest, RemoveFd_WhenCallbackNotAdded_ReturnsZero) { + int result = mLooper->removeFd(1); + + EXPECT_EQ(0, result) + << "removeFd should return 0 because FD not registered"; +} + +TEST_F(LooperTest, RemoveFd_WhenCallbackAddedThenRemovedTwice_ReturnsOnceFirstTimeAndReturnsZeroSecondTime) { + Pipe pipe; + StubCallbackHandler handler(false); + handler.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT); + + // First time. + int result = mLooper->removeFd(pipe.receiveFd); + + EXPECT_EQ(1, result) + << "removeFd should return 1 first time because FD was registered"; + + // Second time. + result = mLooper->removeFd(pipe.receiveFd); + + EXPECT_EQ(0, result) + << "removeFd should return 0 second time because FD was no longer registered"; +} + +TEST_F(LooperTest, PollOnce_WhenCallbackAddedTwice_OnlySecondCallbackShouldBeInvoked) { + Pipe pipe; + StubCallbackHandler handler1(true); + StubCallbackHandler handler2(true); + + handler1.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT); + handler2.setCallback(mLooper, pipe.receiveFd, ALOOPER_EVENT_INPUT); // replace it + pipe.writeSignal(); // would cause FD to be considered signalled + + StopWatch stopWatch("pollOnce"); + int result = mLooper->pollOnce(100); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + ASSERT_EQ(OK, pipe.readSignal()) + << "signal should actually have been written"; + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. zero because FD was already signalled"; + EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) + << "pollOnce result should be ALOOPER_POLL_CALLBACK because FD was signalled"; + EXPECT_EQ(0, handler1.callbackCount) + << "original handler callback should not be invoked because it was replaced"; + EXPECT_EQ(1, handler2.callbackCount) + << "replacement handler callback should be invoked"; +} + + +} // namespace android diff --git a/libs/utils/tests/PollLoop_test.cpp b/libs/utils/tests/PollLoop_test.cpp deleted file mode 100644 index 02f180893..000000000 --- a/libs/utils/tests/PollLoop_test.cpp +++ /dev/null @@ -1,370 +0,0 @@ -// -// Copyright 2010 The Android Open Source Project -// - -#include -#include -#include -#include -#include -#include - -#include "TestHelpers.h" - -// # of milliseconds to fudge stopwatch measurements -#define TIMING_TOLERANCE_MS 25 - -namespace android { - -class DelayedWake : public DelayedTask { - sp mPollLoop; - -public: - DelayedWake(int delayMillis, const sp pollLoop) : - DelayedTask(delayMillis), mPollLoop(pollLoop) { - } - -protected: - virtual void doTask() { - mPollLoop->wake(); - } -}; - -class DelayedWriteSignal : public DelayedTask { - Pipe* mPipe; - -public: - DelayedWriteSignal(int delayMillis, Pipe* pipe) : - DelayedTask(delayMillis), mPipe(pipe) { - } - -protected: - virtual void doTask() { - mPipe->writeSignal(); - } -}; - -class CallbackHandler { -public: - void setCallback(const sp& pollLoop, int fd, int events) { - pollLoop->setCallback(fd, events, staticHandler, this); - } - -protected: - virtual ~CallbackHandler() { } - - virtual bool handler(int fd, int events) = 0; - -private: - static bool staticHandler(int fd, int events, void* data) { - return static_cast(data)->handler(fd, events); - } -}; - -class StubCallbackHandler : public CallbackHandler { -public: - bool nextResult; - int callbackCount; - - int fd; - int events; - - StubCallbackHandler(bool nextResult) : nextResult(nextResult), - callbackCount(0), fd(-1), events(-1) { - } - -protected: - virtual bool handler(int fd, int events) { - callbackCount += 1; - this->fd = fd; - this->events = events; - return nextResult; - } -}; - -class PollLoopTest : public testing::Test { -protected: - sp mPollLoop; - - virtual void SetUp() { - mPollLoop = new PollLoop(false); - } - - virtual void TearDown() { - mPollLoop.clear(); - } -}; - - -TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndNotAwoken_WaitsForTimeoutAndReturnsFalse) { - StopWatch stopWatch("pollOnce"); - int32_t result = mPollLoop->pollOnce(100); - int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); - - EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) - << "elapsed time should approx. equal timeout"; - EXPECT_EQ(result, PollLoop::POLL_TIMEOUT) - << "pollOnce result should be POLL_TIMEOUT"; -} - -TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndAwokenBeforeWaiting_ImmediatelyReturnsTrue) { - mPollLoop->wake(); - - StopWatch stopWatch("pollOnce"); - int32_t result = mPollLoop->pollOnce(1000); - int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); - - EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) - << "elapsed time should approx. zero because wake() was called before waiting"; - EXPECT_EQ(result, PollLoop::POLL_CALLBACK) - << "pollOnce result should be POLL_CALLBACK because loop was awoken"; -} - -TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndAwokenWhileWaiting_PromptlyReturnsTrue) { - sp delayedWake = new DelayedWake(100, mPollLoop); - delayedWake->run(); - - StopWatch stopWatch("pollOnce"); - int32_t result = mPollLoop->pollOnce(1000); - int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); - - EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) - << "elapsed time should approx. equal wake delay"; - EXPECT_EQ(result, PollLoop::POLL_CALLBACK) - << "pollOnce result should be POLL_CALLBACK because loop was awoken"; -} - -TEST_F(PollLoopTest, PollOnce_WhenZeroTimeoutAndNoRegisteredFDs_ImmediatelyReturnsFalse) { - StopWatch stopWatch("pollOnce"); - int32_t result = mPollLoop->pollOnce(0); - int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); - - EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) - << "elapsed time should be approx. zero"; - EXPECT_EQ(result, PollLoop::POLL_TIMEOUT) - << "pollOnce result should be POLL_TIMEOUT"; -} - -TEST_F(PollLoopTest, PollOnce_WhenZeroTimeoutAndNoSignalledFDs_ImmediatelyReturnsFalse) { - Pipe pipe; - StubCallbackHandler handler(true); - - handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN); - - StopWatch stopWatch("pollOnce"); - int32_t result = mPollLoop->pollOnce(0); - int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); - - EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) - << "elapsed time should be approx. zero"; - EXPECT_EQ(result, PollLoop::POLL_TIMEOUT) - << "pollOnce result should be POLL_TIMEOUT"; - EXPECT_EQ(0, handler.callbackCount) - << "callback should not have been invoked because FD was not signalled"; -} - -TEST_F(PollLoopTest, PollOnce_WhenZeroTimeoutAndSignalledFD_ImmediatelyInvokesCallbackAndReturnsTrue) { - Pipe pipe; - StubCallbackHandler handler(true); - - ASSERT_EQ(OK, pipe.writeSignal()); - handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN); - - StopWatch stopWatch("pollOnce"); - int32_t result = mPollLoop->pollOnce(0); - int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); - - EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) - << "elapsed time should be approx. zero"; - EXPECT_EQ(result, PollLoop::POLL_CALLBACK) - << "pollOnce result should be POLL_CALLBACK because FD was signalled"; - EXPECT_EQ(1, handler.callbackCount) - << "callback should be invoked exactly once"; - EXPECT_EQ(pipe.receiveFd, handler.fd) - << "callback should have received pipe fd as parameter"; - EXPECT_EQ(POLL_IN, handler.events) - << "callback should have received POLL_IN as events"; -} - -TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndNoSignalledFDs_WaitsForTimeoutAndReturnsFalse) { - Pipe pipe; - StubCallbackHandler handler(true); - - handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN); - - StopWatch stopWatch("pollOnce"); - int32_t result = mPollLoop->pollOnce(100); - int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); - - EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) - << "elapsed time should approx. equal timeout"; - EXPECT_EQ(result, PollLoop::POLL_TIMEOUT) - << "pollOnce result should be POLL_TIMEOUT"; - EXPECT_EQ(0, handler.callbackCount) - << "callback should not have been invoked because FD was not signalled"; -} - -TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndSignalledFDBeforeWaiting_ImmediatelyInvokesCallbackAndReturnsTrue) { - Pipe pipe; - StubCallbackHandler handler(true); - - pipe.writeSignal(); - handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN); - - StopWatch stopWatch("pollOnce"); - int32_t result = mPollLoop->pollOnce(100); - int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); - - ASSERT_EQ(OK, pipe.readSignal()) - << "signal should actually have been written"; - EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) - << "elapsed time should be approx. zero"; - EXPECT_EQ(result, PollLoop::POLL_CALLBACK) - << "pollOnce result should be POLL_CALLBACK because FD was signalled"; - EXPECT_EQ(1, handler.callbackCount) - << "callback should be invoked exactly once"; - EXPECT_EQ(pipe.receiveFd, handler.fd) - << "callback should have received pipe fd as parameter"; - EXPECT_EQ(POLL_IN, handler.events) - << "callback should have received POLL_IN as events"; -} - -TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndSignalledFDWhileWaiting_PromptlyInvokesCallbackAndReturnsTrue) { - Pipe pipe; - StubCallbackHandler handler(true); - sp delayedWriteSignal = new DelayedWriteSignal(100, & pipe); - - handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN); - delayedWriteSignal->run(); - - StopWatch stopWatch("pollOnce"); - int32_t result = mPollLoop->pollOnce(1000); - int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); - - ASSERT_EQ(OK, pipe.readSignal()) - << "signal should actually have been written"; - EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) - << "elapsed time should approx. equal signal delay"; - EXPECT_EQ(result, PollLoop::POLL_CALLBACK) - << "pollOnce result should be POLL_CALLBACK because FD was signalled"; - EXPECT_EQ(1, handler.callbackCount) - << "callback should be invoked exactly once"; - EXPECT_EQ(pipe.receiveFd, handler.fd) - << "callback should have received pipe fd as parameter"; - EXPECT_EQ(POLL_IN, handler.events) - << "callback should have received POLL_IN as events"; -} - -TEST_F(PollLoopTest, PollOnce_WhenCallbackAddedThenRemoved_CallbackShouldNotBeInvoked) { - Pipe pipe; - StubCallbackHandler handler(true); - - handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN); - pipe.writeSignal(); // would cause FD to be considered signalled - mPollLoop->removeCallback(pipe.receiveFd); - - StopWatch stopWatch("pollOnce"); - int32_t result = mPollLoop->pollOnce(100); - int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); - - ASSERT_EQ(OK, pipe.readSignal()) - << "signal should actually have been written"; - EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) - << "elapsed time should approx. equal timeout because FD was no longer registered"; - EXPECT_EQ(result, PollLoop::POLL_TIMEOUT) - << "pollOnce result should be POLL_TIMEOUT"; - EXPECT_EQ(0, handler.callbackCount) - << "callback should not be invoked"; -} - -TEST_F(PollLoopTest, PollOnce_WhenCallbackReturnsFalse_CallbackShouldNotBeInvokedAgainLater) { - Pipe pipe; - StubCallbackHandler handler(false); - - handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN); - - // First loop: Callback is registered and FD is signalled. - pipe.writeSignal(); - - StopWatch stopWatch("pollOnce"); - int32_t result = mPollLoop->pollOnce(0); - int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); - - ASSERT_EQ(OK, pipe.readSignal()) - << "signal should actually have been written"; - EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) - << "elapsed time should approx. equal zero because FD was already signalled"; - EXPECT_EQ(result, PollLoop::POLL_CALLBACK) - << "pollOnce result should be POLL_CALLBACK because FD was signalled"; - EXPECT_EQ(1, handler.callbackCount) - << "callback should be invoked"; - - // Second loop: Callback is no longer registered and FD is signalled. - pipe.writeSignal(); - - stopWatch.reset(); - result = mPollLoop->pollOnce(0); - elapsedMillis = ns2ms(stopWatch.elapsedTime()); - - ASSERT_EQ(OK, pipe.readSignal()) - << "signal should actually have been written"; - EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) - << "elapsed time should approx. equal zero because timeout was zero"; - EXPECT_EQ(result, PollLoop::POLL_TIMEOUT) - << "pollOnce result should be POLL_TIMEOUT"; - EXPECT_EQ(1, handler.callbackCount) - << "callback should not be invoked this time"; -} - -TEST_F(PollLoopTest, RemoveCallback_WhenCallbackNotAdded_ReturnsFalse) { - bool result = mPollLoop->removeCallback(1); - - EXPECT_FALSE(result) - << "removeCallback should return false because FD not registered"; -} - -TEST_F(PollLoopTest, RemoveCallback_WhenCallbackAddedThenRemovedTwice_ReturnsTrueFirstTimeAndReturnsFalseSecondTime) { - Pipe pipe; - StubCallbackHandler handler(false); - handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN); - - // First time. - bool result = mPollLoop->removeCallback(pipe.receiveFd); - - EXPECT_TRUE(result) - << "removeCallback should return true first time because FD was registered"; - - // Second time. - result = mPollLoop->removeCallback(pipe.receiveFd); - - EXPECT_FALSE(result) - << "removeCallback should return false second time because FD was no longer registered"; -} - -TEST_F(PollLoopTest, PollOnce_WhenCallbackAddedTwice_OnlySecondCallbackShouldBeInvoked) { - Pipe pipe; - StubCallbackHandler handler1(true); - StubCallbackHandler handler2(true); - - handler1.setCallback(mPollLoop, pipe.receiveFd, POLL_IN); - handler2.setCallback(mPollLoop, pipe.receiveFd, POLL_IN); // replace it - pipe.writeSignal(); // would cause FD to be considered signalled - - StopWatch stopWatch("pollOnce"); - int32_t result = mPollLoop->pollOnce(100); - int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); - - ASSERT_EQ(OK, pipe.readSignal()) - << "signal should actually have been written"; - EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) - << "elapsed time should approx. zero because FD was already signalled"; - EXPECT_EQ(result, PollLoop::POLL_CALLBACK) - << "pollOnce result should be POLL_CALLBACK because FD was signalled"; - EXPECT_EQ(0, handler1.callbackCount) - << "original handler callback should not be invoked because it was replaced"; - EXPECT_EQ(1, handler2.callbackCount) - << "replacement handler callback should be invoked"; -} - - -} // namespace android From 171bf9e69792f4796e27334c8a97dbd8576ad78a Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Thu, 16 Sep 2010 17:04:52 -0700 Subject: [PATCH 252/541] Ensure input dispatcher and native looper handles EINTR. Change-Id: I0a42db5f273b9bfe4ab174e4ee65d5d852f9f6bc --- libs/utils/Looper.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/libs/utils/Looper.cpp b/libs/utils/Looper.cpp index fd287da51..4aa50d675 100644 --- a/libs/utils/Looper.cpp +++ b/libs/utils/Looper.cpp @@ -162,9 +162,11 @@ int Looper::pollInner(int timeoutMillis) { struct epoll_event eventItems[EPOLL_MAX_EVENTS]; int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis); if (eventCount < 0) { - if (errno != EINTR) { - LOGW("Poll failed with an unexpected error, errno=%d", errno); + if (errno == EINTR) { + return ALOOPER_POLL_WAKE; } + + LOGW("Poll failed with an unexpected error, errno=%d", errno); return ALOOPER_POLL_ERROR; } @@ -196,7 +198,7 @@ int Looper::pollInner(int timeoutMillis) { ssize_t nRead; do { nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer)); - } while (nRead == sizeof(buffer)); + } while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer)); } else { LOGW("Ignoring unexpected epoll events 0x%x on wake read pipe.", epollEvents); } @@ -272,7 +274,11 @@ void Looper::wake() { LOGD("%p ~ wake", this); #endif - ssize_t nWrite = write(mWakeWritePipeFd, "W", 1); + ssize_t nWrite; + do { + nWrite = write(mWakeWritePipeFd, "W", 1); + } while (nWrite == -1 && errno == EINTR); + if (nWrite != 1) { if (errno != EAGAIN) { LOGW("Could not write wake signal, errno=%d", errno); From 905682a19609e862633f01e69bec58384df2cdf7 Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Thu, 16 Sep 2010 18:28:12 -0700 Subject: [PATCH 253/541] Looper: Drop default parameters in favor of a safer overload. The idea is that if you're writing code that wants fd/events/data on return from pollOnce() / pollAll() you should really pass in all of those arguments. When I changed the Looper API earlier, it was difficult to ensure that all callers were passing the right parameters since they were relying on default parameters to some degree so usage mistakes would not have been caught by the compiler. Change-Id: I1f2812894270aaf1515017ac1616b6b312d9b565 --- include/utils/Looper.h | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/include/utils/Looper.h b/include/utils/Looper.h index 92e4b0ae9..7d908669e 100644 --- a/include/utils/Looper.h +++ b/include/utils/Looper.h @@ -83,16 +83,20 @@ public: * This method does not return until it has finished invoking the appropriate callbacks * for all file descriptors that were signalled. */ - int pollOnce(int timeoutMillis, - int* outFd = NULL, int* outEvents = NULL, void** outData = NULL); + int pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData); + inline int pollOnce(int timeoutMillis) { + return pollOnce(timeoutMillis, NULL, NULL, NULL); + } /** * Like pollOnce(), but performs all pending callbacks until all * data has been consumed or a file descriptor is available with no callback. * This function will never return ALOOPER_POLL_CALLBACK. */ - int pollAll(int timeoutMillis, - int* outFd = NULL, int* outEvents = NULL, void** outData = NULL); + int pollAll(int timeoutMillis, int* outFd, int* outEvents, void** outData); + inline int pollAll(int timeoutMillis) { + return pollAll(timeoutMillis, NULL, NULL, NULL); + } /** * Wakes the poll asynchronously. @@ -128,8 +132,7 @@ public: * This method can be called on any thread. * This method may block briefly if it needs to wake the poll. */ - int addFd(int fd, int ident, - int events, ALooper_callbackFunc callback, void* data = NULL); + int addFd(int fd, int ident, int events, ALooper_callbackFunc callback, void* data); /** * Removes a previously added file descriptor from the looper. From 9da1810050d8825e51dabdd0262173432d49c16c Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Fri, 17 Sep 2010 17:01:23 -0700 Subject: [PATCH 254/541] Reduce lock thrashing in native Looper. In the common case, there is nothing interesting happening on the native Looper besides occasional wake ups. There is no point grabbing the semaphore then. Change-Id: Ib5c426d0e158dfa37891b7ff5537b6f833592fad --- libs/utils/Looper.cpp | 68 +++++++++++++++++++++++-------------------- 1 file changed, 37 insertions(+), 31 deletions(-) diff --git a/libs/utils/Looper.cpp b/libs/utils/Looper.cpp index 4aa50d675..b46279efc 100644 --- a/libs/utils/Looper.cpp +++ b/libs/utils/Looper.cpp @@ -184,44 +184,50 @@ int Looper::pollInner(int timeoutMillis) { #if DEBUG_POLL_AND_WAKE LOGD("%p ~ pollOnce - handling events from %d fds", this, eventCount); #endif - { // acquire lock - AutoMutex _l(mLock); - for (int i = 0; i < eventCount; i++) { - int fd = eventItems[i].data.fd; - uint32_t epollEvents = eventItems[i].events; - if (fd == mWakeReadPipeFd) { - if (epollEvents & EPOLLIN) { + bool acquiredLock = false; + for (int i = 0; i < eventCount; i++) { + int fd = eventItems[i].data.fd; + uint32_t epollEvents = eventItems[i].events; + if (fd == mWakeReadPipeFd) { + if (epollEvents & EPOLLIN) { #if DEBUG_POLL_AND_WAKE - LOGD("%p ~ pollOnce - awoken", this); + LOGD("%p ~ pollOnce - awoken", this); #endif - char buffer[16]; - ssize_t nRead; - do { - nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer)); - } while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer)); - } else { - LOGW("Ignoring unexpected epoll events 0x%x on wake read pipe.", epollEvents); - } + char buffer[16]; + ssize_t nRead; + do { + nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer)); + } while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer)); } else { - ssize_t requestIndex = mRequests.indexOfKey(fd); - if (requestIndex >= 0) { - int events = 0; - if (epollEvents & EPOLLIN) events |= ALOOPER_EVENT_INPUT; - if (epollEvents & EPOLLOUT) events |= ALOOPER_EVENT_OUTPUT; - if (epollEvents & EPOLLERR) events |= ALOOPER_EVENT_ERROR; - if (epollEvents & EPOLLHUP) events |= ALOOPER_EVENT_HANGUP; + LOGW("Ignoring unexpected epoll events 0x%x on wake read pipe.", epollEvents); + } + } else { + if (! acquiredLock) { + mLock.lock(); + acquiredLock = true; + } - Response response; - response.events = events; - response.request = mRequests.valueAt(requestIndex); - mResponses.push(response); - } else { - LOGW("Ignoring unexpected epoll events 0x%x on fd %d that is " - "no longer registered.", epollEvents, fd); - } + ssize_t requestIndex = mRequests.indexOfKey(fd); + if (requestIndex >= 0) { + int events = 0; + if (epollEvents & EPOLLIN) events |= ALOOPER_EVENT_INPUT; + if (epollEvents & EPOLLOUT) events |= ALOOPER_EVENT_OUTPUT; + if (epollEvents & EPOLLERR) events |= ALOOPER_EVENT_ERROR; + if (epollEvents & EPOLLHUP) events |= ALOOPER_EVENT_HANGUP; + + Response response; + response.events = events; + response.request = mRequests.valueAt(requestIndex); + mResponses.push(response); + } else { + LOGW("Ignoring unexpected epoll events 0x%x on fd %d that is " + "no longer registered.", epollEvents, fd); } } } + if (acquiredLock) { + mLock.unlock(); + } for (size_t i = 0; i < mResponses.size(); i++) { const Response& response = mResponses.itemAt(i); From d18051870e8e2a5f55237a2c11fde75f46082639 Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Tue, 21 Sep 2010 15:11:18 -0700 Subject: [PATCH 255/541] Looper: use pthread_once for TLS key initialization. Also fix a Valgrind complaint by zeroing out the entire epoll event struct since otherwise the data field union would be partly uninitialized (but not in a harmful way). Change-Id: I2091ce517e87fcad7c9caf90e2c5e4854a7ca465 --- include/utils/Looper.h | 1 + libs/utils/Looper.cpp | 29 ++++++++++++++--------------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/include/utils/Looper.h b/include/utils/Looper.h index 7d908669e..3f00b788c 100644 --- a/include/utils/Looper.h +++ b/include/utils/Looper.h @@ -205,6 +205,7 @@ private: int pollInner(int timeoutMillis); + static void initTLSKey(); static void threadDestructor(void *st); }; diff --git a/libs/utils/Looper.cpp b/libs/utils/Looper.cpp index b46279efc..d2dd6eb48 100644 --- a/libs/utils/Looper.cpp +++ b/libs/utils/Looper.cpp @@ -24,16 +24,15 @@ namespace android { -static pthread_mutex_t gTLSMutex = PTHREAD_MUTEX_INITIALIZER; -static bool gHaveTLS = false; -static pthread_key_t gTLS = 0; - // Hint for number of file descriptors to be associated with the epoll instance. static const int EPOLL_SIZE_HINT = 8; // Maximum number of file descriptors for which to retrieve poll events each iteration. static const int EPOLL_MAX_EVENTS = 16; +static pthread_once_t gTLSOnce = PTHREAD_ONCE_INIT; +static pthread_key_t gTLSKey = 0; + Looper::Looper(bool allowNonCallbacks) : mAllowNonCallbacks(allowNonCallbacks), mResponseIndex(0) { @@ -56,6 +55,7 @@ Looper::Looper(bool allowNonCallbacks) : errno); struct epoll_event eventItem; + memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union eventItem.events = EPOLLIN; eventItem.data.fd = mWakeReadPipeFd; result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, & eventItem); @@ -69,6 +69,11 @@ Looper::~Looper() { close(mEpollFd); } +void Looper::initTLSKey() { + int result = pthread_key_create(& gTLSKey, threadDestructor); + LOG_ALWAYS_FATAL_IF(result != 0, "Could not allocate TLS key."); +} + void Looper::threadDestructor(void *st) { Looper* const self = static_cast(st); if (self != NULL) { @@ -83,7 +88,7 @@ void Looper::setForThread(const sp& looper) { looper->incStrong((void*)threadDestructor); } - pthread_setspecific(gTLS, looper.get()); + pthread_setspecific(gTLSKey, looper.get()); if (old != NULL) { old->decStrong((void*)threadDestructor); @@ -91,17 +96,10 @@ void Looper::setForThread(const sp& looper) { } sp Looper::getForThread() { - if (!gHaveTLS) { - pthread_mutex_lock(&gTLSMutex); - if (pthread_key_create(&gTLS, threadDestructor) != 0) { - pthread_mutex_unlock(&gTLSMutex); - return NULL; - } - gHaveTLS = true; - pthread_mutex_unlock(&gTLSMutex); - } + int result = pthread_once(& gTLSOnce, initTLSKey); + LOG_ALWAYS_FATAL_IF(result != 0, "pthread_once failed"); - return (Looper*)pthread_getspecific(gTLS); + return (Looper*)pthread_getspecific(gTLSKey); } sp Looper::prepare(int opts) { @@ -331,6 +329,7 @@ int Looper::addFd(int fd, int ident, int events, ALooper_callbackFunc callback, request.data = data; struct epoll_event eventItem; + memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union eventItem.events = epollEvents; eventItem.data.fd = fd; From f7c1be0e62dea8a3f8b2d9b97689de964484f4db Mon Sep 17 00:00:00 2001 From: Kenny Root Date: Fri, 24 Sep 2010 07:57:37 -0700 Subject: [PATCH 256/541] Add locking around ZIP seeking Since we switched to seeking to the LFH to verify its existence instead of a huge mmap of the file, we have to guarantee that another seek doesn't happen before we finish our read on the LFH. Change-Id: If8135d9cb6f2f5cc4db734eafa4f6b5f6269c62a --- include/utils/ZipFileRO.h | 8 ++++++-- libs/utils/ZipFileRO.cpp | 27 +++++++++++++++++---------- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/include/utils/ZipFileRO.h b/include/utils/ZipFileRO.h index 97d31f4db..9668bdeef 100644 --- a/include/utils/ZipFileRO.h +++ b/include/utils/ZipFileRO.h @@ -24,8 +24,9 @@ #ifndef __LIBS_ZIPFILERO_H #define __LIBS_ZIPFILERO_H -#include "Errors.h" -#include "FileMap.h" +#include +#include +#include #include #include @@ -211,6 +212,9 @@ private: /* open Zip archive */ int mFd; + /* Lock for handling the file descriptor (seeks, etc) */ + mutable Mutex mFdLock; + /* zip file name */ char* mFileName; diff --git a/libs/utils/ZipFileRO.cpp b/libs/utils/ZipFileRO.cpp index 2d53136c1..59809c2f9 100644 --- a/libs/utils/ZipFileRO.cpp +++ b/libs/utils/ZipFileRO.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include @@ -195,7 +196,7 @@ bool ZipFileRO::mapCentralDirectory(void) free(scanBuf); return false; } else if (header != kLFHSignature) { - LOGV("Not a Zip archive (found 0x%08x)\n", val); + LOGV("Not a Zip archive (found 0x%08x)\n", header); free(scanBuf); return false; } @@ -496,15 +497,21 @@ bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, size_t* pUncompLen, } unsigned char lfhBuf[kLFHLen]; - if (lseek(mFd, localHdrOffset, SEEK_SET) != localHdrOffset) { - LOGW("failed seeking to lfh at offset %ld\n", localHdrOffset); - return false; - } - ssize_t actual = - TEMP_FAILURE_RETRY(read(mFd, lfhBuf, sizeof(lfhBuf))); - if (actual != sizeof(lfhBuf)) { - LOGW("failed reading lfh from offset %ld\n", localHdrOffset); - return false; + + { + AutoMutex _l(mFdLock); + + if (lseek(mFd, localHdrOffset, SEEK_SET) != localHdrOffset) { + LOGW("failed seeking to lfh at offset %ld\n", localHdrOffset); + return false; + } + + ssize_t actual = + TEMP_FAILURE_RETRY(read(mFd, lfhBuf, sizeof(lfhBuf))); + if (actual != sizeof(lfhBuf)) { + LOGW("failed reading lfh from offset %ld\n", localHdrOffset); + return false; + } } if (get4LE(lfhBuf) != kLFHSignature) { From cea2778d14a84e4dff261b82f5d8d9badfaafa85 Mon Sep 17 00:00:00 2001 From: Kenny Root Date: Fri, 24 Sep 2010 09:11:28 -0700 Subject: [PATCH 257/541] Revert "Revert "Free created FileMap when uncompressing files"" This revert reverts commit a19ef306bd0a257c67b50f5e0e669e9fe52b0889. --- libs/utils/ZipFileRO.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/libs/utils/ZipFileRO.cpp b/libs/utils/ZipFileRO.cpp index 59809c2f9..9fcae7238 100644 --- a/libs/utils/ZipFileRO.cpp +++ b/libs/utils/ZipFileRO.cpp @@ -643,7 +643,7 @@ bool ZipFileRO::uncompressEntry(ZipEntryRO entry, void* buffer) const memcpy(buffer, ptr, uncompLen); } else { if (!inflateBuffer(buffer, ptr, uncompLen, compLen)) - goto bail; + goto unmap; } if (compLen > kSequentialMin) @@ -651,6 +651,8 @@ bool ZipFileRO::uncompressEntry(ZipEntryRO entry, void* buffer) const result = true; +unmap: + file->release(); bail: return result; } @@ -674,7 +676,7 @@ bool ZipFileRO::uncompressEntry(ZipEntryRO entry, int fd) const getEntryInfo(entry, &method, &uncompLen, &compLen, &offset, NULL, NULL); - const FileMap* file = createEntryFileMap(entry); + FileMap* file = createEntryFileMap(entry); if (file == NULL) { goto bail; } @@ -685,21 +687,23 @@ bool ZipFileRO::uncompressEntry(ZipEntryRO entry, int fd) const ssize_t actual = write(fd, ptr, uncompLen); if (actual < 0) { LOGE("Write failed: %s\n", strerror(errno)); - goto bail; + goto unmap; } else if ((size_t) actual != uncompLen) { LOGE("Partial write during uncompress (%zd of %zd)\n", (size_t)actual, (size_t)uncompLen); - goto bail; + goto unmap; } else { LOGI("+++ successful write\n"); } } else { if (!inflateBuffer(fd, ptr, uncompLen, compLen)) - goto bail; + goto unmap; } result = true; +unmap: + file->release(); bail: return result; } From 7d90df8dc30e7b22aea030f7dca01095529cc6b1 Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Sun, 26 Sep 2010 22:20:12 -0700 Subject: [PATCH 258/541] Add suuport for splitting touch events across windows. This feature is currently used to enable dragging the start and end selection handles of a TextView at the same time. Could be used for other things later. Deleted some dead code in ArrowKeyMovementMethod and CursorControllers. Change-Id: I930accd97ca1ca1917aab8a807db2c950fc7b409 --- include/utils/BitSet.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/utils/BitSet.h b/include/utils/BitSet.h index 19c8bf093..f5dbcd942 100644 --- a/include/utils/BitSet.h +++ b/include/utils/BitSet.h @@ -38,6 +38,9 @@ struct BitSet32 { // Clears the bit set. inline void clear() { value = 0; } + // Returns the number of marked bits in the set. + inline uint32_t count() const { return __builtin_popcount(value); } + // Returns true if the bit set does not contain any marked bits. inline bool isEmpty() const { return ! value; } From a1ef2e020664860de562eccd63c2c5895519763c Mon Sep 17 00:00:00 2001 From: Kenny Root Date: Fri, 1 Oct 2010 18:28:28 -0700 Subject: [PATCH 259/541] ZipFileRO: moar logging and wrap close There is apparently still a race upon reading the entry Local File Header that can't be tracked down, so move the LFH check inside the mutex-protected block so we can call lseek again to see where we are when we log an error. Also, close() can fail so use TEMP_FAILURE_RETRY on it so we don't unwittingly leak file descriptors when Mean Mr. EINTR comes a-knocking. Change-Id: I753abad0bd882fe28f7281c406fa76f64393ef4c --- include/utils/ZipFileRO.h | 11 ++--------- libs/utils/ZipFileRO.cpp | 26 +++++++++++++++++++------- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/include/utils/ZipFileRO.h b/include/utils/ZipFileRO.h index 9668bdeef..e1ff780ab 100644 --- a/include/utils/ZipFileRO.h +++ b/include/utils/ZipFileRO.h @@ -64,15 +64,8 @@ public: mNumEntries(-1), mDirectoryOffset(-1), mHashTableSize(-1), mHashTable(NULL) {} - ~ZipFileRO() { - free(mHashTable); - if (mDirectoryMap) - mDirectoryMap->release(); - if (mFd >= 0) - close(mFd); - if (mFileName) - free(mFileName); - } + + ~ZipFileRO(); /* * Open an archive. diff --git a/libs/utils/ZipFileRO.cpp b/libs/utils/ZipFileRO.cpp index 9fcae7238..bee86b2bb 100644 --- a/libs/utils/ZipFileRO.cpp +++ b/libs/utils/ZipFileRO.cpp @@ -86,6 +86,16 @@ using namespace android; */ #define kZipEntryAdj 10000 +ZipFileRO::~ZipFileRO() { + free(mHashTable); + if (mDirectoryMap) + mDirectoryMap->release(); + if (mFd >= 0) + TEMP_FAILURE_RETRY(close(mFd)); + if (mFileName) + free(mFileName); +} + /* * Convert a ZipEntryRO to a hash table index, verifying that it's in a * valid range. @@ -122,7 +132,7 @@ status_t ZipFileRO::open(const char* zipFileName) mFileLength = lseek(fd, 0, SEEK_END); if (mFileLength < kEOCDLen) { - close(fd); + TEMP_FAILURE_RETRY(close(fd)); return UNKNOWN_ERROR; } @@ -152,7 +162,7 @@ status_t ZipFileRO::open(const char* zipFileName) bail: free(mFileName); mFileName = NULL; - close(fd); + TEMP_FAILURE_RETRY(close(fd)); return UNKNOWN_ERROR; } @@ -512,12 +522,14 @@ bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, size_t* pUncompLen, LOGW("failed reading lfh from offset %ld\n", localHdrOffset); return false; } - } - if (get4LE(lfhBuf) != kLFHSignature) { - LOGW("didn't find signature at start of lfh, offset=%ld (got 0x%08lx, expected 0x%08x)\n", - localHdrOffset, get4LE(lfhBuf), kLFHSignature); - return false; + if (get4LE(lfhBuf) != kLFHSignature) { + off_t actualOffset = lseek(mFd, 0, SEEK_CUR); + LOGW("didn't find signature at start of lfh; wanted: offset=%ld data=0x%08x; " + "got: offset=%zd data=0x%08lx\n", + localHdrOffset, kLFHSignature, (size_t)actualOffset, get4LE(lfhBuf)); + return false; + } } off_t dataOffset = localHdrOffset + kLFHLen From bf2ad6dd136ba456cd48f15da59828e2f6743dd6 Mon Sep 17 00:00:00 2001 From: Kenny Root Date: Mon, 4 Oct 2010 14:20:14 -0700 Subject: [PATCH 260/541] Use pread() in ZipFileRO for Linux AssetManager instances are created by zygote and passed to all its children so that they don't have to individually open frameworks-res.apk. This creates a problem for determining the current file offset when using lseek() on those files, because you can't guarantee the cross-process locking of a mutex. Luckily, Linux implements pread() to get around this suckiness. The problem is that only Linux implements this, so we have to keep the old locking for use on host builds with aapt and friends. aapt doesn't have this same problem of sharing file descriptors across forked processes, so we can keep the local AutoMutex to protect accesses of those files. Change-Id: Ibe9f11499a53fe345f50fbaea438815ec0fd363e --- include/utils/ZipFileRO.h | 24 +++++++++++++++++------- libs/utils/ZipFileRO.cpp | 33 ++++++++++++++++++++++++++++++++- 2 files changed, 49 insertions(+), 8 deletions(-) diff --git a/include/utils/ZipFileRO.h b/include/utils/ZipFileRO.h index e1ff780ab..3c1f3caf2 100644 --- a/include/utils/ZipFileRO.h +++ b/include/utils/ZipFileRO.h @@ -14,13 +14,19 @@ * limitations under the License. */ -// -// Read-only access to Zip archives, with minimal heap allocation. -// -// This is similar to the more-complete ZipFile class, but no attempt -// has been made to make them interchangeable. This class operates under -// a very different set of assumptions and constraints. -// +/* + * Read-only access to Zip archives, with minimal heap allocation. + * + * This is similar to the more-complete ZipFile class, but no attempt + * has been made to make them interchangeable. This class operates under + * a very different set of assumptions and constraints. + * + * One such assumption is that if you're getting file descriptors for + * use with this class as a child of a fork() operation, you must be on + * a pread() to guarantee correct operation. This is because pread() can + * atomically read at a file offset without worrying about a lock around an + * lseek() + read() pair. + */ #ifndef __LIBS_ZIPFILERO_H #define __LIBS_ZIPFILERO_H @@ -55,6 +61,10 @@ typedef void* ZipEntryRO; * the record structure. However, this requires a private mapping of * every page that the Central Directory touches. Easier to tuck a copy * of the string length into the hash table entry. + * + * NOTE: If this is used on file descriptors inherited from a fork() operation, + * you must be on a platform that implements pread() to guarantee correctness + * on the shared file descriptors. */ class ZipFileRO { public: diff --git a/libs/utils/ZipFileRO.cpp b/libs/utils/ZipFileRO.cpp index bee86b2bb..9b1f82fab 100644 --- a/libs/utils/ZipFileRO.cpp +++ b/libs/utils/ZipFileRO.cpp @@ -508,6 +508,36 @@ bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, size_t* pUncompLen, unsigned char lfhBuf[kLFHLen]; +#ifdef HAVE_PREAD + /* + * This file descriptor might be from zygote's preloaded assets, + * so we need to do an pread() instead of a lseek() + read() to + * guarantee atomicity across the processes with the shared file + * descriptors. + */ + ssize_t actual = + TEMP_FAILURE_RETRY(pread(mFd, lfhBuf, sizeof(lfhBuf), localHdrOffset)); + + if (actual != sizeof(lfhBuf)) { + LOGW("failed reading lfh from offset %ld\n", localHdrOffset); + return false; + } + + if (get4LE(lfhBuf) != kLFHSignature) { + LOGW("didn't find signature at start of lfh; wanted: offset=%ld data=0x%08x; " + "got: data=0x%08lx\n", + localHdrOffset, kLFHSignature, get4LE(lfhBuf)); + return false; + } +#else /* HAVE_PREAD */ + /* + * For hosts don't have pread() we cannot guarantee atomic reads from + * an offset in a file. Android should never run on those platforms. + * File descriptors inherited from a fork() share file offsets and + * there would be nothing to protect from two different processes + * calling lseek() concurrently. + */ + { AutoMutex _l(mFdLock); @@ -517,7 +547,7 @@ bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, size_t* pUncompLen, } ssize_t actual = - TEMP_FAILURE_RETRY(read(mFd, lfhBuf, sizeof(lfhBuf))); + TEMP_FAILURE_RETRY(read(mFd, lfhBuf, sizeof(lfhBuf))); if (actual != sizeof(lfhBuf)) { LOGW("failed reading lfh from offset %ld\n", localHdrOffset); return false; @@ -531,6 +561,7 @@ bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, size_t* pUncompLen, return false; } } +#endif /* HAVE_PREAD */ off_t dataOffset = localHdrOffset + kLFHLen + get2LE(lfhBuf + kLFHNameLen) + get2LE(lfhBuf + kLFHExtraLen); From 8d15c74d50fd01d6e63970aadd261a9d3bed27e7 Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Tue, 5 Oct 2010 15:35:37 -0700 Subject: [PATCH 261/541] Switch Looper back to using poll() instead of epoll(). Added a couple of micro-optimizations to avoid calling wake() unnecessarily and reduce JNI overhead slightly. Fixed a minor issue where we were not clearing the "next" field of Messages returned by the MessageQueue so the Message would hold on to its successor and potentially prevent the GC from collecting it if the message were leaked somehow. Change-Id: I488d29417ce0cdd7d0e447cda76ec978ef7f811c --- include/utils/Looper.h | 62 ++++++- libs/utils/Looper.cpp | 291 +++++++++++++++++++++++++++---- libs/utils/tests/Looper_test.cpp | 8 - 3 files changed, 313 insertions(+), 48 deletions(-) diff --git a/include/utils/Looper.h b/include/utils/Looper.h index 3f00b788c..cc51490fa 100644 --- a/include/utils/Looper.h +++ b/include/utils/Looper.h @@ -20,9 +20,22 @@ #include #include #include +#include #include +// Currently using poll() instead of epoll_wait() since it does a better job of meeting a +// timeout deadline. epoll_wait() typically causes additional delays of up to 10ms +// beyond the requested timeout. +//#define LOOPER_USES_EPOLL +//#define LOOPER_STATISTICS + +#ifdef LOOPER_USES_EPOLL +#include +#else +#include +#endif + /* * Declare a concrete type for the NDK's looper forward declaration. */ @@ -190,13 +203,54 @@ private: const bool mAllowNonCallbacks; // immutable - int mEpollFd; // immutable int mWakeReadPipeFd; // immutable int mWakeWritePipeFd; // immutable + Mutex mLock; + +#ifdef LOOPER_USES_EPOLL + int mEpollFd; // immutable // Locked list of file descriptor monitoring requests. - Mutex mLock; - KeyedVector mRequests; + KeyedVector mRequests; // guarded by mLock +#else + // The lock guards state used to track whether there is a poll() in progress and whether + // there are any other threads waiting in wakeAndLock(). The condition variables + // are used to transfer control among these threads such that all waiters are + // serviced before a new poll can begin. + // The wakeAndLock() method increments mWaiters, wakes the poll, blocks on mAwake + // until mPolling becomes false, then decrements mWaiters again. + // The poll() method blocks on mResume until mWaiters becomes 0, then sets + // mPolling to true, blocks until the poll completes, then resets mPolling to false + // and signals mResume if there are waiters. + bool mPolling; // guarded by mLock + uint32_t mWaiters; // guarded by mLock + Condition mAwake; // guarded by mLock + Condition mResume; // guarded by mLock + + Vector mRequestedFds; // must hold mLock and mPolling must be false to modify + Vector mRequests; // must hold mLock and mPolling must be false to modify + + ssize_t getRequestIndexLocked(int fd); + void wakeAndLock(); +#endif + +#ifdef LOOPER_STATISTICS + static const int SAMPLED_WAKE_CYCLES_TO_AGGREGATE = 100; + static const int SAMPLED_POLLS_TO_AGGREGATE = 1000; + + nsecs_t mPendingWakeTime; + int mPendingWakeCount; + + int mSampledWakeCycles; + int mSampledWakeCountSum; + nsecs_t mSampledWakeLatencySum; + + int mSampledPolls; + int mSampledZeroPollCount; + int mSampledZeroPollLatencySum; + int mSampledTimeoutPollCount; + int mSampledTimeoutPollLatencySum; +#endif // This state is only used privately by pollOnce and does not require a lock since // it runs on a single thread. @@ -204,6 +258,8 @@ private: size_t mResponseIndex; int pollInner(int timeoutMillis); + void awoken(); + void pushResponse(int events, const Request& request); static void initTLSKey(); static void threadDestructor(void *st); diff --git a/libs/utils/Looper.cpp b/libs/utils/Looper.cpp index d2dd6eb48..a5363d6fc 100644 --- a/libs/utils/Looper.cpp +++ b/libs/utils/Looper.cpp @@ -19,16 +19,17 @@ #include #include -#include namespace android { +#ifdef LOOPER_USES_EPOLL // Hint for number of file descriptors to be associated with the epoll instance. static const int EPOLL_SIZE_HINT = 8; // Maximum number of file descriptors for which to retrieve poll events each iteration. static const int EPOLL_MAX_EVENTS = 16; +#endif static pthread_once_t gTLSOnce = PTHREAD_ONCE_INIT; static pthread_key_t gTLSKey = 0; @@ -36,9 +37,6 @@ static pthread_key_t gTLSKey = 0; Looper::Looper(bool allowNonCallbacks) : mAllowNonCallbacks(allowNonCallbacks), mResponseIndex(0) { - mEpollFd = epoll_create(EPOLL_SIZE_HINT); - LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance. errno=%d", errno); - int wakeFds[2]; int result = pipe(wakeFds); LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe. errno=%d", errno); @@ -54,6 +52,11 @@ Looper::Looper(bool allowNonCallbacks) : LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake write pipe non-blocking. errno=%d", errno); +#ifdef LOOPER_USES_EPOLL + // Allocate the epoll instance and register the wake pipe. + mEpollFd = epoll_create(EPOLL_SIZE_HINT); + LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance. errno=%d", errno); + struct epoll_event eventItem; memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union eventItem.events = EPOLLIN; @@ -61,12 +64,45 @@ Looper::Looper(bool allowNonCallbacks) : result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, & eventItem); LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake read pipe to epoll instance. errno=%d", errno); +#else + // Add the wake pipe to the head of the request list with a null callback. + struct pollfd requestedFd; + requestedFd.fd = mWakeReadPipeFd; + requestedFd.events = POLLIN; + mRequestedFds.push(requestedFd); + + Request request; + request.fd = mWakeReadPipeFd; + request.callback = NULL; + request.ident = 0; + request.data = NULL; + mRequests.push(request); + + mPolling = false; + mWaiters = 0; +#endif + +#ifdef LOOPER_STATISTICS + mPendingWakeTime = -1; + mPendingWakeCount = 0; + mSampledWakeCycles = 0; + mSampledWakeCountSum = 0; + mSampledWakeLatencySum = 0; + + mSampledPolls = 0; + mSampledZeroPollCount = 0; + mSampledZeroPollLatencySum = 0; + mSampledTimeoutPollCount = 0; + mSampledTimeoutPollLatencySum = 0; +#endif } Looper::~Looper() { close(mWakeReadPipeFd); close(mWakeWritePipeFd); +#ifdef LOOPER_USES_EPOLL close(mEpollFd); +#endif } void Looper::initTLSKey() { @@ -157,45 +193,61 @@ int Looper::pollInner(int timeoutMillis) { #if DEBUG_POLL_AND_WAKE LOGD("%p ~ pollOnce - waiting: timeoutMillis=%d", this, timeoutMillis); #endif + + int result = ALOOPER_POLL_WAKE; + mResponses.clear(); + mResponseIndex = 0; + +#ifdef LOOPER_STATISTICS + nsecs_t pollStartTime = systemTime(SYSTEM_TIME_MONOTONIC); +#endif + +#ifdef LOOPER_USES_EPOLL struct epoll_event eventItems[EPOLL_MAX_EVENTS]; int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis); + bool acquiredLock = false; +#else + // Wait for wakeAndLock() waiters to run then set mPolling to true. + mLock.lock(); + while (mWaiters != 0) { + mResume.wait(mLock); + } + mPolling = true; + mLock.unlock(); + + size_t requestedCount = mRequestedFds.size(); + int eventCount = poll(mRequestedFds.editArray(), requestedCount, timeoutMillis); +#endif + if (eventCount < 0) { if (errno == EINTR) { - return ALOOPER_POLL_WAKE; + goto Done; } LOGW("Poll failed with an unexpected error, errno=%d", errno); - return ALOOPER_POLL_ERROR; + result = ALOOPER_POLL_ERROR; + goto Done; } if (eventCount == 0) { #if DEBUG_POLL_AND_WAKE LOGD("%p ~ pollOnce - timeout", this); #endif - return ALOOPER_POLL_TIMEOUT; + result = ALOOPER_POLL_TIMEOUT; + goto Done; } - int result = ALOOPER_POLL_WAKE; - mResponses.clear(); - mResponseIndex = 0; - #if DEBUG_POLL_AND_WAKE LOGD("%p ~ pollOnce - handling events from %d fds", this, eventCount); #endif - bool acquiredLock = false; + +#ifdef LOOPER_USES_EPOLL for (int i = 0; i < eventCount; i++) { int fd = eventItems[i].data.fd; uint32_t epollEvents = eventItems[i].events; if (fd == mWakeReadPipeFd) { if (epollEvents & EPOLLIN) { -#if DEBUG_POLL_AND_WAKE - LOGD("%p ~ pollOnce - awoken", this); -#endif - char buffer[16]; - ssize_t nRead; - do { - nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer)); - } while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer)); + awoken(); } else { LOGW("Ignoring unexpected epoll events 0x%x on wake read pipe.", epollEvents); } @@ -212,11 +264,7 @@ int Looper::pollInner(int timeoutMillis) { if (epollEvents & EPOLLOUT) events |= ALOOPER_EVENT_OUTPUT; if (epollEvents & EPOLLERR) events |= ALOOPER_EVENT_ERROR; if (epollEvents & EPOLLHUP) events |= ALOOPER_EVENT_HANGUP; - - Response response; - response.events = events; - response.request = mRequests.valueAt(requestIndex); - mResponses.push(response); + pushResponse(events, mRequests.valueAt(requestIndex)); } else { LOGW("Ignoring unexpected epoll events 0x%x on fd %d that is " "no longer registered.", epollEvents, fd); @@ -226,6 +274,66 @@ int Looper::pollInner(int timeoutMillis) { if (acquiredLock) { mLock.unlock(); } +Done: ; +#else + for (size_t i = 0; i < requestedCount; i++) { + const struct pollfd& requestedFd = mRequestedFds.itemAt(i); + + short pollEvents = requestedFd.revents; + if (pollEvents) { + if (requestedFd.fd == mWakeReadPipeFd) { + if (pollEvents & POLLIN) { + awoken(); + } else { + LOGW("Ignoring unexpected poll events 0x%x on wake read pipe.", pollEvents); + } + } else { + int events = 0; + if (pollEvents & POLLIN) events |= ALOOPER_EVENT_INPUT; + if (pollEvents & POLLOUT) events |= ALOOPER_EVENT_OUTPUT; + if (pollEvents & POLLERR) events |= ALOOPER_EVENT_ERROR; + if (pollEvents & POLLHUP) events |= ALOOPER_EVENT_HANGUP; + if (pollEvents & POLLNVAL) events |= ALOOPER_EVENT_INVALID; + pushResponse(events, mRequests.itemAt(i)); + } + if (--eventCount == 0) { + break; + } + } + } + +Done: + // Set mPolling to false and wake up the wakeAndLock() waiters. + mLock.lock(); + mPolling = false; + if (mWaiters != 0) { + mAwake.broadcast(); + } + mLock.unlock(); +#endif + +#ifdef LOOPER_STATISTICS + nsecs_t pollEndTime = systemTime(SYSTEM_TIME_MONOTONIC); + mSampledPolls += 1; + if (timeoutMillis == 0) { + mSampledZeroPollCount += 1; + mSampledZeroPollLatencySum += pollEndTime - pollStartTime; + } else if (timeoutMillis > 0 && result == ALOOPER_POLL_TIMEOUT) { + mSampledTimeoutPollCount += 1; + mSampledTimeoutPollLatencySum += pollEndTime - pollStartTime + - milliseconds_to_nanoseconds(timeoutMillis); + } + if (mSampledPolls == SAMPLED_POLLS_TO_AGGREGATE) { + LOGD("%p ~ poll latency statistics: %0.3fms zero timeout, %0.3fms non-zero timeout", this, + 0.000001f * float(mSampledZeroPollLatencySum) / mSampledZeroPollCount, + 0.000001f * float(mSampledTimeoutPollLatencySum) / mSampledTimeoutPollCount); + mSampledPolls = 0; + mSampledZeroPollCount = 0; + mSampledZeroPollLatencySum = 0; + mSampledTimeoutPollCount = 0; + mSampledTimeoutPollLatencySum = 0; + } +#endif for (size_t i = 0; i < mResponses.size(); i++) { const Response& response = mResponses.itemAt(i); @@ -278,6 +386,13 @@ void Looper::wake() { LOGD("%p ~ wake", this); #endif +#ifdef LOOPER_STATISTICS + // FIXME: Possible race with awoken() but this code is for testing only and is rarely enabled. + if (mPendingWakeCount++ == 0) { + mPendingWakeTime = systemTime(SYSTEM_TIME_MONOTONIC); + } +#endif + ssize_t nWrite; do { nWrite = write(mWakeWritePipeFd, "W", 1); @@ -290,23 +405,51 @@ void Looper::wake() { } } +void Looper::awoken() { +#if DEBUG_POLL_AND_WAKE + LOGD("%p ~ awoken", this); +#endif + +#ifdef LOOPER_STATISTICS + if (mPendingWakeCount == 0) { + LOGD("%p ~ awoken: spurious!", this); + } else { + mSampledWakeCycles += 1; + mSampledWakeCountSum += mPendingWakeCount; + mSampledWakeLatencySum += systemTime(SYSTEM_TIME_MONOTONIC) - mPendingWakeTime; + mPendingWakeCount = 0; + mPendingWakeTime = -1; + if (mSampledWakeCycles == SAMPLED_WAKE_CYCLES_TO_AGGREGATE) { + LOGD("%p ~ wake statistics: %0.3fms wake latency, %0.3f wakes per cycle", this, + 0.000001f * float(mSampledWakeLatencySum) / mSampledWakeCycles, + float(mSampledWakeCountSum) / mSampledWakeCycles); + mSampledWakeCycles = 0; + mSampledWakeCountSum = 0; + mSampledWakeLatencySum = 0; + } + } +#endif + + char buffer[16]; + ssize_t nRead; + do { + nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer)); + } while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer)); +} + +void Looper::pushResponse(int events, const Request& request) { + Response response; + response.events = events; + response.request = request; + mResponses.push(response); +} + int Looper::addFd(int fd, int ident, int events, ALooper_callbackFunc callback, void* data) { #if DEBUG_CALLBACKS LOGD("%p ~ addFd - fd=%d, ident=%d, events=0x%x, callback=%p, data=%p", this, fd, ident, events, callback, data); #endif - int epollEvents = 0; - if (events & ALOOPER_EVENT_INPUT) epollEvents |= EPOLLIN; - if (events & ALOOPER_EVENT_OUTPUT) epollEvents |= EPOLLOUT; - if (events & ALOOPER_EVENT_ERROR) epollEvents |= EPOLLERR; - if (events & ALOOPER_EVENT_HANGUP) epollEvents |= EPOLLHUP; - - if (epollEvents == 0) { - LOGE("Invalid attempt to set a callback with no selected poll events."); - return -1; - } - if (! callback) { if (! mAllowNonCallbacks) { LOGE("Invalid attempt to set NULL callback but not allowed for this looper."); @@ -319,6 +462,11 @@ int Looper::addFd(int fd, int ident, int events, ALooper_callbackFunc callback, } } +#ifdef LOOPER_USES_EPOLL + int epollEvents = 0; + if (events & ALOOPER_EVENT_INPUT) epollEvents |= EPOLLIN; + if (events & ALOOPER_EVENT_OUTPUT) epollEvents |= EPOLLOUT; + { // acquire lock AutoMutex _l(mLock); @@ -350,6 +498,33 @@ int Looper::addFd(int fd, int ident, int events, ALooper_callbackFunc callback, mRequests.replaceValueAt(requestIndex, request); } } // release lock +#else + int pollEvents = 0; + if (events & ALOOPER_EVENT_INPUT) pollEvents |= POLLIN; + if (events & ALOOPER_EVENT_OUTPUT) pollEvents |= POLLOUT; + + wakeAndLock(); // acquire lock + + struct pollfd requestedFd; + requestedFd.fd = fd; + requestedFd.events = pollEvents; + + Request request; + request.fd = fd; + request.ident = ident; + request.callback = callback; + request.data = data; + ssize_t index = getRequestIndexLocked(fd); + if (index < 0) { + mRequestedFds.push(requestedFd); + mRequests.push(request); + } else { + mRequestedFds.replaceAt(requestedFd, size_t(index)); + mRequests.replaceAt(request, size_t(index)); + } + + mLock.unlock(); // release lock +#endif return 1; } @@ -358,6 +533,7 @@ int Looper::removeFd(int fd) { LOGD("%p ~ removeFd - fd=%d", this, fd); #endif +#ifdef LOOPER_USES_EPOLL { // acquire lock AutoMutex _l(mLock); ssize_t requestIndex = mRequests.indexOfKey(fd); @@ -372,8 +548,49 @@ int Looper::removeFd(int fd) { } mRequests.removeItemsAt(requestIndex); - } // request lock + } // release lock return 1; +#else + wakeAndLock(); // acquire lock + + ssize_t index = getRequestIndexLocked(fd); + if (index >= 0) { + mRequestedFds.removeAt(size_t(index)); + mRequests.removeAt(size_t(index)); + } + + mLock.unlock(); // release lock + return index >= 0; +#endif } +#ifndef LOOPER_USES_EPOLL +ssize_t Looper::getRequestIndexLocked(int fd) { + size_t requestCount = mRequestedFds.size(); + + for (size_t i = 0; i < requestCount; i++) { + if (mRequestedFds.itemAt(i).fd == fd) { + return i; + } + } + + return -1; +} + +void Looper::wakeAndLock() { + mLock.lock(); + + mWaiters += 1; + while (mPolling) { + wake(); + mAwake.wait(mLock); + } + + mWaiters -= 1; + if (mWaiters == 0) { + mResume.signal(); + } +} +#endif + } // namespace android diff --git a/libs/utils/tests/Looper_test.cpp b/libs/utils/tests/Looper_test.cpp index afc92f8cd..cea1313a4 100644 --- a/libs/utils/tests/Looper_test.cpp +++ b/libs/utils/tests/Looper_test.cpp @@ -354,14 +354,6 @@ TEST_F(LooperTest, AddFd_WhenCallbackAdded_ReturnsOne) { << "addFd should return 1 because FD was added"; } -TEST_F(LooperTest, AddFd_WhenEventsIsZero_ReturnsError) { - Pipe pipe; - int result = mLooper->addFd(pipe.receiveFd, 0, 0, NULL, NULL); - - EXPECT_EQ(-1, result) - << "addFd should return -1 because arguments were invalid"; -} - TEST_F(LooperTest, AddFd_WhenIdentIsNegativeAndCallbackIsNull_ReturnsError) { Pipe pipe; int result = mLooper->addFd(pipe.receiveFd, -1, ALOOPER_EVENT_INPUT, NULL, NULL); From fcefac26820933abeb40170b476b9d04efa3d35d Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Thu, 7 Oct 2010 16:17:57 -0700 Subject: [PATCH 262/541] Revert to using epoll_wait(). This change depends on the kernel having been patched to use hrtimers instead of jiffies for scheduling epoll timeouts. Change-Id: I216bc1c4f565e67ebcb3d2ba4280cb615932bb9e --- include/utils/Looper.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/utils/Looper.h b/include/utils/Looper.h index cc51490fa..eefff3124 100644 --- a/include/utils/Looper.h +++ b/include/utils/Looper.h @@ -24,10 +24,10 @@ #include -// Currently using poll() instead of epoll_wait() since it does a better job of meeting a -// timeout deadline. epoll_wait() typically causes additional delays of up to 10ms -// beyond the requested timeout. -//#define LOOPER_USES_EPOLL +// When defined, uses epoll_wait() for polling, otherwise uses poll(). +#define LOOPER_USES_EPOLL + +// When defined, logs performance statistics for tuning and debugging purposes. //#define LOOPER_STATISTICS #ifdef LOOPER_USES_EPOLL From 60cbc840393ccd98efd9963013bc61f2621d95ab Mon Sep 17 00:00:00 2001 From: Kenny Root Date: Wed, 13 Oct 2010 15:00:07 -0700 Subject: [PATCH 263/541] OBB: use PBKDF2 for key generation. Switch to using PBKDF2 for the key generation for OBBs. Any previously generated OBBs will stop being read correctly. A small pbkdf2gen program is available to allow generation of appropriate keys with the salts. Bug: 3059950 Change-Id: If4305c989fd692fd1150eb270dbf751e09c37295 --- include/utils/ObbFile.h | 27 +++++++++++++++++++ libs/utils/ObbFile.cpp | 44 +++++++++++++++++++------------ libs/utils/tests/ObbFile_test.cpp | 18 +++++++++++++ 3 files changed, 72 insertions(+), 17 deletions(-) diff --git a/include/utils/ObbFile.h b/include/utils/ObbFile.h index 5243f5005..47559cdd0 100644 --- a/include/utils/ObbFile.h +++ b/include/utils/ObbFile.h @@ -27,6 +27,7 @@ namespace android { // OBB flags (bit 0) #define OBB_OVERLAY (1 << 0) +#define OBB_SALTED (1 << 1) class ObbFile : public RefBase { protected: @@ -70,6 +71,26 @@ public: mFlags = flags; } + const unsigned char* getSalt(size_t* length) const { + if ((mFlags & OBB_SALTED) == 0) { + *length = 0; + return NULL; + } + + *length = sizeof(mSalt); + return mSalt; + } + + bool setSalt(const unsigned char* salt, size_t length) { + if (length != sizeof(mSalt)) { + return false; + } + + memcpy(mSalt, salt, sizeof(mSalt)); + mFlags |= OBB_SALTED; + return true; + } + bool isOverlay() { return (mFlags & OBB_OVERLAY) == OBB_OVERLAY; } @@ -103,6 +124,12 @@ private: /* Flags for this OBB type. */ int32_t mFlags; + /* Whether the file is salted. */ + bool mSalted; + + /* The encryption salt. */ + unsigned char mSalt[8]; + const char* mFileName; size_t mFileSize; diff --git a/libs/utils/ObbFile.cpp b/libs/utils/ObbFile.cpp index e170ab88c..2c3724c0a 100644 --- a/libs/utils/ObbFile.cpp +++ b/libs/utils/ObbFile.cpp @@ -29,10 +29,11 @@ #define kFooterTagSize 8 /* last two 32-bit integers */ -#define kFooterMinSize 25 /* 32-bit signature version (4 bytes) +#define kFooterMinSize 33 /* 32-bit signature version (4 bytes) * 32-bit package version (4 bytes) * 32-bit flags (4 bytes) - * 32-bit package name size (4-bytes) + * 64-bit salt (8 bytes) + * 32-bit package name size (4 bytes) * >=1-character package name (1 byte) * 32-bit footer size (4 bytes) * 32-bit footer marker (4 bytes) @@ -47,8 +48,9 @@ /* offsets in version 1 of the header */ #define kPackageVersionOffset 4 #define kFlagsOffset 8 -#define kPackageNameLenOffset 12 -#define kPackageNameOffset 16 +#define kSaltOffset 12 +#define kPackageNameLenOffset 20 +#define kPackageNameOffset 24 /* * TEMP_FAILURE_RETRY is defined by some, but not all, versions of @@ -79,11 +81,12 @@ typedef off64_t my_off64_t; namespace android { -ObbFile::ObbFile() : - mPackageName(""), - mVersion(-1), - mFlags(0) +ObbFile::ObbFile() + : mPackageName("") + , mVersion(-1) + , mFlags(0) { + memset(mSalt, 0, sizeof(mSalt)); } ObbFile::~ObbFile() { @@ -192,7 +195,7 @@ bool ObbFile::parseObbFile(int fd) #ifdef DEBUG for (int i = 0; i < footerSize; ++i) { - LOGI("char: 0x%02x", scanBuf[i]); + LOGI("char: 0x%02x\n", scanBuf[i]); } #endif @@ -206,6 +209,8 @@ bool ObbFile::parseObbFile(int fd) mVersion = (int32_t) get4LE((unsigned char*)scanBuf + kPackageVersionOffset); mFlags = (int32_t) get4LE((unsigned char*)scanBuf + kFlagsOffset); + memcpy(&mSalt, (unsigned char*)scanBuf + kSaltOffset, sizeof(mSalt)); + uint32_t packageNameLen = get4LE((unsigned char*)scanBuf + kPackageNameLenOffset); if (packageNameLen <= 0 || packageNameLen > (footerSize - kPackageNameOffset)) { @@ -255,7 +260,7 @@ bool ObbFile::writeTo(int fd) my_lseek64(fd, 0, SEEK_END); if (mPackageName.size() == 0 || mVersion == -1) { - LOGW("tried to write uninitialized ObbFile data"); + LOGW("tried to write uninitialized ObbFile data\n"); return false; } @@ -264,43 +269,48 @@ bool ObbFile::writeTo(int fd) put4LE(intBuf, kSigVersion); if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) { - LOGW("couldn't write signature version: %s", strerror(errno)); + LOGW("couldn't write signature version: %s\n", strerror(errno)); return false; } put4LE(intBuf, mVersion); if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) { - LOGW("couldn't write package version"); + LOGW("couldn't write package version\n"); return false; } put4LE(intBuf, mFlags); if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) { - LOGW("couldn't write package version"); + LOGW("couldn't write package version\n"); + return false; + } + + if (write(fd, mSalt, sizeof(mSalt)) != (ssize_t)sizeof(mSalt)) { + LOGW("couldn't write salt: %s\n", strerror(errno)); return false; } size_t packageNameLen = mPackageName.size(); put4LE(intBuf, packageNameLen); if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) { - LOGW("couldn't write package name length: %s", strerror(errno)); + LOGW("couldn't write package name length: %s\n", strerror(errno)); return false; } if (write(fd, mPackageName.string(), packageNameLen) != (ssize_t)packageNameLen) { - LOGW("couldn't write package name: %s", strerror(errno)); + LOGW("couldn't write package name: %s\n", strerror(errno)); return false; } put4LE(intBuf, kPackageNameOffset + packageNameLen); if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) { - LOGW("couldn't write footer size: %s", strerror(errno)); + LOGW("couldn't write footer size: %s\n", strerror(errno)); return false; } put4LE(intBuf, kSignature); if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) { - LOGW("couldn't write footer magic signature: %s", strerror(errno)); + LOGW("couldn't write footer magic signature: %s\n", strerror(errno)); return false; } diff --git a/libs/utils/tests/ObbFile_test.cpp b/libs/utils/tests/ObbFile_test.cpp index 29bb70a66..46b30c2a6 100644 --- a/libs/utils/tests/ObbFile_test.cpp +++ b/libs/utils/tests/ObbFile_test.cpp @@ -23,6 +23,7 @@ #include #include +#include namespace android { @@ -63,6 +64,10 @@ TEST_F(ObbFileTest, WriteThenRead) { mObbFile->setPackageName(String8(packageName)); mObbFile->setVersion(versionNum); +#define SALT_SIZE 8 + unsigned char salt[SALT_SIZE] = {0x01, 0x10, 0x55, 0xAA, 0xFF, 0x00, 0x5A, 0xA5}; + EXPECT_TRUE(mObbFile->setSalt(salt, SALT_SIZE)) + << "Salt should be successfully set"; EXPECT_TRUE(mObbFile->writeTo(mFileName)) << "couldn't write to fake .obb file"; @@ -77,6 +82,19 @@ TEST_F(ObbFileTest, WriteThenRead) { const char* currentPackageName = mObbFile->getPackageName().string(); EXPECT_STREQ(packageName, currentPackageName) << "package name didn't come out the same as it went in"; + + size_t saltLen; + const unsigned char* newSalt = mObbFile->getSalt(&saltLen); + + EXPECT_EQ(sizeof(salt), saltLen) + << "salt sizes were not the same"; + + for (int i = 0; i < sizeof(salt); i++) { + EXPECT_EQ(salt[i], newSalt[i]) + << "salt character " << i << " should be equal"; + } + EXPECT_TRUE(memcmp(newSalt, salt, sizeof(salt)) == 0) + << "salts should be the same"; } } From 929e4ef26a7920dd6f3593f60d45a59ffeaedb14 Mon Sep 17 00:00:00 2001 From: Raphael Moll Date: Wed, 13 Oct 2010 19:13:48 -0700 Subject: [PATCH 264/541] MinGW/Cygwin requires open() in O_BINARY mode. Alsso printf %zd is not supported on MinGW/Cygwin. Change-Id: I03811dabb46e2b05dd1d8abcc0ff97b125c77d54 --- libs/utils/ZipFileRO.cpp | 57 ++++++++++++++++++++++++++-------------- 1 file changed, 37 insertions(+), 20 deletions(-) diff --git a/libs/utils/ZipFileRO.cpp b/libs/utils/ZipFileRO.cpp index 9b1f82fab..5ff1f8f2c 100644 --- a/libs/utils/ZipFileRO.cpp +++ b/libs/utils/ZipFileRO.cpp @@ -32,6 +32,22 @@ #include #include +#if HAVE_PRINTF_ZD +# define ZD "%zd" +# define ZD_TYPE ssize_t +#else +# define ZD "%ld" +# define ZD_TYPE long +#endif + +/* + * We must open binary files using open(path, ... | O_BINARY) under Windows. + * Otherwise strange read errors will happen. + */ +#ifndef O_BINARY +# define O_BINARY 0 +#endif + /* * TEMP_FAILURE_RETRY is defined by some, but not all, versions of * . (Alas, it is not as standard as we'd hoped!) So, if it's @@ -124,7 +140,7 @@ status_t ZipFileRO::open(const char* zipFileName) /* * Open and map the specified file. */ - fd = ::open(zipFileName, O_RDONLY); + fd = ::open(zipFileName, O_RDONLY | O_BINARY); if (fd < 0) { LOGW("Unable to open zip '%s': %s\n", zipFileName, strerror(errno)); return NAME_NOT_FOUND; @@ -172,8 +188,8 @@ bail: */ bool ZipFileRO::mapCentralDirectory(void) { - size_t readAmount = kMaxEOCDSearch; - if (readAmount > (size_t) mFileLength) + ssize_t readAmount = kMaxEOCDSearch; + if (readAmount > (ssize_t) mFileLength) readAmount = mFileLength; unsigned char* scanBuf = (unsigned char*) malloc(readAmount); @@ -233,7 +249,8 @@ bool ZipFileRO::mapCentralDirectory(void) } actual = TEMP_FAILURE_RETRY(read(mFd, scanBuf, readAmount)); if (actual != (ssize_t) readAmount) { - LOGW("Zip: read %zd failed: %s\n", readAmount, strerror(errno)); + LOGW("Zip: read " ZD ", expected " ZD ". Failed: %s\n", + (ZD_TYPE) actual, (ZD_TYPE) readAmount, strerror(errno)); free(scanBuf); return false; } @@ -292,8 +309,8 @@ bool ZipFileRO::mapCentralDirectory(void) } if (!mDirectoryMap->create(mFileName, mFd, dirOffset, dirSize, true)) { - LOGW("Unable to map '%s' (%zd to %zd): %s\n", mFileName, - dirOffset, dirOffset + dirSize, strerror(errno)); + LOGW("Unable to map '%s' (" ZD " to " ZD "): %s\n", mFileName, + (ZD_TYPE) dirOffset, (ZD_TYPE) (dirOffset + dirSize), strerror(errno)); return false; } @@ -350,8 +367,8 @@ bool ZipFileRO::parseZipArchive(void) ptr += kCDELen + fileNameLen + extraLen + commentLen; if ((size_t)(ptr - cdPtr) > cdLength) { - LOGW("bad CD advance (%d vs %zd) at entry %d\n", - (int) (ptr - cdPtr), cdLength, i); + LOGW("bad CD advance (%d vs " ZD ") at entry %d\n", + (int) (ptr - cdPtr), (ZD_TYPE) cdLength, i); goto bail; } } @@ -556,8 +573,8 @@ bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, size_t* pUncompLen, if (get4LE(lfhBuf) != kLFHSignature) { off_t actualOffset = lseek(mFd, 0, SEEK_CUR); LOGW("didn't find signature at start of lfh; wanted: offset=%ld data=0x%08x; " - "got: offset=%zd data=0x%08lx\n", - localHdrOffset, kLFHSignature, (size_t)actualOffset, get4LE(lfhBuf)); + "got: offset=" ZD " data=0x%08lx\n", + localHdrOffset, kLFHSignature, (ZD_TYPE) actualOffset, get4LE(lfhBuf)); return false; } } @@ -572,16 +589,16 @@ bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, size_t* pUncompLen, /* check lengths */ if ((off_t)(dataOffset + compLen) > cdOffset) { - LOGW("bad compressed length in zip (%ld + %zd > %ld)\n", - (long) dataOffset, compLen, (long) cdOffset); + LOGW("bad compressed length in zip (%ld + " ZD " > %ld)\n", + (long) dataOffset, (ZD_TYPE) compLen, (long) cdOffset); return false; } if (method == kCompressStored && (off_t)(dataOffset + uncompLen) > cdOffset) { - LOGE("ERROR: bad uncompressed length in zip (%ld + %zd > %ld)\n", - (long) dataOffset, uncompLen, (long) cdOffset); + LOGE("ERROR: bad uncompressed length in zip (%ld + " ZD " > %ld)\n", + (long) dataOffset, (ZD_TYPE) uncompLen, (long) cdOffset); return false; } @@ -732,8 +749,8 @@ bool ZipFileRO::uncompressEntry(ZipEntryRO entry, int fd) const LOGE("Write failed: %s\n", strerror(errno)); goto unmap; } else if ((size_t) actual != uncompLen) { - LOGE("Partial write during uncompress (%zd of %zd)\n", - (size_t)actual, (size_t)uncompLen); + LOGE("Partial write during uncompress (" ZD " of " ZD ")\n", + (ZD_TYPE) actual, (ZD_TYPE) uncompLen); goto unmap; } else { LOGI("+++ successful write\n"); @@ -802,8 +819,8 @@ bail: /* paranoia */ if (zstream.total_out != uncompLen) { - LOGW("Size mismatch on inflated file (%ld vs %zd)\n", - zstream.total_out, uncompLen); + LOGW("Size mismatch on inflated file (%ld vs " ZD ")\n", + zstream.total_out, (ZD_TYPE) uncompLen); goto z_bail; } @@ -891,8 +908,8 @@ bail: /* paranoia */ if (zstream.total_out != uncompLen) { - LOGW("Size mismatch on inflated file (%ld vs %zd)\n", - zstream.total_out, uncompLen); + LOGW("Size mismatch on inflated file (%ld vs " ZD ")\n", + zstream.total_out, (ZD_TYPE) uncompLen); goto z_bail; } From 48da31b735ebbc6a00c4e9fe9631f4af79e2c9df Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Sun, 12 Sep 2010 17:55:08 -0700 Subject: [PATCH 265/541] Add keycodes and meta-key modifiers to support external keyboards. Added new key maps for external keyboards. These maps are intended to be shared across devices by inheriting the "keyboards.mk" product makefile as part of the device's product definition. One of the trickier changes here was to unwind some code in MetaKeyKeyListener that assumed that only the low 8 bits of the meta key state were actually used. The new code abandons bitshifts in favor of simple conditionals that are probably easier to read anyways. The special meta key state constants used by MetaKeyKeyListener are now (@hide) defined in KeyEvent now so as to make it clearer that they share the same code space even if those codes are not valid for KeyEvents. The EventHub now takes care of detecting the appropriate key layout map and key character map when the device is added and sets system properties accordingly. This avoids having duplicate code in KeyCharacterMap to probe for the appropriate key character map although the current probing mechanism has been preserved for legacy reasons just in case. Added support for tracking caps lock, num lock and scroll lock and turning their corresponding LEDs on and off as needed. The key character map format will need to be updated to correctly support PC style external keyboard semantics related to modifier keys. That will come in a later change so caps lock doesn't actually do anything right now except turn the shiny LEDs on and off... Added a list of symbolic key names to KeyEvent and improved the toString() output for debug diagnosis. Having this list in a central place in the framework also allows us to remove it from Monkey so there is one less thing to maintain when we add new keycodes. Bug: 2912307 Change-Id: If8c25e8d50a7c29bbf5d663c94284f5f86de5da4 --- include/utils/String8.h | 8 ++++++++ libs/utils/String8.cpp | 5 +++++ 2 files changed, 13 insertions(+) diff --git a/include/utils/String8.h b/include/utils/String8.h index ef0b51a44..cef8ecab9 100644 --- a/include/utils/String8.h +++ b/include/utils/String8.h @@ -157,9 +157,12 @@ public: inline size_t size() const; inline size_t length() const; inline size_t bytes() const; + inline bool isEmpty() const; inline const SharedBuffer* sharedBuffer() const; + void clear(); + void setTo(const String8& other); status_t setTo(const char* other); status_t setTo(const char* other, size_t numChars); @@ -345,6 +348,11 @@ inline size_t String8::size() const return length(); } +inline bool String8::isEmpty() const +{ + return length() == 0; +} + inline size_t String8::bytes() const { return SharedBuffer::sizeFromData(mString)-1; diff --git a/libs/utils/String8.cpp b/libs/utils/String8.cpp index 1c4f80c1f..6358fc424 100644 --- a/libs/utils/String8.cpp +++ b/libs/utils/String8.cpp @@ -292,6 +292,11 @@ String8::~String8() SharedBuffer::bufferFromData(mString)->release(); } +void String8::clear() { + SharedBuffer::bufferFromData(mString)->release(); + mString = getEmptyString(); +} + void String8::setTo(const String8& other) { SharedBuffer::bufferFromData(other.mString)->acquire(); From d85b55697218655763eb644e677504e6cc66b5a2 Mon Sep 17 00:00:00 2001 From: Joe Onorato Date: Mon, 18 Oct 2010 19:13:23 -0400 Subject: [PATCH 266/541] Reduce logging. Remember, the system and main logs are - Shared resources - Primarily for recording problems - To be used only for large grained events during normal operation Bug: 3104855 Change-Id: I136fbd101917dcbc8ebc3f96f276426b48bde7b7 --- libs/utils/StreamingZipInflater.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libs/utils/StreamingZipInflater.cpp b/libs/utils/StreamingZipInflater.cpp index 7ebde78cd..1f62ac518 100644 --- a/libs/utils/StreamingZipInflater.cpp +++ b/libs/utils/StreamingZipInflater.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#define LOG_NDEBUG 1 #define LOG_TAG "szipinf" #include @@ -157,7 +158,7 @@ ssize_t StreamingZipInflater::read(void* outBuf, size_t count) { */ int result = Z_OK; if (mStreamNeedsInit) { - LOGI("Initializing zlib to inflate"); + LOGD("Initializing zlib to inflate"); result = inflateInit2(&mInflateState, -MAX_WBITS); mStreamNeedsInit = false; } From 935d1bba3bf92992fba9a8322fe7ca7a5fbffb39 Mon Sep 17 00:00:00 2001 From: David Deephanphongs Date: Tue, 19 Oct 2010 14:54:05 -0700 Subject: [PATCH 267/541] Add missing NOTICE entries. Add missing NOTICE block for TagSoup in general NOTICE file. Add comment in Android.mk to help reviewers locate the NOTICE for apache-http. Modify build rule for 'am' command to pull in the NOTICE file. Change-Id: I43c6c1468395b70d0942b3620f12e4b6f3d9a66b --- NOTICE | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/NOTICE b/NOTICE index 20062013e..8d6f583a1 100644 --- a/NOTICE +++ b/NOTICE @@ -17,7 +17,7 @@ The Android Open Source Project (http://source.android.com). ========================================================================= Apache Commons -Copyright 1999-2004 The Apache Software Foundation +Copyright 1999-2006 The Apache Software Foundation This product includes software developed at The Apache Software Foundation (http://www.apache.org/). @@ -53,6 +53,26 @@ Media Codecs These files are Copyright 1998 - 2009 PacketVideo, but released under the Apache2 License. + ========================================================================= + == NOTICE file corresponding to the section 4 d of == + == the Apache License, Version 2.0, == + == in this case for the TagSoup code. == + ========================================================================= + +This file is part of TagSoup and is Copyright 2002-2008 by John Cowan. + +TagSoup is licensed under the Apache License, +Version 2.0. You may obtain a copy of this license at +http://www.apache.org/licenses/LICENSE-2.0 . You may also have +additional legal rights not granted by this license. + +TagSoup is distributed in the hope that it will be useful, but +unless required by applicable law or agreed to in writing, TagSoup +is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS +OF ANY KIND, either express or implied; not even the implied warranty +of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ From 2881c85e38c662050e9635c6ff3861a3be09674f Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Wed, 20 Oct 2010 13:24:58 -0700 Subject: [PATCH 268/541] Add length-equality test in String operator== checks. Change-Id: I6ebc6ef85aac4539269f137c1f29f95b9828d4f9 --- include/utils/String16.h | 4 +++- include/utils/String8.h | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/include/utils/String16.h b/include/utils/String16.h index 07a0c1188..d98b2d04d 100644 --- a/include/utils/String16.h +++ b/include/utils/String16.h @@ -205,7 +205,9 @@ inline bool String16::operator<=(const String16& other) const inline bool String16::operator==(const String16& other) const { - return strzcmp16(mString, size(), other.mString, other.size()) == 0; + const size_t n1 = size(); + const size_t n2 = other.size(); + return n1 == n2 && strzcmp16(mString, n1, other.mString, n2) == 0; } inline bool String16::operator!=(const String16& other) const diff --git a/include/utils/String8.h b/include/utils/String8.h index cef8ecab9..a1292227e 100644 --- a/include/utils/String8.h +++ b/include/utils/String8.h @@ -418,7 +418,9 @@ inline bool String8::operator<=(const String8& other) const inline bool String8::operator==(const String8& other) const { - return strcmp(mString, other.mString) == 0; + return (SharedBuffer::sizeFromData(mString) == + SharedBuffer::sizeFromData(other.mString)) && + strcmp(mString, other.mString) == 0; } inline bool String8::operator!=(const String8& other) const From 9d589aa0d63e1eb088b10f065ee7fb0765d2e285 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Wed, 20 Oct 2010 17:06:28 -0700 Subject: [PATCH 269/541] Revert "Add length-equality test in String operator== checks." This reverts commit e28210d401ae4ed1258b84c9b17a172a757190e8. --- include/utils/String16.h | 4 +--- include/utils/String8.h | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/include/utils/String16.h b/include/utils/String16.h index d98b2d04d..07a0c1188 100644 --- a/include/utils/String16.h +++ b/include/utils/String16.h @@ -205,9 +205,7 @@ inline bool String16::operator<=(const String16& other) const inline bool String16::operator==(const String16& other) const { - const size_t n1 = size(); - const size_t n2 = other.size(); - return n1 == n2 && strzcmp16(mString, n1, other.mString, n2) == 0; + return strzcmp16(mString, size(), other.mString, other.size()) == 0; } inline bool String16::operator!=(const String16& other) const diff --git a/include/utils/String8.h b/include/utils/String8.h index a1292227e..cef8ecab9 100644 --- a/include/utils/String8.h +++ b/include/utils/String8.h @@ -418,9 +418,7 @@ inline bool String8::operator<=(const String8& other) const inline bool String8::operator==(const String8& other) const { - return (SharedBuffer::sizeFromData(mString) == - SharedBuffer::sizeFromData(other.mString)) && - strcmp(mString, other.mString) == 0; + return strcmp(mString, other.mString) == 0; } inline bool String8::operator!=(const String8& other) const From e76184c81fba004b2a72fd8b98baef2b15ff1779 Mon Sep 17 00:00:00 2001 From: Kenny Root Date: Thu, 21 Oct 2010 15:18:28 -0700 Subject: [PATCH 270/541] Initialized check in ZipFileRO::findEntryByName If a ZipFileRO object is uninitialized, the hash table will not have been initialized. This condition wasn't checked in findEntryByName. Bug: 3121109 Change-Id: Ib696e0e7e0cb4dd0fb2e456d6a847e5e8f4fe14e --- libs/utils/ZipFileRO.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/libs/utils/ZipFileRO.cpp b/libs/utils/ZipFileRO.cpp index 5ff1f8f2c..42611964b 100644 --- a/libs/utils/ZipFileRO.cpp +++ b/libs/utils/ZipFileRO.cpp @@ -412,10 +412,18 @@ void ZipFileRO::addToHash(const char* str, int strLen, unsigned int hash) /* * Find a matching entry. * - * Returns 0 if not found. + * Returns NULL if not found. */ ZipEntryRO ZipFileRO::findEntryByName(const char* fileName) const { + /* + * If the ZipFileRO instance is not initialized, the entry number will + * end up being garbage since mHashTableSize is -1. + */ + if (mHashTableSize <= 0) { + return NULL; + } + int nameLen = strlen(fileName); unsigned int hash = computeHash(fileName, nameLen); int ent = hash & (mHashTableSize-1); From 328f501eb99e5b2502deb909de4209cf2dbdecc8 Mon Sep 17 00:00:00 2001 From: Kenny Root Date: Thu, 28 Oct 2010 14:47:01 -0700 Subject: [PATCH 271/541] Add path to get different DPI drawables Allow a caller to request a different density than their current display allows. This can mean a device displaying mdpi can get a resource that's in hdpi and have it pretend to be in mdpi resolution. If a drawable that's returned is not in the requested density, it will set it at the appropriate density to be scaled up later on. The API for this is hidden currently. Bug: 3134688 Change-Id: I6c3908cbdef4907b8d3f1576df9e3b0e7af1755a --- include/utils/ResourceTypes.h | 8 ++++--- libs/utils/ResourceTypes.cpp | 44 +++++++++++++++++++++++++++-------- 2 files changed, 39 insertions(+), 13 deletions(-) diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h index da86da410..ed7f53d17 100644 --- a/include/utils/ResourceTypes.h +++ b/include/utils/ResourceTypes.h @@ -1771,12 +1771,14 @@ public: * * @return ssize_t Either a >= 0 table index or a negative error code. */ - ssize_t getResource(uint32_t resID, Res_value* outValue, bool mayBeBag=false, - uint32_t* outSpecFlags=NULL, ResTable_config* outConfig=NULL) const; + ssize_t getResource(uint32_t resID, Res_value* outValue, bool mayBeBag = false, + uint16_t density = 0, + uint32_t* outSpecFlags = NULL, + ResTable_config* outConfig = NULL) const; inline ssize_t getResource(const ResTable_ref& res, Res_value* outValue, uint32_t* outSpecFlags=NULL) const { - return getResource(res.ident, outValue, false, outSpecFlags, NULL); + return getResource(res.ident, outValue, false, 0, outSpecFlags, NULL); } ssize_t resolveReference(Res_value* inOutValue, diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp index 91e7df3ef..03d2e217a 100644 --- a/libs/utils/ResourceTypes.cpp +++ b/libs/utils/ResourceTypes.cpp @@ -1896,7 +1896,7 @@ bool ResTable::getResourceName(uint32_t resID, resource_name* outName) const return false; } -ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag, +ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag, uint16_t density, uint32_t* outSpecFlags, ResTable_config* outConfig) const { if (mError != NO_ERROR) { @@ -1926,7 +1926,7 @@ ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag memset(&bestItem, 0, sizeof(bestItem)); // make the compiler shut up if (outSpecFlags != NULL) *outSpecFlags = 0; - + // Look through all resource packages, starting with the most // recently added. const PackageGroup* const grp = mPackageGroups[p]; @@ -1934,6 +1934,22 @@ ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag LOGW("Bad identifier when getting value for resource number 0x%08x", resID); return BAD_INDEX; } + + // Allow overriding density + const ResTable_config* desiredConfig = &mParams; + ResTable_config* overrideConfig = NULL; + if (density > 0) { + overrideConfig = (ResTable_config*) malloc(sizeof(ResTable_config)); + if (overrideConfig == NULL) { + LOGE("Couldn't malloc ResTable_config for overrides: %s", strerror(errno)); + return BAD_INDEX; + } + memcpy(overrideConfig, &mParams, sizeof(ResTable_config)); + overrideConfig->density = density; + desiredConfig = overrideConfig; + } + + ssize_t rc = BAD_INDEX; size_t ip = grp->packages.size(); while (ip > 0) { ip--; @@ -1943,12 +1959,13 @@ ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag const ResTable_type* type; const ResTable_entry* entry; const Type* typeClass; - ssize_t offset = getEntry(package, t, e, &mParams, &type, &entry, &typeClass); + ssize_t offset = getEntry(package, t, e, desiredConfig, &type, &entry, &typeClass); if (offset <= 0) { if (offset < 0) { LOGW("Failure getting entry for 0x%08x (t=%d e=%d) in package %zd (error %d)\n", resID, t, e, ip, (int)offset); - return offset; + rc = offset; + goto out; } continue; } @@ -1963,13 +1980,14 @@ ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag TABLE_NOISY(aout << "Resource type data: " << HexDump(type, dtohl(type->header.size)) << endl); - + if ((size_t)offset > (dtohl(type->header.size)-sizeof(Res_value))) { LOGW("ResTable_item at %d is beyond type chunk data %d", (int)offset, dtohl(type->header.size)); - return BAD_TYPE; + rc = BAD_TYPE; + goto out; } - + const Res_value* item = (const Res_value*)(((const uint8_t*)type) + offset); ResTable_config thisConfig; @@ -2011,10 +2029,16 @@ ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag outValue->data, &len)).string() : "", outValue->data)); - return bestPackage->header->index; + rc = bestPackage->header->index; + goto out; } - return BAD_VALUE; +out: + if (overrideConfig != NULL) { + free(overrideConfig); + } + + return rc; } ssize_t ResTable::resolveReference(Res_value* value, ssize_t blockIndex, @@ -2027,7 +2051,7 @@ ssize_t ResTable::resolveReference(Res_value* value, ssize_t blockIndex, if (outLastRef) *outLastRef = value->data; uint32_t lastRef = value->data; uint32_t newFlags = 0; - const ssize_t newIndex = getResource(value->data, value, true, &newFlags, + const ssize_t newIndex = getResource(value->data, value, true, 0, &newFlags, outConfig); if (newIndex == BAD_INDEX) { return BAD_INDEX; From 3a91fca00c6b3db62b4dc0da95ba30671caf3283 Mon Sep 17 00:00:00 2001 From: Kenny Root Date: Tue, 2 Nov 2010 11:27:21 -0700 Subject: [PATCH 272/541] Fix default return code for getResource Reorganization of getResource to allow for other densities accidentally overrode the default return code for getResource from BAD_VALUE to BAD_INDEX. This corrects the default return to BAD_VALUE which restores other things to working. Bug: 3155824 Change-Id: I13dafff85bc6978c5f5435fc09ab0474c7885c4d --- libs/utils/ResourceTypes.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp index 03d2e217a..f2872982c 100644 --- a/libs/utils/ResourceTypes.cpp +++ b/libs/utils/ResourceTypes.cpp @@ -1949,7 +1949,7 @@ ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag desiredConfig = overrideConfig; } - ssize_t rc = BAD_INDEX; + ssize_t rc = BAD_VALUE; size_t ip = grp->packages.size(); while (ip > 0) { ip--; From ba0165bef09729a33ab8e0ca329342be05e0d859 Mon Sep 17 00:00:00 2001 From: Kenny Root Date: Tue, 9 Nov 2010 14:37:23 -0800 Subject: [PATCH 273/541] Split UTF functions from String8/16 Split out all the UTF-8/16/32 handling code from String8/16 to its own file to allow better reuse of code. Change-Id: If9ce63920edc75472c38da4adce0d13cda9ad2f7 --- include/utils/String16.h | 29 +- include/utils/String8.h | 116 +----- include/utils/Unicode.h | 161 +++++++++ libs/utils/Android.mk | 1 + libs/utils/ResourceTypes.cpp | 97 +++-- libs/utils/String16.cpp | 253 +------------ libs/utils/String8.cpp | 403 ++------------------- libs/utils/Unicode.cpp | 575 ++++++++++++++++++++++++++++++ libs/utils/tests/Android.mk | 3 +- libs/utils/tests/Unicode_test.cpp | 115 ++++++ 10 files changed, 986 insertions(+), 767 deletions(-) create mode 100644 include/utils/Unicode.h create mode 100644 libs/utils/Unicode.cpp create mode 100644 libs/utils/tests/Unicode_test.cpp diff --git a/include/utils/String16.h b/include/utils/String16.h index 07a0c1188..584f53f30 100644 --- a/include/utils/String16.h +++ b/include/utils/String16.h @@ -19,39 +19,12 @@ #include #include - -#include -#include +#include // --------------------------------------------------------------------------- extern "C" { -typedef uint16_t char16_t; - -// Standard string functions on char16 strings. -int strcmp16(const char16_t *, const char16_t *); -int strncmp16(const char16_t *s1, const char16_t *s2, size_t n); -size_t strlen16(const char16_t *); -size_t strnlen16(const char16_t *, size_t); -char16_t *strcpy16(char16_t *, const char16_t *); -char16_t *strncpy16(char16_t *, const char16_t *, size_t); - -// Version of comparison that supports embedded nulls. -// This is different than strncmp() because we don't stop -// at a nul character and consider the strings to be different -// if the lengths are different (thus we need to supply the -// lengths of both strings). This can also be used when -// your string is not nul-terminated as it will have the -// equivalent result as strcmp16 (unlike strncmp16). -int strzcmp16(const char16_t *s1, size_t n1, const char16_t *s2, size_t n2); - -// Version of strzcmp16 for comparing strings in different endianness. -int strzcmp16_h_n(const char16_t *s1H, size_t n1, const char16_t *s2N, size_t n2); - -// Convert UTF-8 to UTF-16 including surrogate pairs -void utf8_to_utf16(const uint8_t *src, size_t srcLen, char16_t* dst, const size_t dstLen); - } // --------------------------------------------------------------------------- diff --git a/include/utils/String8.h b/include/utils/String8.h index cef8ecab9..b36f128f9 100644 --- a/include/utils/String8.h +++ b/include/utils/String8.h @@ -18,122 +18,16 @@ #define ANDROID_STRING8_H #include +#include +#include -// Need this for the char16_t type; String8.h should not -// be depedent on the String16 class. -#include - -#include -#include -#include - -// --------------------------------------------------------------------------- - -extern "C" { - -typedef uint32_t char32_t; - -size_t strlen32(const char32_t *); -size_t strnlen32(const char32_t *, size_t); - -/* - * Returns the length of "src" when "src" is valid UTF-8 string. - * Returns 0 if src is NULL, 0-length string or non UTF-8 string. - * This function should be used to determine whether "src" is valid UTF-8 - * characters with valid unicode codepoints. "src" must be null-terminated. - * - * If you are going to use other GetUtf... functions defined in this header - * with string which may not be valid UTF-8 with valid codepoint (form 0 to - * 0x10FFFF), you should use this function before calling others, since the - * other functions do not check whether the string is valid UTF-8 or not. - * - * If you do not care whether "src" is valid UTF-8 or not, you should use - * strlen() as usual, which should be much faster. - */ -size_t utf8_length(const char *src); - -/* - * Returns the UTF-32 length of "src". - */ -size_t utf32_length(const char *src, size_t src_len); - -/* - * Returns the UTF-8 length of "src". - */ -size_t utf8_length_from_utf16(const char16_t *src, size_t src_len); - -/* - * Returns the UTF-8 length of "src". - */ -size_t utf8_length_from_utf32(const char32_t *src, size_t src_len); - -/* - * Returns the unicode value at "index". - * Returns -1 when the index is invalid (equals to or more than "src_len"). - * If returned value is positive, it is able to be converted to char32_t, which - * is unsigned. Then, if "next_index" is not NULL, the next index to be used is - * stored in "next_index". "next_index" can be NULL. - */ -int32_t utf32_at(const char *src, size_t src_len, - size_t index, size_t *next_index); - -/* - * Stores a UTF-32 string converted from "src" in "dst", if "dst_length" is not - * large enough to store the string, the part of the "src" string is stored - * into "dst". - * Returns the size actually used for storing the string. - * "dst" is not null-terminated when dst_len is fully used (like strncpy). - */ -size_t utf8_to_utf32(const char* src, size_t src_len, - char32_t* dst, size_t dst_len); - -/* - * Stores a UTF-8 string converted from "src" in "dst", if "dst_length" is not - * large enough to store the string, the part of the "src" string is stored - * into "dst" as much as possible. See the examples for more detail. - * Returns the size actually used for storing the string. - * dst" is not null-terminated when dst_len is fully used (like strncpy). - * - * Example 1 - * "src" == \u3042\u3044 (\xE3\x81\x82\xE3\x81\x84) - * "src_len" == 2 - * "dst_len" >= 7 - * -> - * Returned value == 6 - * "dst" becomes \xE3\x81\x82\xE3\x81\x84\0 - * (note that "dst" is null-terminated) - * - * Example 2 - * "src" == \u3042\u3044 (\xE3\x81\x82\xE3\x81\x84) - * "src_len" == 2 - * "dst_len" == 5 - * -> - * Returned value == 3 - * "dst" becomes \xE3\x81\x82\0 - * (note that "dst" is null-terminated, but \u3044 is not stored in "dst" - * since "dst" does not have enough size to store the character) - * - * Example 3 - * "src" == \u3042\u3044 (\xE3\x81\x82\xE3\x81\x84) - * "src_len" == 2 - * "dst_len" == 6 - * -> - * Returned value == 6 - * "dst" becomes \xE3\x81\x82\xE3\x81\x84 - * (note that "dst" is NOT null-terminated, like strncpy) - */ -size_t utf32_to_utf8(const char32_t* src, size_t src_len, - char* dst, size_t dst_len); - -size_t utf16_to_utf8(const char16_t* src, size_t src_len, - char* dst, size_t dst_len); - -} +#include // for strcmp // --------------------------------------------------------------------------- namespace android { +class String16; class TextOutput; //! This is a string holding UTF-8 characters. Does not allow the value more @@ -182,7 +76,7 @@ public: size_t getUtf32Length() const; int32_t getUtf32At(size_t index, size_t *next_index) const; - size_t getUtf32(char32_t* dst, size_t dst_len) const; + void getUtf32(char32_t* dst) const; inline String8& operator=(const String8& other); inline String8& operator=(const char* other); diff --git a/include/utils/Unicode.h b/include/utils/Unicode.h new file mode 100644 index 000000000..6afb291f4 --- /dev/null +++ b/include/utils/Unicode.h @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2005 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_UNICODE_H +#define ANDROID_UNICODE_H + +#include +#include + +extern "C" { + +typedef uint32_t char32_t; +typedef uint16_t char16_t; + +// Standard string functions on char16_t strings. +int strcmp16(const char16_t *, const char16_t *); +int strncmp16(const char16_t *s1, const char16_t *s2, size_t n); +size_t strlen16(const char16_t *); +size_t strnlen16(const char16_t *, size_t); +char16_t *strcpy16(char16_t *, const char16_t *); +char16_t *strncpy16(char16_t *, const char16_t *, size_t); + +// Version of comparison that supports embedded nulls. +// This is different than strncmp() because we don't stop +// at a nul character and consider the strings to be different +// if the lengths are different (thus we need to supply the +// lengths of both strings). This can also be used when +// your string is not nul-terminated as it will have the +// equivalent result as strcmp16 (unlike strncmp16). +int strzcmp16(const char16_t *s1, size_t n1, const char16_t *s2, size_t n2); + +// Version of strzcmp16 for comparing strings in different endianness. +int strzcmp16_h_n(const char16_t *s1H, size_t n1, const char16_t *s2N, size_t n2); + +// Standard string functions on char32_t strings. +size_t strlen32(const char32_t *); +size_t strnlen32(const char32_t *, size_t); + +/** + * Measure the length of a UTF-32 string in UTF-8. If the string is invalid + * such as containing a surrogate character, -1 will be returned. + */ +ssize_t utf32_to_utf8_length(const char32_t *src, size_t src_len); + +/** + * Stores a UTF-8 string converted from "src" in "dst", if "dst_length" is not + * large enough to store the string, the part of the "src" string is stored + * into "dst" as much as possible. See the examples for more detail. + * Returns the size actually used for storing the string. + * dst" is not null-terminated when dst_len is fully used (like strncpy). + * + * Example 1 + * "src" == \u3042\u3044 (\xE3\x81\x82\xE3\x81\x84) + * "src_len" == 2 + * "dst_len" >= 7 + * -> + * Returned value == 6 + * "dst" becomes \xE3\x81\x82\xE3\x81\x84\0 + * (note that "dst" is null-terminated) + * + * Example 2 + * "src" == \u3042\u3044 (\xE3\x81\x82\xE3\x81\x84) + * "src_len" == 2 + * "dst_len" == 5 + * -> + * Returned value == 3 + * "dst" becomes \xE3\x81\x82\0 + * (note that "dst" is null-terminated, but \u3044 is not stored in "dst" + * since "dst" does not have enough size to store the character) + * + * Example 3 + * "src" == \u3042\u3044 (\xE3\x81\x82\xE3\x81\x84) + * "src_len" == 2 + * "dst_len" == 6 + * -> + * Returned value == 6 + * "dst" becomes \xE3\x81\x82\xE3\x81\x84 + * (note that "dst" is NOT null-terminated, like strncpy) + */ +void utf32_to_utf8(const char32_t* src, size_t src_len, char* dst); + +/** + * Returns the unicode value at "index". + * Returns -1 when the index is invalid (equals to or more than "src_len"). + * If returned value is positive, it is able to be converted to char32_t, which + * is unsigned. Then, if "next_index" is not NULL, the next index to be used is + * stored in "next_index". "next_index" can be NULL. + */ +int32_t utf32_from_utf8_at(const char *src, size_t src_len, size_t index, size_t *next_index); + + +/** + * Returns the UTF-8 length of UTF-16 string "src". + */ +ssize_t utf16_to_utf8_length(const char16_t *src, size_t src_len); + +/** + * Converts a UTF-16 string to UTF-8. The destination buffer must be large + * enough to fit the UTF-16 as measured by utf16_to_utf8_length with an added + * NULL terminator. + */ +void utf16_to_utf8(const char16_t* src, size_t src_len, char* dst); + +/** + * Returns the length of "src" when "src" is valid UTF-8 string. + * Returns 0 if src is NULL or 0-length string. Returns -1 when the source + * is an invalid string. + * + * This function should be used to determine whether "src" is valid UTF-8 + * characters with valid unicode codepoints. "src" must be null-terminated. + * + * If you are going to use other utf8_to_... functions defined in this header + * with string which may not be valid UTF-8 with valid codepoint (form 0 to + * 0x10FFFF), you should use this function before calling others, since the + * other functions do not check whether the string is valid UTF-8 or not. + * + * If you do not care whether "src" is valid UTF-8 or not, you should use + * strlen() as usual, which should be much faster. + */ +ssize_t utf8_length(const char *src); + +/** + * Measure the length of a UTF-32 string. + */ +size_t utf8_to_utf32_length(const char *src, size_t src_len); + +/** + * Stores a UTF-32 string converted from "src" in "dst". "dst" must be large + * enough to store the entire converted string as measured by + * utf8_to_utf32_length plus space for a NULL terminator. + */ +void utf8_to_utf32(const char* src, size_t src_len, char32_t* dst); + +/** + * Returns the UTF-16 length of UTF-8 string "src". + */ +ssize_t utf8_to_utf16_length(const uint8_t* src, size_t srcLen); + +/** + * Convert UTF-8 to UTF-16 including surrogate pairs. The destination buffer + * must be large enough to hold the result as measured by utf8_to_utf16_length + * plus an added NULL terminator. + */ +void utf8_to_utf16(const uint8_t* src, size_t srcLen, char16_t* dst); + +} + +#endif diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk index eb75ed894..05a967491 100644 --- a/libs/utils/Android.mk +++ b/libs/utils/Android.mk @@ -41,6 +41,7 @@ commonSources:= \ TextOutput.cpp \ Threads.cpp \ Timers.cpp \ + Unicode.cpp \ VectorImpl.cpp \ ZipFileCRO.cpp \ ZipFileRO.cpp \ diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp index f2872982c..bbf50934d 100644 --- a/libs/utils/ResourceTypes.cpp +++ b/libs/utils/ResourceTypes.cpp @@ -444,15 +444,51 @@ void ResStringPool::uninit() } } -#define DECODE_LENGTH(str, chrsz, len) \ - len = *(str); \ - if (*(str)&(1<<(chrsz*8-1))) { \ - (str)++; \ - len = (((len)&((1<<(chrsz*8-1))-1))<<(chrsz*8)) + *(str); \ - } \ - (str)++; +/** + * Strings in UTF-16 format have length indicated by a length encoded in the + * stored data. It is either 1 or 2 characters of length data. This allows a + * maximum length of 0x7FFFFFF (2147483647 bytes), but if you're storing that + * much data in a string, you're abusing them. + * + * If the high bit is set, then there are two characters or 4 bytes of length + * data encoded. In that case, drop the high bit of the first character and + * add it together with the next character. + */ +static inline size_t +decodeLength(const char16_t** str) +{ + size_t len = **str; + if ((len & 0x8000) != 0) { + (*str)++; + len = ((len & 0x7FFF) << 16) | **str; + } + (*str)++; + return len; +} -const uint16_t* ResStringPool::stringAt(size_t idx, size_t* outLen) const +/** + * Strings in UTF-8 format have length indicated by a length encoded in the + * stored data. It is either 1 or 2 characters of length data. This allows a + * maximum length of 0x7FFF (32767 bytes), but you should consider storing + * text in another way if you're using that much data in a single string. + * + * If the high bit is set, then there are two characters or 2 bytes of length + * data encoded. In that case, drop the high bit of the first character and + * add it together with the next character. + */ +static inline size_t +decodeLength(const uint8_t** str) +{ + size_t len = **str; + if ((len & 0x80) != 0) { + (*str)++; + len = ((len & 0x7F) << 8) | **str; + } + (*str)++; + return len; +} + +const uint16_t* ResStringPool::stringAt(size_t idx, size_t* u16len) const { if (mError == NO_ERROR && idx < mHeader->stringCount) { const bool isUTF8 = (mHeader->flags&ResStringPool_header::UTF8_FLAG) != 0; @@ -461,37 +497,51 @@ const uint16_t* ResStringPool::stringAt(size_t idx, size_t* outLen) const if (!isUTF8) { const char16_t* strings = (char16_t*)mStrings; const char16_t* str = strings+off; - DECODE_LENGTH(str, sizeof(char16_t), *outLen) - if ((uint32_t)(str+*outLen-strings) < mStringPoolSize) { + + *u16len = decodeLength(&str); + if ((uint32_t)(str+*u16len-strings) < mStringPoolSize) { return str; } else { LOGW("Bad string block: string #%d extends to %d, past end at %d\n", - (int)idx, (int)(str+*outLen-strings), (int)mStringPoolSize); + (int)idx, (int)(str+*u16len-strings), (int)mStringPoolSize); } } else { const uint8_t* strings = (uint8_t*)mStrings; - const uint8_t* str = strings+off; - DECODE_LENGTH(str, sizeof(uint8_t), *outLen) - size_t encLen; - DECODE_LENGTH(str, sizeof(uint8_t), encLen) - if ((uint32_t)(str+encLen-strings) < mStringPoolSize) { + const uint8_t* u8str = strings+off; + + *u16len = decodeLength(&u8str); + size_t u8len = decodeLength(&u8str); + + // encLen must be less than 0x7FFF due to encoding. + if ((uint32_t)(u8str+u8len-strings) < mStringPoolSize) { AutoMutex lock(mDecodeLock); + if (mCache[idx] != NULL) { return mCache[idx]; } - char16_t *u16str = (char16_t *)calloc(*outLen+1, sizeof(char16_t)); + + ssize_t actualLen = utf8_to_utf16_length(u8str, u8len); + if (actualLen < 0 || (size_t)actualLen != *u16len) { + LOGW("Bad string block: string #%lld decoded length is not correct " + "%lld vs %llu\n", + (long long)idx, (long long)actualLen, (long long)*u16len); + return NULL; + } + + char16_t *u16str = (char16_t *)calloc(*u16len+1, sizeof(char16_t)); if (!u16str) { LOGW("No memory when trying to allocate decode cache for string #%d\n", (int)idx); return NULL; } - const unsigned char *u8src = reinterpret_cast(str); - utf8_to_utf16(u8src, encLen, u16str, *outLen); + + utf8_to_utf16(u8str, u8len, u16str); mCache[idx] = u16str; return u16str; } else { - LOGW("Bad string block: string #%d extends to %d, past end at %d\n", - (int)idx, (int)(str+encLen-strings), (int)mStringPoolSize); + LOGW("Bad string block: string #%lld extends to %lld, past end at %lld\n", + (long long)idx, (long long)(u8str+u8len-strings), + (long long)mStringPoolSize); } } } else { @@ -512,9 +562,8 @@ const char* ResStringPool::string8At(size_t idx, size_t* outLen) const if (isUTF8) { const uint8_t* strings = (uint8_t*)mStrings; const uint8_t* str = strings+off; - DECODE_LENGTH(str, sizeof(uint8_t), *outLen) - size_t encLen; - DECODE_LENGTH(str, sizeof(uint8_t), encLen) + *outLen = decodeLength(&str); + size_t encLen = decodeLength(&str); if ((uint32_t)(str+encLen-strings) < mStringPoolSize) { return (const char*)str; } else { diff --git a/libs/utils/String16.cpp b/libs/utils/String16.cpp index eab7b2b92..4ce166426 100644 --- a/libs/utils/String16.cpp +++ b/libs/utils/String16.cpp @@ -18,228 +18,17 @@ #include #include +#include #include #include #include #include -#ifdef HAVE_WINSOCK -# undef nhtol -# undef htonl -# undef nhtos -# undef htons - -# ifdef HAVE_LITTLE_ENDIAN -# define ntohl(x) ( ((x) << 24) | (((x) >> 24) & 255) | (((x) << 8) & 0xff0000) | (((x) >> 8) & 0xff00) ) -# define htonl(x) ntohl(x) -# define ntohs(x) ( (((x) << 8) & 0xff00) | (((x) >> 8) & 255) ) -# define htons(x) ntohs(x) -# else -# define ntohl(x) (x) -# define htonl(x) (x) -# define ntohs(x) (x) -# define htons(x) (x) -# endif -#else -# include -#endif - #include #include #include -// --------------------------------------------------------------------------- - -int strcmp16(const char16_t *s1, const char16_t *s2) -{ - char16_t ch; - int d = 0; - - while ( 1 ) { - d = (int)(ch = *s1++) - (int)*s2++; - if ( d || !ch ) - break; - } - - return d; -} - -int strncmp16(const char16_t *s1, const char16_t *s2, size_t n) -{ - char16_t ch; - int d = 0; - - while ( n-- ) { - d = (int)(ch = *s1++) - (int)*s2++; - if ( d || !ch ) - break; - } - - return d; -} - -char16_t *strcpy16(char16_t *dst, const char16_t *src) -{ - char16_t *q = dst; - const char16_t *p = src; - char16_t ch; - - do { - *q++ = ch = *p++; - } while ( ch ); - - return dst; -} - -size_t strlen16(const char16_t *s) -{ - const char16_t *ss = s; - while ( *ss ) - ss++; - return ss-s; -} - - -char16_t *strncpy16(char16_t *dst, const char16_t *src, size_t n) -{ - char16_t *q = dst; - const char16_t *p = src; - char ch; - - while (n) { - n--; - *q++ = ch = *p++; - if ( !ch ) - break; - } - - *q = 0; - - return dst; -} - -size_t strnlen16(const char16_t *s, size_t maxlen) -{ - const char16_t *ss = s; - - /* Important: the maxlen test must precede the reference through ss; - since the byte beyond the maximum may segfault */ - while ((maxlen > 0) && *ss) { - ss++; - maxlen--; - } - return ss-s; -} - -int strzcmp16(const char16_t *s1, size_t n1, const char16_t *s2, size_t n2) -{ - const char16_t* e1 = s1+n1; - const char16_t* e2 = s2+n2; - - while (s1 < e1 && s2 < e2) { - const int d = (int)*s1++ - (int)*s2++; - if (d) { - return d; - } - } - - return n1 < n2 - ? (0 - (int)*s2) - : (n1 > n2 - ? ((int)*s1 - 0) - : 0); -} - -int strzcmp16_h_n(const char16_t *s1H, size_t n1, const char16_t *s2N, size_t n2) -{ - const char16_t* e1 = s1H+n1; - const char16_t* e2 = s2N+n2; - - while (s1H < e1 && s2N < e2) { - const char16_t c2 = ntohs(*s2N); - const int d = (int)*s1H++ - (int)c2; - s2N++; - if (d) { - return d; - } - } - - return n1 < n2 - ? (0 - (int)ntohs(*s2N)) - : (n1 > n2 - ? ((int)*s1H - 0) - : 0); -} - -static inline size_t -utf8_char_len(uint8_t ch) -{ - return ((0xe5000000 >> ((ch >> 3) & 0x1e)) & 3) + 1; -} - -#define UTF8_SHIFT_AND_MASK(unicode, byte) (unicode)<<=6; (unicode) |= (0x3f & (byte)); - -static inline uint32_t -utf8_to_utf32(const uint8_t *src, size_t length) -{ - uint32_t unicode; - - switch (length) - { - case 1: - return src[0]; - case 2: - unicode = src[0] & 0x1f; - UTF8_SHIFT_AND_MASK(unicode, src[1]) - return unicode; - case 3: - unicode = src[0] & 0x0f; - UTF8_SHIFT_AND_MASK(unicode, src[1]) - UTF8_SHIFT_AND_MASK(unicode, src[2]) - return unicode; - case 4: - unicode = src[0] & 0x07; - UTF8_SHIFT_AND_MASK(unicode, src[1]) - UTF8_SHIFT_AND_MASK(unicode, src[2]) - UTF8_SHIFT_AND_MASK(unicode, src[3]) - return unicode; - default: - return 0xffff; - } - - //printf("Char at %p: len=%d, utf-16=%p\n", src, length, (void*)result); -} - -void -utf8_to_utf16(const uint8_t *src, size_t srcLen, - char16_t* dst, const size_t dstLen) -{ - const uint8_t* const end = src + srcLen; - const char16_t* const dstEnd = dst + dstLen; - while (src < end && dst < dstEnd) { - size_t len = utf8_char_len(*src); - uint32_t codepoint = utf8_to_utf32((const uint8_t*)src, len); - - // Convert the UTF32 codepoint to one or more UTF16 codepoints - if (codepoint <= 0xFFFF) { - // Single UTF16 character - *dst++ = (char16_t) codepoint; - } else { - // Multiple UTF16 characters with surrogates - codepoint = codepoint - 0x10000; - *dst++ = (char16_t) ((codepoint >> 10) + 0xD800); - *dst++ = (char16_t) ((codepoint & 0x3FF) + 0xDC00); - } - - src += len; - } - if (dst < dstEnd) { - *dst = 0; - } -} - -// --------------------------------------------------------------------------- namespace android { @@ -270,37 +59,33 @@ void terminate_string16() // --------------------------------------------------------------------------- -static char16_t* allocFromUTF8(const char* in, size_t len) +static char16_t* allocFromUTF8(const char* u8str, size_t u8len) { - if (len == 0) return getEmptyString(); - - size_t chars = 0; - const char* end = in+len; - const char* p = in; - - while (p < end) { - chars++; - int utf8len = utf8_char_len(*p); - uint32_t codepoint = utf8_to_utf32((const uint8_t*)p, utf8len); - if (codepoint > 0xFFFF) chars++; // this will be a surrogate pair in utf16 - p += utf8len; + if (u8len == 0) return getEmptyString(); + + const uint8_t* u8cur = (const uint8_t*) u8str; + + const ssize_t u16len = utf8_to_utf16_length(u8cur, u8len); + if (u16len < 0) { + return getEmptyString(); } - - size_t bufSize = (chars+1)*sizeof(char16_t); - SharedBuffer* buf = SharedBuffer::alloc(bufSize); + + const uint8_t* const u8end = u8cur + u8len; + + SharedBuffer* buf = SharedBuffer::alloc(sizeof(char16_t)*(u16len+1)); if (buf) { - p = in; - char16_t* str = (char16_t*)buf->data(); - - utf8_to_utf16((const uint8_t*)p, len, str, bufSize); + u8cur = (const uint8_t*) u8str; + char16_t* u16str = (char16_t*)buf->data(); + + utf8_to_utf16(u8cur, u8len, u16str); //printf("Created UTF-16 string from UTF-8 \"%s\":", in); //printHexData(1, str, buf->size(), 16, 1); //printf("\n"); - return str; + return u16str; } - + return getEmptyString(); } diff --git a/libs/utils/String8.cpp b/libs/utils/String8.cpp index 6358fc424..c8dc0838d 100644 --- a/libs/utils/String8.cpp +++ b/libs/utils/String8.cpp @@ -17,6 +17,8 @@ #include #include +#include +#include #include #include #include @@ -34,94 +36,10 @@ namespace android { -static const char32_t kByteMask = 0x000000BF; -static const char32_t kByteMark = 0x00000080; - -// Surrogates aren't valid for UTF-32 characters, so define some -// constants that will let us screen them out. -static const char32_t kUnicodeSurrogateHighStart = 0x0000D800; -static const char32_t kUnicodeSurrogateHighEnd = 0x0000DBFF; -static const char32_t kUnicodeSurrogateLowStart = 0x0000DC00; -static const char32_t kUnicodeSurrogateLowEnd = 0x0000DFFF; -static const char32_t kUnicodeSurrogateStart = kUnicodeSurrogateHighStart; -static const char32_t kUnicodeSurrogateEnd = kUnicodeSurrogateLowEnd; -static const char32_t kUnicodeMaxCodepoint = 0x0010FFFF; - -// Mask used to set appropriate bits in first byte of UTF-8 sequence, -// indexed by number of bytes in the sequence. -// 0xxxxxxx -// -> (00-7f) 7bit. Bit mask for the first byte is 0x00000000 -// 110yyyyx 10xxxxxx -// -> (c0-df)(80-bf) 11bit. Bit mask is 0x000000C0 -// 1110yyyy 10yxxxxx 10xxxxxx -// -> (e0-ef)(80-bf)(80-bf) 16bit. Bit mask is 0x000000E0 -// 11110yyy 10yyxxxx 10xxxxxx 10xxxxxx -// -> (f0-f7)(80-bf)(80-bf)(80-bf) 21bit. Bit mask is 0x000000F0 -static const char32_t kFirstByteMark[] = { - 0x00000000, 0x00000000, 0x000000C0, 0x000000E0, 0x000000F0 -}; - // Separator used by resource paths. This is not platform dependent contrary // to OS_PATH_SEPARATOR. #define RES_PATH_SEPARATOR '/' -// Return number of utf8 bytes required for the character. -static size_t utf32_to_utf8_bytes(char32_t srcChar) -{ - size_t bytesToWrite; - - // Figure out how many bytes the result will require. - if (srcChar < 0x00000080) - { - bytesToWrite = 1; - } - else if (srcChar < 0x00000800) - { - bytesToWrite = 2; - } - else if (srcChar < 0x00010000) - { - if ((srcChar < kUnicodeSurrogateStart) - || (srcChar > kUnicodeSurrogateEnd)) - { - bytesToWrite = 3; - } - else - { - // Surrogates are invalid UTF-32 characters. - return 0; - } - } - // Max code point for Unicode is 0x0010FFFF. - else if (srcChar <= kUnicodeMaxCodepoint) - { - bytesToWrite = 4; - } - else - { - // Invalid UTF-32 character. - return 0; - } - - return bytesToWrite; -} - -// Write out the source character to . - -static void utf32_to_utf8(uint8_t* dstP, char32_t srcChar, size_t bytes) -{ - dstP += bytes; - switch (bytes) - { /* note: everything falls through. */ - case 4: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6; - case 3: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6; - case 2: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6; - case 1: *--dstP = (uint8_t)(srcChar | kFirstByteMark[bytes]); - } -} - -// --------------------------------------------------------------------------- - static SharedBuffer* gEmptyStringBuf = NULL; static char* gEmptyString = NULL; @@ -175,62 +93,47 @@ static char* allocFromUTF8(const char* in, size_t len) return getEmptyString(); } -template -static char* allocFromUTF16OrUTF32(const T* in, L len) -{ - if (len == 0) return getEmptyString(); - - size_t bytes = 0; - const T* end = in+len; - const T* p = in; - - while (p < end) { - bytes += utf32_to_utf8_bytes(*p); - p++; - } - - SharedBuffer* buf = SharedBuffer::alloc(bytes+1); - LOG_ASSERT(buf, "Unable to allocate shared buffer"); - if (buf) { - p = in; - char* str = (char*)buf->data(); - char* d = str; - while (p < end) { - const T c = *p++; - size_t len = utf32_to_utf8_bytes(c); - utf32_to_utf8((uint8_t*)d, c, len); - d += len; - } - *d = 0; - - return str; - } - - return getEmptyString(); -} - static char* allocFromUTF16(const char16_t* in, size_t len) { if (len == 0) return getEmptyString(); - const size_t bytes = utf8_length_from_utf16(in, len); + const ssize_t bytes = utf16_to_utf8_length(in, len); + if (bytes < 0) { + return getEmptyString(); + } SharedBuffer* buf = SharedBuffer::alloc(bytes+1); LOG_ASSERT(buf, "Unable to allocate shared buffer"); - if (buf) { - char* str = (char*)buf->data(); - - utf16_to_utf8(in, len, str, bytes+1); - - return str; + if (!buf) { + return getEmptyString(); } - return getEmptyString(); + char* str = (char*)buf->data(); + utf16_to_utf8(in, len, str); + return str; } static char* allocFromUTF32(const char32_t* in, size_t len) { - return allocFromUTF16OrUTF32(in, len); + if (len == 0) { + return getEmptyString(); + } + + const ssize_t bytes = utf32_to_utf8_length(in, len); + if (bytes < 0) { + return getEmptyString(); + } + + SharedBuffer* buf = SharedBuffer::alloc(bytes+1); + LOG_ASSERT(buf, "Unable to allocate shared buffer"); + if (!buf) { + return getEmptyString(); + } + + char* str = (char*) buf->data(); + utf32_to_utf8(in, len, str); + + return str; } // --------------------------------------------------------------------------- @@ -510,17 +413,17 @@ void String8::toUpper(size_t start, size_t length) size_t String8::getUtf32Length() const { - return utf32_length(mString, length()); + return utf8_to_utf32_length(mString, length()); } int32_t String8::getUtf32At(size_t index, size_t *next_index) const { - return utf32_at(mString, length(), index, next_index); + return utf32_from_utf8_at(mString, length(), index, next_index); } -size_t String8::getUtf32(char32_t* dst, size_t dst_len) const +void String8::getUtf32(char32_t* dst) const { - return utf8_to_utf32(mString, length(), dst, dst_len); + utf8_to_utf32(mString, length(), dst); } TextOutput& operator<<(TextOutput& to, const String8& val) @@ -705,241 +608,3 @@ String8& String8::convertToResPath() } }; // namespace android - -// --------------------------------------------------------------------------- - -size_t strlen32(const char32_t *s) -{ - const char32_t *ss = s; - while ( *ss ) - ss++; - return ss-s; -} - -size_t strnlen32(const char32_t *s, size_t maxlen) -{ - const char32_t *ss = s; - while ((maxlen > 0) && *ss) { - ss++; - maxlen--; - } - return ss-s; -} - -size_t utf8_length(const char *src) -{ - const char *cur = src; - size_t ret = 0; - while (*cur != '\0') { - const char first_char = *cur++; - if ((first_char & 0x80) == 0) { // ASCII - ret += 1; - continue; - } - // (UTF-8's character must not be like 10xxxxxx, - // but 110xxxxx, 1110xxxx, ... or 1111110x) - if ((first_char & 0x40) == 0) { - return 0; - } - - int32_t mask, to_ignore_mask; - size_t num_to_read = 0; - char32_t utf32 = 0; - for (num_to_read = 1, mask = 0x40, to_ignore_mask = 0x80; - num_to_read < 5 && (first_char & mask); - num_to_read++, to_ignore_mask |= mask, mask >>= 1) { - if ((*cur & 0xC0) != 0x80) { // must be 10xxxxxx - return 0; - } - // 0x3F == 00111111 - utf32 = (utf32 << 6) + (*cur++ & 0x3F); - } - // "first_char" must be (110xxxxx - 11110xxx) - if (num_to_read == 5) { - return 0; - } - to_ignore_mask |= mask; - utf32 |= ((~to_ignore_mask) & first_char) << (6 * (num_to_read - 1)); - if (utf32 > android::kUnicodeMaxCodepoint) { - return 0; - } - - ret += num_to_read; - } - return ret; -} - -size_t utf32_length(const char *src, size_t src_len) -{ - if (src == NULL || src_len == 0) { - return 0; - } - size_t ret = 0; - const char* cur; - const char* end; - size_t num_to_skip; - for (cur = src, end = src + src_len, num_to_skip = 1; - cur < end; - cur += num_to_skip, ret++) { - const char first_char = *cur; - num_to_skip = 1; - if ((first_char & 0x80) == 0) { // ASCII - continue; - } - int32_t mask; - - for (mask = 0x40; (first_char & mask); num_to_skip++, mask >>= 1) { - } - } - return ret; -} - -size_t utf8_length_from_utf32(const char32_t *src, size_t src_len) -{ - if (src == NULL || src_len == 0) { - return 0; - } - size_t ret = 0; - const char32_t *end = src + src_len; - while (src < end) { - ret += android::utf32_to_utf8_bytes(*src++); - } - return ret; -} - -size_t utf8_length_from_utf16(const char16_t *src, size_t src_len) -{ - if (src == NULL || src_len == 0) { - return 0; - } - size_t ret = 0; - const char16_t* const end = src + src_len; - while (src < end) { - if ((*src & 0xFC00) == 0xD800 && (src + 1) < end - && (*++src & 0xFC00) == 0xDC00) { - // surrogate pairs are always 4 bytes. - ret += 4; - src++; - } else { - ret += android::utf32_to_utf8_bytes((char32_t) *src++); - } - } - return ret; -} - -static int32_t utf32_at_internal(const char* cur, size_t *num_read) -{ - const char first_char = *cur; - if ((first_char & 0x80) == 0) { // ASCII - *num_read = 1; - return *cur; - } - cur++; - char32_t mask, to_ignore_mask; - size_t num_to_read = 0; - char32_t utf32 = first_char; - for (num_to_read = 1, mask = 0x40, to_ignore_mask = 0xFFFFFF80; - (first_char & mask); - num_to_read++, to_ignore_mask |= mask, mask >>= 1) { - // 0x3F == 00111111 - utf32 = (utf32 << 6) + (*cur++ & 0x3F); - } - to_ignore_mask |= mask; - utf32 &= ~(to_ignore_mask << (6 * (num_to_read - 1))); - - *num_read = num_to_read; - return static_cast(utf32); -} - -int32_t utf32_at(const char *src, size_t src_len, - size_t index, size_t *next_index) -{ - if (index >= src_len) { - return -1; - } - size_t dummy_index; - if (next_index == NULL) { - next_index = &dummy_index; - } - size_t num_read; - int32_t ret = utf32_at_internal(src + index, &num_read); - if (ret >= 0) { - *next_index = index + num_read; - } - - return ret; -} - -size_t utf8_to_utf32(const char* src, size_t src_len, - char32_t* dst, size_t dst_len) -{ - if (src == NULL || src_len == 0 || dst == NULL || dst_len == 0) { - return 0; - } - - const char* cur = src; - const char* end = src + src_len; - char32_t* cur_utf32 = dst; - const char32_t* end_utf32 = dst + dst_len; - while (cur_utf32 < end_utf32 && cur < end) { - size_t num_read; - *cur_utf32++ = - static_cast(utf32_at_internal(cur, &num_read)); - cur += num_read; - } - if (cur_utf32 < end_utf32) { - *cur_utf32 = 0; - } - return static_cast(cur_utf32 - dst); -} - -size_t utf32_to_utf8(const char32_t* src, size_t src_len, - char* dst, size_t dst_len) -{ - if (src == NULL || src_len == 0 || dst == NULL || dst_len == 0) { - return 0; - } - const char32_t *cur_utf32 = src; - const char32_t *end_utf32 = src + src_len; - char *cur = dst; - const char *end = dst + dst_len; - while (cur_utf32 < end_utf32 && cur < end) { - size_t len = android::utf32_to_utf8_bytes(*cur_utf32); - android::utf32_to_utf8((uint8_t *)cur, *cur_utf32++, len); - cur += len; - } - if (cur < end) { - *cur = '\0'; - } - return cur - dst; -} - -size_t utf16_to_utf8(const char16_t* src, size_t src_len, - char* dst, size_t dst_len) -{ - if (src == NULL || src_len == 0 || dst == NULL || dst_len == 0) { - return 0; - } - const char16_t* cur_utf16 = src; - const char16_t* const end_utf16 = src + src_len; - char *cur = dst; - const char* const end = dst + dst_len; - while (cur_utf16 < end_utf16 && cur < end) { - char32_t utf32; - // surrogate pairs - if ((*cur_utf16 & 0xFC00) == 0xD800 && (cur_utf16 + 1) < end_utf16) { - utf32 = (*cur_utf16++ - 0xD800) << 10; - utf32 |= *cur_utf16++ - 0xDC00; - utf32 += 0x10000; - } else { - utf32 = (char32_t) *cur_utf16++; - } - size_t len = android::utf32_to_utf8_bytes(utf32); - android::utf32_to_utf8((uint8_t*)cur, utf32, len); - cur += len; - } - if (cur < end) { - *cur = '\0'; - } - return cur - dst; -} diff --git a/libs/utils/Unicode.cpp b/libs/utils/Unicode.cpp new file mode 100644 index 000000000..78c61b4fc --- /dev/null +++ b/libs/utils/Unicode.cpp @@ -0,0 +1,575 @@ +/* + * Copyright (C) 2005 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 + +#include + +#ifdef HAVE_WINSOCK +# undef nhtol +# undef htonl +# undef nhtos +# undef htons + +# ifdef HAVE_LITTLE_ENDIAN +# define ntohl(x) ( ((x) << 24) | (((x) >> 24) & 255) | (((x) << 8) & 0xff0000) | (((x) >> 8) & 0xff00) ) +# define htonl(x) ntohl(x) +# define ntohs(x) ( (((x) << 8) & 0xff00) | (((x) >> 8) & 255) ) +# define htons(x) ntohs(x) +# else +# define ntohl(x) (x) +# define htonl(x) (x) +# define ntohs(x) (x) +# define htons(x) (x) +# endif +#else +# include +#endif + +extern "C" { + +static const char32_t kByteMask = 0x000000BF; +static const char32_t kByteMark = 0x00000080; + +// Surrogates aren't valid for UTF-32 characters, so define some +// constants that will let us screen them out. +static const char32_t kUnicodeSurrogateHighStart = 0x0000D800; +static const char32_t kUnicodeSurrogateHighEnd = 0x0000DBFF; +static const char32_t kUnicodeSurrogateLowStart = 0x0000DC00; +static const char32_t kUnicodeSurrogateLowEnd = 0x0000DFFF; +static const char32_t kUnicodeSurrogateStart = kUnicodeSurrogateHighStart; +static const char32_t kUnicodeSurrogateEnd = kUnicodeSurrogateLowEnd; +static const char32_t kUnicodeMaxCodepoint = 0x0010FFFF; + +// Mask used to set appropriate bits in first byte of UTF-8 sequence, +// indexed by number of bytes in the sequence. +// 0xxxxxxx +// -> (00-7f) 7bit. Bit mask for the first byte is 0x00000000 +// 110yyyyx 10xxxxxx +// -> (c0-df)(80-bf) 11bit. Bit mask is 0x000000C0 +// 1110yyyy 10yxxxxx 10xxxxxx +// -> (e0-ef)(80-bf)(80-bf) 16bit. Bit mask is 0x000000E0 +// 11110yyy 10yyxxxx 10xxxxxx 10xxxxxx +// -> (f0-f7)(80-bf)(80-bf)(80-bf) 21bit. Bit mask is 0x000000F0 +static const char32_t kFirstByteMark[] = { + 0x00000000, 0x00000000, 0x000000C0, 0x000000E0, 0x000000F0 +}; + +// -------------------------------------------------------------------------- +// UTF-32 +// -------------------------------------------------------------------------- + +/** + * Return number of UTF-8 bytes required for the character. If the character + * is invalid, return size of 0. + */ +static inline size_t utf32_codepoint_utf8_length(char32_t srcChar) +{ + // Figure out how many bytes the result will require. + if (srcChar < 0x00000080) { + return 1; + } else if (srcChar < 0x00000800) { + return 2; + } else if (srcChar < 0x00010000) { + if ((srcChar < kUnicodeSurrogateStart) || (srcChar > kUnicodeSurrogateEnd)) { + return 3; + } else { + // Surrogates are invalid UTF-32 characters. + return 0; + } + } + // Max code point for Unicode is 0x0010FFFF. + else if (srcChar <= kUnicodeMaxCodepoint) { + return 4; + } else { + // Invalid UTF-32 character. + return 0; + } +} + +// Write out the source character to . + +static inline void utf32_codepoint_to_utf8(uint8_t* dstP, char32_t srcChar, size_t bytes) +{ + dstP += bytes; + switch (bytes) + { /* note: everything falls through. */ + case 4: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6; + case 3: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6; + case 2: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6; + case 1: *--dstP = (uint8_t)(srcChar | kFirstByteMark[bytes]); + } +} + +size_t strlen32(const char32_t *s) +{ + const char32_t *ss = s; + while ( *ss ) + ss++; + return ss-s; +} + +size_t strnlen32(const char32_t *s, size_t maxlen) +{ + const char32_t *ss = s; + while ((maxlen > 0) && *ss) { + ss++; + maxlen--; + } + return ss-s; +} + +static inline int32_t utf32_at_internal(const char* cur, size_t *num_read) +{ + const char first_char = *cur; + if ((first_char & 0x80) == 0) { // ASCII + *num_read = 1; + return *cur; + } + cur++; + char32_t mask, to_ignore_mask; + size_t num_to_read = 0; + char32_t utf32 = first_char; + for (num_to_read = 1, mask = 0x40, to_ignore_mask = 0xFFFFFF80; + (first_char & mask); + num_to_read++, to_ignore_mask |= mask, mask >>= 1) { + // 0x3F == 00111111 + utf32 = (utf32 << 6) + (*cur++ & 0x3F); + } + to_ignore_mask |= mask; + utf32 &= ~(to_ignore_mask << (6 * (num_to_read - 1))); + + *num_read = num_to_read; + return static_cast(utf32); +} + +int32_t utf32_from_utf8_at(const char *src, size_t src_len, size_t index, size_t *next_index) +{ + if (index >= src_len) { + return -1; + } + size_t dummy_index; + if (next_index == NULL) { + next_index = &dummy_index; + } + size_t num_read; + int32_t ret = utf32_at_internal(src + index, &num_read); + if (ret >= 0) { + *next_index = index + num_read; + } + + return ret; +} + +ssize_t utf32_to_utf8_length(const char32_t *src, size_t src_len) +{ + if (src == NULL || src_len == 0) { + return -1; + } + + size_t ret = 0; + const char32_t *end = src + src_len; + while (src < end) { + ret += utf32_codepoint_utf8_length(*src++); + } + return ret; +} + +void utf32_to_utf8(const char32_t* src, size_t src_len, char* dst) +{ + if (src == NULL || src_len == 0 || dst == NULL) { + return; + } + + const char32_t *cur_utf32 = src; + const char32_t *end_utf32 = src + src_len; + char *cur = dst; + while (cur_utf32 < end_utf32) { + size_t len = utf32_codepoint_utf8_length(*cur_utf32); + utf32_codepoint_to_utf8((uint8_t *)cur, *cur_utf32++, len); + cur += len; + } + *cur = '\0'; +} + +// -------------------------------------------------------------------------- +// UTF-16 +// -------------------------------------------------------------------------- + +int strcmp16(const char16_t *s1, const char16_t *s2) +{ + char16_t ch; + int d = 0; + + while ( 1 ) { + d = (int)(ch = *s1++) - (int)*s2++; + if ( d || !ch ) + break; + } + + return d; +} + +int strncmp16(const char16_t *s1, const char16_t *s2, size_t n) +{ + char16_t ch; + int d = 0; + + while ( n-- ) { + d = (int)(ch = *s1++) - (int)*s2++; + if ( d || !ch ) + break; + } + + return d; +} + +char16_t *strcpy16(char16_t *dst, const char16_t *src) +{ + char16_t *q = dst; + const char16_t *p = src; + char16_t ch; + + do { + *q++ = ch = *p++; + } while ( ch ); + + return dst; +} + +size_t strlen16(const char16_t *s) +{ + const char16_t *ss = s; + while ( *ss ) + ss++; + return ss-s; +} + + +char16_t *strncpy16(char16_t *dst, const char16_t *src, size_t n) +{ + char16_t *q = dst; + const char16_t *p = src; + char ch; + + while (n) { + n--; + *q++ = ch = *p++; + if ( !ch ) + break; + } + + *q = 0; + + return dst; +} + +size_t strnlen16(const char16_t *s, size_t maxlen) +{ + const char16_t *ss = s; + + /* Important: the maxlen test must precede the reference through ss; + since the byte beyond the maximum may segfault */ + while ((maxlen > 0) && *ss) { + ss++; + maxlen--; + } + return ss-s; +} + +int strzcmp16(const char16_t *s1, size_t n1, const char16_t *s2, size_t n2) +{ + const char16_t* e1 = s1+n1; + const char16_t* e2 = s2+n2; + + while (s1 < e1 && s2 < e2) { + const int d = (int)*s1++ - (int)*s2++; + if (d) { + return d; + } + } + + return n1 < n2 + ? (0 - (int)*s2) + : (n1 > n2 + ? ((int)*s1 - 0) + : 0); +} + +int strzcmp16_h_n(const char16_t *s1H, size_t n1, const char16_t *s2N, size_t n2) +{ + const char16_t* e1 = s1H+n1; + const char16_t* e2 = s2N+n2; + + while (s1H < e1 && s2N < e2) { + const char16_t c2 = ntohs(*s2N); + const int d = (int)*s1H++ - (int)c2; + s2N++; + if (d) { + return d; + } + } + + return n1 < n2 + ? (0 - (int)ntohs(*s2N)) + : (n1 > n2 + ? ((int)*s1H - 0) + : 0); +} + +void utf16_to_utf8(const char16_t* src, size_t src_len, char* dst) +{ + if (src == NULL || src_len == 0 || dst == NULL) { + return; + } + + const char16_t* cur_utf16 = src; + const char16_t* const end_utf16 = src + src_len; + char *cur = dst; + while (cur_utf16 < end_utf16) { + char32_t utf32; + // surrogate pairs + if ((*cur_utf16 & 0xFC00) == 0xD800) { + utf32 = (*cur_utf16++ - 0xD800) << 10; + utf32 |= *cur_utf16++ - 0xDC00; + utf32 += 0x10000; + } else { + utf32 = (char32_t) *cur_utf16++; + } + const size_t len = utf32_codepoint_utf8_length(utf32); + utf32_codepoint_to_utf8((uint8_t*)cur, utf32, len); + cur += len; + } + *cur = '\0'; +} + +// -------------------------------------------------------------------------- +// UTF-8 +// -------------------------------------------------------------------------- + +ssize_t utf8_length(const char *src) +{ + const char *cur = src; + size_t ret = 0; + while (*cur != '\0') { + const char first_char = *cur++; + if ((first_char & 0x80) == 0) { // ASCII + ret += 1; + continue; + } + // (UTF-8's character must not be like 10xxxxxx, + // but 110xxxxx, 1110xxxx, ... or 1111110x) + if ((first_char & 0x40) == 0) { + return -1; + } + + int32_t mask, to_ignore_mask; + size_t num_to_read = 0; + char32_t utf32 = 0; + for (num_to_read = 1, mask = 0x40, to_ignore_mask = 0x80; + num_to_read < 5 && (first_char & mask); + num_to_read++, to_ignore_mask |= mask, mask >>= 1) { + if ((*cur & 0xC0) != 0x80) { // must be 10xxxxxx + return -1; + } + // 0x3F == 00111111 + utf32 = (utf32 << 6) + (*cur++ & 0x3F); + } + // "first_char" must be (110xxxxx - 11110xxx) + if (num_to_read == 5) { + return -1; + } + to_ignore_mask |= mask; + utf32 |= ((~to_ignore_mask) & first_char) << (6 * (num_to_read - 1)); + if (utf32 > kUnicodeMaxCodepoint) { + return -1; + } + + ret += num_to_read; + } + return ret; +} + +ssize_t utf16_to_utf8_length(const char16_t *src, size_t src_len) +{ + if (src == NULL || src_len == 0) { + return -1; + } + + size_t ret = 0; + const char16_t* const end = src + src_len; + while (src < end) { + if ((*src & 0xFC00) == 0xD800 && (src + 1) < end + && (*++src & 0xFC00) == 0xDC00) { + // surrogate pairs are always 4 bytes. + ret += 4; + src++; + } else { + ret += utf32_codepoint_utf8_length((char32_t) *src++); + } + } + return ret; +} + +/** + * Returns 1-4 based on the number of leading bits. + * + * 1111 -> 4 + * 1110 -> 3 + * 110x -> 2 + * 10xx -> 1 + * 0xxx -> 1 + */ +static inline size_t utf8_codepoint_len(uint8_t ch) +{ + return ((0xe5000000 >> ((ch >> 3) & 0x1e)) & 3) + 1; +} + +static inline void utf8_shift_and_mask(uint32_t* codePoint, const uint8_t byte) +{ + *codePoint <<= 6; + *codePoint |= 0x3F & byte; +} + +size_t utf8_to_utf32_length(const char *src, size_t src_len) +{ + if (src == NULL || src_len == 0) { + return 0; + } + size_t ret = 0; + const char* cur; + const char* end; + size_t num_to_skip; + for (cur = src, end = src + src_len, num_to_skip = 1; + cur < end; + cur += num_to_skip, ret++) { + const char first_char = *cur; + num_to_skip = 1; + if ((first_char & 0x80) == 0) { // ASCII + continue; + } + int32_t mask; + + for (mask = 0x40; (first_char & mask); num_to_skip++, mask >>= 1) { + } + } + return ret; +} + +void utf8_to_utf32(const char* src, size_t src_len, char32_t* dst) +{ + if (src == NULL || src_len == 0 || dst == NULL) { + return; + } + + const char* cur = src; + const char* const end = src + src_len; + char32_t* cur_utf32 = dst; + while (cur < end) { + size_t num_read; + *cur_utf32++ = static_cast(utf32_at_internal(cur, &num_read)); + cur += num_read; + } + *cur_utf32 = 0; +} + +static inline uint32_t utf8_to_utf32_codepoint(const uint8_t *src, size_t length) +{ + uint32_t unicode; + + switch (length) + { + case 1: + return src[0]; + case 2: + unicode = src[0] & 0x1f; + utf8_shift_and_mask(&unicode, src[1]); + return unicode; + case 3: + unicode = src[0] & 0x0f; + utf8_shift_and_mask(&unicode, src[1]); + utf8_shift_and_mask(&unicode, src[2]); + return unicode; + case 4: + unicode = src[0] & 0x07; + utf8_shift_and_mask(&unicode, src[1]); + utf8_shift_and_mask(&unicode, src[2]); + utf8_shift_and_mask(&unicode, src[3]); + return unicode; + default: + return 0xffff; + } + + //printf("Char at %p: len=%d, utf-16=%p\n", src, length, (void*)result); +} + +ssize_t utf8_to_utf16_length(const uint8_t* u8str, size_t u8len) +{ + const uint8_t* const u8end = u8str + u8len; + const uint8_t* u8cur = u8str; + + /* Validate that the UTF-8 is the correct len */ + size_t u16measuredLen = 0; + while (u8cur < u8end) { + u16measuredLen++; + int u8charLen = utf8_codepoint_len(*u8cur); + uint32_t codepoint = utf8_to_utf32_codepoint(u8cur, u8charLen); + if (codepoint > 0xFFFF) u16measuredLen++; // this will be a surrogate pair in utf16 + u8cur += u8charLen; + } + + /** + * Make sure that we ended where we thought we would and the output UTF-16 + * will be exactly how long we were told it would be. + */ + if (u8cur != u8end) { + return -1; + } + + return u16measuredLen; +} + +/** + * Convert a UTF-8 string to UTF-16. The destination UTF-16 buffer must have + * space for NULL at the end. + */ +void utf8_to_utf16(const uint8_t* u8str, size_t u8len, char16_t* u16str) +{ + const uint8_t* const u8end = u8str + u8len; + const uint8_t* u8cur = u8str; + char16_t* u16cur = u16str; + + while (u8cur < u8end) { + size_t u8len = utf8_codepoint_len(*u8cur); + uint32_t codepoint = utf8_to_utf32_codepoint(u8cur, u8len); + + // Convert the UTF32 codepoint to one or more UTF16 codepoints + if (codepoint <= 0xFFFF) { + // Single UTF16 character + *u16cur++ = (char16_t) codepoint; + } else { + // Multiple UTF16 characters with surrogates + codepoint = codepoint - 0x10000; + *u16cur++ = (char16_t) ((codepoint >> 10) + 0xD800); + *u16cur++ = (char16_t) ((codepoint & 0x3FF) + 0xDC00); + } + + u8cur += u8len; + } + *u16cur = 0; +} + +} diff --git a/libs/utils/tests/Android.mk b/libs/utils/tests/Android.mk index 00077eecf..72d48769a 100644 --- a/libs/utils/tests/Android.mk +++ b/libs/utils/tests/Android.mk @@ -8,7 +8,8 @@ ifneq ($(TARGET_SIMULATOR),true) test_src_files := \ ObbFile_test.cpp \ Looper_test.cpp \ - String8_test.cpp + String8_test.cpp \ + Unicode_test.cpp shared_libraries := \ libz \ diff --git a/libs/utils/tests/Unicode_test.cpp b/libs/utils/tests/Unicode_test.cpp new file mode 100644 index 000000000..18c130c55 --- /dev/null +++ b/libs/utils/tests/Unicode_test.cpp @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2010 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 "Unicode_test" +#include +#include + +#include + +namespace android { + +class UnicodeTest : public testing::Test { +protected: + virtual void SetUp() { + } + + virtual void TearDown() { + } +}; + +TEST_F(UnicodeTest, UTF8toUTF16ZeroLength) { + ssize_t measured; + + const uint8_t str[] = { }; + + measured = utf8_to_utf16_length(str, 0); + EXPECT_EQ(0, measured) + << "Zero length input should return zero length output."; +} + +TEST_F(UnicodeTest, UTF8toUTF16ASCIILength) { + ssize_t measured; + + // U+0030 or ASCII '0' + const uint8_t str[] = { 0x30 }; + + measured = utf8_to_utf16_length(str, sizeof(str)); + EXPECT_EQ(1, measured) + << "ASCII glyphs should have a length of 1 char16_t"; +} + +TEST_F(UnicodeTest, UTF8toUTF16Plane1Length) { + ssize_t measured; + + // U+2323 SMILE + const uint8_t str[] = { 0xE2, 0x8C, 0xA3 }; + + measured = utf8_to_utf16_length(str, sizeof(str)); + EXPECT_EQ(1, measured) + << "Plane 1 glyphs should have a length of 1 char16_t"; +} + +TEST_F(UnicodeTest, UTF8toUTF16SurrogateLength) { + ssize_t measured; + + // U+10000 + const uint8_t str[] = { 0xF0, 0x90, 0x80, 0x80 }; + + measured = utf8_to_utf16_length(str, sizeof(str)); + EXPECT_EQ(2, measured) + << "Surrogate pairs should have a length of 2 char16_t"; +} + +TEST_F(UnicodeTest, UTF8toUTF16TruncatedUTF8) { + ssize_t measured; + + // Truncated U+2323 SMILE + // U+2323 SMILE + const uint8_t str[] = { 0xE2, 0x8C }; + + measured = utf8_to_utf16_length(str, sizeof(str)); + EXPECT_EQ(-1, measured) + << "Truncated UTF-8 should return -1 to indicate invalid"; +} + +TEST_F(UnicodeTest, UTF8toUTF16Normal) { + const uint8_t str[] = { + 0x30, // U+0030, 1 UTF-16 character + 0xC4, 0x80, // U+0100, 1 UTF-16 character + 0xE2, 0x8C, 0xA3, // U+2323, 1 UTF-16 character + 0xF0, 0x90, 0x80, 0x80, // U+10000, 2 UTF-16 character + }; + + char16_t output[1 + 1 + 1 + 2 + 1]; // Room for NULL + + utf8_to_utf16(str, sizeof(str), output); + + EXPECT_EQ(0x0030, output[0]) + << "should be U+0030"; + EXPECT_EQ(0x0100, output[1]) + << "should be U+0100"; + EXPECT_EQ(0x2323, output[2]) + << "should be U+2323"; + EXPECT_EQ(0xD800, output[3]) + << "should be first half of surrogate U+10000"; + EXPECT_EQ(0xDC00, output[4]) + << "should be second half of surrogate U+10000"; + EXPECT_EQ(NULL, output[5]) + << "should be NULL terminated"; +} + +} From 647925ddf053989b641b4c5c8a51efd55c931f22 Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Wed, 10 Nov 2010 16:03:06 -0800 Subject: [PATCH 274/541] Added support for full PC-style keyboards. BREAKING CHANGE: Redesigned the key character map format to accomodate full keyboards with more comprehensive suite of modifiers. Old key character maps will not work anymore and must be updated. The new format is plain text only and it not compiled to a binary file (so the "kcm" tool will be removed in a subsequent check-in). Added FULL keyboard type to support full PC-style keyboards. Added SPECIAL_FUNCTION keyboard type to support special function keypads that do not have any printable keys suitable for typing and only have keys like HOME and POWER Added a special VIRTUAL_KEYBOARD device id convention that maps to a virtual keyboard with a fixed known layout. This is designed to work around issues injecting input events on devices whose built-in keyboard does not have a useful key character map (ie. when the built-in keyboard is a special function keyboard only.) Modified several places where events were being synthesized to use the virtual keyboard. Removed support for the "qwerty" default layout. The new default layout is "Generic". For the most part "qwerty" was being used as a backstop in case the built-in keyboard did not have a key character map (probably because it was a special function keypad) and the framework needed to be able to inject key events anyways. The latter issue is resolved by using the special VIRTUAL_KEYBOARD device instead of BUILT_IN_KEYBOARD. Added the concept of a key modifier behavior so that MetaKeyKeyListener can distinguish between keyboards that use chorded vs. toggled modifiers. Wrote more robust key layout and key character map parsers to enable support for new keyboard features and user installable key maps. Fixed a bug in InputReader generating key ups when keys are released out of sequence. Updated tons of documentation. Currently QwertyKeyListener is being used for full keyboards with autotext and capitalization disabled. This mostly works but causes some problems with character pickers, etc. These issues will be resolved in subsequent changes. Change-Id: Ica48f6097a551141c215bc0d2c6f7b3fb634d354 --- include/utils/String8.h | 2 + include/utils/Tokenizer.h | 123 +++++++++++++++++++++++++++++++ libs/utils/Android.mk | 1 + libs/utils/String8.cpp | 18 +++-- libs/utils/Tokenizer.cpp | 150 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 288 insertions(+), 6 deletions(-) create mode 100644 include/utils/Tokenizer.h create mode 100644 libs/utils/Tokenizer.cpp diff --git a/include/utils/String8.h b/include/utils/String8.h index b36f128f9..6abfb0634 100644 --- a/include/utils/String8.h +++ b/include/utils/String8.h @@ -22,6 +22,7 @@ #include #include // for strcmp +#include // --------------------------------------------------------------------------- @@ -70,6 +71,7 @@ public: status_t appendFormat(const char* fmt, ...) __attribute__((format (printf, 2, 3))); + status_t appendFormatV(const char* fmt, va_list args); // Note that this function takes O(N) time to calculate the value. // No cache value is stored. diff --git a/include/utils/Tokenizer.h b/include/utils/Tokenizer.h new file mode 100644 index 000000000..bfe89232d --- /dev/null +++ b/include/utils/Tokenizer.h @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2010 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 _UTILS_TOKENIZER_H +#define _UTILS_TOKENIZER_H + +#include +#include +#include + +namespace android { + +/** + * A simple tokenizer for loading and parsing ASCII text files line by line. + */ +class Tokenizer { + Tokenizer(const String8& filename, const char* buffer, size_t length); + +public: + ~Tokenizer(); + + /** + * Opens a file and maps it into memory. + * + * Returns NO_ERROR and a tokenizer for the file, if successful. + * Otherwise returns an error and sets outTokenizer to NULL. + */ + static status_t open(const String8& filename, Tokenizer** outTokenizer); + + /** + * Returns true if at the end of the file. + */ + inline bool isEof() const { return mCurrent == getEnd(); } + + /** + * Returns true if at the end of the line or end of the file. + */ + inline bool isEol() const { return isEof() || *mCurrent == '\n'; } + + /** + * Gets the name of the file. + */ + inline String8 getFilename() const { return mFilename; } + + /** + * Gets a 1-based line number index for the current position. + */ + inline int32_t getLineNumber() const { return mLineNumber; } + + /** + * Formats a location string consisting of the filename and current line number. + * Returns a string like "MyFile.txt:33". + */ + String8 getLocation() const; + + /** + * Gets the character at the current position. + * Returns null at end of file. + */ + inline char peekChar() const { return isEof() ? '\0' : *mCurrent; } + + /** + * Gets the remainder of the current line as a string, excluding the newline character. + */ + String8 peekRemainderOfLine() const; + + /** + * Gets the character at the current position and advances past it. + * Returns null at end of file. + */ + inline char nextChar() { return isEof() ? '\0' : *(mCurrent++); } + + /** + * Gets the next token on this line stopping at the specified delimiters + * or the end of the line whichever comes first and advances past it. + * Also stops at embedded nulls. + * Returns the token or an empty string if the current character is a delimiter + * or is at the end of the line. + */ + String8 nextToken(const char* delimiters); + + /** + * Advances to the next line. + * Does nothing if already at the end of the file. + */ + void nextLine(); + + /** + * Skips over the specified delimiters in the line. + * Also skips embedded nulls. + */ + void skipDelimiters(const char* delimiters); + +private: + Tokenizer(const Tokenizer& other); // not copyable + + String8 mFilename; + const char* mBuffer; + size_t mLength; + + const char* mCurrent; + int32_t mLineNumber; + + inline const char* getEnd() const { return mBuffer + mLength; } + +}; + +} // namespace android + +#endif // _UTILS_TOKENIZER_H diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk index 05a967491..9c01aea92 100644 --- a/libs/utils/Android.mk +++ b/libs/utils/Android.mk @@ -41,6 +41,7 @@ commonSources:= \ TextOutput.cpp \ Threads.cpp \ Timers.cpp \ + Tokenizer.cpp \ Unicode.cpp \ VectorImpl.cpp \ ZipFileCRO.cpp \ diff --git a/libs/utils/String8.cpp b/libs/utils/String8.cpp index c8dc0838d..e531a2a0f 100644 --- a/libs/utils/String8.cpp +++ b/libs/utils/String8.cpp @@ -282,22 +282,28 @@ status_t String8::append(const char* other, size_t otherLen) status_t String8::appendFormat(const char* fmt, ...) { - va_list ap; - va_start(ap, fmt); + va_list args; + va_start(args, fmt); + status_t result = appendFormatV(fmt, args); + + va_end(args); + return result; +} + +status_t String8::appendFormatV(const char* fmt, va_list args) +{ int result = NO_ERROR; - int n = vsnprintf(NULL, 0, fmt, ap); + int n = vsnprintf(NULL, 0, fmt, args); if (n != 0) { size_t oldLength = length(); char* buf = lockBuffer(oldLength + n); if (buf) { - vsnprintf(buf + oldLength, n + 1, fmt, ap); + vsnprintf(buf + oldLength, n + 1, fmt, args); } else { result = NO_MEMORY; } } - - va_end(ap); return result; } diff --git a/libs/utils/Tokenizer.cpp b/libs/utils/Tokenizer.cpp new file mode 100644 index 000000000..19dadf08d --- /dev/null +++ b/libs/utils/Tokenizer.cpp @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2010 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 "Tokenizer" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Enables debug output for the tokenizer. +#define DEBUG_TOKENIZER 0 + + +namespace android { + +static inline bool isDelimiter(char ch, const char* delimiters) { + return strchr(delimiters, ch) != NULL; +} + + +Tokenizer::Tokenizer(const String8& filename, const char* buffer, size_t length) : + mFilename(filename), mBuffer(buffer), mLength(length), + mCurrent(buffer), mLineNumber(1) { +} + +Tokenizer::~Tokenizer() { + munmap((void*)mBuffer, mLength); +} + +status_t Tokenizer::open(const String8& filename, Tokenizer** outTokenizer) { + *outTokenizer = NULL; + + int result = NO_ERROR; + int fd = ::open(filename.string(), O_RDONLY); + if (fd < 0) { + result = -errno; + LOGE("Error opening file '%s', %s.", filename.string(), strerror(errno)); + } else { + struct stat64 stat; + if (fstat64(fd, &stat)) { + result = -errno; + LOGE("Error getting size of file '%s', %s.", filename.string(), strerror(errno)); + } else { + size_t length = size_t(stat.st_size); + void* buffer = mmap(NULL, length, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0); + if (buffer == MAP_FAILED) { + result = -errno; + LOGE("Error mapping file '%s', %s.", filename.string(), strerror(errno)); + } else { + if (madvise(buffer, length, MADV_SEQUENTIAL)) { + LOGW("Error calling madvise for mmapped file '%s', %s.", filename.string(), + strerror(errno)); + } + + *outTokenizer = new Tokenizer(filename, static_cast(buffer), length); + if (!*outTokenizer) { + result = NO_MEMORY; + LOGE("Error allocating tokenizer for file=%s.", filename.string()); + munmap(buffer, length); + } + } + } + close(fd); + } + return result; +} + +String8 Tokenizer::getLocation() const { + String8 result; + result.appendFormat("%s:%d", mFilename.string(), mLineNumber); + return result; +} + +String8 Tokenizer::peekRemainderOfLine() const { + const char* end = getEnd(); + const char* eol = mCurrent; + while (eol != end) { + char ch = *eol; + if (ch == '\n') { + break; + } + eol += 1; + } + return String8(mCurrent, eol - mCurrent); +} + +String8 Tokenizer::nextToken(const char* delimiters) { +#if DEBUG_TOKENIZER + LOGD("nextToken"); +#endif + const char* end = getEnd(); + const char* tokenStart = mCurrent; + while (mCurrent != end) { + char ch = *mCurrent; + if (ch == '\n' || isDelimiter(ch, delimiters)) { + break; + } + mCurrent += 1; + } + return String8(tokenStart, mCurrent - tokenStart); +} + +void Tokenizer::nextLine() { +#if DEBUG_TOKENIZER + LOGD("nextLine"); +#endif + const char* end = getEnd(); + while (mCurrent != end) { + char ch = *(mCurrent++); + if (ch == '\n') { + mLineNumber += 1; + break; + } + } +} + +void Tokenizer::skipDelimiters(const char* delimiters) { +#if DEBUG_TOKENIZER + LOGD("skipDelimiters"); +#endif + const char* end = getEnd(); + while (mCurrent != end) { + char ch = *mCurrent; + if (ch == '\n' || !isDelimiter(ch, delimiters)) { + break; + } + mCurrent += 1; + } +} + +} // namespace android From d36ec3afdac0ca4158a786b96599ee8bdf64b043 Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Fri, 19 Nov 2010 13:13:07 -0800 Subject: [PATCH 275/541] Fix SDK build on Windows due to use of mmap. Change-Id: Id4bd9a6f932285c93c5853e540efc20b99876564 --- include/utils/Tokenizer.h | 4 +++- libs/utils/Tokenizer.cpp | 32 +++++++++++++++++--------------- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/include/utils/Tokenizer.h b/include/utils/Tokenizer.h index bfe89232d..21e58e62c 100644 --- a/include/utils/Tokenizer.h +++ b/include/utils/Tokenizer.h @@ -19,6 +19,7 @@ #include #include +#include #include namespace android { @@ -27,7 +28,7 @@ namespace android { * A simple tokenizer for loading and parsing ASCII text files line by line. */ class Tokenizer { - Tokenizer(const String8& filename, const char* buffer, size_t length); + Tokenizer(const String8& filename, FileMap* fileMap, const char* buffer, size_t length); public: ~Tokenizer(); @@ -108,6 +109,7 @@ private: Tokenizer(const Tokenizer& other); // not copyable String8 mFilename; + FileMap* mFileMap; const char* mBuffer; size_t mLength; diff --git a/libs/utils/Tokenizer.cpp b/libs/utils/Tokenizer.cpp index 19dadf08d..9251973ca 100644 --- a/libs/utils/Tokenizer.cpp +++ b/libs/utils/Tokenizer.cpp @@ -22,7 +22,6 @@ #include #include #include -#include #include #include @@ -37,13 +36,16 @@ static inline bool isDelimiter(char ch, const char* delimiters) { } -Tokenizer::Tokenizer(const String8& filename, const char* buffer, size_t length) : - mFilename(filename), mBuffer(buffer), mLength(length), +Tokenizer::Tokenizer(const String8& filename, FileMap* fileMap, + const char* buffer, size_t length) : + mFilename(filename), mFileMap(fileMap), mBuffer(buffer), mLength(length), mCurrent(buffer), mLineNumber(1) { } Tokenizer::~Tokenizer() { - munmap((void*)mBuffer, mLength); + if (mFileMap) { + mFileMap->release(); + } } status_t Tokenizer::open(const String8& filename, Tokenizer** outTokenizer) { @@ -55,29 +57,29 @@ status_t Tokenizer::open(const String8& filename, Tokenizer** outTokenizer) { result = -errno; LOGE("Error opening file '%s', %s.", filename.string(), strerror(errno)); } else { - struct stat64 stat; - if (fstat64(fd, &stat)) { + struct stat stat; + if (fstat(fd, &stat)) { result = -errno; LOGE("Error getting size of file '%s', %s.", filename.string(), strerror(errno)); } else { size_t length = size_t(stat.st_size); - void* buffer = mmap(NULL, length, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0); - if (buffer == MAP_FAILED) { - result = -errno; + FileMap* fileMap = new FileMap(); + if (!fileMap->create(NULL, fd, 0, length, true)) { + result = NO_MEMORY; LOGE("Error mapping file '%s', %s.", filename.string(), strerror(errno)); } else { - if (madvise(buffer, length, MADV_SEQUENTIAL)) { - LOGW("Error calling madvise for mmapped file '%s', %s.", filename.string(), - strerror(errno)); - } + fileMap->advise(FileMap::SEQUENTIAL); - *outTokenizer = new Tokenizer(filename, static_cast(buffer), length); + *outTokenizer = new Tokenizer(filename, fileMap, + static_cast(fileMap->getDataPtr()), length); if (!*outTokenizer) { result = NO_MEMORY; LOGE("Error allocating tokenizer for file=%s.", filename.string()); - munmap(buffer, length); } } + if (result) { + fileMap->release(); + } } close(fd); } From 04cbbc1c47c68e805d2674d9fc702fc44385805f Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Mon, 29 Nov 2010 17:37:49 -0800 Subject: [PATCH 276/541] Support non-orientation aware keyboards and other devices. Fixed a bug with dpad keys on external keyboards being rotated according to the display orientation by adding a new input device configuration property called "keyboard.orientationAware". Added a mechanism for overriding the key layout and key character map in the input device configuration file using the new "keyboard.layout" and "keyboard.characterMap" properties. Also added "trackball.orientationAware", "touch.orientationAware" and "touch.deviceType" configuration properties. Rewrote the configuration property reading code in native code so that it can be used by EventHub and other components. Added basic support for installable idc, kl, and kcm files in /data/system/devices. However, there is no provision for copying files there yet. Disabled long-press character pickers on full keyboards so that key repeating works as expected. Change-Id: I1bd9f0c3d344421db444e7d271eb09bc8bab4791 --- include/utils/PropertyMap.h | 100 +++++++++++++++++ libs/utils/Android.mk | 1 + libs/utils/PropertyMap.cpp | 212 ++++++++++++++++++++++++++++++++++++ 3 files changed, 313 insertions(+) create mode 100644 include/utils/PropertyMap.h create mode 100644 libs/utils/PropertyMap.cpp diff --git a/include/utils/PropertyMap.h b/include/utils/PropertyMap.h new file mode 100644 index 000000000..a54f819b5 --- /dev/null +++ b/include/utils/PropertyMap.h @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2010 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 _UTILS_PROPERTY_MAP_H +#define _UTILS_PROPERTY_MAP_H + +#include +#include +#include +#include + +namespace android { + +/* + * Provides a mechanism for passing around string-based property key / value pairs + * and loading them from property files. + * + * The property files have the following simple structure: + * + * # Comment + * key = value + * + * Keys and values are any sequence of printable ASCII characters. + * The '=' separates the key from the value. + * The key and value may not contain whitespace. + * + * The '\' character is reserved for escape sequences and is not currently supported. + * The '"" character is reserved for quoting and is not currently supported. + * Files that contain the '\' or '"' character will fail to parse. + * + * The file must not contain duplicate keys. + * + * TODO Support escape sequences and quoted values when needed. + */ +class PropertyMap { +public: + /* Creates an empty property map. */ + PropertyMap(); + ~PropertyMap(); + + /* Clears the property map. */ + void clear(); + + /* Adds a property. + * Replaces the property with the same key if it is already present. + */ + void addProperty(const String8& key, const String8& value); + + /* Returns true if the property map contains the specified key. */ + bool hasProperty(const String8& key) const; + + /* Gets the value of a property and parses it. + * Returns true and sets outValue if the key was found and its value was parsed successfully. + * Otherwise returns false and does not modify outValue. (Also logs a warning.) + */ + bool tryGetProperty(const String8& key, String8& outValue) const; + bool tryGetProperty(const String8& key, bool& outValue) const; + bool tryGetProperty(const String8& key, int32_t& outValue) const; + bool tryGetProperty(const String8& key, float& outValue) const; + + /* Loads a property map from a file. */ + static status_t load(const String8& filename, PropertyMap** outMap); + +private: + class Parser { + PropertyMap* mMap; + Tokenizer* mTokenizer; + + public: + Parser(PropertyMap* map, Tokenizer* tokenizer); + ~Parser(); + status_t parse(); + + private: + status_t parseType(); + status_t parseKey(); + status_t parseKeyProperty(); + status_t parseModifier(const String8& token, int32_t* outMetaState); + status_t parseCharacterLiteral(char16_t* outCharacter); + }; + + KeyedVector mProperties; +}; + +} // namespace android + +#endif // _UTILS_PROPERTY_MAP_H diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk index 9c01aea92..8bd833b50 100644 --- a/libs/utils/Android.mk +++ b/libs/utils/Android.mk @@ -28,6 +28,7 @@ commonSources:= \ Flattenable.cpp \ ObbFile.cpp \ Pool.cpp \ + PropertyMap.cpp \ RefBase.cpp \ ResourceTypes.cpp \ SharedBuffer.cpp \ diff --git a/libs/utils/PropertyMap.cpp b/libs/utils/PropertyMap.cpp new file mode 100644 index 000000000..fd7edecf9 --- /dev/null +++ b/libs/utils/PropertyMap.cpp @@ -0,0 +1,212 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "PropertyMap" + +#include +#include + +#include +#include + +// Enables debug output for the parser. +#define DEBUG_PARSER 0 + +// Enables debug output for parser performance. +#define DEBUG_PARSER_PERFORMANCE 0 + + +namespace android { + +static const char* WHITESPACE = " \t\r"; +static const char* WHITESPACE_OR_PROPERTY_DELIMITER = " \t\r="; + + +// --- PropertyMap --- + +PropertyMap::PropertyMap() { +} + +PropertyMap::~PropertyMap() { +} + +void PropertyMap::clear() { + mProperties.clear(); +} + +void PropertyMap::addProperty(const String8& key, const String8& value) { + mProperties.add(key, value); +} + +bool PropertyMap::hasProperty(const String8& key) const { + return mProperties.indexOfKey(key) >= 0; +} + +bool PropertyMap::tryGetProperty(const String8& key, String8& outValue) const { + ssize_t index = mProperties.indexOfKey(key); + if (index < 0) { + return false; + } + + outValue = mProperties.valueAt(index); + return true; +} + +bool PropertyMap::tryGetProperty(const String8& key, bool& outValue) const { + int32_t intValue; + if (!tryGetProperty(key, intValue)) { + return false; + } + + outValue = intValue; + return true; +} + +bool PropertyMap::tryGetProperty(const String8& key, int32_t& outValue) const { + String8 stringValue; + if (! tryGetProperty(key, stringValue) || stringValue.length() == 0) { + return false; + } + + char* end; + int value = strtol(stringValue.string(), & end, 10); + if (*end != '\0') { + LOGW("Property key '%s' has invalid value '%s'. Expected an integer.", + key.string(), stringValue.string()); + return false; + } + outValue = value; + return true; +} + +bool PropertyMap::tryGetProperty(const String8& key, float& outValue) const { + String8 stringValue; + if (! tryGetProperty(key, stringValue) || stringValue.length() == 0) { + return false; + } + + char* end; + float value = strtof(stringValue.string(), & end); + if (*end != '\0') { + LOGW("Property key '%s' has invalid value '%s'. Expected a float.", + key.string(), stringValue.string()); + return false; + } + outValue = value; + return true; +} + +status_t PropertyMap::load(const String8& filename, PropertyMap** outMap) { + *outMap = NULL; + + Tokenizer* tokenizer; + status_t status = Tokenizer::open(filename, &tokenizer); + if (status) { + LOGE("Error %d opening property file %s.", status, filename.string()); + } else { + PropertyMap* map = new PropertyMap(); + if (!map) { + LOGE("Error allocating property map."); + status = NO_MEMORY; + } else { +#if DEBUG_PARSER_PERFORMANCE + nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC); +#endif + Parser parser(map, tokenizer); + status = parser.parse(); +#if DEBUG_PARSER_PERFORMANCE + nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime; + LOGD("Parsed property file '%s' %d lines in %0.3fms.", + tokenizer->getFilename().string(), tokenizer->getLineNumber(), + elapsedTime / 1000000.0); +#endif + if (status) { + delete map; + } else { + *outMap = map; + } + } + delete tokenizer; + } + return status; +} + + +// --- PropertyMap::Parser --- + +PropertyMap::Parser::Parser(PropertyMap* map, Tokenizer* tokenizer) : + mMap(map), mTokenizer(tokenizer) { +} + +PropertyMap::Parser::~Parser() { +} + +status_t PropertyMap::Parser::parse() { + while (!mTokenizer->isEof()) { +#if DEBUG_PARSER + LOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(), + mTokenizer->peekRemainderOfLine().string()); +#endif + + mTokenizer->skipDelimiters(WHITESPACE); + + if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') { + String8 keyToken = mTokenizer->nextToken(WHITESPACE_OR_PROPERTY_DELIMITER); + if (keyToken.isEmpty()) { + LOGE("%s: Expected non-empty property key.", mTokenizer->getLocation().string()); + return BAD_VALUE; + } + + mTokenizer->skipDelimiters(WHITESPACE); + + if (mTokenizer->nextChar() != '=') { + LOGE("%s: Expected '=' between property key and value.", + mTokenizer->getLocation().string()); + return BAD_VALUE; + } + + mTokenizer->skipDelimiters(WHITESPACE); + + String8 valueToken = mTokenizer->nextToken(WHITESPACE); + if (valueToken.find("\\", 0) >= 0 || valueToken.find("\"", 0) >= 0) { + LOGE("%s: Found reserved character '\\' or '\"' in property value.", + mTokenizer->getLocation().string()); + return BAD_VALUE; + } + + mTokenizer->skipDelimiters(WHITESPACE); + if (!mTokenizer->isEol()) { + LOGE("%s: Expected end of line, got '%s'.", + mTokenizer->getLocation().string(), + mTokenizer->peekRemainderOfLine().string()); + return BAD_VALUE; + } + + if (mMap->hasProperty(keyToken)) { + LOGE("%s: Duplicate property value for key '%s'.", + mTokenizer->getLocation().string(), keyToken.string()); + return BAD_VALUE; + } + + mMap->addProperty(keyToken, valueToken); + } + + mTokenizer->nextLine(); + } + return NO_ERROR; +} + +} // namespace android From 1d618d63c1bb99728b5b0afe320f5a6afa95436c Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Thu, 2 Dec 2010 13:50:46 -0800 Subject: [PATCH 277/541] Improve support for external keyboards. Use Vendor ID, Product ID and optionally the Version to locate keymaps and configuration files for external devices. Moved virtual key definition parsing to native code so that EventHub can identify touch screens with virtual keys and load the appropriate key layout file. Cleaned up a lot of old code in EventHub. Fixed a regression in ViewRoot's fallback event handling. Fixed a minor bug in FileMap that caused it to try to munmap or close invalid handled when released if the attempt to map the file failed. Added a couple of new String8 conveniences for formatting strings. Modified Tokenizer to fall back to open+read when mmap fails since we can't mmap sysfs files as needed to open the virtual key definition files in /sys/board_properties/. Change-Id: I6ca5e5f9547619fd082ddac47e87ce185da69ee6 --- include/utils/String8.h | 11 ++++++++- include/utils/Tokenizer.h | 4 ++-- libs/utils/FileMap.cpp | 8 ++++--- libs/utils/String8.cpp | 18 +++++++++++++++ libs/utils/Tokenizer.cpp | 47 ++++++++++++++++++++++++--------------- 5 files changed, 64 insertions(+), 24 deletions(-) diff --git a/include/utils/String8.h b/include/utils/String8.h index 6abfb0634..6b49ff5b4 100644 --- a/include/utils/String8.h +++ b/include/utils/String8.h @@ -47,7 +47,12 @@ public: explicit String8(const char32_t* o); explicit String8(const char32_t* o, size_t numChars); ~String8(); - + + static inline const String8 empty(); + + static String8 format(const char* fmt, ...) __attribute__((format (printf, 1, 2))); + static String8 formatV(const char* fmt, va_list args); + inline const char* string() const; inline size_t size() const; inline size_t length() const; @@ -229,6 +234,10 @@ inline int strictly_order_type(const String8& lhs, const String8& rhs) return compare_type(lhs, rhs) < 0; } +inline const String8 String8::empty() { + return String8(); +} + inline const char* String8::string() const { return mString; diff --git a/include/utils/Tokenizer.h b/include/utils/Tokenizer.h index 21e58e62c..c7db5fb9e 100644 --- a/include/utils/Tokenizer.h +++ b/include/utils/Tokenizer.h @@ -28,7 +28,7 @@ namespace android { * A simple tokenizer for loading and parsing ASCII text files line by line. */ class Tokenizer { - Tokenizer(const String8& filename, FileMap* fileMap, const char* buffer, size_t length); + Tokenizer(const String8& filename, FileMap* fileMap, char* buffer, size_t length); public: ~Tokenizer(); @@ -110,7 +110,7 @@ private: String8 mFilename; FileMap* mFileMap; - const char* mBuffer; + char* mBuffer; size_t mLength; const char* mCurrent; diff --git a/libs/utils/FileMap.cpp b/libs/utils/FileMap.cpp index e1ba9b238..f1f8bdadf 100644 --- a/libs/utils/FileMap.cpp +++ b/libs/utils/FileMap.cpp @@ -63,16 +63,18 @@ FileMap::~FileMap(void) free(mFileName); } #ifdef HAVE_POSIX_FILEMAP - if (munmap(mBasePtr, mBaseLength) != 0) { + if (mBasePtr && munmap(mBasePtr, mBaseLength) != 0) { LOGD("munmap(%p, %d) failed\n", mBasePtr, (int) mBaseLength); } #endif #ifdef HAVE_WIN32_FILEMAP - if ( UnmapViewOfFile(mBasePtr) == 0) { + if (mBasePtr && UnmapViewOfFile(mBasePtr) == 0) { LOGD("UnmapViewOfFile(%p) failed, error = %ld\n", mBasePtr, GetLastError() ); } - CloseHandle(mFileMapping); + if (mFileMapping != INVALID_HANDLE_VALUE) { + CloseHandle(mFileMapping); + } CloseHandle(mFileHandle); #endif } diff --git a/libs/utils/String8.cpp b/libs/utils/String8.cpp index e531a2a0f..0bc5aff22 100644 --- a/libs/utils/String8.cpp +++ b/libs/utils/String8.cpp @@ -195,6 +195,24 @@ String8::~String8() SharedBuffer::bufferFromData(mString)->release(); } +String8 String8::format(const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + + String8 result(formatV(fmt, args)); + + va_end(args); + return result; +} + +String8 String8::formatV(const char* fmt, va_list args) +{ + String8 result; + result.appendFormatV(fmt, args); + return result; +} + void String8::clear() { SharedBuffer::bufferFromData(mString)->release(); mString = getEmptyString(); diff --git a/libs/utils/Tokenizer.cpp b/libs/utils/Tokenizer.cpp index 9251973ca..b3445b797 100644 --- a/libs/utils/Tokenizer.cpp +++ b/libs/utils/Tokenizer.cpp @@ -35,16 +35,16 @@ static inline bool isDelimiter(char ch, const char* delimiters) { return strchr(delimiters, ch) != NULL; } - -Tokenizer::Tokenizer(const String8& filename, FileMap* fileMap, - const char* buffer, size_t length) : - mFilename(filename), mFileMap(fileMap), mBuffer(buffer), mLength(length), - mCurrent(buffer), mLineNumber(1) { +Tokenizer::Tokenizer(const String8& filename, FileMap* fileMap, char* buffer, size_t length) : + mFilename(filename), mFileMap(fileMap), + mBuffer(buffer), mLength(length), mCurrent(buffer), mLineNumber(1) { } Tokenizer::~Tokenizer() { if (mFileMap) { mFileMap->release(); + } else { + delete[] mBuffer; } } @@ -63,22 +63,33 @@ status_t Tokenizer::open(const String8& filename, Tokenizer** outTokenizer) { LOGE("Error getting size of file '%s', %s.", filename.string(), strerror(errno)); } else { size_t length = size_t(stat.st_size); - FileMap* fileMap = new FileMap(); - if (!fileMap->create(NULL, fd, 0, length, true)) { - result = NO_MEMORY; - LOGE("Error mapping file '%s', %s.", filename.string(), strerror(errno)); - } else { - fileMap->advise(FileMap::SEQUENTIAL); - *outTokenizer = new Tokenizer(filename, fileMap, - static_cast(fileMap->getDataPtr()), length); - if (!*outTokenizer) { - result = NO_MEMORY; - LOGE("Error allocating tokenizer for file=%s.", filename.string()); + FileMap* fileMap = new FileMap(); + char* buffer; + if (fileMap->create(NULL, fd, 0, length, true)) { + fileMap->advise(FileMap::SEQUENTIAL); + buffer = static_cast(fileMap->getDataPtr()); + } else { + fileMap->release(); + fileMap = NULL; + + // Fall back to reading into a buffer since we can't mmap files in sysfs. + // The length we obtained from stat is wrong too (it will always be 4096) + // so we must trust that read will read the entire file. + buffer = new char[length]; + ssize_t nrd = read(fd, buffer, length); + if (nrd < 0) { + result = -errno; + LOGE("Error reading file '%s', %s.", filename.string(), strerror(errno)); + delete[] buffer; + buffer = NULL; + } else { + length = size_t(nrd); } } - if (result) { - fileMap->release(); + + if (!result) { + *outTokenizer = new Tokenizer(filename, fileMap, buffer, length); } } close(fd); From e2fa7dc58eaf34f30b89350d143d97fd4a501199 Mon Sep 17 00:00:00 2001 From: Kenny Root Date: Wed, 24 Nov 2010 12:56:06 -0800 Subject: [PATCH 278/541] Change assets to use 64-bit API The asset system and supporting libraries were using off_t instead of off64_t to access files larger than 2GB (32-bit signed). This change replaces all off_t with off64_t and lseek64. There is a new utils/Compat.h added for Mac OS compatibility. Also fixed some size-related compiler warnings. Bug: 3205336 Change-Id: I9097b3cb7a602e811fe52f245939d8975da55e9e --- include/utils/Asset.h | 62 +++++++++++------------ include/utils/Compat.h | 42 ++++++++++++++++ include/utils/FileMap.h | 8 +-- include/utils/StreamingZipInflater.h | 10 ++-- include/utils/ZipFileCRO.h | 4 +- include/utils/ZipFileRO.h | 5 +- libs/utils/Android.mk | 5 -- libs/utils/Asset.cpp | 73 +++++++++++++++++----------- libs/utils/FileMap.cpp | 7 +-- libs/utils/ObbFile.cpp | 29 ++++------- libs/utils/StreamingZipInflater.cpp | 4 +- libs/utils/ZipFileCRO.cpp | 2 +- libs/utils/ZipFileRO.cpp | 38 +++++++-------- 13 files changed, 172 insertions(+), 117 deletions(-) create mode 100644 include/utils/Compat.h diff --git a/include/utils/Asset.h b/include/utils/Asset.h index 2a09095bd..1fe0e063c 100644 --- a/include/utils/Asset.h +++ b/include/utils/Asset.h @@ -23,9 +23,11 @@ #include #include -#include "FileMap.h" -#include "String8.h" -#include "Errors.h" + +#include +#include +#include +#include namespace android { @@ -69,10 +71,10 @@ public: /* * Seek to the specified offset. "whence" uses the same values as - * lseek/fseek. Returns the new position on success, or (off_t) -1 + * lseek/fseek. Returns the new position on success, or (off64_t) -1 * on failure. */ - virtual off_t seek(off_t offset, int whence) = 0; + virtual off64_t seek(off64_t offset, int whence) = 0; /* * Close the asset, freeing all associated resources. @@ -87,26 +89,26 @@ public: /* * Get the total amount of data that can be read. */ - virtual off_t getLength(void) const = 0; + virtual off64_t getLength(void) const = 0; /* * Get the total amount of data that can be read from the current position. */ - virtual off_t getRemainingLength(void) const = 0; + virtual off64_t getRemainingLength(void) const = 0; /* * Open a new file descriptor that can be used to read this asset. * Returns -1 if you can not use the file descriptor (for example if the * asset is compressed). */ - virtual int openFileDescriptor(off_t* outStart, off_t* outLength) const = 0; - + virtual int openFileDescriptor(off64_t* outStart, off64_t* outLength) const = 0; + /* * Return whether this asset's buffer is allocated in RAM (not mmapped). * Note: not virtual so it is safe to call even when being destroyed. */ virtual bool isAllocated(void) const { return false; } - + /* * Get a string identifying the asset's source. This might be a full * path, it might be a colon-separated list of identifiers. @@ -120,7 +122,7 @@ protected: Asset(void); // constructor; only invoked indirectly /* handle common seek() housekeeping */ - off_t handleSeek(off_t offset, int whence, off_t curPosn, off_t maxPosn); + off64_t handleSeek(off64_t offset, int whence, off64_t curPosn, off64_t maxPosn); /* set the asset source string */ void setAssetSource(const String8& path) { mAssetSource = path; } @@ -153,7 +155,7 @@ private: * * The asset takes ownership of the file descriptor. */ - static Asset* createFromFileSegment(int fd, off_t offset, size_t length, + static Asset* createFromFileSegment(int fd, off64_t offset, size_t length, AccessMode mode); /* @@ -166,7 +168,7 @@ private: * This may not verify the validity of the compressed data until first * use. */ - static Asset* createFromCompressedData(int fd, off_t offset, + static Asset* createFromCompressedData(int fd, off64_t offset, int compressionMethod, size_t compressedLength, size_t uncompressedLength, AccessMode mode); #endif @@ -221,7 +223,7 @@ public: * * On success, the object takes ownership of "fd". */ - status_t openChunk(const char* fileName, int fd, off_t offset, size_t length); + status_t openChunk(const char* fileName, int fd, off64_t offset, size_t length); /* * Use a memory-mapped region. @@ -234,18 +236,18 @@ public: * Standard Asset interfaces. */ virtual ssize_t read(void* buf, size_t count); - virtual off_t seek(off_t offset, int whence); + virtual off64_t seek(off64_t offset, int whence); virtual void close(void); virtual const void* getBuffer(bool wordAligned); - virtual off_t getLength(void) const { return mLength; } - virtual off_t getRemainingLength(void) const { return mLength-mOffset; } - virtual int openFileDescriptor(off_t* outStart, off_t* outLength) const; + virtual off64_t getLength(void) const { return mLength; } + virtual off64_t getRemainingLength(void) const { return mLength-mOffset; } + virtual int openFileDescriptor(off64_t* outStart, off64_t* outLength) const; virtual bool isAllocated(void) const { return mBuf != NULL; } private: - off_t mStart; // absolute file offset of start of chunk - off_t mLength; // length of the chunk - off_t mOffset; // current local offset, 0 == mStart + off64_t mStart; // absolute file offset of start of chunk + off64_t mLength; // length of the chunk + off64_t mOffset; // current local offset, 0 == mStart FILE* mFp; // for read/seek char* mFileName; // for opening @@ -276,7 +278,7 @@ public: * * On success, the object takes ownership of "fd". */ - status_t openChunk(int fd, off_t offset, int compressionMethod, + status_t openChunk(int fd, off64_t offset, int compressionMethod, size_t uncompressedLen, size_t compressedLen); /* @@ -291,19 +293,19 @@ public: * Standard Asset interfaces. */ virtual ssize_t read(void* buf, size_t count); - virtual off_t seek(off_t offset, int whence); + virtual off64_t seek(off64_t offset, int whence); virtual void close(void); virtual const void* getBuffer(bool wordAligned); - virtual off_t getLength(void) const { return mUncompressedLen; } - virtual off_t getRemainingLength(void) const { return mUncompressedLen-mOffset; } - virtual int openFileDescriptor(off_t* outStart, off_t* outLength) const { return -1; } + virtual off64_t getLength(void) const { return mUncompressedLen; } + virtual off64_t getRemainingLength(void) const { return mUncompressedLen-mOffset; } + virtual int openFileDescriptor(off64_t* outStart, off64_t* outLength) const { return -1; } virtual bool isAllocated(void) const { return mBuf != NULL; } private: - off_t mStart; // offset to start of compressed data - off_t mCompressedLen; // length of the compressed data - off_t mUncompressedLen; // length of the uncompressed data - off_t mOffset; // current offset, 0 == start of uncomp data + off64_t mStart; // offset to start of compressed data + off64_t mCompressedLen; // length of the compressed data + off64_t mUncompressedLen; // length of the uncompressed data + off64_t mOffset; // current offset, 0 == start of uncomp data FileMap* mMap; // for memory-mapped input int mFd; // for file input diff --git a/include/utils/Compat.h b/include/utils/Compat.h new file mode 100644 index 000000000..18192661c --- /dev/null +++ b/include/utils/Compat.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2010 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 __LIB_UTILS_COMPAT_H +#define __LIB_UTILS_COMPAT_H + +#include + +/* Compatibility definitions for non-Linux (i.e., BSD-based) hosts. */ +#ifndef HAVE_OFF64_T +#if _FILE_OFFSET_BITS < 64 +#error "_FILE_OFFSET_BITS < 64; large files are not supported on this platform" +#endif /* _FILE_OFFSET_BITS < 64 */ + +typedef off_t off64_t; + +static inline off64_t lseek64(int fd, off64_t offset, int whence) { + return lseek(fd, offset, whence); +} + +#ifdef HAVE_PREAD +static inline ssize_t pread64(int fd, void* buf, size_t nbytes, off64_t offset) { + return pread(fd, buf, nbytes, offset); +} +#endif + +#endif /* !HAVE_OFF64_T */ + +#endif /* __LIB_UTILS_COMPAT_H */ diff --git a/include/utils/FileMap.h b/include/utils/FileMap.h index 8dfd3bea6..dfe6d513d 100644 --- a/include/utils/FileMap.h +++ b/include/utils/FileMap.h @@ -22,6 +22,8 @@ #include +#include + #ifdef HAVE_WIN32_FILEMAP #include #endif @@ -55,7 +57,7 @@ public: * Returns "false" on failure. */ bool create(const char* origFileName, int fd, - off_t offset, size_t length, bool readOnly); + off64_t offset, size_t length, bool readOnly); /* * Return the name of the file this map came from, if known. @@ -75,7 +77,7 @@ public: /* * Get the data offset used to create this map. */ - off_t getDataOffset(void) const { return mDataOffset; } + off64_t getDataOffset(void) const { return mDataOffset; } /* * Get a "copy" of the object. @@ -118,7 +120,7 @@ private: char* mFileName; // original file name, if known void* mBasePtr; // base of mmap area; page aligned size_t mBaseLength; // length, measured from "mBasePtr" - off_t mDataOffset; // offset used when map was created + off64_t mDataOffset; // offset used when map was created void* mDataPtr; // start of requested data, offset from base size_t mDataLength; // length, measured from "mDataPtr" #ifdef HAVE_WIN32_FILEMAP diff --git a/include/utils/StreamingZipInflater.h b/include/utils/StreamingZipInflater.h index 16867d8db..3ace5d5a8 100644 --- a/include/utils/StreamingZipInflater.h +++ b/include/utils/StreamingZipInflater.h @@ -21,6 +21,8 @@ #include #include +#include + namespace android { class StreamingZipInflater { @@ -29,7 +31,7 @@ public: static const size_t OUTPUT_CHUNK_SIZE = 64 * 1024; // Flavor that pages in the compressed data from a fd - StreamingZipInflater(int fd, off_t compDataStart, size_t uncompSize, size_t compSize); + StreamingZipInflater(int fd, off64_t compDataStart, size_t uncompSize, size_t compSize); // Flavor that gets the compressed data from an in-memory buffer StreamingZipInflater(class FileMap* dataMap, size_t uncompSize); @@ -43,7 +45,7 @@ public: // seeking backwards requires uncompressing fom the beginning, so is very // expensive. seeking forwards only requires uncompressing from the current // position to the destination. - off_t seekAbsolute(off_t absoluteInputPosition); + off64_t seekAbsolute(off64_t absoluteInputPosition); private: void initInflateState(); @@ -51,7 +53,7 @@ private: // where to find the uncompressed data int mFd; - off_t mInFileStart; // where the compressed data lives in the file + off64_t mInFileStart; // where the compressed data lives in the file class FileMap* mDataMap; z_stream mInflateState; @@ -63,7 +65,7 @@ private: size_t mOutTotalSize; // total uncompressed size of the blob // current output state bookkeeping - off_t mOutCurPosition; // current position in total offset + off64_t mOutCurPosition; // current position in total offset size_t mOutLastDecoded; // last decoded byte + 1 in mOutbuf size_t mOutDeliverable; // next undelivered byte of decoded output in mOutBuf diff --git a/include/utils/ZipFileCRO.h b/include/utils/ZipFileCRO.h index e38bf669d..3e42a958b 100644 --- a/include/utils/ZipFileCRO.h +++ b/include/utils/ZipFileCRO.h @@ -24,6 +24,8 @@ #include #include +#include + #ifdef __cplusplus extern "C" { #endif @@ -48,7 +50,7 @@ extern ZipEntryCRO ZipFileCRO_findEntryByName(ZipFileCRO zip, extern bool ZipFileCRO_getEntryInfo(ZipFileCRO zip, ZipEntryCRO entry, int* pMethod, size_t* pUncompLen, - size_t* pCompLen, off_t* pOffset, long* pModWhen, long* pCrc32); + size_t* pCompLen, off64_t* pOffset, long* pModWhen, long* pCrc32); extern bool ZipFileCRO_uncompressEntry(ZipFileCRO zip, ZipEntryCRO entry, int fd); diff --git a/include/utils/ZipFileRO.h b/include/utils/ZipFileRO.h index 3c1f3caf2..3a999797b 100644 --- a/include/utils/ZipFileRO.h +++ b/include/utils/ZipFileRO.h @@ -30,6 +30,7 @@ #ifndef __LIBS_ZIPFILERO_H #define __LIBS_ZIPFILERO_H +#include #include #include #include @@ -128,7 +129,7 @@ public: * appears to be bad. */ bool getEntryInfo(ZipEntryRO entry, int* pMethod, size_t* pUncompLen, - size_t* pCompLen, off_t* pOffset, long* pModWhen, long* pCrc32) const; + size_t* pCompLen, off64_t* pOffset, long* pModWhen, long* pCrc32) const; /* * Create a new FileMap object that maps a subset of the archive. For @@ -231,7 +232,7 @@ private: int mNumEntries; /* CD directory offset in the Zip archive */ - off_t mDirectoryOffset; + off64_t mDirectoryOffset; /* * We know how many entries are in the Zip archive, so we have a diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk index 8bd833b50..e8d40ba08 100644 --- a/libs/utils/Android.mk +++ b/libs/utils/Android.mk @@ -70,11 +70,6 @@ LOCAL_CFLAGS += -DMB_CUR_MAX=1 endif endif -ifeq ($(HOST_OS),darwin) -# MacOS doesn't have lseek64. However, off_t is 64-bit anyway. -LOCAL_CFLAGS += -DOFF_T_IS_64_BIT -endif - include $(BUILD_HOST_STATIC_LIBRARY) diff --git a/libs/utils/Asset.cpp b/libs/utils/Asset.cpp index cef7db492..a18294b18 100644 --- a/libs/utils/Asset.cpp +++ b/libs/utils/Asset.cpp @@ -35,6 +35,9 @@ #include #include #include +#include +#include +#include using namespace android; @@ -62,7 +65,7 @@ String8 Asset::getAssetAllocations() if (cur->isAllocated()) { res.append(" "); res.append(cur->getAssetSource()); - off_t size = (cur->getLength()+512)/1024; + off64_t size = (cur->getLength()+512)/1024; char buf[64]; sprintf(buf, ": %dK\n", (int)size); res.append(buf); @@ -119,7 +122,7 @@ Asset::~Asset(void) { _FileAsset* pAsset; status_t result; - off_t length; + off64_t length; int fd; fd = open(fileName, O_RDONLY | O_BINARY); @@ -132,12 +135,26 @@ Asset::~Asset(void) * always open things read-only it doesn't really matter, so there's * no value in incurring the extra overhead of an fstat() call. */ - length = lseek(fd, 0, SEEK_END); + // TODO(kroot): replace this with fstat despite the plea above. +#if 1 + length = lseek64(fd, 0, SEEK_END); if (length < 0) { ::close(fd); return NULL; } - (void) lseek(fd, 0, SEEK_SET); + (void) lseek64(fd, 0, SEEK_SET); +#else + struct stat st; + if (fstat(fd, &st) < 0) { + ::close(fd); + return NULL; + } + + if (!S_ISREG(st.st_mode)) { + ::close(fd); + return NULL; + } +#endif pAsset = new _FileAsset; result = pAsset->openChunk(fileName, fd, 0, length); @@ -162,7 +179,7 @@ Asset::~Asset(void) { _CompressedAsset* pAsset; status_t result; - off_t fileLen; + off64_t fileLen; bool scanResult; long offset; int method; @@ -215,7 +232,7 @@ Asset::~Asset(void) /* * Create a new Asset from part of an open file. */ -/*static*/ Asset* Asset::createFromFileSegment(int fd, off_t offset, +/*static*/ Asset* Asset::createFromFileSegment(int fd, off64_t offset, size_t length, AccessMode mode) { _FileAsset* pAsset; @@ -233,7 +250,7 @@ Asset::~Asset(void) /* * Create a new Asset from compressed data in an open file. */ -/*static*/ Asset* Asset::createFromCompressedData(int fd, off_t offset, +/*static*/ Asset* Asset::createFromCompressedData(int fd, off64_t offset, int compressionMethod, size_t uncompressedLen, size_t compressedLen, AccessMode mode) { @@ -295,9 +312,9 @@ Asset::~Asset(void) * * Returns the new chunk offset, or -1 if the seek is illegal. */ -off_t Asset::handleSeek(off_t offset, int whence, off_t curPosn, off_t maxPosn) +off64_t Asset::handleSeek(off64_t offset, int whence, off64_t curPosn, off64_t maxPosn) { - off_t newOffset; + off64_t newOffset; switch (whence) { case SEEK_SET: @@ -311,15 +328,15 @@ off_t Asset::handleSeek(off_t offset, int whence, off_t curPosn, off_t maxPosn) break; default: LOGW("unexpected whence %d\n", whence); - // this was happening due to an off_t size mismatch + // this was happening due to an off64_t size mismatch assert(false); - return (off_t) -1; + return (off64_t) -1; } if (newOffset < 0 || newOffset > maxPosn) { LOGW("seek out of range: want %ld, end=%ld\n", (long) newOffset, (long) maxPosn); - return (off_t) -1; + return (off64_t) -1; } return newOffset; @@ -353,7 +370,7 @@ _FileAsset::~_FileAsset(void) * * Zero-length chunks are allowed. */ -status_t _FileAsset::openChunk(const char* fileName, int fd, off_t offset, size_t length) +status_t _FileAsset::openChunk(const char* fileName, int fd, off64_t offset, size_t length) { assert(mFp == NULL); // no reopen assert(mMap == NULL); @@ -363,15 +380,15 @@ status_t _FileAsset::openChunk(const char* fileName, int fd, off_t offset, size_ /* * Seek to end to get file length. */ - off_t fileLength; - fileLength = lseek(fd, 0, SEEK_END); - if (fileLength == (off_t) -1) { + off64_t fileLength; + fileLength = lseek64(fd, 0, SEEK_END); + if (fileLength == (off64_t) -1) { // probably a bad file descriptor LOGD("failed lseek (errno=%d)\n", errno); return UNKNOWN_ERROR; } - if ((off_t) (offset + length) > fileLength) { + if ((off64_t) (offset + length) > fileLength) { LOGD("start (%ld) + len (%ld) > end (%ld)\n", (long) offset, (long) length, (long) fileLength); return BAD_INDEX; @@ -482,21 +499,21 @@ ssize_t _FileAsset::read(void* buf, size_t count) /* * Seek to a new position. */ -off_t _FileAsset::seek(off_t offset, int whence) +off64_t _FileAsset::seek(off64_t offset, int whence) { - off_t newPosn; - long actualOffset; + off64_t newPosn; + off64_t actualOffset; // compute new position within chunk newPosn = handleSeek(offset, whence, mOffset, mLength); - if (newPosn == (off_t) -1) + if (newPosn == (off64_t) -1) return newPosn; - actualOffset = (long) (mStart + newPosn); + actualOffset = mStart + newPosn; if (mFp != NULL) { if (fseek(mFp, (long) actualOffset, SEEK_SET) != 0) - return (off_t) -1; + return (off64_t) -1; } mOffset = actualOffset - mStart; @@ -603,7 +620,7 @@ const void* _FileAsset::getBuffer(bool wordAligned) } } -int _FileAsset::openFileDescriptor(off_t* outStart, off_t* outLength) const +int _FileAsset::openFileDescriptor(off64_t* outStart, off64_t* outLength) const { if (mMap != NULL) { const char* fname = mMap->getFileName(); @@ -678,7 +695,7 @@ _CompressedAsset::~_CompressedAsset(void) * This currently just sets up some values and returns. On the first * read, we expand the entire file into a buffer and return data from it. */ -status_t _CompressedAsset::openChunk(int fd, off_t offset, +status_t _CompressedAsset::openChunk(int fd, off64_t offset, int compressionMethod, size_t uncompressedLen, size_t compressedLen) { assert(mFd < 0); // no re-open @@ -782,13 +799,13 @@ ssize_t _CompressedAsset::read(void* buf, size_t count) * expensive, because it requires plowing through a bunch of compressed * data. */ -off_t _CompressedAsset::seek(off_t offset, int whence) +off64_t _CompressedAsset::seek(off64_t offset, int whence) { - off_t newPosn; + off64_t newPosn; // compute new position within chunk newPosn = handleSeek(offset, whence, mOffset, mUncompressedLen); - if (newPosn == (off_t) -1) + if (newPosn == (off64_t) -1) return newPosn; if (mZipInflater) { diff --git a/libs/utils/FileMap.cpp b/libs/utils/FileMap.cpp index f1f8bdadf..c220a9016 100644 --- a/libs/utils/FileMap.cpp +++ b/libs/utils/FileMap.cpp @@ -88,11 +88,12 @@ FileMap::~FileMap(void) * * Returns "false" on failure. */ -bool FileMap::create(const char* origFileName, int fd, off_t offset, size_t length, bool readOnly) +bool FileMap::create(const char* origFileName, int fd, off64_t offset, size_t length, + bool readOnly) { #ifdef HAVE_WIN32_FILEMAP int adjust; - off_t adjOffset; + off64_t adjOffset; size_t adjLength; if (mPageSize == -1) { @@ -131,7 +132,7 @@ bool FileMap::create(const char* origFileName, int fd, off_t offset, size_t leng #endif #ifdef HAVE_POSIX_FILEMAP int prot, flags, adjust; - off_t adjOffset; + off64_t adjOffset; size_t adjLength; void* ptr; diff --git a/libs/utils/ObbFile.cpp b/libs/utils/ObbFile.cpp index 2c3724c0a..2907b5666 100644 --- a/libs/utils/ObbFile.cpp +++ b/libs/utils/ObbFile.cpp @@ -22,6 +22,8 @@ #include #define LOG_TAG "ObbFile" + +#include #include #include @@ -67,17 +69,6 @@ _rc; }) #endif -/* - * Work around situations where off_t is 64-bit and use off64_t in - * situations where it's 32-bit. - */ -#ifdef OFF_T_IS_64_BIT -#define my_lseek64 lseek -typedef off_t my_off64_t; -#else -#define my_lseek64 lseek64 -typedef off64_t my_off64_t; -#endif namespace android { @@ -125,7 +116,7 @@ bool ObbFile::readFrom(int fd) bool ObbFile::parseObbFile(int fd) { - my_off64_t fileLength = my_lseek64(fd, 0, SEEK_END); + off64_t fileLength = lseek64(fd, 0, SEEK_END); if (fileLength < kFooterMinSize) { if (fileLength < 0) { @@ -140,7 +131,7 @@ bool ObbFile::parseObbFile(int fd) size_t footerSize; { - my_lseek64(fd, fileLength - kFooterTagSize, SEEK_SET); + lseek64(fd, fileLength - kFooterTagSize, SEEK_SET); char *footer = new char[kFooterTagSize]; actual = TEMP_FAILURE_RETRY(read(fd, footer, kFooterTagSize)); @@ -171,8 +162,8 @@ bool ObbFile::parseObbFile(int fd) } } - my_off64_t fileOffset = fileLength - footerSize - kFooterTagSize; - if (my_lseek64(fd, fileOffset, SEEK_SET) != fileOffset) { + off64_t fileOffset = fileLength - footerSize - kFooterTagSize; + if (lseek64(fd, fileOffset, SEEK_SET) != fileOffset) { LOGW("seek %lld failed: %s\n", fileOffset, strerror(errno)); return false; } @@ -211,10 +202,10 @@ bool ObbFile::parseObbFile(int fd) memcpy(&mSalt, (unsigned char*)scanBuf + kSaltOffset, sizeof(mSalt)); - uint32_t packageNameLen = get4LE((unsigned char*)scanBuf + kPackageNameLenOffset); - if (packageNameLen <= 0 + size_t packageNameLen = get4LE((unsigned char*)scanBuf + kPackageNameLenOffset); + if (packageNameLen == 0 || packageNameLen > (footerSize - kPackageNameOffset)) { - LOGW("bad ObbFile package name length (0x%04x; 0x%04x possible)\n", + LOGW("bad ObbFile package name length (0x%04zx; 0x%04zx possible)\n", packageNameLen, footerSize - kPackageNameOffset); free(scanBuf); return false; @@ -257,7 +248,7 @@ bool ObbFile::writeTo(int fd) return false; } - my_lseek64(fd, 0, SEEK_END); + lseek64(fd, 0, SEEK_END); if (mPackageName.size() == 0 || mVersion == -1) { LOGW("tried to write uninitialized ObbFile data\n"); diff --git a/libs/utils/StreamingZipInflater.cpp b/libs/utils/StreamingZipInflater.cpp index 1f62ac518..5a162cc49 100644 --- a/libs/utils/StreamingZipInflater.cpp +++ b/libs/utils/StreamingZipInflater.cpp @@ -31,7 +31,7 @@ using namespace android; /* * Streaming access to compressed asset data in an open fd */ -StreamingZipInflater::StreamingZipInflater(int fd, off_t compDataStart, +StreamingZipInflater::StreamingZipInflater(int fd, off64_t compDataStart, size_t uncompSize, size_t compSize) { mFd = fd; mDataMap = NULL; @@ -210,7 +210,7 @@ int StreamingZipInflater::readNextChunk() { // seeking backwards requires uncompressing fom the beginning, so is very // expensive. seeking forwards only requires uncompressing from the current // position to the destination. -off_t StreamingZipInflater::seekAbsolute(off_t absoluteInputPosition) { +off64_t StreamingZipInflater::seekAbsolute(off64_t absoluteInputPosition) { if (absoluteInputPosition < mOutCurPosition) { // rewind and reprocess the data from the beginning if (!mStreamNeedsInit) { diff --git a/libs/utils/ZipFileCRO.cpp b/libs/utils/ZipFileCRO.cpp index 16b219cad..55dfd9f87 100644 --- a/libs/utils/ZipFileCRO.cpp +++ b/libs/utils/ZipFileCRO.cpp @@ -40,7 +40,7 @@ ZipEntryCRO ZipFileCRO_findEntryByName(ZipFileCRO zipToken, bool ZipFileCRO_getEntryInfo(ZipFileCRO zipToken, ZipEntryRO entryToken, int* pMethod, size_t* pUncompLen, - size_t* pCompLen, off_t* pOffset, long* pModWhen, long* pCrc32) { + size_t* pCompLen, off64_t* pOffset, long* pModWhen, long* pCrc32) { ZipFileRO* zip = (ZipFileRO*)zipToken; ZipEntryRO entry = (ZipEntryRO)entryToken; return zip->getEntryInfo(entry, pMethod, pUncompLen, pCompLen, pOffset, diff --git a/libs/utils/ZipFileRO.cpp b/libs/utils/ZipFileRO.cpp index 42611964b..b18c383ae 100644 --- a/libs/utils/ZipFileRO.cpp +++ b/libs/utils/ZipFileRO.cpp @@ -146,7 +146,7 @@ status_t ZipFileRO::open(const char* zipFileName) return NAME_NOT_FOUND; } - mFileLength = lseek(fd, 0, SEEK_END); + mFileLength = lseek64(fd, 0, SEEK_END); if (mFileLength < kEOCDLen) { TEMP_FAILURE_RETRY(close(fd)); return UNKNOWN_ERROR; @@ -202,7 +202,7 @@ bool ZipFileRO::mapCentralDirectory(void) /* * Make sure this is a Zip archive. */ - if (lseek(mFd, 0, SEEK_SET) != 0) { + if (lseek64(mFd, 0, SEEK_SET) != 0) { LOGW("seek to start failed: %s", strerror(errno)); free(scanBuf); return false; @@ -240,9 +240,9 @@ bool ZipFileRO::mapCentralDirectory(void) * * We start by pulling in the last part of the file. */ - off_t searchStart = mFileLength - readAmount; + off64_t searchStart = mFileLength - readAmount; - if (lseek(mFd, searchStart, SEEK_SET) != searchStart) { + if (lseek64(mFd, searchStart, SEEK_SET) != searchStart) { LOGW("seek %ld failed: %s\n", (long) searchStart, strerror(errno)); free(scanBuf); return false; @@ -274,7 +274,7 @@ bool ZipFileRO::mapCentralDirectory(void) return false; } - off_t eocdOffset = searchStart + i; + off64_t eocdOffset = searchStart + i; const unsigned char* eocdPtr = scanBuf + i; assert(eocdOffset < mFileLength); @@ -473,7 +473,7 @@ ZipEntryRO ZipFileRO::findEntryByIndex(int idx) const * appear to be bogus. */ bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, size_t* pUncompLen, - size_t* pCompLen, off_t* pOffset, long* pModWhen, long* pCrc32) const + size_t* pCompLen, off64_t* pOffset, long* pModWhen, long* pCrc32) const { bool ret = false; @@ -489,7 +489,7 @@ bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, size_t* pUncompLen, * so we can just subtract back from that. */ const unsigned char* ptr = (const unsigned char*) hashEntry.name; - off_t cdOffset = mDirectoryOffset; + off64_t cdOffset = mDirectoryOffset; ptr -= kCDELen; @@ -536,12 +536,12 @@ bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, size_t* pUncompLen, #ifdef HAVE_PREAD /* * This file descriptor might be from zygote's preloaded assets, - * so we need to do an pread() instead of a lseek() + read() to + * so we need to do an pread64() instead of a lseek64() + read() to * guarantee atomicity across the processes with the shared file * descriptors. */ ssize_t actual = - TEMP_FAILURE_RETRY(pread(mFd, lfhBuf, sizeof(lfhBuf), localHdrOffset)); + TEMP_FAILURE_RETRY(pread64(mFd, lfhBuf, sizeof(lfhBuf), localHdrOffset)); if (actual != sizeof(lfhBuf)) { LOGW("failed reading lfh from offset %ld\n", localHdrOffset); @@ -556,17 +556,17 @@ bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, size_t* pUncompLen, } #else /* HAVE_PREAD */ /* - * For hosts don't have pread() we cannot guarantee atomic reads from + * For hosts don't have pread64() we cannot guarantee atomic reads from * an offset in a file. Android should never run on those platforms. * File descriptors inherited from a fork() share file offsets and * there would be nothing to protect from two different processes - * calling lseek() concurrently. + * calling lseek64() concurrently. */ { AutoMutex _l(mFdLock); - if (lseek(mFd, localHdrOffset, SEEK_SET) != localHdrOffset) { + if (lseek64(mFd, localHdrOffset, SEEK_SET) != localHdrOffset) { LOGW("failed seeking to lfh at offset %ld\n", localHdrOffset); return false; } @@ -579,7 +579,7 @@ bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, size_t* pUncompLen, } if (get4LE(lfhBuf) != kLFHSignature) { - off_t actualOffset = lseek(mFd, 0, SEEK_CUR); + off64_t actualOffset = lseek64(mFd, 0, SEEK_CUR); LOGW("didn't find signature at start of lfh; wanted: offset=%ld data=0x%08x; " "got: offset=" ZD " data=0x%08lx\n", localHdrOffset, kLFHSignature, (ZD_TYPE) actualOffset, get4LE(lfhBuf)); @@ -588,7 +588,7 @@ bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, size_t* pUncompLen, } #endif /* HAVE_PREAD */ - off_t dataOffset = localHdrOffset + kLFHLen + off64_t dataOffset = localHdrOffset + kLFHLen + get2LE(lfhBuf + kLFHNameLen) + get2LE(lfhBuf + kLFHExtraLen); if (dataOffset >= cdOffset) { LOGW("bad data offset %ld in zip\n", (long) dataOffset); @@ -596,14 +596,14 @@ bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, size_t* pUncompLen, } /* check lengths */ - if ((off_t)(dataOffset + compLen) > cdOffset) { + if ((off64_t)(dataOffset + compLen) > cdOffset) { LOGW("bad compressed length in zip (%ld + " ZD " > %ld)\n", (long) dataOffset, (ZD_TYPE) compLen, (long) cdOffset); return false; } if (method == kCompressStored && - (off_t)(dataOffset + uncompLen) > cdOffset) + (off64_t)(dataOffset + uncompLen) > cdOffset) { LOGE("ERROR: bad uncompressed length in zip (%ld + " ZD " > %ld)\n", (long) dataOffset, (ZD_TYPE) uncompLen, (long) cdOffset); @@ -649,7 +649,7 @@ FileMap* ZipFileRO::createEntryFileMap(ZipEntryRO entry) const FileMap* newMap; size_t compLen; - off_t offset; + off64_t offset; if (!getEntryInfo(entry, NULL, NULL, &compLen, &offset, NULL, NULL)) return NULL; @@ -679,7 +679,7 @@ bool ZipFileRO::uncompressEntry(ZipEntryRO entry, void* buffer) const int method; size_t uncompLen, compLen; - off_t offset; + off64_t offset; const unsigned char* ptr; getEntryInfo(entry, &method, &uncompLen, &compLen, &offset, NULL, NULL); @@ -739,7 +739,7 @@ bool ZipFileRO::uncompressEntry(ZipEntryRO entry, int fd) const int method; size_t uncompLen, compLen; - off_t offset; + off64_t offset; const unsigned char* ptr; getEntryInfo(entry, &method, &uncompLen, &compLen, &offset, NULL, NULL); From c974d4dbe00c46bb9f12e4802b47d4b41bc1fca6 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Fri, 10 Dec 2010 15:37:48 -0800 Subject: [PATCH 279/541] dump callstack on Surface use error Change-Id: I38e260dd47349b9af3a999dda683a083a94be16d --- include/utils/CallStack.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/utils/CallStack.h b/include/utils/CallStack.h index c2c8ce514..8817120ef 100644 --- a/include/utils/CallStack.h +++ b/include/utils/CallStack.h @@ -50,7 +50,7 @@ public: void clear(); - void update(int32_t ignoreDepth=0, int32_t maxDepth=MAX_DEPTH); + void update(int32_t ignoreDepth=1, int32_t maxDepth=MAX_DEPTH); // Dump a stack trace to the log void dump(const char* prefix = 0) const; From 8659f0be9a7cac99d55f58601d68c0c6da55af7b Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Thu, 23 Dec 2010 17:50:18 -0800 Subject: [PATCH 280/541] Add initial support for cursor-based pointing devices. Some parts stubbed out but you can plug in a mouse and move a green cursor around to interact with the UI. Change-Id: I80d597a7f11d3bd92041890f74b3c77326975e6e --- include/utils/PropertyMap.h | 6 ++++++ libs/utils/PropertyMap.cpp | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/include/utils/PropertyMap.h b/include/utils/PropertyMap.h index a54f819b5..a9e674f9a 100644 --- a/include/utils/PropertyMap.h +++ b/include/utils/PropertyMap.h @@ -71,6 +71,12 @@ public: bool tryGetProperty(const String8& key, int32_t& outValue) const; bool tryGetProperty(const String8& key, float& outValue) const; + /* Adds all values from the specified property map. */ + void addAll(const PropertyMap* map); + + /* Gets the underlying property map. */ + inline const KeyedVector& getProperties() const { return mProperties; } + /* Loads a property map from a file. */ static status_t load(const String8& filename, PropertyMap** outMap); diff --git a/libs/utils/PropertyMap.cpp b/libs/utils/PropertyMap.cpp index fd7edecf9..d472d45c4 100644 --- a/libs/utils/PropertyMap.cpp +++ b/libs/utils/PropertyMap.cpp @@ -109,6 +109,12 @@ bool PropertyMap::tryGetProperty(const String8& key, float& outValue) const { return true; } +void PropertyMap::addAll(const PropertyMap* map) { + for (size_t i = 0; i < map->mProperties.size(); i++) { + mProperties.add(map->mProperties.keyAt(i), map->mProperties.valueAt(i)); + } +} + status_t PropertyMap::load(const String8& filename, PropertyMap** outMap) { *outMap = NULL; From b69f49bab9502ddb0850280f2aea12fb34475777 Mon Sep 17 00:00:00 2001 From: Shachar Shemesh Date: Mon, 20 Dec 2010 17:38:33 +0200 Subject: [PATCH 281/541] Normalize output from aapt d Make the output from aapt dump --values resources and aapt dump xmltree normalized, so that it is unambigously displayed regardless of the content of the strings. Change-Id: Ia3bff36c4ee1e9a44f474534e154830948beabdf --- include/utils/ResourceTypes.h | 1 + libs/utils/ResourceTypes.cpp | 36 +++++++++++++++++++++++++++++++++-- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h index ed7f53d17..6227f3e92 100644 --- a/include/utils/ResourceTypes.h +++ b/include/utils/ResourceTypes.h @@ -1985,6 +1985,7 @@ public: #ifndef HAVE_ANDROID_OS void print(bool inclValues) const; + static String8 normalizeForOutput(const char* input); #endif private: diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp index bbf50934d..cae91ac80 100644 --- a/libs/utils/ResourceTypes.cpp +++ b/libs/utils/ResourceTypes.cpp @@ -4141,6 +4141,38 @@ void print_complex(uint32_t complex, bool isFraction) } } +// Normalize a string for output +String8 ResTable::normalizeForOutput( const char *input ) +{ + String8 ret; + char buff[2]; + buff[1] = '\0'; + + while (*input != '\0') { + switch (*input) { + // All interesting characters are in the ASCII zone, so we are making our own lives + // easier by scanning the string one byte at a time. + case '\\': + ret += "\\\\"; + break; + case '\n': + ret += "\\n"; + break; + case '"': + ret += "\\\""; + break; + default: + buff[0] = *input; + ret += buff; + break; + } + + input++; + } + + return ret; +} + void ResTable::print_value(const Package* pkg, const Res_value& value) const { if (value.dataType == Res_value::TYPE_NULL) { @@ -4154,13 +4186,13 @@ void ResTable::print_value(const Package* pkg, const Res_value& value) const const char* str8 = pkg->header->values.string8At( value.data, &len); if (str8 != NULL) { - printf("(string8) \"%s\"\n", str8); + printf("(string8) \"%s\"\n", normalizeForOutput(str8).string()); } else { const char16_t* str16 = pkg->header->values.stringAt( value.data, &len); if (str16 != NULL) { printf("(string16) \"%s\"\n", - String8(str16, len).string()); + normalizeForOutput(String8(str16, len).string()).string()); } else { printf("(string) null\n"); } From fe2c46327ff4bf5639d1634261797456a06d6ac4 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Tue, 4 Jan 2011 11:58:04 -0800 Subject: [PATCH 282/541] Fix issue 3302649. The cause of the problem is that AudioTrack::start() can fail if it is called from a newly created thread that has the same ID as the AudioTrack callback thread that has just been stopped and not yet exited. This is possible as the thread ID used by the Thread class is not the TID. The fix consists in clearing the thread ID before exiting the thread loop. Change-Id: I66e679665c384403cb3ba2c31746f5de72d5836d --- libs/utils/Threads.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp index f6c55e4d8..ad9a94f59 100644 --- a/libs/utils/Threads.cpp +++ b/libs/utils/Threads.cpp @@ -774,6 +774,9 @@ int Thread::_threadLoop(void* user) self->mExitPending = true; self->mLock.lock(); self->mRunning = false; + // clear thread ID so that requestExitAndWait() does not exit if + // called by a new thread using the same thread ID as this one. + self->mThread = thread_id_t(-1); self->mThreadExitedCondition.broadcast(); self->mLock.unlock(); break; From 0c3faa3ee2c9a69f55af2bbab2d1d7c281d98753 Mon Sep 17 00:00:00 2001 From: Dan Morrill Date: Thu, 13 Jan 2011 09:24:01 -0800 Subject: [PATCH 283/541] Adding ASL2 attribution for VisualOn. Change-Id: I870133fc366507d95741f8aa234776de58ba60a9 --- NOTICE | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/NOTICE b/NOTICE index 8d6f583a1..9324631ef 100644 --- a/NOTICE +++ b/NOTICE @@ -72,6 +72,15 @@ is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied; not even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + ========================================================================= + == NOTICE file corresponding to the section 4 d of == + == the Apache License, Version 2.0, == + == in this case for Additional Codecs code. == + ========================================================================= + +Additional Codecs +These files are Copyright 2003-2010 VisualOn, but released under +the Apache2 License. Apache License Version 2.0, January 2004 From 0e05bd908d19a863333ec88f9c38b78181e81781 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Thu, 13 Jan 2011 16:07:00 -0800 Subject: [PATCH 284/541] Fix omission in NOTICE file. Added copyright notice for audio effects source files under media/libeffects/lvm. Change-Id: I5ac961000f7bbbfa1f95523712d8517844b1e5f9 --- NOTICE | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/NOTICE b/NOTICE index 9324631ef..152be2059 100644 --- a/NOTICE +++ b/NOTICE @@ -82,6 +82,18 @@ Additional Codecs These files are Copyright 2003-2010 VisualOn, but released under the Apache2 License. + ========================================================================= + == NOTICE file corresponding to the section 4 d of == + == the Apache License, Version 2.0, == + == in this case for the Audio Effects code. == + ========================================================================= + +Audio Effects +These files are Copyright (C) 2004-2010 NXP Software and +Copyright (C) 2010 The Android Open Source Project, but released under +the Apache2 License. + + Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ From b90c51a7258966c0372a84ec2059fd1b03e2a782 Mon Sep 17 00:00:00 2001 From: Chet Haase Date: Mon, 10 Jan 2011 14:10:36 -0800 Subject: [PATCH 285/541] Use optimized display lists for all hwaccelerated rendering Previously, display lists were used only if hardware acceleration was enabled for an application (hardwareAccelerated=true) *and* if setDrawingCacheEnabled(true) was called. This change makes the framework use display lists for all views in an application if hardware acceleration is enabled. In addition, display list renderering has been optimized so that any view's recreation of its own display list (which is necessary whenever the visuals of that view change) will not cause any other display list in its parent hierarchy to change. Instead, when there are any visual changes in the hierarchy, only those views which need to have new display list content will recreate their display lists. This optimization works by caching display list references in each parent display list (so the container of some child will refer to its child's display list by a reference to the child's display list). Then when a view needs to recreate its display list, it will do so inside the same display list object. This will cause the content to get refreshed, but not the reference to that content. Then when the view hierarchy is redrawn, it will automatically pick up the new content from the old reference. This optimization will not necessarily improve performance when applications need to update the entire view hierarchy or redraw the entire screen, but it does show significant improvements when redrawing only a portion of the screen, especially when the regions that are not refreshed are complex and time- consuming to redraw. Change-Id: I68d21cac6a224a05703070ec85253220cb001eb4 --- include/utils/Functor.h | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 include/utils/Functor.h diff --git a/include/utils/Functor.h b/include/utils/Functor.h new file mode 100644 index 000000000..3955bc346 --- /dev/null +++ b/include/utils/Functor.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2011 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_FUNCTOR_H +#define ANDROID_FUNCTOR_H + +#include + +namespace android { + +class Functor { +public: + Functor() {} + virtual ~Functor() {} + virtual status_t operator ()() { return true; } +}; + +}; // namespace android + +#endif // ANDROID_FUNCTOR_H From ac61abe286a768445670a2d0e0cf99de0e697718 Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Mon, 24 Jan 2011 15:20:05 -0800 Subject: [PATCH 286/541] libutils: Fix race condition in Thread::requestExitAndWait() Hold a reference to the thread in requestExitAndWait() so the condition variable it is waiting on will not be destroyed before its wait() call returns. Change-Id: If8b6cf84117203926a4180f43f0224469e92a500 Signed-off-by: Mike Lockwood --- libs/utils/Threads.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp index ad9a94f59..00d009b14 100644 --- a/libs/utils/Threads.cpp +++ b/libs/utils/Threads.cpp @@ -799,6 +799,9 @@ void Thread::requestExit() status_t Thread::requestExitAndWait() { + // hold a reference so mThreadExitedCondition is not destroyed before wait() returns + sp strong(mHoldSelf); + if (mThread == getThreadId()) { LOGW( "Thread (this=%p): don't call waitForExit() from this " From a25d2caf15c8f75f94f49807e749cde298b7f631 Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Tue, 25 Jan 2011 09:41:20 -0800 Subject: [PATCH 287/541] Revert "libutils: Fix race condition in Thread::requestExitAndWait()" This change turned out to be unnecessary This reverts commit 4a7f412e1d932f46f6adf079df4d9ee7279bd795. --- libs/utils/Threads.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp index 00d009b14..ad9a94f59 100644 --- a/libs/utils/Threads.cpp +++ b/libs/utils/Threads.cpp @@ -799,9 +799,6 @@ void Thread::requestExit() status_t Thread::requestExitAndWait() { - // hold a reference so mThreadExitedCondition is not destroyed before wait() returns - sp strong(mHoldSelf); - if (mThread == getThreadId()) { LOGW( "Thread (this=%p): don't call waitForExit() from this " From 741cbc33820cb7c8891f3ed0eae820ec447e298d Mon Sep 17 00:00:00 2001 From: Ritu Srivastava Date: Tue, 25 Jan 2011 16:23:08 -0800 Subject: [PATCH 288/541] fix failing thread object run A previously exited Thread object refuses to run again, if the thread-id of the caller, conincides with the thread-id it previously used in the worker thread. Hence reset the previously used worker thread-id to -1 when it exits. Signed-off-by: Ritu Srivastava Change-Id: I873925c312a43ec8a16392b98cc959042ff6bfd2 Signed-off-by: Madan Ankapura --- libs/utils/Threads.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp index ad9a94f59..b1bd82844 100644 --- a/libs/utils/Threads.cpp +++ b/libs/utils/Threads.cpp @@ -778,6 +778,7 @@ int Thread::_threadLoop(void* user) // called by a new thread using the same thread ID as this one. self->mThread = thread_id_t(-1); self->mThreadExitedCondition.broadcast(); + self->mThread = thread_id_t(-1); // thread id could be reused self->mLock.unlock(); break; } From da74bc06a8e5c5c102fa06b0c64cd5dc00efeb97 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Thu, 27 Jan 2011 19:37:40 -0800 Subject: [PATCH 289/541] Fix issue #3392073: At times soft keyboard comes up in... ...gallery while attaching picture to gmail message In various places we could block switching the IME target incorrectly. Change-Id: I7e647fb35f4ea6f2e39eb7efd911420ea9ee64fa --- libs/utils/ResourceTypes.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp index cae91ac80..7197ad7a2 100644 --- a/libs/utils/ResourceTypes.cpp +++ b/libs/utils/ResourceTypes.cpp @@ -3703,9 +3703,9 @@ void ResTable::getConfigurations(Vector* configs) const void ResTable::getLocales(Vector* locales) const { Vector configs; - LOGD("calling getConfigurations"); + LOGV("calling getConfigurations"); getConfigurations(&configs); - LOGD("called getConfigurations size=%d", (int)configs.size()); + LOGV("called getConfigurations size=%d", (int)configs.size()); const size_t I = configs.size(); for (size_t i=0; i Date: Wed, 9 Feb 2011 18:38:55 -0800 Subject: [PATCH 290/541] fix [3408713] Dialog window invisible sometimes weak pointer comparison operators were implemented wrong, they were using the internal "unsafe" pointer. We could end up with two "equal" weak pointer pointing to different objects. this caused KeyedVector keyed by weak pointer to work incorrectly, in turn causing a window that just got added to a list to be immediately removed. Change-Id: Ib191010c39aafa9229109e4211f6c3b2b2f9696d --- include/utils/RefBase.h | 62 +++++++++++++++++++++++++++++++---------- 1 file changed, 48 insertions(+), 14 deletions(-) diff --git a/include/utils/RefBase.h b/include/utils/RefBase.h index 9c64ac044..c24c0dbcb 100644 --- a/include/utils/RefBase.h +++ b/include/utils/RefBase.h @@ -31,13 +31,10 @@ template class wp; // --------------------------------------------------------------------------- -#define COMPARE(_op_) \ +#define COMPARE_WEAK(_op_) \ inline bool operator _op_ (const sp& o) const { \ return m_ptr _op_ o.m_ptr; \ } \ -inline bool operator _op_ (const wp& o) const { \ - return m_ptr _op_ o.m_ptr; \ -} \ inline bool operator _op_ (const T* o) const { \ return m_ptr _op_ o; \ } \ @@ -46,12 +43,18 @@ inline bool operator _op_ (const sp& o) const { \ return m_ptr _op_ o.m_ptr; \ } \ template \ -inline bool operator _op_ (const wp& o) const { \ +inline bool operator _op_ (const U* o) const { \ + return m_ptr _op_ o; \ +} + +#define COMPARE(_op_) \ +COMPARE_WEAK(_op_) \ +inline bool operator _op_ (const wp& o) const { \ return m_ptr _op_ o.m_ptr; \ } \ template \ -inline bool operator _op_ (const U* o) const { \ - return m_ptr _op_ o; \ +inline bool operator _op_ (const wp& o) const { \ + return m_ptr _op_ o.m_ptr; \ } // --------------------------------------------------------------------------- @@ -274,13 +277,43 @@ public: inline T* unsafe_get() const { return m_ptr; } // Operators - - COMPARE(==) - COMPARE(!=) - COMPARE(>) - COMPARE(<) - COMPARE(<=) - COMPARE(>=) + + COMPARE_WEAK(==) + COMPARE_WEAK(!=) + COMPARE_WEAK(>) + COMPARE_WEAK(<) + COMPARE_WEAK(<=) + COMPARE_WEAK(>=) + + inline bool operator == (const wp& o) const { + return (m_ptr == o.m_ptr) && (m_refs == o.m_refs); + } + template + inline bool operator == (const wp& o) const { + return m_ptr == o.m_ptr; + } + + inline bool operator > (const wp& o) const { + return (m_ptr == o.m_ptr) ? (m_refs > o.m_refs) : (m_ptr > o.m_ptr); + } + template + inline bool operator > (const wp& o) const { + return (m_ptr == o.m_ptr) ? (m_refs > o.m_refs) : (m_ptr > o.m_ptr); + } + + inline bool operator < (const wp& o) const { + return (m_ptr == o.m_ptr) ? (m_refs < o.m_refs) : (m_ptr < o.m_ptr); + } + template + inline bool operator < (const wp& o) const { + return (m_ptr == o.m_ptr) ? (m_refs < o.m_refs) : (m_ptr < o.m_ptr); + } + inline bool operator != (const wp& o) const { return m_refs != o.m_refs; } + template inline bool operator != (const wp& o) const { return !operator == (o); } + inline bool operator <= (const wp& o) const { return !operator > (o); } + template inline bool operator <= (const wp& o) const { return !operator > (o); } + inline bool operator >= (const wp& o) const { return !operator < (o); } + template inline bool operator >= (const wp& o) const { return !operator < (o); } private: template friend class sp; @@ -294,6 +327,7 @@ template TextOutput& operator<<(TextOutput& to, const wp& val); #undef COMPARE +#undef COMPARE_WEAK // --------------------------------------------------------------------------- // No user serviceable parts below here. From dafff0bcc10fe1ff80e68a31793bbdea2ec8d0c3 Mon Sep 17 00:00:00 2001 From: Kenny Root Date: Wed, 16 Feb 2011 10:13:53 -0800 Subject: [PATCH 291/541] Clean up use of HAVE_ANDROID_OS HAVE_ANDROID_OS was defined as "1" for targets, but never defined as "0" for non-targets. Changing them to #ifdef should be safe and matches all the other uses of HAVE_ANDROID_OS throughout the system. Change-Id: I82257325a8ae5e4e4371ddfc4dbf51cea8ea0abb --- libs/utils/SystemClock.cpp | 8 ++++---- libs/utils/Threads.cpp | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/libs/utils/SystemClock.cpp b/libs/utils/SystemClock.cpp index 2bdc0ce27..062e6d76b 100644 --- a/libs/utils/SystemClock.cpp +++ b/libs/utils/SystemClock.cpp @@ -19,7 +19,7 @@ * System clock functions. */ -#if HAVE_ANDROID_OS +#ifdef HAVE_ANDROID_OS #include #include #include @@ -50,7 +50,7 @@ int setCurrentTimeMillis(int64_t millis) return -1; #else struct timeval tv; -#if HAVE_ANDROID_OS +#ifdef HAVE_ANDROID_OS struct timespec ts; int fd; int res; @@ -66,7 +66,7 @@ int setCurrentTimeMillis(int64_t millis) LOGD("Setting time of day to sec=%d\n", (int) tv.tv_sec); -#if HAVE_ANDROID_OS +#ifdef HAVE_ANDROID_OS fd = open("/dev/alarm", O_RDWR); if(fd < 0) { LOGW("Unable to open alarm driver: %s\n", strerror(errno)); @@ -106,7 +106,7 @@ int64_t uptimeMillis() */ int64_t elapsedRealtime() { -#if HAVE_ANDROID_OS +#ifdef HAVE_ANDROID_OS static int s_fd = -1; if (s_fd == -1) { diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp index b1bd82844..35dcbcb64 100644 --- a/libs/utils/Threads.cpp +++ b/libs/utils/Threads.cpp @@ -739,7 +739,7 @@ int Thread::_threadLoop(void* user) wp weak(strong); self->mHoldSelf.clear(); -#if HAVE_ANDROID_OS +#ifdef HAVE_ANDROID_OS // this is very useful for debugging with gdb self->mTid = gettid(); #endif From 84a23fa4a9a2dca0f53fae2283b57fff988d1c74 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Wed, 16 Feb 2011 15:23:08 -0800 Subject: [PATCH 292/541] Remove RefBase.h dependency on TextOutput.h Change-Id: I72cd6b98ef82b4868fe1c8ec87862cf43fb4ee73 --- include/utils/RefBase.h | 11 ++++++----- libs/utils/RefBase.cpp | 18 +++++++++++++++++- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/include/utils/RefBase.h b/include/utils/RefBase.h index c24c0dbcb..f8d96cf1e 100644 --- a/include/utils/RefBase.h +++ b/include/utils/RefBase.h @@ -18,7 +18,6 @@ #define ANDROID_REF_BASE_H #include -#include #include #include @@ -27,6 +26,10 @@ // --------------------------------------------------------------------------- namespace android { +class TextOutput; +TextOutput& printStrongPointer(TextOutput& to, const void* val); +TextOutput& printWeakPointer(TextOutput& to, const void* val); + template class wp; // --------------------------------------------------------------------------- @@ -427,8 +430,7 @@ sp::sp(T* p, weakref_type* refs) template inline TextOutput& operator<<(TextOutput& to, const sp& val) { - to << "sp<>(" << val.get() << ")"; - return to; + return printStrongPointer(to, val.get()); } // --------------------------------------------------------------------------- @@ -585,8 +587,7 @@ void wp::clear() template inline TextOutput& operator<<(TextOutput& to, const wp& val) { - to << "wp<>(" << val.unsafe_get() << ")"; - return to; + return printWeakPointer(to, val.unsafe_get()); } }; // namespace android diff --git a/libs/utils/RefBase.cpp b/libs/utils/RefBase.cpp index 0bd1af4eb..f934eec80 100644 --- a/libs/utils/RefBase.cpp +++ b/libs/utils/RefBase.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -530,5 +531,20 @@ bool RefBase::onIncStrongAttempted(uint32_t flags, const void* id) void RefBase::onLastWeakRef(const void* /*id*/) { } - + +// --------------------------------------------------------------------------- + +TextOutput& printStrongPointer(TextOutput& to, const void* val) +{ + to << "sp<>(" << val << ")"; + return to; +} + +TextOutput& printWeakPointer(TextOutput& to, const void* val) +{ + to << "wp<>(" << val << ")"; + return to; +} + + }; // namespace android From 966a48f3e78e75c3a4f3a2f23b98e970f06bf983 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Tue, 1 Feb 2011 11:32:29 -0800 Subject: [PATCH 293/541] Bug 3362814 Fix SMP race in access to mRequestExit Also fix an unlikely SMP race in access to mHoldSelf on entry to _threadLoop. Change-Id: I6cbc0b94739c7dd5e77e8a5ba0da22cdc0b1a4db --- include/utils/threads.h | 3 ++- libs/utils/Threads.cpp | 27 +++++++++++++++++++++++---- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/include/utils/threads.h b/include/utils/threads.h index 1bcfaede6..41e5766a0 100644 --- a/include/utils/threads.h +++ b/include/utils/threads.h @@ -527,9 +527,10 @@ private: static int _threadLoop(void* user); const bool mCanCallJava; thread_id_t mThread; - Mutex mLock; + mutable Mutex mLock; Condition mThreadExitedCondition; status_t mStatus; + // note that all accesses of mExitPending and mRunning need to hold mLock volatile bool mExitPending; volatile bool mRunning; sp mHoldSelf; diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp index 35dcbcb64..8b5da0e58 100644 --- a/libs/utils/Threads.cpp +++ b/libs/utils/Threads.cpp @@ -675,6 +675,9 @@ Thread::Thread(bool canCallJava) mLock("Thread::mLock"), mStatus(NO_ERROR), mExitPending(false), mRunning(false) +#ifdef HAVE_ANDROID_OS + , mTid(-1) +#endif { } @@ -715,6 +718,7 @@ status_t Thread::run(const char* name, int32_t priority, size_t stack) res = androidCreateRawThreadEtc(_threadLoop, this, name, priority, stack, &mThread); } + // The new thread wakes up at _threadLoop, but immediately blocks on mLock if (res == false) { mStatus = UNKNOWN_ERROR; // something happened! @@ -730,11 +734,19 @@ status_t Thread::run(const char* name, int32_t priority, size_t stack) // here merely indicates successfully starting the thread and does not // imply successful termination/execution. return NO_ERROR; + + // Exiting scope of mLock is a memory barrier and allows new thread to run } int Thread::_threadLoop(void* user) { Thread* const self = static_cast(user); + + // force a memory barrier before reading any fields, in particular mHoldSelf + { + Mutex::Autolock _l(self->mLock); + } + sp strong(self->mHoldSelf); wp weak(strong); self->mHoldSelf.clear(); @@ -753,7 +765,7 @@ int Thread::_threadLoop(void* user) self->mStatus = self->readyToRun(); result = (self->mStatus == NO_ERROR); - if (result && !self->mExitPending) { + if (result && !self->exitPending()) { // Binder threads (and maybe others) rely on threadLoop // running at least once after a successful ::readyToRun() // (unless, of course, the thread has already been asked to exit @@ -770,18 +782,21 @@ int Thread::_threadLoop(void* user) result = self->threadLoop(); } + // establish a scope for mLock + { + Mutex::Autolock _l(self->mLock); if (result == false || self->mExitPending) { self->mExitPending = true; - self->mLock.lock(); self->mRunning = false; // clear thread ID so that requestExitAndWait() does not exit if // called by a new thread using the same thread ID as this one. self->mThread = thread_id_t(-1); + // note that interested observers blocked in requestExitAndWait are + // awoken by broadcast, but blocked on mLock until break exits scope self->mThreadExitedCondition.broadcast(); - self->mThread = thread_id_t(-1); // thread id could be reused - self->mLock.unlock(); break; } + } // Release our strong reference, to let a chance to the thread // to die a peaceful death. @@ -795,6 +810,7 @@ int Thread::_threadLoop(void* user) void Thread::requestExit() { + Mutex::Autolock _l(mLock); mExitPending = true; } @@ -815,6 +831,8 @@ status_t Thread::requestExitAndWait() while (mRunning == true) { mThreadExitedCondition.wait(mLock); } + // This next line is probably not needed any more, but is being left for + // historical reference. Note that each interested party will clear flag. mExitPending = false; return mStatus; @@ -822,6 +840,7 @@ status_t Thread::requestExitAndWait() bool Thread::exitPending() const { + Mutex::Autolock _l(mLock); return mExitPending; } From b26ea8b30f11cf0ad11ac7983208514a1bfafb75 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Wed, 16 Feb 2011 20:23:43 -0800 Subject: [PATCH 294/541] Fix some issues with RefBase debugging. First slipt sp<> out of RefBase into StrongPointer.h so it can be reused more easily and to make it clear that it doesn't require RefBase. Note: the rest of the change only affects the system when DEBUG_REFS is enabled. The main problem we fix here is that the owner id associated with each reference could get out of date when a sp<> or wp<> was moved, for instance when they're used in a Vector< >. We fix this issue by calling into RefBase::moveReferences from a template specialization for sp and wp of the type helpers. RefBase::moveReferences() has then a chance to update the owner ids. There is a little bit of trickery to implement this generically in RefBase, where we need to use a templatized functor that can turn a sp* casted to a void* into a RefBase*. Introduced a new debug option DEBUG_REFS_FATAL_SANITY_CHECKS currently set to 0 by default as there seem to be an issue with sp which trips the sanity checks. Change-Id: I4825b21c8ec47d4a0ef35d760760ae0c9cdfbd7f --- include/utils/RefBase.h | 277 +++++++++++++--------------------- include/utils/StrongPointer.h | 224 +++++++++++++++++++++++++++ include/utils/TypeHelpers.h | 13 -- libs/utils/RefBase.cpp | 171 ++++++++++++--------- 4 files changed, 426 insertions(+), 259 deletions(-) create mode 100644 include/utils/StrongPointer.h diff --git a/include/utils/RefBase.h b/include/utils/RefBase.h index f8d96cf1e..9b0e7d8a4 100644 --- a/include/utils/RefBase.h +++ b/include/utils/RefBase.h @@ -22,16 +22,16 @@ #include #include #include +#include + +#include // --------------------------------------------------------------------------- namespace android { class TextOutput; -TextOutput& printStrongPointer(TextOutput& to, const void* val); TextOutput& printWeakPointer(TextOutput& to, const void* val); -template class wp; - // --------------------------------------------------------------------------- #define COMPARE_WEAK(_op_) \ @@ -50,15 +50,15 @@ inline bool operator _op_ (const U* o) const { \ return m_ptr _op_ o; \ } -#define COMPARE(_op_) \ -COMPARE_WEAK(_op_) \ -inline bool operator _op_ (const wp& o) const { \ - return m_ptr _op_ o.m_ptr; \ -} \ -template \ -inline bool operator _op_ (const wp& o) const { \ - return m_ptr _op_ o.m_ptr; \ -} +// --------------------------------------------------------------------------- + +class ReferenceMover; +class ReferenceConverterBase { +public: + virtual size_t getReferenceTypeSize() const = 0; + virtual void* getReferenceBase(void const*) const = 0; + inline virtual ~ReferenceConverterBase() { } +}; // --------------------------------------------------------------------------- @@ -115,6 +115,8 @@ public: getWeakRefs()->trackMe(enable, retain); } + typedef RefBase basetype; + protected: RefBase(); virtual ~RefBase(); @@ -137,6 +139,11 @@ protected: virtual bool onIncStrongAttempted(uint32_t flags, const void* id); virtual void onLastWeakRef(const void* id); +private: + friend class ReferenceMover; + static void moveReferences(void* d, void const* s, size_t n, + const ReferenceConverterBase& caster); + private: friend class weakref_type; class weakref_impl; @@ -166,76 +173,23 @@ public: inline int32_t getStrongCount() const { return mCount; } - + + typedef LightRefBase basetype; + protected: inline ~LightRefBase() { } - + +private: + friend class ReferenceMover; + inline static void moveReferences(void* d, void const* s, size_t n, + const ReferenceConverterBase& caster) { } + private: mutable volatile int32_t mCount; }; // --------------------------------------------------------------------------- -template -class sp -{ -public: - typedef typename RefBase::weakref_type weakref_type; - - inline sp() : m_ptr(0) { } - - sp(T* other); - sp(const sp& other); - template sp(U* other); - template sp(const sp& other); - - ~sp(); - - // Assignment - - sp& operator = (T* other); - sp& operator = (const sp& other); - - template sp& operator = (const sp& other); - template sp& operator = (U* other); - - //! Special optimization for use by ProcessState (and nobody else). - void force_set(T* other); - - // Reset - - void clear(); - - // Accessors - - inline T& operator* () const { return *m_ptr; } - inline T* operator-> () const { return m_ptr; } - inline T* get() const { return m_ptr; } - - // Operators - - COMPARE(==) - COMPARE(!=) - COMPARE(>) - COMPARE(<) - COMPARE(<=) - COMPARE(>=) - -private: - template friend class sp; - template friend class wp; - - // Optimization for wp::promote(). - sp(T* p, weakref_type* refs); - - T* m_ptr; -}; - -template -TextOutput& operator<<(TextOutput& to, const sp& val); - -// --------------------------------------------------------------------------- - template class wp { @@ -329,112 +283,11 @@ private: template TextOutput& operator<<(TextOutput& to, const wp& val); -#undef COMPARE #undef COMPARE_WEAK // --------------------------------------------------------------------------- // No user serviceable parts below here. -template -sp::sp(T* other) - : m_ptr(other) -{ - if (other) other->incStrong(this); -} - -template -sp::sp(const sp& other) - : m_ptr(other.m_ptr) -{ - if (m_ptr) m_ptr->incStrong(this); -} - -template template -sp::sp(U* other) : m_ptr(other) -{ - if (other) other->incStrong(this); -} - -template template -sp::sp(const sp& other) - : m_ptr(other.m_ptr) -{ - if (m_ptr) m_ptr->incStrong(this); -} - -template -sp::~sp() -{ - if (m_ptr) m_ptr->decStrong(this); -} - -template -sp& sp::operator = (const sp& other) { - T* otherPtr(other.m_ptr); - if (otherPtr) otherPtr->incStrong(this); - if (m_ptr) m_ptr->decStrong(this); - m_ptr = otherPtr; - return *this; -} - -template -sp& sp::operator = (T* other) -{ - if (other) other->incStrong(this); - if (m_ptr) m_ptr->decStrong(this); - m_ptr = other; - return *this; -} - -template template -sp& sp::operator = (const sp& other) -{ - U* otherPtr(other.m_ptr); - if (otherPtr) otherPtr->incStrong(this); - if (m_ptr) m_ptr->decStrong(this); - m_ptr = otherPtr; - return *this; -} - -template template -sp& sp::operator = (U* other) -{ - if (other) other->incStrong(this); - if (m_ptr) m_ptr->decStrong(this); - m_ptr = other; - return *this; -} - -template -void sp::force_set(T* other) -{ - other->forceIncStrong(this); - m_ptr = other; -} - -template -void sp::clear() -{ - if (m_ptr) { - m_ptr->decStrong(this); - m_ptr = 0; - } -} - -template -sp::sp(T* p, weakref_type* refs) - : m_ptr((p && refs->attemptIncStrong(this)) ? p : 0) -{ -} - -template -inline TextOutput& operator<<(TextOutput& to, const sp& val) -{ - return printStrongPointer(to, val.get()); -} - -// --------------------------------------------------------------------------- - template wp::wp(T* other) : m_ptr(other) @@ -572,7 +425,8 @@ void wp::set_object_and_refs(T* other, weakref_type* refs) template sp wp::promote() const { - return sp(m_ptr, m_refs); + T* p = (m_ptr && m_refs->attemptIncStrong(this)) ? m_ptr : 0; + return sp(p, true); } template @@ -590,6 +444,77 @@ inline TextOutput& operator<<(TextOutput& to, const wp& val) return printWeakPointer(to, val.unsafe_get()); } +// --------------------------------------------------------------------------- + +// this class just serves as a namespace so TYPE::moveReferences can stay +// private. + +class ReferenceMover { + // StrongReferenceCast and WeakReferenceCast do the impedance matching + // between the generic (void*) implementation in Refbase and the strongly typed + // template specializations below. + + template + struct StrongReferenceCast : public ReferenceConverterBase { + virtual size_t getReferenceTypeSize() const { return sizeof( sp ); } + virtual void* getReferenceBase(void const* p) const { + sp const* sptr(reinterpret_cast const*>(p)); + return static_cast(sptr->get()); + } + }; + + template + struct WeakReferenceCast : public ReferenceConverterBase { + virtual size_t getReferenceTypeSize() const { return sizeof( wp ); } + virtual void* getReferenceBase(void const* p) const { + wp const* sptr(reinterpret_cast const*>(p)); + return static_cast(sptr->unsafe_get()); + } + }; + +public: + template static inline + void move_references(sp* d, sp const* s, size_t n) { + memmove(d, s, n*sizeof(sp)); + StrongReferenceCast caster; + TYPE::moveReferences(d, s, n, caster); + } + template static inline + void move_references(wp* d, wp const* s, size_t n) { + memmove(d, s, n*sizeof(wp)); + WeakReferenceCast caster; + TYPE::moveReferences(d, s, n, caster); + } +}; + +// specialization for moving sp<> and wp<> types. +// these are used by the [Sorted|Keyed]Vector<> implementations +// sp<> and wp<> need to be handled specially, because they do not +// have trivial copy operation in the general case (see RefBase.cpp +// when DEBUG ops are enabled), but can be implemented very +// efficiently in most cases. + +template inline +void move_forward_type(sp* d, sp const* s, size_t n) { + ReferenceMover::move_references(d, s, n); +} + +template inline +void move_backward_type(sp* d, sp const* s, size_t n) { + ReferenceMover::move_references(d, s, n); +} + +template inline +void move_forward_type(wp* d, wp const* s, size_t n) { + ReferenceMover::move_references(d, s, n); +} + +template inline +void move_backward_type(wp* d, wp const* s, size_t n) { + ReferenceMover::move_references(d, s, n); +} + + }; // namespace android // --------------------------------------------------------------------------- diff --git a/include/utils/StrongPointer.h b/include/utils/StrongPointer.h new file mode 100644 index 000000000..5daccf4f0 --- /dev/null +++ b/include/utils/StrongPointer.h @@ -0,0 +1,224 @@ +/* + * Copyright (C) 2005 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_STRONG_POINTER_H +#define ANDROID_STRONG_POINTER_H + +#include + +#include +#include +#include + +// --------------------------------------------------------------------------- +namespace android { + +class TextOutput; +TextOutput& printStrongPointer(TextOutput& to, const void* val); + +template class wp; + +// --------------------------------------------------------------------------- + +#define COMPARE(_op_) \ +inline bool operator _op_ (const sp& o) const { \ + return m_ptr _op_ o.m_ptr; \ +} \ +inline bool operator _op_ (const T* o) const { \ + return m_ptr _op_ o; \ +} \ +template \ +inline bool operator _op_ (const sp& o) const { \ + return m_ptr _op_ o.m_ptr; \ +} \ +template \ +inline bool operator _op_ (const U* o) const { \ + return m_ptr _op_ o; \ +} \ +inline bool operator _op_ (const wp& o) const { \ + return m_ptr _op_ o.m_ptr; \ +} \ +template \ +inline bool operator _op_ (const wp& o) const { \ + return m_ptr _op_ o.m_ptr; \ +} + +// --------------------------------------------------------------------------- + +template +class sp +{ +public: + inline sp() : m_ptr(0) { } + + sp(T* other); + sp(const sp& other); + template sp(U* other); + template sp(const sp& other); + + ~sp(); + + // Assignment + + sp& operator = (T* other); + sp& operator = (const sp& other); + + template sp& operator = (const sp& other); + template sp& operator = (U* other); + + //! Special optimization for use by ProcessState (and nobody else). + void force_set(T* other); + + // Reset + + void clear(); + + // Accessors + + inline T& operator* () const { return *m_ptr; } + inline T* operator-> () const { return m_ptr; } + inline T* get() const { return m_ptr; } + + // Operators + + COMPARE(==) + COMPARE(!=) + COMPARE(>) + COMPARE(<) + COMPARE(<=) + COMPARE(>=) + +private: + template friend class sp; + template friend class wp; + + // Optimization for wp::promote(). + sp(T* p, bool); + + T* m_ptr; +}; + +#undef COMPARE + +template +TextOutput& operator<<(TextOutput& to, const sp& val); + +// --------------------------------------------------------------------------- +// No user serviceable parts below here. + +template +sp::sp(T* other) +: m_ptr(other) + { + if (other) other->incStrong(this); + } + +template +sp::sp(const sp& other) +: m_ptr(other.m_ptr) + { + if (m_ptr) m_ptr->incStrong(this); + } + +template template +sp::sp(U* other) : m_ptr(other) +{ + if (other) other->incStrong(this); +} + +template template +sp::sp(const sp& other) +: m_ptr(other.m_ptr) + { + if (m_ptr) m_ptr->incStrong(this); + } + +template +sp::~sp() +{ + if (m_ptr) m_ptr->decStrong(this); +} + +template +sp& sp::operator = (const sp& other) { + T* otherPtr(other.m_ptr); + if (otherPtr) otherPtr->incStrong(this); + if (m_ptr) m_ptr->decStrong(this); + m_ptr = otherPtr; + return *this; +} + +template +sp& sp::operator = (T* other) +{ + if (other) other->incStrong(this); + if (m_ptr) m_ptr->decStrong(this); + m_ptr = other; + return *this; +} + +template template +sp& sp::operator = (const sp& other) +{ + U* otherPtr(other.m_ptr); + if (otherPtr) otherPtr->incStrong(this); + if (m_ptr) m_ptr->decStrong(this); + m_ptr = otherPtr; + return *this; +} + +template template +sp& sp::operator = (U* other) +{ + if (other) other->incStrong(this); + if (m_ptr) m_ptr->decStrong(this); + m_ptr = other; + return *this; +} + +template +void sp::force_set(T* other) +{ + other->forceIncStrong(this); + m_ptr = other; +} + +template +void sp::clear() +{ + if (m_ptr) { + m_ptr->decStrong(this); + m_ptr = 0; + } +} + +template +sp::sp(T* p, bool) +: m_ptr(p) + { + } + +template +inline TextOutput& operator<<(TextOutput& to, const sp& val) +{ + return printStrongPointer(to, val.get()); +} + +}; // namespace android + +// --------------------------------------------------------------------------- + +#endif // ANDROID_STRONG_POINTER_H diff --git a/include/utils/TypeHelpers.h b/include/utils/TypeHelpers.h index 2ff2749ba..a1663f30e 100644 --- a/include/utils/TypeHelpers.h +++ b/include/utils/TypeHelpers.h @@ -37,18 +37,6 @@ template struct trait_trivial_move { enum { value = false }; }; template struct trait_pointer { enum { value = false }; }; template struct trait_pointer { enum { value = true }; }; -// sp<> can be trivially moved -template class sp; -template struct trait_trivial_move< sp >{ - enum { value = true }; -}; - -// wp<> can be trivially moved -template class wp; -template struct trait_trivial_move< wp >{ - enum { value = true }; -}; - template struct traits { enum { @@ -217,7 +205,6 @@ void move_backward_type(TYPE* d, const TYPE* s, size_t n = 1) { } } - // --------------------------------------------------------------------------- /* diff --git a/libs/utils/RefBase.cpp b/libs/utils/RefBase.cpp index f934eec80..0fd404d66 100644 --- a/libs/utils/RefBase.cpp +++ b/libs/utils/RefBase.cpp @@ -20,7 +20,6 @@ #include #include -#include #include #include #include @@ -35,6 +34,7 @@ // compile with refcounting debugging enabled #define DEBUG_REFS 0 +#define DEBUG_REFS_FATAL_SANITY_CHECKS 0 #define DEBUG_REFS_ENABLED_BY_DEFAULT 1 #define DEBUG_REFS_CALLSTACK_ENABLED 1 @@ -70,8 +70,10 @@ public: void addStrongRef(const void* /*id*/) { } void removeStrongRef(const void* /*id*/) { } + void renameStrongRefId(const void* /*old_id*/, const void* /*new_id*/) { } void addWeakRef(const void* /*id*/) { } void removeWeakRef(const void* /*id*/) { } + void renameWeakRefId(const void* /*old_id*/, const void* /*new_id*/) { } void printRefs() const { } void trackMe(bool, bool) { } @@ -87,39 +89,73 @@ public: , mTrackEnabled(!!DEBUG_REFS_ENABLED_BY_DEFAULT) , mRetain(false) { - //LOGI("NEW weakref_impl %p for RefBase %p", this, base); } ~weakref_impl() { - LOG_ALWAYS_FATAL_IF(!mRetain && mStrongRefs != NULL, "Strong references remain!"); - LOG_ALWAYS_FATAL_IF(!mRetain && mWeakRefs != NULL, "Weak references remain!"); + bool dumpStack = false; + if (!mRetain && mStrongRefs != NULL) { + dumpStack = true; +#if DEBUG_REFS_FATAL_SANITY_CHECKS + LOG_ALWAYS_FATAL("Strong references remain!"); +#else + LOGE("Strong references remain!"); +#endif + } + + if (!mRetain && mWeakRefs != NULL) { + dumpStack = true; +#if DEBUG_REFS_FATAL_SANITY_CHECKS + LOG_ALWAYS_FATAL("Weak references remain!"); +#else + LOGE("Weak references remain!"); +#endif + } + + if (dumpStack) { + CallStack stack; + stack.update(); + stack.dump(); + } } - void addStrongRef(const void* id) - { + void addStrongRef(const void* id) { + //LOGD_IF(mTrackEnabled, + // "addStrongRef: RefBase=%p, id=%p", mBase, id); addRef(&mStrongRefs, id, mStrong); } - void removeStrongRef(const void* id) - { - if (!mRetain) + void removeStrongRef(const void* id) { + //LOGD_IF(mTrackEnabled, + // "removeStrongRef: RefBase=%p, id=%p", mBase, id); + if (!mRetain) { removeRef(&mStrongRefs, id); - else + } else { addRef(&mStrongRefs, id, -mStrong); + } } - void addWeakRef(const void* id) - { + void renameStrongRefId(const void* old_id, const void* new_id) { + //LOGD_IF(mTrackEnabled, + // "renameStrongRefId: RefBase=%p, oid=%p, nid=%p", + // mBase, old_id, new_id); + renameRefsId(mStrongRefs, old_id, new_id); + } + + void addWeakRef(const void* id) { addRef(&mWeakRefs, id, mWeak); } - void removeWeakRef(const void* id) - { - if (!mRetain) + void removeWeakRef(const void* id) { + if (!mRetain) { removeRef(&mWeakRefs, id); - else + } else { addRef(&mWeakRefs, id, -mWeak); + } + } + + void renameWeakRefId(const void* old_id, const void* new_id) { + renameRefsId(mWeakRefs, old_id, new_id); } void trackMe(bool track, bool retain) @@ -133,8 +169,7 @@ public: String8 text; { - AutoMutex _l(const_cast(this)->mMutex); - + Mutex::Autolock _l(const_cast(this)->mMutex); char buf[128]; sprintf(buf, "Strong references on RefBase %p (weakref_type %p):\n", mBase, this); text.append(buf); @@ -173,6 +208,7 @@ private: { if (mTrackEnabled) { AutoMutex _l(mMutex); + ref_entry* ref = new ref_entry; // Reference count at the time of the snapshot, but before the // update. Positive value means we increment, negative--we @@ -182,7 +218,6 @@ private: #if DEBUG_REFS_CALLSTACK_ENABLED ref->stack.update(2); #endif - ref->next = *refs; *refs = ref; } @@ -200,13 +235,37 @@ private: delete ref; return; } - refs = &ref->next; ref = *refs; } - - LOG_ALWAYS_FATAL("RefBase: removing id %p on RefBase %p (weakref_type %p) that doesn't exist!", - id, mBase, this); + +#if DEBUG_REFS_FATAL_SANITY_CHECKS + LOG_ALWAYS_FATAL("RefBase: removing id %p on RefBase %p" + "(weakref_type %p) that doesn't exist!", + id, mBase, this); +#endif + + LOGE("RefBase: removing id %p on RefBase %p" + "(weakref_type %p) that doesn't exist!", + id, mBase, this); + + CallStack stack; + stack.update(); + stack.dump(); + } + } + + void renameRefsId(ref_entry* r, const void* old_id, const void* new_id) + { + if (mTrackEnabled) { + AutoMutex _l(mMutex); + ref_entry* ref = r; + while (ref != NULL) { + if (ref->id == old_id) { + ref->id = new_id; + } + ref = ref->next; + } } } @@ -236,44 +295,6 @@ private: // on removeref that match the address ones. bool mRetain; -#if 0 - void addRef(KeyedVector* refs, const void* id) - { - AutoMutex _l(mMutex); - ssize_t i = refs->indexOfKey(id); - if (i >= 0) { - ++(refs->editValueAt(i)); - } else { - i = refs->add(id, 1); - } - } - - void removeRef(KeyedVector* refs, const void* id) - { - AutoMutex _l(mMutex); - ssize_t i = refs->indexOfKey(id); - LOG_ALWAYS_FATAL_IF(i < 0, "RefBase: removing id %p that doesn't exist!", id); - if (i >= 0) { - int32_t val = --(refs->editValueAt(i)); - if (val == 0) { - refs->removeItemsAt(i); - } - } - } - - void printRefs(const KeyedVector& refs) - { - const size_t N=refs.size(); - for (size_t i=0; i mStrongRefs; - KeyedVector mWeakRefs; -#endif - #endif }; @@ -282,7 +303,6 @@ private: void RefBase::incStrong(const void* id) const { weakref_impl* const refs = mRefs; - refs->addWeakRef(id); refs->incWeak(id); refs->addStrongRef(id); @@ -314,14 +334,12 @@ void RefBase::decStrong(const void* id) const delete this; } } - refs->removeWeakRef(id); refs->decWeak(id); } void RefBase::forceIncStrong(const void* id) const { weakref_impl* const refs = mRefs; - refs->addWeakRef(id); refs->incWeak(id); refs->addStrongRef(id); @@ -373,7 +391,7 @@ void RefBase::weakref_type::decWeak(const void* id) if (impl->mStrong == INITIAL_STRONG_VALUE) delete impl->mBase; else { -// LOGV("Freeing refs %p of old RefBase %p\n", this, impl->mBase); + // LOGV("Freeing refs %p of old RefBase %p\n", this, impl->mBase); delete impl; } } else { @@ -433,7 +451,6 @@ bool RefBase::weakref_type::attemptIncStrong(const void* id) } } - impl->addWeakRef(id); impl->addStrongRef(id); #if PRINT_REFS @@ -451,7 +468,7 @@ bool RefBase::weakref_type::attemptIncStrong(const void* id) bool RefBase::weakref_type::attemptIncWeak(const void* id) { weakref_impl* const impl = static_cast(this); - + int32_t curCount = impl->mWeak; LOG_ASSERT(curCount >= 0, "attemptIncWeak called on %p after underflow", this); @@ -498,14 +515,11 @@ RefBase::weakref_type* RefBase::getWeakRefs() const RefBase::RefBase() : mRefs(new weakref_impl(this)) { -// LOGV("Creating refs %p with RefBase %p\n", mRefs, this); } RefBase::~RefBase() { -// LOGV("Destroying RefBase %p (refs %p)\n", this, mRefs); if (mRefs->mWeak == 0) { -// LOGV("Freeing refs %p of old RefBase %p\n", mRefs, this); delete mRefs; } } @@ -534,6 +548,23 @@ void RefBase::onLastWeakRef(const void* /*id*/) // --------------------------------------------------------------------------- +void RefBase::moveReferences(void* dst, void const* src, size_t n, + const ReferenceConverterBase& caster) +{ +#if DEBUG_REFS + const size_t itemSize = caster.getReferenceTypeSize(); + for (size_t i=0 ; i(intptr_t(dst) + i*itemSize); + void const* s = reinterpret_cast(intptr_t(src) + i*itemSize); + RefBase* ref(reinterpret_cast(caster.getReferenceBase(d))); + ref->mRefs->renameStrongRefId(s, d); + ref->mRefs->renameWeakRefId(s, d); + } +#endif +} + +// --------------------------------------------------------------------------- + TextOutput& printStrongPointer(TextOutput& to, const void* val) { to << "sp<>(" << val << ")"; From 3e0f87541f7871ffde51c722d54550774fe1b7c3 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Thu, 24 Feb 2011 18:12:34 -0800 Subject: [PATCH 295/541] Fix a wp<> bug where the owner ID would be wrong this was introduced recently. we make sure to use the correct owner id (the sp) instead of the wp. Change-Id: I78fdc6ec0c2d3e687278b70442d74d1924b512a2 --- include/utils/RefBase.h | 7 +++++-- include/utils/StrongPointer.h | 14 +++++--------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/include/utils/RefBase.h b/include/utils/RefBase.h index 9b0e7d8a4..f35508771 100644 --- a/include/utils/RefBase.h +++ b/include/utils/RefBase.h @@ -425,8 +425,11 @@ void wp::set_object_and_refs(T* other, weakref_type* refs) template sp wp::promote() const { - T* p = (m_ptr && m_refs->attemptIncStrong(this)) ? m_ptr : 0; - return sp(p, true); + sp result; + if (m_ptr && m_refs->attemptIncStrong(&result)) { + result.set_pointer(m_ptr); + } + return result; } template diff --git a/include/utils/StrongPointer.h b/include/utils/StrongPointer.h index 5daccf4f0..a8c989749 100644 --- a/include/utils/StrongPointer.h +++ b/include/utils/StrongPointer.h @@ -104,11 +104,8 @@ public: private: template friend class sp; template friend class wp; - - // Optimization for wp::promote(). - sp(T* p, bool); - - T* m_ptr; + void set_pointer(T* ptr); + T* m_ptr; }; #undef COMPARE @@ -206,10 +203,9 @@ void sp::clear() } template -sp::sp(T* p, bool) -: m_ptr(p) - { - } +void sp::set_pointer(T* ptr) { + m_ptr = ptr; +} template inline TextOutput& operator<<(TextOutput& to, const sp& val) From 7332f80db5b942253f69b9b2e6cc3ae08d957d0e Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Fri, 25 Feb 2011 16:11:44 -0800 Subject: [PATCH 296/541] Fix sp<> conversion operator / constructor some of the conversion operators were not using the proper pointer type when calling incStrong/decStrong, usually it has no bad consequences, but for some implementation of the ref-counted object it could lead to recording the wrong owner id. Change-Id: If574b9069b8a4cf6e0911a992c8f095aba799995 --- include/utils/StrongPointer.h | 6 +++--- libs/utils/RefBase.cpp | 34 ++++++++++++++++++++++++++++++---- 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/include/utils/StrongPointer.h b/include/utils/StrongPointer.h index a8c989749..49fa3a8d6 100644 --- a/include/utils/StrongPointer.h +++ b/include/utils/StrongPointer.h @@ -133,7 +133,7 @@ sp::sp(const sp& other) template template sp::sp(U* other) : m_ptr(other) { - if (other) other->incStrong(this); + if (other) ((T*)other)->incStrong(this); } template template @@ -170,7 +170,7 @@ sp& sp::operator = (T* other) template template sp& sp::operator = (const sp& other) { - U* otherPtr(other.m_ptr); + T* otherPtr(other.m_ptr); if (otherPtr) otherPtr->incStrong(this); if (m_ptr) m_ptr->decStrong(this); m_ptr = otherPtr; @@ -180,7 +180,7 @@ sp& sp::operator = (const sp& other) template template sp& sp::operator = (U* other) { - if (other) other->incStrong(this); + if (other) ((T*)other)->incStrong(this); if (m_ptr) m_ptr->decStrong(this); m_ptr = other; return *this; diff --git a/libs/utils/RefBase.cpp b/libs/utils/RefBase.cpp index 0fd404d66..bb6c1255f 100644 --- a/libs/utils/RefBase.cpp +++ b/libs/utils/RefBase.cpp @@ -99,20 +99,38 @@ public: #if DEBUG_REFS_FATAL_SANITY_CHECKS LOG_ALWAYS_FATAL("Strong references remain!"); #else - LOGE("Strong references remain!"); + LOGE("Strong references remain:"); #endif + ref_entry* refs = mStrongRefs; + while (refs) { + char inc = refs->ref >= 0 ? '+' : '-'; + LOGD("\t%c ID %p (ref %d):", inc, refs->id, refs->ref); +#if DEBUG_REFS_CALLSTACK_ENABLED + refs->stack.dump(); +#endif; + refs = refs->next; + } } if (!mRetain && mWeakRefs != NULL) { dumpStack = true; #if DEBUG_REFS_FATAL_SANITY_CHECKS - LOG_ALWAYS_FATAL("Weak references remain!"); + LOG_ALWAYS_FATAL("Weak references remain:"); #else LOGE("Weak references remain!"); #endif + ref_entry* refs = mWeakRefs; + while (refs) { + char inc = refs->ref >= 0 ? '+' : '-'; + LOGD("\t%c ID %p (ref %d):", inc, refs->id, refs->ref); +#if DEBUG_REFS_CALLSTACK_ENABLED + refs->stack.dump(); +#endif; + refs = refs->next; + } } - if (dumpStack) { + LOGE("above errors at:"); CallStack stack; stack.update(); stack.dump(); @@ -228,7 +246,8 @@ private: if (mTrackEnabled) { AutoMutex _l(mMutex); - ref_entry* ref = *refs; + ref_entry* const head = *refs; + ref_entry* ref = head; while (ref != NULL) { if (ref->id == id) { *refs = ref->next; @@ -249,6 +268,13 @@ private: "(weakref_type %p) that doesn't exist!", id, mBase, this); + ref = head; + while (ref) { + char inc = ref->ref >= 0 ? '+' : '-'; + LOGD("\t%c ID %p (ref %d):", inc, ref->id, ref->ref); + ref = ref->next; + } + CallStack stack; stack.update(); stack.dump(); From 5a14f4a721fe052be8cfbfaf603c4d262edd686a Mon Sep 17 00:00:00 2001 From: Fabrice Di Meglio Date: Thu, 24 Feb 2011 19:56:18 -0800 Subject: [PATCH 297/541] Add TextLayout Cache - use GenerationCache for caching - move GenerationCache.h from libs/hwui/utils to include/utils - add #define for cache activation / deactivation Change-Id: Ifaf519f0b5e33b087a453e4aa6430162d8438f20 --- GenerationCache.h | 245 ++++++++++++++++++++++++++++++ include/utils/GenerationCache.h | 255 ++++++++++++++++++++++++++++++++ 2 files changed, 500 insertions(+) create mode 100644 GenerationCache.h create mode 100644 include/utils/GenerationCache.h diff --git a/GenerationCache.h b/GenerationCache.h new file mode 100644 index 000000000..42e6d9bb8 --- /dev/null +++ b/GenerationCache.h @@ -0,0 +1,245 @@ +/* + * Copyright (C) 2010 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_HWUI_GENERATION_CACHE_H +#define ANDROID_HWUI_GENERATION_CACHE_H + +#include +#include + +namespace android { +namespace uirenderer { + +template +class OnEntryRemoved { +public: + virtual ~OnEntryRemoved() { }; + virtual void operator()(EntryKey& key, EntryValue& value) = 0; +}; // class OnEntryRemoved + +template +struct Entry: public LightRefBase > { + Entry() { } + Entry(const Entry& e): + key(e.key), value(e.value), parent(e.parent), child(e.child) { } + Entry(sp > e): + key(e->key), value(e->value), parent(e->parent), child(e->child) { } + + EntryKey key; + EntryValue value; + + sp > parent; + sp > child; +}; // struct Entry + +template +class GenerationCache { +public: + GenerationCache(uint32_t maxCapacity); + virtual ~GenerationCache(); + + enum Capacity { + kUnlimitedCapacity, + }; + + void setOnEntryRemovedListener(OnEntryRemoved* listener); + + void clear(); + + bool contains(K key) const; + V get(K key); + K getKeyAt(uint32_t index) const; + bool put(K key, V value); + V remove(K key); + V removeOldest(); + V getValueAt(uint32_t index) const; + + uint32_t size() const; + + void addToCache(sp > entry, K key, V value); + void attachToCache(sp > entry); + void detachFromCache(sp > entry); + + V removeAt(ssize_t index); + + KeyedVector > > mCache; + uint32_t mMaxCapacity; + + OnEntryRemoved* mListener; + + sp > mOldest; + sp > mYoungest; +}; // class GenerationCache + +template +GenerationCache::GenerationCache(uint32_t maxCapacity): mMaxCapacity(maxCapacity), mListener(NULL) { +}; + +template +GenerationCache::~GenerationCache() { + clear(); +}; + +template +uint32_t GenerationCache::size() const { + return mCache.size(); +} + +template +void GenerationCache::setOnEntryRemovedListener(OnEntryRemoved* listener) { + mListener = listener; +} + +template +void GenerationCache::clear() { + if (mListener) { + for (uint32_t i = 0; i < mCache.size(); i++) { + sp > entry = mCache.valueAt(i); + if (mListener) { + (*mListener)(entry->key, entry->value); + } + } + } + mCache.clear(); + mYoungest.clear(); + mOldest.clear(); +} + +template +bool GenerationCache::contains(K key) const { + return mCache.indexOfKey(key) >= 0; +} + +template +K GenerationCache::getKeyAt(uint32_t index) const { + return mCache.keyAt(index); +} + +template +V GenerationCache::getValueAt(uint32_t index) const { + return mCache.valueAt(index)->value; +} + +template +V GenerationCache::get(K key) { + ssize_t index = mCache.indexOfKey(key); + if (index >= 0) { + sp > entry = mCache.valueAt(index); + if (entry.get()) { + detachFromCache(entry); + attachToCache(entry); + return entry->value; + } + } + + return NULL; +} + +template +bool GenerationCache::put(K key, V value) { + if (mMaxCapacity != kUnlimitedCapacity && mCache.size() >= mMaxCapacity) { + removeOldest(); + } + + ssize_t index = mCache.indexOfKey(key); + if (index < 0) { + sp > entry = new Entry; + addToCache(entry, key, value); + return true; + } + + return false; +} + +template +void GenerationCache::addToCache(sp > entry, K key, V value) { + entry->key = key; + entry->value = value; + mCache.add(key, entry); + attachToCache(entry); +} + +template +V GenerationCache::remove(K key) { + ssize_t index = mCache.indexOfKey(key); + if (index >= 0) { + return removeAt(index); + } + + return NULL; +} + +template +V GenerationCache::removeAt(ssize_t index) { + sp > entry = mCache.valueAt(index); + if (mListener) { + (*mListener)(entry->key, entry->value); + } + mCache.removeItemsAt(index, 1); + detachFromCache(entry); + + return entry->value; +} + +template +V GenerationCache::removeOldest() { + if (mOldest.get()) { + ssize_t index = mCache.indexOfKey(mOldest->key); + if (index >= 0) { + return removeAt(index); + } + } + + return NULL; +} + +template +void GenerationCache::attachToCache(sp > entry) { + if (!mYoungest.get()) { + mYoungest = mOldest = entry; + } else { + entry->parent = mYoungest; + mYoungest->child = entry; + mYoungest = entry; + } +} + +template +void GenerationCache::detachFromCache(sp > entry) { + if (entry->parent.get()) { + entry->parent->child = entry->child; + } + + if (entry->child.get()) { + entry->child->parent = entry->parent; + } + + if (mOldest == entry) { + mOldest = entry->child; + } + + if (mYoungest == entry) { + mYoungest = entry->parent; + } + + entry->parent.clear(); + entry->child.clear(); +} + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_HWUI_GENERATION_CACHE_H diff --git a/include/utils/GenerationCache.h b/include/utils/GenerationCache.h new file mode 100644 index 000000000..bb9ddd677 --- /dev/null +++ b/include/utils/GenerationCache.h @@ -0,0 +1,255 @@ +/* + * Copyright (C) 2010 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_UTILS_GENERATION_CACHE_H +#define ANDROID_UTILS_GENERATION_CACHE_H + +#include +#include + +namespace android { + +/** + * GenerationCache callback used when an item is removed + */ +template +class OnEntryRemoved { +public: + virtual ~OnEntryRemoved() { }; + virtual void operator()(EntryKey& key, EntryValue& value) = 0; +}; // class OnEntryRemoved + +template +struct Entry: public LightRefBase > { + Entry() { } + Entry(const Entry& e): + key(e.key), value(e.value), parent(e.parent), child(e.child) { } + Entry(sp > e): + key(e->key), value(e->value), parent(e->parent), child(e->child) { } + + EntryKey key; + EntryValue value; + + sp > parent; + sp > child; +}; // struct Entry + +/** + * A LRU type cache + */ +template +class GenerationCache { +public: + GenerationCache(uint32_t maxCapacity); + virtual ~GenerationCache(); + + enum Capacity { + kUnlimitedCapacity, + }; + + void setOnEntryRemovedListener(OnEntryRemoved* listener); + + void clear(); + + bool contains(K key) const; + V get(K key); + K getKeyAt(uint32_t index) const; + bool put(K key, V value); + V remove(K key); + V removeOldest(); + V getValueAt(uint32_t index) const; + + uint32_t size() const; + + void addToCache(sp > entry, K key, V value); + void attachToCache(sp > entry); + void detachFromCache(sp > entry); + + V removeAt(ssize_t index); + +private: + KeyedVector > > mCache; + uint32_t mMaxCapacity; + + OnEntryRemoved* mListener; + + sp > mOldest; + sp > mYoungest; +}; // class GenerationCache + +template +GenerationCache::GenerationCache(uint32_t maxCapacity): mMaxCapacity(maxCapacity), + mListener(NULL) { +}; + +template +GenerationCache::~GenerationCache() { + clear(); +}; + +template +uint32_t GenerationCache::size() const { + return mCache.size(); +} + +/** + * Should be set by the user of the Cache so that the callback is called whenever an item is + * removed from the cache + */ +template +void GenerationCache::setOnEntryRemovedListener(OnEntryRemoved* listener) { + mListener = listener; +} + +template +void GenerationCache::clear() { + if (mListener) { + for (uint32_t i = 0; i < mCache.size(); i++) { + sp > entry = mCache.valueAt(i); + if (mListener) { + (*mListener)(entry->key, entry->value); + } + } + } + mCache.clear(); + mYoungest.clear(); + mOldest.clear(); +} + +template +bool GenerationCache::contains(K key) const { + return mCache.indexOfKey(key) >= 0; +} + +template +K GenerationCache::getKeyAt(uint32_t index) const { + return mCache.keyAt(index); +} + +template +V GenerationCache::getValueAt(uint32_t index) const { + return mCache.valueAt(index)->value; +} + +template +V GenerationCache::get(K key) { + ssize_t index = mCache.indexOfKey(key); + if (index >= 0) { + sp > entry = mCache.valueAt(index); + if (entry.get()) { + detachFromCache(entry); + attachToCache(entry); + return entry->value; + } + } + + return NULL; +} + +template +bool GenerationCache::put(K key, V value) { + if (mMaxCapacity != kUnlimitedCapacity && mCache.size() >= mMaxCapacity) { + removeOldest(); + } + + ssize_t index = mCache.indexOfKey(key); + if (index < 0) { + sp > entry = new Entry; + addToCache(entry, key, value); + return true; + } + + return false; +} + +template +void GenerationCache::addToCache(sp > entry, K key, V value) { + entry->key = key; + entry->value = value; + mCache.add(key, entry); + attachToCache(entry); +} + +template +V GenerationCache::remove(K key) { + ssize_t index = mCache.indexOfKey(key); + if (index >= 0) { + return removeAt(index); + } + + return NULL; +} + +template +V GenerationCache::removeAt(ssize_t index) { + sp > entry = mCache.valueAt(index); + if (mListener) { + (*mListener)(entry->key, entry->value); + } + mCache.removeItemsAt(index, 1); + detachFromCache(entry); + + return entry->value; +} + +template +V GenerationCache::removeOldest() { + if (mOldest.get()) { + ssize_t index = mCache.indexOfKey(mOldest->key); + if (index >= 0) { + return removeAt(index); + } + } + + return NULL; +} + +template +void GenerationCache::attachToCache(sp > entry) { + if (!mYoungest.get()) { + mYoungest = mOldest = entry; + } else { + entry->parent = mYoungest; + mYoungest->child = entry; + mYoungest = entry; + } +} + +template +void GenerationCache::detachFromCache(sp > entry) { + if (entry->parent.get()) { + entry->parent->child = entry->child; + } + + if (entry->child.get()) { + entry->child->parent = entry->parent; + } + + if (mOldest == entry) { + mOldest = entry->child; + } + + if (mYoungest == entry) { + mYoungest = entry->parent; + } + + entry->parent.clear(); + entry->child.clear(); +} + +}; // namespace android + +#endif // ANDROID_UTILS_GENERATION_CACHE_H From 3e2e38bc5bbf658eb9940f72974270b11c5b84e1 Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Wed, 2 Mar 2011 14:41:58 -0800 Subject: [PATCH 298/541] Fade out the mouse pointer after inactivity or other events. Fades out the mouse pointer: - after 15 seconds of inactivity normally - after 3 seconds of inactivity in lights out mode - after a non-modifier key down - after a touch down Extended the native Looper to support enqueuing time delayed messages. This is used by the PointerController to control pointer fade timing. Change-Id: I87792fea7dbe2d9376c78cf354fe3189a484d9da --- include/utils/Looper.h | 107 ++++++++++++ libs/utils/Looper.cpp | 212 ++++++++++++++++++++---- libs/utils/tests/Looper_test.cpp | 268 +++++++++++++++++++++++++++++++ 3 files changed, 557 insertions(+), 30 deletions(-) diff --git a/include/utils/Looper.h b/include/utils/Looper.h index eefff3124..3c2905d59 100644 --- a/include/utils/Looper.h +++ b/include/utils/Looper.h @@ -44,6 +44,51 @@ struct ALooper { namespace android { +/** + * A message that can be posted to a Looper. + */ +struct Message { + Message() : what(0) { } + Message(int what) : what(what) { } + + /* The message type. (interpretation is left up to the handler) */ + int what; +}; + + +/** + * Interface for a Looper message handler. + * + * The Looper holds a strong reference to the message handler whenever it has + * a message to deliver to it. Make sure to call Looper::removeMessages + * to remove any pending messages destined for the handler so that the handler + * can be destroyed. + */ +class MessageHandler : public virtual RefBase { +protected: + virtual ~MessageHandler() { } + +public: + /** + * Handles a message. + */ + virtual void handleMessage(const Message& message) = 0; +}; + + +/** + * A simple proxy that holds a weak reference to a message handler. + */ +class WeakMessageHandler : public MessageHandler { +public: + WeakMessageHandler(const wp& handler); + virtual void handleMessage(const Message& message); + +private: + wp mHandler; +}; + + /** * A polling loop that supports monitoring file descriptor events, optionally * using callbacks. The implementation uses epoll() internally. @@ -165,6 +210,52 @@ public: */ int removeFd(int fd); + /** + * Enqueues a message to be processed by the specified handler. + * + * The handler must not be null. + * This method can be called on any thread. + */ + void sendMessage(const sp& handler, const Message& message); + + /** + * Enqueues a message to be processed by the specified handler after all pending messages + * after the specified delay. + * + * The time delay is specified in uptime nanoseconds. + * The handler must not be null. + * This method can be called on any thread. + */ + void sendMessageDelayed(nsecs_t uptimeDelay, const sp& handler, + const Message& message); + + /** + * Enqueues a message to be processed by the specified handler after all pending messages + * at the specified time. + * + * The time is specified in uptime nanoseconds. + * The handler must not be null. + * This method can be called on any thread. + */ + void sendMessageAtTime(nsecs_t uptime, const sp& handler, + const Message& message); + + /** + * Removes all messages for the specified handler from the queue. + * + * The handler must not be null. + * This method can be called on any thread. + */ + void removeMessages(const sp& handler); + + /** + * Removes all messages of a particular type for the specified handler from the queue. + * + * The handler must not be null. + * This method can be called on any thread. + */ + void removeMessages(const sp& handler, int what); + /** * Prepares a looper associated with the calling thread, and returns it. * If the thread already has a looper, it is returned. Otherwise, a new @@ -201,12 +292,27 @@ private: Request request; }; + struct MessageEnvelope { + MessageEnvelope() : uptime(0) { } + + MessageEnvelope(nsecs_t uptime, const sp handler, + const Message& message) : uptime(uptime), handler(handler), message(message) { + } + + nsecs_t uptime; + sp handler; + Message message; + }; + const bool mAllowNonCallbacks; // immutable int mWakeReadPipeFd; // immutable int mWakeWritePipeFd; // immutable Mutex mLock; + Vector mMessageEnvelopes; // guarded by mLock + bool mSendingMessage; // guarded by mLock + #ifdef LOOPER_USES_EPOLL int mEpollFd; // immutable @@ -256,6 +362,7 @@ private: // it runs on a single thread. Vector mResponses; size_t mResponseIndex; + nsecs_t mNextMessageUptime; // set to LLONG_MAX when none int pollInner(int timeoutMillis); void awoken(); diff --git a/libs/utils/Looper.cpp b/libs/utils/Looper.cpp index a5363d6fc..18f858b4b 100644 --- a/libs/utils/Looper.cpp +++ b/libs/utils/Looper.cpp @@ -19,10 +19,27 @@ #include #include +#include namespace android { +// --- WeakMessageHandler --- + +WeakMessageHandler::WeakMessageHandler(const wp& handler) : + mHandler(handler) { +} + +void WeakMessageHandler::handleMessage(const Message& message) { + sp handler = mHandler.promote(); + if (handler != NULL) { + handler->handleMessage(message); + } +} + + +// --- Looper --- + #ifdef LOOPER_USES_EPOLL // Hint for number of file descriptors to be associated with the epoll instance. static const int EPOLL_SIZE_HINT = 8; @@ -35,8 +52,8 @@ static pthread_once_t gTLSOnce = PTHREAD_ONCE_INIT; static pthread_key_t gTLSKey = 0; Looper::Looper(bool allowNonCallbacks) : - mAllowNonCallbacks(allowNonCallbacks), - mResponseIndex(0) { + mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false), + mResponseIndex(0), mNextMessageUptime(LLONG_MAX) { int wakeFds[2]; int result = pipe(wakeFds); LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe. errno=%d", errno); @@ -161,17 +178,21 @@ int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outDa for (;;) { while (mResponseIndex < mResponses.size()) { const Response& response = mResponses.itemAt(mResponseIndex++); - if (! response.request.callback) { + ALooper_callbackFunc callback = response.request.callback; + if (!callback) { + int ident = response.request.ident; + int fd = response.request.fd; + int events = response.events; + void* data = response.request.data; #if DEBUG_POLL_AND_WAKE LOGD("%p ~ pollOnce - returning signalled identifier %d: " - "fd=%d, events=0x%x, data=%p", this, - response.request.ident, response.request.fd, - response.events, response.request.data); + "fd=%d, events=0x%x, data=%p", + this, ident, fd, events, data); #endif - if (outFd != NULL) *outFd = response.request.fd; - if (outEvents != NULL) *outEvents = response.events; - if (outData != NULL) *outData = response.request.data; - return response.request.ident; + if (outFd != NULL) *outFd = fd; + if (outEvents != NULL) *outEvents = events; + if (outData != NULL) *outData = data; + return ident; } } @@ -194,6 +215,25 @@ int Looper::pollInner(int timeoutMillis) { LOGD("%p ~ pollOnce - waiting: timeoutMillis=%d", this, timeoutMillis); #endif + // Adjust the timeout based on when the next message is due. + if (timeoutMillis != 0 && mNextMessageUptime != LLONG_MAX) { + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + if (mNextMessageUptime <= now) { + timeoutMillis = 0; + } else { + uint64_t delay = (mNextMessageUptime - now + 999999LL) / 1000000LL; + if (delay < INT_MAX + && (timeoutMillis < 0 || int(delay) < timeoutMillis)) { + timeoutMillis = int(delay); + } + } +#if DEBUG_POLL_AND_WAKE + LOGD("%p ~ pollOnce - next message in %lldns, adjusted timeout: timeoutMillis=%d", + this, mNextMessageUptime - now, timeoutMillis); +#endif + } + + // Poll. int result = ALOOPER_POLL_WAKE; mResponses.clear(); mResponseIndex = 0; @@ -205,7 +245,6 @@ int Looper::pollInner(int timeoutMillis) { #ifdef LOOPER_USES_EPOLL struct epoll_event eventItems[EPOLL_MAX_EVENTS]; int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis); - bool acquiredLock = false; #else // Wait for wakeAndLock() waiters to run then set mPolling to true. mLock.lock(); @@ -219,16 +258,20 @@ int Looper::pollInner(int timeoutMillis) { int eventCount = poll(mRequestedFds.editArray(), requestedCount, timeoutMillis); #endif + // Acquire lock. + mLock.lock(); + + // Check for poll error. if (eventCount < 0) { if (errno == EINTR) { goto Done; } - LOGW("Poll failed with an unexpected error, errno=%d", errno); result = ALOOPER_POLL_ERROR; goto Done; } + // Check for poll timeout. if (eventCount == 0) { #if DEBUG_POLL_AND_WAKE LOGD("%p ~ pollOnce - timeout", this); @@ -237,6 +280,7 @@ int Looper::pollInner(int timeoutMillis) { goto Done; } + // Handle all events. #if DEBUG_POLL_AND_WAKE LOGD("%p ~ pollOnce - handling events from %d fds", this, eventCount); #endif @@ -252,11 +296,6 @@ int Looper::pollInner(int timeoutMillis) { LOGW("Ignoring unexpected epoll events 0x%x on wake read pipe.", epollEvents); } } else { - if (! acquiredLock) { - mLock.lock(); - acquiredLock = true; - } - ssize_t requestIndex = mRequests.indexOfKey(fd); if (requestIndex >= 0) { int events = 0; @@ -271,9 +310,6 @@ int Looper::pollInner(int timeoutMillis) { } } } - if (acquiredLock) { - mLock.unlock(); - } Done: ; #else for (size_t i = 0; i < requestedCount; i++) { @@ -301,15 +337,12 @@ Done: ; } } } - Done: // Set mPolling to false and wake up the wakeAndLock() waiters. - mLock.lock(); mPolling = false; if (mWaiters != 0) { mAwake.broadcast(); } - mLock.unlock(); #endif #ifdef LOOPER_STATISTICS @@ -335,19 +368,59 @@ Done: } #endif + // Invoke pending message callbacks. + mNextMessageUptime = LLONG_MAX; + while (mMessageEnvelopes.size() != 0) { + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + const MessageEnvelope& messageEnvelope = mMessageEnvelopes.itemAt(0); + if (messageEnvelope.uptime <= now) { + // Remove the envelope from the list. + // We keep a strong reference to the handler until the call to handleMessage + // finishes. Then we drop it so that the handler can be deleted *before* + // we reacquire our lock. + { // obtain handler + sp handler = messageEnvelope.handler; + Message message = messageEnvelope.message; + mMessageEnvelopes.removeAt(0); + mSendingMessage = true; + mLock.unlock(); + +#if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS + LOGD("%p ~ pollOnce - sending message: handler=%p, what=%d", + this, handler.get(), message.what); +#endif + handler->handleMessage(message); + } // release handler + + mLock.lock(); + mSendingMessage = false; + result = ALOOPER_POLL_CALLBACK; + } else { + // The last message left at the head of the queue determines the next wakeup time. + mNextMessageUptime = messageEnvelope.uptime; + break; + } + } + + // Release lock. + mLock.unlock(); + + // Invoke all response callbacks. for (size_t i = 0; i < mResponses.size(); i++) { const Response& response = mResponses.itemAt(i); - if (response.request.callback) { + ALooper_callbackFunc callback = response.request.callback; + if (callback) { + int fd = response.request.fd; + int events = response.events; + void* data = response.request.data; #if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS - LOGD("%p ~ pollOnce - invoking callback: fd=%d, events=0x%x, data=%p", this, - response.request.fd, response.events, response.request.data); + LOGD("%p ~ pollOnce - invoking fd event callback %p: fd=%d, events=0x%x, data=%p", + this, callback, fd, events, data); #endif - int callbackResult = response.request.callback( - response.request.fd, response.events, response.request.data); + int callbackResult = callback(fd, events, data); if (callbackResult == 0) { - removeFd(response.request.fd); + removeFd(fd); } - result = ALOOPER_POLL_CALLBACK; } } @@ -593,4 +666,83 @@ void Looper::wakeAndLock() { } #endif +void Looper::sendMessage(const sp& handler, const Message& message) { + sendMessageAtTime(LLONG_MIN, handler, message); +} + +void Looper::sendMessageDelayed(nsecs_t uptimeDelay, const sp& handler, + const Message& message) { + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + sendMessageAtTime(now + uptimeDelay, handler, message); +} + +void Looper::sendMessageAtTime(nsecs_t uptime, const sp& handler, + const Message& message) { +#if DEBUG_CALLBACKS + LOGD("%p ~ sendMessageAtTime - uptime=%lld, handler=%p, what=%d", + this, uptime, handler.get(), message.what); +#endif + + size_t i = 0; + { // acquire lock + AutoMutex _l(mLock); + + size_t messageCount = mMessageEnvelopes.size(); + while (i < messageCount && uptime >= mMessageEnvelopes.itemAt(i).uptime) { + i += 1; + } + + MessageEnvelope messageEnvelope(uptime, handler, message); + mMessageEnvelopes.insertAt(messageEnvelope, i, 1); + + // Optimization: If the Looper is currently sending a message, then we can skip + // the call to wake() because the next thing the Looper will do after processing + // messages is to decide when the next wakeup time should be. In fact, it does + // not even matter whether this code is running on the Looper thread. + if (mSendingMessage) { + return; + } + } // release lock + + // Wake the poll loop only when we enqueue a new message at the head. + if (i == 0) { + wake(); + } +} + +void Looper::removeMessages(const sp& handler) { +#if DEBUG_CALLBACKS + LOGD("%p ~ removeMessages - handler=%p", this, handler.get()); +#endif + + { // acquire lock + AutoMutex _l(mLock); + + for (size_t i = mMessageEnvelopes.size(); i != 0; ) { + const MessageEnvelope& messageEnvelope = mMessageEnvelopes.itemAt(--i); + if (messageEnvelope.handler == handler) { + mMessageEnvelopes.removeAt(i); + } + } + } // release lock +} + +void Looper::removeMessages(const sp& handler, int what) { +#if DEBUG_CALLBACKS + LOGD("%p ~ removeMessages - handler=%p, what=%d", this, handler.get(), what); +#endif + + { // acquire lock + AutoMutex _l(mLock); + + for (size_t i = mMessageEnvelopes.size(); i != 0; ) { + const MessageEnvelope& messageEnvelope = mMessageEnvelopes.itemAt(--i); + if (messageEnvelope.handler == handler + && messageEnvelope.message.what == what) { + mMessageEnvelopes.removeAt(i); + } + } + } // release lock +} + } // namespace android diff --git a/libs/utils/tests/Looper_test.cpp b/libs/utils/tests/Looper_test.cpp index cea1313a4..8bf2ba297 100644 --- a/libs/utils/tests/Looper_test.cpp +++ b/libs/utils/tests/Looper_test.cpp @@ -16,6 +16,13 @@ namespace android { +enum { + MSG_TEST1 = 1, + MSG_TEST2 = 2, + MSG_TEST3 = 3, + MSG_TEST4 = 4, +}; + class DelayedWake : public DelayedTask { sp mLooper; @@ -82,6 +89,15 @@ protected: } }; +class StubMessageHandler : public MessageHandler { +public: + Vector messages; + + virtual void handleMessage(const Message& message) { + messages.push(message); + } +}; + class LooperTest : public testing::Test { protected: sp mLooper; @@ -421,5 +437,257 @@ TEST_F(LooperTest, PollOnce_WhenCallbackAddedTwice_OnlySecondCallbackShouldBeInv << "replacement handler callback should be invoked"; } +TEST_F(LooperTest, SendMessage_WhenOneMessageIsEnqueue_ShouldInvokeHandlerDuringNextPoll) { + sp handler = new StubMessageHandler(); + mLooper->sendMessage(handler, Message(MSG_TEST1)); + + StopWatch stopWatch("pollOnce"); + int result = mLooper->pollOnce(100); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. zero because message was already sent"; + EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) + << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent"; + EXPECT_EQ(size_t(1), handler->messages.size()) + << "handled message"; + EXPECT_EQ(MSG_TEST1, handler->messages[0].what) + << "handled message"; +} + +TEST_F(LooperTest, SendMessage_WhenMultipleMessagesAreEnqueued_ShouldInvokeHandlersInOrderDuringNextPoll) { + sp handler1 = new StubMessageHandler(); + sp handler2 = new StubMessageHandler(); + mLooper->sendMessage(handler1, Message(MSG_TEST1)); + mLooper->sendMessage(handler2, Message(MSG_TEST2)); + mLooper->sendMessage(handler1, Message(MSG_TEST3)); + mLooper->sendMessage(handler1, Message(MSG_TEST4)); + + StopWatch stopWatch("pollOnce"); + int result = mLooper->pollOnce(1000); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. zero because message was already sent"; + EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) + << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent"; + EXPECT_EQ(size_t(3), handler1->messages.size()) + << "handled message"; + EXPECT_EQ(MSG_TEST1, handler1->messages[0].what) + << "handled message"; + EXPECT_EQ(MSG_TEST3, handler1->messages[1].what) + << "handled message"; + EXPECT_EQ(MSG_TEST4, handler1->messages[2].what) + << "handled message"; + EXPECT_EQ(size_t(1), handler2->messages.size()) + << "handled message"; + EXPECT_EQ(MSG_TEST2, handler2->messages[0].what) + << "handled message"; +} + +TEST_F(LooperTest, SendMessageDelayed_WhenSentToTheFuture_ShouldInvokeHandlerAfterDelayTime) { + sp handler = new StubMessageHandler(); + mLooper->sendMessageDelayed(ms2ns(100), handler, Message(MSG_TEST1)); + + StopWatch stopWatch("pollOnce"); + int result = mLooper->pollOnce(1000); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "first poll should end quickly because next message timeout was computed"; + EXPECT_EQ(ALOOPER_POLL_WAKE, result) + << "pollOnce result should be ALOOPER_POLL_WAKE due to wakeup"; + EXPECT_EQ(size_t(0), handler->messages.size()) + << "no message handled yet"; + + result = mLooper->pollOnce(1000); + elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_EQ(size_t(1), handler->messages.size()) + << "handled message"; + EXPECT_EQ(MSG_TEST1, handler->messages[0].what) + << "handled message"; + EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) + << "second poll should end around the time of the delayed message dispatch"; + EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) + << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent"; + + result = mLooper->pollOnce(100); + elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(100 + 100, elapsedMillis, TIMING_TOLERANCE_MS) + << "third poll should timeout"; + EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result) + << "pollOnce result should be ALOOPER_POLL_TIMEOUT because there were no messages left"; +} + +TEST_F(LooperTest, SendMessageDelayed_WhenSentToThePast_ShouldInvokeHandlerDuringNextPoll) { + sp handler = new StubMessageHandler(); + mLooper->sendMessageDelayed(ms2ns(-1000), handler, Message(MSG_TEST1)); + + StopWatch stopWatch("pollOnce"); + int result = mLooper->pollOnce(100); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. zero because message was already sent"; + EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) + << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent"; + EXPECT_EQ(size_t(1), handler->messages.size()) + << "handled message"; + EXPECT_EQ(MSG_TEST1, handler->messages[0].what) + << "handled message"; +} + +TEST_F(LooperTest, SendMessageDelayed_WhenSentToThePresent_ShouldInvokeHandlerDuringNextPoll) { + sp handler = new StubMessageHandler(); + mLooper->sendMessageDelayed(0, handler, Message(MSG_TEST1)); + + StopWatch stopWatch("pollOnce"); + int result = mLooper->pollOnce(100); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. zero because message was already sent"; + EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) + << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent"; + EXPECT_EQ(size_t(1), handler->messages.size()) + << "handled message"; + EXPECT_EQ(MSG_TEST1, handler->messages[0].what) + << "handled message"; +} + +TEST_F(LooperTest, SendMessageAtTime_WhenSentToTheFuture_ShouldInvokeHandlerAfterDelayTime) { + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + sp handler = new StubMessageHandler(); + mLooper->sendMessageAtTime(now + ms2ns(100), handler, Message(MSG_TEST1)); + + StopWatch stopWatch("pollOnce"); + int result = mLooper->pollOnce(1000); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "first poll should end quickly because next message timeout was computed"; + EXPECT_EQ(ALOOPER_POLL_WAKE, result) + << "pollOnce result should be ALOOPER_POLL_WAKE due to wakeup"; + EXPECT_EQ(size_t(0), handler->messages.size()) + << "no message handled yet"; + + result = mLooper->pollOnce(1000); + elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_EQ(size_t(1), handler->messages.size()) + << "handled message"; + EXPECT_EQ(MSG_TEST1, handler->messages[0].what) + << "handled message"; + EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) + << "second poll should end around the time of the delayed message dispatch"; + EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) + << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent"; + + result = mLooper->pollOnce(100); + elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(100 + 100, elapsedMillis, TIMING_TOLERANCE_MS) + << "third poll should timeout"; + EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result) + << "pollOnce result should be ALOOPER_POLL_TIMEOUT because there were no messages left"; +} + +TEST_F(LooperTest, SendMessageAtTime_WhenSentToThePast_ShouldInvokeHandlerDuringNextPoll) { + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + sp handler = new StubMessageHandler(); + mLooper->sendMessageAtTime(now - ms2ns(1000), handler, Message(MSG_TEST1)); + + StopWatch stopWatch("pollOnce"); + int result = mLooper->pollOnce(100); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. zero because message was already sent"; + EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) + << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent"; + EXPECT_EQ(size_t(1), handler->messages.size()) + << "handled message"; + EXPECT_EQ(MSG_TEST1, handler->messages[0].what) + << "handled message"; +} + +TEST_F(LooperTest, SendMessageAtTime_WhenSentToThePresent_ShouldInvokeHandlerDuringNextPoll) { + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + sp handler = new StubMessageHandler(); + mLooper->sendMessageAtTime(now, handler, Message(MSG_TEST1)); + + StopWatch stopWatch("pollOnce"); + int result = mLooper->pollOnce(100); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. zero because message was already sent"; + EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) + << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent"; + EXPECT_EQ(size_t(1), handler->messages.size()) + << "handled message"; + EXPECT_EQ(MSG_TEST1, handler->messages[0].what) + << "handled message"; +} + +TEST_F(LooperTest, RemoveMessage_WhenRemovingAllMessagesForHandler_ShouldRemoveThoseMessage) { + sp handler = new StubMessageHandler(); + mLooper->sendMessage(handler, Message(MSG_TEST1)); + mLooper->sendMessage(handler, Message(MSG_TEST2)); + mLooper->sendMessage(handler, Message(MSG_TEST3)); + mLooper->removeMessages(handler); + + StopWatch stopWatch("pollOnce"); + int result = mLooper->pollOnce(0); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. zero because message was sent so looper was awoken"; + EXPECT_EQ(ALOOPER_POLL_WAKE, result) + << "pollOnce result should be ALOOPER_POLL_WAKE because looper was awoken"; + EXPECT_EQ(size_t(0), handler->messages.size()) + << "no messages to handle"; + + result = mLooper->pollOnce(0); + + EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result) + << "pollOnce result should be ALOOPER_POLL_TIMEOUT because there was nothing to do"; + EXPECT_EQ(size_t(0), handler->messages.size()) + << "no messages to handle"; +} + +TEST_F(LooperTest, RemoveMessage_WhenRemovingSomeMessagesForHandler_ShouldRemoveThoseMessage) { + sp handler = new StubMessageHandler(); + mLooper->sendMessage(handler, Message(MSG_TEST1)); + mLooper->sendMessage(handler, Message(MSG_TEST2)); + mLooper->sendMessage(handler, Message(MSG_TEST3)); + mLooper->sendMessage(handler, Message(MSG_TEST4)); + mLooper->removeMessages(handler, MSG_TEST3); + mLooper->removeMessages(handler, MSG_TEST1); + + StopWatch stopWatch("pollOnce"); + int result = mLooper->pollOnce(0); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. zero because message was sent so looper was awoken"; + EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) + << "pollOnce result should be ALOOPER_POLL_CALLBACK because two messages were sent"; + EXPECT_EQ(size_t(2), handler->messages.size()) + << "no messages to handle"; + EXPECT_EQ(MSG_TEST2, handler->messages[0].what) + << "handled message"; + EXPECT_EQ(MSG_TEST4, handler->messages[1].what) + << "handled message"; + + result = mLooper->pollOnce(0); + + EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result) + << "pollOnce result should be ALOOPER_POLL_TIMEOUT because there was nothing to do"; + EXPECT_EQ(size_t(2), handler->messages.size()) + << "no more messages to handle"; +} } // namespace android From b6e92ae9a12f2fe0106e0187c252f39d5ad6bfa9 Mon Sep 17 00:00:00 2001 From: Romain Guy Date: Mon, 7 Mar 2011 18:06:46 -0800 Subject: [PATCH 299/541] Add support for partial invalidates in WebView Bug #3461349 This change also fixes two bugs that prevented partial invalidates from working with other views. Both bugs were in our EGL implementation: they were preventing the caller from comparing the current context/surface with another context/surface. This was causing HardwareRenderer to always redraw the entire screen. Change-Id: I33e096b304d4a0b7e6c8f92930f71d2ece9bebf5 --- include/utils/Functor.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/utils/Functor.h b/include/utils/Functor.h index 3955bc346..565f4a3c8 100644 --- a/include/utils/Functor.h +++ b/include/utils/Functor.h @@ -26,6 +26,7 @@ public: Functor() {} virtual ~Functor() {} virtual status_t operator ()() { return true; } + virtual status_t operator ()(float* data, uint32_t len) { return true; } }; }; // namespace android From 9ae794de4685c080d92d8c2a09d195a0d71ae2a8 Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Wed, 9 Mar 2011 17:39:48 -0800 Subject: [PATCH 300/541] Use touch pad gestures to manipulate the pointer. 1. Single finger tap performs a click. 2. Single finger movement moves the pointer (hovers). 3. Button press plus movement performs click or drag. While dragging, the pointer follows the finger that is moving fastest. This is important if there are additional fingers down on the touch pad for the purpose of applying force to an integrated button underneath. 4. Two fingers near each other moving in the same direction are coalesced as a swipe gesture under the pointer. 5. Two or more fingers moving in arbitrary directions are transformed into touches in the vicinity of the pointer. This makes scale/zoom and rotate gestures possible. Added a native VelocityTracker implementation to enable intelligent switching of the active pointer during drags. Change-Id: I5ada57e7f2bdb9b0a791843eb354a8c706b365dc --- include/utils/BitSet.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/include/utils/BitSet.h b/include/utils/BitSet.h index f5dbcd942..f03825ae0 100644 --- a/include/utils/BitSet.h +++ b/include/utils/BitSet.h @@ -61,6 +61,12 @@ struct BitSet32 { // Result is undefined if all bits are marked. inline uint32_t firstUnmarkedBit() const { return __builtin_clz(~ value); } + // Gets the index of the specified bit in the set, which is the number of + // marked bits that appear before the specified bit. + inline uint32_t getIndexOfBit(uint32_t n) const { + return __builtin_popcount(value & ~(0xffffffffUL >> n)); + } + inline bool operator== (const BitSet32& other) const { return value == other.value; } inline bool operator!= (const BitSet32& other) const { return value != other.value; } }; From 29d4d20c24fa94f2db71bd0edcbe4b64d59e4d6e Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Mon, 14 Mar 2011 11:32:29 -0700 Subject: [PATCH 301/541] Bug 4016329 do full string comparisons Use full string comparisons instead of partial for file extension and MIME type. Do case-insensitive comparison of MIME type and file extensions. Fix error in comment for String8::getPathExtension. Remove dead code -- StringTokenizer is unused. Change-Id: I322be6235abbdaab5f7eafa48926dbb2cf46dc29 --- include/utils/String8.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/utils/String8.h b/include/utils/String8.h index 6b49ff5b4..4163697d2 100644 --- a/include/utils/String8.h +++ b/include/utils/String8.h @@ -165,8 +165,8 @@ public: String8 walkPath(String8* outRemains = NULL) const; /* - * Return the filename extension. This is the last '.' and up to - * four characters that follow it. The '.' is included in case we + * Return the filename extension. This is the last '.' and any number + * of characters that follow it. The '.' is included in case we * decide to expand our definition of what constitutes an extension. * * "/tmp/foo/bar.c" --> ".c" From 5e35370a3bd941d8e797b9e9beb1b378e00157d5 Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Mon, 14 Mar 2011 19:39:54 -0700 Subject: [PATCH 302/541] Improve VelocityTracker numerical stability. Replaced VelocityTracker with a faster and more accurate native implementation. This avoids the duplicate maintenance overhead of having two implementations. The new algorithm requires that the sample duration be at least 10ms in order to contribute to the velocity calculation. This ensures that the velocity is not severely overestimated when samples arrive in bursts. The new algorithm computes the exponentially weighted moving average using weights based on the relative duration of successive sample periods. The new algorithm is also more careful about how it handles individual pointers going down or up and their effects on the collected movement traces. The intent is to preserve the last known velocity of pointers as they go up while also ensuring that other motion samples do not count twice in that case. Bug: 4086785 Change-Id: I2632321232c64d6b8faacdb929e33f60e64dcdd3 --- include/utils/BitSet.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/utils/BitSet.h b/include/utils/BitSet.h index f03825ae0..de748b54f 100644 --- a/include/utils/BitSet.h +++ b/include/utils/BitSet.h @@ -61,6 +61,10 @@ struct BitSet32 { // Result is undefined if all bits are marked. inline uint32_t firstUnmarkedBit() const { return __builtin_clz(~ value); } + // Finds the last marked bit in the set. + // Result is undefined if all bits are unmarked. + inline uint32_t lastMarkedBit() const { return 31 - __builtin_ctz(value); } + // Gets the index of the specified bit in the set, which is the number of // marked bits that appear before the specified bit. inline uint32_t getIndexOfBit(uint32_t n) const { From 349626eb76ed2618e9a57e63d7acb4b28a85dae4 Mon Sep 17 00:00:00 2001 From: Romain Guy Date: Wed, 16 Mar 2011 15:30:12 -0700 Subject: [PATCH 303/541] Modify the GL renderer's functor to pass the clip to WebView Change-Id: If5efe399ca58f3000b2883e24e9f3736a2025184 --- include/utils/Functor.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/include/utils/Functor.h b/include/utils/Functor.h index 565f4a3c8..e24ded4cf 100644 --- a/include/utils/Functor.h +++ b/include/utils/Functor.h @@ -25,8 +25,7 @@ class Functor { public: Functor() {} virtual ~Functor() {} - virtual status_t operator ()() { return true; } - virtual status_t operator ()(float* data, uint32_t len) { return true; } + virtual status_t operator ()(int what, void* data) { return NO_ERROR; } }; }; // namespace android From 7c123375ffee76d6e15b611e8146c13bb67a8c9f Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Wed, 16 Mar 2011 23:18:07 -0700 Subject: [PATCH 304/541] fix [4093196] Device lock up - log spam with SharedBufferStack: waitForCondition(LockCondition) timed out a memory corruption happned when the buffer pool was resized (like when playing a video or using camera) and there was no current active buffer. In this case, the faulty code would index into an array at position -1 which corrupted 24 bytes of data. also improved region validation code (ifdef'ed out by default) Bug: 4093196 Change-Id: I915c581d131148959d720e00e3892e9186ab733d --- include/utils/Vector.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/utils/Vector.h b/include/utils/Vector.h index ec851bd0b..6fd307f3b 100644 --- a/include/utils/Vector.h +++ b/include/utils/Vector.h @@ -162,6 +162,9 @@ public: inline status_t sort(compar_t cmp); inline status_t sort(compar_r_t cmp, void* state); + // for debugging only + inline size_t getItemSize() const { return itemSize(); } + protected: virtual void do_construct(void* storage, size_t num) const; virtual void do_destroy(void* storage, size_t num) const; From 43550eee5bfeaf7832487a2285ae86be0f7ce561 Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Thu, 17 Mar 2011 01:34:19 -0700 Subject: [PATCH 305/541] Refactor how timeouts are calculated. Added a timeout mechanism to EventHub and InputReader so that InputMappers can request timeouts to perform delayed processing of input when needed. Change-Id: Iec2045baaf4e67690b15eef3c09a58d5cac76897 --- include/utils/Timers.h | 10 ++++++++++ libs/utils/Looper.cpp | 19 +++++++------------ libs/utils/Timers.cpp | 18 ++++++++++++++++++ 3 files changed, 35 insertions(+), 12 deletions(-) diff --git a/include/utils/Timers.h b/include/utils/Timers.h index 9a9e07c60..8b4d32287 100644 --- a/include/utils/Timers.h +++ b/include/utils/Timers.h @@ -88,6 +88,16 @@ nsecs_t systemTime(int clock = SYSTEM_TIME_MONOTONIC); nsecs_t systemTime(int clock); #endif // def __cplusplus +/** + * Returns the number of milliseconds to wait between the reference time and the timeout time. + * If the timeout is in the past relative to the reference time, returns 0. + * If the timeout is more than INT_MAX milliseconds in the future relative to the reference time, + * such as when timeoutTime == LLONG_MAX, returns -1 to indicate an infinite timeout delay. + * Otherwise, returns the difference between the reference time and timeout time + * rounded up to the next millisecond. + */ +int toMillisecondTimeoutDelay(nsecs_t referenceTime, nsecs_t timeoutTime); + #ifdef __cplusplus } // extern "C" #endif diff --git a/libs/utils/Looper.cpp b/libs/utils/Looper.cpp index 18f858b4b..d5dd12606 100644 --- a/libs/utils/Looper.cpp +++ b/libs/utils/Looper.cpp @@ -218,14 +218,10 @@ int Looper::pollInner(int timeoutMillis) { // Adjust the timeout based on when the next message is due. if (timeoutMillis != 0 && mNextMessageUptime != LLONG_MAX) { nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); - if (mNextMessageUptime <= now) { - timeoutMillis = 0; - } else { - uint64_t delay = (mNextMessageUptime - now + 999999LL) / 1000000LL; - if (delay < INT_MAX - && (timeoutMillis < 0 || int(delay) < timeoutMillis)) { - timeoutMillis = int(delay); - } + int messageTimeoutMillis = toMillisecondTimeoutDelay(now, mNextMessageUptime); + if (messageTimeoutMillis >= 0 + && (timeoutMillis < 0 || messageTimeoutMillis < timeoutMillis)) { + timeoutMillis = messageTimeoutMillis; } #if DEBUG_POLL_AND_WAKE LOGD("%p ~ pollOnce - next message in %lldns, adjusted timeout: timeoutMillis=%d", @@ -444,12 +440,11 @@ int Looper::pollAll(int timeoutMillis, int* outFd, int* outEvents, void** outDat return result; } - nsecs_t timeoutNanos = endTime - systemTime(SYSTEM_TIME_MONOTONIC); - if (timeoutNanos <= 0) { + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + timeoutMillis = toMillisecondTimeoutDelay(now, endTime); + if (timeoutMillis == 0) { return ALOOPER_POLL_TIMEOUT; } - - timeoutMillis = int(nanoseconds_to_milliseconds(timeoutNanos + 999999LL)); } } } diff --git a/libs/utils/Timers.cpp b/libs/utils/Timers.cpp index 784f035dc..64a29f587 100644 --- a/libs/utils/Timers.cpp +++ b/libs/utils/Timers.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #ifdef HAVE_WIN32_THREADS #include @@ -53,6 +54,23 @@ nsecs_t systemTime(int clock) #endif } +int toMillisecondTimeoutDelay(nsecs_t referenceTime, nsecs_t timeoutTime) +{ + int timeoutDelayMillis; + if (timeoutTime > referenceTime) { + uint64_t timeoutDelay = uint64_t(timeoutTime - referenceTime); + if (timeoutDelay > uint64_t((INT_MAX - 1) * 1000000LL)) { + timeoutDelayMillis = -1; + } else { + timeoutDelayMillis = (timeoutDelay + 999999LL) / 1000000LL; + } + } else { + timeoutDelayMillis = 0; + } + return timeoutDelayMillis; +} + + /* * =========================================================================== * DurationTimer From 36357bd7a7941265dca9e1905aaad06ccc66593c Mon Sep 17 00:00:00 2001 From: Carl Shapiro Date: Mon, 21 Mar 2011 20:26:25 -0700 Subject: [PATCH 306/541] Include strings.h for the strcasecmp prototype. Change-Id: I0659ce2186547274e58f8b2027e16d3e45189353 --- libs/utils/AssetManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/utils/AssetManager.cpp b/libs/utils/AssetManager.cpp index e09e75586..6e57d93d8 100644 --- a/libs/utils/AssetManager.cpp +++ b/libs/utils/AssetManager.cpp @@ -36,6 +36,7 @@ #include #include #include +#include using namespace android; @@ -1764,4 +1765,3 @@ int AssetManager::ZipSet::getIndex(const String8& zip) const return mZipPath.size()-1; } - From 3145b384626d8ddb4ff7673a29fee322374c0131 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A5rten=20Kongstad?= Date: Thu, 17 Mar 2011 14:13:41 +0100 Subject: [PATCH 307/541] Runtime resource overlay, iteration 1. Runtime resource overlay allows unmodified applications to appear as if they had been compiled with additional resources defined. See libs/utils/README for more information. This commit is the first iteration of runtime resource overlay. It provides the actual overlay modifications and loading of trusted overlay packages (ie residing in /vendor) targeting framework-res.apk. This commit loads exactly one overlay package. The overlay, if present, must target framework-res.apk and be located at /vendor/overlay/framework/framework-res.apk. Change-Id: If26ee7754813004a96c043dba37fbe99fa3919db --- include/utils/AssetManager.h | 11 + include/utils/ResourceTypes.h | 26 ++- libs/utils/AssetManager.cpp | 237 +++++++++++++++++++- libs/utils/README | 275 +++++++++++++++++++++++ libs/utils/ResourceTypes.cpp | 408 +++++++++++++++++++++++++++++----- 5 files changed, 895 insertions(+), 62 deletions(-) diff --git a/include/utils/AssetManager.h b/include/utils/AssetManager.h index 9e2bf37e8..a8c7ddbd7 100644 --- a/include/utils/AssetManager.h +++ b/include/utils/AssetManager.h @@ -222,6 +222,7 @@ private: { String8 path; FileType type; + String8 idmap; }; Asset* openInPathLocked(const char* fileName, AccessMode mode, @@ -262,6 +263,16 @@ private: void setLocaleLocked(const char* locale); void updateResourceParamsLocked() const; + bool createIdmapFileLocked(const String8& originalPath, const String8& overlayPath, + const String8& idmapPath); + + bool isIdmapStaleLocked(const String8& originalPath, const String8& overlayPath, + const String8& idmapPath); + + Asset* openIdmapLocked(const struct asset_path& ap) const; + + bool getZipEntryCrcLocked(const String8& zipPath, const char* entryFilename, uint32_t* pCrc); + class SharedZip : public RefBase { public: static sp get(const String8& path); diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h index 6227f3e92..24e72e90e 100644 --- a/include/utils/ResourceTypes.h +++ b/include/utils/ResourceTypes.h @@ -1735,9 +1735,9 @@ public: ~ResTable(); status_t add(const void* data, size_t size, void* cookie, - bool copyData=false); + bool copyData=false, const void* idmap = NULL); status_t add(Asset* asset, void* cookie, - bool copyData=false); + bool copyData=false, const void* idmap = NULL); status_t add(ResTable* src); status_t getError() const; @@ -1983,6 +1983,24 @@ public: void getLocales(Vector* locales) const; + // Generate an idmap. + // + // Return value: on success: NO_ERROR; caller is responsible for free-ing + // outData (using free(3)). On failure, any status_t value other than + // NO_ERROR; the caller should not free outData. + status_t createIdmap(const ResTable& overlay, uint32_t originalCrc, uint32_t overlayCrc, + void** outData, size_t* outSize) const; + + enum { + IDMAP_HEADER_SIZE_BYTES = 3 * sizeof(uint32_t), + }; + // Retrieve idmap meta-data. + // + // This function only requires the idmap header (the first + // IDMAP_HEADER_SIZE_BYTES) bytes of an idmap file. + static bool getIdmapInfo(const void* idmap, size_t size, + uint32_t* pOriginalCrc, uint32_t* pOverlayCrc); + #ifndef HAVE_ANDROID_OS void print(bool inclValues) const; static String8 normalizeForOutput(const char* input); @@ -1996,7 +2014,7 @@ private: struct bag_set; status_t add(const void* data, size_t size, void* cookie, - Asset* asset, bool copyData); + Asset* asset, bool copyData, const Asset* idmap); ssize_t getResourcePackageIndex(uint32_t resID) const; ssize_t getEntry( @@ -2005,7 +2023,7 @@ private: const ResTable_type** outType, const ResTable_entry** outEntry, const Type** outTypeClass) const; status_t parsePackage( - const ResTable_package* const pkg, const Header* const header); + const ResTable_package* const pkg, const Header* const header, uint32_t idmap_id); void print_value(const Package* pkg, const Res_value& value) const; diff --git a/libs/utils/AssetManager.cpp b/libs/utils/AssetManager.cpp index 6e57d93d8..e41dd3968 100644 --- a/libs/utils/AssetManager.cpp +++ b/libs/utils/AssetManager.cpp @@ -37,6 +37,19 @@ #include #include #include +#include +#include +#include + +#ifndef TEMP_FAILURE_RETRY +/* Used to retry syscalls that can return EINTR. */ +#define TEMP_FAILURE_RETRY(exp) ({ \ + typeof (exp) _rc; \ + do { \ + _rc = (exp); \ + } while (_rc == -1 && errno == EINTR); \ + _rc; }) +#endif using namespace android; @@ -49,6 +62,7 @@ static const char* kDefaultVendor = "default"; static const char* kAssetsRoot = "assets"; static const char* kAppZipName = NULL; //"classes.jar"; static const char* kSystemAssets = "framework/framework-res.apk"; +static const char* kIdmapCacheDir = "resource-cache"; static const char* kExcludeExtension = ".EXCLUDE"; @@ -56,6 +70,35 @@ static Asset* const kExcludedAsset = (Asset*) 0xd000000d; static volatile int32_t gCount = 0; +namespace { + // Transform string /a/b/c.apk to /data/resource-cache/a@b@c.apk@idmap + String8 idmapPathForPackagePath(const String8& pkgPath) + { + const char* root = getenv("ANDROID_DATA"); + LOG_ALWAYS_FATAL_IF(root == NULL, "ANDROID_DATA not set"); + String8 path(root); + path.appendPath(kIdmapCacheDir); + + char buf[256]; // 256 chars should be enough for anyone... + strncpy(buf, pkgPath.string(), 255); + buf[255] = '\0'; + char* filename = buf; + while (*filename && *filename == '/') { + ++filename; + } + char* p = filename; + while (*p) { + if (*p == '/') { + *p = '@'; + } + ++p; + } + path.appendPath(filename); + path.append("@idmap"); + + return path; + } +} /* * =========================================================================== @@ -123,7 +166,7 @@ bool AssetManager::addAssetPath(const String8& path, void** cookie) return true; } } - + LOGV("In %p Asset %s path: %s", this, ap.type == kFileTypeDirectory ? "dir" : "zip", ap.path.string()); @@ -134,9 +177,181 @@ bool AssetManager::addAssetPath(const String8& path, void** cookie) *cookie = (void*)mAssetPaths.size(); } + // add overlay packages for /system/framework; apps are handled by the + // (Java) package manager + if (strncmp(path.string(), "/system/framework/", 18) == 0) { + // When there is an environment variable for /vendor, this + // should be changed to something similar to how ANDROID_ROOT + // and ANDROID_DATA are used in this file. + String8 overlayPath("/vendor/overlay/framework/"); + overlayPath.append(path.getPathLeaf()); + if (TEMP_FAILURE_RETRY(access(overlayPath.string(), R_OK)) == 0) { + asset_path oap; + oap.path = overlayPath; + oap.type = ::getFileType(overlayPath.string()); + bool addOverlay = (oap.type == kFileTypeRegular); // only .apks supported as overlay + if (addOverlay) { + oap.idmap = idmapPathForPackagePath(overlayPath); + + if (isIdmapStaleLocked(ap.path, oap.path, oap.idmap)) { + addOverlay = createIdmapFileLocked(ap.path, oap.path, oap.idmap); + } + } + if (addOverlay) { + mAssetPaths.add(oap); + } else { + LOGW("failed to add overlay package %s\n", overlayPath.string()); + } + } + } + return true; } +bool AssetManager::isIdmapStaleLocked(const String8& originalPath, const String8& overlayPath, + const String8& idmapPath) +{ + struct stat st; + if (TEMP_FAILURE_RETRY(stat(idmapPath.string(), &st)) == -1) { + if (errno == ENOENT) { + return true; // non-existing idmap is always stale + } else { + LOGW("failed to stat file %s: %s\n", idmapPath.string(), strerror(errno)); + return false; + } + } + if (st.st_size < ResTable::IDMAP_HEADER_SIZE_BYTES) { + LOGW("file %s has unexpectedly small size=%zd\n", idmapPath.string(), (size_t)st.st_size); + return false; + } + int fd = TEMP_FAILURE_RETRY(::open(idmapPath.string(), O_RDONLY)); + if (fd == -1) { + LOGW("failed to open file %s: %s\n", idmapPath.string(), strerror(errno)); + return false; + } + char buf[ResTable::IDMAP_HEADER_SIZE_BYTES]; + ssize_t bytesLeft = ResTable::IDMAP_HEADER_SIZE_BYTES; + for (;;) { + ssize_t r = TEMP_FAILURE_RETRY(read(fd, buf + ResTable::IDMAP_HEADER_SIZE_BYTES - bytesLeft, + bytesLeft)); + if (r < 0) { + TEMP_FAILURE_RETRY(close(fd)); + return false; + } + bytesLeft -= r; + if (bytesLeft == 0) { + break; + } + } + TEMP_FAILURE_RETRY(close(fd)); + + uint32_t cachedOriginalCrc, cachedOverlayCrc; + if (!ResTable::getIdmapInfo(buf, ResTable::IDMAP_HEADER_SIZE_BYTES, + &cachedOriginalCrc, &cachedOverlayCrc)) { + return false; + } + + uint32_t actualOriginalCrc, actualOverlayCrc; + if (!getZipEntryCrcLocked(originalPath, "resources.arsc", &actualOriginalCrc)) { + return false; + } + if (!getZipEntryCrcLocked(overlayPath, "resources.arsc", &actualOverlayCrc)) { + return false; + } + return cachedOriginalCrc != actualOriginalCrc || cachedOverlayCrc != actualOverlayCrc; +} + +bool AssetManager::getZipEntryCrcLocked(const String8& zipPath, const char* entryFilename, + uint32_t* pCrc) +{ + asset_path ap; + ap.path = zipPath; + const ZipFileRO* zip = getZipFileLocked(ap); + if (zip == NULL) { + return false; + } + const ZipEntryRO entry = zip->findEntryByName(entryFilename); + if (entry == NULL) { + return false; + } + if (!zip->getEntryInfo(entry, NULL, NULL, NULL, NULL, NULL, (long*)pCrc)) { + return false; + } + return true; +} + +bool AssetManager::createIdmapFileLocked(const String8& originalPath, const String8& overlayPath, + const String8& idmapPath) +{ + LOGD("%s: originalPath=%s overlayPath=%s idmapPath=%s\n", + __FUNCTION__, originalPath.string(), overlayPath.string(), idmapPath.string()); + ResTable tables[2]; + const String8* paths[2] = { &originalPath, &overlayPath }; + uint32_t originalCrc, overlayCrc; + bool retval = false; + ssize_t offset = 0; + int fd = 0; + uint32_t* data = NULL; + size_t size; + + for (int i = 0; i < 2; ++i) { + asset_path ap; + ap.type = kFileTypeRegular; + ap.path = *paths[i]; + Asset* ass = openNonAssetInPathLocked("resources.arsc", Asset::ACCESS_BUFFER, ap); + if (ass == NULL) { + LOGW("failed to find resources.arsc in %s\n", ap.path.string()); + goto error; + } + tables[i].add(ass, (void*)1, false); + } + + if (!getZipEntryCrcLocked(originalPath, "resources.arsc", &originalCrc)) { + LOGW("failed to retrieve crc for resources.arsc in %s\n", originalPath.string()); + goto error; + } + if (!getZipEntryCrcLocked(overlayPath, "resources.arsc", &overlayCrc)) { + LOGW("failed to retrieve crc for resources.arsc in %s\n", overlayPath.string()); + goto error; + } + + if (tables[0].createIdmap(tables[1], originalCrc, overlayCrc, + (void**)&data, &size) != NO_ERROR) { + LOGW("failed to generate idmap data for file %s\n", idmapPath.string()); + goto error; + } + + // This should be abstracted (eg replaced by a stand-alone + // application like dexopt, triggered by something equivalent to + // installd). + fd = TEMP_FAILURE_RETRY(::open(idmapPath.string(), O_WRONLY | O_CREAT | O_TRUNC, 0644)); + if (fd == -1) { + LOGW("failed to write idmap file %s (open: %s)\n", idmapPath.string(), strerror(errno)); + goto error_free; + } + for (;;) { + ssize_t written = TEMP_FAILURE_RETRY(write(fd, data + offset, size)); + if (written < 0) { + LOGW("failed to write idmap file %s (write: %s)\n", idmapPath.string(), + strerror(errno)); + goto error_close; + } + size -= (size_t)written; + offset += written; + if (size == 0) { + break; + } + } + + retval = true; +error_close: + TEMP_FAILURE_RETRY(close(fd)); +error_free: + free(data); +error: + return retval; +} + bool AssetManager::addDefaultAssets() { const char* root = getenv("ANDROID_ROOT"); @@ -405,6 +620,7 @@ const ResTable* AssetManager::getResTable(bool required) const ResTable* sharedRes = NULL; bool shared = true; const asset_path& ap = mAssetPaths.itemAt(i); + Asset* idmap = openIdmapLocked(ap); LOGV("Looking for resource asset in '%s'\n", ap.path.string()); if (ap.type != kFileTypeDirectory) { if (i == 0) { @@ -434,7 +650,7 @@ const ResTable* AssetManager::getResTable(bool required) const // can quickly copy it out for others. LOGV("Creating shared resources for %s", ap.path.string()); sharedRes = new ResTable(); - sharedRes->add(ass, (void*)(i+1), false); + sharedRes->add(ass, (void*)(i+1), false, idmap); sharedRes = const_cast(this)-> mZipSet.setZipResourceTable(ap.path, sharedRes); } @@ -458,7 +674,7 @@ const ResTable* AssetManager::getResTable(bool required) const rt->add(sharedRes); } else { LOGV("Parsing resources for %s", ap.path.string()); - rt->add(ass, (void*)(i+1), !shared); + rt->add(ass, (void*)(i+1), !shared, idmap); } if (!shared) { @@ -499,6 +715,21 @@ void AssetManager::updateResourceParamsLocked() const res->setParameters(mConfig); } +Asset* AssetManager::openIdmapLocked(const struct asset_path& ap) const +{ + Asset* ass = NULL; + if (ap.idmap.size() != 0) { + ass = const_cast(this)-> + openAssetFromFileLocked(ap.idmap, Asset::ACCESS_BUFFER); + if (ass) { + LOGV("loading idmap %s\n", ap.idmap.string()); + } else { + LOGW("failed to load idmap %s\n", ap.idmap.string()); + } + } + return ass; +} + const ResTable& AssetManager::getResources(bool required) const { const ResTable* rt = getResTable(required); diff --git a/libs/utils/README b/libs/utils/README index 36a706d5c..01741e093 100644 --- a/libs/utils/README +++ b/libs/utils/README @@ -1,4 +1,6 @@ Android Utility Function Library +================================ + If you need a feature that is native to Linux but not present on other platforms, construct a platform-dependent implementation that shares @@ -12,3 +14,276 @@ The ultimate goal is *not* to create a super-duper platform abstraction layer. The goal is to provide an optimized solution for Linux with reasonable implementations for other platforms. + + +Resource overlay +================ + + +Introduction +------------ + +Overlay packages are special .apk files which provide no code but +additional resource values (and possibly new configurations) for +resources in other packages. When an application requests resources, +the system will return values from either the application's original +package or any associated overlay package. Any redirection is completely +transparent to the calling application. + +Resource values have the following precedence table, listed in +descending precedence. + + * overlay package, matching config (eg res/values-en-land) + + * original package, matching config + + * overlay package, no config (eg res/values) + + * original package, no config + +During compilation, overlay packages are differentiated from regular +packages by passing the -o flag to aapt. + + +Background +---------- + +This section provides generic background material on resources in +Android. + + +How resources are bundled in .apk files +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Android .apk files are .zip files, usually housing .dex code, +certificates and resources, though packages containing resources but +no code are possible. Resources can be divided into the following +categories; a `configuration' indicates a set of phone language, display +density, network operator, etc. + + * assets: uncompressed, raw files packaged as part of an .apk and + explicitly referenced by filename. These files are + independent of configuration. + + * res/drawable: bitmap or xml graphics. Each file may have different + values depending on configuration. + + * res/values: integers, strings, etc. Each resource may have different + values depending on configuration. + +Resource meta information and information proper is stored in a binary +format in a named file resources.arsc, bundled as part of the .apk. + +Resource IDs and lookup +~~~~~~~~~~~~~~~~~~~~~~~ +During compilation, the aapt tool gathers application resources and +generates a resources.arsc file. Each resource name is assigned an +integer ID 0xppttiii (translated to a symbolic name via R.java), where + + * pp: corresponds to the package namespace (details below). + + * tt: corresponds to the resource type (string, int, etc). Every + resource of the same type within the same package has the same + tt value, but depending on available types, the actual numerical + value may be different between packages. + + * iiii: sequential number, assigned in the order resources are found. + +Resource values are specified paired with a set of configuration +constraints (the default being the empty set), eg res/values-sv-port +which imposes restrictions on language (Swedish) and display orientation +(portrait). During lookup, every constraint set is matched against the +current configuration, and the value corresponding to the best matching +constraint set is returned (ResourceTypes.{h,cpp}). + +Parsing of resources.arsc is handled by ResourceTypes.cpp; this utility +is governed by AssetManager.cpp, which tracks loaded resources per +process. + +Assets are looked up by path and filename in AssetManager.cpp. The path +to resources in res/drawable are located by ResourceTypes.cpp and then +handled like assets by AssetManager.cpp. Other resources are handled +solely by ResourceTypes.cpp. + +Package ID as namespace +~~~~~~~~~~~~~~~~~~~~~~~ +The pp part of a resource ID defines a namespace. Android currently +defines two namespaces: + + * 0x01: system resources (pre-installed in framework-res.apk) + + * 0x7f: application resources (bundled in the application .apk) + +ResourceTypes.cpp supports package IDs between 0x01 and 0x7f +(inclusive); values outside this range are invalid. + +Each running (Dalvik) process is assigned a unique instance of +AssetManager, which in turn keeps a forest structure of loaded +resource.arsc files. Normally, this forest is structured as follows, +where mPackageMap is the internal vector employed in ResourceTypes.cpp. + +mPackageMap[0x00] -> system package +mPackageMap[0x01] -> NULL +mPackageMap[0x02] -> NULL +... +mPackageMap[0x7f - 2] -> NULL +mPackageMap[0x7f - 1] -> application package + + + +The resource overlay extension +------------------------------ + +The resource overlay mechanism aims to (partly) shadow and extend +existing resources with new values for defined and new configurations. +Technically, this is achieved by adding resource-only packages (called +overlay packages) to existing resource namespaces, like so: + +mPackageMap[0x00] -> system package -> system overlay package +mPackageMap[0x01] -> NULL +mPackageMap[0x02] -> NULL +... +mPackageMap[0x7f - 2] -> NULL +mPackageMap[0x7f - 1] -> application package -> overlay 1 -> overlay 2 + +The use of overlay resources is completely transparent to +applications; no additional resource identifiers are introduced, only +configuration/value pairs. Any number of overlay packages may be loaded +at a time; overlay packages are agnostic to what they target -- both +system and application resources are fair game. + +The package targeted by an overlay package is called the target or +original package. + +Resource overlay operates on symbolic resources names. Hence, to +override the string/str1 resources in a package, the overlay package +would include a resource also named string/str1. The end user does not +have to worry about the numeric resources IDs assigned by aapt, as this +is resolved automatically by the system. + +As of this writing, the use of resource overlay has not been fully +explored. Until it has, only OEMs are trusted to use resource overlay. +For this reason, overlay packages must reside in /system/overlay. + + +Resource ID mapping +~~~~~~~~~~~~~~~~~~~ +Resource identifiers must be coherent within the same namespace (ie +PackageGroup in ResourceTypes.cpp). Calling applications will refer to +resources using the IDs defined in the original package, but there is no +guarantee aapt has assigned the same ID to the corresponding resource in +an overlay package. To translate between the two, a resource ID mapping +{original ID -> overlay ID} is created during package installation +(PackageManagerService.java) and used during resource lookup. The +mapping is stored in /data/resource-cache, with a @idmap file name +suffix. + +The idmap file format is documented in a separate section, below. + + +Package management +~~~~~~~~~~~~~~~~~~ +Packages are managed by the PackageManagerService. Addition and removal +of packages are monitored via the inotify framework, exposed via +android.os.FileObserver. + +During initialization of a Dalvik process, ActivityThread.java requests +the process' AssetManager (by proxy, via AssetManager.java and JNI) +to load a list of packages. This list includes overlay packages, if +present. + +When a target package or a corresponding overlay package is installed, +the target package's process is stopped and a new idmap is generated. +This is similar to how applications are stopped when their packages are +upgraded. + + +Creating overlay packages +------------------------- + +Overlay packages should contain no code, define (some) resources with +the same type and name as in the original package, and be compiled with +the -o flag passed to aapt. + +The aapt -o flag instructs aapt to create an overlay package. +Technically, this means the package will be assigned package id 0x00. + +There are no restrictions on overlay packages names, though the naming +convention .overlay. is recommended. + + +Example overlay package +~~~~~~~~~~~~~~~~~~~~~~~ + +To overlay the resource bool/b in package com.foo.bar, to be applied +when the display is in landscape mode, create a new package with +no source code and a single .xml file under res/values-land, with +an entry for bool/b. Compile with aapt -o and place the results in +/system/overlay by adding the following to Android.mk: + +LOCAL_AAPT_FLAGS := -o com.foo.bar +LOCAL_MODULE_PATH := $(TARGET_OUT)/overlay + + +The ID map (idmap) file format +------------------------------ + +The idmap format is designed for lookup performance. However, leading +and trailing undefined overlay values are discarded to reduce the memory +footprint. + + +idmap grammar +~~~~~~~~~~~~~ +All atoms (names in square brackets) are uint32_t integers. The +idmap-magic constant spells "idmp" in ASCII. Offsets are given relative +to the data_header, not to the beginning of the file. + +map := header data +header := idmap-magic +idmap-magic := <0x706d6469> +data := data_header type_block+ +data_header := header_block{m} +header_block := <0> | +type_block := entry{n} +entry := + + +idmap example +~~~~~~~~~~~~~ +Given a pair of target and overlay packages with CRC sums 0x216a8fe2 +and 0x6b9beaec, each defining the following resources + +Name Target package Overlay package +string/str0 0x7f010000 - +string/str1 0x7f010001 0x7f010000 +string/str2 0x7f010002 - +string/str3 0x7f010003 0x7f010001 +string/str4 0x7f010004 - +bool/bool0 0x7f020000 - +integer/int0 0x7f030000 0x7f020000 +integer/int1 0x7f030001 - + +the corresponding resource map is + +0x706d6469 0x216a8fe2 0x6b9beaec 0x00000003 \ +0x00000004 0x00000000 0x00000009 0x00000003 \ +0x00000001 0x7f010000 0x00000000 0x7f010001 \ +0x00000001 0x00000000 0x7f020000 + +or, formatted differently + +0x706d6469 # magic: all idmap files begin with this constant +0x216a8fe2 # CRC32 of the resources.arsc file in the original package +0x6b9beaec # CRC32 of the resources.arsc file in the overlay package +0x00000003 # header; three types (string, bool, integer) in the target package +0x00000004 # header_block for type 0 (string) is located at offset 4 +0x00000000 # no bool type exists in overlay package -> no header_block +0x00000009 # header_block for type 2 (integer) is located at offset 9 +0x00000003 # header_block for string; overlay IDs span 3 elements +0x00000001 # the first string in target package is entry 1 == offset +0x7f010000 # target 0x7f01001 -> overlay 0x7f010000 +0x00000000 # str2 not defined in overlay package +0x7f010001 # target 0x7f010003 -> overlay 0x7f010001 +0x00000001 # header_block for integer; overlay IDs span 1 element +0x00000000 # offset == 0 +0x7f020000 # target 0x7f030000 -> overlay 0x7f020000 diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp index 7197ad7a2..a6cdb23f3 100644 --- a/libs/utils/ResourceTypes.cpp +++ b/libs/utils/ResourceTypes.cpp @@ -63,6 +63,10 @@ namespace android { #endif #endif +#define IDMAP_MAGIC 0x706d6469 +// size measured in sizeof(uint32_t) +#define IDMAP_HEADER_SIZE (ResTable::IDMAP_HEADER_SIZE_BYTES / sizeof(uint32_t)) + static void printToLogFunc(void* cookie, const char* txt) { LOGV("%s", txt); @@ -214,6 +218,81 @@ static void deserializeInternal(const void* inData, Res_png_9patch* outData) { outData->colors = (uint32_t*) data; } +static bool assertIdmapHeader(const uint32_t* map, size_t sizeBytes) +{ + if (sizeBytes < ResTable::IDMAP_HEADER_SIZE_BYTES) { + LOGW("idmap assertion failed: size=%d bytes\n", sizeBytes); + return false; + } + if (*map != htodl(IDMAP_MAGIC)) { // htodl: map data expected to be in correct endianess + LOGW("idmap assertion failed: invalid magic found (is 0x%08x, expected 0x%08x)\n", + *map, htodl(IDMAP_MAGIC)); + return false; + } + return true; +} + +static status_t idmapLookup(const uint32_t* map, size_t sizeBytes, uint32_t key, uint32_t* outValue) +{ + // see README for details on the format of map + if (!assertIdmapHeader(map, sizeBytes)) { + return UNKNOWN_ERROR; + } + map = map + IDMAP_HEADER_SIZE; // skip ahead to data segment + // size of data block, in uint32_t + const size_t size = (sizeBytes - ResTable::IDMAP_HEADER_SIZE_BYTES) / sizeof(uint32_t); + const uint32_t type = Res_GETTYPE(key) + 1; // add one, idmap stores "public" type id + const uint32_t entry = Res_GETENTRY(key); + const uint32_t typeCount = *map; + + if (type > typeCount) { + LOGW("Resource ID map: type=%d exceeds number of types=%d\n", type, typeCount); + return UNKNOWN_ERROR; + } + if (typeCount > size) { + LOGW("Resource ID map: number of types=%d exceeds size of map=%d\n", typeCount, size); + return UNKNOWN_ERROR; + } + const uint32_t typeOffset = map[type]; + if (typeOffset == 0) { + *outValue = 0; + return NO_ERROR; + } + if (typeOffset + 1 > size) { + LOGW("Resource ID map: type offset=%d exceeds reasonable value, size of map=%d\n", + typeOffset, size); + return UNKNOWN_ERROR; + } + const uint32_t entryCount = map[typeOffset]; + const uint32_t entryOffset = map[typeOffset + 1]; + if (entryCount == 0 || entry < entryOffset || entry - entryOffset > entryCount - 1) { + *outValue = 0; + return NO_ERROR; + } + const uint32_t index = typeOffset + 2 + entry - entryOffset; + if (index > size) { + LOGW("Resource ID map: entry index=%d exceeds size of map=%d\n", index, size); + *outValue = 0; + return NO_ERROR; + } + *outValue = map[index]; + + return NO_ERROR; +} + +static status_t getIdmapPackageId(const uint32_t* map, size_t mapSize, uint32_t *outId) +{ + if (!assertIdmapHeader(map, mapSize)) { + return UNKNOWN_ERROR; + } + const uint32_t* p = map + IDMAP_HEADER_SIZE + 1; + while (*p == 0) { + ++p; + } + *outId = (map[*p + IDMAP_HEADER_SIZE + 2] >> 24) & 0x000000ff; + return NO_ERROR; +} + Res_png_9patch* Res_png_9patch::deserialize(const void* inData) { if (sizeof(void*) != sizeof(int32_t)) { @@ -1290,7 +1369,13 @@ status_t ResXMLTree::validateNode(const ResXMLTree_node* node) const struct ResTable::Header { - Header(ResTable* _owner) : owner(_owner), ownedData(NULL), header(NULL) { } + Header(ResTable* _owner) : owner(_owner), ownedData(NULL), header(NULL), + resourceIDMap(NULL), resourceIDMapSize(0) { } + + ~Header() + { + free(resourceIDMap); + } ResTable* const owner; void* ownedData; @@ -1301,6 +1386,8 @@ struct ResTable::Header void* cookie; ResStringPool values; + uint32_t* resourceIDMap; + size_t resourceIDMapSize; }; struct ResTable::Type @@ -1716,12 +1803,13 @@ inline ssize_t ResTable::getResourcePackageIndex(uint32_t resID) const return ((ssize_t)mPackageMap[Res_GETPACKAGE(resID)+1])-1; } -status_t ResTable::add(const void* data, size_t size, void* cookie, bool copyData) +status_t ResTable::add(const void* data, size_t size, void* cookie, bool copyData, + const void* idmap) { - return add(data, size, cookie, NULL, copyData); + return add(data, size, cookie, NULL, copyData, reinterpret_cast(idmap)); } -status_t ResTable::add(Asset* asset, void* cookie, bool copyData) +status_t ResTable::add(Asset* asset, void* cookie, bool copyData, const void* idmap) { const void* data = asset->getBuffer(true); if (data == NULL) { @@ -1729,7 +1817,7 @@ status_t ResTable::add(Asset* asset, void* cookie, bool copyData) return UNKNOWN_ERROR; } size_t size = (size_t)asset->getLength(); - return add(data, size, cookie, asset, copyData); + return add(data, size, cookie, asset, copyData, reinterpret_cast(idmap)); } status_t ResTable::add(ResTable* src) @@ -1757,19 +1845,30 @@ status_t ResTable::add(ResTable* src) } status_t ResTable::add(const void* data, size_t size, void* cookie, - Asset* asset, bool copyData) + Asset* asset, bool copyData, const Asset* idmap) { if (!data) return NO_ERROR; Header* header = new Header(this); header->index = mHeaders.size(); header->cookie = cookie; + if (idmap != NULL) { + const size_t idmap_size = idmap->getLength(); + const void* idmap_data = const_cast(idmap)->getBuffer(true); + header->resourceIDMap = (uint32_t*)malloc(idmap_size); + if (header->resourceIDMap == NULL) { + delete header; + return (mError = NO_MEMORY); + } + memcpy((void*)header->resourceIDMap, idmap_data, idmap_size); + header->resourceIDMapSize = idmap_size; + } mHeaders.add(header); const bool notDeviceEndian = htods(0xf0) != 0xf0; LOAD_TABLE_NOISY( - LOGV("Adding resources to ResTable: data=%p, size=0x%x, cookie=%p, asset=%p, copy=%d\n", - data, size, cookie, asset, copyData)); + LOGV("Adding resources to ResTable: data=%p, size=0x%x, cookie=%p, asset=%p, copy=%d " + "idmap=%p\n", data, size, cookie, asset, copyData, idmap)); if (copyData || notDeviceEndian) { header->ownedData = malloc(size); @@ -1836,7 +1935,16 @@ status_t ResTable::add(const void* data, size_t size, void* cookie, dtohl(header->header->packageCount)); return (mError=BAD_TYPE); } - if (parsePackage((ResTable_package*)chunk, header) != NO_ERROR) { + uint32_t idmap_id = 0; + if (idmap != NULL) { + uint32_t tmp; + if (getIdmapPackageId(header->resourceIDMap, + header->resourceIDMapSize, + &tmp) == NO_ERROR) { + idmap_id = tmp; + } + } + if (parsePackage((ResTable_package*)chunk, header, idmap_id) != NO_ERROR) { return mError; } curPackage++; @@ -1858,6 +1966,7 @@ status_t ResTable::add(const void* data, size_t size, void* cookie, if (mError != NO_ERROR) { LOGW("No string values found in resource table!"); } + TABLE_NOISY(LOGV("Returning from add with mError=%d\n", mError)); return mError; } @@ -2002,17 +2111,38 @@ ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag size_t ip = grp->packages.size(); while (ip > 0) { ip--; + int T = t; + int E = e; const Package* const package = grp->packages[ip]; + if (package->header->resourceIDMap) { + uint32_t overlayResID = 0x0; + status_t retval = idmapLookup(package->header->resourceIDMap, + package->header->resourceIDMapSize, + resID, &overlayResID); + if (retval == NO_ERROR && overlayResID != 0x0) { + // for this loop iteration, this is the type and entry we really want + LOGV("resource map 0x%08x -> 0x%08x\n", resID, overlayResID); + T = Res_GETTYPE(overlayResID); + E = Res_GETENTRY(overlayResID); + } else { + // resource not present in overlay package, continue with the next package + continue; + } + } const ResTable_type* type; const ResTable_entry* entry; const Type* typeClass; - ssize_t offset = getEntry(package, t, e, desiredConfig, &type, &entry, &typeClass); + ssize_t offset = getEntry(package, T, E, desiredConfig, &type, &entry, &typeClass); if (offset <= 0) { - if (offset < 0) { + // No {entry, appropriate config} pair found in package. If this + // package is an overlay package (ip != 0), this simply means the + // overlay package did not specify a default. + // Non-overlay packages are still required to provide a default. + if (offset < 0 && ip == 0) { LOGW("Failure getting entry for 0x%08x (t=%d e=%d) in package %zd (error %d)\n", - resID, t, e, ip, (int)offset); + resID, T, E, ip, (int)offset); rc = offset; goto out; } @@ -2044,13 +2174,16 @@ ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag if (outSpecFlags != NULL) { if (typeClass->typeSpecFlags != NULL) { - *outSpecFlags |= dtohl(typeClass->typeSpecFlags[e]); + *outSpecFlags |= dtohl(typeClass->typeSpecFlags[E]); } else { *outSpecFlags = -1; } } - - if (bestPackage != NULL && bestItem.isMoreSpecificThan(thisConfig)) { + + if (bestPackage != NULL && + (bestItem.isMoreSpecificThan(thisConfig) || bestItem.diff(thisConfig) == 0)) { + // Discard thisConfig not only if bestItem is more specific, but also if the two configs + // are identical (diff == 0), or overlay packages will not take effect. continue; } @@ -2250,21 +2383,45 @@ ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag, TABLE_NOISY(LOGI("Building bag: %p\n", (void*)resID)); + ResTable_config bestConfig; + memset(&bestConfig, 0, sizeof(bestConfig)); + // Now collect all bag attributes from all packages. size_t ip = grp->packages.size(); while (ip > 0) { ip--; + int T = t; + int E = e; const Package* const package = grp->packages[ip]; + if (package->header->resourceIDMap) { + uint32_t overlayResID = 0x0; + status_t retval = idmapLookup(package->header->resourceIDMap, + package->header->resourceIDMapSize, + resID, &overlayResID); + if (retval == NO_ERROR && overlayResID != 0x0) { + // for this loop iteration, this is the type and entry we really want + LOGV("resource map 0x%08x -> 0x%08x\n", resID, overlayResID); + T = Res_GETTYPE(overlayResID); + E = Res_GETENTRY(overlayResID); + } else { + // resource not present in overlay package, continue with the next package + continue; + } + } const ResTable_type* type; const ResTable_entry* entry; const Type* typeClass; - LOGV("Getting entry pkg=%p, t=%d, e=%d\n", package, t, e); - ssize_t offset = getEntry(package, t, e, &mParams, &type, &entry, &typeClass); + LOGV("Getting entry pkg=%p, t=%d, e=%d\n", package, T, E); + ssize_t offset = getEntry(package, T, E, &mParams, &type, &entry, &typeClass); LOGV("Resulting offset=%d\n", offset); if (offset <= 0) { - if (offset < 0) { + // No {entry, appropriate config} pair found in package. If this + // package is an overlay package (ip != 0), this simply means the + // overlay package did not specify a default. + // Non-overlay packages are still required to provide a default. + if (offset < 0 && ip == 0) { if (set) free(set); return offset; } @@ -2277,6 +2434,15 @@ ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag, continue; } + if (set != NULL && !type->config.isBetterThan(bestConfig, NULL)) { + continue; + } + bestConfig = type->config; + if (set) { + free(set); + set = NULL; + } + const uint16_t entrySize = dtohs(entry->size); const uint32_t parent = entrySize >= sizeof(ResTable_map_entry) ? dtohl(((const ResTable_map_entry*)entry)->parent.ident) : 0; @@ -2288,43 +2454,41 @@ ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag, TABLE_NOISY(LOGI("Found map: size=%p parent=%p count=%d\n", entrySize, parent, count)); - if (set == NULL) { - // If this map inherits from another, we need to start - // with its parent's values. Otherwise start out empty. - TABLE_NOISY(printf("Creating new bag, entrySize=0x%08x, parent=0x%08x\n", - entrySize, parent)); - if (parent) { - const bag_entry* parentBag; - uint32_t parentTypeSpecFlags = 0; - const ssize_t NP = getBagLocked(parent, &parentBag, &parentTypeSpecFlags); - const size_t NT = ((NP >= 0) ? NP : 0) + N; - set = (bag_set*)malloc(sizeof(bag_set)+sizeof(bag_entry)*NT); - if (set == NULL) { - return NO_MEMORY; - } - if (NP > 0) { - memcpy(set+1, parentBag, NP*sizeof(bag_entry)); - set->numAttrs = NP; - TABLE_NOISY(LOGI("Initialized new bag with %d inherited attributes.\n", NP)); - } else { - TABLE_NOISY(LOGI("Initialized new bag with no inherited attributes.\n")); - set->numAttrs = 0; - } - set->availAttrs = NT; - set->typeSpecFlags = parentTypeSpecFlags; - } else { - set = (bag_set*)malloc(sizeof(bag_set)+sizeof(bag_entry)*N); - if (set == NULL) { - return NO_MEMORY; - } - set->numAttrs = 0; - set->availAttrs = N; - set->typeSpecFlags = 0; + // If this map inherits from another, we need to start + // with its parent's values. Otherwise start out empty. + TABLE_NOISY(printf("Creating new bag, entrySize=0x%08x, parent=0x%08x\n", + entrySize, parent)); + if (parent) { + const bag_entry* parentBag; + uint32_t parentTypeSpecFlags = 0; + const ssize_t NP = getBagLocked(parent, &parentBag, &parentTypeSpecFlags); + const size_t NT = ((NP >= 0) ? NP : 0) + N; + set = (bag_set*)malloc(sizeof(bag_set)+sizeof(bag_entry)*NT); + if (set == NULL) { + return NO_MEMORY; } + if (NP > 0) { + memcpy(set+1, parentBag, NP*sizeof(bag_entry)); + set->numAttrs = NP; + TABLE_NOISY(LOGI("Initialized new bag with %d inherited attributes.\n", NP)); + } else { + TABLE_NOISY(LOGI("Initialized new bag with no inherited attributes.\n")); + set->numAttrs = 0; + } + set->availAttrs = NT; + set->typeSpecFlags = parentTypeSpecFlags; + } else { + set = (bag_set*)malloc(sizeof(bag_set)+sizeof(bag_entry)*N); + if (set == NULL) { + return NO_MEMORY; + } + set->numAttrs = 0; + set->availAttrs = N; + set->typeSpecFlags = 0; } if (typeClass->typeSpecFlags != NULL) { - set->typeSpecFlags |= dtohl(typeClass->typeSpecFlags[e]); + set->typeSpecFlags |= dtohl(typeClass->typeSpecFlags[E]); } else { set->typeSpecFlags = -1; } @@ -3862,7 +4026,7 @@ ssize_t ResTable::getEntry( } status_t ResTable::parsePackage(const ResTable_package* const pkg, - const Header* const header) + const Header* const header, uint32_t idmap_id) { const uint8_t* base = (const uint8_t*)pkg; status_t err = validate_chunk(&pkg->header, sizeof(*pkg), @@ -3896,8 +4060,12 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg, Package* package = NULL; PackageGroup* group = NULL; - uint32_t id = dtohl(pkg->id); - if (id != 0 && id < 256) { + uint32_t id = idmap_id != 0 ? idmap_id : dtohl(pkg->id); + // If at this point id == 0, pkg is an overlay package without a + // corresponding idmap. During regular usage, overlay packages are + // always loaded alongside their idmaps, but during idmap creation + // the package is temporarily loaded by itself. + if (id < 256) { package = new Package(this, header, pkg); if (package == NULL) { @@ -3950,7 +4118,7 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg, return (mError=err); } } else { - LOG_ALWAYS_FATAL("Skins not supported!"); + LOG_ALWAYS_FATAL("Package id out of range"); return NO_ERROR; } @@ -4101,6 +4269,136 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg, return NO_ERROR; } +status_t ResTable::createIdmap(const ResTable& overlay, uint32_t originalCrc, uint32_t overlayCrc, + void** outData, size_t* outSize) const +{ + // see README for details on the format of map + if (mPackageGroups.size() == 0) { + return UNKNOWN_ERROR; + } + if (mPackageGroups[0]->packages.size() == 0) { + return UNKNOWN_ERROR; + } + + Vector > map; + const PackageGroup* pg = mPackageGroups[0]; + const Package* pkg = pg->packages[0]; + size_t typeCount = pkg->types.size(); + // starting size is header + first item (number of types in map) + *outSize = (IDMAP_HEADER_SIZE + 1) * sizeof(uint32_t); + const String16 overlayPackage(overlay.mPackageGroups[0]->packages[0]->package->name); + const uint32_t pkg_id = pkg->package->id << 24; + + for (size_t typeIndex = 0; typeIndex < typeCount; ++typeIndex) { + ssize_t offset = -1; + const Type* typeConfigs = pkg->getType(typeIndex); + ssize_t mapIndex = map.add(); + if (mapIndex < 0) { + return NO_MEMORY; + } + Vector& vector = map.editItemAt(mapIndex); + for (size_t entryIndex = 0; entryIndex < typeConfigs->entryCount; ++entryIndex) { + uint32_t resID = (0xff000000 & ((pkg->package->id)<<24)) + | (0x00ff0000 & ((typeIndex+1)<<16)) + | (0x0000ffff & (entryIndex)); + resource_name resName; + if (!this->getResourceName(resID, &resName)) { + return UNKNOWN_ERROR; + } + + const String16 overlayType(resName.type, resName.typeLen); + const String16 overlayName(resName.name, resName.nameLen); + uint32_t overlayResID = overlay.identifierForName(overlayName.string(), + overlayName.size(), + overlayType.string(), + overlayType.size(), + overlayPackage.string(), + overlayPackage.size()); + if (overlayResID != 0) { + // overlay package has package ID == 0, use original package's ID instead + overlayResID |= pkg_id; + } + vector.push(overlayResID); + if (overlayResID != 0 && offset == -1) { + offset = Res_GETENTRY(resID); + } +#if 0 + if (overlayResID != 0) { + LOGD("%s/%s 0x%08x -> 0x%08x\n", + String8(String16(resName.type)).string(), + String8(String16(resName.name)).string(), + resID, overlayResID); + } +#endif + } + + if (offset != -1) { + // shave off leading and trailing entries which lack overlay values + vector.removeItemsAt(0, offset); + vector.insertAt((uint32_t)offset, 0, 1); + while (vector.top() == 0) { + vector.pop(); + } + // reserve space for number and offset of entries, and the actual entries + *outSize += (2 + vector.size()) * sizeof(uint32_t); + } else { + // no entries of current type defined in overlay package + vector.clear(); + // reserve space for type offset + *outSize += 1 * sizeof(uint32_t); + } + } + + if ((*outData = malloc(*outSize)) == NULL) { + return NO_MEMORY; + } + uint32_t* data = (uint32_t*)*outData; + *data++ = htodl(IDMAP_MAGIC); + *data++ = htodl(originalCrc); + *data++ = htodl(overlayCrc); + const size_t mapSize = map.size(); + *data++ = htodl(mapSize); + size_t offset = mapSize; + for (size_t i = 0; i < mapSize; ++i) { + const Vector& vector = map.itemAt(i); + const size_t N = vector.size(); + if (N == 0) { + *data++ = htodl(0); + } else { + offset++; + *data++ = htodl(offset); + offset += N; + } + } + for (size_t i = 0; i < mapSize; ++i) { + const Vector& vector = map.itemAt(i); + const size_t N = vector.size(); + if (N == 0) { + continue; + } + *data++ = htodl(N - 1); // do not count the offset (which is vector's first element) + for (size_t j = 0; j < N; ++j) { + const uint32_t& overlayResID = vector.itemAt(j); + *data++ = htodl(overlayResID); + } + } + + return NO_ERROR; +} + +bool ResTable::getIdmapInfo(const void* idmap, size_t sizeBytes, + uint32_t* pOriginalCrc, uint32_t* pOverlayCrc) +{ + const uint32_t* map = (const uint32_t*)idmap; + if (!assertIdmapHeader(map, sizeBytes)) { + return false; + } + *pOriginalCrc = map[1]; + *pOverlayCrc = map[2]; + return true; +} + + #ifndef HAVE_ANDROID_OS #define CHAR16_TO_CSTR(c16, len) (String8(String16(c16,len)).string()) From aa3a2c561323b7c68594b2079b9d4f02ca9178d0 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Wed, 30 Mar 2011 16:20:26 -0700 Subject: [PATCH 308/541] Add new resource configurations for screen width/height in "dp". You can now specify resource configuration variants "wNNNdp" and "hNNNdp". These are the minimum screen width/height in "dp" units. This allows you to do things like have your app adjust its layout based only on the about of horizontal space available. This introduces a new configuration change flag for screen size. Note that this configuration change happens each time the orientation changes. Applications often say they handle the orientation change to avoid being restarted at a screen rotation, and this will now cause them to be restarted. To address this, we assume the app can handle this new config change if its target SDK version is < ICS. Change-Id: I22f8afa136b4f274423978c570fa7c9855040496 --- include/utils/ResourceTypes.h | 67 +++++++++++++++++++++++++++++++++-- libs/utils/ResourceTypes.cpp | 31 ++++++++++++---- 2 files changed, 89 insertions(+), 9 deletions(-) diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h index 24e72e90e..173412e41 100644 --- a/include/utils/ResourceTypes.h +++ b/include/utils/ResourceTypes.h @@ -972,6 +972,14 @@ struct ResTable_config uint32_t screenConfig; }; + union { + struct { + uint16_t screenWidthDp; + uint16_t screenHeightDp; + }; + uint32_t screenSizeDp; + }; + inline void copyFromDeviceNoSwap(const ResTable_config& o) { const size_t size = dtohl(o.size); if (size >= sizeof(ResTable_config)) { @@ -992,6 +1000,8 @@ struct ResTable_config screenHeight = dtohs(screenHeight); sdkVersion = dtohs(sdkVersion); minorVersion = dtohs(minorVersion); + screenWidthDp = dtohs(screenWidthDp); + screenHeightDp = dtohs(screenHeightDp); } inline void swapHtoD() { @@ -1003,6 +1013,8 @@ struct ResTable_config screenHeight = htods(screenHeight); sdkVersion = htods(sdkVersion); minorVersion = htods(minorVersion); + screenWidthDp = htods(screenWidthDp); + screenHeightDp = htods(screenHeightDp); } inline int compare(const ResTable_config& o) const { @@ -1021,6 +1033,8 @@ struct ResTable_config diff = (int32_t)(screenLayout - o.screenLayout); if (diff != 0) return diff; diff = (int32_t)(uiMode - o.uiMode); + if (diff != 0) return diff; + diff = (int32_t)(screenSizeDp - o.screenSizeDp); return (int)diff; } @@ -1061,6 +1075,7 @@ struct ResTable_config if (version != o.version) diffs |= CONFIG_VERSION; if (screenLayout != o.screenLayout) diffs |= CONFIG_SCREEN_LAYOUT; if (uiMode != o.uiMode) diffs |= CONFIG_UI_MODE; + if (screenSizeDp != o.screenSizeDp) diffs |= CONFIG_SCREEN_SIZE; return diffs; } @@ -1105,6 +1120,18 @@ struct ResTable_config } } + if (screenSizeDp || o.screenSizeDp) { + if (screenWidthDp != o.screenWidthDp) { + if (!screenWidthDp) return false; + if (!o.screenWidthDp) return true; + } + + if (screenHeightDp != o.screenHeightDp) { + if (!screenHeightDp) return false; + if (!o.screenHeightDp) return true; + } + } + if (orientation != o.orientation) { if (!orientation) return false; if (!o.orientation) return true; @@ -1243,6 +1270,30 @@ struct ResTable_config } } + if (screenSizeDp || o.screenSizeDp) { + // Better is based on the sum of the difference between both + // width and height from the requested dimensions. We are + // assuming the invalid configs (with smaller dimens) have + // already been filtered. Note that if a particular dimension + // is unspecified, we will end up with a large value (the + // difference between 0 and the requested dimension), which is + // good since we will prefer a config that has specified a + // dimension value. + int myDelta = 0, otherDelta = 0; + if (requested->screenWidthDp) { + myDelta += requested->screenWidthDp - screenWidthDp; + otherDelta += requested->screenWidthDp - o.screenWidthDp; + } + if (requested->screenHeightDp) { + myDelta += requested->screenHeightDp - screenHeightDp; + otherDelta += requested->screenHeightDp - o.screenHeightDp; + } + //LOGI("Comparing this %dx%d to other %dx%d in %dx%d: myDelta=%d otherDelta=%d", + // screenWidthDp, screenHeightDp, o.screenWidthDp, o.screenHeightDp, + // requested->screenWidthDp, requested->screenHeightDp, myDelta, otherDelta); + return (myDelta <= otherDelta); + } + if ((orientation != o.orientation) && requested->orientation) { return (orientation); } @@ -1426,6 +1477,18 @@ struct ResTable_config return false; } } + if (screenSizeDp != 0) { + if (settings.screenWidthDp != 0 && screenWidthDp != 0 + && screenWidthDp > settings.screenWidthDp) { + //LOGI("Filtering out width %d in requested %d", screenWidthDp, settings.screenWidthDp); + return false; + } + if (settings.screenHeightDp != 0 && screenHeightDp != 0 + && screenHeightDp > settings.screenHeightDp) { + //LOGI("Filtering out height %d in requested %d", screenHeightDp, settings.screenHeightDp); + return false; + } + } if (screenType != 0) { if (settings.orientation != 0 && orientation != 0 && orientation != settings.orientation) { @@ -1505,13 +1568,13 @@ struct ResTable_config String8 toString() const { char buf[200]; sprintf(buf, "imsi=%d/%d lang=%c%c reg=%c%c orient=%d touch=%d dens=%d " - "kbd=%d nav=%d input=%d scrnW=%d scrnH=%d sz=%d long=%d " + "kbd=%d nav=%d input=%d ssz=%dx%d %ddp x %ddp sz=%d long=%d " "ui=%d night=%d vers=%d.%d", mcc, mnc, language[0] ? language[0] : '-', language[1] ? language[1] : '-', country[0] ? country[0] : '-', country[1] ? country[1] : '-', orientation, touchscreen, density, keyboard, navigation, inputFlags, - screenWidth, screenHeight, + screenWidth, screenHeight, screenWidthDp, screenHeightDp, screenLayout&MASK_SCREENSIZE, screenLayout&MASK_SCREENLONG, uiMode&MASK_UI_MODE_TYPE, uiMode&MASK_UI_MODE_NIGHT, sdkVersion, minorVersion); diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp index a6cdb23f3..784c9d284 100644 --- a/libs/utils/ResourceTypes.cpp +++ b/libs/utils/ResourceTypes.cpp @@ -2588,7 +2588,7 @@ void ResTable::setParameters(const ResTable_config* params) { mLock.lock(); TABLE_GETENTRY(LOGI("Setting parameters: imsi:%d/%d lang:%c%c cnt:%c%c " - "orien:%d touch:%d density:%d key:%d inp:%d nav:%d w:%d h:%d\n", + "orien:%d touch:%d density:%d key:%d inp:%d nav:%d sz:%dx%d %ddp x %ddp\n", params->mcc, params->mnc, params->language[0] ? params->language[0] : '-', params->language[1] ? params->language[1] : '-', @@ -2601,7 +2601,9 @@ void ResTable::setParameters(const ResTable_config* params) params->inputFlags, params->navigation, params->screenWidth, - params->screenHeight)); + params->screenHeight, + params->screenWidthDp, + params->screenHeightDp)); mParams = *params; for (size_t i=0; iconfig); - TABLE_GETENTRY(LOGI("Match entry 0x%x in type 0x%x (sz 0x%x): imsi:%d/%d=%d/%d lang:%c%c=%c%c cnt:%c%c=%c%c " - "orien:%d=%d touch:%d=%d density:%d=%d key:%d=%d inp:%d=%d nav:%d=%d w:%d=%d h:%d=%d\n", + TABLE_GETENTRY(LOGI("Match entry 0x%x in type 0x%x (sz 0x%x): imsi:%d/%d=%d/%d " + "lang:%c%c=%c%c cnt:%c%c=%c%c orien:%d=%d touch:%d=%d " + "density:%d=%d key:%d=%d inp:%d=%d nav:%d=%d w:%d=%d h:%d=%d " + "wdp:%d=%d hdp:%d=%d\n", entryIndex, typeIndex+1, dtohl(thisType->config.size), thisConfig.mcc, thisConfig.mnc, config ? config->mcc : 0, config ? config->mnc : 0, @@ -3950,7 +3954,11 @@ ssize_t ResTable::getEntry( thisConfig.screenWidth, config ? config->screenWidth : 0, thisConfig.screenHeight, - config ? config->screenHeight : 0)); + config ? config->screenHeight : 0, + thisConfig.screenWidthDp, + config ? config->screenWidthDp : 0, + thisConfig.screenHeightDp, + config ? config->screenHeightDp : 0)); // Check to make sure this one is valid for the current parameters. if (config && !thisConfig.match(*config)) { @@ -4235,7 +4243,8 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg, ResTable_config thisConfig; thisConfig.copyFromDtoH(type->config); LOGI("Adding config to type %d: imsi:%d/%d lang:%c%c cnt:%c%c " - "orien:%d touch:%d density:%d key:%d inp:%d nav:%d w:%d h:%d\n", + "orien:%d touch:%d density:%d key:%d inp:%d nav:%d w:%d h:%d " + "wdp:%d hdp:%d\n", type->id, thisConfig.mcc, thisConfig.mnc, thisConfig.language[0] ? thisConfig.language[0] : '-', @@ -4249,7 +4258,9 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg, thisConfig.inputFlags, thisConfig.navigation, thisConfig.screenWidth, - thisConfig.screenHeight)); + thisConfig.screenHeight, + thisConfig.screenWidthDp, + thisConfig.screenHeightDp)); t->configs.add(type); } else { status_t err = validate_chunk(chunk, sizeof(ResChunk_header), @@ -4742,6 +4753,12 @@ void ResTable::print(bool inclValues) const if (type->config.screenHeight != 0) { printf(" h=%d", dtohs(type->config.screenHeight)); } + if (type->config.screenWidthDp != 0) { + printf(" wdp=%d", dtohs(type->config.screenWidthDp)); + } + if (type->config.screenHeightDp != 0) { + printf(" hdp=%d", dtohs(type->config.screenHeightDp)); + } if (type->config.sdkVersion != 0) { printf(" sdk=%d", dtohs(type->config.sdkVersion)); } From aa13c1b90e5d9cae064bb425dd094ccbd411e073 Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Tue, 12 Apr 2011 22:39:53 -0700 Subject: [PATCH 309/541] Initial checkin of spot presentation for touchpad gestures. Added a new PointerIcon API (hidden for now) for loading pointer icons. Fixed a starvation problem in the native Looper's sendMessage implementation which caused new messages to be posted ahead of old messages sent with sendMessageDelayed. Redesigned the touch pad gestures to be defined in terms of more fluid finger / spot movements. The objective is to reinforce the natural mapping between fingers and spots which means there must not be any discontinuities in spot motion relative to the fingers. Removed the SpotController stub and folded its responsibilities into PointerController. Change-Id: I5126b1e69d95252fda7f2a684c9287e239a57163 --- libs/utils/Looper.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libs/utils/Looper.cpp b/libs/utils/Looper.cpp index d5dd12606..b54fb9dd7 100644 --- a/libs/utils/Looper.cpp +++ b/libs/utils/Looper.cpp @@ -662,7 +662,8 @@ void Looper::wakeAndLock() { #endif void Looper::sendMessage(const sp& handler, const Message& message) { - sendMessageAtTime(LLONG_MIN, handler, message); + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + sendMessageAtTime(now, handler, message); } void Looper::sendMessageDelayed(nsecs_t uptimeDelay, const sp& handler, From 8bb27951f592713bcb2c0e6605d89da665131637 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Fri, 22 Apr 2011 11:13:35 -0700 Subject: [PATCH 310/541] libutils: Fix an improper const-cast in RefBase MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Under Fedora 15 Beta, gcc 4.6.0 warns: frameworks/base/libs/utils/RefBase.cpp: In member function ‘void android::RefBase::weakref_type::trackMe(bool, bool)’: frameworks/base/libs/utils/RefBase.cpp:483:67: error: passing ‘const android::RefBase::weakref_impl’ as ‘this’ argument of ‘void android::RefBase::weakref_impl::trackMe(bool, bool)’ discards qualifiers [-fpermissive] trackMe is not a const function, so don't use const in the static_cast to a weakref_impl pointer. Change-Id: I3c9ba73eb127985f5f54197ffecf2939c50f632c --- libs/utils/RefBase.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/utils/RefBase.cpp b/libs/utils/RefBase.cpp index bb6c1255f..2034486aa 100644 --- a/libs/utils/RefBase.cpp +++ b/libs/utils/RefBase.cpp @@ -524,7 +524,7 @@ void RefBase::weakref_type::printRefs() const void RefBase::weakref_type::trackMe(bool enable, bool retain) { - static_cast(this)->trackMe(enable, retain); + static_cast(this)->trackMe(enable, retain); } RefBase::weakref_type* RefBase::createWeak(const void* id) const From bc55d727f3c7a5c95cc7a458ea8309bcff29919b Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Mon, 25 Apr 2011 15:28:17 -0700 Subject: [PATCH 311/541] Add some basic STL compatibility to Vector<> Change-Id: Iaf72623170ee415372c7989d7ba9ff627167449e --- include/utils/Vector.h | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/include/utils/Vector.h b/include/utils/Vector.h index 6fd307f3b..90477b75d 100644 --- a/include/utils/Vector.h +++ b/include/utils/Vector.h @@ -165,6 +165,26 @@ public: // for debugging only inline size_t getItemSize() const { return itemSize(); } + + /* + * these inlines add some level of compatibility with STL. eventually + * we should probably turn things around. + */ + typedef TYPE* iterator; + typedef TYPE const* const_iterator; + + inline iterator begin() { return editArray(); } + inline iterator end() { return editArray() + size(); } + inline const_iterator begin() const { return array(); } + inline const_iterator end() const { return array() + size(); } + inline void reserve(size_t n) { setCapacity(n); } + inline bool empty() const{ return isEmpty(); } + inline void push_back(const TYPE& item) { insertAt(size(), item); } + inline void push_front(const TYPE& item) { insertAt(0, item); } + inline iterator erase(iterator pos) { + return begin() + removeItemsAt(pos-array()); + } + protected: virtual void do_construct(void* storage, size_t num) const; virtual void do_destroy(void* storage, size_t num) const; From 58dc2c8101d273b5fa29ac182183c3508b35dfd7 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Mon, 25 Apr 2011 22:56:54 -0700 Subject: [PATCH 312/541] fix typo in Vector<>:: push_back and push_front parameter were inverted. Change-Id: I867b01de5f58354bb9668fa662dd2a7d78dd3b37 --- include/utils/Vector.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/utils/Vector.h b/include/utils/Vector.h index 90477b75d..f1e87e609 100644 --- a/include/utils/Vector.h +++ b/include/utils/Vector.h @@ -179,8 +179,8 @@ public: inline const_iterator end() const { return array() + size(); } inline void reserve(size_t n) { setCapacity(n); } inline bool empty() const{ return isEmpty(); } - inline void push_back(const TYPE& item) { insertAt(size(), item); } - inline void push_front(const TYPE& item) { insertAt(0, item); } + inline void push_back(const TYPE& item) { insertAt(item, size()); } + inline void push_front(const TYPE& item) { insertAt(item, 0); } inline iterator erase(iterator pos) { return begin() + removeItemsAt(pos-array()); } From 0897fe5eaa3a7ad9529ba059cf8b1a5375cb8940 Mon Sep 17 00:00:00 2001 From: Romain Guy Date: Thu, 28 Apr 2011 18:55:29 -0700 Subject: [PATCH 313/541] This file should not be here. Change-Id: I0ccb41869303fa765ed13763bf17c7e0ee9f036e --- GenerationCache.h | 245 ---------------------------------------------- 1 file changed, 245 deletions(-) delete mode 100644 GenerationCache.h diff --git a/GenerationCache.h b/GenerationCache.h deleted file mode 100644 index 42e6d9bb8..000000000 --- a/GenerationCache.h +++ /dev/null @@ -1,245 +0,0 @@ -/* - * Copyright (C) 2010 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_HWUI_GENERATION_CACHE_H -#define ANDROID_HWUI_GENERATION_CACHE_H - -#include -#include - -namespace android { -namespace uirenderer { - -template -class OnEntryRemoved { -public: - virtual ~OnEntryRemoved() { }; - virtual void operator()(EntryKey& key, EntryValue& value) = 0; -}; // class OnEntryRemoved - -template -struct Entry: public LightRefBase > { - Entry() { } - Entry(const Entry& e): - key(e.key), value(e.value), parent(e.parent), child(e.child) { } - Entry(sp > e): - key(e->key), value(e->value), parent(e->parent), child(e->child) { } - - EntryKey key; - EntryValue value; - - sp > parent; - sp > child; -}; // struct Entry - -template -class GenerationCache { -public: - GenerationCache(uint32_t maxCapacity); - virtual ~GenerationCache(); - - enum Capacity { - kUnlimitedCapacity, - }; - - void setOnEntryRemovedListener(OnEntryRemoved* listener); - - void clear(); - - bool contains(K key) const; - V get(K key); - K getKeyAt(uint32_t index) const; - bool put(K key, V value); - V remove(K key); - V removeOldest(); - V getValueAt(uint32_t index) const; - - uint32_t size() const; - - void addToCache(sp > entry, K key, V value); - void attachToCache(sp > entry); - void detachFromCache(sp > entry); - - V removeAt(ssize_t index); - - KeyedVector > > mCache; - uint32_t mMaxCapacity; - - OnEntryRemoved* mListener; - - sp > mOldest; - sp > mYoungest; -}; // class GenerationCache - -template -GenerationCache::GenerationCache(uint32_t maxCapacity): mMaxCapacity(maxCapacity), mListener(NULL) { -}; - -template -GenerationCache::~GenerationCache() { - clear(); -}; - -template -uint32_t GenerationCache::size() const { - return mCache.size(); -} - -template -void GenerationCache::setOnEntryRemovedListener(OnEntryRemoved* listener) { - mListener = listener; -} - -template -void GenerationCache::clear() { - if (mListener) { - for (uint32_t i = 0; i < mCache.size(); i++) { - sp > entry = mCache.valueAt(i); - if (mListener) { - (*mListener)(entry->key, entry->value); - } - } - } - mCache.clear(); - mYoungest.clear(); - mOldest.clear(); -} - -template -bool GenerationCache::contains(K key) const { - return mCache.indexOfKey(key) >= 0; -} - -template -K GenerationCache::getKeyAt(uint32_t index) const { - return mCache.keyAt(index); -} - -template -V GenerationCache::getValueAt(uint32_t index) const { - return mCache.valueAt(index)->value; -} - -template -V GenerationCache::get(K key) { - ssize_t index = mCache.indexOfKey(key); - if (index >= 0) { - sp > entry = mCache.valueAt(index); - if (entry.get()) { - detachFromCache(entry); - attachToCache(entry); - return entry->value; - } - } - - return NULL; -} - -template -bool GenerationCache::put(K key, V value) { - if (mMaxCapacity != kUnlimitedCapacity && mCache.size() >= mMaxCapacity) { - removeOldest(); - } - - ssize_t index = mCache.indexOfKey(key); - if (index < 0) { - sp > entry = new Entry; - addToCache(entry, key, value); - return true; - } - - return false; -} - -template -void GenerationCache::addToCache(sp > entry, K key, V value) { - entry->key = key; - entry->value = value; - mCache.add(key, entry); - attachToCache(entry); -} - -template -V GenerationCache::remove(K key) { - ssize_t index = mCache.indexOfKey(key); - if (index >= 0) { - return removeAt(index); - } - - return NULL; -} - -template -V GenerationCache::removeAt(ssize_t index) { - sp > entry = mCache.valueAt(index); - if (mListener) { - (*mListener)(entry->key, entry->value); - } - mCache.removeItemsAt(index, 1); - detachFromCache(entry); - - return entry->value; -} - -template -V GenerationCache::removeOldest() { - if (mOldest.get()) { - ssize_t index = mCache.indexOfKey(mOldest->key); - if (index >= 0) { - return removeAt(index); - } - } - - return NULL; -} - -template -void GenerationCache::attachToCache(sp > entry) { - if (!mYoungest.get()) { - mYoungest = mOldest = entry; - } else { - entry->parent = mYoungest; - mYoungest->child = entry; - mYoungest = entry; - } -} - -template -void GenerationCache::detachFromCache(sp > entry) { - if (entry->parent.get()) { - entry->parent->child = entry->child; - } - - if (entry->child.get()) { - entry->child->parent = entry->parent; - } - - if (mOldest == entry) { - mOldest = entry->child; - } - - if (mYoungest == entry) { - mYoungest = entry->parent; - } - - entry->parent.clear(); - entry->child.clear(); -} - -}; // namespace uirenderer -}; // namespace android - -#endif // ANDROID_HWUI_GENERATION_CACHE_H From 90d59043de5bf940c23688d7e1c9feadd1a85ddb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A5rten=20Kongstad?= Date: Thu, 5 May 2011 10:40:42 +0200 Subject: [PATCH 314/541] Add missing clean-up of idmap file descriptors. Change-Id: I9bdc9a4b7962f1a8dce77f4b213c8b9dc26e4b0f --- libs/utils/AssetManager.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libs/utils/AssetManager.cpp b/libs/utils/AssetManager.cpp index e41dd3968..22034c593 100644 --- a/libs/utils/AssetManager.cpp +++ b/libs/utils/AssetManager.cpp @@ -681,6 +681,9 @@ const ResTable* AssetManager::getResTable(bool required) const delete ass; } } + if (idmap != NULL) { + delete idmap; + } } if (required && !rt) LOGW("Unable to find resources file resources.arsc"); From 424ec5a8aa268e56520ed0c099109f400ebc40be Mon Sep 17 00:00:00 2001 From: Christopher Tate Date: Fri, 1 Apr 2011 14:43:32 -0700 Subject: [PATCH 315/541] Full local backup infrastructure This is the basic infrastructure for pulling a full(*) backup of the device's data over an adb(**) connection to the local device. The basic process consists of these interacting pieces: 1. The framework's BackupManagerService, which coordinates the collection of app data and routing to the destination. 2. A new framework-provided BackupAgent implementation called FullBackupAgent, which is instantiated in the target applications' processes in turn, and knows how to emit a datastream that contains all of the app's saved data files. 3. A new shell-level program called "bu" that is used to bridge from adb to the framework's Backup Manager. 4. adb itself, which now knows how to use 'bu' to kick off a backup operation and pull the resulting data stream to the desktop host. 5. A system-provided application that verifies with the user that an attempted backup/restore operation is in fact expected and to be allowed. The full agent implementation is not used during normal operation of the delta-based app-customized remote backup process. Instead it's used during user-confirmed *full* backup of applications and all their data to a local destination, e.g. via the adb connection. The output format is 'tar'. This makes it very easy for the end user to examine the resulting dataset, e.g. for purpose of extracting files for debug purposes; as well as making it easy to contemplate adding things like a direct gzip stage to the data pipeline during backup/restore. It also makes it convenient to construct and maintain synthetic backup datasets for testing purposes. Within the tar format, certain artificial conventions are used. All files are stored within top-level directories according to their semantic origin: apps/pkgname/a/ : Application .apk file itself apps/pkgname/obb/: The application's associated .obb containers apps/pkgname/f/ : The subtree rooted at the getFilesDir() location apps/pkgname/db/ : The subtree rooted at the getDatabasePath() parent apps/pkgname/sp/ : The subtree rooted at the getSharedPrefsFile() parent apps/pkgname/r/ : Files stored relative to the root of the app's file tree apps/pkgname/c/ : Reserved for the app's getCacheDir() tree; not stored. For each package, the first entry in the tar stream is a file called "_manifest", nominally rooted at apps/pkgname. This file contains some metadata about the package whose data is stored in the archive. The contents of shared storage can optionally be included in the tar stream. It is placed in the synthetic location: shared/... uid/gid are ignored; app uids are assigned at install time, and the app's data is handled from within its own execution environment, so will automatically have the app's correct uid. Forward-locked .apk files are never backed up. System-partition .apk files are not backed up unless they have been overridden by a post-factory upgrade, in which case the current .apk *is* backed up -- i.e. the .apk that matches the on-disk data. The manifest preceding each application's portion of the tar stream provides version numbers and signature blocks for version checking, as well as an indication of whether the restore logic should expect to install the .apk before extracting the data. System packages can designate their own full backup agents. This is to manage things like the settings provider which (a) cannot be shut down on the fly in order to do a clean snapshot of their file trees, and (b) manage data that is not only irrelevant but actively hostile to non-identical devices -- CDMA telephony settings would seriously mess up a GSM device if emplaced there blind, for example. When a full backup or restore is initiated from adb, the system will present a confirmation UI that the user must explicitly respond to within a short [~ 30 seconds] timeout. This is to avoid the possibility of malicious desktop-side software secretly grabbing a copy of all the user's data for nefarious purposes. (*) The backup is not strictly a full mirror. In particular, the settings database is not cloned; it is handled the same way that it is in cloud backup/restore. This is because some settings are actively destructive if cloned onto a different (or especially a different-model) device: telephony settings and AndroidID are good examples of this. (**) On the framework side it doesn't care that it's adb; it just sends the tar stream to a file descriptor. This can easily be retargeted around whatever transport we might decide to use in the future. KNOWN ISSUES: * the security UI is desperately ugly; no proper designs have yet been done for it * restore is not yet implemented * shared storage backup is not yet implemented * symlinks aren't yet handled, though some infrastructure for dealing with them has been put in place. Change-Id: Ia8347611e23b398af36ea22c36dff0a276b1ce91 --- include/utils/BackupHelpers.h | 13 ++- libs/utils/BackupData.cpp | 16 ++- libs/utils/BackupHelpers.cpp | 178 ++++++++++++++++++++++++++++++++++ 3 files changed, 204 insertions(+), 3 deletions(-) diff --git a/include/utils/BackupHelpers.h b/include/utils/BackupHelpers.h index b1f504512..1bb04a712 100644 --- a/include/utils/BackupHelpers.h +++ b/include/utils/BackupHelpers.h @@ -70,6 +70,14 @@ public: ~BackupDataWriter(); status_t WriteEntityHeader(const String8& key, size_t dataSize); + + /* Note: WriteEntityData will write arbitrary data into the file without + * validation or a previously-supplied header. The full backup implementation + * uses it this way to generate a controlled binary stream that is not + * entity-structured. If the implementation here is changed, either this + * use case must remain valid, or the full backup implementation should be + * adjusted to use some other appropriate mechanism. + */ status_t WriteEntityData(const void* data, size_t size); void SetKeyPrefix(const String8& keyPrefix); @@ -103,7 +111,7 @@ public: bool HasEntities(); status_t ReadEntityHeader(String8* key, size_t* dataSize); - status_t SkipEntityData(); // must be called with the pointer at the begining of the data. + status_t SkipEntityData(); // must be called with the pointer at the beginning of the data. ssize_t ReadEntityData(void* data, size_t size); private: @@ -126,6 +134,9 @@ private: int back_up_files(int oldSnapshotFD, BackupDataWriter* dataStream, int newSnapshotFD, char const* const* files, char const* const *keys, int fileCount); +int write_tarfile(const String8& packageName, const String8& domain, + const String8& rootPath, const String8& filePath, BackupDataWriter* outputStream); + class RestoreHelperBase { public: diff --git a/libs/utils/BackupData.cpp b/libs/utils/BackupData.cpp index adb317499..f963058fa 100644 --- a/libs/utils/BackupData.cpp +++ b/libs/utils/BackupData.cpp @@ -20,12 +20,15 @@ #include #include +#include #include #include namespace android { +static const bool DEBUG = false; + /* * File Format (v1): * @@ -75,6 +78,7 @@ BackupDataWriter::write_padding_for(int n) paddingSize = padding_extra(n); if (paddingSize > 0) { uint32_t padding = 0xbcbcbcbc; + if (DEBUG) LOGI("writing %d padding bytes for %d", paddingSize, n); amt = write(m_fd, &padding, paddingSize); if (amt != paddingSize) { m_status = errno; @@ -107,8 +111,8 @@ BackupDataWriter::WriteEntityHeader(const String8& key, size_t dataSize) } else { k = key; } - if (false) { - LOGD("Writing entity: prefix='%s' key='%s' dataSize=%d", m_keyPrefix.string(), key.string(), + if (DEBUG) { + LOGD("Writing header: prefix='%s' key='%s' dataSize=%d", m_keyPrefix.string(), key.string(), dataSize); } @@ -121,6 +125,7 @@ BackupDataWriter::WriteEntityHeader(const String8& key, size_t dataSize) header.keyLen = tolel(keyLen); header.dataSize = tolel(dataSize); + if (DEBUG) LOGI("writing entity header, %d bytes", sizeof(entity_header_v1)); amt = write(m_fd, &header, sizeof(entity_header_v1)); if (amt != sizeof(entity_header_v1)) { m_status = errno; @@ -128,6 +133,7 @@ BackupDataWriter::WriteEntityHeader(const String8& key, size_t dataSize) } m_pos += amt; + if (DEBUG) LOGI("writing entity header key, %d bytes", keyLen+1); amt = write(m_fd, k.string(), keyLen+1); if (amt != keyLen+1) { m_status = errno; @@ -145,7 +151,12 @@ BackupDataWriter::WriteEntityHeader(const String8& key, size_t dataSize) status_t BackupDataWriter::WriteEntityData(const void* data, size_t size) { + if (DEBUG) LOGD("Writing data: size=%lu", (unsigned long) size); + if (m_status != NO_ERROR) { + if (DEBUG) { + LOGD("Not writing data - stream in error state %d (%s)", m_status, strerror(m_status)); + } return m_status; } @@ -155,6 +166,7 @@ BackupDataWriter::WriteEntityData(const void* data, size_t size) ssize_t amt = write(m_fd, data, size); if (amt != (ssize_t)size) { m_status = errno; + if (DEBUG) LOGD("write returned error %d (%s)", m_status, strerror(m_status)); return m_status; } m_pos += amt; diff --git a/libs/utils/BackupHelpers.cpp b/libs/utils/BackupHelpers.cpp index 4ad9b5179..ad4a30877 100644 --- a/libs/utils/BackupHelpers.cpp +++ b/libs/utils/BackupHelpers.cpp @@ -442,6 +442,184 @@ back_up_files(int oldSnapshotFD, BackupDataWriter* dataStream, int newSnapshotFD return 0; } +// Utility function, equivalent to stpcpy(): perform a strcpy, but instead of +// returning the initial dest, return a pointer to the trailing NUL. +static char* strcpy_ptr(char* dest, const char* str) { + if (dest && str) { + while ((*dest = *str) != 0) { + dest++; + str++; + } + } + return dest; +} + +int write_tarfile(const String8& packageName, const String8& domain, + const String8& rootpath, const String8& filepath, BackupDataWriter* writer) +{ + // In the output stream everything is stored relative to the root + const char* relstart = filepath.string() + rootpath.length(); + if (*relstart == '/') relstart++; // won't be true when path == rootpath + String8 relpath(relstart); + + // Too long a name for the ustar format? + // "apps/" + packagename + '/' + domainpath < 155 chars + // relpath < 100 chars + if ((5 + packageName.length() + 1 + domain.length() >= 155) || (relpath.length() >= 100)) { + LOGE("Filename [%s] too long, skipping", relpath.string()); + return -1; + } + + int err = 0; + struct stat64 s; + if (lstat64(filepath.string(), &s) != 0) { + err = errno; + LOGE("Error %d (%s) from lstat64(%s)", err, strerror(err), filepath.string()); + return err; + } + + const int isdir = S_ISDIR(s.st_mode); + + // !!! TODO: use mmap when possible to avoid churning the buffer cache + // !!! TODO: this will break with symlinks; need to use readlink(2) + int fd = open(filepath.string(), O_RDONLY); + if (fd < 0) { + err = errno; + LOGE("Error %d (%s) from open(%s)", err, strerror(err), filepath.string()); + return err; + } + + // read/write up to this much at a time. + const size_t BUFSIZE = 32 * 1024; + + char* buf = new char[BUFSIZE]; + if (buf == NULL) { + LOGE("Out of mem allocating transfer buffer"); + err = ENOMEM; + goto done; + } + + // Good to go -- first construct the standard tar header at the start of the buffer + memset(buf, 0, 512); // tar header is 512 bytes + + // Magic fields for the ustar file format + strcat(buf + 257, "ustar"); + strcat(buf + 263, "00"); + + { + // Prefix and main relative path. Path lengths have been preflighted. + + // [ 345 : 155 ] filename path prefix [ustar] + // + // packagename and domain can each be empty. + char* cp = buf + 345; + if (packageName.length() > 0) { + // it's an app; so prefix with "apps/packagename/" + cp = strcpy_ptr(cp, "apps/"); + cp = strcpy_ptr(cp, packageName.string()); + } + + if (domain.length() > 0) { + // only need a / if there was a package name + if (packageName.length() > 0) *cp++ = '/'; + cp = strcpy_ptr(cp, domain.string()); + } + + // [ 0 : 100 ]; file name/path + strncpy(buf, relpath.string(), 100); + + LOGI(" Name: %s/%s", buf + 345, buf); + } + + // [ 100 : 8 ] file mode + snprintf(buf + 100, 8, "0%o", s.st_mode); + + // [ 108 : 8 ] uid -- ignored in Android format; uids are remapped at restore time + // [ 116 : 8 ] gid -- ignored in Android format + snprintf(buf + 108, 8, "0%lo", s.st_uid); + snprintf(buf + 116, 8, "0%lo", s.st_gid); + + // [ 124 : 12 ] file size in bytes + snprintf(buf + 124, 12, "0%llo", s.st_size); + + // [ 136 : 12 ] last mod time as a UTC time_t + snprintf(buf + 136, 12, "%0lo", s.st_mtime); + + // [ 148 : 8 ] checksum -- to be calculated with this field as space chars + memset(buf + 148, ' ', 8); + + // [ 156 : 1 ] link/file type + uint8_t type; + if (isdir) { + type = '5'; // tar magic: '5' == directory + } else if (S_ISREG(s.st_mode)) { + type = '0'; // tar magic: '0' == normal file + } else { + LOGW("Error: unknown file mode 0%o [%s]", s.st_mode, filepath.string()); + goto cleanup; + } + buf[156] = type; + + // [ 157 : 100 ] name of linked file [not implemented] + + // Now go back and calculate the header checksum + { + uint16_t sum = 0; + for (uint8_t* p = (uint8_t*) buf; p < ((uint8_t*)buf) + 512; p++) { + sum += *p; + } + + // Now write the real checksum value: + // [ 148 : 8 ] checksum: 6 octal digits [leading zeroes], NUL, SPC + sprintf(buf + 148, "%06o", sum); // the trailing space is already in place + } + + // Write the 512-byte tar file header block to the output + writer->WriteEntityData(buf, 512); + + // Now write the file data itself, for real files. We honor tar's convention that + // only full 512-byte blocks are sent to write(). + if (!isdir) { + off64_t toWrite = s.st_size; + while (toWrite > 0) { + size_t toRead = (toWrite < BUFSIZE) ? toWrite : BUFSIZE; + ssize_t nRead = read(fd, buf, toRead); + if (nRead < 0) { + err = errno; + LOGE("Unable to read file [%s], err=%d (%s)", filepath.string(), + err, strerror(err)); + break; + } else if (nRead == 0) { + LOGE("EOF but expect %lld more bytes in [%s]", (long long) toWrite, + filepath.string()); + err = EIO; + break; + } + + // At EOF we might have a short block; NUL-pad that to a 512-byte multiple. This + // depends on the OS guarantee that for ordinary files, read() will never return + // less than the number of bytes requested. + ssize_t partial = (nRead+512) % 512; + if (partial > 0) { + ssize_t remainder = 512 - partial; + memset(buf + nRead, 0, remainder); + nRead += remainder; + } + writer->WriteEntityData(buf, nRead); + toWrite -= nRead; + } + } + +cleanup: + delete [] buf; +done: + close(fd); + return err; +} +// end tarfile + + + #define RESTORE_BUF_SIZE (8*1024) RestoreHelperBase::RestoreHelperBase() From fc5e703bce2a2998985ed487b168fe5477c9d360 Mon Sep 17 00:00:00 2001 From: Christopher Tate Date: Thu, 12 May 2011 17:47:12 -0700 Subject: [PATCH 316/541] Use pax extended tar format to support long filenames etc. 'tar' supports only 100-character paths; 'ustar' supports only 155+100 character prefix + paths; neither supports files larger than about 8 gigabytes. We now use the POSIX.1-2001 'pax' extended tar format for those files in the backup stream that are too large or have too-long paths for the 'ustar' format. Change-Id: I2f256823091deaec9b1ccea685d2344753c6cb67 --- libs/utils/BackupHelpers.cpp | 157 ++++++++++++++++++++++++++--------- 1 file changed, 116 insertions(+), 41 deletions(-) diff --git a/libs/utils/BackupHelpers.cpp b/libs/utils/BackupHelpers.cpp index ad4a30877..cfb013e7c 100644 --- a/libs/utils/BackupHelpers.cpp +++ b/libs/utils/BackupHelpers.cpp @@ -454,6 +454,20 @@ static char* strcpy_ptr(char* dest, const char* str) { return dest; } +static void calc_tar_checksum(char* buf) { + // [ 148 : 8 ] checksum -- to be calculated with this field as space chars + memset(buf + 148, ' ', 8); + + uint16_t sum = 0; + for (uint8_t* p = (uint8_t*) buf; p < ((uint8_t*)buf) + 512; p++) { + sum += *p; + } + + // Now write the real checksum value: + // [ 148 : 8 ] checksum: 6 octal digits [leading zeroes], NUL, SPC + sprintf(buf + 148, "%06o", sum); // the trailing space is already in place +} + int write_tarfile(const String8& packageName, const String8& domain, const String8& rootpath, const String8& filepath, BackupDataWriter* writer) { @@ -462,12 +476,18 @@ int write_tarfile(const String8& packageName, const String8& domain, if (*relstart == '/') relstart++; // won't be true when path == rootpath String8 relpath(relstart); + // If relpath is empty, it means this is the top of one of the standard named + // domain directories, so we should just skip it + if (relpath.length() == 0) { + return 0; + } + // Too long a name for the ustar format? // "apps/" + packagename + '/' + domainpath < 155 chars // relpath < 100 chars + bool needExtended = false; if ((5 + packageName.length() + 1 + domain.length() >= 155) || (relpath.length() >= 100)) { - LOGE("Filename [%s] too long, skipping", relpath.string()); - return -1; + needExtended = true; } int err = 0; @@ -478,6 +498,9 @@ int write_tarfile(const String8& packageName, const String8& domain, return err; } + String8 fullname; // for pax later on + String8 prefix; + const int isdir = S_ISDIR(s.st_mode); // !!! TODO: use mmap when possible to avoid churning the buffer cache @@ -491,48 +514,29 @@ int write_tarfile(const String8& packageName, const String8& domain, // read/write up to this much at a time. const size_t BUFSIZE = 32 * 1024; - char* buf = new char[BUFSIZE]; + char* paxHeader = buf + 512; // use a different chunk of it as separate scratch + char* paxData = buf + 1024; + if (buf == NULL) { LOGE("Out of mem allocating transfer buffer"); err = ENOMEM; - goto done; + goto cleanup; } // Good to go -- first construct the standard tar header at the start of the buffer memset(buf, 0, 512); // tar header is 512 bytes + memset(paxHeader, 0, 512); // Magic fields for the ustar file format strcat(buf + 257, "ustar"); strcat(buf + 263, "00"); - { - // Prefix and main relative path. Path lengths have been preflighted. - - // [ 345 : 155 ] filename path prefix [ustar] - // - // packagename and domain can each be empty. - char* cp = buf + 345; - if (packageName.length() > 0) { - // it's an app; so prefix with "apps/packagename/" - cp = strcpy_ptr(cp, "apps/"); - cp = strcpy_ptr(cp, packageName.string()); - } - - if (domain.length() > 0) { - // only need a / if there was a package name - if (packageName.length() > 0) *cp++ = '/'; - cp = strcpy_ptr(cp, domain.string()); - } - - // [ 0 : 100 ]; file name/path - strncpy(buf, relpath.string(), 100); - - LOGI(" Name: %s/%s", buf + 345, buf); - } + // [ 265 : 32 ] user name, ignored on restore + // [ 297 : 32 ] group name, ignored on restore // [ 100 : 8 ] file mode - snprintf(buf + 100, 8, "0%o", s.st_mode); + snprintf(buf + 100, 8, "%06o ", s.st_mode & ~S_IFMT); // [ 108 : 8 ] uid -- ignored in Android format; uids are remapped at restore time // [ 116 : 8 ] gid -- ignored in Android format @@ -540,14 +544,15 @@ int write_tarfile(const String8& packageName, const String8& domain, snprintf(buf + 116, 8, "0%lo", s.st_gid); // [ 124 : 12 ] file size in bytes - snprintf(buf + 124, 12, "0%llo", s.st_size); + if (s.st_size > 077777777777LL) { + // very large files need a pax extended size header + needExtended = true; + } + snprintf(buf + 124, 12, "%011llo", (isdir) ? 0LL : s.st_size); // [ 136 : 12 ] last mod time as a UTC time_t snprintf(buf + 136, 12, "%0lo", s.st_mtime); - // [ 148 : 8 ] checksum -- to be calculated with this field as space chars - memset(buf + 148, ' ', 8); - // [ 156 : 1 ] link/file type uint8_t type; if (isdir) { @@ -562,19 +567,89 @@ int write_tarfile(const String8& packageName, const String8& domain, // [ 157 : 100 ] name of linked file [not implemented] - // Now go back and calculate the header checksum { - uint16_t sum = 0; - for (uint8_t* p = (uint8_t*) buf; p < ((uint8_t*)buf) + 512; p++) { - sum += *p; + // Prefix and main relative path. Path lengths have been preflighted. + if (packageName.length() > 0) { + prefix = "apps/"; + prefix += packageName; + } + if (domain.length() > 0) { + prefix.appendPath(domain); } - // Now write the real checksum value: - // [ 148 : 8 ] checksum: 6 octal digits [leading zeroes], NUL, SPC - sprintf(buf + 148, "%06o", sum); // the trailing space is already in place + // pax extended means we don't put in a prefix field, and put a different + // string in the basic name field. We can also construct the full path name + // out of the substrings we've now built. + fullname = prefix; + fullname.appendPath(relpath); + + // ustar: + // [ 0 : 100 ]; file name/path + // [ 345 : 155 ] filename path prefix + // We only use the prefix area if fullname won't fit in the path + if (fullname.length() > 100) { + strncpy(buf, relpath.string(), 100); + strncpy(buf + 345, prefix.string(), 155); + } else { + strncpy(buf, fullname.string(), 100); + } } - // Write the 512-byte tar file header block to the output + // [ 329 : 8 ] and [ 337 : 8 ] devmajor/devminor, not used + + LOGI(" Name: %s", fullname.string()); + + // If we're using a pax extended header, build & write that here; lengths are + // already preflighted + if (needExtended) { + // construct the pax extended header data block + memset(paxData, 0, BUFSIZE - (paxData - buf)); + char* p = paxData; + int len; + + // size header -- calc len in digits by actually rendering the number + // to a string - brute force but simple + len = sprintf(p, "%lld", s.st_size) + 8; // 8 for "1 size=" and final LF + if (len >= 10) len++; + + memset(p, 0, 512); + p += sprintf(p, "%d size=%lld\n", len, s.st_size); + + // fullname was generated above with the ustar paths + len = fullname.length() + 8; // 8 for "1 path=" and final LF + if (len >= 10) len++; + if (len >= 100) len++; + p += sprintf(p, "%d path=%s\n", len, fullname.string()); + + // Now we know how big the pax data is + int paxLen = p - paxData; + + // Now build the pax *header* templated on the ustar header + memcpy(paxHeader, buf, 512); + + String8 leaf = fullname.getPathLeaf(); + memset(paxHeader, 0, 100); // rewrite the name area + snprintf(paxHeader, 100, "PaxHeader/%s", leaf.string()); + memset(paxHeader + 345, 0, 155); // rewrite the prefix area + strncpy(paxHeader + 345, prefix.string(), 155); + + paxHeader[156] = 'x'; // mark it as a pax extended header + + // [ 124 : 12 ] size of pax extended header data + memset(paxHeader + 124, 0, 12); + snprintf(paxHeader + 124, 12, "%011o", p - paxData); + + // Checksum and write the pax block header + calc_tar_checksum(paxHeader); + writer->WriteEntityData(paxHeader, 512); + + // Now write the pax data itself + int paxblocks = (paxLen + 511) / 512; + writer->WriteEntityData(paxData, 512 * paxblocks); + } + + // Checksum and write the 512-byte ustar file header block to the output + calc_tar_checksum(buf); writer->WriteEntityData(buf, 512); // Now write the file data itself, for real files. We honor tar's convention that From cd122333c9c45b531004f33f0cc9e059a0cd9d1e Mon Sep 17 00:00:00 2001 From: Christopher Tate Date: Fri, 13 May 2011 15:38:02 -0700 Subject: [PATCH 317/541] Full backup tweaks * provide placeholder UI showing backup/restore start/stop/timeout * don't kill the progress UI in mid stream * tidy up the pax extended header data writing a little Change-Id: Ife0cb78e3facb541d8327f1d5ca5fe77faa6cbca --- libs/utils/BackupHelpers.cpp | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/libs/utils/BackupHelpers.cpp b/libs/utils/BackupHelpers.cpp index cfb013e7c..e15875f38 100644 --- a/libs/utils/BackupHelpers.cpp +++ b/libs/utils/BackupHelpers.cpp @@ -468,6 +468,19 @@ static void calc_tar_checksum(char* buf) { sprintf(buf + 148, "%06o", sum); // the trailing space is already in place } +// Returns number of bytes written +static int write_pax_header_entry(char* buf, const char* key, const char* value) { + // start with the size of "1 key=value\n" + int len = strlen(key) + strlen(value) + 4; + if (len > 9) len++; + if (len > 99) len++; + if (len > 999) len++; + // since PATH_MAX is 4096 we don't expect to have to generate any single + // header entry longer than 9999 characters + + return sprintf(buf, "%d %s=%s\n", len, key, value); +} + int write_tarfile(const String8& packageName, const String8& domain, const String8& rootpath, const String8& filepath, BackupDataWriter* writer) { @@ -525,8 +538,7 @@ int write_tarfile(const String8& packageName, const String8& domain, } // Good to go -- first construct the standard tar header at the start of the buffer - memset(buf, 0, 512); // tar header is 512 bytes - memset(paxHeader, 0, 512); + memset(buf, 0, BUFSIZE); // Magic fields for the ustar file format strcat(buf + 257, "ustar"); @@ -602,24 +614,20 @@ int write_tarfile(const String8& packageName, const String8& domain, // If we're using a pax extended header, build & write that here; lengths are // already preflighted if (needExtended) { + char sizeStr[32]; // big enough for a 64-bit unsigned value in decimal + char* p = paxData; + // construct the pax extended header data block memset(paxData, 0, BUFSIZE - (paxData - buf)); - char* p = paxData; int len; // size header -- calc len in digits by actually rendering the number // to a string - brute force but simple - len = sprintf(p, "%lld", s.st_size) + 8; // 8 for "1 size=" and final LF - if (len >= 10) len++; - - memset(p, 0, 512); - p += sprintf(p, "%d size=%lld\n", len, s.st_size); + snprintf(sizeStr, sizeof(sizeStr), "%lld", s.st_size); + p += write_pax_header_entry(p, "size", sizeStr); // fullname was generated above with the ustar paths - len = fullname.length() + 8; // 8 for "1 path=" and final LF - if (len >= 10) len++; - if (len >= 100) len++; - p += sprintf(p, "%d path=%s\n", len, fullname.string()); + p += write_pax_header_entry(p, "path", fullname.string()); // Now we know how big the pax data is int paxLen = p - paxData; From 1a20993c1000da3f008b34b7a5e6dbbd6fe291a6 Mon Sep 17 00:00:00 2001 From: Jamie Gennis Date: Thu, 28 Apr 2011 16:19:45 -0700 Subject: [PATCH 318/541] libutils: add a binary blob cache implementation. This change adds an implementation of a cache that stores key/value pairs of unstructured binary blobs. Change-Id: Idd01fdabedfa3aed6d359a6efb0592967af52651 --- include/utils/BlobCache.h | 181 ++++++++++++++++++++ libs/utils/Android.mk | 1 + libs/utils/BlobCache.cpp | 232 +++++++++++++++++++++++++ libs/utils/tests/Android.mk | 1 + libs/utils/tests/BlobCache_test.cpp | 257 ++++++++++++++++++++++++++++ 5 files changed, 672 insertions(+) create mode 100644 include/utils/BlobCache.h create mode 100644 libs/utils/BlobCache.cpp create mode 100644 libs/utils/tests/BlobCache_test.cpp diff --git a/include/utils/BlobCache.h b/include/utils/BlobCache.h new file mode 100644 index 000000000..8f76d72c1 --- /dev/null +++ b/include/utils/BlobCache.h @@ -0,0 +1,181 @@ +/* + ** Copyright 2011, 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_BLOB_CACHE_H +#define ANDROID_BLOB_CACHE_H + +#include + +#include +#include +#include + +namespace android { + +// A BlobCache is an in-memory cache for binary key/value pairs. All the public +// methods are thread-safe. +// +// The cache contents can be serialized to a file and reloaded in a subsequent +// execution of the program. This serialization is non-portable and should only +// be loaded by the device that generated it. +class BlobCache : public RefBase { +public: + + // Create an empty blob cache. The blob cache will cache key/value pairs + // with key and value sizes less than or equal to maxKeySize and + // maxValueSize, respectively. The total combined size of ALL cache entries + // (key sizes plus value sizes) will not exceed maxTotalSize. + BlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxTotalSize); + + // set inserts a new binary value into the cache and associates it with the + // given binary key. If the key or value are too large for the cache then + // the cache remains unchanged. This includes the case where a different + // value was previously associated with the given key - the old value will + // remain in the cache. If the given key and value are small enough to be + // put in the cache (based on the maxKeySize, maxValueSize, and maxTotalSize + // values specified to the BlobCache constructor), then the key/value pair + // will be in the cache after set returns. Note, however, that a subsequent + // call to set may evict old key/value pairs from the cache. + // + // Preconditions: + // key != NULL + // 0 < keySize + // value != NULL + // 0 < valueSize + void set(const void* key, size_t keySize, const void* value, + size_t valueSize); + + // The get function retrieves from the cache the binary value associated + // with a given binary key. If the key is present in the cache then the + // length of the binary value associated with that key is returned. If the + // value argument is non-NULL and the size of the cached value is less than + // valueSize bytes then the cached value is copied into the buffer pointed + // to by the value argument. If the key is not present in the cache then 0 + // is returned and the buffer pointed to by the value argument is not + // modified. + // + // Note that when calling get multiple times with the same key, the later + // calls may fail, returning 0, even if earlier calls succeeded. The return + // value must be checked for each call. + // + // Preconditions: + // key != NULL + // 0 < keySize + // 0 <= valueSize + size_t get(const void* key, size_t keySize, void* value, size_t valueSize); + +private: + // Copying is disallowed. + BlobCache(const BlobCache&); + void operator=(const BlobCache&); + + // clean evicts a randomly chosen set of entries from the cache such that + // the total size of all remaining entries is less than mMaxTotalSize/2. + void clean(); + + // isCleanable returns true if the cache is full enough for the clean method + // to have some effect, and false otherwise. + bool isCleanable() const; + + // A Blob is an immutable sized unstructured data blob. + class Blob : public RefBase { + public: + Blob(const void* data, size_t size, bool copyData); + ~Blob(); + + bool operator<(const Blob& rhs) const; + + const void* getData() const; + size_t getSize() const; + + private: + // Copying is not allowed. + Blob(const Blob&); + void operator=(const Blob&); + + // mData points to the buffer containing the blob data. + const void* mData; + + // mSize is the size of the blob data in bytes. + size_t mSize; + + // mOwnsData indicates whether or not this Blob object should free the + // memory pointed to by mData when the Blob gets destructed. + bool mOwnsData; + }; + + // A CacheEntry is a single key/value pair in the cache. + class CacheEntry { + public: + CacheEntry(); + CacheEntry(const sp& key, const sp& value); + CacheEntry(const CacheEntry& ce); + + bool operator<(const CacheEntry& rhs) const; + const CacheEntry& operator=(const CacheEntry&); + + sp getKey() const; + sp getValue() const; + + void setValue(const sp& value); + + private: + + // mKey is the key that identifies the cache entry. + sp mKey; + + // mValue is the cached data associated with the key. + sp mValue; + }; + + // mMaxKeySize is the maximum key size that will be cached. Calls to + // BlobCache::set with a keySize parameter larger than mMaxKeySize will + // simply not add the key/value pair to the cache. + const size_t mMaxKeySize; + + // mMaxValueSize is the maximum value size that will be cached. Calls to + // BlobCache::set with a valueSize parameter larger than mMaxValueSize will + // simply not add the key/value pair to the cache. + const size_t mMaxValueSize; + + // mMaxTotalSize is the maximum size that all cache entries can occupy. This + // includes space for both keys and values. When a call to BlobCache::set + // would otherwise cause this limit to be exceeded, either the key/value + // pair passed to BlobCache::set will not be cached or other cache entries + // will be evicted from the cache to make room for the new entry. + const size_t mMaxTotalSize; + + // mTotalSize is the total combined size of all keys and values currently in + // the cache. + size_t mTotalSize; + + // mRandState is the pseudo-random number generator state. It is passed to + // nrand48 to generate random numbers when needed. It must be protected by + // mMutex. + unsigned short mRandState[3]; + + // mCacheEntries stores all the cache entries that are resident in memory. + // Cache entries are added to it by the 'set' method. + SortedVector mCacheEntries; + + // mMutex is used to synchronize access to all member variables. It must be + // locked any time the member variables are written or read. + Mutex mMutex; +}; + +} + +#endif // ANDROID_BLOB_CACHE_H diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk index e8d40ba08..093189c5c 100644 --- a/libs/utils/Android.mk +++ b/libs/utils/Android.mk @@ -21,6 +21,7 @@ commonSources:= \ Asset.cpp \ AssetDir.cpp \ AssetManager.cpp \ + BlobCache.cpp \ BufferedTextOutput.cpp \ CallStack.cpp \ Debug.cpp \ diff --git a/libs/utils/BlobCache.cpp b/libs/utils/BlobCache.cpp new file mode 100644 index 000000000..1298fa733 --- /dev/null +++ b/libs/utils/BlobCache.cpp @@ -0,0 +1,232 @@ +/* + ** Copyright 2011, 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 "BlobCache" +//#define LOG_NDEBUG 0 + +#include +#include + +#include +#include + +namespace android { + +BlobCache::BlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxTotalSize): + mMaxKeySize(maxKeySize), + mMaxValueSize(maxValueSize), + mMaxTotalSize(maxTotalSize), + mTotalSize(0) { + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + mRandState[0] = (now >> 0) & 0xFFFF; + mRandState[1] = (now >> 16) & 0xFFFF; + mRandState[2] = (now >> 32) & 0xFFFF; + LOGV("initializing random seed using %lld", now); +} + +void BlobCache::set(const void* key, size_t keySize, const void* value, + size_t valueSize) { + if (mMaxKeySize < keySize) { + LOGV("set: not caching because the key is too large: %d (limit: %d)", + keySize, mMaxKeySize); + return; + } + if (mMaxValueSize < valueSize) { + LOGV("set: not caching because the value is too large: %d (limit: %d)", + valueSize, mMaxValueSize); + return; + } + if (mMaxTotalSize < keySize + valueSize) { + LOGV("set: not caching because the combined key/value size is too " + "large: %d (limit: %d)", keySize + valueSize, mMaxTotalSize); + return; + } + if (keySize == 0) { + LOGW("set: not caching because keySize is 0"); + return; + } + if (valueSize <= 0) { + LOGW("set: not caching because valueSize is 0"); + return; + } + + Mutex::Autolock lock(mMutex); + sp dummyKey(new Blob(key, keySize, false)); + CacheEntry dummyEntry(dummyKey, NULL); + + while (true) { + + ssize_t index = mCacheEntries.indexOf(dummyEntry); + if (index < 0) { + // Create a new cache entry. + sp keyBlob(new Blob(key, keySize, true)); + sp valueBlob(new Blob(value, valueSize, true)); + size_t newTotalSize = mTotalSize + keySize + valueSize; + if (mMaxTotalSize < newTotalSize) { + if (isCleanable()) { + // Clean the cache and try again. + clean(); + continue; + } else { + LOGV("set: not caching new key/value pair because the " + "total cache size limit would be exceeded: %d " + "(limit: %d)", + keySize + valueSize, mMaxTotalSize); + break; + } + } + mCacheEntries.add(CacheEntry(keyBlob, valueBlob)); + mTotalSize = newTotalSize; + LOGV("set: created new cache entry with %d byte key and %d byte value", + keySize, valueSize); + } else { + // Update the existing cache entry. + sp valueBlob(new Blob(value, valueSize, true)); + sp oldValueBlob(mCacheEntries[index].getValue()); + size_t newTotalSize = mTotalSize + valueSize - oldValueBlob->getSize(); + if (mMaxTotalSize < newTotalSize) { + if (isCleanable()) { + // Clean the cache and try again. + clean(); + continue; + } else { + LOGV("set: not caching new value because the total cache " + "size limit would be exceeded: %d (limit: %d)", + keySize + valueSize, mMaxTotalSize); + break; + } + } + mCacheEntries.editItemAt(index).setValue(valueBlob); + mTotalSize = newTotalSize; + LOGV("set: updated existing cache entry with %d byte key and %d byte " + "value", keySize, valueSize); + } + break; + } +} + +size_t BlobCache::get(const void* key, size_t keySize, void* value, + size_t valueSize) { + if (mMaxKeySize < keySize) { + LOGV("get: not searching because the key is too large: %d (limit %d)", + keySize, mMaxKeySize); + return 0; + } + Mutex::Autolock lock(mMutex); + sp dummyKey(new Blob(key, keySize, false)); + CacheEntry dummyEntry(dummyKey, NULL); + ssize_t index = mCacheEntries.indexOf(dummyEntry); + if (index < 0) { + LOGV("get: no cache entry found for key of size %d", keySize); + return 0; + } + + // The key was found. Return the value if the caller's buffer is large + // enough. + sp valueBlob(mCacheEntries[index].getValue()); + size_t valueBlobSize = valueBlob->getSize(); + if (valueBlobSize <= valueSize) { + LOGV("get: copying %d bytes to caller's buffer", valueBlobSize); + memcpy(value, valueBlob->getData(), valueBlobSize); + } else { + LOGV("get: caller's buffer is too small for value: %d (needs %d)", + valueSize, valueBlobSize); + } + return valueBlobSize; +} + +void BlobCache::clean() { + // Remove a random cache entry until the total cache size gets below half + // the maximum total cache size. + while (mTotalSize > mMaxTotalSize / 2) { + size_t i = size_t(nrand48(mRandState) % (mCacheEntries.size())); + const CacheEntry& entry(mCacheEntries[i]); + mTotalSize -= entry.getKey()->getSize() + entry.getValue()->getSize(); + mCacheEntries.removeAt(i); + } +} + +bool BlobCache::isCleanable() const { + return mTotalSize > mMaxTotalSize / 2; +} + +BlobCache::Blob::Blob(const void* data, size_t size, bool copyData): + mData(copyData ? malloc(size) : data), + mSize(size), + mOwnsData(copyData) { + if (copyData) { + memcpy(const_cast(mData), data, size); + } +} + +BlobCache::Blob::~Blob() { + if (mOwnsData) { + free(const_cast(mData)); + } +} + +bool BlobCache::Blob::operator<(const Blob& rhs) const { + if (mSize == rhs.mSize) { + return memcmp(mData, rhs.mData, mSize) < 0; + } else { + return mSize < rhs.mSize; + } +} + +const void* BlobCache::Blob::getData() const { + return mData; +} + +size_t BlobCache::Blob::getSize() const { + return mSize; +} + +BlobCache::CacheEntry::CacheEntry() { +} + +BlobCache::CacheEntry::CacheEntry(const sp& key, const sp& value): + mKey(key), + mValue(value) { +} + +BlobCache::CacheEntry::CacheEntry(const CacheEntry& ce): + mKey(ce.mKey), + mValue(ce.mValue) { +} + +bool BlobCache::CacheEntry::operator<(const CacheEntry& rhs) const { + return *mKey < *rhs.mKey; +} + +const BlobCache::CacheEntry& BlobCache::CacheEntry::operator=(const CacheEntry& rhs) { + mKey = rhs.mKey; + mValue = rhs.mValue; + return *this; +} + +sp BlobCache::CacheEntry::getKey() const { + return mKey; +} + +sp BlobCache::CacheEntry::getValue() const { + return mValue; +} + +void BlobCache::CacheEntry::setValue(const sp& value) { + mValue = value; +} + +} // namespace android diff --git a/libs/utils/tests/Android.mk b/libs/utils/tests/Android.mk index 72d48769a..87ad98eaa 100644 --- a/libs/utils/tests/Android.mk +++ b/libs/utils/tests/Android.mk @@ -6,6 +6,7 @@ ifneq ($(TARGET_SIMULATOR),true) # Build the unit tests. test_src_files := \ + BlobCache_test.cpp \ ObbFile_test.cpp \ Looper_test.cpp \ String8_test.cpp \ diff --git a/libs/utils/tests/BlobCache_test.cpp b/libs/utils/tests/BlobCache_test.cpp new file mode 100644 index 000000000..653ea5e91 --- /dev/null +++ b/libs/utils/tests/BlobCache_test.cpp @@ -0,0 +1,257 @@ +/* + ** Copyright 2011, 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 + +#include + +namespace android { + +class BlobCacheTest : public ::testing::Test { +protected: + enum { + MAX_KEY_SIZE = 6, + MAX_VALUE_SIZE = 8, + MAX_TOTAL_SIZE = 13, + }; + + virtual void SetUp() { + mBC = new BlobCache(MAX_KEY_SIZE, MAX_VALUE_SIZE, MAX_TOTAL_SIZE); + } + + virtual void TearDown() { + mBC.clear(); + } + + sp mBC; +}; + +TEST_F(BlobCacheTest, CacheSingleValueSucceeds) { + char buf[4] = { 0xee, 0xee, 0xee, 0xee }; + mBC->set("abcd", 4, "efgh", 4); + ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 4)); + ASSERT_EQ('e', buf[0]); + ASSERT_EQ('f', buf[1]); + ASSERT_EQ('g', buf[2]); + ASSERT_EQ('h', buf[3]); +} + +TEST_F(BlobCacheTest, CacheTwoValuesSucceeds) { + char buf[2] = { 0xee, 0xee }; + mBC->set("ab", 2, "cd", 2); + mBC->set("ef", 2, "gh", 2); + ASSERT_EQ(size_t(2), mBC->get("ab", 2, buf, 2)); + ASSERT_EQ('c', buf[0]); + ASSERT_EQ('d', buf[1]); + ASSERT_EQ(size_t(2), mBC->get("ef", 2, buf, 2)); + ASSERT_EQ('g', buf[0]); + ASSERT_EQ('h', buf[1]); +} + +TEST_F(BlobCacheTest, GetOnlyWritesInsideBounds) { + char buf[6] = { 0xee, 0xee, 0xee, 0xee, 0xee, 0xee }; + mBC->set("abcd", 4, "efgh", 4); + ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf+1, 4)); + ASSERT_EQ(0xee, buf[0]); + ASSERT_EQ('e', buf[1]); + ASSERT_EQ('f', buf[2]); + ASSERT_EQ('g', buf[3]); + ASSERT_EQ('h', buf[4]); + ASSERT_EQ(0xee, buf[5]); +} + +TEST_F(BlobCacheTest, GetOnlyWritesIfBufferIsLargeEnough) { + char buf[3] = { 0xee, 0xee, 0xee }; + mBC->set("abcd", 4, "efgh", 4); + ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 3)); + ASSERT_EQ(0xee, buf[0]); + ASSERT_EQ(0xee, buf[1]); + ASSERT_EQ(0xee, buf[2]); +} + +TEST_F(BlobCacheTest, GetDoesntAccessNullBuffer) { + mBC->set("abcd", 4, "efgh", 4); + ASSERT_EQ(size_t(4), mBC->get("abcd", 4, NULL, 0)); +} + +TEST_F(BlobCacheTest, MultipleSetsCacheLatestValue) { + char buf[4] = { 0xee, 0xee, 0xee, 0xee }; + mBC->set("abcd", 4, "efgh", 4); + mBC->set("abcd", 4, "ijkl", 4); + ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 4)); + ASSERT_EQ('i', buf[0]); + ASSERT_EQ('j', buf[1]); + ASSERT_EQ('k', buf[2]); + ASSERT_EQ('l', buf[3]); +} + +TEST_F(BlobCacheTest, SecondSetKeepsFirstValueIfTooLarge) { + char buf[MAX_VALUE_SIZE+1] = { 0xee, 0xee, 0xee, 0xee }; + mBC->set("abcd", 4, "efgh", 4); + mBC->set("abcd", 4, buf, MAX_VALUE_SIZE+1); + ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 4)); + ASSERT_EQ('e', buf[0]); + ASSERT_EQ('f', buf[1]); + ASSERT_EQ('g', buf[2]); + ASSERT_EQ('h', buf[3]); +} + +TEST_F(BlobCacheTest, DoesntCacheIfKeyIsTooBig) { + char key[MAX_KEY_SIZE+1]; + char buf[4] = { 0xee, 0xee, 0xee, 0xee }; + for (int i = 0; i < MAX_KEY_SIZE+1; i++) { + key[i] = 'a'; + } + mBC->set(key, MAX_KEY_SIZE+1, "bbbb", 4); + ASSERT_EQ(size_t(0), mBC->get(key, MAX_KEY_SIZE+1, buf, 4)); + ASSERT_EQ(0xee, buf[0]); + ASSERT_EQ(0xee, buf[1]); + ASSERT_EQ(0xee, buf[2]); + ASSERT_EQ(0xee, buf[3]); +} + +TEST_F(BlobCacheTest, DoesntCacheIfValueIsTooBig) { + char buf[MAX_VALUE_SIZE+1]; + for (int i = 0; i < MAX_VALUE_SIZE+1; i++) { + buf[i] = 'b'; + } + mBC->set("abcd", 4, buf, MAX_VALUE_SIZE+1); + for (int i = 0; i < MAX_VALUE_SIZE+1; i++) { + buf[i] = 0xee; + } + ASSERT_EQ(size_t(0), mBC->get("abcd", 4, buf, MAX_VALUE_SIZE+1)); + for (int i = 0; i < MAX_VALUE_SIZE+1; i++) { + SCOPED_TRACE(i); + ASSERT_EQ(0xee, buf[i]); + } +} + +TEST_F(BlobCacheTest, DoesntCacheIfKeyValuePairIsTooBig) { + // Check a testing assumptions + ASSERT_TRUE(MAX_TOTAL_SIZE < MAX_KEY_SIZE + MAX_VALUE_SIZE); + ASSERT_TRUE(MAX_KEY_SIZE < MAX_TOTAL_SIZE); + + enum { bufSize = MAX_TOTAL_SIZE - MAX_KEY_SIZE + 1 }; + + char key[MAX_KEY_SIZE]; + char buf[bufSize]; + for (int i = 0; i < MAX_KEY_SIZE; i++) { + key[i] = 'a'; + } + for (int i = 0; i < bufSize; i++) { + buf[i] = 'b'; + } + + mBC->set(key, MAX_KEY_SIZE, buf, MAX_VALUE_SIZE); + ASSERT_EQ(size_t(0), mBC->get(key, MAX_KEY_SIZE, NULL, 0)); +} + +TEST_F(BlobCacheTest, CacheMaxKeySizeSucceeds) { + char key[MAX_KEY_SIZE]; + char buf[4] = { 0xee, 0xee, 0xee, 0xee }; + for (int i = 0; i < MAX_KEY_SIZE; i++) { + key[i] = 'a'; + } + mBC->set(key, MAX_KEY_SIZE, "wxyz", 4); + ASSERT_EQ(size_t(4), mBC->get(key, MAX_KEY_SIZE, buf, 4)); + ASSERT_EQ('w', buf[0]); + ASSERT_EQ('x', buf[1]); + ASSERT_EQ('y', buf[2]); + ASSERT_EQ('z', buf[3]); +} + +TEST_F(BlobCacheTest, CacheMaxValueSizeSucceeds) { + char buf[MAX_VALUE_SIZE]; + for (int i = 0; i < MAX_VALUE_SIZE; i++) { + buf[i] = 'b'; + } + mBC->set("abcd", 4, buf, MAX_VALUE_SIZE); + for (int i = 0; i < MAX_VALUE_SIZE; i++) { + buf[i] = 0xee; + } + ASSERT_EQ(size_t(MAX_VALUE_SIZE), mBC->get("abcd", 4, buf, + MAX_VALUE_SIZE)); + for (int i = 0; i < MAX_VALUE_SIZE; i++) { + SCOPED_TRACE(i); + ASSERT_EQ('b', buf[i]); + } +} + +TEST_F(BlobCacheTest, CacheMaxKeyValuePairSizeSucceeds) { + // Check a testing assumption + ASSERT_TRUE(MAX_KEY_SIZE < MAX_TOTAL_SIZE); + + enum { bufSize = MAX_TOTAL_SIZE - MAX_KEY_SIZE }; + + char key[MAX_KEY_SIZE]; + char buf[bufSize]; + for (int i = 0; i < MAX_KEY_SIZE; i++) { + key[i] = 'a'; + } + for (int i = 0; i < bufSize; i++) { + buf[i] = 'b'; + } + + mBC->set(key, MAX_KEY_SIZE, buf, bufSize); + ASSERT_EQ(size_t(bufSize), mBC->get(key, MAX_KEY_SIZE, NULL, 0)); +} + +TEST_F(BlobCacheTest, CacheMinKeyAndValueSizeSucceeds) { + char buf[1] = { 0xee }; + mBC->set("x", 1, "y", 1); + ASSERT_EQ(size_t(1), mBC->get("x", 1, buf, 1)); + ASSERT_EQ('y', buf[0]); +} + +TEST_F(BlobCacheTest, CacheSizeDoesntExceedTotalLimit) { + for (int i = 0; i < 256; i++) { + uint8_t k = i; + mBC->set(&k, 1, "x", 1); + } + int numCached = 0; + for (int i = 0; i < 256; i++) { + uint8_t k = i; + if (mBC->get(&k, 1, NULL, 0) == 1) { + numCached++; + } + } + ASSERT_GE(MAX_TOTAL_SIZE / 2, numCached); +} + +TEST_F(BlobCacheTest, ExceedingTotalLimitHalvesCacheSize) { + // Fill up the entire cache with 1 char key/value pairs. + const int maxEntries = MAX_TOTAL_SIZE / 2; + for (int i = 0; i < maxEntries; i++) { + uint8_t k = i; + mBC->set(&k, 1, "x", 1); + } + // Insert one more entry, causing a cache overflow. + { + uint8_t k = maxEntries; + mBC->set(&k, 1, "x", 1); + } + // Count the number of entries in the cache. + int numCached = 0; + for (int i = 0; i < maxEntries+1; i++) { + uint8_t k = i; + if (mBC->get(&k, 1, NULL, 0) == 1) { + numCached++; + } + } + ASSERT_EQ(maxEntries/2 + 1, numCached); +} + +} // namespace android From b0ce850fac6b14a54990b98b3ac0b34bd2416653 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A5rten=20Kongstad?= Date: Thu, 19 May 2011 16:02:35 +0200 Subject: [PATCH 319/541] Continue idmap generation even if name lookup fails. In resources.arsc files, a resource is represented by a specification block and one or more value blocks. In rare cases, a resource name is also given a new resource ID, a specification block and no values blocks. This commit ensures idmap generation does not fail if such an entry is encountered. Change-Id: I32302a0b07a7a320b7eeb31886931be3bb7b7e9a --- libs/utils/ResourceTypes.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp index 784c9d284..c001ec962 100644 --- a/libs/utils/ResourceTypes.cpp +++ b/libs/utils/ResourceTypes.cpp @@ -4314,7 +4314,8 @@ status_t ResTable::createIdmap(const ResTable& overlay, uint32_t originalCrc, ui | (0x0000ffff & (entryIndex)); resource_name resName; if (!this->getResourceName(resID, &resName)) { - return UNKNOWN_ERROR; + LOGW("idmap: resource 0x%08x has spec but lacks values, skipping\n", resID); + continue; } const String16 overlayType(resName.type, resName.typeLen); From 23138b63df8788333f09a468cce36fd76d562577 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Thu, 19 May 2011 18:13:32 -0700 Subject: [PATCH 320/541] Add new "-swNNNdp" resource qualifier. Change-Id: I0101e88ca9d8d44138bdcaf571f24b0352f4f6ce --- include/utils/ResourceTypes.h | 126 +++++++++++++++++++++------------- libs/utils/ResourceTypes.cpp | 13 +++- 2 files changed, 90 insertions(+), 49 deletions(-) diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h index 173412e41..9e4e132a5 100644 --- a/include/utils/ResourceTypes.h +++ b/include/utils/ResourceTypes.h @@ -966,8 +966,7 @@ struct ResTable_config struct { uint8_t screenLayout; uint8_t uiMode; - uint8_t screenConfigPad1; - uint8_t screenConfigPad2; + uint16_t smallestScreenWidthDp; }; uint32_t screenConfig; }; @@ -1000,6 +999,7 @@ struct ResTable_config screenHeight = dtohs(screenHeight); sdkVersion = dtohs(sdkVersion); minorVersion = dtohs(minorVersion); + smallestScreenWidthDp = dtohs(smallestScreenWidthDp); screenWidthDp = dtohs(screenWidthDp); screenHeightDp = dtohs(screenHeightDp); } @@ -1013,6 +1013,7 @@ struct ResTable_config screenHeight = htods(screenHeight); sdkVersion = htods(sdkVersion); minorVersion = htods(minorVersion); + smallestScreenWidthDp = htods(smallestScreenWidthDp); screenWidthDp = htods(screenWidthDp); screenHeightDp = htods(screenHeightDp); } @@ -1034,6 +1035,8 @@ struct ResTable_config if (diff != 0) return diff; diff = (int32_t)(uiMode - o.uiMode); if (diff != 0) return diff; + diff = (int32_t)(smallestScreenWidthDp - o.smallestScreenWidthDp); + if (diff != 0) return diff; diff = (int32_t)(screenSizeDp - o.screenSizeDp); return (int)diff; } @@ -1052,6 +1055,7 @@ struct ResTable_config CONFIG_ORIENTATION = ACONFIGURATION_ORIENTATION, CONFIG_DENSITY = ACONFIGURATION_DENSITY, CONFIG_SCREEN_SIZE = ACONFIGURATION_SCREEN_SIZE, + CONFIG_SMALLEST_SCREEN_SIZE = ACONFIGURATION_SMALLEST_SCREEN_SIZE, CONFIG_VERSION = ACONFIGURATION_VERSION, CONFIG_SCREEN_LAYOUT = ACONFIGURATION_SCREEN_LAYOUT, CONFIG_UI_MODE = ACONFIGURATION_UI_MODE @@ -1075,6 +1079,7 @@ struct ResTable_config if (version != o.version) diffs |= CONFIG_VERSION; if (screenLayout != o.screenLayout) diffs |= CONFIG_SCREEN_LAYOUT; if (uiMode != o.uiMode) diffs |= CONFIG_UI_MODE; + if (smallestScreenWidthDp != o.smallestScreenWidthDp) diffs |= CONFIG_SMALLEST_SCREEN_SIZE; if (screenSizeDp != o.screenSizeDp) diffs |= CONFIG_SCREEN_SIZE; return diffs; } @@ -1109,14 +1114,10 @@ struct ResTable_config } } - if (screenLayout || o.screenLayout) { - if (((screenLayout^o.screenLayout) & MASK_SCREENSIZE) != 0) { - if (!(screenLayout & MASK_SCREENSIZE)) return false; - if (!(o.screenLayout & MASK_SCREENSIZE)) return true; - } - if (((screenLayout^o.screenLayout) & MASK_SCREENLONG) != 0) { - if (!(screenLayout & MASK_SCREENLONG)) return false; - if (!(o.screenLayout & MASK_SCREENLONG)) return true; + if (smallestScreenWidthDp || o.smallestScreenWidthDp) { + if (smallestScreenWidthDp != o.smallestScreenWidthDp) { + if (!smallestScreenWidthDp) return false; + if (!o.smallestScreenWidthDp) return true; } } @@ -1132,6 +1133,17 @@ struct ResTable_config } } + if (screenLayout || o.screenLayout) { + if (((screenLayout^o.screenLayout) & MASK_SCREENSIZE) != 0) { + if (!(screenLayout & MASK_SCREENSIZE)) return false; + if (!(o.screenLayout & MASK_SCREENSIZE)) return true; + } + if (((screenLayout^o.screenLayout) & MASK_SCREENLONG) != 0) { + if (!(screenLayout & MASK_SCREENLONG)) return false; + if (!(o.screenLayout & MASK_SCREENLONG)) return true; + } + } + if (orientation != o.orientation) { if (!orientation) return false; if (!o.orientation) return true; @@ -1238,6 +1250,37 @@ struct ResTable_config } } + if (smallestScreenWidthDp || o.smallestScreenWidthDp) { + // The configuration closest to the actual size is best. + // We assume that larger configs have already been filtered + // out at this point. That means we just want the largest one. + return smallestScreenWidthDp >= o.smallestScreenWidthDp; + } + + if (screenSizeDp || o.screenSizeDp) { + // "Better" is based on the sum of the difference between both + // width and height from the requested dimensions. We are + // assuming the invalid configs (with smaller dimens) have + // already been filtered. Note that if a particular dimension + // is unspecified, we will end up with a large value (the + // difference between 0 and the requested dimension), which is + // good since we will prefer a config that has specified a + // dimension value. + int myDelta = 0, otherDelta = 0; + if (requested->screenWidthDp) { + myDelta += requested->screenWidthDp - screenWidthDp; + otherDelta += requested->screenWidthDp - o.screenWidthDp; + } + if (requested->screenHeightDp) { + myDelta += requested->screenHeightDp - screenHeightDp; + otherDelta += requested->screenHeightDp - o.screenHeightDp; + } + //LOGI("Comparing this %dx%d to other %dx%d in %dx%d: myDelta=%d otherDelta=%d", + // screenWidthDp, screenHeightDp, o.screenWidthDp, o.screenHeightDp, + // requested->screenWidthDp, requested->screenHeightDp, myDelta, otherDelta); + return (myDelta <= otherDelta); + } + if (screenLayout || o.screenLayout) { if (((screenLayout^o.screenLayout) & MASK_SCREENSIZE) != 0 && (requested->screenLayout & MASK_SCREENSIZE)) { @@ -1270,30 +1313,6 @@ struct ResTable_config } } - if (screenSizeDp || o.screenSizeDp) { - // Better is based on the sum of the difference between both - // width and height from the requested dimensions. We are - // assuming the invalid configs (with smaller dimens) have - // already been filtered. Note that if a particular dimension - // is unspecified, we will end up with a large value (the - // difference between 0 and the requested dimension), which is - // good since we will prefer a config that has specified a - // dimension value. - int myDelta = 0, otherDelta = 0; - if (requested->screenWidthDp) { - myDelta += requested->screenWidthDp - screenWidthDp; - otherDelta += requested->screenWidthDp - o.screenWidthDp; - } - if (requested->screenHeightDp) { - myDelta += requested->screenHeightDp - screenHeightDp; - otherDelta += requested->screenHeightDp - o.screenHeightDp; - } - //LOGI("Comparing this %dx%d to other %dx%d in %dx%d: myDelta=%d otherDelta=%d", - // screenWidthDp, screenHeightDp, o.screenWidthDp, o.screenHeightDp, - // requested->screenWidthDp, requested->screenHeightDp, myDelta, otherDelta); - return (myDelta <= otherDelta); - } - if ((orientation != o.orientation) && requested->orientation) { return (orientation); } @@ -1388,14 +1407,24 @@ struct ResTable_config } if (screenSize || o.screenSize) { - if ((screenWidth != o.screenWidth) && requested->screenWidth) { - return (screenWidth); + // "Better" is based on the sum of the difference between both + // width and height from the requested dimensions. We are + // assuming the invalid configs (with smaller sizes) have + // already been filtered. Note that if a particular dimension + // is unspecified, we will end up with a large value (the + // difference between 0 and the requested dimension), which is + // good since we will prefer a config that has specified a + // size value. + int myDelta = 0, otherDelta = 0; + if (requested->screenWidth) { + myDelta += requested->screenWidth - screenWidth; + otherDelta += requested->screenWidth - o.screenWidth; } - - if ((screenHeight != o.screenHeight) && - requested->screenHeight) { - return (screenHeight); + if (requested->screenHeight) { + myDelta += requested->screenHeight - screenHeight; + otherDelta += requested->screenHeight - o.screenHeight; } + return (myDelta <= otherDelta); } if (version || o.version) { @@ -1476,15 +1505,20 @@ struct ResTable_config && uiModeNight != setUiModeNight) { return false; } + + if (settings.smallestScreenWidthDp != 0 && smallestScreenWidthDp != 0 + && smallestScreenWidthDp > settings.smallestScreenWidthDp) { + return false; + } } if (screenSizeDp != 0) { if (settings.screenWidthDp != 0 && screenWidthDp != 0 - && screenWidthDp > settings.screenWidthDp) { + && screenWidthDp > settings.screenWidthDp) { //LOGI("Filtering out width %d in requested %d", screenWidthDp, settings.screenWidthDp); return false; } if (settings.screenHeightDp != 0 && screenHeightDp != 0 - && screenHeightDp > settings.screenHeightDp) { + && screenHeightDp > settings.screenHeightDp) { //LOGI("Filtering out height %d in requested %d", screenHeightDp, settings.screenHeightDp); return false; } @@ -1531,11 +1565,11 @@ struct ResTable_config } if (screenSize != 0) { if (settings.screenWidth != 0 && screenWidth != 0 - && screenWidth != settings.screenWidth) { + && screenWidth > settings.screenWidth) { return false; } if (settings.screenHeight != 0 && screenHeight != 0 - && screenHeight != settings.screenHeight) { + && screenHeight > settings.screenHeight) { return false; } } @@ -1568,13 +1602,13 @@ struct ResTable_config String8 toString() const { char buf[200]; sprintf(buf, "imsi=%d/%d lang=%c%c reg=%c%c orient=%d touch=%d dens=%d " - "kbd=%d nav=%d input=%d ssz=%dx%d %ddp x %ddp sz=%d long=%d " + "kbd=%d nav=%d input=%d ssz=%dx%d sw%ddp w%ddp h%ddp sz=%d long=%d " "ui=%d night=%d vers=%d.%d", mcc, mnc, language[0] ? language[0] : '-', language[1] ? language[1] : '-', country[0] ? country[0] : '-', country[1] ? country[1] : '-', orientation, touchscreen, density, keyboard, navigation, inputFlags, - screenWidth, screenHeight, screenWidthDp, screenHeightDp, + screenWidth, screenHeight, smallestScreenWidthDp, screenWidthDp, screenHeightDp, screenLayout&MASK_SCREENSIZE, screenLayout&MASK_SCREENLONG, uiMode&MASK_UI_MODE_TYPE, uiMode&MASK_UI_MODE_NIGHT, sdkVersion, minorVersion); diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp index c001ec962..4655a7e1f 100644 --- a/libs/utils/ResourceTypes.cpp +++ b/libs/utils/ResourceTypes.cpp @@ -2588,7 +2588,7 @@ void ResTable::setParameters(const ResTable_config* params) { mLock.lock(); TABLE_GETENTRY(LOGI("Setting parameters: imsi:%d/%d lang:%c%c cnt:%c%c " - "orien:%d touch:%d density:%d key:%d inp:%d nav:%d sz:%dx%d %ddp x %ddp\n", + "orien:%d touch:%d density:%d key:%d inp:%d nav:%d sz:%dx%d sw%ddp w%ddp h%ddp\n", params->mcc, params->mnc, params->language[0] ? params->language[0] : '-', params->language[1] ? params->language[1] : '-', @@ -2602,6 +2602,7 @@ void ResTable::setParameters(const ResTable_config* params) params->navigation, params->screenWidth, params->screenHeight, + params->smallestScreenWidthDp, params->screenWidthDp, params->screenHeightDp)); mParams = *params; @@ -3927,7 +3928,7 @@ ssize_t ResTable::getEntry( TABLE_GETENTRY(LOGI("Match entry 0x%x in type 0x%x (sz 0x%x): imsi:%d/%d=%d/%d " "lang:%c%c=%c%c cnt:%c%c=%c%c orien:%d=%d touch:%d=%d " "density:%d=%d key:%d=%d inp:%d=%d nav:%d=%d w:%d=%d h:%d=%d " - "wdp:%d=%d hdp:%d=%d\n", + "swdp:%d=%d wdp:%d=%d hdp:%d=%d\n", entryIndex, typeIndex+1, dtohl(thisType->config.size), thisConfig.mcc, thisConfig.mnc, config ? config->mcc : 0, config ? config->mnc : 0, @@ -3955,6 +3956,8 @@ ssize_t ResTable::getEntry( config ? config->screenWidth : 0, thisConfig.screenHeight, config ? config->screenHeight : 0, + thisConfig.smallestScreenWidthDp, + config ? config->smallestScreenWidthDp : 0, thisConfig.screenWidthDp, config ? config->screenWidthDp : 0, thisConfig.screenHeightDp, @@ -4244,7 +4247,7 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg, thisConfig.copyFromDtoH(type->config); LOGI("Adding config to type %d: imsi:%d/%d lang:%c%c cnt:%c%c " "orien:%d touch:%d density:%d key:%d inp:%d nav:%d w:%d h:%d " - "wdp:%d hdp:%d\n", + "swdp:%d wdp:%d hdp:%d\n", type->id, thisConfig.mcc, thisConfig.mnc, thisConfig.language[0] ? thisConfig.language[0] : '-', @@ -4259,6 +4262,7 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg, thisConfig.navigation, thisConfig.screenWidth, thisConfig.screenHeight, + thisConfig.smallestScreenWidthDp, thisConfig.screenWidthDp, thisConfig.screenHeightDp)); t->configs.add(type); @@ -4754,6 +4758,9 @@ void ResTable::print(bool inclValues) const if (type->config.screenHeight != 0) { printf(" h=%d", dtohs(type->config.screenHeight)); } + if (type->config.smallestScreenWidthDp != 0) { + printf(" swdp=%d", dtohs(type->config.smallestScreenWidthDp)); + } if (type->config.screenWidthDp != 0) { printf(" wdp=%d", dtohs(type->config.screenWidthDp)); } From d781137601c05b623305592fbbe7b2d7d69087dd Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Thu, 19 May 2011 18:03:31 -0700 Subject: [PATCH 321/541] RefBase subclasses can now decide how they want to be destroyed. This adds a destroy() virtual on RefBase which sublasses can implement. destroy() is called in lieu of the destructor whenthe last strong ref goes away. --- include/utils/RefBase.h | 10 ++++++++-- libs/utils/RefBase.cpp | 6 +++++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/include/utils/RefBase.h b/include/utils/RefBase.h index f35508771..412622596 100644 --- a/include/utils/RefBase.h +++ b/include/utils/RefBase.h @@ -51,7 +51,6 @@ inline bool operator _op_ (const U* o) const { \ } // --------------------------------------------------------------------------- - class ReferenceMover; class ReferenceConverterBase { public: @@ -120,7 +119,14 @@ public: protected: RefBase(); virtual ~RefBase(); - + + // called when the last reference goes away. this is responsible for + // calling the destructor. The default implementation just does + // "delete this;". + // Make sure to never acquire a strong reference from this function. The + // same restrictions than for destructors apply. + virtual void destroy() const; + //! Flags for extendObjectLifetime() enum { OBJECT_LIFETIME_WEAK = 0x0001, diff --git a/libs/utils/RefBase.cpp b/libs/utils/RefBase.cpp index 2034486aa..9f55a7157 100644 --- a/libs/utils/RefBase.cpp +++ b/libs/utils/RefBase.cpp @@ -345,6 +345,10 @@ void RefBase::incStrong(const void* id) const const_cast(this)->onFirstRef(); } +void RefBase::destroy() const { + delete this; +} + void RefBase::decStrong(const void* id) const { weakref_impl* const refs = mRefs; @@ -357,7 +361,7 @@ void RefBase::decStrong(const void* id) const if (c == 1) { const_cast(this)->onLastStrongRef(id); if ((refs->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) { - delete this; + destroy(); } } refs->decWeak(id); From 2be848de6b7041da7f335a1053cbfbf841a5d1e3 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Thu, 19 May 2011 20:41:27 -0700 Subject: [PATCH 322/541] Call RefBase::destroy() when OBJECT_LIFETIME_* is not the default Change-Id: Ifb2069e095dba57b7d97e9f2d942fd85fa975f58 --- libs/utils/RefBase.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libs/utils/RefBase.cpp b/libs/utils/RefBase.cpp index 9f55a7157..dd0052ab9 100644 --- a/libs/utils/RefBase.cpp +++ b/libs/utils/RefBase.cpp @@ -419,7 +419,8 @@ void RefBase::weakref_type::decWeak(const void* id) if ((impl->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) { if (impl->mStrong == INITIAL_STRONG_VALUE) - delete impl->mBase; + if (impl->mBase) + impl->mBase->destroy(); else { // LOGV("Freeing refs %p of old RefBase %p\n", this, impl->mBase); delete impl; @@ -427,7 +428,8 @@ void RefBase::weakref_type::decWeak(const void* id) } else { impl->mBase->onLastWeakRef(id); if ((impl->mFlags&OBJECT_LIFETIME_FOREVER) != OBJECT_LIFETIME_FOREVER) { - delete impl->mBase; + if (impl->mBase) + impl->mBase->destroy(); } } } From 19797e22ac241fe78a714e9159830de1a73e9e37 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Fri, 20 May 2011 16:11:04 -0700 Subject: [PATCH 323/541] Add "television" mode. Change-Id: Ida1fdb61b036a8b489dbeda196fb4bc82e651b2b --- include/utils/ResourceTypes.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h index 9e4e132a5..2c7cf75c5 100644 --- a/include/utils/ResourceTypes.h +++ b/include/utils/ResourceTypes.h @@ -953,6 +953,7 @@ struct ResTable_config UI_MODE_TYPE_NORMAL = ACONFIGURATION_UI_MODE_TYPE_NORMAL, UI_MODE_TYPE_DESK = ACONFIGURATION_UI_MODE_TYPE_DESK, UI_MODE_TYPE_CAR = ACONFIGURATION_UI_MODE_TYPE_CAR, + UI_MODE_TYPE_TELEVISION = ACONFIGURATION_UI_MODE_TYPE_TELEVISION, // uiMode bits for the night switch. MASK_UI_MODE_NIGHT = 0x30, From 4a8bf7977e7d3dbcd394a4f41948695343e14a5a Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Fri, 27 May 2011 12:09:11 -0700 Subject: [PATCH 324/541] Small fixes and cleanup of resource config handling. Change-Id: I402c31b68f2b4825cb4c610a65ee8e1da471c7bb --- include/utils/ResourceTypes.h | 63 ++++++++++++----------------------- 1 file changed, 21 insertions(+), 42 deletions(-) diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h index 2c7cf75c5..7d3fc0611 100644 --- a/include/utils/ResourceTypes.h +++ b/include/utils/ResourceTypes.h @@ -1452,24 +1452,20 @@ struct ResTable_config // settings is the requested settings inline bool match(const ResTable_config& settings) const { if (imsi != 0) { - if ((settings.mcc != 0 && mcc != 0 - && mcc != settings.mcc) || - (settings.mcc == 0 && mcc != 0)) { + if (mcc != 0 && mcc != settings.mcc) { return false; } - if ((settings.mnc != 0 && mnc != 0 - && mnc != settings.mnc) || - (settings.mnc == 0 && mnc != 0)) { + if (mnc != 0 && mnc != settings.mnc) { return false; } } if (locale != 0) { - if (settings.language[0] != 0 && language[0] != 0 + if (language[0] != 0 && (language[0] != settings.language[0] || language[1] != settings.language[1])) { return false; } - if (settings.country[0] != 0 && country[0] != 0 + if (country[0] != 0 && (country[0] != settings.country[0] || country[1] != settings.country[1])) { return false; @@ -1480,66 +1476,56 @@ struct ResTable_config const int setScreenSize = settings.screenLayout&MASK_SCREENSIZE; // Any screen sizes for larger screens than the setting do not // match. - if ((setScreenSize != 0 && screenSize != 0 - && screenSize > setScreenSize) || - (setScreenSize == 0 && screenSize != 0)) { + if (screenSize != 0 && screenSize > setScreenSize) { return false; } const int screenLong = screenLayout&MASK_SCREENLONG; const int setScreenLong = settings.screenLayout&MASK_SCREENLONG; - if (setScreenLong != 0 && screenLong != 0 - && screenLong != setScreenLong) { + if (screenLong != 0 && screenLong != setScreenLong) { return false; } const int uiModeType = uiMode&MASK_UI_MODE_TYPE; const int setUiModeType = settings.uiMode&MASK_UI_MODE_TYPE; - if (setUiModeType != 0 && uiModeType != 0 - && uiModeType != setUiModeType) { + if (uiModeType != 0 && uiModeType != setUiModeType) { return false; } const int uiModeNight = uiMode&MASK_UI_MODE_NIGHT; const int setUiModeNight = settings.uiMode&MASK_UI_MODE_NIGHT; - if (setUiModeNight != 0 && uiModeNight != 0 - && uiModeNight != setUiModeNight) { + if (uiModeNight != 0 && uiModeNight != setUiModeNight) { return false; } - if (settings.smallestScreenWidthDp != 0 && smallestScreenWidthDp != 0 + if (smallestScreenWidthDp != 0 && smallestScreenWidthDp > settings.smallestScreenWidthDp) { return false; } } if (screenSizeDp != 0) { - if (settings.screenWidthDp != 0 && screenWidthDp != 0 - && screenWidthDp > settings.screenWidthDp) { + if (screenWidthDp != 0 && screenWidthDp > settings.screenWidthDp) { //LOGI("Filtering out width %d in requested %d", screenWidthDp, settings.screenWidthDp); return false; } - if (settings.screenHeightDp != 0 && screenHeightDp != 0 - && screenHeightDp > settings.screenHeightDp) { + if (screenHeightDp != 0 && screenHeightDp > settings.screenHeightDp) { //LOGI("Filtering out height %d in requested %d", screenHeightDp, settings.screenHeightDp); return false; } } if (screenType != 0) { - if (settings.orientation != 0 && orientation != 0 - && orientation != settings.orientation) { + if (orientation != 0 && orientation != settings.orientation) { return false; } // density always matches - we can scale it. See isBetterThan - if (settings.touchscreen != 0 && touchscreen != 0 - && touchscreen != settings.touchscreen) { + if (touchscreen != 0 && touchscreen != settings.touchscreen) { return false; } } if (input != 0) { const int keysHidden = inputFlags&MASK_KEYSHIDDEN; const int setKeysHidden = settings.inputFlags&MASK_KEYSHIDDEN; - if (setKeysHidden != 0 && keysHidden != 0 - && keysHidden != setKeysHidden) { + if (keysHidden != 0 && keysHidden != setKeysHidden) { // For compatibility, we count a request for KEYSHIDDEN_NO as also // matching the more recent KEYSHIDDEN_SOFT. Basically // KEYSHIDDEN_NO means there is some kind of keyboard available. @@ -1551,36 +1537,29 @@ struct ResTable_config } const int navHidden = inputFlags&MASK_NAVHIDDEN; const int setNavHidden = settings.inputFlags&MASK_NAVHIDDEN; - if (setNavHidden != 0 && navHidden != 0 - && navHidden != setNavHidden) { + if (navHidden != 0 && navHidden != setNavHidden) { return false; } - if (settings.keyboard != 0 && keyboard != 0 - && keyboard != settings.keyboard) { + if (keyboard != 0 && keyboard != settings.keyboard) { return false; } - if (settings.navigation != 0 && navigation != 0 - && navigation != settings.navigation) { + if (navigation != 0 && navigation != settings.navigation) { return false; } } if (screenSize != 0) { - if (settings.screenWidth != 0 && screenWidth != 0 - && screenWidth > settings.screenWidth) { + if (screenWidth != 0 && screenWidth > settings.screenWidth) { return false; } - if (settings.screenHeight != 0 && screenHeight != 0 - && screenHeight > settings.screenHeight) { + if (screenHeight != 0 && screenHeight > settings.screenHeight) { return false; } } if (version != 0) { - if (settings.sdkVersion != 0 && sdkVersion != 0 - && sdkVersion > settings.sdkVersion) { + if (sdkVersion != 0 && sdkVersion > settings.sdkVersion) { return false; } - if (settings.minorVersion != 0 && minorVersion != 0 - && minorVersion != settings.minorVersion) { + if (minorVersion != 0 && minorVersion != settings.minorVersion) { return false; } } From 2d57c038725fb59587600e0b74e1c9b4ad01c4ab Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Fri, 27 May 2011 13:40:26 -0700 Subject: [PATCH 325/541] Add "tv" density for 720p screens. Change-Id: I028969b007f2fceea66947d77a2ae31ef1d1a630 --- include/utils/ResourceTypes.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h index 7d3fc0611..8a92cd6e1 100644 --- a/include/utils/ResourceTypes.h +++ b/include/utils/ResourceTypes.h @@ -841,6 +841,7 @@ struct ResTable_config DENSITY_DEFAULT = ACONFIGURATION_DENSITY_DEFAULT, DENSITY_LOW = ACONFIGURATION_DENSITY_LOW, DENSITY_MEDIUM = ACONFIGURATION_DENSITY_MEDIUM, + DENSITY_TV = ACONFIGURATION_DENSITY_TV, DENSITY_HIGH = ACONFIGURATION_DENSITY_HIGH, DENSITY_NONE = ACONFIGURATION_DENSITY_NONE }; From 8deb51e8e492219c72b5f143868958af2ad56459 Mon Sep 17 00:00:00 2001 From: Christopher Tate Date: Wed, 18 May 2011 16:28:19 -0700 Subject: [PATCH 326/541] Restore from a previous full backup's tarfile Usage: adb restore [tarfilename] Restores app data [and installs the apps if necessary from the backup file] captured in a previous invocation of 'adb backup'. The user must explicitly acknowledge the action on-device before it is allowed to proceed; this prevents any "invisible" pushes of content from the host to the device. Known issues: * The settings databases and wallpaper are saved/restored, but lots of other system state is not yet captured in the full backup. This means that for practical purposes this is usable for 3rd party apps at present but not for full-system cloning/imaging. Change-Id: I0c748b645845e7c9178e30bf142857861a64efd3 --- libs/utils/BackupHelpers.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/libs/utils/BackupHelpers.cpp b/libs/utils/BackupHelpers.cpp index e15875f38..f933199fe 100644 --- a/libs/utils/BackupHelpers.cpp +++ b/libs/utils/BackupHelpers.cpp @@ -503,6 +503,16 @@ int write_tarfile(const String8& packageName, const String8& domain, needExtended = true; } + // Non-7bit-clean path also means needing pax extended format + if (!needExtended) { + for (size_t i = 0; i < filepath.length(); i++) { + if ((filepath[i] & 0x80) != 0) { + needExtended = true; + break; + } + } + } + int err = 0; struct stat64 s; if (lstat64(filepath.string(), &s) != 0) { From 9ac51b0991a2a9f00f3b452eec805873781463cd Mon Sep 17 00:00:00 2001 From: Christopher Tate Date: Thu, 2 Jun 2011 15:08:13 -0700 Subject: [PATCH 327/541] Implement shared-storage full backup/restore Every available shared-storage volume is backed up, tagged with its ordinal in the set of mounted shared volumes. This is an approximation of "internal + the external card". This lets us restore things to the same volume [or "equivalent" volume, in the case of a cross-model restore] as they originated on. Also fixed a bug in the handling of files/dirs with spaces in their names. Change-Id: I380019da8d0bb5b3699bd7c11eeff621a88e78c3 --- libs/utils/BackupHelpers.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/utils/BackupHelpers.cpp b/libs/utils/BackupHelpers.cpp index f933199fe..b433fd3cf 100644 --- a/libs/utils/BackupHelpers.cpp +++ b/libs/utils/BackupHelpers.cpp @@ -503,10 +503,10 @@ int write_tarfile(const String8& packageName, const String8& domain, needExtended = true; } - // Non-7bit-clean path also means needing pax extended format + // Non-7bit-clean path or embedded spaces also mean needing pax extended format if (!needExtended) { for (size_t i = 0; i < filepath.length(); i++) { - if ((filepath[i] & 0x80) != 0) { + if ((filepath[i] & 0x80) != 0 || filepath[i] == ' ') { needExtended = true; break; } From 7c9f673ed619f0054dc6de45ac9c9396112c30d4 Mon Sep 17 00:00:00 2001 From: Christopher Tate Date: Tue, 7 Jun 2011 13:17:17 -0700 Subject: [PATCH 328/541] Fix embedded spaces in tar stream EVEN HARDER Change-Id: I97ac586ff3541a05d73e1e53f680517c15e6c662 --- libs/utils/BackupHelpers.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/utils/BackupHelpers.cpp b/libs/utils/BackupHelpers.cpp index b433fd3cf..f933199fe 100644 --- a/libs/utils/BackupHelpers.cpp +++ b/libs/utils/BackupHelpers.cpp @@ -503,10 +503,10 @@ int write_tarfile(const String8& packageName, const String8& domain, needExtended = true; } - // Non-7bit-clean path or embedded spaces also mean needing pax extended format + // Non-7bit-clean path also means needing pax extended format if (!needExtended) { for (size_t i = 0; i < filepath.length(); i++) { - if ((filepath[i] & 0x80) != 0 || filepath[i] == ' ') { + if ((filepath[i] & 0x80) != 0) { needExtended = true; break; } From 4571143fd31f03a63e7d251b28bb58f2c8b584e0 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Wed, 8 Jun 2011 16:05:30 -0700 Subject: [PATCH 329/541] Fix a leak in RefBase (DO NOT MERGE) this bug was introduced recently. it caused RefBase's weakref_impl structure to be leaked for every RefBase object (about 20 bytes). Change-Id: Ia9b155fbfa643ef72cfb8129e96260a3b806a78c --- libs/utils/RefBase.cpp | 231 ++++++++++++++--------------------------- 1 file changed, 80 insertions(+), 151 deletions(-) diff --git a/libs/utils/RefBase.cpp b/libs/utils/RefBase.cpp index dd0052ab9..1c870cf78 100644 --- a/libs/utils/RefBase.cpp +++ b/libs/utils/RefBase.cpp @@ -20,9 +20,9 @@ #include #include +#include #include #include -#include #include #include @@ -34,7 +34,6 @@ // compile with refcounting debugging enabled #define DEBUG_REFS 0 -#define DEBUG_REFS_FATAL_SANITY_CHECKS 0 #define DEBUG_REFS_ENABLED_BY_DEFAULT 1 #define DEBUG_REFS_CALLSTACK_ENABLED 1 @@ -70,10 +69,8 @@ public: void addStrongRef(const void* /*id*/) { } void removeStrongRef(const void* /*id*/) { } - void renameStrongRefId(const void* /*old_id*/, const void* /*new_id*/) { } void addWeakRef(const void* /*id*/) { } void removeWeakRef(const void* /*id*/) { } - void renameWeakRefId(const void* /*old_id*/, const void* /*new_id*/) { } void printRefs() const { } void trackMe(bool, bool) { } @@ -89,91 +86,39 @@ public: , mTrackEnabled(!!DEBUG_REFS_ENABLED_BY_DEFAULT) , mRetain(false) { + //LOGI("NEW weakref_impl %p for RefBase %p", this, base); } ~weakref_impl() { - bool dumpStack = false; - if (!mRetain && mStrongRefs != NULL) { - dumpStack = true; -#if DEBUG_REFS_FATAL_SANITY_CHECKS - LOG_ALWAYS_FATAL("Strong references remain!"); -#else - LOGE("Strong references remain:"); -#endif - ref_entry* refs = mStrongRefs; - while (refs) { - char inc = refs->ref >= 0 ? '+' : '-'; - LOGD("\t%c ID %p (ref %d):", inc, refs->id, refs->ref); -#if DEBUG_REFS_CALLSTACK_ENABLED - refs->stack.dump(); -#endif; - refs = refs->next; - } - } - - if (!mRetain && mWeakRefs != NULL) { - dumpStack = true; -#if DEBUG_REFS_FATAL_SANITY_CHECKS - LOG_ALWAYS_FATAL("Weak references remain:"); -#else - LOGE("Weak references remain!"); -#endif - ref_entry* refs = mWeakRefs; - while (refs) { - char inc = refs->ref >= 0 ? '+' : '-'; - LOGD("\t%c ID %p (ref %d):", inc, refs->id, refs->ref); -#if DEBUG_REFS_CALLSTACK_ENABLED - refs->stack.dump(); -#endif; - refs = refs->next; - } - } - if (dumpStack) { - LOGE("above errors at:"); - CallStack stack; - stack.update(); - stack.dump(); - } + LOG_ALWAYS_FATAL_IF(!mRetain && mStrongRefs != NULL, "Strong references remain!"); + LOG_ALWAYS_FATAL_IF(!mRetain && mWeakRefs != NULL, "Weak references remain!"); } - void addStrongRef(const void* id) { - //LOGD_IF(mTrackEnabled, - // "addStrongRef: RefBase=%p, id=%p", mBase, id); + void addStrongRef(const void* id) + { addRef(&mStrongRefs, id, mStrong); } - void removeStrongRef(const void* id) { - //LOGD_IF(mTrackEnabled, - // "removeStrongRef: RefBase=%p, id=%p", mBase, id); - if (!mRetain) { + void removeStrongRef(const void* id) + { + if (!mRetain) removeRef(&mStrongRefs, id); - } else { + else addRef(&mStrongRefs, id, -mStrong); - } } - void renameStrongRefId(const void* old_id, const void* new_id) { - //LOGD_IF(mTrackEnabled, - // "renameStrongRefId: RefBase=%p, oid=%p, nid=%p", - // mBase, old_id, new_id); - renameRefsId(mStrongRefs, old_id, new_id); - } - - void addWeakRef(const void* id) { + void addWeakRef(const void* id) + { addRef(&mWeakRefs, id, mWeak); } - void removeWeakRef(const void* id) { - if (!mRetain) { + void removeWeakRef(const void* id) + { + if (!mRetain) removeRef(&mWeakRefs, id); - } else { + else addRef(&mWeakRefs, id, -mWeak); - } - } - - void renameWeakRefId(const void* old_id, const void* new_id) { - renameRefsId(mWeakRefs, old_id, new_id); } void trackMe(bool track, bool retain) @@ -187,7 +132,8 @@ public: String8 text; { - Mutex::Autolock _l(const_cast(this)->mMutex); + AutoMutex _l(const_cast(this)->mMutex); + char buf[128]; sprintf(buf, "Strong references on RefBase %p (weakref_type %p):\n", mBase, this); text.append(buf); @@ -226,7 +172,6 @@ private: { if (mTrackEnabled) { AutoMutex _l(mMutex); - ref_entry* ref = new ref_entry; // Reference count at the time of the snapshot, but before the // update. Positive value means we increment, negative--we @@ -236,6 +181,7 @@ private: #if DEBUG_REFS_CALLSTACK_ENABLED ref->stack.update(2); #endif + ref->next = *refs; *refs = ref; } @@ -246,52 +192,20 @@ private: if (mTrackEnabled) { AutoMutex _l(mMutex); - ref_entry* const head = *refs; - ref_entry* ref = head; + ref_entry* ref = *refs; while (ref != NULL) { if (ref->id == id) { *refs = ref->next; delete ref; return; } + refs = &ref->next; ref = *refs; } - -#if DEBUG_REFS_FATAL_SANITY_CHECKS - LOG_ALWAYS_FATAL("RefBase: removing id %p on RefBase %p" - "(weakref_type %p) that doesn't exist!", - id, mBase, this); -#endif - - LOGE("RefBase: removing id %p on RefBase %p" - "(weakref_type %p) that doesn't exist!", - id, mBase, this); - - ref = head; - while (ref) { - char inc = ref->ref >= 0 ? '+' : '-'; - LOGD("\t%c ID %p (ref %d):", inc, ref->id, ref->ref); - ref = ref->next; - } - - CallStack stack; - stack.update(); - stack.dump(); - } - } - - void renameRefsId(ref_entry* r, const void* old_id, const void* new_id) - { - if (mTrackEnabled) { - AutoMutex _l(mMutex); - ref_entry* ref = r; - while (ref != NULL) { - if (ref->id == old_id) { - ref->id = new_id; - } - ref = ref->next; - } + + LOG_ALWAYS_FATAL("RefBase: removing id %p on RefBase %p (weakref_type %p) that doesn't exist!", + id, mBase, this); } } @@ -321,6 +235,44 @@ private: // on removeref that match the address ones. bool mRetain; +#if 0 + void addRef(KeyedVector* refs, const void* id) + { + AutoMutex _l(mMutex); + ssize_t i = refs->indexOfKey(id); + if (i >= 0) { + ++(refs->editValueAt(i)); + } else { + i = refs->add(id, 1); + } + } + + void removeRef(KeyedVector* refs, const void* id) + { + AutoMutex _l(mMutex); + ssize_t i = refs->indexOfKey(id); + LOG_ALWAYS_FATAL_IF(i < 0, "RefBase: removing id %p that doesn't exist!", id); + if (i >= 0) { + int32_t val = --(refs->editValueAt(i)); + if (val == 0) { + refs->removeItemsAt(i); + } + } + } + + void printRefs(const KeyedVector& refs) + { + const size_t N=refs.size(); + for (size_t i=0; i mStrongRefs; + KeyedVector mWeakRefs; +#endif + #endif }; @@ -329,6 +281,7 @@ private: void RefBase::incStrong(const void* id) const { weakref_impl* const refs = mRefs; + refs->addWeakRef(id); refs->incWeak(id); refs->addStrongRef(id); @@ -364,12 +317,14 @@ void RefBase::decStrong(const void* id) const destroy(); } } + refs->removeWeakRef(id); refs->decWeak(id); } void RefBase::forceIncStrong(const void* id) const { weakref_impl* const refs = mRefs; + refs->addWeakRef(id); refs->incWeak(id); refs->addStrongRef(id); @@ -418,18 +373,20 @@ void RefBase::weakref_type::decWeak(const void* id) if (c != 1) return; if ((impl->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) { - if (impl->mStrong == INITIAL_STRONG_VALUE) - if (impl->mBase) + if (impl->mStrong == INITIAL_STRONG_VALUE) { + if (impl->mBase) { impl->mBase->destroy(); - else { + } + } else { // LOGV("Freeing refs %p of old RefBase %p\n", this, impl->mBase); delete impl; } } else { impl->mBase->onLastWeakRef(id); if ((impl->mFlags&OBJECT_LIFETIME_FOREVER) != OBJECT_LIFETIME_FOREVER) { - if (impl->mBase) + if (impl->mBase) { impl->mBase->destroy(); + } } } } @@ -483,6 +440,7 @@ bool RefBase::weakref_type::attemptIncStrong(const void* id) } } + impl->addWeakRef(id); impl->addStrongRef(id); #if PRINT_REFS @@ -500,7 +458,7 @@ bool RefBase::weakref_type::attemptIncStrong(const void* id) bool RefBase::weakref_type::attemptIncWeak(const void* id) { weakref_impl* const impl = static_cast(this); - + int32_t curCount = impl->mWeak; LOG_ASSERT(curCount >= 0, "attemptIncWeak called on %p after underflow", this); @@ -530,7 +488,7 @@ void RefBase::weakref_type::printRefs() const void RefBase::weakref_type::trackMe(bool enable, bool retain) { - static_cast(this)->trackMe(enable, retain); + static_cast(this)->trackMe(enable, retain); } RefBase::weakref_type* RefBase::createWeak(const void* id) const @@ -547,12 +505,15 @@ RefBase::weakref_type* RefBase::getWeakRefs() const RefBase::RefBase() : mRefs(new weakref_impl(this)) { +// LOGV("Creating refs %p with RefBase %p\n", mRefs, this); } RefBase::~RefBase() { - if (mRefs->mWeak == 0) { - delete mRefs; + if ((mRefs->mFlags & OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_WEAK) { + if (mRefs->mWeak == 0) { + delete mRefs; + } } } @@ -577,37 +538,5 @@ bool RefBase::onIncStrongAttempted(uint32_t flags, const void* id) void RefBase::onLastWeakRef(const void* /*id*/) { } - -// --------------------------------------------------------------------------- - -void RefBase::moveReferences(void* dst, void const* src, size_t n, - const ReferenceConverterBase& caster) -{ -#if DEBUG_REFS - const size_t itemSize = caster.getReferenceTypeSize(); - for (size_t i=0 ; i(intptr_t(dst) + i*itemSize); - void const* s = reinterpret_cast(intptr_t(src) + i*itemSize); - RefBase* ref(reinterpret_cast(caster.getReferenceBase(d))); - ref->mRefs->renameStrongRefId(s, d); - ref->mRefs->renameWeakRefId(s, d); - } -#endif -} - -// --------------------------------------------------------------------------- - -TextOutput& printStrongPointer(TextOutput& to, const void* val) -{ - to << "sp<>(" << val << ")"; - return to; -} - -TextOutput& printWeakPointer(TextOutput& to, const void* val) -{ - to << "wp<>(" << val << ")"; - return to; -} - - + }; // namespace android From f90c1e290acc7e5b68fcf6bf1fd7c9ea5136dd89 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Tue, 7 Jun 2011 14:09:47 -0700 Subject: [PATCH 330/541] Work on issue #4518815: Compatibility mode introduces compatibility regression... ...for Market App iRunner There were a lot of serious issues with how we updated (or often didn't update) the display and resource state when switching compatibility mode in conjunction with restarting and updating application components. This addresses everything I could find. Unfortunately it does *not* fix this particular app. I am starting to think this is just an issue in the app. This change does fix a number of other problems I could repro, such as switching the compatibility mode of an IME. Also a few changes here and there to get rid of $#*&^!! debug logs. Change-Id: Ib15572eac9ec93b4b9966ddcbbc830ce9dec1317 --- libs/utils/StreamingZipInflater.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/libs/utils/StreamingZipInflater.cpp b/libs/utils/StreamingZipInflater.cpp index 5a162cc49..00498bd1f 100644 --- a/libs/utils/StreamingZipInflater.cpp +++ b/libs/utils/StreamingZipInflater.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#define LOG_NDEBUG 1 +//#define LOG_NDEBUG 0 #define LOG_TAG "szipinf" #include @@ -77,7 +77,7 @@ StreamingZipInflater::~StreamingZipInflater() { } void StreamingZipInflater::initInflateState() { - LOGD("Initializing inflate state"); + LOGV("Initializing inflate state"); memset(&mInflateState, 0, sizeof(mInflateState)); mInflateState.zalloc = Z_NULL; @@ -152,13 +152,13 @@ ssize_t StreamingZipInflater::read(void* outBuf, size_t count) { mInflateState.avail_out = mOutBufSize; /* - LOGD("Inflating to outbuf: avail_in=%u avail_out=%u next_in=%p next_out=%p", + LOGV("Inflating to outbuf: avail_in=%u avail_out=%u next_in=%p next_out=%p", mInflateState.avail_in, mInflateState.avail_out, mInflateState.next_in, mInflateState.next_out); */ int result = Z_OK; if (mStreamNeedsInit) { - LOGD("Initializing zlib to inflate"); + LOGV("Initializing zlib to inflate"); result = inflateInit2(&mInflateState, -MAX_WBITS); mStreamNeedsInit = false; } @@ -192,7 +192,7 @@ int StreamingZipInflater::readNextChunk() { size_t toRead = min_of(mInBufSize, mInTotalSize - mInNextChunkOffset); if (toRead > 0) { ssize_t didRead = ::read(mFd, mInBuf, toRead); - //LOGD("Reading input chunk, size %08x didread %08x", toRead, didRead); + //LOGV("Reading input chunk, size %08x didread %08x", toRead, didRead); if (didRead < 0) { // TODO: error LOGE("Error reading asset data"); From 384f13a078cd6ac36b4c15be732036cdfb572ca8 Mon Sep 17 00:00:00 2001 From: Christopher Tate Date: Wed, 8 Jun 2011 20:09:31 -0700 Subject: [PATCH 331/541] Fix handling of directory entries Don't emit tar blocks for directories with an invalid nonzero size. Also, if such an entry is encountered during restore, don't actually attempt to treat it as valid and thus skip over the next actual tar entry. This patch also adds tracking of the data actually consumed during restore, and reports a total at the end of stream. Change-Id: I625173f76df3c007e899209101ff2b587841f184 --- libs/utils/BackupHelpers.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libs/utils/BackupHelpers.cpp b/libs/utils/BackupHelpers.cpp index f933199fe..87549fe97 100644 --- a/libs/utils/BackupHelpers.cpp +++ b/libs/utils/BackupHelpers.cpp @@ -525,6 +525,7 @@ int write_tarfile(const String8& packageName, const String8& domain, String8 prefix; const int isdir = S_ISDIR(s.st_mode); + if (isdir) s.st_size = 0; // directories get no actual data in the tar stream // !!! TODO: use mmap when possible to avoid churning the buffer cache // !!! TODO: this will break with symlinks; need to use readlink(2) From bf7a884f22299ad424e350c497248bf6cdcd6467 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Thu, 9 Jun 2011 11:29:08 -0700 Subject: [PATCH 332/541] Enforce public resource restriction on bag parents. Need to put some more styles in the SDK to avoid breaking apps. Also, welcome Android 3.2. Change-Id: Ia31d07c9b1b91ad868d8630437fdc1b5ae24f37d --- include/utils/ResourceTypes.h | 3 ++- libs/utils/ResourceTypes.cpp | 32 ++++++++++++++++++++++++++++++-- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h index 8a92cd6e1..612ff9346 100644 --- a/include/utils/ResourceTypes.h +++ b/include/utils/ResourceTypes.h @@ -1988,7 +1988,8 @@ public: String16* outName, const String16* defType = NULL, const String16* defPackage = NULL, - const char** outErrorMsg = NULL); + const char** outErrorMsg = NULL, + bool* outPublicOnly = NULL); static bool stringToInt(const char16_t* s, size_t len, Res_value* outValue); static bool stringToFloat(const char16_t* s, size_t len, Res_value* outValue); diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp index 4655a7e1f..6cf01c8d6 100644 --- a/libs/utils/ResourceTypes.cpp +++ b/libs/utils/ResourceTypes.cpp @@ -2663,6 +2663,9 @@ uint32_t ResTable::identifierForName(const char16_t* name, size_t nameLen, goto nope; } } + if (outTypeSpecFlags) { + *outTypeSpecFlags = ResTable_typeSpec::SPEC_PUBLIC; + } return m->id; nope: ; @@ -2677,6 +2680,9 @@ nope: index); return 0; } + if (outTypeSpecFlags) { + *outTypeSpecFlags = ResTable_typeSpec::SPEC_PUBLIC; + } return Res_MAKEARRAY(index); } } @@ -2687,6 +2693,8 @@ nope: return 0; } + bool fakePublic = false; + // Figure out the package and type we are looking in... const char16_t* packageEnd = NULL; @@ -2698,7 +2706,13 @@ nope: else if (*p == '/') typeEnd = p; p++; } - if (*name == '@') name++; + if (*name == '@') { + name++; + if (*name == '*') { + fakePublic = true; + name++; + } + } if (name >= nameEnd) { return 0; } @@ -2803,6 +2817,9 @@ nope: if (dtohl(entry->key.index) == (size_t)ei) { if (outTypeSpecFlags) { *outTypeSpecFlags = typeConfigs->typeSpecFlags[i]; + if (fakePublic) { + *outTypeSpecFlags |= ResTable_typeSpec::SPEC_PUBLIC; + } } return Res_MAKEID(group->id-1, ti, i); } @@ -2819,7 +2836,8 @@ bool ResTable::expandResourceRef(const uint16_t* refStr, size_t refLen, String16* outName, const String16* defType, const String16* defPackage, - const char** outErrorMsg) + const char** outErrorMsg, + bool* outPublicOnly) { const char16_t* packageEnd = NULL; const char16_t* typeEnd = NULL; @@ -2836,6 +2854,16 @@ bool ResTable::expandResourceRef(const uint16_t* refStr, size_t refLen, p = refStr; if (*p == '@') p++; + if (outPublicOnly != NULL) { + *outPublicOnly = true; + } + if (*p == '*') { + p++; + if (outPublicOnly != NULL) { + *outPublicOnly = false; + } + } + if (packageEnd) { *outPackage = String16(p, packageEnd-p); p = packageEnd+1; From a538e268e653d8fa1ea27659f507bc0ce5ceead0 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Thu, 2 Jun 2011 08:59:28 -0700 Subject: [PATCH 333/541] Remove redundant memory barrier pthread_create already includes the necessary memory barriers: - parent at pthread_create : pthread_mutex_unlock(start_mutex) - child at __thread_entry : pthread_mutex_lock(start_mutex) Add lock around uses of mThread. Added comments: - uses of mThread require lock - androidCreateRawThreadEtc returned ID is not safe for direct use from non-parent threads. Change-Id: I18cb296b41ddaf64cf127b57aab31154319b5970 --- include/utils/threads.h | 1 + libs/utils/Threads.cpp | 13 +++++-------- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/include/utils/threads.h b/include/utils/threads.h index 41e5766a0..0bd69cf37 100644 --- a/include/utils/threads.h +++ b/include/utils/threads.h @@ -526,6 +526,7 @@ private: Thread& operator=(const Thread&); static int _threadLoop(void* user); const bool mCanCallJava; + // always hold mLock when reading or writing thread_id_t mThread; mutable Mutex mLock; Condition mThreadExitedCondition; diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp index 8b5da0e58..c74822866 100644 --- a/libs/utils/Threads.cpp +++ b/libs/utils/Threads.cpp @@ -168,6 +168,9 @@ int androidCreateRawThreadEtc(android_thread_func_t entryFunction, return 0; } + // Note that *threadID is directly available to the parent only, as it is + // assigned after the child starts. Use memory barrier / lock if the child + // or other threads also need access. if (threadId != NULL) { *threadId = (android_thread_id_t)thread; // XXX: this is not portable } @@ -718,7 +721,6 @@ status_t Thread::run(const char* name, int32_t priority, size_t stack) res = androidCreateRawThreadEtc(_threadLoop, this, name, priority, stack, &mThread); } - // The new thread wakes up at _threadLoop, but immediately blocks on mLock if (res == false) { mStatus = UNKNOWN_ERROR; // something happened! @@ -742,11 +744,6 @@ int Thread::_threadLoop(void* user) { Thread* const self = static_cast(user); - // force a memory barrier before reading any fields, in particular mHoldSelf - { - Mutex::Autolock _l(self->mLock); - } - sp strong(self->mHoldSelf); wp weak(strong); self->mHoldSelf.clear(); @@ -816,6 +813,7 @@ void Thread::requestExit() status_t Thread::requestExitAndWait() { + Mutex::Autolock _l(mLock); if (mThread == getThreadId()) { LOGW( "Thread (this=%p): don't call waitForExit() from this " @@ -825,9 +823,8 @@ status_t Thread::requestExitAndWait() return WOULD_BLOCK; } - requestExit(); + mExitPending = true; - Mutex::Autolock _l(mLock); while (mRunning == true) { mThreadExitedCondition.wait(mLock); } From 9b6259aa579291deb7334a968bd30878a5fec386 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Sun, 12 Jun 2011 18:05:53 -0700 Subject: [PATCH 334/541] fix RefBase so it retains binary-compatibility with gingerbread (DO NOT MERGE) Bug: 4595257 Change-Id: I1db83149107d7dab1f0b7e73c684e0ff82e17e62 --- include/utils/RefBase.h | 20 +++++++++++++------- libs/utils/RefBase.cpp | 34 +++++++++++++++++++++++++--------- 2 files changed, 38 insertions(+), 16 deletions(-) diff --git a/include/utils/RefBase.h b/include/utils/RefBase.h index 412622596..f8c3216b4 100644 --- a/include/utils/RefBase.h +++ b/include/utils/RefBase.h @@ -116,17 +116,23 @@ public: typedef RefBase basetype; + // used to override the RefBase destruction. + class Destroyer { + friend class RefBase; + public: + virtual ~Destroyer(); + private: + virtual void destroy(RefBase const* base) = 0; + }; + + // Make sure to never acquire a strong reference from this function. The + // same restrictions than for destructors apply. + void setDestroyer(Destroyer* destroyer); + protected: RefBase(); virtual ~RefBase(); - // called when the last reference goes away. this is responsible for - // calling the destructor. The default implementation just does - // "delete this;". - // Make sure to never acquire a strong reference from this function. The - // same restrictions than for destructors apply. - virtual void destroy() const; - //! Flags for extendObjectLifetime() enum { OBJECT_LIFETIME_WEAK = 0x0001, diff --git a/libs/utils/RefBase.cpp b/libs/utils/RefBase.cpp index 1c870cf78..47ef546df 100644 --- a/libs/utils/RefBase.cpp +++ b/libs/utils/RefBase.cpp @@ -48,6 +48,11 @@ namespace android { // --------------------------------------------------------------------------- +RefBase::Destroyer::~Destroyer() { +} + +// --------------------------------------------------------------------------- + class RefBase::weakref_impl : public RefBase::weakref_type { public: @@ -55,7 +60,7 @@ public: volatile int32_t mWeak; RefBase* const mBase; volatile int32_t mFlags; - + Destroyer* mDestroyer; #if !DEBUG_REFS @@ -64,6 +69,7 @@ public: , mWeak(0) , mBase(base) , mFlags(0) + , mDestroyer(0) { } @@ -298,10 +304,6 @@ void RefBase::incStrong(const void* id) const const_cast(this)->onFirstRef(); } -void RefBase::destroy() const { - delete this; -} - void RefBase::decStrong(const void* id) const { weakref_impl* const refs = mRefs; @@ -314,7 +316,11 @@ void RefBase::decStrong(const void* id) const if (c == 1) { const_cast(this)->onLastStrongRef(id); if ((refs->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) { - destroy(); + if (refs->mDestroyer) { + refs->mDestroyer->destroy(this); + } else { + delete this; + } } } refs->removeWeakRef(id); @@ -349,7 +355,9 @@ int32_t RefBase::getStrongCount() const return mRefs->mStrong; } - +void RefBase::setDestroyer(RefBase::Destroyer* destroyer) { + mRefs->mDestroyer = destroyer; +} RefBase* RefBase::weakref_type::refBase() const { @@ -375,7 +383,11 @@ void RefBase::weakref_type::decWeak(const void* id) if ((impl->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) { if (impl->mStrong == INITIAL_STRONG_VALUE) { if (impl->mBase) { - impl->mBase->destroy(); + if (impl->mDestroyer) { + impl->mDestroyer->destroy(impl->mBase); + } else { + delete impl->mBase; + } } } else { // LOGV("Freeing refs %p of old RefBase %p\n", this, impl->mBase); @@ -385,7 +397,11 @@ void RefBase::weakref_type::decWeak(const void* id) impl->mBase->onLastWeakRef(id); if ((impl->mFlags&OBJECT_LIFETIME_FOREVER) != OBJECT_LIFETIME_FOREVER) { if (impl->mBase) { - impl->mBase->destroy(); + if (impl->mDestroyer) { + impl->mDestroyer->destroy(impl->mBase); + } else { + delete impl->mBase; + } } } } From 47f48572db6a5b4b1b890f93f07ff2bd7c7dc95c Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Tue, 14 Jun 2011 10:35:34 -0700 Subject: [PATCH 335/541] Bug 4608375 Update priority and policy together for audio threads Change-Id: Ib3b07b32586c222c4aacbf23414ae8b05db502be --- libs/utils/Threads.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp index c74822866..15bb1d226 100644 --- a/libs/utils/Threads.cpp +++ b/libs/utils/Threads.cpp @@ -335,10 +335,17 @@ int androidSetThreadPriority(pid_t tid, int pri) pthread_once(&gDoSchedulingGroupOnce, checkDoSchedulingGroup); if (gDoSchedulingGroup) { + // set_sched_policy does not support tid == 0 + int policy_tid; + if (tid == 0) { + policy_tid = androidGetTid(); + } else { + policy_tid = tid; + } if (pri >= ANDROID_PRIORITY_BACKGROUND) { - rc = set_sched_policy(tid, SP_BACKGROUND); + rc = set_sched_policy(policy_tid, SP_BACKGROUND); } else if (getpriority(PRIO_PROCESS, tid) >= ANDROID_PRIORITY_BACKGROUND) { - rc = set_sched_policy(tid, SP_FOREGROUND); + rc = set_sched_policy(policy_tid, SP_FOREGROUND); } } From 0151ac8945216bb6c7ae23b6a7e449e5ede8a69c Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Tue, 14 Jun 2011 11:50:22 -0700 Subject: [PATCH 336/541] may fix build on some version of gcc Change-Id: Ia88787c0432ec84d51fe505fd6a2b6f98ebe8f33 --- include/utils/RefBase.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/utils/RefBase.h b/include/utils/RefBase.h index f8c3216b4..ca170827f 100644 --- a/include/utils/RefBase.h +++ b/include/utils/RefBase.h @@ -119,6 +119,7 @@ public: // used to override the RefBase destruction. class Destroyer { friend class RefBase; + friend class weakref_type; public: virtual ~Destroyer(); private: From dace0b45d00814dda275d81b82c1673dd88cf3e9 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Wed, 15 Jun 2011 19:20:52 -0700 Subject: [PATCH 337/541] revert surfaceflinger leak fix as it uncovered a crasher on xoom Bug: 4600244 Change-Id: Ia68ebf0f243a051ff6a21b3863e3e5d259bbf7ac --- include/utils/RefBase.h | 14 ------------- libs/utils/RefBase.cpp | 44 +++++++++-------------------------------- 2 files changed, 9 insertions(+), 49 deletions(-) diff --git a/include/utils/RefBase.h b/include/utils/RefBase.h index ca170827f..a21a18bda 100644 --- a/include/utils/RefBase.h +++ b/include/utils/RefBase.h @@ -116,20 +116,6 @@ public: typedef RefBase basetype; - // used to override the RefBase destruction. - class Destroyer { - friend class RefBase; - friend class weakref_type; - public: - virtual ~Destroyer(); - private: - virtual void destroy(RefBase const* base) = 0; - }; - - // Make sure to never acquire a strong reference from this function. The - // same restrictions than for destructors apply. - void setDestroyer(Destroyer* destroyer); - protected: RefBase(); virtual ~RefBase(); diff --git a/libs/utils/RefBase.cpp b/libs/utils/RefBase.cpp index 47ef546df..32e900a7f 100644 --- a/libs/utils/RefBase.cpp +++ b/libs/utils/RefBase.cpp @@ -48,11 +48,6 @@ namespace android { // --------------------------------------------------------------------------- -RefBase::Destroyer::~Destroyer() { -} - -// --------------------------------------------------------------------------- - class RefBase::weakref_impl : public RefBase::weakref_type { public: @@ -60,7 +55,7 @@ public: volatile int32_t mWeak; RefBase* const mBase; volatile int32_t mFlags; - Destroyer* mDestroyer; + #if !DEBUG_REFS @@ -69,7 +64,6 @@ public: , mWeak(0) , mBase(base) , mFlags(0) - , mDestroyer(0) { } @@ -316,11 +310,7 @@ void RefBase::decStrong(const void* id) const if (c == 1) { const_cast(this)->onLastStrongRef(id); if ((refs->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) { - if (refs->mDestroyer) { - refs->mDestroyer->destroy(this); - } else { - delete this; - } + delete this; } } refs->removeWeakRef(id); @@ -355,9 +345,7 @@ int32_t RefBase::getStrongCount() const return mRefs->mStrong; } -void RefBase::setDestroyer(RefBase::Destroyer* destroyer) { - mRefs->mDestroyer = destroyer; -} + RefBase* RefBase::weakref_type::refBase() const { @@ -381,28 +369,16 @@ void RefBase::weakref_type::decWeak(const void* id) if (c != 1) return; if ((impl->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) { - if (impl->mStrong == INITIAL_STRONG_VALUE) { - if (impl->mBase) { - if (impl->mDestroyer) { - impl->mDestroyer->destroy(impl->mBase); - } else { - delete impl->mBase; - } - } - } else { + if (impl->mStrong == INITIAL_STRONG_VALUE) + delete impl->mBase; + else { // LOGV("Freeing refs %p of old RefBase %p\n", this, impl->mBase); delete impl; } } else { impl->mBase->onLastWeakRef(id); if ((impl->mFlags&OBJECT_LIFETIME_FOREVER) != OBJECT_LIFETIME_FOREVER) { - if (impl->mBase) { - if (impl->mDestroyer) { - impl->mDestroyer->destroy(impl->mBase); - } else { - delete impl->mBase; - } - } + delete impl->mBase; } } } @@ -526,10 +502,8 @@ RefBase::RefBase() RefBase::~RefBase() { - if ((mRefs->mFlags & OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_WEAK) { - if (mRefs->mWeak == 0) { - delete mRefs; - } + if (mRefs->mWeak == 0) { + delete mRefs; } } From 267ba69c2e4ae39b9fe98d4be592c7b59e7e57a1 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Wed, 15 Jun 2011 20:41:24 -0700 Subject: [PATCH 338/541] Revert "revert surfaceflinger leak fix as it uncovered a crasher on xoom" This reverts commit af6edba59e250adbdfa5b3c3be134f70d8c38a16. Change-Id: I7793d3ca8a4d20a2b188364f47854328ab5f586d --- include/utils/RefBase.h | 14 +++++++++++++ libs/utils/RefBase.cpp | 44 ++++++++++++++++++++++++++++++++--------- 2 files changed, 49 insertions(+), 9 deletions(-) diff --git a/include/utils/RefBase.h b/include/utils/RefBase.h index a21a18bda..ca170827f 100644 --- a/include/utils/RefBase.h +++ b/include/utils/RefBase.h @@ -116,6 +116,20 @@ public: typedef RefBase basetype; + // used to override the RefBase destruction. + class Destroyer { + friend class RefBase; + friend class weakref_type; + public: + virtual ~Destroyer(); + private: + virtual void destroy(RefBase const* base) = 0; + }; + + // Make sure to never acquire a strong reference from this function. The + // same restrictions than for destructors apply. + void setDestroyer(Destroyer* destroyer); + protected: RefBase(); virtual ~RefBase(); diff --git a/libs/utils/RefBase.cpp b/libs/utils/RefBase.cpp index 32e900a7f..47ef546df 100644 --- a/libs/utils/RefBase.cpp +++ b/libs/utils/RefBase.cpp @@ -48,6 +48,11 @@ namespace android { // --------------------------------------------------------------------------- +RefBase::Destroyer::~Destroyer() { +} + +// --------------------------------------------------------------------------- + class RefBase::weakref_impl : public RefBase::weakref_type { public: @@ -55,7 +60,7 @@ public: volatile int32_t mWeak; RefBase* const mBase; volatile int32_t mFlags; - + Destroyer* mDestroyer; #if !DEBUG_REFS @@ -64,6 +69,7 @@ public: , mWeak(0) , mBase(base) , mFlags(0) + , mDestroyer(0) { } @@ -310,7 +316,11 @@ void RefBase::decStrong(const void* id) const if (c == 1) { const_cast(this)->onLastStrongRef(id); if ((refs->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) { - delete this; + if (refs->mDestroyer) { + refs->mDestroyer->destroy(this); + } else { + delete this; + } } } refs->removeWeakRef(id); @@ -345,7 +355,9 @@ int32_t RefBase::getStrongCount() const return mRefs->mStrong; } - +void RefBase::setDestroyer(RefBase::Destroyer* destroyer) { + mRefs->mDestroyer = destroyer; +} RefBase* RefBase::weakref_type::refBase() const { @@ -369,16 +381,28 @@ void RefBase::weakref_type::decWeak(const void* id) if (c != 1) return; if ((impl->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) { - if (impl->mStrong == INITIAL_STRONG_VALUE) - delete impl->mBase; - else { + if (impl->mStrong == INITIAL_STRONG_VALUE) { + if (impl->mBase) { + if (impl->mDestroyer) { + impl->mDestroyer->destroy(impl->mBase); + } else { + delete impl->mBase; + } + } + } else { // LOGV("Freeing refs %p of old RefBase %p\n", this, impl->mBase); delete impl; } } else { impl->mBase->onLastWeakRef(id); if ((impl->mFlags&OBJECT_LIFETIME_FOREVER) != OBJECT_LIFETIME_FOREVER) { - delete impl->mBase; + if (impl->mBase) { + if (impl->mDestroyer) { + impl->mDestroyer->destroy(impl->mBase); + } else { + delete impl->mBase; + } + } } } } @@ -502,8 +526,10 @@ RefBase::RefBase() RefBase::~RefBase() { - if (mRefs->mWeak == 0) { - delete mRefs; + if ((mRefs->mFlags & OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_WEAK) { + if (mRefs->mWeak == 0) { + delete mRefs; + } } } From 9c8fa9ed4111c69c82ace01c8a7ac3beeacdce78 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Wed, 15 Jun 2011 20:42:47 -0700 Subject: [PATCH 339/541] revert surfaceflinger leak fix as it uncovered a crasher on xoom (DO NOT MERGE) This reverts commit 52a43990880b27808bcf562afcc4209d34728e6e. Change-Id: I1856a48f863b051395b8091ddfd1e01292fa1b1e --- include/utils/RefBase.h | 14 ------------- libs/utils/RefBase.cpp | 44 +++++++++-------------------------------- 2 files changed, 9 insertions(+), 49 deletions(-) diff --git a/include/utils/RefBase.h b/include/utils/RefBase.h index ca170827f..a21a18bda 100644 --- a/include/utils/RefBase.h +++ b/include/utils/RefBase.h @@ -116,20 +116,6 @@ public: typedef RefBase basetype; - // used to override the RefBase destruction. - class Destroyer { - friend class RefBase; - friend class weakref_type; - public: - virtual ~Destroyer(); - private: - virtual void destroy(RefBase const* base) = 0; - }; - - // Make sure to never acquire a strong reference from this function. The - // same restrictions than for destructors apply. - void setDestroyer(Destroyer* destroyer); - protected: RefBase(); virtual ~RefBase(); diff --git a/libs/utils/RefBase.cpp b/libs/utils/RefBase.cpp index 47ef546df..32e900a7f 100644 --- a/libs/utils/RefBase.cpp +++ b/libs/utils/RefBase.cpp @@ -48,11 +48,6 @@ namespace android { // --------------------------------------------------------------------------- -RefBase::Destroyer::~Destroyer() { -} - -// --------------------------------------------------------------------------- - class RefBase::weakref_impl : public RefBase::weakref_type { public: @@ -60,7 +55,7 @@ public: volatile int32_t mWeak; RefBase* const mBase; volatile int32_t mFlags; - Destroyer* mDestroyer; + #if !DEBUG_REFS @@ -69,7 +64,6 @@ public: , mWeak(0) , mBase(base) , mFlags(0) - , mDestroyer(0) { } @@ -316,11 +310,7 @@ void RefBase::decStrong(const void* id) const if (c == 1) { const_cast(this)->onLastStrongRef(id); if ((refs->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) { - if (refs->mDestroyer) { - refs->mDestroyer->destroy(this); - } else { - delete this; - } + delete this; } } refs->removeWeakRef(id); @@ -355,9 +345,7 @@ int32_t RefBase::getStrongCount() const return mRefs->mStrong; } -void RefBase::setDestroyer(RefBase::Destroyer* destroyer) { - mRefs->mDestroyer = destroyer; -} + RefBase* RefBase::weakref_type::refBase() const { @@ -381,28 +369,16 @@ void RefBase::weakref_type::decWeak(const void* id) if (c != 1) return; if ((impl->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) { - if (impl->mStrong == INITIAL_STRONG_VALUE) { - if (impl->mBase) { - if (impl->mDestroyer) { - impl->mDestroyer->destroy(impl->mBase); - } else { - delete impl->mBase; - } - } - } else { + if (impl->mStrong == INITIAL_STRONG_VALUE) + delete impl->mBase; + else { // LOGV("Freeing refs %p of old RefBase %p\n", this, impl->mBase); delete impl; } } else { impl->mBase->onLastWeakRef(id); if ((impl->mFlags&OBJECT_LIFETIME_FOREVER) != OBJECT_LIFETIME_FOREVER) { - if (impl->mBase) { - if (impl->mDestroyer) { - impl->mDestroyer->destroy(impl->mBase); - } else { - delete impl->mBase; - } - } + delete impl->mBase; } } } @@ -526,10 +502,8 @@ RefBase::RefBase() RefBase::~RefBase() { - if ((mRefs->mFlags & OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_WEAK) { - if (mRefs->mWeak == 0) { - delete mRefs; - } + if (mRefs->mWeak == 0) { + delete mRefs; } } From 967ad860da30d09c2003274389cb53abe165b6cc Mon Sep 17 00:00:00 2001 From: Kenny Root Date: Wed, 15 Jun 2011 20:41:15 -0700 Subject: [PATCH 340/541] Use rand() for MinGW The version of MinGW we use doesn't have nrand48() which is really lame, but we need to use libutils in the Windows SDK. Change-Id: If854c03dbf02bc29e79f49e4539f08c2bf057517 --- include/utils/BlobCache.h | 3 +++ libs/utils/BlobCache.cpp | 14 +++++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/include/utils/BlobCache.h b/include/utils/BlobCache.h index 8f76d72c1..dc45ff0f3 100644 --- a/include/utils/BlobCache.h +++ b/include/utils/BlobCache.h @@ -82,6 +82,9 @@ private: BlobCache(const BlobCache&); void operator=(const BlobCache&); + // A random function helper to get around MinGW not having nrand48() + long int blob_random(); + // clean evicts a randomly chosen set of entries from the cache such that // the total size of all remaining entries is less than mMaxTotalSize/2. void clean(); diff --git a/libs/utils/BlobCache.cpp b/libs/utils/BlobCache.cpp index 1298fa733..590576a8d 100644 --- a/libs/utils/BlobCache.cpp +++ b/libs/utils/BlobCache.cpp @@ -31,9 +31,13 @@ BlobCache::BlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxTotalSize mMaxTotalSize(maxTotalSize), mTotalSize(0) { nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); +#ifdef _WIN32 + srand(now); +#else mRandState[0] = (now >> 0) & 0xFFFF; mRandState[1] = (now >> 16) & 0xFFFF; mRandState[2] = (now >> 32) & 0xFFFF; +#endif LOGV("initializing random seed using %lld", now); } @@ -148,11 +152,19 @@ size_t BlobCache::get(const void* key, size_t keySize, void* value, return valueBlobSize; } +long int BlobCache::blob_random() { +#ifdef _WIN32 + return rand(); +#else + return nrand48(mRandState); +#endif +} + void BlobCache::clean() { // Remove a random cache entry until the total cache size gets below half // the maximum total cache size. while (mTotalSize > mMaxTotalSize / 2) { - size_t i = size_t(nrand48(mRandState) % (mCacheEntries.size())); + size_t i = size_t(blob_random() % (mCacheEntries.size())); const CacheEntry& entry(mCacheEntries[i]); mTotalSize -= entry.getKey()->getSize() + entry.getValue()->getSize(); mCacheEntries.removeAt(i); From 7f57eac6a706b471f2dcadd15868797ea081306a Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Thu, 16 Jun 2011 17:15:51 -0700 Subject: [PATCH 341/541] Revert "merge various SF fixes from gingerbread to honeycomb-mr2" (DO NOT MERGE) Also revert all dependent changes: This reverts commit 8e18668d14adf601cbe5973030c310ec23d88461. This reverts commit 69b4587bfbb3e98f793959d9123340360fa233a2. This reverts commit a9c9a4baf24700e8817d47d8ea8da1742caea0b5. This reverts commit 2c0042b666a969091c931614f2fc0dce2f1cfac8. This reverts commit f6c8206735e7e078461e5f2aef6e1a1446fdd075. This reverts commit 24855c09173a6caaec7dcedd0c2d7ce15121d39b. Change-Id: I33e699640f3f59e42fa03c99a9a1b7af0d27d4d8 --- include/utils/RefBase.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/utils/RefBase.h b/include/utils/RefBase.h index a21a18bda..e81cd0063 100644 --- a/include/utils/RefBase.h +++ b/include/utils/RefBase.h @@ -119,7 +119,7 @@ public: protected: RefBase(); virtual ~RefBase(); - + //! Flags for extendObjectLifetime() enum { OBJECT_LIFETIME_WEAK = 0x0001, From 5e0243f4d6313419fdc3affc205b790a2a965208 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Wed, 22 Jun 2011 17:42:23 -0700 Subject: [PATCH 342/541] Thread ID zero for androidSetThreadSchedulingGroup Already implemented by androidSetThreadPriority but not documented Change-Id: I85302b17092952065f3f3a4214d8d8abdd465dbd --- include/utils/threads.h | 4 ++-- libs/utils/Threads.cpp | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/include/utils/threads.h b/include/utils/threads.h index 0bd69cf37..41f67e405 100644 --- a/include/utils/threads.h +++ b/include/utils/threads.h @@ -133,13 +133,13 @@ extern pid_t androidGetTid(); // Change the scheduling group of a particular thread. The group // should be one of the ANDROID_TGROUP constants. Returns BAD_VALUE if // grp is out of range, else another non-zero value with errno set if -// the operation failed. +// the operation failed. Thread ID zero means current thread. extern int androidSetThreadSchedulingGroup(pid_t tid, int grp); // Change the priority AND scheduling group of a particular thread. The priority // should be one of the ANDROID_PRIORITY constants. Returns INVALID_OPERATION // if the priority set failed, else another value if just the group set failed; -// in either case errno is set. +// in either case errno is set. Thread ID zero means current thread. extern int androidSetThreadPriority(pid_t tid, int prio); #ifdef __cplusplus diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp index 15bb1d226..71352a864 100644 --- a/libs/utils/Threads.cpp +++ b/libs/utils/Threads.cpp @@ -316,6 +316,10 @@ int androidSetThreadSchedulingGroup(pid_t tid, int grp) #if defined(HAVE_PTHREADS) pthread_once(&gDoSchedulingGroupOnce, checkDoSchedulingGroup); if (gDoSchedulingGroup) { + // set_sched_policy does not support tid == 0 + if (tid == 0) { + tid = androidGetTid(); + } if (set_sched_policy(tid, (grp == ANDROID_TGROUP_BG_NONINTERACT) ? SP_BACKGROUND : SP_FOREGROUND)) { return PERMISSION_DENIED; From 6839e8e9ee894191e3fa857859311337357fe191 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Thu, 23 Jun 2011 12:55:29 -0700 Subject: [PATCH 343/541] Add Thread::join This new API will be used by applications that previously used the lower-level pthread APIs (including pthread_join). Centralizing on the Thread class instead of pthread will permit additional functionality to be added later in only one location. Change-Id: I8460169ac9c61ac9f85752405ed54c94651058d7 --- include/utils/threads.h | 4 ++++ libs/utils/Threads.cpp | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/include/utils/threads.h b/include/utils/threads.h index 41f67e405..c8e9c0413 100644 --- a/include/utils/threads.h +++ b/include/utils/threads.h @@ -510,6 +510,10 @@ public: // that case. status_t requestExitAndWait(); + // Wait until this object's thread exits. Returns immediately if not yet running. + // Do not call from this object's thread; will return WOULD_BLOCK in that case. + status_t join(); + protected: // exitPending() returns true if requestExit() has been called. bool exitPending() const; diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp index 71352a864..50312e7bb 100644 --- a/libs/utils/Threads.cpp +++ b/libs/utils/Threads.cpp @@ -846,6 +846,25 @@ status_t Thread::requestExitAndWait() return mStatus; } +status_t Thread::join() +{ + Mutex::Autolock _l(mLock); + if (mThread == getThreadId()) { + LOGW( + "Thread (this=%p): don't call join() from this " + "Thread object's thread. It's a guaranteed deadlock!", + this); + + return WOULD_BLOCK; + } + + while (mRunning == true) { + mThreadExitedCondition.wait(mLock); + } + + return mStatus; +} + bool Thread::exitPending() const { Mutex::Autolock _l(mLock); From 9d05973685fc6ebe8e2878597128c41a61e149f9 Mon Sep 17 00:00:00 2001 From: Christopher Tate Date: Fri, 24 Jun 2011 15:06:48 -0700 Subject: [PATCH 344/541] Fix settings restore Also correct the debug-mode logging of error locations in backup data. Bug 4914182 Change-Id: Ie7dda0192afa819e42490b7ffd2d3db6f11968f6 --- libs/utils/BackupData.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libs/utils/BackupData.cpp b/libs/utils/BackupData.cpp index f963058fa..879126393 100644 --- a/libs/utils/BackupData.cpp +++ b/libs/utils/BackupData.cpp @@ -285,7 +285,8 @@ BackupDataReader::ReadNextHeader(bool* done, int* type) break; } default: - LOGD("Chunk header at %d has invalid type: 0x%08x", (int)m_pos, (int)m_header.type); + LOGD("Chunk header at %d has invalid type: 0x%08x", + (int)(m_pos - sizeof(m_header)), (int)m_header.type); m_status = EINVAL; } From b86c8e4e55c860a6a7e1009fda2a4803da45b412 Mon Sep 17 00:00:00 2001 From: Jason Simmons Date: Tue, 28 Jun 2011 17:43:30 -0700 Subject: [PATCH 345/541] Add a linear transform library to libutils Change-Id: Icdec5a6bebd9d8f24b3f335f8ec8b09a5810a774 --- include/utils/LinearTransform.h | 64 ++++++++ libs/utils/Android.mk | 1 + libs/utils/LinearTransform.cpp | 262 ++++++++++++++++++++++++++++++++ 3 files changed, 327 insertions(+) create mode 100644 include/utils/LinearTransform.h create mode 100644 libs/utils/LinearTransform.cpp diff --git a/include/utils/LinearTransform.h b/include/utils/LinearTransform.h new file mode 100644 index 000000000..04cb355c7 --- /dev/null +++ b/include/utils/LinearTransform.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2011 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 _LIBS_UTILS_LINEAR_TRANSFORM_H +#define _LIBS_UTILS_LINEAR_TRANSFORM_H + +#include + +namespace android { + +// LinearTransform defines a structure which hold the definition of a +// transformation from single dimensional coordinate system A into coordinate +// system B (and back again). Values in A and in B are 64 bit, the linear +// scale factor is expressed as a rational number using two 32 bit values. +// +// Specifically, let +// f(a) = b +// F(b) = f^-1(b) = a +// then +// +// f(a) = (((a - a_zero) * a_to_b_numer) / a_to_b_denom) + b_zero; +// +// and +// +// F(b) = (((b - b_zero) * a_to_b_denom) / a_to_b_numer) + a_zero; +// +struct LinearTransform { + int64_t a_zero; + int64_t b_zero; + int32_t a_to_b_numer; + uint32_t a_to_b_denom; + + // Transform from A->B + // Returns true on success, or false in the case of a singularity or an + // overflow. + bool doForwardTransform(int64_t a_in, int64_t* b_out) const; + + // Transform from B->A + // Returns true on success, or false in the case of a singularity or an + // overflow. + bool doReverseTransform(int64_t b_in, int64_t* a_out) const; + + // Helpers which will reduce the fraction N/D using Euclid's method. + template static void reduce(T* N, T* D); + static void reduce(int32_t* N, uint32_t* D); +}; + + +} + +#endif // _LIBS_UTILS_LINEAR_TRANSFORM_H diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk index 093189c5c..774e8c974 100644 --- a/libs/utils/Android.mk +++ b/libs/utils/Android.mk @@ -27,6 +27,7 @@ commonSources:= \ Debug.cpp \ FileMap.cpp \ Flattenable.cpp \ + LinearTransform.cpp \ ObbFile.cpp \ Pool.cpp \ PropertyMap.cpp \ diff --git a/libs/utils/LinearTransform.cpp b/libs/utils/LinearTransform.cpp new file mode 100644 index 000000000..d752415cf --- /dev/null +++ b/libs/utils/LinearTransform.cpp @@ -0,0 +1,262 @@ +/* + * Copyright (C) 2011 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 __STDC_LIMIT_MACROS + +#include +#include + +#include + +namespace android { + +template static inline T ABS(T x) { return (x < 0) ? -x : x; } + +// Static math methods involving linear transformations +static bool scale_u64_to_u64( + uint64_t val, + uint32_t N, + uint32_t D, + uint64_t* res, + bool round_up_not_down) { + uint64_t tmp1, tmp2; + uint32_t r; + + assert(res); + assert(D); + + // Let U32(X) denote a uint32_t containing the upper 32 bits of a 64 bit + // integer X. + // Let L32(X) denote a uint32_t containing the lower 32 bits of a 64 bit + // integer X. + // Let X[A, B] with A <= B denote bits A through B of the integer X. + // Let (A | B) denote the concatination of two 32 bit ints, A and B. + // IOW X = (A | B) => U32(X) == A && L32(X) == B + // + // compute M = val * N (a 96 bit int) + // --------------------------------- + // tmp2 = U32(val) * N (a 64 bit int) + // tmp1 = L32(val) * N (a 64 bit int) + // which means + // M = val * N = (tmp2 << 32) + tmp1 + tmp2 = (val >> 32) * N; + tmp1 = (val & UINT32_MAX) * N; + + // compute M[32, 95] + // tmp2 = tmp2 + U32(tmp1) + // = (U32(val) * N) + U32(L32(val) * N) + // = M[32, 95] + tmp2 += tmp1 >> 32; + + // if M[64, 95] >= D, then M/D has bits > 63 set and we have + // an overflow. + if ((tmp2 >> 32) >= D) { + *res = UINT64_MAX; + return false; + } + + // Divide. Going in we know + // tmp2 = M[32, 95] + // U32(tmp2) < D + r = tmp2 % D; + tmp2 /= D; + + // At this point + // tmp1 = L32(val) * N + // tmp2 = M[32, 95] / D + // = (M / D)[32, 95] + // r = M[32, 95] % D + // U32(tmp2) = 0 + // + // compute tmp1 = (r | M[0, 31]) + tmp1 = (tmp1 & UINT32_MAX) | ((uint64_t)r << 32); + + // Divide again. Keep the remainder around in order to round properly. + r = tmp1 % D; + tmp1 /= D; + + // At this point + // tmp2 = (M / D)[32, 95] + // tmp1 = (M / D)[ 0, 31] + // r = M % D + // U32(tmp1) = 0 + // U32(tmp2) = 0 + + // Pack the result and deal with the round-up case (As well as the + // remote possiblility over overflow in such a case). + *res = (tmp2 << 32) | tmp1; + if (r && round_up_not_down) { + ++(*res); + if (!(*res)) { + *res = UINT64_MAX; + return false; + } + } + + return true; +} + +static bool linear_transform_s64_to_s64( + int64_t val, + int64_t basis1, + int32_t N, + uint32_t D, + int64_t basis2, + int64_t* out) { + uint64_t scaled, res; + uint64_t abs_val; + bool is_neg; + + if (!out) + return false; + + // Compute abs(val - basis_64). Keep track of whether or not this delta + // will be negative after the scale opertaion. + if (val < basis1) { + is_neg = true; + abs_val = basis1 - val; + } else { + is_neg = false; + abs_val = val - basis1; + } + + if (N < 0) + is_neg = !is_neg; + + if (!scale_u64_to_u64(abs_val, + ABS(N), + D, + &scaled, + is_neg)) + return false; // overflow/undeflow + + // if scaled is >= 0x8000, then we are going to overflow or + // underflow unless ABS(basis2) is large enough to pull us back into the + // non-overflow/underflow region. + if (scaled & INT64_MIN) { + if (is_neg && (basis2 < 0)) + return false; // certain underflow + + if (!is_neg && (basis2 >= 0)) + return false; // certain overflow + + if (ABS(basis2) <= static_cast(scaled & INT64_MAX)) + return false; // not enough + + // Looks like we are OK + *out = (is_neg ? (-scaled) : scaled) + basis2; + } else { + // Scaled fits within signed bounds, so we just need to check for + // over/underflow for two signed integers. Basically, if both scaled + // and basis2 have the same sign bit, and the result has a different + // sign bit, then we have under/overflow. An easy way to compute this + // is + // (scaled_signbit XNOR basis_signbit) && + // (scaled_signbit XOR res_signbit) + // == + // (scaled_signbit XOR basis_signbit XOR 1) && + // (scaled_signbit XOR res_signbit) + + if (is_neg) + scaled = -scaled; + res = scaled + basis2; + + if ((scaled ^ basis2 ^ INT64_MIN) & (scaled ^ res) & INT64_MIN) + return false; + + *out = res; + } + + return true; +} + +bool LinearTransform::doForwardTransform(int64_t a_in, int64_t* b_out) const { + if (0 == a_to_b_denom) + return false; + + return linear_transform_s64_to_s64(a_in, + a_zero, + a_to_b_numer, + a_to_b_denom, + b_zero, + b_out); +} + +bool LinearTransform::doReverseTransform(int64_t b_in, int64_t* a_out) const { + if (0 == a_to_b_numer) + return false; + + return linear_transform_s64_to_s64(b_in, + b_zero, + a_to_b_denom, + a_to_b_numer, + a_zero, + a_out); +} + +template void LinearTransform::reduce(T* N, T* D) { + T a, b; + if (!N || !D || !(*D)) { + assert(false); + return; + } + + a = *N; + b = *D; + + if (a == 0) { + *D = 1; + return; + } + + // This implements Euclid's method to find GCD. + if (a < b) { + T tmp = a; + a = b; + b = tmp; + } + + while (1) { + // a is now the greater of the two. + const T remainder = a % b; + if (remainder == 0) { + *N /= b; + *D /= b; + return; + } + // by swapping remainder and b, we are guaranteeing that a is + // still the greater of the two upon entrance to the loop. + a = b; + b = remainder; + } +}; + +template void LinearTransform::reduce(uint64_t* N, uint64_t* D); +template void LinearTransform::reduce(uint32_t* N, uint32_t* D); + +void LinearTransform::reduce(int32_t* N, uint32_t* D) { + if (N && D && *D) { + if (*N < 0) { + *N = -(*N); + reduce(reinterpret_cast(N), D); + *N = -(*N); + } else { + reduce(reinterpret_cast(N), D); + } + } +} + +} // namespace android From 320a2b410329998a60d754171e06f872aa9c0467 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Tue, 28 Jun 2011 19:09:31 -0700 Subject: [PATCH 346/541] SF transactions are now O(1) wrt IPC instead of O(N). Change-Id: I57669852cbf6aabae244ea86940a08a5a27ffc43 --- include/utils/SortedVector.h | 2 ++ include/utils/Vector.h | 26 +++++++++++++++++++++++++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/include/utils/SortedVector.h b/include/utils/SortedVector.h index 8beec5732..0e98aeb05 100644 --- a/include/utils/SortedVector.h +++ b/include/utils/SortedVector.h @@ -32,6 +32,8 @@ namespace android { template class SortedVector : private SortedVectorImpl { + friend class Vector; + public: typedef TYPE value_type; diff --git a/include/utils/Vector.h b/include/utils/Vector.h index f1e87e609..b908e2ab2 100644 --- a/include/utils/Vector.h +++ b/include/utils/Vector.h @@ -29,6 +29,9 @@ namespace android { +template +class SortedVector; + /*! * The main templated vector class ensuring type safety * while making use of VectorImpl. @@ -47,13 +50,17 @@ public: Vector(); Vector(const Vector& rhs); + explicit Vector(const SortedVector& rhs); virtual ~Vector(); /*! copy operator */ const Vector& operator = (const Vector& rhs) const; Vector& operator = (const Vector& rhs); - /* + const Vector& operator = (const SortedVector& rhs) const; + Vector& operator = (const SortedVector& rhs); + + /* * empty the vector */ @@ -214,6 +221,11 @@ Vector::Vector(const Vector& rhs) : VectorImpl(rhs) { } +template inline +Vector::Vector(const SortedVector& rhs) + : VectorImpl(static_cast(rhs)) { +} + template inline Vector::~Vector() { finish_vector(); @@ -227,6 +239,18 @@ Vector& Vector::operator = (const Vector& rhs) { template inline const Vector& Vector::operator = (const Vector& rhs) const { + VectorImpl::operator = (static_cast(rhs)); + return *this; +} + +template inline +Vector& Vector::operator = (const SortedVector& rhs) { + VectorImpl::operator = (static_cast(rhs)); + return *this; +} + +template inline +const Vector& Vector::operator = (const SortedVector& rhs) const { VectorImpl::operator = (rhs); return *this; } From 82e14f67803d3457b639a8aea772a6490b34165c Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Fri, 1 Jul 2011 17:59:27 -0700 Subject: [PATCH 347/541] Workaround apps that make assumptions about pointer ids. Modified the touch input mapper to assign pointer ids sequentially starting from 0 instead of using the tracking id or slot index supplied by the driver. Applications should not depend on this ordering but some do. (sigh) Bug: 4980884 Change-Id: I0dfeb3ac27c57a7102a13c960c760e2a02eb7669 --- include/utils/BitSet.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/utils/BitSet.h b/include/utils/BitSet.h index de748b54f..600017e89 100644 --- a/include/utils/BitSet.h +++ b/include/utils/BitSet.h @@ -44,6 +44,9 @@ struct BitSet32 { // Returns true if the bit set does not contain any marked bits. inline bool isEmpty() const { return ! value; } + // Returns true if the bit set does not contain any unmarked bits. + inline bool isFull() const { return value == 0xffffffff; } + // Returns true if the specified bit is marked. inline bool hasBit(uint32_t n) const { return value & valueForBit(n); } From 6fbe0a86a8bb9da0ff879c3d237c2637eb0d5a8d Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Wed, 22 Jun 2011 16:20:37 -0700 Subject: [PATCH 348/541] Add C++ thread API androidGetThreadSchedulingGroup This API is intended for applications that need to read a thread's scheduling group, while using the higher-level (C++) family of thread APIs. Change-Id: I5e58017f74c3989b20b5b1cc2bc4483c95720520 --- include/utils/threads.h | 7 +++++++ libs/utils/Threads.cpp | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/include/utils/threads.h b/include/utils/threads.h index c8e9c0413..79e02ebb3 100644 --- a/include/utils/threads.h +++ b/include/utils/threads.h @@ -142,6 +142,13 @@ extern int androidSetThreadSchedulingGroup(pid_t tid, int grp); // in either case errno is set. Thread ID zero means current thread. extern int androidSetThreadPriority(pid_t tid, int prio); +// Get the current scheduling group of a particular thread. Normally returns +// one of the ANDROID_TGROUP constants other than ANDROID_TGROUP_DEFAULT. +// Returns ANDROID_TGROUP_DEFAULT if no pthread support (e.g. on host) or if +// scheduling groups are disabled. Returns INVALID_OPERATION if unexpected error. +// Thread ID zero means current thread. +extern int androidGetThreadSchedulingGroup(pid_t tid); + #ifdef __cplusplus } #endif diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp index 50312e7bb..6d5067b51 100644 --- a/libs/utils/Threads.cpp +++ b/libs/utils/Threads.cpp @@ -367,6 +367,41 @@ int androidSetThreadPriority(pid_t tid, int pri) return rc; } +int androidGetThreadSchedulingGroup(pid_t tid) +{ + int ret = ANDROID_TGROUP_DEFAULT; + +#if defined(HAVE_PTHREADS) + // convention is to not call get/set_sched_policy methods if disabled by property + pthread_once(&gDoSchedulingGroupOnce, checkDoSchedulingGroup); + if (gDoSchedulingGroup) { + SchedPolicy policy; + // get_sched_policy does not support tid == 0 + if (tid == 0) { + tid = androidGetTid(); + } + if (get_sched_policy(tid, &policy) < 0) { + ret = INVALID_OPERATION; + } else { + switch (policy) { + case SP_BACKGROUND: + ret = ANDROID_TGROUP_BG_NONINTERACT; + break; + case SP_FOREGROUND: + ret = ANDROID_TGROUP_FG_BOOST; + break; + default: + // should not happen, as enum SchedPolicy does not have any other values + ret = INVALID_OPERATION; + break; + } + } + } +#endif + + return ret; +} + namespace android { /* From 266a7d6223c90dd9707304f581d5e6c340335604 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Mon, 11 Jul 2011 16:26:18 -0700 Subject: [PATCH 349/541] Fix typo in an assert's log Change-Id: I94883a23a0a92eaf3e4976f942f747a2137499ac --- libs/utils/VectorImpl.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/utils/VectorImpl.cpp b/libs/utils/VectorImpl.cpp index 289c826d3..87ae3d51f 100644 --- a/libs/utils/VectorImpl.cpp +++ b/libs/utils/VectorImpl.cpp @@ -290,7 +290,7 @@ void VectorImpl::clear() void* VectorImpl::editItemLocation(size_t index) { LOG_ASSERT(index Date: Mon, 11 Jul 2011 22:12:16 -0700 Subject: [PATCH 350/541] Remove the simulator target from all makefiles. Bug: 5010576 Change-Id: I04d722f258951a3078fe07899f5bbe8aac02a8e8 --- libs/utils/Android.mk | 4 ---- libs/utils/tests/Android.mk | 4 ---- 2 files changed, 8 deletions(-) diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk index 774e8c974..f6333576a 100644 --- a/libs/utils/Android.mk +++ b/libs/utils/Android.mk @@ -103,17 +103,14 @@ LOCAL_SHARED_LIBRARIES := \ liblog \ libcutils -ifneq ($(TARGET_SIMULATOR),true) ifeq ($(TARGET_OS)-$(TARGET_ARCH),linux-x86) # This is needed on x86 to bring in dl_iterate_phdr for CallStack.cpp LOCAL_SHARED_LIBRARIES += libdl endif # linux-x86 -endif # sim LOCAL_MODULE:= libutils include $(BUILD_SHARED_LIBRARY) -ifneq ($(TARGET_SIMULATOR),true) ifeq ($(TARGET_OS),linux) include $(CLEAR_VARS) LOCAL_C_INCLUDES += external/zlib external/icu4c/common @@ -122,7 +119,6 @@ LOCAL_MODULE := libutils LOCAL_SRC_FILES := $(commonSources) BackupData.cpp BackupHelpers.cpp include $(BUILD_STATIC_LIBRARY) endif -endif # Include subdirectory makefiles diff --git a/libs/utils/tests/Android.mk b/libs/utils/tests/Android.mk index 87ad98eaa..8726a536c 100644 --- a/libs/utils/tests/Android.mk +++ b/libs/utils/tests/Android.mk @@ -2,8 +2,6 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) -ifneq ($(TARGET_SIMULATOR),true) - # Build the unit tests. test_src_files := \ BlobCache_test.cpp \ @@ -43,5 +41,3 @@ $(foreach file,$(test_src_files), \ $(eval LOCAL_MODULE_TAGS := $(module_tags)) \ $(eval include $(BUILD_EXECUTABLE)) \ ) - -endif From 8c79cc91e4962339a4680fd5cfdb94fcd7e022f6 Mon Sep 17 00:00:00 2001 From: Christopher Tate Date: Mon, 11 Jul 2011 11:31:57 -0700 Subject: [PATCH 351/541] Compress the backup output stream Zlib compression, with a full flush between each application's data. Encryption will be performed on the already-compressed data once that's implemented. On restore, the streamed data is similarly uncompressed on the fly. Change-Id: I19b65c88e759a66527d10913d18fffa9df0bc011 --- libs/utils/BackupHelpers.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/libs/utils/BackupHelpers.cpp b/libs/utils/BackupHelpers.cpp index 87549fe97..7ef30f999 100644 --- a/libs/utils/BackupHelpers.cpp +++ b/libs/utils/BackupHelpers.cpp @@ -481,6 +481,14 @@ static int write_pax_header_entry(char* buf, const char* key, const char* value) return sprintf(buf, "%d %s=%s\n", len, key, value); } +// Wire format to the backup manager service is chunked: each chunk is prefixed by +// a 4-byte count of its size. A chunk size of zero (four zero bytes) indicates EOD. +void send_tarfile_chunk(BackupDataWriter* writer, const char* buffer, size_t size) { + uint32_t chunk_size_no = htonl(size); + writer->WriteEntityData(&chunk_size_no, 4); + if (size != 0) writer->WriteEntityData(buffer, size); +} + int write_tarfile(const String8& packageName, const String8& domain, const String8& rootpath, const String8& filepath, BackupDataWriter* writer) { @@ -660,16 +668,16 @@ int write_tarfile(const String8& packageName, const String8& domain, // Checksum and write the pax block header calc_tar_checksum(paxHeader); - writer->WriteEntityData(paxHeader, 512); + send_tarfile_chunk(writer, paxHeader, 512); // Now write the pax data itself int paxblocks = (paxLen + 511) / 512; - writer->WriteEntityData(paxData, 512 * paxblocks); + send_tarfile_chunk(writer, paxData, 512 * paxblocks); } // Checksum and write the 512-byte ustar file header block to the output calc_tar_checksum(buf); - writer->WriteEntityData(buf, 512); + send_tarfile_chunk(writer, buf, 512); // Now write the file data itself, for real files. We honor tar's convention that // only full 512-byte blocks are sent to write(). @@ -699,7 +707,7 @@ int write_tarfile(const String8& packageName, const String8& domain, memset(buf + nRead, 0, remainder); nRead += remainder; } - writer->WriteEntityData(buf, nRead); + send_tarfile_chunk(writer, buf, nRead); toWrite -= nRead; } } From aa5daede6a9e72f7611b19aa4e580a98878e6825 Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Wed, 13 Jul 2011 22:22:02 -0700 Subject: [PATCH 352/541] Replace Vector _grow/_shrink checks with assert. On review of the code, _grow and _shrink are checking for conditions that cannot happen and that don't even really make sense. For example, if _shrink is called with where + amount > mCount then this is really bad, however the check only considered the case when where >= mCount and then it would arbitrarily choose a new value for where. Huh? As it happens, the callers are correctly validating the arguments before passing them down to these methods so we can get rid of this code. Change-Id: I921852dba8997065bb0e9cac733e82028d14afcd --- libs/utils/VectorImpl.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/libs/utils/VectorImpl.cpp b/libs/utils/VectorImpl.cpp index 87ae3d51f..0701a51a1 100644 --- a/libs/utils/VectorImpl.cpp +++ b/libs/utils/VectorImpl.cpp @@ -347,9 +347,10 @@ void* VectorImpl::_grow(size_t where, size_t amount) // LOGV("_grow(this=%p, where=%d, amount=%d) count=%d, capacity=%d", // this, (int)where, (int)amount, (int)mCount, (int)capacity()); - if (where > mCount) - where = mCount; - + LOG_ASSERT(where <= mCount, + "[%p] _grow: where=%d, amount=%d, count=%d", + this, (int)where, (int)amount, (int)mCount); // caller already checked + const size_t new_size = mCount + amount; if (capacity() < new_size) { const size_t new_capacity = max(kMinVectorCapacity, ((new_size*3)+1)/2); @@ -400,8 +401,9 @@ void VectorImpl::_shrink(size_t where, size_t amount) // LOGV("_shrink(this=%p, where=%d, amount=%d) count=%d, capacity=%d", // this, (int)where, (int)amount, (int)mCount, (int)capacity()); - if (where >= mCount) - where = mCount - amount; + LOG_ASSERT(where + amount <= mCount, + "[%p] _shrink: where=%d, amount=%d, count=%d", + this, (int)where, (int)amount, (int)mCount); // caller already checked const size_t new_size = mCount - amount; if (new_size*3 < capacity()) { From bbbd761de125b50ac83047bccb0b6e0e32ce74a1 Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Thu, 14 Jul 2011 00:29:49 -0700 Subject: [PATCH 353/541] Minor code cleanups in vector. Fixed a potential bug where calling replaceAt with a reference to an existing element in the vector at the same index would cause the element to be destroyed while being copied to itself. Refactored the conditions in _grow and _shrink for clarity. The computations are exactly the same but I think it reads better this way. In particular, the ssize_t variable 's' is gone: it didn't need to be signed anyways because its value could never be negative. Change-Id: If087841c15e6a87160eee874720c4a77eb0e99a6 --- libs/utils/VectorImpl.cpp | 50 +++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 26 deletions(-) diff --git a/libs/utils/VectorImpl.cpp b/libs/utils/VectorImpl.cpp index 0701a51a1..bfb37a60d 100644 --- a/libs/utils/VectorImpl.cpp +++ b/libs/utils/VectorImpl.cpp @@ -252,13 +252,15 @@ ssize_t VectorImpl::replaceAt(const void* prototype, size_t index) "[%p] replace: index=%d, size=%d", this, (int)index, (int)size()); void* item = editItemLocation(index); - if (item == 0) - return NO_MEMORY; - _do_destroy(item, 1); - if (prototype == 0) { - _do_construct(item, 1); - } else { - _do_copy(item, prototype, 1); + if (item != prototype) { + if (item == 0) + return NO_MEMORY; + _do_destroy(item, 1); + if (prototype == 0) { + _do_construct(item, 1); + } else { + _do_copy(item, prototype, 1); + } } return ssize_t(index); } @@ -367,10 +369,10 @@ void* VectorImpl::_grow(size_t where, size_t amount) SharedBuffer* sb = SharedBuffer::alloc(new_capacity * mItemSize); if (sb) { void* array = sb->data(); - if (where>0) { + if (where != 0) { _do_copy(array, mStorage, where); } - if (mCount>where) { + if (where != mCount) { const void* from = reinterpret_cast(mStorage) + where*mItemSize; void* dest = reinterpret_cast(array) + (where+amount)*mItemSize; _do_copy(dest, from, mCount-where); @@ -380,15 +382,14 @@ void* VectorImpl::_grow(size_t where, size_t amount) } } } else { - ssize_t s = mCount-where; - if (s>0) { - void* array = editArrayImpl(); - void* to = reinterpret_cast(array) + (where+amount)*mItemSize; + if (where != mCount) { + void* array = editArrayImpl(); const void* from = reinterpret_cast(array) + where*mItemSize; - _do_move_forward(to, from, s); + void* to = reinterpret_cast(array) + (where+amount)*mItemSize; + _do_move_forward(to, from, mCount - where); } } - mCount += amount; + mCount = new_size; void* free_space = const_cast(itemLocation(where)); return free_space; } @@ -409,7 +410,7 @@ void VectorImpl::_shrink(size_t where, size_t amount) if (new_size*3 < capacity()) { const size_t new_capacity = max(kMinVectorCapacity, new_size*2); // LOGV("shrink vector %p, new_capacity=%d", this, (int)new_capacity); - if ((where == mCount-amount) && + if ((where == new_size) && (mFlags & HAS_TRIVIAL_COPY) && (mFlags & HAS_TRIVIAL_DTOR)) { @@ -420,31 +421,28 @@ void VectorImpl::_shrink(size_t where, size_t amount) SharedBuffer* sb = SharedBuffer::alloc(new_capacity * mItemSize); if (sb) { void* array = sb->data(); - if (where>0) { + if (where != 0) { _do_copy(array, mStorage, where); } - if (mCount > where+amount) { + if (where != new_size) { const void* from = reinterpret_cast(mStorage) + (where+amount)*mItemSize; void* dest = reinterpret_cast(array) + where*mItemSize; - _do_copy(dest, from, mCount-(where+amount)); + _do_copy(dest, from, new_size - where); } release_storage(); mStorage = const_cast(array); } } } else { - void* array = editArrayImpl(); + void* array = editArrayImpl(); void* to = reinterpret_cast(array) + where*mItemSize; _do_destroy(to, amount); - ssize_t s = mCount-(where+amount); - if (s>0) { + if (where != new_size) { const void* from = reinterpret_cast(array) + (where+amount)*mItemSize; - _do_move_backward(to, from, s); + _do_move_backward(to, from, new_size - where); } } - - // adjust the number of items... - mCount -= amount; + mCount = new_size; } size_t VectorImpl::itemSize() const { From d8734d19ec3a24f62b5c61af0dd20ec19dc145ec Mon Sep 17 00:00:00 2001 From: Le-Chun Wu Date: Thu, 14 Jul 2011 14:27:18 -0700 Subject: [PATCH 354/541] Add a call to pthread_attr_destroy to avoid potential memory leaks. Change-Id: Ib57efc3530e9793298190cc9cab19c9af54e11a7 --- libs/utils/Threads.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp index 6d5067b51..02c380b0e 100644 --- a/libs/utils/Threads.cpp +++ b/libs/utils/Threads.cpp @@ -161,6 +161,7 @@ int androidCreateRawThreadEtc(android_thread_func_t entryFunction, pthread_t thread; int result = pthread_create(&thread, &attr, (android_pthread_entry)entryFunction, userData); + pthread_attr_destroy(&attr); if (result != 0) { LOGE("androidCreateRawThreadEtc failed (entry=%p, res=%d, errno=%d)\n" "(android threadPriority=%d)", From 7e01e60e5ee3f833d3017b575d5c449a5068148b Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Wed, 20 Jul 2011 15:19:50 -0700 Subject: [PATCH 355/541] Remove unimplemented memory pool. dlmalloc does such a great job that we don't need a pool anyways. Change-Id: I105b28c4a5c491543959190c7c0a9de75a5b8052 --- include/utils/Pool.h | 71 ------------------------------------------- libs/utils/Android.mk | 1 - libs/utils/Pool.cpp | 37 ---------------------- 3 files changed, 109 deletions(-) delete mode 100644 include/utils/Pool.h delete mode 100644 libs/utils/Pool.cpp diff --git a/include/utils/Pool.h b/include/utils/Pool.h deleted file mode 100644 index 2ee768eef..000000000 --- a/include/utils/Pool.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (C) 2010 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 UTILS_POOL_H -#define UTILS_POOL_H - -#include - -namespace android { - -class PoolImpl { -public: - PoolImpl(size_t objSize); - ~PoolImpl(); - - void* allocImpl(); - void freeImpl(void* obj); - -private: - size_t mObjSize; -}; - -/* - * A homogeneous typed memory pool for fixed size objects. - * Not intended to be thread-safe. - */ -template -class Pool : private PoolImpl { -public: - /* Creates an initially empty pool. */ - Pool() : PoolImpl(sizeof(T)) { } - - /* Destroys the pool. - * Assumes that the pool is empty. */ - ~Pool() { } - - /* Allocates an object from the pool, growing the pool if needed. */ - inline T* alloc() { - void* mem = allocImpl(); - if (! traits::has_trivial_ctor) { - return new (mem) T(); - } else { - return static_cast(mem); - } - } - - /* Frees an object from the pool. */ - inline void free(T* obj) { - if (! traits::has_trivial_dtor) { - obj->~T(); - } - freeImpl(obj); - } -}; - -} // namespace android - -#endif // UTILS_POOL_H diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk index f6333576a..e4eadbd00 100644 --- a/libs/utils/Android.mk +++ b/libs/utils/Android.mk @@ -29,7 +29,6 @@ commonSources:= \ Flattenable.cpp \ LinearTransform.cpp \ ObbFile.cpp \ - Pool.cpp \ PropertyMap.cpp \ RefBase.cpp \ ResourceTypes.cpp \ diff --git a/libs/utils/Pool.cpp b/libs/utils/Pool.cpp deleted file mode 100644 index 8f18cb913..000000000 --- a/libs/utils/Pool.cpp +++ /dev/null @@ -1,37 +0,0 @@ -// -// Copyright 2010 The Android Open Source Project -// -// A simple memory pool. -// -#define LOG_TAG "Pool" - -//#define LOG_NDEBUG 0 - -#include -#include - -#include - -namespace android { - -// TODO Provide a real implementation of a pool. This is just a stub for initial development. - -PoolImpl::PoolImpl(size_t objSize) : - mObjSize(objSize) { -} - -PoolImpl::~PoolImpl() { -} - -void* PoolImpl::allocImpl() { - void* ptr = malloc(mObjSize); - LOG_ALWAYS_FATAL_IF(ptr == NULL, "Cannot allocate new pool object."); - return ptr; -} - -void PoolImpl::freeImpl(void* obj) { - LOG_ALWAYS_FATAL_IF(obj == NULL, "Caller attempted to free NULL pool object."); - return free(obj); -} - -} // namespace android From 88761b2796cf7b57be99d8141dae4a7e78ab5494 Mon Sep 17 00:00:00 2001 From: Kenny Root Date: Tue, 12 Jul 2011 14:14:01 -0700 Subject: [PATCH 356/541] Move extract native libraries to JNI code The built-in ZipFile class was quite a long time to find an unpack libraries. Move everything to using the libutils ZipFileRO class that goes quite a bit faster. Initial measurements are 6 times faster than the Java code. Also, read files off the disk and compare their CRC against the APK's CRC to see if we need to write the new file to disk. This also cuts down the bootup time by up to a second per APK that has native files. Change-Id: Ic464a7969a17368fb6a6b81d026888c4136c7603 --- include/utils/ZipFileRO.h | 15 +++++++ libs/utils/tests/Android.mk | 3 +- libs/utils/tests/ZipFileRO_test.cpp | 64 +++++++++++++++++++++++++++++ 3 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 libs/utils/tests/ZipFileRO_test.cpp diff --git a/include/utils/ZipFileRO.h b/include/utils/ZipFileRO.h index 3a999797b..547e36a09 100644 --- a/include/utils/ZipFileRO.h +++ b/include/utils/ZipFileRO.h @@ -38,6 +38,7 @@ #include #include #include +#include namespace android { @@ -173,6 +174,20 @@ public: static bool inflateBuffer(int fd, const void* inBuf, size_t uncompLen, size_t compLen); + /* + * Utility function to convert ZIP's time format to a timespec struct. + */ + static inline void zipTimeToTimespec(long when, struct tm* timespec) { + const long date = when >> 16; + timespec->tm_year = ((date >> 9) & 0x7F) + 80; // Zip is years since 1980 + timespec->tm_mon = (date >> 5) & 0x0F; + timespec->tm_mday = date & 0x1F; + + timespec->tm_hour = (when >> 11) & 0x1F; + timespec->tm_min = (when >> 5) & 0x3F; + timespec->tm_sec = (when & 0x1F) << 1; + } + /* * Some basic functions for raw data manipulation. "LE" means * Little Endian. diff --git a/libs/utils/tests/Android.mk b/libs/utils/tests/Android.mk index 8726a536c..b97f52f5b 100644 --- a/libs/utils/tests/Android.mk +++ b/libs/utils/tests/Android.mk @@ -8,7 +8,8 @@ test_src_files := \ ObbFile_test.cpp \ Looper_test.cpp \ String8_test.cpp \ - Unicode_test.cpp + Unicode_test.cpp \ + ZipFileRO_test.cpp \ shared_libraries := \ libz \ diff --git a/libs/utils/tests/ZipFileRO_test.cpp b/libs/utils/tests/ZipFileRO_test.cpp new file mode 100644 index 000000000..7a1d0bd95 --- /dev/null +++ b/libs/utils/tests/ZipFileRO_test.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2011 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 "ZipFileRO_test" +#include +#include + +#include + +#include +#include + +namespace android { + +class ZipFileROTest : public testing::Test { +protected: + virtual void SetUp() { + } + + virtual void TearDown() { + } +}; + +TEST_F(ZipFileROTest, ZipTimeConvertSuccess) { + struct tm t; + + // 2011-06-29 14:40:40 + long when = 0x3EDD7514; + + ZipFileRO::zipTimeToTimespec(when, &t); + + EXPECT_EQ(2011, t.tm_year + 1900) + << "Year was improperly converted."; + + EXPECT_EQ(6, t.tm_mon) + << "Month was improperly converted."; + + EXPECT_EQ(29, t.tm_mday) + << "Day was improperly converted."; + + EXPECT_EQ(14, t.tm_hour) + << "Hour was improperly converted."; + + EXPECT_EQ(40, t.tm_min) + << "Minute was improperly converted."; + + EXPECT_EQ(40, t.tm_sec) + << "Second was improperly converted."; +} + +} From 4ccb2fc8e129236f2832fc18b1ba0f8bbac5abbc Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Wed, 27 Jul 2011 16:04:54 -0700 Subject: [PATCH 357/541] Refactor input reader to add stylus support. Bug: 5064702 Introduced the concept of an InputListener to further decouple the InputReader from the InputDispatcher. The InputListener exposes just the minimum interface that the InputReader needs to communicate with the outside world. The InputReader passes arguments to the InputListener by reference, which makes it easy to queue them up. Consolidated all of the InputReader locks into one simple global Mutex. The reason this wasn't done before was due to potential re-entrance in outbound calls to the InputDispatcher. To fix this, the InputReader now queues up all of the events it wants to send using a QueuedInputListener, then flushes them outside of the critical section after all of the event processing is finished. Removing all of the InputMapper locks greatly simplifies the implementation. Added tests for new stylus features such as buttons, tool types, and hovering. Added some helpers to BitSet32 to handle common code patterns like finding the first marked bit and clearing it. Fixed a bug in VelocityTracker where the wrong pointer trace could get cleared when handling ACTION_POINTER_DOWN. Oops. Changed PointerCoords so it no longer stores useless zero axis values. Removed editAxisValue because it is not very useful when all zero value axes are absent and therefore cannot be edited in place. Added dispatch of stylus hover events. Added support for distance and tool types. Change-Id: I4cf14d134fcb1db7d10be5f2af7b37deef8f8468 --- include/utils/BitSet.h | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/include/utils/BitSet.h b/include/utils/BitSet.h index 600017e89..9452e86d6 100644 --- a/include/utils/BitSet.h +++ b/include/utils/BitSet.h @@ -68,6 +68,30 @@ struct BitSet32 { // Result is undefined if all bits are unmarked. inline uint32_t lastMarkedBit() const { return 31 - __builtin_ctz(value); } + // Finds the first marked bit in the set and clears it. Returns the bit index. + // Result is undefined if all bits are unmarked. + inline uint32_t clearFirstMarkedBit() { + uint32_t n = firstMarkedBit(); + clearBit(n); + return n; + } + + // Finds the first unmarked bit in the set and marks it. Returns the bit index. + // Result is undefined if all bits are marked. + inline uint32_t markFirstUnmarkedBit() { + uint32_t n = firstUnmarkedBit(); + markBit(n); + return n; + } + + // Finds the last marked bit in the set and clears it. Returns the bit index. + // Result is undefined if all bits are unmarked. + inline uint32_t clearLastMarkedBit() { + uint32_t n = lastMarkedBit(); + clearBit(n); + return n; + } + // Gets the index of the specified bit in the set, which is the number of // marked bits that appear before the specified bit. inline uint32_t getIndexOfBit(uint32_t n) const { From c3a5225d4ec0e98ca671ada8cba17878c656e3d5 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Tue, 9 Aug 2011 18:57:37 -0700 Subject: [PATCH 358/541] Fix typos and use new HAL_PRIORITY_URGENT_DISPLAY constant Change-Id: I703f8a96dd0dfec427e74363ad95729bfefc95c7 --- include/utils/threads.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/include/utils/threads.h b/include/utils/threads.h index 79e02ebb3..c68562505 100644 --- a/include/utils/threads.h +++ b/include/utils/threads.h @@ -20,6 +20,7 @@ #include #include #include +#include #if defined(HAVE_PTHREADS) # include @@ -42,8 +43,8 @@ enum { * ** Keep in sync with android.os.Process.java ** * *********************************************** * - * This maps directly to the "nice" priorites we use in Android. - * A thread priority should be chosen inverse-proportinally to + * This maps directly to the "nice" priorities we use in Android. + * A thread priority should be chosen inverse-proportionally to * the amount of work the thread is expected to do. The more work * a thread will do, the less favorable priority it should get so that * it doesn't starve the system. Threads not behaving properly might @@ -66,7 +67,7 @@ enum { ANDROID_PRIORITY_DISPLAY = -4, /* ui service treads might want to run at a urgent display (uncommon) */ - ANDROID_PRIORITY_URGENT_DISPLAY = -8, + ANDROID_PRIORITY_URGENT_DISPLAY = HAL_PRIORITY_URGENT_DISPLAY, /* all normal audio threads */ ANDROID_PRIORITY_AUDIO = -16, From ad09965050f8226fda6f5238db060ce65abaa71c Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Wed, 10 Aug 2011 21:07:02 -0700 Subject: [PATCH 359/541] fix a memory leak and memory corruption in RefBase we would leak a weakref_impl if a RefBase was never incWeak()'ed. there was also a dangling pointer that would cause memory corruption and double-delete when a custom destroyer was used to delay the execution of ~RefBase. it turns out that the custom destroyer feature caused most of the problems, so it's now gone. The only client was SurfaceFlinger who now handles things on its own. RefBase is essentially back its "gingerbread" state, but the code was slightly cleaned-up. Bug: 5151207, 5084978 Change-Id: Id6ef1d707f96d96366f75068f77b30e0ce2722a5 --- include/utils/RefBase.h | 10 +- libs/utils/RefBase.cpp | 264 +++++++++++++++++++++++++++------------- 2 files changed, 187 insertions(+), 87 deletions(-) diff --git a/include/utils/RefBase.h b/include/utils/RefBase.h index e81cd0063..51eff5ac9 100644 --- a/include/utils/RefBase.h +++ b/include/utils/RefBase.h @@ -80,9 +80,12 @@ public: void incWeak(const void* id); void decWeak(const void* id); + // acquires a strong reference if there is already one. bool attemptIncStrong(const void* id); - //! This is only safe if you have set OBJECT_LIFETIME_FOREVER. + // acquires a weak reference if there is already one. + // This is not always safe. see ProcessState.cpp and BpBinder.cpp + // for proper use. bool attemptIncWeak(const void* id); //! DEBUGGING ONLY: Get current weak ref count. @@ -122,8 +125,9 @@ protected: //! Flags for extendObjectLifetime() enum { + OBJECT_LIFETIME_STRONG = 0x0000, OBJECT_LIFETIME_WEAK = 0x0001, - OBJECT_LIFETIME_FOREVER = 0x0003 + OBJECT_LIFETIME_MASK = 0x0001 }; void extendObjectLifetime(int32_t mode); @@ -149,7 +153,7 @@ private: RefBase(const RefBase& o); RefBase& operator=(const RefBase& o); - + weakref_impl* const mRefs; }; diff --git a/libs/utils/RefBase.cpp b/libs/utils/RefBase.cpp index 32e900a7f..37d061cb3 100644 --- a/libs/utils/RefBase.cpp +++ b/libs/utils/RefBase.cpp @@ -20,9 +20,9 @@ #include #include -#include #include #include +#include #include #include @@ -34,6 +34,7 @@ // compile with refcounting debugging enabled #define DEBUG_REFS 0 +#define DEBUG_REFS_FATAL_SANITY_CHECKS 0 #define DEBUG_REFS_ENABLED_BY_DEFAULT 1 #define DEBUG_REFS_CALLSTACK_ENABLED 1 @@ -56,7 +57,6 @@ public: RefBase* const mBase; volatile int32_t mFlags; - #if !DEBUG_REFS weakref_impl(RefBase* base) @@ -69,8 +69,10 @@ public: void addStrongRef(const void* /*id*/) { } void removeStrongRef(const void* /*id*/) { } + void renameStrongRefId(const void* /*old_id*/, const void* /*new_id*/) { } void addWeakRef(const void* /*id*/) { } void removeWeakRef(const void* /*id*/) { } + void renameWeakRefId(const void* /*old_id*/, const void* /*new_id*/) { } void printRefs() const { } void trackMe(bool, bool) { } @@ -86,39 +88,91 @@ public: , mTrackEnabled(!!DEBUG_REFS_ENABLED_BY_DEFAULT) , mRetain(false) { - //LOGI("NEW weakref_impl %p for RefBase %p", this, base); } ~weakref_impl() { - LOG_ALWAYS_FATAL_IF(!mRetain && mStrongRefs != NULL, "Strong references remain!"); - LOG_ALWAYS_FATAL_IF(!mRetain && mWeakRefs != NULL, "Weak references remain!"); + bool dumpStack = false; + if (!mRetain && mStrongRefs != NULL) { + dumpStack = true; +#if DEBUG_REFS_FATAL_SANITY_CHECKS + LOG_ALWAYS_FATAL("Strong references remain!"); +#else + LOGE("Strong references remain:"); +#endif + ref_entry* refs = mStrongRefs; + while (refs) { + char inc = refs->ref >= 0 ? '+' : '-'; + LOGD("\t%c ID %p (ref %d):", inc, refs->id, refs->ref); +#if DEBUG_REFS_CALLSTACK_ENABLED + refs->stack.dump(); +#endif + refs = refs->next; + } + } + + if (!mRetain && mWeakRefs != NULL) { + dumpStack = true; +#if DEBUG_REFS_FATAL_SANITY_CHECKS + LOG_ALWAYS_FATAL("Weak references remain:"); +#else + LOGE("Weak references remain!"); +#endif + ref_entry* refs = mWeakRefs; + while (refs) { + char inc = refs->ref >= 0 ? '+' : '-'; + LOGD("\t%c ID %p (ref %d):", inc, refs->id, refs->ref); +#if DEBUG_REFS_CALLSTACK_ENABLED + refs->stack.dump(); +#endif + refs = refs->next; + } + } + if (dumpStack) { + LOGE("above errors at:"); + CallStack stack; + stack.update(); + stack.dump(); + } } - void addStrongRef(const void* id) - { + void addStrongRef(const void* id) { + //LOGD_IF(mTrackEnabled, + // "addStrongRef: RefBase=%p, id=%p", mBase, id); addRef(&mStrongRefs, id, mStrong); } - void removeStrongRef(const void* id) - { - if (!mRetain) + void removeStrongRef(const void* id) { + //LOGD_IF(mTrackEnabled, + // "removeStrongRef: RefBase=%p, id=%p", mBase, id); + if (!mRetain) { removeRef(&mStrongRefs, id); - else + } else { addRef(&mStrongRefs, id, -mStrong); + } } - void addWeakRef(const void* id) - { + void renameStrongRefId(const void* old_id, const void* new_id) { + //LOGD_IF(mTrackEnabled, + // "renameStrongRefId: RefBase=%p, oid=%p, nid=%p", + // mBase, old_id, new_id); + renameRefsId(mStrongRefs, old_id, new_id); + } + + void addWeakRef(const void* id) { addRef(&mWeakRefs, id, mWeak); } - void removeWeakRef(const void* id) - { - if (!mRetain) + void removeWeakRef(const void* id) { + if (!mRetain) { removeRef(&mWeakRefs, id); - else + } else { addRef(&mWeakRefs, id, -mWeak); + } + } + + void renameWeakRefId(const void* old_id, const void* new_id) { + renameRefsId(mWeakRefs, old_id, new_id); } void trackMe(bool track, bool retain) @@ -132,8 +186,7 @@ public: String8 text; { - AutoMutex _l(const_cast(this)->mMutex); - + Mutex::Autolock _l(mMutex); char buf[128]; sprintf(buf, "Strong references on RefBase %p (weakref_type %p):\n", mBase, this); text.append(buf); @@ -172,6 +225,7 @@ private: { if (mTrackEnabled) { AutoMutex _l(mMutex); + ref_entry* ref = new ref_entry; // Reference count at the time of the snapshot, but before the // update. Positive value means we increment, negative--we @@ -181,7 +235,6 @@ private: #if DEBUG_REFS_CALLSTACK_ENABLED ref->stack.update(2); #endif - ref->next = *refs; *refs = ref; } @@ -192,20 +245,52 @@ private: if (mTrackEnabled) { AutoMutex _l(mMutex); - ref_entry* ref = *refs; + ref_entry* const head = *refs; + ref_entry* ref = head; while (ref != NULL) { if (ref->id == id) { *refs = ref->next; delete ref; return; } - refs = &ref->next; ref = *refs; } - - LOG_ALWAYS_FATAL("RefBase: removing id %p on RefBase %p (weakref_type %p) that doesn't exist!", - id, mBase, this); + +#if DEBUG_REFS_FATAL_SANITY_CHECKS + LOG_ALWAYS_FATAL("RefBase: removing id %p on RefBase %p" + "(weakref_type %p) that doesn't exist!", + id, mBase, this); +#endif + + LOGE("RefBase: removing id %p on RefBase %p" + "(weakref_type %p) that doesn't exist!", + id, mBase, this); + + ref = head; + while (ref) { + char inc = ref->ref >= 0 ? '+' : '-'; + LOGD("\t%c ID %p (ref %d):", inc, ref->id, ref->ref); + ref = ref->next; + } + + CallStack stack; + stack.update(); + stack.dump(); + } + } + + void renameRefsId(ref_entry* r, const void* old_id, const void* new_id) + { + if (mTrackEnabled) { + AutoMutex _l(mMutex); + ref_entry* ref = r; + while (ref != NULL) { + if (ref->id == old_id) { + ref->id = new_id; + } + ref = ref->next; + } } } @@ -226,7 +311,7 @@ private: } } - Mutex mMutex; + mutable Mutex mMutex; ref_entry* mStrongRefs; ref_entry* mWeakRefs; @@ -235,44 +320,6 @@ private: // on removeref that match the address ones. bool mRetain; -#if 0 - void addRef(KeyedVector* refs, const void* id) - { - AutoMutex _l(mMutex); - ssize_t i = refs->indexOfKey(id); - if (i >= 0) { - ++(refs->editValueAt(i)); - } else { - i = refs->add(id, 1); - } - } - - void removeRef(KeyedVector* refs, const void* id) - { - AutoMutex _l(mMutex); - ssize_t i = refs->indexOfKey(id); - LOG_ALWAYS_FATAL_IF(i < 0, "RefBase: removing id %p that doesn't exist!", id); - if (i >= 0) { - int32_t val = --(refs->editValueAt(i)); - if (val == 0) { - refs->removeItemsAt(i); - } - } - } - - void printRefs(const KeyedVector& refs) - { - const size_t N=refs.size(); - for (size_t i=0; i mStrongRefs; - KeyedVector mWeakRefs; -#endif - #endif }; @@ -281,7 +328,6 @@ private: void RefBase::incStrong(const void* id) const { weakref_impl* const refs = mRefs; - refs->addWeakRef(id); refs->incWeak(id); refs->addStrongRef(id); @@ -295,7 +341,7 @@ void RefBase::incStrong(const void* id) const } android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong); - const_cast(this)->onFirstRef(); + refs->mBase->onFirstRef(); } void RefBase::decStrong(const void* id) const @@ -308,19 +354,17 @@ void RefBase::decStrong(const void* id) const #endif LOG_ASSERT(c >= 1, "decStrong() called on %p too many times", refs); if (c == 1) { - const_cast(this)->onLastStrongRef(id); - if ((refs->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) { + refs->mBase->onLastStrongRef(id); + if ((refs->mFlags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_STRONG) { delete this; } } - refs->removeWeakRef(id); refs->decWeak(id); } void RefBase::forceIncStrong(const void* id) const { weakref_impl* const refs = mRefs; - refs->addWeakRef(id); refs->incWeak(id); refs->addStrongRef(id); @@ -336,7 +380,7 @@ void RefBase::forceIncStrong(const void* id) const android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong); // fall through... case 0: - const_cast(this)->onFirstRef(); + refs->mBase->onFirstRef(); } } @@ -345,8 +389,6 @@ int32_t RefBase::getStrongCount() const return mRefs->mStrong; } - - RefBase* RefBase::weakref_type::refBase() const { return static_cast(this)->mBase; @@ -360,6 +402,7 @@ void RefBase::weakref_type::incWeak(const void* id) LOG_ASSERT(c >= 0, "incWeak called on %p after last weak ref", this); } + void RefBase::weakref_type::decWeak(const void* id) { weakref_impl* const impl = static_cast(this); @@ -367,17 +410,26 @@ void RefBase::weakref_type::decWeak(const void* id) const int32_t c = android_atomic_dec(&impl->mWeak); LOG_ASSERT(c >= 1, "decWeak called on %p too many times", this); if (c != 1) return; - - if ((impl->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) { - if (impl->mStrong == INITIAL_STRONG_VALUE) + + if ((impl->mFlags&OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_STRONG) { + // This is the regular lifetime case. The object is destroyed + // when the last strong reference goes away. Since weakref_impl + // outlive the object, it is not destroyed in the dtor, and + // we'll have to do it here. + if (impl->mStrong == INITIAL_STRONG_VALUE) { + // Special case: we never had a strong reference, so we need to + // destroy the object now. delete impl->mBase; - else { + } else { // LOGV("Freeing refs %p of old RefBase %p\n", this, impl->mBase); delete impl; } } else { + // less common case: lifetime is OBJECT_LIFETIME_{WEAK|FOREVER} impl->mBase->onLastWeakRef(id); - if ((impl->mFlags&OBJECT_LIFETIME_FOREVER) != OBJECT_LIFETIME_FOREVER) { + if ((impl->mFlags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_WEAK) { + // this is the OBJECT_LIFETIME_WEAK case. The last weak-reference + // is gone, we can destroy the object. delete impl->mBase; } } @@ -432,7 +484,6 @@ bool RefBase::weakref_type::attemptIncStrong(const void* id) } } - impl->addWeakRef(id); impl->addStrongRef(id); #if PRINT_REFS @@ -450,7 +501,7 @@ bool RefBase::weakref_type::attemptIncStrong(const void* id) bool RefBase::weakref_type::attemptIncWeak(const void* id) { weakref_impl* const impl = static_cast(this); - + int32_t curCount = impl->mWeak; LOG_ASSERT(curCount >= 0, "attemptIncWeak called on %p after underflow", this); @@ -480,7 +531,7 @@ void RefBase::weakref_type::printRefs() const void RefBase::weakref_type::trackMe(bool enable, bool retain) { - static_cast(this)->trackMe(enable, retain); + static_cast(this)->trackMe(enable, retain); } RefBase::weakref_type* RefBase::createWeak(const void* id) const @@ -497,14 +548,27 @@ RefBase::weakref_type* RefBase::getWeakRefs() const RefBase::RefBase() : mRefs(new weakref_impl(this)) { -// LOGV("Creating refs %p with RefBase %p\n", mRefs, this); } RefBase::~RefBase() { - if (mRefs->mWeak == 0) { + if (mRefs->mStrong == INITIAL_STRONG_VALUE) { + // we never acquired a strong (and/or weak) reference on this object. delete mRefs; + } else { + // life-time of this object is extended to WEAK or FOREVER, in + // which case weakref_impl doesn't out-live the object and we + // can free it now. + if ((mRefs->mFlags & OBJECT_LIFETIME_MASK) != OBJECT_LIFETIME_STRONG) { + // It's possible that the weak count is not 0 if the object + // re-acquired a weak reference in its destructor + if (mRefs->mWeak == 0) { + delete mRefs; + } + } } + // for debugging purposes, clear this. + const_cast(mRefs) = NULL; } void RefBase::extendObjectLifetime(int32_t mode) @@ -528,5 +592,37 @@ bool RefBase::onIncStrongAttempted(uint32_t flags, const void* id) void RefBase::onLastWeakRef(const void* /*id*/) { } - + +// --------------------------------------------------------------------------- + +void RefBase::moveReferences(void* dst, void const* src, size_t n, + const ReferenceConverterBase& caster) +{ +#if DEBUG_REFS + const size_t itemSize = caster.getReferenceTypeSize(); + for (size_t i=0 ; i(intptr_t(dst) + i*itemSize); + void const* s = reinterpret_cast(intptr_t(src) + i*itemSize); + RefBase* ref(reinterpret_cast(caster.getReferenceBase(d))); + ref->mRefs->renameStrongRefId(s, d); + ref->mRefs->renameWeakRefId(s, d); + } +#endif +} + +// --------------------------------------------------------------------------- + +TextOutput& printStrongPointer(TextOutput& to, const void* val) +{ + to << "sp<>(" << val << ")"; + return to; +} + +TextOutput& printWeakPointer(TextOutput& to, const void* val) +{ + to << "wp<>(" << val << ")"; + return to; +} + + }; // namespace android From 9ee3fc4dc3219bc68f2efdffa38d6dfc158d8961 Mon Sep 17 00:00:00 2001 From: Josiah Gaskin Date: Tue, 16 Aug 2011 15:16:04 -0700 Subject: [PATCH 360/541] Fix String16 operator+ to actually work This change fixes the String16 utility's operator+ function. Change-Id: Ibed7b07277b50bf178febc72b6ce497893e66f17 --- include/utils/String16.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/utils/String16.h b/include/utils/String16.h index 584f53f30..360f407c3 100644 --- a/include/utils/String16.h +++ b/include/utils/String16.h @@ -156,7 +156,7 @@ inline String16& String16::operator+=(const String16& other) inline String16 String16::operator+(const String16& other) const { - String16 tmp; + String16 tmp(*this); tmp += other; return tmp; } From f71114b6a4e350d389c6cdd314a4cbb970c71f5b Mon Sep 17 00:00:00 2001 From: Andy McFadden Date: Wed, 31 Aug 2011 07:43:40 -0700 Subject: [PATCH 361/541] Resurrect verbose binder logging Updated the command name lists, and masked off the additional bits in the command word when doing the name lookup. Made descriptor values easier to grep for and consistent with kernel output (i.e. decimal rather than hex). Attempt to show transaction descriptors as such (they're in a union with a pointer). Also, the writeLines() function in Static was using a no-op logging call to write an iovec. It looks like all callers are using N=1, so I just added a log for the first string. Bug 5155269 Change-Id: I417b8d77da3eb6ee1d2069ba94047210f75738bc --- libs/utils/Static.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libs/utils/Static.cpp b/libs/utils/Static.cpp index 4dfa57830..ceca43552 100644 --- a/libs/utils/Static.cpp +++ b/libs/utils/Static.cpp @@ -56,7 +56,9 @@ public: protected: virtual status_t writeLines(const struct iovec& vec, size_t N) { - android_writevLog(&vec, N); + //android_writevLog(&vec, N); <-- this is now a no-op + if (N != 1) LOGI("WARNING: writeLines N=%d\n", N); + LOGI("%.*s", vec.iov_len, (const char*) vec.iov_base); return NO_ERROR; } }; From 8ddbed9efdd879cee3553d7be2f534dc76c191d2 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Thu, 15 Sep 2011 12:21:40 -0700 Subject: [PATCH 362/541] A new API "androidGetThreadPriority" Change-Id: I6baeead8c70460863343fd557250635fb1e6a170 --- include/utils/threads.h | 4 ++++ libs/utils/Threads.cpp | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/include/utils/threads.h b/include/utils/threads.h index c68562505..ab3e8cdb6 100644 --- a/include/utils/threads.h +++ b/include/utils/threads.h @@ -143,6 +143,10 @@ extern int androidSetThreadSchedulingGroup(pid_t tid, int grp); // in either case errno is set. Thread ID zero means current thread. extern int androidSetThreadPriority(pid_t tid, int prio); +// Get the current priority of a particular thread. Returns one of the +// ANDROID_PRIORITY constants or a negative result in case of error. +extern int androidGetThreadPriority(pid_t tid); + // Get the current scheduling group of a particular thread. Normally returns // one of the ANDROID_TGROUP constants other than ANDROID_TGROUP_DEFAULT. // Returns ANDROID_TGROUP_DEFAULT if no pthread support (e.g. on host) or if diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp index 02c380b0e..38c4b35a7 100644 --- a/libs/utils/Threads.cpp +++ b/libs/utils/Threads.cpp @@ -368,6 +368,10 @@ int androidSetThreadPriority(pid_t tid, int pri) return rc; } +int androidGetThreadPriority(pid_t tid) { + return getpriority(PRIO_PROCESS, tid); +} + int androidGetThreadSchedulingGroup(pid_t tid) { int ret = ANDROID_TGROUP_DEFAULT; From 7b4ce614cf350e08680a71712c00dc152c3fa6cc Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Fri, 16 Sep 2011 11:47:13 -0700 Subject: [PATCH 363/541] Fix androidGetThreadPriority for non-pthread configurations. related-to-build Change-Id: Ic865af0865906f96fd615a56a030c8e3adaf13c4 --- libs/utils/Threads.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp index 38c4b35a7..5dbcb75b0 100644 --- a/libs/utils/Threads.cpp +++ b/libs/utils/Threads.cpp @@ -369,7 +369,11 @@ int androidSetThreadPriority(pid_t tid, int pri) } int androidGetThreadPriority(pid_t tid) { +#if defined(HAVE_PTHREADS) return getpriority(PRIO_PROCESS, tid); +#else + return ANDROID_PRIORITY_NORMAL; +#endif } int androidGetThreadSchedulingGroup(pid_t tid) From 9c63a80f1f39fb969ec439e6b51614abb883d370 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Wed, 28 Sep 2011 17:33:11 -0700 Subject: [PATCH 364/541] may fix the build Change-Id: I065b1e6652f0e800ba5674a6d9aa954c3ac6c790 --- libs/utils/Android.mk | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk index e4eadbd00..638f72f0b 100644 --- a/libs/utils/Android.mk +++ b/libs/utils/Android.mk @@ -100,12 +100,8 @@ LOCAL_LDLIBS += -lpthread LOCAL_SHARED_LIBRARIES := \ libz \ liblog \ - libcutils - -ifeq ($(TARGET_OS)-$(TARGET_ARCH),linux-x86) -# This is needed on x86 to bring in dl_iterate_phdr for CallStack.cpp -LOCAL_SHARED_LIBRARIES += libdl -endif # linux-x86 + libcutils \ + libdl LOCAL_MODULE:= libutils include $(BUILD_SHARED_LIBRARY) From 6c31a2fb6080049e444b4aac71b037eccd0d3ddc Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Wed, 28 Sep 2011 23:19:47 -0400 Subject: [PATCH 365/541] Add mechanism for Parcel to not allow FDs to be written to it. This is to help implement issue #5224703. Change-Id: I026a5890495537d15b57fe61227a640aac806d46 --- include/utils/Errors.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/utils/Errors.h b/include/utils/Errors.h index 81f818b75..0b75b1926 100644 --- a/include/utils/Errors.h +++ b/include/utils/Errors.h @@ -72,6 +72,7 @@ enum { TIMED_OUT = 0x80000005, UNKNOWN_TRANSACTION = 0x80000006, #endif + FDS_NOT_ALLOWED = 0x80000007, }; // Restore define; enumeration is in "android" namespace, so the value defined From aa983c91caf848b9659599336a4156c28a125fa9 Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Fri, 7 Oct 2011 13:28:18 -0700 Subject: [PATCH 366/541] Fix regression in CursorWindow.copyStingToBuffer. Bug: 5332296 Change-Id: Iff9eed786f0a8293b6156f883a66a322ddad5e99 --- include/utils/Unicode.h | 7 +++++++ libs/utils/Unicode.cpp | 13 +++++++------ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/include/utils/Unicode.h b/include/utils/Unicode.h index 6afb291f4..927353377 100644 --- a/include/utils/Unicode.h +++ b/include/utils/Unicode.h @@ -149,6 +149,13 @@ void utf8_to_utf32(const char* src, size_t src_len, char32_t* dst); */ ssize_t utf8_to_utf16_length(const uint8_t* src, size_t srcLen); +/** + * Convert UTF-8 to UTF-16 including surrogate pairs. + * Returns a pointer to the end of the string (where a null terminator might go + * if you wanted to add one). + */ +char16_t* utf8_to_utf16_no_null_terminator(const uint8_t* src, size_t srcLen, char16_t* dst); + /** * Convert UTF-8 to UTF-16 including surrogate pairs. The destination buffer * must be large enough to hold the result as measured by utf8_to_utf16_length diff --git a/libs/utils/Unicode.cpp b/libs/utils/Unicode.cpp index 78c61b4fc..41cbf035e 100644 --- a/libs/utils/Unicode.cpp +++ b/libs/utils/Unicode.cpp @@ -542,11 +542,7 @@ ssize_t utf8_to_utf16_length(const uint8_t* u8str, size_t u8len) return u16measuredLen; } -/** - * Convert a UTF-8 string to UTF-16. The destination UTF-16 buffer must have - * space for NULL at the end. - */ -void utf8_to_utf16(const uint8_t* u8str, size_t u8len, char16_t* u16str) +char16_t* utf8_to_utf16_no_null_terminator(const uint8_t* u8str, size_t u8len, char16_t* u16str) { const uint8_t* const u8end = u8str + u8len; const uint8_t* u8cur = u8str; @@ -569,7 +565,12 @@ void utf8_to_utf16(const uint8_t* u8str, size_t u8len, char16_t* u16str) u8cur += u8len; } - *u16cur = 0; + return u16cur; +} + +void utf8_to_utf16(const uint8_t* u8str, size_t u8len, char16_t* u16str) { + char16_t* end = utf8_to_utf16_no_null_terminator(u8str, u8len, u16str); + *end = 0; } } From 5cd324794ccadfefcdcde849fb6d682622773944 Mon Sep 17 00:00:00 2001 From: Romain Guy Date: Wed, 12 Oct 2011 13:48:51 -0700 Subject: [PATCH 367/541] Reduce the size of libhwui by 50% This change removes unnessary symbols. All symbols are hidden by default, public APIs with exported symbols are explicitly marked with ANDROID_API. Change-Id: I692fde432a86c12108de1cfd1f6504919a7d5f3f --- include/utils/Singleton.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/utils/Singleton.h b/include/utils/Singleton.h index e1ee8eb06..a42ce210d 100644 --- a/include/utils/Singleton.h +++ b/include/utils/Singleton.h @@ -20,12 +20,13 @@ #include #include #include +#include namespace android { // --------------------------------------------------------------------------- template -class Singleton +class ANDROID_API Singleton { public: static TYPE& getInstance() { From 26ad34522e85b4427ec0ae662dd43eb78a2c5f95 Mon Sep 17 00:00:00 2001 From: Iliyan Malchev Date: Wed, 19 Oct 2011 22:35:56 -0700 Subject: [PATCH 368/541] add -ldl to host executables This fixes the build on Linux when RefBase is compiled with reference tracking enabled. Change-Id: I3e931e0b463ce836f6fdb30c37068d77144631a3 Signed-off-by: Iliyan Malchev --- libs/utils/Android.mk | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk index 638f72f0b..831d9e378 100644 --- a/libs/utils/Android.mk +++ b/libs/utils/Android.mk @@ -71,6 +71,10 @@ LOCAL_CFLAGS += -DMB_CUR_MAX=1 endif endif +ifeq ($(TARGET_OS),linux) +LOCAL_LDLIBS += -lrt -ldl +endif + include $(BUILD_HOST_STATIC_LIBRARY) From ea45b01f9bc2d1ef1f8d97ca0480336d23e0aa97 Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Wed, 19 Oct 2011 20:32:43 -0700 Subject: [PATCH 369/541] Use libcorkscrew for stack unwinding. Change-Id: Iee1ee5a2018ab8cfc1ce12ec2a124809245eaa02 --- include/utils/CallStack.h | 8 +- libs/utils/Android.mk | 3 +- libs/utils/CallStack.cpp | 314 +++++++------------------------------- 3 files changed, 58 insertions(+), 267 deletions(-) diff --git a/include/utils/CallStack.h b/include/utils/CallStack.h index 8817120ef..079e20c69 100644 --- a/include/utils/CallStack.h +++ b/include/utils/CallStack.h @@ -21,6 +21,7 @@ #include #include +#include // --------------------------------------------------------------------------- @@ -61,11 +62,8 @@ public: size_t size() const { return mCount; } private: - // Internal helper function - String8 toStringSingleLevel(const char* prefix, int32_t level) const; - - size_t mCount; - const void* mStack[MAX_DEPTH]; + size_t mCount; + backtrace_frame_t mStack[MAX_DEPTH]; }; }; // namespace android diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk index 831d9e378..d168d190a 100644 --- a/libs/utils/Android.mk +++ b/libs/utils/Android.mk @@ -105,7 +105,8 @@ LOCAL_SHARED_LIBRARIES := \ libz \ liblog \ libcutils \ - libdl + libdl \ + libcorkscrew LOCAL_MODULE:= libutils include $(BUILD_SHARED_LIBRARY) diff --git a/libs/utils/CallStack.cpp b/libs/utils/CallStack.cpp index 55b6024f6..d79a75780 100644 --- a/libs/utils/CallStack.cpp +++ b/libs/utils/CallStack.cpp @@ -17,218 +17,33 @@ #define LOG_TAG "CallStack" #include -#include -#include - -#if HAVE_DLADDR -#include -#endif - -#if HAVE_CXXABI -#include -#endif - -#include #include #include #include -#include - +#include /*****************************************************************************/ namespace android { - -typedef struct { - size_t count; - size_t ignore; - const void** addrs; -} stack_crawl_state_t; - -static -_Unwind_Reason_Code trace_function(_Unwind_Context *context, void *arg) -{ - stack_crawl_state_t* state = (stack_crawl_state_t*)arg; - if (state->count) { - void* ip = (void*)_Unwind_GetIP(context); - if (ip) { - if (state->ignore) { - state->ignore--; - } else { - state->addrs[0] = ip; - state->addrs++; - state->count--; - } - } - } - return _URC_NO_REASON; +CallStack::CallStack() : + mCount(0) { } -static -int backtrace(const void** addrs, size_t ignore, size_t size) -{ - stack_crawl_state_t state; - state.count = size; - state.ignore = ignore; - state.addrs = addrs; - _Unwind_Backtrace(trace_function, (void*)&state); - return size - state.count; -} - -/*****************************************************************************/ - -static -const char *lookup_symbol(const void* addr, void **offset, char* name, size_t bufSize) -{ -#if HAVE_DLADDR - Dl_info info; - if (dladdr(addr, &info)) { - *offset = info.dli_saddr; - return info.dli_sname; - } -#endif - return NULL; -} - -static -int32_t linux_gcc_demangler(const char *mangled_name, char *unmangled_name, size_t buffersize) -{ - size_t out_len = 0; -#if HAVE_CXXABI - int status = 0; - char *demangled = abi::__cxa_demangle(mangled_name, 0, &out_len, &status); - if (status == 0) { - // OK - if (out_len < buffersize) memcpy(unmangled_name, demangled, out_len); - else out_len = 0; - free(demangled); - } else { - out_len = 0; - } -#endif - return out_len; -} - -/*****************************************************************************/ - -class MapInfo { - struct mapinfo { - struct mapinfo *next; - uint64_t start; - uint64_t end; - char name[]; - }; - - const char *map_to_name(uint64_t pc, const char* def, uint64_t* start) { - mapinfo* mi = getMapInfoList(); - while(mi) { - if ((pc >= mi->start) && (pc < mi->end)) { - if (start) - *start = mi->start; - return mi->name; - } - mi = mi->next; - } - if (start) - *start = 0; - return def; - } - - mapinfo *parse_maps_line(char *line) { - mapinfo *mi; - int len = strlen(line); - if (len < 1) return 0; - line[--len] = 0; - if (len < 50) return 0; - if (line[20] != 'x') return 0; - mi = (mapinfo*)malloc(sizeof(mapinfo) + (len - 47)); - if (mi == 0) return 0; - mi->start = strtoull(line, 0, 16); - mi->end = strtoull(line + 9, 0, 16); - mi->next = 0; - strcpy(mi->name, line + 49); - return mi; - } - - mapinfo* getMapInfoList() { - Mutex::Autolock _l(mLock); - if (milist == 0) { - char data[1024]; - FILE *fp; - sprintf(data, "/proc/%d/maps", getpid()); - fp = fopen(data, "r"); - if (fp) { - while(fgets(data, 1024, fp)) { - mapinfo *mi = parse_maps_line(data); - if(mi) { - mi->next = milist; - milist = mi; - } - } - fclose(fp); - } - } - return milist; - } - mapinfo* milist; - Mutex mLock; - static MapInfo sMapInfo; - -public: - MapInfo() - : milist(0) { - } - - ~MapInfo() { - while (milist) { - mapinfo *next = milist->next; - free(milist); - milist = next; - } - } - - static const char *mapAddressToName(const void* pc, const char* def, - void const** start) - { - uint64_t s; - char const* name = sMapInfo.map_to_name(uint64_t(uintptr_t(pc)), def, &s); - if (start) { - *start = (void*)s; - } - return name; - } - -}; - -/*****************************************************************************/ - -MapInfo MapInfo::sMapInfo; - -/*****************************************************************************/ - -CallStack::CallStack() - : mCount(0) -{ -} - -CallStack::CallStack(const CallStack& rhs) - : mCount(rhs.mCount) -{ +CallStack::CallStack(const CallStack& rhs) : + mCount(rhs.mCount) { if (mCount) { - memcpy(mStack, rhs.mStack, mCount*sizeof(void*)); + memcpy(mStack, rhs.mStack, mCount * sizeof(backtrace_frame_t)); } } -CallStack::~CallStack() -{ +CallStack::~CallStack() { } -CallStack& CallStack::operator = (const CallStack& rhs) -{ +CallStack& CallStack::operator = (const CallStack& rhs) { mCount = rhs.mCount; if (mCount) { - memcpy(mStack, rhs.mStack, mCount*sizeof(void*)); + memcpy(mStack, rhs.mStack, mCount * sizeof(backtrace_frame_t)); } return *this; } @@ -236,7 +51,7 @@ CallStack& CallStack::operator = (const CallStack& rhs) bool CallStack::operator == (const CallStack& rhs) const { if (mCount != rhs.mCount) return false; - return !mCount || (memcmp(mStack, rhs.mStack, mCount*sizeof(void*)) == 0); + return !mCount || memcmp(mStack, rhs.mStack, mCount * sizeof(backtrace_frame_t)) == 0; } bool CallStack::operator != (const CallStack& rhs) const { @@ -246,7 +61,7 @@ bool CallStack::operator != (const CallStack& rhs) const { bool CallStack::operator < (const CallStack& rhs) const { if (mCount != rhs.mCount) return mCount < rhs.mCount; - return memcmp(mStack, rhs.mStack, mCount*sizeof(void*)) < 0; + return memcmp(mStack, rhs.mStack, mCount * sizeof(backtrace_frame_t)) < 0; } bool CallStack::operator >= (const CallStack& rhs) const { @@ -256,7 +71,7 @@ bool CallStack::operator >= (const CallStack& rhs) const { bool CallStack::operator > (const CallStack& rhs) const { if (mCount != rhs.mCount) return mCount > rhs.mCount; - return memcmp(mStack, rhs.mStack, mCount*sizeof(void*)) > 0; + return memcmp(mStack, rhs.mStack, mCount * sizeof(backtrace_frame_t)) > 0; } bool CallStack::operator <= (const CallStack& rhs) const { @@ -266,84 +81,61 @@ bool CallStack::operator <= (const CallStack& rhs) const { const void* CallStack::operator [] (int index) const { if (index >= int(mCount)) return 0; - return mStack[index]; + return reinterpret_cast(mStack[index].absolute_pc); } - -void CallStack::clear() -{ +void CallStack::clear() { mCount = 0; } -void CallStack::update(int32_t ignoreDepth, int32_t maxDepth) -{ - if (maxDepth > MAX_DEPTH) +void CallStack::update(int32_t ignoreDepth, int32_t maxDepth) { + if (maxDepth > MAX_DEPTH) { maxDepth = MAX_DEPTH; - mCount = backtrace(mStack, ignoreDepth, maxDepth); -} - -// Return the stack frame name on the designated level -String8 CallStack::toStringSingleLevel(const char* prefix, int32_t level) const -{ - String8 res; - char namebuf[1024]; - char tmp[256]; - char tmp1[32]; - char tmp2[32]; - void *offs; - - const void* ip = mStack[level]; - if (!ip) return res; - - if (prefix) res.append(prefix); - snprintf(tmp1, 32, "#%02d ", level); - res.append(tmp1); - - const char* name = lookup_symbol(ip, &offs, namebuf, sizeof(namebuf)); - if (name) { - if (linux_gcc_demangler(name, tmp, 256) != 0) - name = tmp; - snprintf(tmp1, 32, "0x%p: <", ip); - snprintf(tmp2, 32, ">+0x%p", offs); - res.append(tmp1); - res.append(name); - res.append(tmp2); - } else { - void const* start = 0; - name = MapInfo::mapAddressToName(ip, "", &start); - snprintf(tmp, 256, "pc %08lx %s", - long(uintptr_t(ip)-uintptr_t(start)), name); - res.append(tmp); } - res.append("\n"); - - return res; + ssize_t count = unwind_backtrace(mStack, ignoreDepth + 1, maxDepth); + mCount = count > 0 ? count : 0; } -// Dump a stack trace to the log -void CallStack::dump(const char* prefix) const -{ - /* - * Sending a single long log may be truncated since the stack levels can - * get very deep. So we request function names of each frame individually. - */ - for (int i=0; iname : ""; + const char* symbolName = symbol.demangled_name ? symbol.demangled_name : symbol.name; + if (symbolName) { + LOGD("%s#%02d pc %08x %s (%s)\n", prefix, + int(i), uint32_t(symbol.relative_pc), mapName, symbolName); + } else { + LOGD("%s#%02d pc %08x %s\n", prefix, + int(i), uint32_t(symbol.relative_pc), mapName); + } } + free_backtrace_symbols(symbols, mCount); } -// Return a string (possibly very long) containing the complete stack trace -String8 CallStack::toString(const char* prefix) const -{ - String8 res; +String8 CallStack::toString(const char* prefix) const { + String8 str; + backtrace_symbol_t symbols[mCount]; - for (int i=0; iname : ""; + const char* symbolName = symbol.demangled_name ? symbol.demangled_name : symbol.name; + if (symbolName) { + str.appendFormat("%s#%02d pc %08x %s (%s)\n", prefix, + int(i), uint32_t(symbol.relative_pc), mapName, symbolName); + } else { + str.appendFormat("%s#%02d pc %08x %s\n", prefix, + int(i), uint32_t(symbol.relative_pc), mapName); + } } - - return res; + free_backtrace_symbols(symbols, mCount); + return str; } -/*****************************************************************************/ - }; // namespace android From 8b4cf779b29144d6d469afb4daabcbf069725db2 Mon Sep 17 00:00:00 2001 From: Steve Block Date: Wed, 12 Oct 2011 17:27:03 +0100 Subject: [PATCH 370/541] Rename (IF_)LOG() to (IF_)ALOG() DO NOT MERGE See https://android-git.corp.google.com/g/#/c/141576 Bug: 5449033 Change-Id: I42575e7c29cf1c0f465c357a5c97ab118df6f473 --- libs/utils/Threads.cpp | 8 ++++---- libs/utils/Timers.cpp | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp index 5dbcb75b0..fe4b8e62b 100644 --- a/libs/utils/Threads.cpp +++ b/libs/utils/Threads.cpp @@ -205,7 +205,7 @@ static __stdcall unsigned int threadIntermediary(void* vDetails) delete pDetails; - LOG(LOG_VERBOSE, "thread", "thread exiting\n"); + ALOG(LOG_VERBOSE, "thread", "thread exiting\n"); return (unsigned int) result; } @@ -232,7 +232,7 @@ static bool doCreateThread(android_thread_func_t fn, void* arg, android_thread_i if (hThread == NULL) #endif { - LOG(LOG_WARN, "thread", "WARNING: thread create failed\n"); + ALOG(LOG_WARN, "thread", "WARNING: thread create failed\n"); return false; } @@ -470,7 +470,7 @@ status_t Mutex::lock() void Mutex::unlock() { if (!ReleaseMutex((HANDLE) mState)) - LOG(LOG_WARN, "thread", "WARNING: bad result from unlocking mutex\n"); + ALOG(LOG_WARN, "thread", "WARNING: bad result from unlocking mutex\n"); } status_t Mutex::tryLock() @@ -479,7 +479,7 @@ status_t Mutex::tryLock() dwWaitResult = WaitForSingleObject((HANDLE) mState, 0); if (dwWaitResult != WAIT_OBJECT_0 && dwWaitResult != WAIT_TIMEOUT) - LOG(LOG_WARN, "thread", "WARNING: bad result from try-locking mutex\n"); + ALOG(LOG_WARN, "thread", "WARNING: bad result from try-locking mutex\n"); return (dwWaitResult == WAIT_OBJECT_0) ? 0 : -1; } diff --git a/libs/utils/Timers.cpp b/libs/utils/Timers.cpp index 64a29f587..64b470181 100644 --- a/libs/utils/Timers.cpp +++ b/libs/utils/Timers.cpp @@ -113,7 +113,7 @@ long long DurationTimer::durationUsecs(void) const /*static*/ void DurationTimer::addToTimeval(struct timeval* ptv, long usec) { if (usec < 0) { - LOG(LOG_WARN, "", "Negative values not supported in addToTimeval\n"); + ALOG(LOG_WARN, "", "Negative values not supported in addToTimeval\n"); return; } From b37fbe9f810545bf62468fea5958325caa3635be Mon Sep 17 00:00:00 2001 From: Steve Block Date: Thu, 20 Oct 2011 11:56:00 +0100 Subject: [PATCH 371/541] Rename (IF_)LOGV(_IF) to (IF_)ALOGV(_IF) DO NOT MERGE See https://android-git.corp.google.com/g/#/c/143865 Bug: 5449033 Change-Id: I0122812ed6ff6f5b59fe4a43ab8bff0577adde0a --- libs/utils/Asset.cpp | 10 +++--- libs/utils/AssetManager.cpp | 50 ++++++++++++++--------------- libs/utils/BlobCache.cpp | 24 +++++++------- libs/utils/FileMap.cpp | 2 +- libs/utils/RefBase.cpp | 2 +- libs/utils/ResourceTypes.cpp | 22 ++++++------- libs/utils/StreamingZipInflater.cpp | 8 ++--- libs/utils/VectorImpl.cpp | 8 ++--- libs/utils/ZipFileRO.cpp | 8 ++--- libs/utils/ZipUtils.cpp | 4 +-- 10 files changed, 69 insertions(+), 69 deletions(-) diff --git a/libs/utils/Asset.cpp b/libs/utils/Asset.cpp index a18294b18..7fd2c8731 100644 --- a/libs/utils/Asset.cpp +++ b/libs/utils/Asset.cpp @@ -585,7 +585,7 @@ const void* _FileAsset::getBuffer(bool wordAligned) return NULL; } - LOGV("Asset %p allocating buffer size %d (smaller than threshold)", this, (int)allocLen); + ALOGV("Asset %p allocating buffer size %d (smaller than threshold)", this, (int)allocLen); if (mLength > 0) { long oldPosn = ftell(mFp); fseek(mFp, mStart, SEEK_SET); @@ -597,7 +597,7 @@ const void* _FileAsset::getBuffer(bool wordAligned) fseek(mFp, oldPosn, SEEK_SET); } - LOGV(" getBuffer: loaded into buffer\n"); + ALOGV(" getBuffer: loaded into buffer\n"); mBuf = buf; return mBuf; @@ -610,7 +610,7 @@ const void* _FileAsset::getBuffer(bool wordAligned) return NULL; } - LOGV(" getBuffer: mapped\n"); + ALOGV(" getBuffer: mapped\n"); mMap = map; if (!wordAligned) { @@ -648,13 +648,13 @@ const void* _FileAsset::ensureAlignment(FileMap* map) if ((((size_t)data)&0x3) == 0) { // We can return this directly if it is aligned on a word // boundary. - LOGV("Returning aligned FileAsset %p (%s).", this, + ALOGV("Returning aligned FileAsset %p (%s).", this, getAssetSource()); return data; } // If not aligned on a word boundary, then we need to copy it into // our own buffer. - LOGV("Copying FileAsset %p (%s) to buffer size %d to make it aligned.", this, + ALOGV("Copying FileAsset %p (%s) to buffer size %d to make it aligned.", this, getAssetSource(), (int)mLength); unsigned char* buf = new unsigned char[mLength]; if (buf == NULL) { diff --git a/libs/utils/AssetManager.cpp b/libs/utils/AssetManager.cpp index 22034c593..203e6fa37 100644 --- a/libs/utils/AssetManager.cpp +++ b/libs/utils/AssetManager.cpp @@ -167,7 +167,7 @@ bool AssetManager::addAssetPath(const String8& path, void** cookie) } } - LOGV("In %p Asset %s path: %s", this, + ALOGV("In %p Asset %s path: %s", this, ap.type == kFileTypeDirectory ? "dir" : "zip", ap.path.string()); mAssetPaths.add(ap); @@ -498,7 +498,7 @@ Asset* AssetManager::open(const char* fileName, AccessMode mode) size_t i = mAssetPaths.size(); while (i > 0) { i--; - LOGV("Looking for asset '%s' in '%s'\n", + ALOGV("Looking for asset '%s' in '%s'\n", assetName.string(), mAssetPaths.itemAt(i).path.string()); Asset* pAsset = openNonAssetInPathLocked(assetName.string(), mode, mAssetPaths.itemAt(i)); if (pAsset != NULL) { @@ -532,7 +532,7 @@ Asset* AssetManager::openNonAsset(const char* fileName, AccessMode mode) size_t i = mAssetPaths.size(); while (i > 0) { i--; - LOGV("Looking for non-asset '%s' in '%s'\n", fileName, mAssetPaths.itemAt(i).path.string()); + ALOGV("Looking for non-asset '%s' in '%s'\n", fileName, mAssetPaths.itemAt(i).path.string()); Asset* pAsset = openNonAssetInPathLocked( fileName, mode, mAssetPaths.itemAt(i)); if (pAsset != NULL) { @@ -556,7 +556,7 @@ Asset* AssetManager::openNonAsset(void* cookie, const char* fileName, AccessMode loadFileNameCacheLocked(); if (which < mAssetPaths.size()) { - LOGV("Looking for non-asset '%s' in '%s'\n", fileName, + ALOGV("Looking for non-asset '%s' in '%s'\n", fileName, mAssetPaths.itemAt(which).path.string()); Asset* pAsset = openNonAssetInPathLocked( fileName, mode, mAssetPaths.itemAt(which)); @@ -621,7 +621,7 @@ const ResTable* AssetManager::getResTable(bool required) const bool shared = true; const asset_path& ap = mAssetPaths.itemAt(i); Asset* idmap = openIdmapLocked(ap); - LOGV("Looking for resource asset in '%s'\n", ap.path.string()); + ALOGV("Looking for resource asset in '%s'\n", ap.path.string()); if (ap.type != kFileTypeDirectory) { if (i == 0) { // The first item is typically the framework resources, @@ -633,7 +633,7 @@ const ResTable* AssetManager::getResTable(bool required) const ass = const_cast(this)-> mZipSet.getZipResourceTableAsset(ap.path); if (ass == NULL) { - LOGV("loading resource table %s\n", ap.path.string()); + ALOGV("loading resource table %s\n", ap.path.string()); ass = const_cast(this)-> openNonAssetInPathLocked("resources.arsc", Asset::ACCESS_BUFFER, @@ -648,7 +648,7 @@ const ResTable* AssetManager::getResTable(bool required) const // If this is the first resource table in the asset // manager, then we are going to cache it so that we // can quickly copy it out for others. - LOGV("Creating shared resources for %s", ap.path.string()); + ALOGV("Creating shared resources for %s", ap.path.string()); sharedRes = new ResTable(); sharedRes->add(ass, (void*)(i+1), false, idmap); sharedRes = const_cast(this)-> @@ -656,7 +656,7 @@ const ResTable* AssetManager::getResTable(bool required) const } } } else { - LOGV("loading resource table %s\n", ap.path.string()); + ALOGV("loading resource table %s\n", ap.path.string()); Asset* ass = const_cast(this)-> openNonAssetInPathLocked("resources.arsc", Asset::ACCESS_BUFFER, @@ -668,12 +668,12 @@ const ResTable* AssetManager::getResTable(bool required) const mResources = rt = new ResTable(); updateResourceParamsLocked(); } - LOGV("Installing resource asset %p in to table %p\n", ass, mResources); + ALOGV("Installing resource asset %p in to table %p\n", ass, mResources); if (sharedRes != NULL) { - LOGV("Copying existing resources for %s", ap.path.string()); + ALOGV("Copying existing resources for %s", ap.path.string()); rt->add(sharedRes); } else { - LOGV("Parsing resources for %s", ap.path.string()); + ALOGV("Parsing resources for %s", ap.path.string()); rt->add(ass, (void*)(i+1), !shared, idmap); } @@ -725,7 +725,7 @@ Asset* AssetManager::openIdmapLocked(const struct asset_path& ap) const ass = const_cast(this)-> openAssetFromFileLocked(ap.idmap, Asset::ACCESS_BUFFER); if (ass) { - LOGV("loading idmap %s\n", ap.idmap.string()); + ALOGV("loading idmap %s\n", ap.idmap.string()); } else { LOGW("failed to load idmap %s\n", ap.idmap.string()); } @@ -1019,7 +1019,7 @@ String8 AssetManager::createPathNameLocked(const asset_path& ap, const char* roo */ ZipFileRO* AssetManager::getZipFileLocked(const asset_path& ap) { - LOGV("getZipFileLocked() in %p\n", this); + ALOGV("getZipFileLocked() in %p\n", this); return mZipSet.getZip(ap.path); } @@ -1086,12 +1086,12 @@ Asset* AssetManager::openAssetFromZipLocked(const ZipFileRO* pZipFile, if (method == ZipFileRO::kCompressStored) { pAsset = Asset::createFromUncompressedMap(dataMap, mode); - LOGV("Opened uncompressed entry %s in zip %s mode %d: %p", entryName.string(), + ALOGV("Opened uncompressed entry %s in zip %s mode %d: %p", entryName.string(), dataMap->getFileName(), mode, pAsset); } else { pAsset = Asset::createFromCompressedMap(dataMap, method, uncompressedLen, mode); - LOGV("Opened compressed entry %s in zip %s mode %d: %p", entryName.string(), + ALOGV("Opened compressed entry %s in zip %s mode %d: %p", entryName.string(), dataMap->getFileName(), mode, pAsset); } if (pAsset == NULL) { @@ -1146,10 +1146,10 @@ AssetDir* AssetManager::openDir(const char* dirName) i--; const asset_path& ap = mAssetPaths.itemAt(i); if (ap.type == kFileTypeRegular) { - LOGV("Adding directory %s from zip %s", dirName, ap.path.string()); + ALOGV("Adding directory %s from zip %s", dirName, ap.path.string()); scanAndMergeZipLocked(pMergedInfo, ap, kAssetsRoot, dirName); } else { - LOGV("Adding directory %s from dir %s", dirName, ap.path.string()); + ALOGV("Adding directory %s from dir %s", dirName, ap.path.string()); scanAndMergeDirLocked(pMergedInfo, ap, kAssetsRoot, dirName); } } @@ -1200,10 +1200,10 @@ AssetDir* AssetManager::openNonAssetDir(void* cookie, const char* dirName) if (which < mAssetPaths.size()) { const asset_path& ap = mAssetPaths.itemAt(which); if (ap.type == kFileTypeRegular) { - LOGV("Adding directory %s from zip %s", dirName, ap.path.string()); + ALOGV("Adding directory %s from zip %s", dirName, ap.path.string()); scanAndMergeZipLocked(pMergedInfo, ap, NULL, dirName); } else { - LOGV("Adding directory %s from dir %s", dirName, ap.path.string()); + ALOGV("Adding directory %s from dir %s", dirName, ap.path.string()); scanAndMergeDirLocked(pMergedInfo, ap, NULL, dirName); } } @@ -1325,7 +1325,7 @@ bool AssetManager::scanAndMergeDirLocked(SortedVector* pMerg matchIdx = AssetDir::FileInfo::findEntry(pMergedInfo, match); if (matchIdx > 0) { - LOGV("Excluding '%s' [%s]\n", + ALOGV("Excluding '%s' [%s]\n", pMergedInfo->itemAt(matchIdx).getFileName().string(), pMergedInfo->itemAt(matchIdx).getSourceName().string()); pMergedInfo->removeAt(matchIdx); @@ -1365,7 +1365,7 @@ SortedVector* AssetManager::scanDirLocked(const String8& pat struct dirent* entry; FileType fileType; - LOGV("Scanning dir '%s'\n", path.string()); + ALOGV("Scanning dir '%s'\n", path.string()); dir = opendir(path.string()); if (dir == NULL) @@ -1782,7 +1782,7 @@ AssetManager::SharedZip::SharedZip(const String8& path, time_t modWhen) { //LOGI("Creating SharedZip %p %s\n", this, (const char*)mPath); mZipFile = new ZipFileRO; - LOGV("+++ opening zip '%s'\n", mPath.string()); + ALOGV("+++ opening zip '%s'\n", mPath.string()); if (mZipFile->open(mPath.string()) != NO_ERROR) { LOGD("failed to open Zip archive '%s'\n", mPath.string()); delete mZipFile; @@ -1811,7 +1811,7 @@ ZipFileRO* AssetManager::SharedZip::getZip() Asset* AssetManager::SharedZip::getResourceTableAsset() { - LOGV("Getting from SharedZip %p resource asset %p\n", this, mResourceTableAsset); + ALOGV("Getting from SharedZip %p resource asset %p\n", this, mResourceTableAsset); return mResourceTableAsset; } @@ -1833,7 +1833,7 @@ Asset* AssetManager::SharedZip::setResourceTableAsset(Asset* asset) ResTable* AssetManager::SharedZip::getResourceTable() { - LOGV("Getting from SharedZip %p resource table %p\n", this, mResourceTable); + ALOGV("Getting from SharedZip %p resource table %p\n", this, mResourceTable); return mResourceTable; } @@ -1867,7 +1867,7 @@ AssetManager::SharedZip::~SharedZip() } if (mZipFile != NULL) { delete mZipFile; - LOGV("Closed '%s'\n", mPath.string()); + ALOGV("Closed '%s'\n", mPath.string()); } } diff --git a/libs/utils/BlobCache.cpp b/libs/utils/BlobCache.cpp index 590576a8d..4039807d8 100644 --- a/libs/utils/BlobCache.cpp +++ b/libs/utils/BlobCache.cpp @@ -38,23 +38,23 @@ BlobCache::BlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxTotalSize mRandState[1] = (now >> 16) & 0xFFFF; mRandState[2] = (now >> 32) & 0xFFFF; #endif - LOGV("initializing random seed using %lld", now); + ALOGV("initializing random seed using %lld", now); } void BlobCache::set(const void* key, size_t keySize, const void* value, size_t valueSize) { if (mMaxKeySize < keySize) { - LOGV("set: not caching because the key is too large: %d (limit: %d)", + ALOGV("set: not caching because the key is too large: %d (limit: %d)", keySize, mMaxKeySize); return; } if (mMaxValueSize < valueSize) { - LOGV("set: not caching because the value is too large: %d (limit: %d)", + ALOGV("set: not caching because the value is too large: %d (limit: %d)", valueSize, mMaxValueSize); return; } if (mMaxTotalSize < keySize + valueSize) { - LOGV("set: not caching because the combined key/value size is too " + ALOGV("set: not caching because the combined key/value size is too " "large: %d (limit: %d)", keySize + valueSize, mMaxTotalSize); return; } @@ -85,7 +85,7 @@ void BlobCache::set(const void* key, size_t keySize, const void* value, clean(); continue; } else { - LOGV("set: not caching new key/value pair because the " + ALOGV("set: not caching new key/value pair because the " "total cache size limit would be exceeded: %d " "(limit: %d)", keySize + valueSize, mMaxTotalSize); @@ -94,7 +94,7 @@ void BlobCache::set(const void* key, size_t keySize, const void* value, } mCacheEntries.add(CacheEntry(keyBlob, valueBlob)); mTotalSize = newTotalSize; - LOGV("set: created new cache entry with %d byte key and %d byte value", + ALOGV("set: created new cache entry with %d byte key and %d byte value", keySize, valueSize); } else { // Update the existing cache entry. @@ -107,7 +107,7 @@ void BlobCache::set(const void* key, size_t keySize, const void* value, clean(); continue; } else { - LOGV("set: not caching new value because the total cache " + ALOGV("set: not caching new value because the total cache " "size limit would be exceeded: %d (limit: %d)", keySize + valueSize, mMaxTotalSize); break; @@ -115,7 +115,7 @@ void BlobCache::set(const void* key, size_t keySize, const void* value, } mCacheEntries.editItemAt(index).setValue(valueBlob); mTotalSize = newTotalSize; - LOGV("set: updated existing cache entry with %d byte key and %d byte " + ALOGV("set: updated existing cache entry with %d byte key and %d byte " "value", keySize, valueSize); } break; @@ -125,7 +125,7 @@ void BlobCache::set(const void* key, size_t keySize, const void* value, size_t BlobCache::get(const void* key, size_t keySize, void* value, size_t valueSize) { if (mMaxKeySize < keySize) { - LOGV("get: not searching because the key is too large: %d (limit %d)", + ALOGV("get: not searching because the key is too large: %d (limit %d)", keySize, mMaxKeySize); return 0; } @@ -134,7 +134,7 @@ size_t BlobCache::get(const void* key, size_t keySize, void* value, CacheEntry dummyEntry(dummyKey, NULL); ssize_t index = mCacheEntries.indexOf(dummyEntry); if (index < 0) { - LOGV("get: no cache entry found for key of size %d", keySize); + ALOGV("get: no cache entry found for key of size %d", keySize); return 0; } @@ -143,10 +143,10 @@ size_t BlobCache::get(const void* key, size_t keySize, void* value, sp valueBlob(mCacheEntries[index].getValue()); size_t valueBlobSize = valueBlob->getSize(); if (valueBlobSize <= valueSize) { - LOGV("get: copying %d bytes to caller's buffer", valueBlobSize); + ALOGV("get: copying %d bytes to caller's buffer", valueBlobSize); memcpy(value, valueBlob->getData(), valueBlobSize); } else { - LOGV("get: caller's buffer is too small for value: %d (needs %d)", + ALOGV("get: caller's buffer is too small for value: %d (needs %d)", valueSize, valueBlobSize); } return valueBlobSize; diff --git a/libs/utils/FileMap.cpp b/libs/utils/FileMap.cpp index c220a9016..294f7b6b2 100644 --- a/libs/utils/FileMap.cpp +++ b/libs/utils/FileMap.cpp @@ -190,7 +190,7 @@ try_again: assert(mBasePtr != NULL); - LOGV("MAP: base %p/%d data %p/%d\n", + ALOGV("MAP: base %p/%d data %p/%d\n", mBasePtr, (int) mBaseLength, mDataPtr, (int) mDataLength); return true; diff --git a/libs/utils/RefBase.cpp b/libs/utils/RefBase.cpp index 37d061cb3..959b38235 100644 --- a/libs/utils/RefBase.cpp +++ b/libs/utils/RefBase.cpp @@ -421,7 +421,7 @@ void RefBase::weakref_type::decWeak(const void* id) // destroy the object now. delete impl->mBase; } else { - // LOGV("Freeing refs %p of old RefBase %p\n", this, impl->mBase); + // ALOGV("Freeing refs %p of old RefBase %p\n", this, impl->mBase); delete impl; } } else { diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp index 6cf01c8d6..6a9e91d2b 100644 --- a/libs/utils/ResourceTypes.cpp +++ b/libs/utils/ResourceTypes.cpp @@ -69,7 +69,7 @@ namespace android { static void printToLogFunc(void* cookie, const char* txt) { - LOGV("%s", txt); + ALOGV("%s", txt); } // Standard C isspace() is only required to look at the low byte of its input, so @@ -1867,7 +1867,7 @@ status_t ResTable::add(const void* data, size_t size, void* cookie, const bool notDeviceEndian = htods(0xf0) != 0xf0; LOAD_TABLE_NOISY( - LOGV("Adding resources to ResTable: data=%p, size=0x%x, cookie=%p, asset=%p, copy=%d " + ALOGV("Adding resources to ResTable: data=%p, size=0x%x, cookie=%p, asset=%p, copy=%d " "idmap=%p\n", data, size, cookie, asset, copyData, idmap)); if (copyData || notDeviceEndian) { @@ -2122,7 +2122,7 @@ ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag resID, &overlayResID); if (retval == NO_ERROR && overlayResID != 0x0) { // for this loop iteration, this is the type and entry we really want - LOGV("resource map 0x%08x -> 0x%08x\n", resID, overlayResID); + ALOGV("resource map 0x%08x -> 0x%08x\n", resID, overlayResID); T = Res_GETTYPE(overlayResID); E = Res_GETENTRY(overlayResID); } else { @@ -2401,7 +2401,7 @@ ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag, resID, &overlayResID); if (retval == NO_ERROR && overlayResID != 0x0) { // for this loop iteration, this is the type and entry we really want - LOGV("resource map 0x%08x -> 0x%08x\n", resID, overlayResID); + ALOGV("resource map 0x%08x -> 0x%08x\n", resID, overlayResID); T = Res_GETTYPE(overlayResID); E = Res_GETENTRY(overlayResID); } else { @@ -2413,9 +2413,9 @@ ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag, const ResTable_type* type; const ResTable_entry* entry; const Type* typeClass; - LOGV("Getting entry pkg=%p, t=%d, e=%d\n", package, T, E); + ALOGV("Getting entry pkg=%p, t=%d, e=%d\n", package, T, E); ssize_t offset = getEntry(package, T, E, &mParams, &type, &entry, &typeClass); - LOGV("Resulting offset=%d\n", offset); + ALOGV("Resulting offset=%d\n", offset); if (offset <= 0) { // No {entry, appropriate config} pair found in package. If this // package is an overlay package (ip != 0), this simply means the @@ -3898,9 +3898,9 @@ void ResTable::getConfigurations(Vector* configs) const void ResTable::getLocales(Vector* locales) const { Vector configs; - LOGV("calling getConfigurations"); + ALOGV("calling getConfigurations"); getConfigurations(&configs); - LOGV("called getConfigurations size=%d", (int)configs.size()); + ALOGV("called getConfigurations size=%d", (int)configs.size()); const size_t I = configs.size(); for (size_t i=0; ipackage; const Type* allTypes = package->getType(typeIndex); - LOGV("allTypes=%p\n", allTypes); + ALOGV("allTypes=%p\n", allTypes); if (allTypes == NULL) { - LOGV("Skipping entry type index 0x%02x because type is NULL!\n", typeIndex); + ALOGV("Skipping entry type index 0x%02x because type is NULL!\n", typeIndex); return 0; } diff --git a/libs/utils/StreamingZipInflater.cpp b/libs/utils/StreamingZipInflater.cpp index 00498bd1f..59a46f977 100644 --- a/libs/utils/StreamingZipInflater.cpp +++ b/libs/utils/StreamingZipInflater.cpp @@ -77,7 +77,7 @@ StreamingZipInflater::~StreamingZipInflater() { } void StreamingZipInflater::initInflateState() { - LOGV("Initializing inflate state"); + ALOGV("Initializing inflate state"); memset(&mInflateState, 0, sizeof(mInflateState)); mInflateState.zalloc = Z_NULL; @@ -152,13 +152,13 @@ ssize_t StreamingZipInflater::read(void* outBuf, size_t count) { mInflateState.avail_out = mOutBufSize; /* - LOGV("Inflating to outbuf: avail_in=%u avail_out=%u next_in=%p next_out=%p", + ALOGV("Inflating to outbuf: avail_in=%u avail_out=%u next_in=%p next_out=%p", mInflateState.avail_in, mInflateState.avail_out, mInflateState.next_in, mInflateState.next_out); */ int result = Z_OK; if (mStreamNeedsInit) { - LOGV("Initializing zlib to inflate"); + ALOGV("Initializing zlib to inflate"); result = inflateInit2(&mInflateState, -MAX_WBITS); mStreamNeedsInit = false; } @@ -192,7 +192,7 @@ int StreamingZipInflater::readNextChunk() { size_t toRead = min_of(mInBufSize, mInTotalSize - mInNextChunkOffset); if (toRead > 0) { ssize_t didRead = ::read(mFd, mInBuf, toRead); - //LOGV("Reading input chunk, size %08x didread %08x", toRead, didRead); + //ALOGV("Reading input chunk, size %08x didread %08x", toRead, didRead); if (didRead < 0) { // TODO: error LOGE("Error reading asset data"); diff --git a/libs/utils/VectorImpl.cpp b/libs/utils/VectorImpl.cpp index bfb37a60d..4a9029671 100644 --- a/libs/utils/VectorImpl.cpp +++ b/libs/utils/VectorImpl.cpp @@ -346,7 +346,7 @@ void VectorImpl::release_storage() void* VectorImpl::_grow(size_t where, size_t amount) { -// LOGV("_grow(this=%p, where=%d, amount=%d) count=%d, capacity=%d", +// ALOGV("_grow(this=%p, where=%d, amount=%d) count=%d, capacity=%d", // this, (int)where, (int)amount, (int)mCount, (int)capacity()); LOG_ASSERT(where <= mCount, @@ -356,7 +356,7 @@ void* VectorImpl::_grow(size_t where, size_t amount) const size_t new_size = mCount + amount; if (capacity() < new_size) { const size_t new_capacity = max(kMinVectorCapacity, ((new_size*3)+1)/2); -// LOGV("grow vector %p, new_capacity=%d", this, (int)new_capacity); +// ALOGV("grow vector %p, new_capacity=%d", this, (int)new_capacity); if ((mStorage) && (mCount==where) && (mFlags & HAS_TRIVIAL_COPY) && @@ -399,7 +399,7 @@ void VectorImpl::_shrink(size_t where, size_t amount) if (!mStorage) return; -// LOGV("_shrink(this=%p, where=%d, amount=%d) count=%d, capacity=%d", +// ALOGV("_shrink(this=%p, where=%d, amount=%d) count=%d, capacity=%d", // this, (int)where, (int)amount, (int)mCount, (int)capacity()); LOG_ASSERT(where + amount <= mCount, @@ -409,7 +409,7 @@ void VectorImpl::_shrink(size_t where, size_t amount) const size_t new_size = mCount - amount; if (new_size*3 < capacity()) { const size_t new_capacity = max(kMinVectorCapacity, new_size*2); -// LOGV("shrink vector %p, new_capacity=%d", this, (int)new_capacity); +// ALOGV("shrink vector %p, new_capacity=%d", this, (int)new_capacity); if ((where == new_size) && (mFlags & HAS_TRIVIAL_COPY) && (mFlags & HAS_TRIVIAL_DTOR)) diff --git a/libs/utils/ZipFileRO.cpp b/libs/utils/ZipFileRO.cpp index b18c383ae..d880f550c 100644 --- a/libs/utils/ZipFileRO.cpp +++ b/libs/utils/ZipFileRO.cpp @@ -222,7 +222,7 @@ bool ZipFileRO::mapCentralDirectory(void) free(scanBuf); return false; } else if (header != kLFHSignature) { - LOGV("Not a Zip archive (found 0x%08x)\n", header); + ALOGV("Not a Zip archive (found 0x%08x)\n", header); free(scanBuf); return false; } @@ -264,7 +264,7 @@ bool ZipFileRO::mapCentralDirectory(void) int i; for (i = readAmount - kEOCDLen; i >= 0; i--) { if (scanBuf[i] == 0x50 && get4LE(&scanBuf[i]) == kEOCDSignature) { - LOGV("+++ Found EOCD at buf+%d\n", i); + ALOGV("+++ Found EOCD at buf+%d\n", i); break; } } @@ -299,7 +299,7 @@ bool ZipFileRO::mapCentralDirectory(void) return false; } - LOGV("+++ numEntries=%d dirSize=%d dirOffset=%d\n", + ALOGV("+++ numEntries=%d dirSize=%d dirOffset=%d\n", numEntries, dirSize, dirOffset); mDirectoryMap = new FileMap(); @@ -372,7 +372,7 @@ bool ZipFileRO::parseZipArchive(void) goto bail; } } - LOGV("+++ zip good scan %d entries\n", numEntries); + ALOGV("+++ zip good scan %d entries\n", numEntries); result = true; bail: diff --git a/libs/utils/ZipUtils.cpp b/libs/utils/ZipUtils.cpp index 9138878ff..76725b4e2 100644 --- a/libs/utils/ZipUtils.cpp +++ b/libs/utils/ZipUtils.cpp @@ -95,7 +95,7 @@ using namespace android; if (zstream.avail_in == 0) { getSize = (compRemaining > kReadBufSize) ? kReadBufSize : compRemaining; - LOGV("+++ reading %ld bytes (%ld left)\n", + ALOGV("+++ reading %ld bytes (%ld left)\n", getSize, compRemaining); int cc = read(fd, readBuf, getSize); @@ -207,7 +207,7 @@ bail: if (zstream.avail_in == 0) { getSize = (compRemaining > kReadBufSize) ? kReadBufSize : compRemaining; - LOGV("+++ reading %ld bytes (%ld left)\n", + ALOGV("+++ reading %ld bytes (%ld left)\n", getSize, compRemaining); int cc = fread(readBuf, 1, getSize, fp); From 90f43dce840b2a815426b6bfcdbe5f92358ab956 Mon Sep 17 00:00:00 2001 From: Jamie Gennis Date: Sun, 30 Oct 2011 18:10:41 -0700 Subject: [PATCH 372/541] BlobCache: remove the mutex locking This change removes the mutex from the BlobCache class. The caller must be responsible for thread synchronization in order to properly implement the Flattenable interface, which is coming soon. Otherwise would be the potential for the cache contents to change between the call to the getFlattenedSize and flatten methods. Because the caller must do this synchronization anyway there's no reason to also some synchronization inside BlobCache. Change-Id: Ie1f4f6f82b78744f46a41ce863cac0cad276a20e --- include/utils/BlobCache.h | 11 +++-------- libs/utils/BlobCache.cpp | 2 -- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/include/utils/BlobCache.h b/include/utils/BlobCache.h index dc45ff0f3..11d424666 100644 --- a/include/utils/BlobCache.h +++ b/include/utils/BlobCache.h @@ -25,8 +25,8 @@ namespace android { -// A BlobCache is an in-memory cache for binary key/value pairs. All the public -// methods are thread-safe. +// A BlobCache is an in-memory cache for binary key/value pairs. A BlobCache +// does NOT provide any thread-safety guarantees. // // The cache contents can be serialized to a file and reloaded in a subsequent // execution of the program. This serialization is non-portable and should only @@ -166,17 +166,12 @@ private: size_t mTotalSize; // mRandState is the pseudo-random number generator state. It is passed to - // nrand48 to generate random numbers when needed. It must be protected by - // mMutex. + // nrand48 to generate random numbers when needed. unsigned short mRandState[3]; // mCacheEntries stores all the cache entries that are resident in memory. // Cache entries are added to it by the 'set' method. SortedVector mCacheEntries; - - // mMutex is used to synchronize access to all member variables. It must be - // locked any time the member variables are written or read. - Mutex mMutex; }; } diff --git a/libs/utils/BlobCache.cpp b/libs/utils/BlobCache.cpp index 4039807d8..24fdca868 100644 --- a/libs/utils/BlobCache.cpp +++ b/libs/utils/BlobCache.cpp @@ -67,7 +67,6 @@ void BlobCache::set(const void* key, size_t keySize, const void* value, return; } - Mutex::Autolock lock(mMutex); sp dummyKey(new Blob(key, keySize, false)); CacheEntry dummyEntry(dummyKey, NULL); @@ -129,7 +128,6 @@ size_t BlobCache::get(const void* key, size_t keySize, void* value, keySize, mMaxKeySize); return 0; } - Mutex::Autolock lock(mMutex); sp dummyKey(new Blob(key, keySize, false)); CacheEntry dummyEntry(dummyKey, NULL); ssize_t index = mCacheEntries.indexOf(dummyEntry); From 7451bb44100d9d4915c3c4e4366ab92213947b4b Mon Sep 17 00:00:00 2001 From: Jamie Gennis Date: Thu, 12 May 2011 17:39:03 -0700 Subject: [PATCH 373/541] BlobCache: implement cache serialization This change adds serialization and deserialization functionality to BlobCache, conforming to the Flattenable interface. Change-Id: Ibc99cb1c3d015f363d57d0713eabccec07ff975e --- include/utils/BlobCache.h | 96 ++++++++++++++-- libs/utils/BlobCache.cpp | 140 +++++++++++++++++++++++- libs/utils/tests/BlobCache_test.cpp | 164 ++++++++++++++++++++++++++++ 3 files changed, 386 insertions(+), 14 deletions(-) diff --git a/include/utils/BlobCache.h b/include/utils/BlobCache.h index 11d424666..4f342a2ad 100644 --- a/include/utils/BlobCache.h +++ b/include/utils/BlobCache.h @@ -19,6 +19,7 @@ #include +#include #include #include #include @@ -28,10 +29,11 @@ namespace android { // A BlobCache is an in-memory cache for binary key/value pairs. A BlobCache // does NOT provide any thread-safety guarantees. // -// The cache contents can be serialized to a file and reloaded in a subsequent -// execution of the program. This serialization is non-portable and should only -// be loaded by the device that generated it. -class BlobCache : public RefBase { +// The cache contents can be serialized to an in-memory buffer or mmap'd file +// and then reloaded in a subsequent execution of the program. This +// serialization is non-portable and the data should only be used by the device +// that generated it. +class BlobCache : public RefBase, public Flattenable { public: // Create an empty blob cache. The blob cache will cache key/value pairs @@ -58,14 +60,13 @@ public: void set(const void* key, size_t keySize, const void* value, size_t valueSize); - // The get function retrieves from the cache the binary value associated - // with a given binary key. If the key is present in the cache then the - // length of the binary value associated with that key is returned. If the - // value argument is non-NULL and the size of the cached value is less than - // valueSize bytes then the cached value is copied into the buffer pointed - // to by the value argument. If the key is not present in the cache then 0 - // is returned and the buffer pointed to by the value argument is not - // modified. + // get retrieves from the cache the binary value associated with a given + // binary key. If the key is present in the cache then the length of the + // binary value associated with that key is returned. If the value argument + // is non-NULL and the size of the cached value is less than valueSize bytes + // then the cached value is copied into the buffer pointed to by the value + // argument. If the key is not present in the cache then 0 is returned and + // the buffer pointed to by the value argument is not modified. // // Note that when calling get multiple times with the same key, the later // calls may fail, returning 0, even if earlier calls succeeded. The return @@ -77,6 +78,37 @@ public: // 0 <= valueSize size_t get(const void* key, size_t keySize, void* value, size_t valueSize); + // getFlattenedSize returns the number of bytes needed to store the entire + // serialized cache. + virtual size_t getFlattenedSize() const; + + // getFdCount returns the number of file descriptors that will result from + // flattening the cache. This will always return 0 so as to allow the + // flattened cache to be saved to disk and then later restored. + virtual size_t getFdCount() const; + + // flatten serializes the current contents of the cache into the memory + // pointed to by 'buffer'. The serialized cache contents can later be + // loaded into a BlobCache object using the unflatten method. The contents + // of the BlobCache object will not be modified. + // + // Preconditions: + // size >= this.getFlattenedSize() + // count == 0 + virtual status_t flatten(void* buffer, size_t size, int fds[], + size_t count) const; + + // unflatten replaces the contents of the cache with the serialized cache + // contents in the memory pointed to by 'buffer'. The previous contents of + // the BlobCache will be evicted from the cache. If an error occurs while + // unflattening the serialized cache contents then the BlobCache will be + // left in an empty state. + // + // Preconditions: + // count == 0 + virtual status_t unflatten(void const* buffer, size_t size, int fds[], + size_t count); + private: // Copying is disallowed. BlobCache(const BlobCache&); @@ -144,6 +176,46 @@ private: sp mValue; }; + // A Header is the header for the entire BlobCache serialization format. No + // need to make this portable, so we simply write the struct out. + struct Header { + // mMagicNumber is the magic number that identifies the data as + // serialized BlobCache contents. It must always contain 'Blb$'. + uint32_t mMagicNumber; + + // mBlobCacheVersion is the serialization format version. + uint32_t mBlobCacheVersion; + + // mDeviceVersion is the device-specific version of the cache. This can + // be used to invalidate the cache. + uint32_t mDeviceVersion; + + // mNumEntries is number of cache entries following the header in the + // data. + size_t mNumEntries; + }; + + // An EntryHeader is the header for a serialized cache entry. No need to + // make this portable, so we simply write the struct out. Each EntryHeader + // is followed imediately by the key data and then the value data. + // + // The beginning of each serialized EntryHeader is 4-byte aligned, so the + // number of bytes that a serialized cache entry will occupy is: + // + // ((sizeof(EntryHeader) + keySize + valueSize) + 3) & ~3 + // + struct EntryHeader { + // mKeySize is the size of the entry key in bytes. + size_t mKeySize; + + // mValueSize is the size of the entry value in bytes. + size_t mValueSize; + + // mData contains both the key and value data for the cache entry. The + // key comes first followed immediately by the value. + uint8_t mData[]; + }; + // mMaxKeySize is the maximum key size that will be cached. Calls to // BlobCache::set with a keySize parameter larger than mMaxKeySize will // simply not add the key/value pair to the cache. diff --git a/libs/utils/BlobCache.cpp b/libs/utils/BlobCache.cpp index 24fdca868..497082869 100644 --- a/libs/utils/BlobCache.cpp +++ b/libs/utils/BlobCache.cpp @@ -21,10 +21,20 @@ #include #include +#include #include namespace android { +// BlobCache::Header::mMagicNumber value +static const uint32_t blobCacheMagic = '_Bb$'; + +// BlobCache::Header::mBlobCacheVersion value +static const uint32_t blobCacheVersion = 1; + +// BlobCache::Header::mDeviceVersion value +static const uint32_t blobCacheDeviceVersion = 1; + BlobCache::BlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxTotalSize): mMaxKeySize(maxKeySize), mMaxValueSize(maxValueSize), @@ -71,7 +81,6 @@ void BlobCache::set(const void* key, size_t keySize, const void* value, CacheEntry dummyEntry(dummyKey, NULL); while (true) { - ssize_t index = mCacheEntries.indexOf(dummyEntry); if (index < 0) { // Create a new cache entry. @@ -150,6 +159,133 @@ size_t BlobCache::get(const void* key, size_t keySize, void* value, return valueBlobSize; } +static inline size_t align4(size_t size) { + return (size + 3) & ~3; +} + +size_t BlobCache::getFlattenedSize() const { + size_t size = sizeof(Header); + for (size_t i = 0; i < mCacheEntries.size(); i++) { + const CacheEntry& e(mCacheEntries[i]); + sp keyBlob = e.getKey(); + sp valueBlob = e.getValue(); + size = align4(size); + size += sizeof(EntryHeader) + keyBlob->getSize() + + valueBlob->getSize(); + } + return size; +} + +size_t BlobCache::getFdCount() const { + return 0; +} + +status_t BlobCache::flatten(void* buffer, size_t size, int fds[], size_t count) + const { + if (count != 0) { + LOGE("flatten: nonzero fd count: %d", count); + return BAD_VALUE; + } + + // Write the cache header + if (size < sizeof(Header)) { + LOGE("flatten: not enough room for cache header"); + return BAD_VALUE; + } + Header* header = reinterpret_cast(buffer); + header->mMagicNumber = blobCacheMagic; + header->mBlobCacheVersion = blobCacheVersion; + header->mDeviceVersion = blobCacheDeviceVersion; + header->mNumEntries = mCacheEntries.size(); + + // Write cache entries + uint8_t* byteBuffer = reinterpret_cast(buffer); + off_t byteOffset = align4(sizeof(Header)); + for (size_t i = 0; i < mCacheEntries.size(); i++) { + const CacheEntry& e(mCacheEntries[i]); + sp keyBlob = e.getKey(); + sp valueBlob = e.getValue(); + size_t keySize = keyBlob->getSize(); + size_t valueSize = valueBlob->getSize(); + + size_t entrySize = sizeof(EntryHeader) + keySize + valueSize; + if (byteOffset + entrySize > size) { + LOGE("flatten: not enough room for cache entries"); + return BAD_VALUE; + } + + EntryHeader* eheader = reinterpret_cast( + &byteBuffer[byteOffset]); + eheader->mKeySize = keySize; + eheader->mValueSize = valueSize; + + memcpy(eheader->mData, keyBlob->getData(), keySize); + memcpy(eheader->mData + keySize, valueBlob->getData(), valueSize); + + byteOffset += align4(entrySize); + } + + return OK; +} + +status_t BlobCache::unflatten(void const* buffer, size_t size, int fds[], + size_t count) { + // All errors should result in the BlobCache being in an empty state. + mCacheEntries.clear(); + + if (count != 0) { + LOGE("unflatten: nonzero fd count: %d", count); + return BAD_VALUE; + } + + // Read the cache header + if (size < sizeof(Header)) { + LOGE("unflatten: not enough room for cache header"); + return BAD_VALUE; + } + const Header* header = reinterpret_cast(buffer); + if (header->mMagicNumber != blobCacheMagic) { + LOGE("unflatten: bad magic number: %d", header->mMagicNumber); + return BAD_VALUE; + } + if (header->mBlobCacheVersion != blobCacheVersion || + header->mDeviceVersion != blobCacheDeviceVersion) { + // We treat version mismatches as an empty cache. + return OK; + } + + // Read cache entries + const uint8_t* byteBuffer = reinterpret_cast(buffer); + off_t byteOffset = align4(sizeof(Header)); + size_t numEntries = header->mNumEntries; + for (size_t i = 0; i < numEntries; i++) { + if (byteOffset + sizeof(EntryHeader) > size) { + mCacheEntries.clear(); + LOGE("unflatten: not enough room for cache entry headers"); + return BAD_VALUE; + } + + const EntryHeader* eheader = reinterpret_cast( + &byteBuffer[byteOffset]); + size_t keySize = eheader->mKeySize; + size_t valueSize = eheader->mValueSize; + size_t entrySize = sizeof(EntryHeader) + keySize + valueSize; + + if (byteOffset + entrySize > size) { + mCacheEntries.clear(); + LOGE("unflatten: not enough room for cache entry headers"); + return BAD_VALUE; + } + + const uint8_t* data = eheader->mData; + set(data, keySize, data + keySize, valueSize); + + byteOffset += align4(entrySize); + } + + return OK; +} + long int BlobCache::blob_random() { #ifdef _WIN32 return rand(); @@ -177,7 +313,7 @@ BlobCache::Blob::Blob(const void* data, size_t size, bool copyData): mData(copyData ? malloc(size) : data), mSize(size), mOwnsData(copyData) { - if (copyData) { + if (data != NULL && copyData) { memcpy(const_cast(mData), data, size); } } diff --git a/libs/utils/tests/BlobCache_test.cpp b/libs/utils/tests/BlobCache_test.cpp index 653ea5e91..b64cc3956 100644 --- a/libs/utils/tests/BlobCache_test.cpp +++ b/libs/utils/tests/BlobCache_test.cpp @@ -14,9 +14,13 @@ ** limitations under the License. */ +#include +#include + #include #include +#include namespace android { @@ -254,4 +258,164 @@ TEST_F(BlobCacheTest, ExceedingTotalLimitHalvesCacheSize) { ASSERT_EQ(maxEntries/2 + 1, numCached); } +class BlobCacheFlattenTest : public BlobCacheTest { +protected: + virtual void SetUp() { + BlobCacheTest::SetUp(); + mBC2 = new BlobCache(MAX_KEY_SIZE, MAX_VALUE_SIZE, MAX_TOTAL_SIZE); + } + + virtual void TearDown() { + mBC2.clear(); + BlobCacheTest::TearDown(); + } + + void roundTrip() { + size_t size = mBC->getFlattenedSize(); + uint8_t* flat = new uint8_t[size]; + ASSERT_EQ(OK, mBC->flatten(flat, size, NULL, 0)); + ASSERT_EQ(OK, mBC2->unflatten(flat, size, NULL, 0)); + delete[] flat; + } + + sp mBC2; +}; + +TEST_F(BlobCacheFlattenTest, FlattenOneValue) { + char buf[4] = { 0xee, 0xee, 0xee, 0xee }; + mBC->set("abcd", 4, "efgh", 4); + roundTrip(); + ASSERT_EQ(size_t(4), mBC2->get("abcd", 4, buf, 4)); + ASSERT_EQ('e', buf[0]); + ASSERT_EQ('f', buf[1]); + ASSERT_EQ('g', buf[2]); + ASSERT_EQ('h', buf[3]); +} + +TEST_F(BlobCacheFlattenTest, FlattenFullCache) { + // Fill up the entire cache with 1 char key/value pairs. + const int maxEntries = MAX_TOTAL_SIZE / 2; + for (int i = 0; i < maxEntries; i++) { + uint8_t k = i; + mBC->set(&k, 1, &k, 1); + } + + roundTrip(); + + // Verify the deserialized cache + for (int i = 0; i < maxEntries; i++) { + uint8_t k = i; + uint8_t v = 0xee; + ASSERT_EQ(size_t(1), mBC2->get(&k, 1, &v, 1)); + ASSERT_EQ(k, v); + } +} + +TEST_F(BlobCacheFlattenTest, FlattenDoesntChangeCache) { + // Fill up the entire cache with 1 char key/value pairs. + const int maxEntries = MAX_TOTAL_SIZE / 2; + for (int i = 0; i < maxEntries; i++) { + uint8_t k = i; + mBC->set(&k, 1, &k, 1); + } + + size_t size = mBC->getFlattenedSize(); + uint8_t* flat = new uint8_t[size]; + ASSERT_EQ(OK, mBC->flatten(flat, size, NULL, 0)); + delete[] flat; + + // Verify the cache that we just serialized + for (int i = 0; i < maxEntries; i++) { + uint8_t k = i; + uint8_t v = 0xee; + ASSERT_EQ(size_t(1), mBC->get(&k, 1, &v, 1)); + ASSERT_EQ(k, v); + } +} + +TEST_F(BlobCacheFlattenTest, FlattenCatchesBufferTooSmall) { + // Fill up the entire cache with 1 char key/value pairs. + const int maxEntries = MAX_TOTAL_SIZE / 2; + for (int i = 0; i < maxEntries; i++) { + uint8_t k = i; + mBC->set(&k, 1, &k, 1); + } + + size_t size = mBC->getFlattenedSize() - 1; + uint8_t* flat = new uint8_t[size]; + ASSERT_EQ(BAD_VALUE, mBC->flatten(flat, size, NULL, 0)); + delete[] flat; +} + +TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadMagic) { + char buf[4] = { 0xee, 0xee, 0xee, 0xee }; + mBC->set("abcd", 4, "efgh", 4); + + size_t size = mBC->getFlattenedSize(); + uint8_t* flat = new uint8_t[size]; + ASSERT_EQ(OK, mBC->flatten(flat, size, NULL, 0)); + flat[1] = ~flat[1]; + + // Bad magic should cause an error. + ASSERT_EQ(BAD_VALUE, mBC2->unflatten(flat, size, NULL, 0)); + delete[] flat; + + // The error should cause the unflatten to result in an empty cache + ASSERT_EQ(size_t(0), mBC2->get("abcd", 4, buf, 4)); +} + +TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadBlobCacheVersion) { + char buf[4] = { 0xee, 0xee, 0xee, 0xee }; + mBC->set("abcd", 4, "efgh", 4); + + size_t size = mBC->getFlattenedSize(); + uint8_t* flat = new uint8_t[size]; + ASSERT_EQ(OK, mBC->flatten(flat, size, NULL, 0)); + flat[5] = ~flat[5]; + + // Version mismatches shouldn't cause errors, but should not use the + // serialized entries + ASSERT_EQ(OK, mBC2->unflatten(flat, size, NULL, 0)); + delete[] flat; + + // The version mismatch should cause the unflatten to result in an empty + // cache + ASSERT_EQ(size_t(0), mBC2->get("abcd", 4, buf, 4)); +} + +TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadBlobCacheDeviceVersion) { + char buf[4] = { 0xee, 0xee, 0xee, 0xee }; + mBC->set("abcd", 4, "efgh", 4); + + size_t size = mBC->getFlattenedSize(); + uint8_t* flat = new uint8_t[size]; + ASSERT_EQ(OK, mBC->flatten(flat, size, NULL, 0)); + flat[10] = ~flat[10]; + + // Version mismatches shouldn't cause errors, but should not use the + // serialized entries + ASSERT_EQ(OK, mBC2->unflatten(flat, size, NULL, 0)); + delete[] flat; + + // The version mismatch should cause the unflatten to result in an empty + // cache + ASSERT_EQ(size_t(0), mBC2->get("abcd", 4, buf, 4)); +} + +TEST_F(BlobCacheFlattenTest, UnflattenCatchesBufferTooSmall) { + char buf[4] = { 0xee, 0xee, 0xee, 0xee }; + mBC->set("abcd", 4, "efgh", 4); + + size_t size = mBC->getFlattenedSize(); + uint8_t* flat = new uint8_t[size]; + ASSERT_EQ(OK, mBC->flatten(flat, size, NULL, 0)); + + // A buffer truncation shouldt cause an error + ASSERT_EQ(BAD_VALUE, mBC2->unflatten(flat, size-1, NULL, 0)); + delete[] flat; + + // The error should cause the unflatten to result in an empty cache + ASSERT_EQ(size_t(0), mBC2->get("abcd", 4, buf, 4)); +} + } // namespace android From e7f371657255d0b407d375a3342d4fea3cf3cfa1 Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Thu, 3 Nov 2011 17:30:54 -0700 Subject: [PATCH 374/541] Slight change to the unwinder API. Change-Id: I5424de76a21b74842e004e0281936b3f492d3c7a --- libs/utils/CallStack.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/utils/CallStack.cpp b/libs/utils/CallStack.cpp index d79a75780..b4c581b03 100644 --- a/libs/utils/CallStack.cpp +++ b/libs/utils/CallStack.cpp @@ -103,7 +103,7 @@ void CallStack::dump(const char* prefix) const { for (size_t i = 0; i < mCount; i++) { const backtrace_frame_t& frame = mStack[i]; const backtrace_symbol_t& symbol = symbols[i]; - const char* mapName = symbol.map_info ? symbol.map_info->name : ""; + const char* mapName = symbol.map_name ? symbol.map_name : ""; const char* symbolName = symbol.demangled_name ? symbol.demangled_name : symbol.name; if (symbolName) { LOGD("%s#%02d pc %08x %s (%s)\n", prefix, @@ -124,7 +124,7 @@ String8 CallStack::toString(const char* prefix) const { for (size_t i = 0; i < mCount; i++) { const backtrace_frame_t& frame = mStack[i]; const backtrace_symbol_t& symbol = symbols[i]; - const char* mapName = symbol.map_info ? symbol.map_info->name : ""; + const char* mapName = symbol.map_name ? symbol.map_name : ""; const char* symbolName = symbol.demangled_name ? symbol.demangled_name : symbol.name; if (symbolName) { str.appendFormat("%s#%02d pc %08x %s (%s)\n", prefix, From f36821289cfa4a1924be4c0800f0e1ac29241c0a Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Fri, 11 Nov 2011 15:40:13 -0800 Subject: [PATCH 375/541] Clean up GenerationCache. Use const references to keys and values where appropriate to avoid copying them unnecessarily. Deleted some dead code. Simplified a few pieces that were doing unnecessary redundant work. Change-Id: Ib2145b7094a40db2d679e05dafe050fe1e87b846 --- include/utils/GenerationCache.h | 108 +++++++++++++++----------------- 1 file changed, 49 insertions(+), 59 deletions(-) diff --git a/include/utils/GenerationCache.h b/include/utils/GenerationCache.h index bb9ddd677..83cda8689 100644 --- a/include/utils/GenerationCache.h +++ b/include/utils/GenerationCache.h @@ -34,17 +34,17 @@ public: template struct Entry: public LightRefBase > { - Entry() { } - Entry(const Entry& e): - key(e.key), value(e.value), parent(e.parent), child(e.child) { } - Entry(sp > e): - key(e->key), value(e->value), parent(e->parent), child(e->child) { } + Entry(const Entry& e) : + key(e.key), value(e.value), + parent(e.parent), child(e.child) { } + Entry(const EntryKey& key, const EntryValue& value) : + key(key), value(value) { } EntryKey key; EntryValue value; - sp > parent; - sp > child; + sp > parent; // next older entry + sp > child; // next younger entry }; // struct Entry /** @@ -62,23 +62,20 @@ public: void setOnEntryRemovedListener(OnEntryRemoved* listener); + size_t size() const; + void clear(); - bool contains(K key) const; - V get(K key); - K getKeyAt(uint32_t index) const; - bool put(K key, V value); - V remove(K key); - V removeOldest(); - V getValueAt(uint32_t index) const; + bool contains(const K& key) const; + const K& getKeyAt(size_t index) const; + const V& getValueAt(size_t index) const; - uint32_t size() const; + const V& get(const K& key); + bool put(const K& key, const V& value); - void addToCache(sp > entry, K key, V value); - void attachToCache(sp > entry); - void detachFromCache(sp > entry); - - V removeAt(ssize_t index); + void removeAt(ssize_t index); + bool remove(const K& key); + bool removeOldest(); private: KeyedVector > > mCache; @@ -88,6 +85,9 @@ private: sp > mOldest; sp > mYoungest; + + void attachToCache(const sp >& entry); + void detachFromCache(const sp >& entry); }; // class GenerationCache template @@ -130,45 +130,44 @@ void GenerationCache::clear() { } template -bool GenerationCache::contains(K key) const { +bool GenerationCache::contains(const K& key) const { return mCache.indexOfKey(key) >= 0; } template -K GenerationCache::getKeyAt(uint32_t index) const { +const K& GenerationCache::getKeyAt(size_t index) const { return mCache.keyAt(index); } template -V GenerationCache::getValueAt(uint32_t index) const { +const V& GenerationCache::getValueAt(size_t index) const { return mCache.valueAt(index)->value; } template -V GenerationCache::get(K key) { +const V& GenerationCache::get(const K& key) { ssize_t index = mCache.indexOfKey(key); if (index >= 0) { - sp > entry = mCache.valueAt(index); - if (entry.get()) { - detachFromCache(entry); - attachToCache(entry); - return entry->value; - } + const sp >& entry = mCache.valueAt(index); + detachFromCache(entry); + attachToCache(entry); + return entry->value; } return NULL; } template -bool GenerationCache::put(K key, V value) { +bool GenerationCache::put(const K& key, const V& value) { if (mMaxCapacity != kUnlimitedCapacity && mCache.size() >= mMaxCapacity) { removeOldest(); } ssize_t index = mCache.indexOfKey(key); if (index < 0) { - sp > entry = new Entry; - addToCache(entry, key, value); + sp > entry = new Entry(key, value); + mCache.add(key, entry); + attachToCache(entry); return true; } @@ -176,49 +175,44 @@ bool GenerationCache::put(K key, V value) { } template -void GenerationCache::addToCache(sp > entry, K key, V value) { - entry->key = key; - entry->value = value; - mCache.add(key, entry); - attachToCache(entry); -} - -template -V GenerationCache::remove(K key) { +bool GenerationCache::remove(const K& key) { ssize_t index = mCache.indexOfKey(key); if (index >= 0) { - return removeAt(index); + removeAt(index); + return true; } - return NULL; + return false; } template -V GenerationCache::removeAt(ssize_t index) { +void GenerationCache::removeAt(ssize_t index) { sp > entry = mCache.valueAt(index); if (mListener) { (*mListener)(entry->key, entry->value); } mCache.removeItemsAt(index, 1); detachFromCache(entry); - - return entry->value; } template -V GenerationCache::removeOldest() { +bool GenerationCache::removeOldest() { if (mOldest.get()) { ssize_t index = mCache.indexOfKey(mOldest->key); if (index >= 0) { - return removeAt(index); + removeAt(index); + return true; } + LOGE("GenerationCache: removeOldest failed to find the item in the cache " + "with the given key, but we know it must be in there. " + "Is the key comparator kaput?"); } - return NULL; + return false; } template -void GenerationCache::attachToCache(sp > entry) { +void GenerationCache::attachToCache(const sp >& entry) { if (!mYoungest.get()) { mYoungest = mOldest = entry; } else { @@ -229,20 +223,16 @@ void GenerationCache::attachToCache(sp > entry) { } template -void GenerationCache::detachFromCache(sp > entry) { +void GenerationCache::detachFromCache(const sp >& entry) { if (entry->parent.get()) { entry->parent->child = entry->child; + } else { + mOldest = entry->child; } if (entry->child.get()) { entry->child->parent = entry->parent; - } - - if (mOldest == entry) { - mOldest = entry->child; - } - - if (mYoungest == entry) { + } else { mYoungest = entry->parent; } From 11189f5f797ef29ee670ab4683f97c2cfc109899 Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Mon, 21 Nov 2011 21:04:55 -0800 Subject: [PATCH 376/541] Use libcorkscrew to format the stack trace. Change-Id: I3a5439ada76bc77c2dd491eaed2272e16a811cc7 --- libs/utils/CallStack.cpp | 32 ++++++++++---------------------- 1 file changed, 10 insertions(+), 22 deletions(-) diff --git a/libs/utils/CallStack.cpp b/libs/utils/CallStack.cpp index b4c581b03..c2a5e5534 100644 --- a/libs/utils/CallStack.cpp +++ b/libs/utils/CallStack.cpp @@ -101,17 +101,10 @@ void CallStack::dump(const char* prefix) const { get_backtrace_symbols(mStack, mCount, symbols); for (size_t i = 0; i < mCount; i++) { - const backtrace_frame_t& frame = mStack[i]; - const backtrace_symbol_t& symbol = symbols[i]; - const char* mapName = symbol.map_name ? symbol.map_name : ""; - const char* symbolName = symbol.demangled_name ? symbol.demangled_name : symbol.name; - if (symbolName) { - LOGD("%s#%02d pc %08x %s (%s)\n", prefix, - int(i), uint32_t(symbol.relative_pc), mapName, symbolName); - } else { - LOGD("%s#%02d pc %08x %s\n", prefix, - int(i), uint32_t(symbol.relative_pc), mapName); - } + char line[MAX_BACKTRACE_LINE_LENGTH]; + format_backtrace_line(i, &mStack[i], &symbols[i], + line, MAX_BACKTRACE_LINE_LENGTH); + LOGD("%s%s", prefix, line); } free_backtrace_symbols(symbols, mCount); } @@ -122,17 +115,12 @@ String8 CallStack::toString(const char* prefix) const { get_backtrace_symbols(mStack, mCount, symbols); for (size_t i = 0; i < mCount; i++) { - const backtrace_frame_t& frame = mStack[i]; - const backtrace_symbol_t& symbol = symbols[i]; - const char* mapName = symbol.map_name ? symbol.map_name : ""; - const char* symbolName = symbol.demangled_name ? symbol.demangled_name : symbol.name; - if (symbolName) { - str.appendFormat("%s#%02d pc %08x %s (%s)\n", prefix, - int(i), uint32_t(symbol.relative_pc), mapName, symbolName); - } else { - str.appendFormat("%s#%02d pc %08x %s\n", prefix, - int(i), uint32_t(symbol.relative_pc), mapName); - } + char line[MAX_BACKTRACE_LINE_LENGTH]; + format_backtrace_line(i, &mStack[i], &symbols[i], + line, MAX_BACKTRACE_LINE_LENGTH); + str.append(prefix); + str.append(line); + str.append("\n"); } free_backtrace_symbols(symbols, mCount); return str; From e735f23018b398f45bd052b63616d7a45e29515b Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Mon, 14 Nov 2011 18:29:15 -0800 Subject: [PATCH 377/541] Add a basic hashtable data structure, with tests! The basic hashtable is intended to be used to support a variety of different datastructures such as map, set, multimap, multiset, linkedmap, generationcache, etc. Consequently its interface is fairly primitive. The basic hashtable supports copy-on-write style functionality using SharedBuffer. The change introduces a simple generic function in TypeHelpers for specifying hash functions. The idea is to add template specializations of hash_type next to the relevant data structures such as String8, String16, sp, etc. Change-Id: I2c479229e9d4527b4fbfe3b8b04776a2fd32c973 --- include/utils/BasicHashtable.h | 393 +++++++++++++++ include/utils/TypeHelpers.h | 44 ++ libs/utils/Android.mk | 1 + libs/utils/BasicHashtable.cpp | 338 +++++++++++++ libs/utils/primes.py | 47 ++ libs/utils/tests/Android.mk | 3 +- libs/utils/tests/BasicHashtable_test.cpp | 577 +++++++++++++++++++++++ 7 files changed, 1402 insertions(+), 1 deletion(-) create mode 100644 include/utils/BasicHashtable.h create mode 100644 libs/utils/BasicHashtable.cpp create mode 100755 libs/utils/primes.py create mode 100644 libs/utils/tests/BasicHashtable_test.cpp diff --git a/include/utils/BasicHashtable.h b/include/utils/BasicHashtable.h new file mode 100644 index 000000000..fdf97385f --- /dev/null +++ b/include/utils/BasicHashtable.h @@ -0,0 +1,393 @@ +/* + * Copyright (C) 2011 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_BASIC_HASHTABLE_H +#define ANDROID_BASIC_HASHTABLE_H + +#include +#include +#include +#include + +namespace android { + +/* Implementation type. Nothing to see here. */ +class BasicHashtableImpl { +protected: + struct Bucket { + // The collision flag indicates that the bucket is part of a collision chain + // such that at least two entries both hash to this bucket. When true, we + // may need to seek further along the chain to find the entry. + static const uint32_t COLLISION = 0x80000000UL; + + // The present flag indicates that the bucket contains an initialized entry value. + static const uint32_t PRESENT = 0x40000000UL; + + // Mask for 30 bits worth of the hash code that are stored within the bucket to + // speed up lookups and rehashing by eliminating the need to recalculate the + // hash code of the entry's key. + static const uint32_t HASH_MASK = 0x3fffffffUL; + + // Combined value that stores the collision and present flags as well as + // a 30 bit hash code. + uint32_t cookie; + + // Storage for the entry begins here. + char entry[0]; + }; + + BasicHashtableImpl(size_t entrySize, bool hasTrivialDestructor, + size_t minimumInitialCapacity, float loadFactor); + BasicHashtableImpl(const BasicHashtableImpl& other); + + void dispose(); + + inline void edit() { + if (mBuckets && !SharedBuffer::bufferFromData(mBuckets)->onlyOwner()) { + clone(); + } + } + + void setTo(const BasicHashtableImpl& other); + void clear(); + + ssize_t next(ssize_t index) const; + ssize_t find(ssize_t index, hash_t hash, const void* __restrict__ key) const; + size_t add(hash_t hash, const void* __restrict__ entry); + void removeAt(size_t index); + void rehash(size_t minimumCapacity, float loadFactor); + + const size_t mBucketSize; // number of bytes per bucket including the entry + const bool mHasTrivialDestructor; // true if the entry type does not require destruction + size_t mCapacity; // number of buckets that can be filled before exceeding load factor + float mLoadFactor; // load factor + size_t mSize; // number of elements actually in the table + size_t mFilledBuckets; // number of buckets for which collision or present is true + size_t mBucketCount; // number of slots in the mBuckets array + void* mBuckets; // array of buckets, as a SharedBuffer + + inline const Bucket& bucketAt(const void* __restrict__ buckets, size_t index) const { + return *reinterpret_cast( + static_cast(buckets) + index * mBucketSize); + } + + inline Bucket& bucketAt(void* __restrict__ buckets, size_t index) const { + return *reinterpret_cast(static_cast(buckets) + index * mBucketSize); + } + + virtual bool compareBucketKey(const Bucket& bucket, const void* __restrict__ key) const = 0; + virtual void initializeBucketEntry(Bucket& bucket, const void* __restrict__ entry) const = 0; + virtual void destroyBucketEntry(Bucket& bucket) const = 0; + +private: + void clone(); + + // Allocates a bucket array as a SharedBuffer. + void* allocateBuckets(size_t count) const; + + // Releases a bucket array's associated SharedBuffer. + void releaseBuckets(void* __restrict__ buckets, size_t count) const; + + // Destroys the contents of buckets (invokes destroyBucketEntry for each + // populated bucket if needed). + void destroyBuckets(void* __restrict__ buckets, size_t count) const; + + // Copies the content of buckets (copies the cookie and invokes copyBucketEntry + // for each populated bucket if needed). + void copyBuckets(const void* __restrict__ fromBuckets, + void* __restrict__ toBuckets, size_t count) const; + + // Determines the appropriate size of a bucket array to store a certain minimum + // number of entries and returns its effective capacity. + static void determineCapacity(size_t minimumCapacity, float loadFactor, + size_t* __restrict__ outBucketCount, size_t* __restrict__ outCapacity); + + // Trim a hash code to 30 bits to match what we store in the bucket's cookie. + inline static hash_t trimHash(hash_t hash) { + return (hash & Bucket::HASH_MASK) ^ (hash >> 30); + } + + // Returns the index of the first bucket that is in the collision chain + // for the specified hash code, given the total number of buckets. + // (Primary hash) + inline static size_t chainStart(hash_t hash, size_t count) { + return hash % count; + } + + // Returns the increment to add to a bucket index to seek to the next bucket + // in the collision chain for the specified hash code, given the total number of buckets. + // (Secondary hash) + inline static size_t chainIncrement(hash_t hash, size_t count) { + return ((hash >> 7) | (hash << 25)) % (count - 1) + 1; + } + + // Returns the index of the next bucket that is in the collision chain + // that is defined by the specified increment, given the total number of buckets. + inline static size_t chainSeek(size_t index, size_t increment, size_t count) { + return (index + increment) % count; + } +}; + +/* + * A BasicHashtable stores entries that are indexed by hash code in place + * within an array. The basic operations are finding entries by key, + * adding new entries and removing existing entries. + * + * This class provides a very limited set of operations with simple semantics. + * It is intended to be used as a building block to construct more complex + * and interesting data structures such as HashMap. Think very hard before + * adding anything extra to BasicHashtable, it probably belongs at a + * higher level of abstraction. + * + * TKey: The key type. + * TEntry: The entry type which is what is actually stored in the array. + * + * TKey must support the following contract: + * bool operator==(const TKey& other) const; // return true if equal + * bool operator!=(const TKey& other) const; // return true if unequal + * + * TEntry must support the following contract: + * const TKey& getKey() const; // get the key from the entry + * + * This class supports storing entries with duplicate keys. Of course, it can't + * tell them apart during removal so only the first entry will be removed. + * We do this because it means that operations like add() can't fail. + */ +template +class BasicHashtable : private BasicHashtableImpl { +public: + /* Creates a hashtable with the specified minimum initial capacity. + * The underlying array will be created when the first entry is added. + * + * minimumInitialCapacity: The minimum initial capacity for the hashtable. + * Default is 0. + * loadFactor: The desired load factor for the hashtable, between 0 and 1. + * Default is 0.75. + */ + BasicHashtable(size_t minimumInitialCapacity = 0, float loadFactor = 0.75f); + + /* Copies a hashtable. + * The underlying storage is shared copy-on-write. + */ + BasicHashtable(const BasicHashtable& other); + + /* Clears and destroys the hashtable. + */ + virtual ~BasicHashtable(); + + /* Making this hashtable a copy of the other hashtable. + * The underlying storage is shared copy-on-write. + * + * other: The hashtable to copy. + */ + inline BasicHashtable& operator =(const BasicHashtable & other) { + setTo(other); + return *this; + } + + /* Returns the number of entries in the hashtable. + */ + inline size_t size() const { + return mSize; + } + + /* Returns the capacity of the hashtable, which is the number of elements that can + * added to the hashtable without requiring it to be grown. + */ + inline size_t capacity() const { + return mCapacity; + } + + /* Returns the number of buckets that the hashtable has, which is the size of its + * underlying array. + */ + inline size_t bucketCount() const { + return mBucketCount; + } + + /* Returns the load factor of the hashtable. */ + inline float loadFactor() const { + return mLoadFactor; + }; + + /* Returns a const reference to the entry at the specified index. + * + * index: The index of the entry to retrieve. Must be a valid index within + * the bounds of the hashtable. + */ + inline const TEntry& entryAt(size_t index) const { + return entryFor(bucketAt(mBuckets, index)); + } + + /* Returns a non-const reference to the entry at the specified index. + * + * index: The index of the entry to edit. Must be a valid index within + * the bounds of the hashtable. + */ + inline TEntry& editEntryAt(size_t index) { + edit(); + return entryFor(bucketAt(mBuckets, index)); + } + + /* Clears the hashtable. + * All entries in the hashtable are destroyed immediately. + * If you need to do something special with the entries in the hashtable then iterate + * over them and do what you need before clearing the hashtable. + */ + inline void clear() { + BasicHashtableImpl::clear(); + } + + /* Returns the index of the next entry in the hashtable given the index of a previous entry. + * If the given index is -1, then returns the index of the first entry in the hashtable, + * if there is one, or -1 otherwise. + * If the given index is not -1, then returns the index of the next entry in the hashtable, + * in strictly increasing order, or -1 if there are none left. + * + * index: The index of the previous entry that was iterated, or -1 to begin + * iteration at the beginning of the hashtable. + */ + inline ssize_t next(ssize_t index) const { + return BasicHashtableImpl::next(index); + } + + /* Finds the index of an entry with the specified key. + * If the given index is -1, then returns the index of the first matching entry, + * otherwise returns the index of the next matching entry. + * If the hashtable contains multiple entries with keys that match the requested + * key, then the sequence of entries returned is arbitrary. + * Returns -1 if no entry was found. + * + * index: The index of the previous entry with the specified key, or -1 to + * find the first matching entry. + * hash: The hashcode of the key. + * key: The key. + */ + inline ssize_t find(ssize_t index, hash_t hash, const TKey& key) const { + return BasicHashtableImpl::find(index, hash, &key); + } + + /* Adds the entry to the hashtable. + * Returns the index of the newly added entry. + * If an entry with the same key already exists, then a duplicate entry is added. + * If the entry will not fit, then the hashtable's capacity is increased and + * its contents are rehashed. See rehash(). + * + * hash: The hashcode of the key. + * entry: The entry to add. + */ + inline size_t add(hash_t hash, const TEntry& entry) { + return BasicHashtableImpl::add(hash, &entry); + } + + /* Removes the entry with the specified index from the hashtable. + * The entry is destroyed immediately. + * The index must be valid. + * + * The hashtable is not compacted after an item is removed, so it is legal + * to continue iterating over the hashtable using next() or find(). + * + * index: The index of the entry to remove. Must be a valid index within the + * bounds of the hashtable, and it must refer to an existing entry. + */ + inline void removeAt(size_t index) { + BasicHashtableImpl::removeAt(index); + } + + /* Rehashes the contents of the hashtable. + * Grows the hashtable to at least the specified minimum capacity or the + * current number of elements, whichever is larger. + * + * Rehashing causes all entries to be copied and the entry indices may change. + * Although the hash codes are cached by the hashtable, rehashing can be an + * expensive operation and should be avoided unless the hashtable's size + * needs to be changed. + * + * Rehashing is the only way to change the capacity or load factor of the + * hashtable once it has been created. It can be used to compact the + * hashtable by choosing a minimum capacity that is smaller than the current + * capacity (such as 0). + * + * minimumCapacity: The desired minimum capacity after rehashing. + * loadFactor: The desired load factor after rehashing. + */ + inline void rehash(size_t minimumCapacity, float loadFactor) { + BasicHashtableImpl::rehash(minimumCapacity, loadFactor); + } + +protected: + static inline const TEntry& entryFor(const Bucket& bucket) { + return reinterpret_cast(bucket.entry); + } + + static inline TEntry& entryFor(Bucket& bucket) { + return reinterpret_cast(bucket.entry); + } + + virtual bool compareBucketKey(const Bucket& bucket, const void* __restrict__ key) const; + virtual void initializeBucketEntry(Bucket& bucket, const void* __restrict__ entry) const; + virtual void destroyBucketEntry(Bucket& bucket) const; + +private: + // For dumping the raw contents of a hashtable during testing. + friend class BasicHashtableTest; + inline uint32_t cookieAt(size_t index) const { + return bucketAt(mBuckets, index).cookie; + } +}; + +template +BasicHashtable::BasicHashtable(size_t minimumInitialCapacity, float loadFactor) : + BasicHashtableImpl(sizeof(TEntry), traits::has_trivial_dtor, + minimumInitialCapacity, loadFactor) { +} + +template +BasicHashtable::BasicHashtable(const BasicHashtable& other) : + BasicHashtableImpl(other) { +} + +template +BasicHashtable::~BasicHashtable() { + dispose(); +} + +template +bool BasicHashtable::compareBucketKey(const Bucket& bucket, + const void* __restrict__ key) const { + return entryFor(bucket).getKey() == *static_cast(key); +} + +template +void BasicHashtable::initializeBucketEntry(Bucket& bucket, + const void* __restrict__ entry) const { + if (!traits::has_trivial_copy) { + new (&entryFor(bucket)) TEntry(*(static_cast(entry))); + } else { + memcpy(&entryFor(bucket), entry, sizeof(TEntry)); + } +} + +template +void BasicHashtable::destroyBucketEntry(Bucket& bucket) const { + if (!traits::has_trivial_dtor) { + entryFor(bucket).~TEntry(); + } +} + +}; // namespace android + +#endif // ANDROID_BASIC_HASHTABLE_H diff --git a/include/utils/TypeHelpers.h b/include/utils/TypeHelpers.h index a1663f30e..75388176d 100644 --- a/include/utils/TypeHelpers.h +++ b/include/utils/TypeHelpers.h @@ -213,6 +213,9 @@ void move_backward_type(TYPE* d, const TYPE* s, size_t n = 1) { template struct key_value_pair_t { + typedef KEY key_t; + typedef VALUE value_t; + KEY key; VALUE value; key_value_pair_t() { } @@ -222,6 +225,12 @@ struct key_value_pair_t { inline bool operator < (const key_value_pair_t& o) const { return strictly_order_type(key, o.key); } + inline const KEY& getKey() const { + return key; + } + inline const VALUE& getValue() const { + return value; + } }; template<> @@ -243,6 +252,41 @@ struct trait_trivial_move< key_value_pair_t > // --------------------------------------------------------------------------- +/* + * Hash codes. + */ +typedef uint32_t hash_t; + +template +hash_t hash_type(const TKey& key); + +/* Built-in hash code specializations. + * Assumes pointers are 32bit. */ +#define ANDROID_INT32_HASH(T) \ + template <> inline hash_t hash_type(const T& value) { return hash_t(value); } +#define ANDROID_INT64_HASH(T) \ + template <> inline hash_t hash_type(const T& value) { \ + return hash_t((value >> 32) ^ value); } +#define ANDROID_REINTERPRET_HASH(T, R) \ + template <> inline hash_t hash_type(const T& value) { \ + return hash_type(*reinterpret_cast(&value)); } + +ANDROID_INT32_HASH(bool) +ANDROID_INT32_HASH(char) +ANDROID_INT32_HASH(unsigned char) +ANDROID_INT32_HASH(short) +ANDROID_INT32_HASH(unsigned short) +ANDROID_INT32_HASH(int) +ANDROID_INT32_HASH(unsigned int) +ANDROID_INT64_HASH(long) +ANDROID_INT64_HASH(unsigned long) +ANDROID_REINTERPRET_HASH(float, uint32_t) +ANDROID_REINTERPRET_HASH(double, uint64_t) + +template inline hash_t hash_type(const T*& value) { + return hash_type(uintptr_t(value)); +} + }; // namespace android // --------------------------------------------------------------------------- diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk index d168d190a..544ab744e 100644 --- a/libs/utils/Android.mk +++ b/libs/utils/Android.mk @@ -21,6 +21,7 @@ commonSources:= \ Asset.cpp \ AssetDir.cpp \ AssetManager.cpp \ + BasicHashtable.cpp \ BlobCache.cpp \ BufferedTextOutput.cpp \ CallStack.cpp \ diff --git a/libs/utils/BasicHashtable.cpp b/libs/utils/BasicHashtable.cpp new file mode 100644 index 000000000..fb8ec9f83 --- /dev/null +++ b/libs/utils/BasicHashtable.cpp @@ -0,0 +1,338 @@ +/* + * Copyright (C) 2011 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 "BasicHashtable" + +#include + +#include +#include +#include + +namespace android { + +BasicHashtableImpl::BasicHashtableImpl(size_t entrySize, bool hasTrivialDestructor, + size_t minimumInitialCapacity, float loadFactor) : + mBucketSize(entrySize + sizeof(Bucket)), mHasTrivialDestructor(hasTrivialDestructor), + mLoadFactor(loadFactor), mSize(0), + mFilledBuckets(0), mBuckets(NULL) { + determineCapacity(minimumInitialCapacity, mLoadFactor, &mBucketCount, &mCapacity); +} + +BasicHashtableImpl::BasicHashtableImpl(const BasicHashtableImpl& other) : + mBucketSize(other.mBucketSize), mHasTrivialDestructor(other.mHasTrivialDestructor), + mCapacity(other.mCapacity), mLoadFactor(other.mLoadFactor), + mSize(other.mSize), mFilledBuckets(other.mFilledBuckets), + mBucketCount(other.mBucketCount), mBuckets(other.mBuckets) { + if (mBuckets) { + SharedBuffer::bufferFromData(mBuckets)->acquire(); + } +} + +void BasicHashtableImpl::dispose() { + if (mBuckets) { + releaseBuckets(mBuckets, mBucketCount); + } +} + +void BasicHashtableImpl::clone() { + if (mBuckets) { + void* newBuckets = allocateBuckets(mBucketCount); + copyBuckets(mBuckets, newBuckets, mBucketCount); + releaseBuckets(mBuckets, mBucketCount); + mBuckets = newBuckets; + } +} + +void BasicHashtableImpl::setTo(const BasicHashtableImpl& other) { + if (mBuckets) { + releaseBuckets(mBuckets, mBucketCount); + } + + mCapacity = other.mCapacity; + mLoadFactor = other.mLoadFactor; + mSize = other.mSize; + mFilledBuckets = other.mFilledBuckets; + mBucketCount = other.mBucketCount; + mBuckets = other.mBuckets; + + if (mBuckets) { + SharedBuffer::bufferFromData(mBuckets)->acquire(); + } +} + +void BasicHashtableImpl::clear() { + if (mBuckets) { + if (mFilledBuckets) { + SharedBuffer* sb = SharedBuffer::bufferFromData(mBuckets); + if (sb->onlyOwner()) { + destroyBuckets(mBuckets, mBucketCount); + for (size_t i = 0; i < mSize; i++) { + Bucket& bucket = bucketAt(mBuckets, i); + bucket.cookie = 0; + } + } else { + releaseBuckets(mBuckets, mBucketCount); + mBuckets = NULL; + } + mFilledBuckets = 0; + } + mSize = 0; + } +} + +ssize_t BasicHashtableImpl::next(ssize_t index) const { + if (mSize) { + while (size_t(++index) < mBucketCount) { + const Bucket& bucket = bucketAt(mBuckets, index); + if (bucket.cookie & Bucket::PRESENT) { + return index; + } + } + } + return -1; +} + +ssize_t BasicHashtableImpl::find(ssize_t index, hash_t hash, + const void* __restrict__ key) const { + if (!mSize) { + return -1; + } + + hash = trimHash(hash); + if (index < 0) { + index = chainStart(hash, mBucketCount); + + const Bucket& bucket = bucketAt(mBuckets, size_t(index)); + if (bucket.cookie & Bucket::PRESENT) { + if (compareBucketKey(bucket, key)) { + return index; + } + } else { + if (!(bucket.cookie & Bucket::COLLISION)) { + return -1; + } + } + } + + size_t inc = chainIncrement(hash, mBucketCount); + for (;;) { + index = chainSeek(index, inc, mBucketCount); + + const Bucket& bucket = bucketAt(mBuckets, size_t(index)); + if (bucket.cookie & Bucket::PRESENT) { + if ((bucket.cookie & Bucket::HASH_MASK) == hash + && compareBucketKey(bucket, key)) { + return index; + } + } + if (!(bucket.cookie & Bucket::COLLISION)) { + return -1; + } + } +} + +size_t BasicHashtableImpl::add(hash_t hash, const void* entry) { + if (!mBuckets) { + mBuckets = allocateBuckets(mBucketCount); + } else { + edit(); + } + + hash = trimHash(hash); + for (;;) { + size_t index = chainStart(hash, mBucketCount); + Bucket* bucket = &bucketAt(mBuckets, size_t(index)); + if (bucket->cookie & Bucket::PRESENT) { + size_t inc = chainIncrement(hash, mBucketCount); + do { + bucket->cookie |= Bucket::COLLISION; + index = chainSeek(index, inc, mBucketCount); + bucket = &bucketAt(mBuckets, size_t(index)); + } while (bucket->cookie & Bucket::PRESENT); + } + + uint32_t collision = bucket->cookie & Bucket::COLLISION; + if (!collision) { + if (mFilledBuckets >= mCapacity) { + rehash(mCapacity * 2, mLoadFactor); + continue; + } + mFilledBuckets += 1; + } + + bucket->cookie = collision | Bucket::PRESENT | hash; + mSize += 1; + initializeBucketEntry(*bucket, entry); + return index; + } +} + +void BasicHashtableImpl::removeAt(size_t index) { + edit(); + + Bucket& bucket = bucketAt(mBuckets, index); + bucket.cookie &= ~Bucket::PRESENT; + if (!(bucket.cookie & Bucket::COLLISION)) { + mFilledBuckets -= 1; + } + mSize -= 1; + if (!mHasTrivialDestructor) { + destroyBucketEntry(bucket); + } +} + +void BasicHashtableImpl::rehash(size_t minimumCapacity, float loadFactor) { + if (minimumCapacity < mSize) { + minimumCapacity = mSize; + } + size_t newBucketCount, newCapacity; + determineCapacity(minimumCapacity, loadFactor, &newBucketCount, &newCapacity); + + if (newBucketCount != mBucketCount || newCapacity != mCapacity) { + if (mBuckets) { + void* newBuckets; + if (mSize) { + newBuckets = allocateBuckets(newBucketCount); + for (size_t i = 0; i < mBucketCount; i++) { + const Bucket& fromBucket = bucketAt(mBuckets, i); + if (fromBucket.cookie & Bucket::PRESENT) { + hash_t hash = fromBucket.cookie & Bucket::HASH_MASK; + size_t index = chainStart(hash, newBucketCount); + Bucket* toBucket = &bucketAt(newBuckets, size_t(index)); + if (toBucket->cookie & Bucket::PRESENT) { + size_t inc = chainIncrement(hash, newBucketCount); + do { + toBucket->cookie |= Bucket::COLLISION; + index = chainSeek(index, inc, newBucketCount); + toBucket = &bucketAt(newBuckets, size_t(index)); + } while (toBucket->cookie & Bucket::PRESENT); + } + toBucket->cookie = Bucket::PRESENT | hash; + initializeBucketEntry(*toBucket, fromBucket.entry); + } + } + } else { + newBuckets = NULL; + } + releaseBuckets(mBuckets, mBucketCount); + mBuckets = newBuckets; + mFilledBuckets = mSize; + } + mBucketCount = newBucketCount; + mCapacity = newCapacity; + } + mLoadFactor = loadFactor; +} + +void* BasicHashtableImpl::allocateBuckets(size_t count) const { + size_t bytes = count * mBucketSize; + SharedBuffer* sb = SharedBuffer::alloc(bytes); + LOG_ALWAYS_FATAL_IF(!sb, "Could not allocate %u bytes for hashtable with %u buckets.", + uint32_t(bytes), uint32_t(count)); + void* buckets = sb->data(); + for (size_t i = 0; i < count; i++) { + Bucket& bucket = bucketAt(buckets, i); + bucket.cookie = 0; + } + return buckets; +} + +void BasicHashtableImpl::releaseBuckets(void* __restrict__ buckets, size_t count) const { + SharedBuffer* sb = SharedBuffer::bufferFromData(buckets); + if (sb->release(SharedBuffer::eKeepStorage) == 1) { + destroyBuckets(buckets, count); + SharedBuffer::dealloc(sb); + } +} + +void BasicHashtableImpl::destroyBuckets(void* __restrict__ buckets, size_t count) const { + if (!mHasTrivialDestructor) { + for (size_t i = 0; i < count; i++) { + Bucket& bucket = bucketAt(buckets, i); + if (bucket.cookie & Bucket::PRESENT) { + destroyBucketEntry(bucket); + } + } + } +} + +void BasicHashtableImpl::copyBuckets(const void* __restrict__ fromBuckets, + void* __restrict__ toBuckets, size_t count) const { + for (size_t i = 0; i < count; i++) { + const Bucket& fromBucket = bucketAt(fromBuckets, i); + Bucket& toBucket = bucketAt(toBuckets, i); + toBucket.cookie = fromBucket.cookie; + if (fromBucket.cookie & Bucket::PRESENT) { + initializeBucketEntry(toBucket, fromBucket.entry); + } + } +} + +// Table of 31-bit primes where each prime is no less than twice as large +// as the previous one. Generated by "primes.py". +static size_t PRIMES[] = { + 5, + 11, + 23, + 47, + 97, + 197, + 397, + 797, + 1597, + 3203, + 6421, + 12853, + 25717, + 51437, + 102877, + 205759, + 411527, + 823117, + 1646237, + 3292489, + 6584983, + 13169977, + 26339969, + 52679969, + 105359939, + 210719881, + 421439783, + 842879579, + 1685759167, + 0, +}; + +void BasicHashtableImpl::determineCapacity(size_t minimumCapacity, float loadFactor, + size_t* __restrict__ outBucketCount, size_t* __restrict__ outCapacity) { + LOG_ALWAYS_FATAL_IF(loadFactor <= 0.0f || loadFactor > 1.0f, + "Invalid load factor %0.3f. Must be in the range (0, 1].", loadFactor); + + size_t count = ceilf(minimumCapacity / loadFactor) + 1; + size_t i = 0; + while (count > PRIMES[i] && i < NELEM(PRIMES)) { + i++; + } + count = PRIMES[i]; + LOG_ALWAYS_FATAL_IF(!count, "Could not determine required number of buckets for " + "hashtable with minimum capacity %u and load factor %0.3f.", + uint32_t(minimumCapacity), loadFactor); + *outBucketCount = count; + *outCapacity = ceilf((count - 1) * loadFactor); +} + +}; // namespace android diff --git a/libs/utils/primes.py b/libs/utils/primes.py new file mode 100755 index 000000000..e161dd801 --- /dev/null +++ b/libs/utils/primes.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python2.6 +# +# Copyright (C) 2011 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. +# + +# +# Generates a table of prime numbers for use in BasicHashtable.cpp. +# +# Each prime is chosen such that it is a little more than twice as large as +# the previous prime in the table. This makes it easier to choose a new +# hashtable size when the underlying array is grown by as nominal factor +# of two each time. +# + +def is_odd_prime(n): + limit = (n - 1) / 2 + d = 3 + while d <= limit: + if n % d == 0: + return False + d += 2 + return True + +print "static size_t PRIMES[] = {" + +n = 5 +max = 2**31 - 1 +while n < max: + print " %d," % (n) + n = n * 2 + 1 + while not is_odd_prime(n): + n += 2 + +print " 0," +print "};" diff --git a/libs/utils/tests/Android.mk b/libs/utils/tests/Android.mk index b97f52f5b..58230f429 100644 --- a/libs/utils/tests/Android.mk +++ b/libs/utils/tests/Android.mk @@ -4,9 +4,10 @@ include $(CLEAR_VARS) # Build the unit tests. test_src_files := \ + BasicHashtable_test.cpp \ BlobCache_test.cpp \ - ObbFile_test.cpp \ Looper_test.cpp \ + ObbFile_test.cpp \ String8_test.cpp \ Unicode_test.cpp \ ZipFileRO_test.cpp \ diff --git a/libs/utils/tests/BasicHashtable_test.cpp b/libs/utils/tests/BasicHashtable_test.cpp new file mode 100644 index 000000000..764082dc0 --- /dev/null +++ b/libs/utils/tests/BasicHashtable_test.cpp @@ -0,0 +1,577 @@ +/* + * Copyright (C) 2011 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 "BasicHashtable_test" + +#include +#include +#include +#include + +namespace android { + +typedef int SimpleKey; +typedef int SimpleValue; +typedef key_value_pair_t SimpleEntry; +typedef BasicHashtable SimpleHashtable; + +struct ComplexKey { + int k; + + explicit ComplexKey(int k) : k(k) { + instanceCount += 1; + } + + ComplexKey(const ComplexKey& other) : k(other.k) { + instanceCount += 1; + } + + ~ComplexKey() { + instanceCount -= 1; + } + + bool operator ==(const ComplexKey& other) const { + return k == other.k; + } + + bool operator !=(const ComplexKey& other) const { + return k != other.k; + } + + static ssize_t instanceCount; +}; + +ssize_t ComplexKey::instanceCount = 0; + +template<> inline hash_t hash_type(const ComplexKey& value) { + return hash_type(value.k); +} + +struct ComplexValue { + int v; + + explicit ComplexValue(int v) : v(v) { + instanceCount += 1; + } + + ComplexValue(const ComplexValue& other) : v(other.v) { + instanceCount += 1; + } + + ~ComplexValue() { + instanceCount -= 1; + } + + static ssize_t instanceCount; +}; + +ssize_t ComplexValue::instanceCount = 0; + +typedef key_value_pair_t ComplexEntry; +typedef BasicHashtable ComplexHashtable; + +class BasicHashtableTest : public testing::Test { +protected: + virtual void SetUp() { + ComplexKey::instanceCount = 0; + ComplexValue::instanceCount = 0; + } + + virtual void TearDown() { + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(0, 0)); + } + + void assertInstanceCount(ssize_t keys, ssize_t values) { + if (keys != ComplexKey::instanceCount || values != ComplexValue::instanceCount) { + FAIL() << "Expected " << keys << " keys and " << values << " values " + "but there were actually " << ComplexKey::instanceCount << " keys and " + << ComplexValue::instanceCount << " values"; + } + } + +public: + template + static void cookieAt(const BasicHashtable& h, size_t index, + bool* collision, bool* present, hash_t* hash) { + uint32_t cookie = h.cookieAt(index); + *collision = cookie & BasicHashtable::Bucket::COLLISION; + *present = cookie & BasicHashtable::Bucket::PRESENT; + *hash = cookie & BasicHashtable::Bucket::HASH_MASK; + } + + template + static const void* getBuckets(const BasicHashtable& h) { + return h.mBuckets; + } +}; + +template +static size_t add(BasicHashtable >& h, + const TKey& key, const TValue& value) { + return h.add(hash_type(key), key_value_pair_t(key, value)); +} + +template +static ssize_t find(BasicHashtable >& h, + ssize_t index, const TKey& key) { + return h.find(index, hash_type(key), key); +} + +template +static bool remove(BasicHashtable >& h, + const TKey& key) { + ssize_t index = find(h, -1, key); + if (index >= 0) { + h.removeAt(index); + return true; + } + return false; +} + +template +static void getKeyValue(const TEntry& entry, int* key, int* value); + +template <> void getKeyValue(const SimpleEntry& entry, int* key, int* value) { + *key = entry.key; + *value = entry.value; +} + +template <> void getKeyValue(const ComplexEntry& entry, int* key, int* value) { + *key = entry.key.k; + *value = entry.value.v; +} + +template +static void dump(BasicHashtable >& h) { + LOGD("hashtable %p, size=%u, capacity=%u, bucketCount=%u", + &h, h.size(), h.capacity(), h.bucketCount()); + for (size_t i = 0; i < h.bucketCount(); i++) { + bool collision, present; + hash_t hash; + BasicHashtableTest::cookieAt(h, i, &collision, &present, &hash); + if (present) { + int key, value; + getKeyValue(h.entryAt(i), &key, &value); + LOGD(" [%3u] = collision=%d, present=%d, hash=0x%08x, key=%3d, value=%3d, " + "hash_type(key)=0x%08x", + i, collision, present, hash, key, value, hash_type(key)); + } else { + LOGD(" [%3u] = collision=%d, present=%d", + i, collision, present); + } + } +} + +TEST_F(BasicHashtableTest, DefaultConstructor_WithDefaultProperties) { + SimpleHashtable h; + + EXPECT_EQ(0U, h.size()); + EXPECT_EQ(3U, h.capacity()); + EXPECT_EQ(5U, h.bucketCount()); + EXPECT_EQ(0.75f, h.loadFactor()); +} + +TEST_F(BasicHashtableTest, Constructor_WithNonUnityLoadFactor) { + SimpleHashtable h(52, 0.8f); + + EXPECT_EQ(0U, h.size()); + EXPECT_EQ(77U, h.capacity()); + EXPECT_EQ(97U, h.bucketCount()); + EXPECT_EQ(0.8f, h.loadFactor()); +} + +TEST_F(BasicHashtableTest, Constructor_WithUnityLoadFactorAndExactCapacity) { + SimpleHashtable h(46, 1.0f); + + EXPECT_EQ(0U, h.size()); + EXPECT_EQ(46U, h.capacity()); // must be one less than bucketCount because loadFactor == 1.0f + EXPECT_EQ(47U, h.bucketCount()); + EXPECT_EQ(1.0f, h.loadFactor()); +} + +TEST_F(BasicHashtableTest, Constructor_WithUnityLoadFactorAndInexactCapacity) { + SimpleHashtable h(42, 1.0f); + + EXPECT_EQ(0U, h.size()); + EXPECT_EQ(46U, h.capacity()); // must be one less than bucketCount because loadFactor == 1.0f + EXPECT_EQ(47U, h.bucketCount()); + EXPECT_EQ(1.0f, h.loadFactor()); +} + +TEST_F(BasicHashtableTest, FindAddFindRemoveFind_OneEntry) { + SimpleHashtable h; + ssize_t index = find(h, -1, 8); + ASSERT_EQ(-1, index); + + index = add(h, 8, 1); + ASSERT_EQ(1U, h.size()); + + ASSERT_EQ(index, find(h, -1, 8)); + ASSERT_EQ(8, h.entryAt(index).key); + ASSERT_EQ(1, h.entryAt(index).value); + + index = find(h, index, 8); + ASSERT_EQ(-1, index); + + ASSERT_TRUE(remove(h, 8)); + ASSERT_EQ(0U, h.size()); + + index = find(h, -1, 8); + ASSERT_EQ(-1, index); +} + +TEST_F(BasicHashtableTest, FindAddFindRemoveFind_MultipleEntryWithUniqueKey) { + const size_t N = 11; + + SimpleHashtable h; + for (size_t i = 0; i < N; i++) { + ssize_t index = find(h, -1, int(i)); + ASSERT_EQ(-1, index); + + index = add(h, int(i), int(i * 10)); + ASSERT_EQ(i + 1, h.size()); + + ASSERT_EQ(index, find(h, -1, int(i))); + ASSERT_EQ(int(i), h.entryAt(index).key); + ASSERT_EQ(int(i * 10), h.entryAt(index).value); + + index = find(h, index, int(i)); + ASSERT_EQ(-1, index); + } + + for (size_t i = N; --i > 0; ) { + ASSERT_TRUE(remove(h, int(i))) << "i = " << i; + ASSERT_EQ(i, h.size()); + + ssize_t index = find(h, -1, int(i)); + ASSERT_EQ(-1, index); + } +} + +TEST_F(BasicHashtableTest, FindAddFindRemoveFind_MultipleEntryWithDuplicateKey) { + const size_t N = 11; + const int K = 1; + + SimpleHashtable h; + for (size_t i = 0; i < N; i++) { + ssize_t index = find(h, -1, K); + if (i == 0) { + ASSERT_EQ(-1, index); + } else { + ASSERT_NE(-1, index); + } + + add(h, K, int(i)); + ASSERT_EQ(i + 1, h.size()); + + index = -1; + int values = 0; + for (size_t j = 0; j <= i; j++) { + index = find(h, index, K); + ASSERT_GE(index, 0); + ASSERT_EQ(K, h.entryAt(index).key); + values |= 1 << h.entryAt(index).value; + } + ASSERT_EQ(values, (1 << (i + 1)) - 1); + + index = find(h, index, K); + ASSERT_EQ(-1, index); + } + + for (size_t i = N; --i > 0; ) { + ASSERT_TRUE(remove(h, K)) << "i = " << i; + ASSERT_EQ(i, h.size()); + + ssize_t index = -1; + for (size_t j = 0; j < i; j++) { + index = find(h, index, K); + ASSERT_GE(index, 0); + ASSERT_EQ(K, h.entryAt(index).key); + } + + index = find(h, index, K); + ASSERT_EQ(-1, index); + } +} + +TEST_F(BasicHashtableTest, Clear_WhenAlreadyEmpty_DoesNothing) { + SimpleHashtable h; + h.clear(); + + EXPECT_EQ(0U, h.size()); + EXPECT_EQ(3U, h.capacity()); + EXPECT_EQ(5U, h.bucketCount()); + EXPECT_EQ(0.75f, h.loadFactor()); +} + +TEST_F(BasicHashtableTest, Clear_AfterElementsAdded_RemovesThem) { + SimpleHashtable h; + add(h, 0, 0); + add(h, 1, 0); + h.clear(); + + EXPECT_EQ(0U, h.size()); + EXPECT_EQ(3U, h.capacity()); + EXPECT_EQ(5U, h.bucketCount()); + EXPECT_EQ(0.75f, h.loadFactor()); +} + +TEST_F(BasicHashtableTest, Clear_AfterElementsAdded_DestroysThem) { + ComplexHashtable h; + add(h, ComplexKey(0), ComplexValue(0)); + add(h, ComplexKey(1), ComplexValue(0)); + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2)); + + h.clear(); + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(0, 0)); + + EXPECT_EQ(0U, h.size()); + EXPECT_EQ(3U, h.capacity()); + EXPECT_EQ(5U, h.bucketCount()); + EXPECT_EQ(0.75f, h.loadFactor()); +} + +TEST_F(BasicHashtableTest, Remove_AfterElementsAdded_DestroysThem) { + ComplexHashtable h; + add(h, ComplexKey(0), ComplexValue(0)); + add(h, ComplexKey(1), ComplexValue(0)); + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2)); + + ASSERT_TRUE(remove(h, ComplexKey(0))); + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(1, 1)); + + ASSERT_TRUE(remove(h, ComplexKey(1))); + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(0, 0)); + + EXPECT_EQ(0U, h.size()); + EXPECT_EQ(3U, h.capacity()); + EXPECT_EQ(5U, h.bucketCount()); + EXPECT_EQ(0.75f, h.loadFactor()); +} + +TEST_F(BasicHashtableTest, Destructor_AfterElementsAdded_DestroysThem) { + { + ComplexHashtable h; + add(h, ComplexKey(0), ComplexValue(0)); + add(h, ComplexKey(1), ComplexValue(0)); + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2)); + } // h is destroyed here + + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(0, 0)); +} + +TEST_F(BasicHashtableTest, Next_WhenEmpty_ReturnsMinusOne) { + SimpleHashtable h; + + ASSERT_EQ(-1, h.next(-1)); +} + +TEST_F(BasicHashtableTest, Next_WhenNonEmpty_IteratesOverAllEntries) { + const int N = 88; + + SimpleHashtable h; + for (int i = 0; i < N; i++) { + add(h, i, i * 10); + } + + bool set[N]; + memset(set, 0, sizeof(bool) * N); + int count = 0; + for (ssize_t index = -1; (index = h.next(index)) != -1; ) { + ASSERT_GE(index, 0); + ASSERT_LT(size_t(index), h.bucketCount()); + + const SimpleEntry& entry = h.entryAt(index); + ASSERT_GE(entry.key, 0); + ASSERT_LT(entry.key, N); + ASSERT_EQ(false, set[entry.key]); + ASSERT_EQ(entry.key * 10, entry.value); + + set[entry.key] = true; + count += 1; + } + ASSERT_EQ(N, count); +} + +TEST_F(BasicHashtableTest, Add_RehashesOnDemand) { + SimpleHashtable h; + size_t initialCapacity = h.capacity(); + size_t initialBucketCount = h.bucketCount(); + + for (size_t i = 0; i < initialCapacity; i++) { + add(h, int(i), 0); + } + + EXPECT_EQ(initialCapacity, h.size()); + EXPECT_EQ(initialCapacity, h.capacity()); + EXPECT_EQ(initialBucketCount, h.bucketCount()); + + add(h, -1, -1); + + EXPECT_EQ(initialCapacity + 1, h.size()); + EXPECT_GT(h.capacity(), initialCapacity); + EXPECT_GT(h.bucketCount(), initialBucketCount); + EXPECT_GT(h.bucketCount(), h.capacity()); +} + +TEST_F(BasicHashtableTest, Rehash_WhenCapacityAndBucketCountUnchanged_DoesNothing) { + ComplexHashtable h; + add(h, ComplexKey(0), ComplexValue(0)); + const void* oldBuckets = getBuckets(h); + ASSERT_NE((void*)NULL, oldBuckets); + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(1, 1)); + + h.rehash(h.capacity(), h.loadFactor()); + + ASSERT_EQ(oldBuckets, getBuckets(h)); + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(1, 1)); +} + +TEST_F(BasicHashtableTest, Rehash_WhenEmptyAndHasNoBuckets_ButDoesNotAllocateBuckets) { + ComplexHashtable h; + ASSERT_EQ((void*)NULL, getBuckets(h)); + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(0, 0)); + + h.rehash(9, 1.0f); + + EXPECT_EQ(0U, h.size()); + EXPECT_EQ(10U, h.capacity()); + EXPECT_EQ(11U, h.bucketCount()); + EXPECT_EQ(1.0f, h.loadFactor()); + EXPECT_EQ((void*)NULL, getBuckets(h)); + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(0, 0)); +} + +TEST_F(BasicHashtableTest, Rehash_WhenEmptyAndHasBuckets_ReleasesBucketsAndSetsCapacity) { + ComplexHashtable h(10); + add(h, ComplexKey(0), ComplexValue(0)); + ASSERT_TRUE(remove(h, ComplexKey(0))); + ASSERT_NE((void*)NULL, getBuckets(h)); + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(0, 0)); + + h.rehash(0, 0.75f); + + EXPECT_EQ(0U, h.size()); + EXPECT_EQ(3U, h.capacity()); + EXPECT_EQ(5U, h.bucketCount()); + EXPECT_EQ(0.75f, h.loadFactor()); + EXPECT_EQ((void*)NULL, getBuckets(h)); + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(0, 0)); +} + +TEST_F(BasicHashtableTest, Rehash_WhenLessThanCurrentCapacity_ShrinksBuckets) { + ComplexHashtable h(10); + add(h, ComplexKey(0), ComplexValue(0)); + add(h, ComplexKey(1), ComplexValue(1)); + const void* oldBuckets = getBuckets(h); + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2)); + + h.rehash(0, 0.75f); + + EXPECT_EQ(2U, h.size()); + EXPECT_EQ(3U, h.capacity()); + EXPECT_EQ(5U, h.bucketCount()); + EXPECT_EQ(0.75f, h.loadFactor()); + EXPECT_NE(oldBuckets, getBuckets(h)); + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2)); +} + +TEST_F(BasicHashtableTest, CopyOnWrite) { + ComplexHashtable h1; + add(h1, ComplexKey(0), ComplexValue(0)); + add(h1, ComplexKey(1), ComplexValue(1)); + const void* originalBuckets = getBuckets(h1); + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2)); + ssize_t index0 = find(h1, -1, ComplexKey(0)); + EXPECT_GE(index0, 0); + + // copy constructor acquires shared reference + ComplexHashtable h2(h1); + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2)); + ASSERT_EQ(originalBuckets, getBuckets(h2)); + EXPECT_EQ(h1.size(), h2.size()); + EXPECT_EQ(h1.capacity(), h2.capacity()); + EXPECT_EQ(h1.bucketCount(), h2.bucketCount()); + EXPECT_EQ(h1.loadFactor(), h2.loadFactor()); + EXPECT_EQ(index0, find(h2, -1, ComplexKey(0))); + + // operator= acquires shared reference + ComplexHashtable h3; + h3 = h2; + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2)); + ASSERT_EQ(originalBuckets, getBuckets(h3)); + EXPECT_EQ(h1.size(), h3.size()); + EXPECT_EQ(h1.capacity(), h3.capacity()); + EXPECT_EQ(h1.bucketCount(), h3.bucketCount()); + EXPECT_EQ(h1.loadFactor(), h3.loadFactor()); + EXPECT_EQ(index0, find(h3, -1, ComplexKey(0))); + + // editEntryAt copies shared contents + h1.editEntryAt(index0).value.v = 42; + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(4, 4)); + ASSERT_NE(originalBuckets, getBuckets(h1)); + EXPECT_EQ(42, h1.entryAt(index0).value.v); + EXPECT_EQ(0, h2.entryAt(index0).value.v); + EXPECT_EQ(0, h3.entryAt(index0).value.v); + + // clear releases reference to shared contents + h2.clear(); + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(4, 4)); + EXPECT_EQ(0U, h2.size()); + ASSERT_NE(originalBuckets, getBuckets(h2)); + + // operator= acquires shared reference, destroys unshared contents + h1 = h3; + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2)); + ASSERT_EQ(originalBuckets, getBuckets(h1)); + EXPECT_EQ(h3.size(), h1.size()); + EXPECT_EQ(h3.capacity(), h1.capacity()); + EXPECT_EQ(h3.bucketCount(), h1.bucketCount()); + EXPECT_EQ(h3.loadFactor(), h1.loadFactor()); + EXPECT_EQ(index0, find(h1, -1, ComplexKey(0))); + + // add copies shared contents + add(h1, ComplexKey(2), ComplexValue(2)); + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(5, 5)); + ASSERT_NE(originalBuckets, getBuckets(h1)); + EXPECT_EQ(3U, h1.size()); + EXPECT_EQ(0U, h2.size()); + EXPECT_EQ(2U, h3.size()); + + // remove copies shared contents + h1 = h3; + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2)); + ASSERT_EQ(originalBuckets, getBuckets(h1)); + h1.removeAt(index0); + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(3, 3)); + ASSERT_NE(originalBuckets, getBuckets(h1)); + EXPECT_EQ(1U, h1.size()); + EXPECT_EQ(0U, h2.size()); + EXPECT_EQ(2U, h3.size()); + + // rehash copies shared contents + h1 = h3; + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2)); + ASSERT_EQ(originalBuckets, getBuckets(h1)); + h1.rehash(10, 1.0f); + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(4, 4)); + ASSERT_NE(originalBuckets, getBuckets(h1)); + EXPECT_EQ(2U, h1.size()); + EXPECT_EQ(0U, h2.size()); + EXPECT_EQ(2U, h3.size()); +} + +} // namespace android From 142dbcd817ac7960061735e5e7ea174e0938fb09 Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Thu, 24 Nov 2011 11:54:21 -0800 Subject: [PATCH 378/541] Use sized integer typedefs in hash_type specializations. Change-Id: I3f9e004db2f3be1cb43a885c3ae142f251fd6845 --- include/utils/TypeHelpers.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/include/utils/TypeHelpers.h b/include/utils/TypeHelpers.h index 75388176d..7b4fb70ba 100644 --- a/include/utils/TypeHelpers.h +++ b/include/utils/TypeHelpers.h @@ -272,14 +272,14 @@ hash_t hash_type(const TKey& key); return hash_type(*reinterpret_cast(&value)); } ANDROID_INT32_HASH(bool) -ANDROID_INT32_HASH(char) -ANDROID_INT32_HASH(unsigned char) -ANDROID_INT32_HASH(short) -ANDROID_INT32_HASH(unsigned short) -ANDROID_INT32_HASH(int) -ANDROID_INT32_HASH(unsigned int) -ANDROID_INT64_HASH(long) -ANDROID_INT64_HASH(unsigned long) +ANDROID_INT32_HASH(int8_t) +ANDROID_INT32_HASH(uint8_t) +ANDROID_INT32_HASH(int16_t) +ANDROID_INT32_HASH(uint16_t) +ANDROID_INT32_HASH(int32_t) +ANDROID_INT32_HASH(uint32_t) +ANDROID_INT64_HASH(int64_t) +ANDROID_INT64_HASH(uint64_t) ANDROID_REINTERPRET_HASH(float, uint32_t) ANDROID_REINTERPRET_HASH(double, uint64_t) From 587042dfa3544f2c4925724f91d87a2f31a72b9d Mon Sep 17 00:00:00 2001 From: Romain Guy Date: Thu, 8 Dec 2011 18:19:39 -0800 Subject: [PATCH 379/541] GenerationCache::get would return a random value instead of NULL Bug #5401917 This was causing a ton of random crashes in apps. Change-Id: I9069a060824ec89115cd3bcd38beaeb9ecc4488e --- include/utils/GenerationCache.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/include/utils/GenerationCache.h b/include/utils/GenerationCache.h index 83cda8689..da85a9aeb 100644 --- a/include/utils/GenerationCache.h +++ b/include/utils/GenerationCache.h @@ -88,11 +88,13 @@ private: void attachToCache(const sp >& entry); void detachFromCache(const sp >& entry); + + const V mNullValue; }; // class GenerationCache template GenerationCache::GenerationCache(uint32_t maxCapacity): mMaxCapacity(maxCapacity), - mListener(NULL) { + mListener(NULL), mNullValue(NULL) { }; template @@ -154,7 +156,7 @@ const V& GenerationCache::get(const K& key) { return entry->value; } - return NULL; + return mNullValue; } template From 53ddace1a585c0515ea1c2e2091c4a8cd3a94864 Mon Sep 17 00:00:00 2001 From: Joe Onorato Date: Wed, 14 Dec 2011 20:59:30 -0800 Subject: [PATCH 380/541] Add a new ui mode for "appliance" The idea is that this is a device which is more-or-less headless. It might have some limited interaction capabilities, but it's not something that you want to rely on having. Change-Id: Ib92f53a120bf83de781728011721a4859def7d9f --- include/utils/ResourceTypes.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h index 612ff9346..e045b2c78 100644 --- a/include/utils/ResourceTypes.h +++ b/include/utils/ResourceTypes.h @@ -955,6 +955,7 @@ struct ResTable_config UI_MODE_TYPE_DESK = ACONFIGURATION_UI_MODE_TYPE_DESK, UI_MODE_TYPE_CAR = ACONFIGURATION_UI_MODE_TYPE_CAR, UI_MODE_TYPE_TELEVISION = ACONFIGURATION_UI_MODE_TYPE_TELEVISION, + UI_MODE_TYPE_APPLIANCE = ACONFIGURATION_UI_MODE_TYPE_APPLIANCE, // uiMode bits for the night switch. MASK_UI_MODE_NIGHT = 0x30, From eb0953307ce75cec031aedbf21abff08e5a737e5 Mon Sep 17 00:00:00 2001 From: Steve Block Date: Tue, 20 Dec 2011 16:23:08 +0000 Subject: [PATCH 381/541] Rename (IF_)LOGD(_IF) to (IF_)ALOGD(_IF) DO NOT MERGE See https://android-git.corp.google.com/g/156016 Bug: 5449033 Change-Id: I4c4e33bb9df3e39e11cd985e193e6fbab4635298 --- libs/utils/Asset.cpp | 6 ++-- libs/utils/AssetManager.cpp | 10 +++---- libs/utils/BackupData.cpp | 22 +++++++-------- libs/utils/BackupHelpers.cpp | 2 +- libs/utils/CallStack.cpp | 2 +- libs/utils/FileMap.cpp | 4 +-- libs/utils/Looper.cpp | 36 ++++++++++++------------ libs/utils/PropertyMap.cpp | 4 +-- libs/utils/RefBase.cpp | 22 +++++++-------- libs/utils/ResourceTypes.cpp | 2 +- libs/utils/StopWatch.cpp | 4 +-- libs/utils/SystemClock.cpp | 2 +- libs/utils/Tokenizer.cpp | 6 ++-- libs/utils/ZipFileRO.cpp | 2 +- libs/utils/ZipUtils.cpp | 8 +++--- libs/utils/tests/BasicHashtable_test.cpp | 6 ++-- 16 files changed, 69 insertions(+), 69 deletions(-) diff --git a/libs/utils/Asset.cpp b/libs/utils/Asset.cpp index 7fd2c8731..7c34c6a35 100644 --- a/libs/utils/Asset.cpp +++ b/libs/utils/Asset.cpp @@ -210,7 +210,7 @@ Asset::~Asset(void) offset = ftell(fp); fclose(fp); if (!scanResult) { - LOGD("File '%s' is not in gzip format\n", fileName); + ALOGD("File '%s' is not in gzip format\n", fileName); ::close(fd); return NULL; } @@ -384,12 +384,12 @@ status_t _FileAsset::openChunk(const char* fileName, int fd, off64_t offset, siz fileLength = lseek64(fd, 0, SEEK_END); if (fileLength == (off64_t) -1) { // probably a bad file descriptor - LOGD("failed lseek (errno=%d)\n", errno); + ALOGD("failed lseek (errno=%d)\n", errno); return UNKNOWN_ERROR; } if ((off64_t) (offset + length) > fileLength) { - LOGD("start (%ld) + len (%ld) > end (%ld)\n", + ALOGD("start (%ld) + len (%ld) > end (%ld)\n", (long) offset, (long) length, (long) fileLength); return BAD_INDEX; } diff --git a/libs/utils/AssetManager.cpp b/libs/utils/AssetManager.cpp index 203e6fa37..e7c4d4757 100644 --- a/libs/utils/AssetManager.cpp +++ b/libs/utils/AssetManager.cpp @@ -283,7 +283,7 @@ bool AssetManager::getZipEntryCrcLocked(const String8& zipPath, const char* entr bool AssetManager::createIdmapFileLocked(const String8& originalPath, const String8& overlayPath, const String8& idmapPath) { - LOGD("%s: originalPath=%s overlayPath=%s idmapPath=%s\n", + ALOGD("%s: originalPath=%s overlayPath=%s idmapPath=%s\n", __FUNCTION__, originalPath.string(), overlayPath.string(), idmapPath.string()); ResTable tables[2]; const String8* paths[2] = { &originalPath, &overlayPath }; @@ -923,7 +923,7 @@ Asset* AssetManager::openInLocaleVendorLocked(const char* fileName, AccessMode m */ if (found) { if (pAsset == NULL) - LOGD("Expected file not found: '%s'\n", path.string()); + ALOGD("Expected file not found: '%s'\n", path.string()); return pAsset; } } @@ -1333,7 +1333,7 @@ bool AssetManager::scanAndMergeDirLocked(SortedVector* pMerg //printf("+++ no match on '%s'\n", (const char*) match); } - LOGD("HEY: size=%d removing %d\n", (int)pContents->size(), i); + ALOGD("HEY: size=%d removing %d\n", (int)pContents->size(), i); pContents->removeAt(i); i--; // adjust "for" loop count--; // and loop limit @@ -1652,7 +1652,7 @@ void AssetManager::loadFileNameCacheLocked(void) #ifdef DO_TIMINGS timer.stop(); - LOGD("Cache scan took %.3fms\n", + ALOGD("Cache scan took %.3fms\n", timer.durationUsecs() / 1000.0); #endif @@ -1784,7 +1784,7 @@ AssetManager::SharedZip::SharedZip(const String8& path, time_t modWhen) mZipFile = new ZipFileRO; ALOGV("+++ opening zip '%s'\n", mPath.string()); if (mZipFile->open(mPath.string()) != NO_ERROR) { - LOGD("failed to open Zip archive '%s'\n", mPath.string()); + ALOGD("failed to open Zip archive '%s'\n", mPath.string()); delete mZipFile; mZipFile = NULL; } diff --git a/libs/utils/BackupData.cpp b/libs/utils/BackupData.cpp index 879126393..5afe2dc05 100644 --- a/libs/utils/BackupData.cpp +++ b/libs/utils/BackupData.cpp @@ -112,8 +112,8 @@ BackupDataWriter::WriteEntityHeader(const String8& key, size_t dataSize) k = key; } if (DEBUG) { - LOGD("Writing header: prefix='%s' key='%s' dataSize=%d", m_keyPrefix.string(), key.string(), - dataSize); + ALOGD("Writing header: prefix='%s' key='%s' dataSize=%d", m_keyPrefix.string(), + key.string(), dataSize); } entity_header_v1 header; @@ -151,11 +151,11 @@ BackupDataWriter::WriteEntityHeader(const String8& key, size_t dataSize) status_t BackupDataWriter::WriteEntityData(const void* data, size_t size) { - if (DEBUG) LOGD("Writing data: size=%lu", (unsigned long) size); + if (DEBUG) ALOGD("Writing data: size=%lu", (unsigned long) size); if (m_status != NO_ERROR) { if (DEBUG) { - LOGD("Not writing data - stream in error state %d (%s)", m_status, strerror(m_status)); + ALOGD("Not writing data - stream in error state %d (%s)", m_status, strerror(m_status)); } return m_status; } @@ -166,7 +166,7 @@ BackupDataWriter::WriteEntityData(const void* data, size_t size) ssize_t amt = write(m_fd, data, size); if (amt != (ssize_t)size) { m_status = errno; - if (DEBUG) LOGD("write returned error %d (%s)", m_status, strerror(m_status)); + if (DEBUG) ALOGD("write returned error %d (%s)", m_status, strerror(m_status)); return m_status; } m_pos += amt; @@ -208,7 +208,7 @@ BackupDataReader::Status() m_done = true; \ } else { \ m_status = errno; \ - LOGD("CHECK_SIZE(a=%ld e=%ld) failed at line %d m_status='%s'", \ + ALOGD("CHECK_SIZE(a=%ld e=%ld) failed at line %d m_status='%s'", \ long(actual), long(expected), __LINE__, strerror(m_status)); \ } \ return m_status; \ @@ -218,7 +218,7 @@ BackupDataReader::Status() do { \ status_t err = skip_padding(); \ if (err != NO_ERROR) { \ - LOGD("SKIP_PADDING FAILED at line %d", __LINE__); \ + ALOGD("SKIP_PADDING FAILED at line %d", __LINE__); \ m_status = err; \ return err; \ } \ @@ -261,7 +261,7 @@ BackupDataReader::ReadNextHeader(bool* done, int* type) { m_header.entity.keyLen = fromlel(m_header.entity.keyLen); if (m_header.entity.keyLen <= 0) { - LOGD("Entity header at %d has keyLen<=0: 0x%08x\n", (int)m_pos, + ALOGD("Entity header at %d has keyLen<=0: 0x%08x\n", (int)m_pos, (int)m_header.entity.keyLen); m_status = EINVAL; } @@ -285,7 +285,7 @@ BackupDataReader::ReadNextHeader(bool* done, int* type) break; } default: - LOGD("Chunk header at %d has invalid type: 0x%08x", + ALOGD("Chunk header at %d has invalid type: 0x%08x", (int)(m_pos - sizeof(m_header)), (int)m_header.type); m_status = EINVAL; } @@ -339,7 +339,7 @@ BackupDataReader::ReadEntityData(void* data, size_t size) return -1; } int remaining = m_dataEndPos - m_pos; - //LOGD("ReadEntityData size=%d m_pos=0x%x m_dataEndPos=0x%x remaining=%d\n", + //ALOGD("ReadEntityData size=%d m_pos=0x%x m_dataEndPos=0x%x remaining=%d\n", // size, m_pos, m_dataEndPos, remaining); if (remaining <= 0) { return 0; @@ -347,7 +347,7 @@ BackupDataReader::ReadEntityData(void* data, size_t size) if (((int)size) > remaining) { size = remaining; } - //LOGD(" reading %d bytes", size); + //ALOGD(" reading %d bytes", size); int amt = read(m_fd, data, size); if (amt < 0) { m_status = errno; diff --git a/libs/utils/BackupHelpers.cpp b/libs/utils/BackupHelpers.cpp index 7ef30f999..aee7ff089 100644 --- a/libs/utils/BackupHelpers.cpp +++ b/libs/utils/BackupHelpers.cpp @@ -74,7 +74,7 @@ const static int CURRENT_METADATA_VERSION = 1; #if TEST_BACKUP_HELPERS #define LOGP(f, x...) printf(f "\n", x) #else -#define LOGP(x...) LOGD(x) +#define LOGP(x...) ALOGD(x) #endif #endif diff --git a/libs/utils/CallStack.cpp b/libs/utils/CallStack.cpp index c2a5e5534..18fd84f25 100644 --- a/libs/utils/CallStack.cpp +++ b/libs/utils/CallStack.cpp @@ -104,7 +104,7 @@ void CallStack::dump(const char* prefix) const { char line[MAX_BACKTRACE_LINE_LENGTH]; format_backtrace_line(i, &mStack[i], &symbols[i], line, MAX_BACKTRACE_LINE_LENGTH); - LOGD("%s%s", prefix, line); + ALOGD("%s%s", prefix, line); } free_backtrace_symbols(symbols, mCount); } diff --git a/libs/utils/FileMap.cpp b/libs/utils/FileMap.cpp index 294f7b6b2..b2a61f122 100644 --- a/libs/utils/FileMap.cpp +++ b/libs/utils/FileMap.cpp @@ -64,12 +64,12 @@ FileMap::~FileMap(void) } #ifdef HAVE_POSIX_FILEMAP if (mBasePtr && munmap(mBasePtr, mBaseLength) != 0) { - LOGD("munmap(%p, %d) failed\n", mBasePtr, (int) mBaseLength); + ALOGD("munmap(%p, %d) failed\n", mBasePtr, (int) mBaseLength); } #endif #ifdef HAVE_WIN32_FILEMAP if (mBasePtr && UnmapViewOfFile(mBasePtr) == 0) { - LOGD("UnmapViewOfFile(%p) failed, error = %ld\n", mBasePtr, + ALOGD("UnmapViewOfFile(%p) failed, error = %ld\n", mBasePtr, GetLastError() ); } if (mFileMapping != INVALID_HANDLE_VALUE) { diff --git a/libs/utils/Looper.cpp b/libs/utils/Looper.cpp index b54fb9dd7..1bc92cf88 100644 --- a/libs/utils/Looper.cpp +++ b/libs/utils/Looper.cpp @@ -185,7 +185,7 @@ int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outDa int events = response.events; void* data = response.request.data; #if DEBUG_POLL_AND_WAKE - LOGD("%p ~ pollOnce - returning signalled identifier %d: " + ALOGD("%p ~ pollOnce - returning signalled identifier %d: " "fd=%d, events=0x%x, data=%p", this, ident, fd, events, data); #endif @@ -198,7 +198,7 @@ int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outDa if (result != 0) { #if DEBUG_POLL_AND_WAKE - LOGD("%p ~ pollOnce - returning result %d", this, result); + ALOGD("%p ~ pollOnce - returning result %d", this, result); #endif if (outFd != NULL) *outFd = 0; if (outEvents != NULL) *outEvents = NULL; @@ -212,7 +212,7 @@ int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outDa int Looper::pollInner(int timeoutMillis) { #if DEBUG_POLL_AND_WAKE - LOGD("%p ~ pollOnce - waiting: timeoutMillis=%d", this, timeoutMillis); + ALOGD("%p ~ pollOnce - waiting: timeoutMillis=%d", this, timeoutMillis); #endif // Adjust the timeout based on when the next message is due. @@ -224,7 +224,7 @@ int Looper::pollInner(int timeoutMillis) { timeoutMillis = messageTimeoutMillis; } #if DEBUG_POLL_AND_WAKE - LOGD("%p ~ pollOnce - next message in %lldns, adjusted timeout: timeoutMillis=%d", + ALOGD("%p ~ pollOnce - next message in %lldns, adjusted timeout: timeoutMillis=%d", this, mNextMessageUptime - now, timeoutMillis); #endif } @@ -270,7 +270,7 @@ int Looper::pollInner(int timeoutMillis) { // Check for poll timeout. if (eventCount == 0) { #if DEBUG_POLL_AND_WAKE - LOGD("%p ~ pollOnce - timeout", this); + ALOGD("%p ~ pollOnce - timeout", this); #endif result = ALOOPER_POLL_TIMEOUT; goto Done; @@ -278,7 +278,7 @@ int Looper::pollInner(int timeoutMillis) { // Handle all events. #if DEBUG_POLL_AND_WAKE - LOGD("%p ~ pollOnce - handling events from %d fds", this, eventCount); + ALOGD("%p ~ pollOnce - handling events from %d fds", this, eventCount); #endif #ifdef LOOPER_USES_EPOLL @@ -353,7 +353,7 @@ Done: - milliseconds_to_nanoseconds(timeoutMillis); } if (mSampledPolls == SAMPLED_POLLS_TO_AGGREGATE) { - LOGD("%p ~ poll latency statistics: %0.3fms zero timeout, %0.3fms non-zero timeout", this, + ALOGD("%p ~ poll latency statistics: %0.3fms zero timeout, %0.3fms non-zero timeout", this, 0.000001f * float(mSampledZeroPollLatencySum) / mSampledZeroPollCount, 0.000001f * float(mSampledTimeoutPollLatencySum) / mSampledTimeoutPollCount); mSampledPolls = 0; @@ -382,7 +382,7 @@ Done: mLock.unlock(); #if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS - LOGD("%p ~ pollOnce - sending message: handler=%p, what=%d", + ALOGD("%p ~ pollOnce - sending message: handler=%p, what=%d", this, handler.get(), message.what); #endif handler->handleMessage(message); @@ -410,7 +410,7 @@ Done: int events = response.events; void* data = response.request.data; #if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS - LOGD("%p ~ pollOnce - invoking fd event callback %p: fd=%d, events=0x%x, data=%p", + ALOGD("%p ~ pollOnce - invoking fd event callback %p: fd=%d, events=0x%x, data=%p", this, callback, fd, events, data); #endif int callbackResult = callback(fd, events, data); @@ -451,7 +451,7 @@ int Looper::pollAll(int timeoutMillis, int* outFd, int* outEvents, void** outDat void Looper::wake() { #if DEBUG_POLL_AND_WAKE - LOGD("%p ~ wake", this); + ALOGD("%p ~ wake", this); #endif #ifdef LOOPER_STATISTICS @@ -475,12 +475,12 @@ void Looper::wake() { void Looper::awoken() { #if DEBUG_POLL_AND_WAKE - LOGD("%p ~ awoken", this); + ALOGD("%p ~ awoken", this); #endif #ifdef LOOPER_STATISTICS if (mPendingWakeCount == 0) { - LOGD("%p ~ awoken: spurious!", this); + ALOGD("%p ~ awoken: spurious!", this); } else { mSampledWakeCycles += 1; mSampledWakeCountSum += mPendingWakeCount; @@ -488,7 +488,7 @@ void Looper::awoken() { mPendingWakeCount = 0; mPendingWakeTime = -1; if (mSampledWakeCycles == SAMPLED_WAKE_CYCLES_TO_AGGREGATE) { - LOGD("%p ~ wake statistics: %0.3fms wake latency, %0.3f wakes per cycle", this, + ALOGD("%p ~ wake statistics: %0.3fms wake latency, %0.3f wakes per cycle", this, 0.000001f * float(mSampledWakeLatencySum) / mSampledWakeCycles, float(mSampledWakeCountSum) / mSampledWakeCycles); mSampledWakeCycles = 0; @@ -514,7 +514,7 @@ void Looper::pushResponse(int events, const Request& request) { int Looper::addFd(int fd, int ident, int events, ALooper_callbackFunc callback, void* data) { #if DEBUG_CALLBACKS - LOGD("%p ~ addFd - fd=%d, ident=%d, events=0x%x, callback=%p, data=%p", this, fd, ident, + ALOGD("%p ~ addFd - fd=%d, ident=%d, events=0x%x, callback=%p, data=%p", this, fd, ident, events, callback, data); #endif @@ -598,7 +598,7 @@ int Looper::addFd(int fd, int ident, int events, ALooper_callbackFunc callback, int Looper::removeFd(int fd) { #if DEBUG_CALLBACKS - LOGD("%p ~ removeFd - fd=%d", this, fd); + ALOGD("%p ~ removeFd - fd=%d", this, fd); #endif #ifdef LOOPER_USES_EPOLL @@ -675,7 +675,7 @@ void Looper::sendMessageDelayed(nsecs_t uptimeDelay, const sp& h void Looper::sendMessageAtTime(nsecs_t uptime, const sp& handler, const Message& message) { #if DEBUG_CALLBACKS - LOGD("%p ~ sendMessageAtTime - uptime=%lld, handler=%p, what=%d", + ALOGD("%p ~ sendMessageAtTime - uptime=%lld, handler=%p, what=%d", this, uptime, handler.get(), message.what); #endif @@ -708,7 +708,7 @@ void Looper::sendMessageAtTime(nsecs_t uptime, const sp& handler void Looper::removeMessages(const sp& handler) { #if DEBUG_CALLBACKS - LOGD("%p ~ removeMessages - handler=%p", this, handler.get()); + ALOGD("%p ~ removeMessages - handler=%p", this, handler.get()); #endif { // acquire lock @@ -725,7 +725,7 @@ void Looper::removeMessages(const sp& handler) { void Looper::removeMessages(const sp& handler, int what) { #if DEBUG_CALLBACKS - LOGD("%p ~ removeMessages - handler=%p, what=%d", this, handler.get(), what); + ALOGD("%p ~ removeMessages - handler=%p, what=%d", this, handler.get(), what); #endif { // acquire lock diff --git a/libs/utils/PropertyMap.cpp b/libs/utils/PropertyMap.cpp index d472d45c4..99603abca 100644 --- a/libs/utils/PropertyMap.cpp +++ b/libs/utils/PropertyMap.cpp @@ -135,7 +135,7 @@ status_t PropertyMap::load(const String8& filename, PropertyMap** outMap) { status = parser.parse(); #if DEBUG_PARSER_PERFORMANCE nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime; - LOGD("Parsed property file '%s' %d lines in %0.3fms.", + ALOGD("Parsed property file '%s' %d lines in %0.3fms.", tokenizer->getFilename().string(), tokenizer->getLineNumber(), elapsedTime / 1000000.0); #endif @@ -163,7 +163,7 @@ PropertyMap::Parser::~Parser() { status_t PropertyMap::Parser::parse() { while (!mTokenizer->isEof()) { #if DEBUG_PARSER - LOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(), + ALOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(), mTokenizer->peekRemainderOfLine().string()); #endif diff --git a/libs/utils/RefBase.cpp b/libs/utils/RefBase.cpp index 959b38235..0b7dd92e3 100644 --- a/libs/utils/RefBase.cpp +++ b/libs/utils/RefBase.cpp @@ -103,7 +103,7 @@ public: ref_entry* refs = mStrongRefs; while (refs) { char inc = refs->ref >= 0 ? '+' : '-'; - LOGD("\t%c ID %p (ref %d):", inc, refs->id, refs->ref); + ALOGD("\t%c ID %p (ref %d):", inc, refs->id, refs->ref); #if DEBUG_REFS_CALLSTACK_ENABLED refs->stack.dump(); #endif @@ -121,7 +121,7 @@ public: ref_entry* refs = mWeakRefs; while (refs) { char inc = refs->ref >= 0 ? '+' : '-'; - LOGD("\t%c ID %p (ref %d):", inc, refs->id, refs->ref); + ALOGD("\t%c ID %p (ref %d):", inc, refs->id, refs->ref); #if DEBUG_REFS_CALLSTACK_ENABLED refs->stack.dump(); #endif @@ -137,13 +137,13 @@ public: } void addStrongRef(const void* id) { - //LOGD_IF(mTrackEnabled, + //ALOGD_IF(mTrackEnabled, // "addStrongRef: RefBase=%p, id=%p", mBase, id); addRef(&mStrongRefs, id, mStrong); } void removeStrongRef(const void* id) { - //LOGD_IF(mTrackEnabled, + //ALOGD_IF(mTrackEnabled, // "removeStrongRef: RefBase=%p, id=%p", mBase, id); if (!mRetain) { removeRef(&mStrongRefs, id); @@ -153,7 +153,7 @@ public: } void renameStrongRefId(const void* old_id, const void* new_id) { - //LOGD_IF(mTrackEnabled, + //ALOGD_IF(mTrackEnabled, // "renameStrongRefId: RefBase=%p, oid=%p, nid=%p", // mBase, old_id, new_id); renameRefsId(mStrongRefs, old_id, new_id); @@ -203,7 +203,7 @@ public: if (rc >= 0) { write(rc, text.string(), text.length()); close(rc); - LOGD("STACK TRACE for %p saved in %s", this, name); + ALOGD("STACK TRACE for %p saved in %s", this, name); } else LOGE("FAILED TO PRINT STACK TRACE for %p in %s: %s", this, name, strerror(errno)); @@ -270,7 +270,7 @@ private: ref = head; while (ref) { char inc = ref->ref >= 0 ? '+' : '-'; - LOGD("\t%c ID %p (ref %d):", inc, ref->id, ref->ref); + ALOGD("\t%c ID %p (ref %d):", inc, ref->id, ref->ref); ref = ref->next; } @@ -334,7 +334,7 @@ void RefBase::incStrong(const void* id) const const int32_t c = android_atomic_inc(&refs->mStrong); LOG_ASSERT(c > 0, "incStrong() called on %p after last strong ref", refs); #if PRINT_REFS - LOGD("incStrong of %p from %p: cnt=%d\n", this, id, c); + ALOGD("incStrong of %p from %p: cnt=%d\n", this, id, c); #endif if (c != INITIAL_STRONG_VALUE) { return; @@ -350,7 +350,7 @@ void RefBase::decStrong(const void* id) const refs->removeStrongRef(id); const int32_t c = android_atomic_dec(&refs->mStrong); #if PRINT_REFS - LOGD("decStrong of %p from %p: cnt=%d\n", this, id, c); + ALOGD("decStrong of %p from %p: cnt=%d\n", this, id, c); #endif LOG_ASSERT(c >= 1, "decStrong() called on %p too many times", refs); if (c == 1) { @@ -372,7 +372,7 @@ void RefBase::forceIncStrong(const void* id) const LOG_ASSERT(c >= 0, "forceIncStrong called on %p after ref count underflow", refs); #if PRINT_REFS - LOGD("forceIncStrong of %p from %p: cnt=%d\n", this, id, c); + ALOGD("forceIncStrong of %p from %p: cnt=%d\n", this, id, c); #endif switch (c) { @@ -487,7 +487,7 @@ bool RefBase::weakref_type::attemptIncStrong(const void* id) impl->addStrongRef(id); #if PRINT_REFS - LOGD("attemptIncStrong of %p from %p: cnt=%d\n", this, id, curCount); + ALOGD("attemptIncStrong of %p from %p: cnt=%d\n", this, id, curCount); #endif if (curCount == INITIAL_STRONG_VALUE) { diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp index 6a9e91d2b..559afd8b1 100644 --- a/libs/utils/ResourceTypes.cpp +++ b/libs/utils/ResourceTypes.cpp @@ -4368,7 +4368,7 @@ status_t ResTable::createIdmap(const ResTable& overlay, uint32_t originalCrc, ui } #if 0 if (overlayResID != 0) { - LOGD("%s/%s 0x%08x -> 0x%08x\n", + ALOGD("%s/%s 0x%08x -> 0x%08x\n", String8(String16(resName.type)).string(), String8(String16(resName.name)).string(), resID, overlayResID); diff --git a/libs/utils/StopWatch.cpp b/libs/utils/StopWatch.cpp index b5dda2fac..595aec359 100644 --- a/libs/utils/StopWatch.cpp +++ b/libs/utils/StopWatch.cpp @@ -39,11 +39,11 @@ StopWatch::~StopWatch() { nsecs_t elapsed = elapsedTime(); const int n = mNumLaps; - LOGD("StopWatch %s (us): %lld ", mName, ns2us(elapsed)); + ALOGD("StopWatch %s (us): %lld ", mName, ns2us(elapsed)); for (int i=0 ; i void getKeyValue(const ComplexEntry& entry, int* key, int* value) { template static void dump(BasicHashtable >& h) { - LOGD("hashtable %p, size=%u, capacity=%u, bucketCount=%u", + ALOGD("hashtable %p, size=%u, capacity=%u, bucketCount=%u", &h, h.size(), h.capacity(), h.bucketCount()); for (size_t i = 0; i < h.bucketCount(); i++) { bool collision, present; @@ -165,11 +165,11 @@ static void dump(BasicHashtable >& h) { if (present) { int key, value; getKeyValue(h.entryAt(i), &key, &value); - LOGD(" [%3u] = collision=%d, present=%d, hash=0x%08x, key=%3d, value=%3d, " + ALOGD(" [%3u] = collision=%d, present=%d, hash=0x%08x, key=%3d, value=%3d, " "hash_type(key)=0x%08x", i, collision, present, hash, key, value, hash_type(key)); } else { - LOGD(" [%3u] = collision=%d, present=%d", + ALOGD(" [%3u] = collision=%d, present=%d", i, collision, present); } } From a1d3391b1ec04dece202baf288f1741006afa552 Mon Sep 17 00:00:00 2001 From: Steve Block Date: Wed, 4 Jan 2012 20:05:49 +0000 Subject: [PATCH 382/541] Rename (IF_)LOGI(_IF) to (IF_)ALOGI(_IF) DO NOT MERGE See https://android-git.corp.google.com/g/156801 Bug: 5449033 Change-Id: Ib08fe86d23db91ee153e9f91a99a35c42b9208ea --- include/utils/ResourceTypes.h | 10 +++++----- libs/utils/Asset.cpp | 4 ++-- libs/utils/AssetManager.cpp | 8 ++++---- libs/utils/BackupData.cpp | 6 +++--- libs/utils/BackupHelpers.cpp | 2 +- libs/utils/ObbFile.cpp | 6 +++--- libs/utils/ResourceTypes.cpp | 32 ++++++++++++++++---------------- libs/utils/Static.cpp | 4 ++-- libs/utils/ZipFileRO.cpp | 6 +++--- 9 files changed, 39 insertions(+), 39 deletions(-) diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h index e045b2c78..46420c131 100644 --- a/include/utils/ResourceTypes.h +++ b/include/utils/ResourceTypes.h @@ -1278,7 +1278,7 @@ struct ResTable_config myDelta += requested->screenHeightDp - screenHeightDp; otherDelta += requested->screenHeightDp - o.screenHeightDp; } - //LOGI("Comparing this %dx%d to other %dx%d in %dx%d: myDelta=%d otherDelta=%d", + //ALOGI("Comparing this %dx%d to other %dx%d in %dx%d: myDelta=%d otherDelta=%d", // screenWidthDp, screenHeightDp, o.screenWidthDp, o.screenHeightDp, // requested->screenWidthDp, requested->screenHeightDp, myDelta, otherDelta); return (myDelta <= otherDelta); @@ -1507,11 +1507,11 @@ struct ResTable_config } if (screenSizeDp != 0) { if (screenWidthDp != 0 && screenWidthDp > settings.screenWidthDp) { - //LOGI("Filtering out width %d in requested %d", screenWidthDp, settings.screenWidthDp); + //ALOGI("Filtering out width %d in requested %d", screenWidthDp, settings.screenWidthDp); return false; } if (screenHeightDp != 0 && screenHeightDp > settings.screenHeightDp) { - //LOGI("Filtering out height %d in requested %d", screenHeightDp, settings.screenHeightDp); + //ALOGI("Filtering out height %d in requested %d", screenHeightDp, settings.screenHeightDp); return false; } } @@ -1531,9 +1531,9 @@ struct ResTable_config // For compatibility, we count a request for KEYSHIDDEN_NO as also // matching the more recent KEYSHIDDEN_SOFT. Basically // KEYSHIDDEN_NO means there is some kind of keyboard available. - //LOGI("Matching keysHidden: have=%d, config=%d\n", keysHidden, setKeysHidden); + //ALOGI("Matching keysHidden: have=%d, config=%d\n", keysHidden, setKeysHidden); if (keysHidden != KEYSHIDDEN_NO || setKeysHidden != KEYSHIDDEN_SOFT) { - //LOGI("No match!"); + //ALOGI("No match!"); return false; } } diff --git a/libs/utils/Asset.cpp b/libs/utils/Asset.cpp index 7c34c6a35..07693bbd5 100644 --- a/libs/utils/Asset.cpp +++ b/libs/utils/Asset.cpp @@ -89,7 +89,7 @@ Asset::Asset(void) gTail->mNext = this; gTail = this; } - //LOGI("Creating Asset %p #%d\n", this, gCount); + //ALOGI("Creating Asset %p #%d\n", this, gCount); } Asset::~Asset(void) @@ -109,7 +109,7 @@ Asset::~Asset(void) mPrev->mNext = mNext; } mNext = mPrev = NULL; - //LOGI("Destroying Asset in %p #%d\n", this, gCount); + //ALOGI("Destroying Asset in %p #%d\n", this, gCount); } /* diff --git a/libs/utils/AssetManager.cpp b/libs/utils/AssetManager.cpp index e7c4d4757..77db3d488 100644 --- a/libs/utils/AssetManager.cpp +++ b/libs/utils/AssetManager.cpp @@ -117,14 +117,14 @@ AssetManager::AssetManager(CacheMode cacheMode) mCacheMode(cacheMode), mCacheValid(false) { int count = android_atomic_inc(&gCount)+1; - //LOGI("Creating AssetManager %p #%d\n", this, count); + //ALOGI("Creating AssetManager %p #%d\n", this, count); memset(mConfig, 0, sizeof(ResTable_config)); } AssetManager::~AssetManager(void) { int count = android_atomic_dec(&gCount); - //LOGI("Destroying AssetManager in %p #%d\n", this, count); + //ALOGI("Destroying AssetManager in %p #%d\n", this, count); delete mConfig; delete mResources; @@ -1780,7 +1780,7 @@ AssetManager::SharedZip::SharedZip(const String8& path, time_t modWhen) : mPath(path), mZipFile(NULL), mModWhen(modWhen), mResourceTableAsset(NULL), mResourceTable(NULL) { - //LOGI("Creating SharedZip %p %s\n", this, (const char*)mPath); + //ALOGI("Creating SharedZip %p %s\n", this, (const char*)mPath); mZipFile = new ZipFileRO; ALOGV("+++ opening zip '%s'\n", mPath.string()); if (mZipFile->open(mPath.string()) != NO_ERROR) { @@ -1858,7 +1858,7 @@ bool AssetManager::SharedZip::isUpToDate() AssetManager::SharedZip::~SharedZip() { - //LOGI("Destroying SharedZip %p %s\n", this, (const char*)mPath); + //ALOGI("Destroying SharedZip %p %s\n", this, (const char*)mPath); if (mResourceTable != NULL) { delete mResourceTable; } diff --git a/libs/utils/BackupData.cpp b/libs/utils/BackupData.cpp index 5afe2dc05..f95630613 100644 --- a/libs/utils/BackupData.cpp +++ b/libs/utils/BackupData.cpp @@ -78,7 +78,7 @@ BackupDataWriter::write_padding_for(int n) paddingSize = padding_extra(n); if (paddingSize > 0) { uint32_t padding = 0xbcbcbcbc; - if (DEBUG) LOGI("writing %d padding bytes for %d", paddingSize, n); + if (DEBUG) ALOGI("writing %d padding bytes for %d", paddingSize, n); amt = write(m_fd, &padding, paddingSize); if (amt != paddingSize) { m_status = errno; @@ -125,7 +125,7 @@ BackupDataWriter::WriteEntityHeader(const String8& key, size_t dataSize) header.keyLen = tolel(keyLen); header.dataSize = tolel(dataSize); - if (DEBUG) LOGI("writing entity header, %d bytes", sizeof(entity_header_v1)); + if (DEBUG) ALOGI("writing entity header, %d bytes", sizeof(entity_header_v1)); amt = write(m_fd, &header, sizeof(entity_header_v1)); if (amt != sizeof(entity_header_v1)) { m_status = errno; @@ -133,7 +133,7 @@ BackupDataWriter::WriteEntityHeader(const String8& key, size_t dataSize) } m_pos += amt; - if (DEBUG) LOGI("writing entity header key, %d bytes", keyLen+1); + if (DEBUG) ALOGI("writing entity header key, %d bytes", keyLen+1); amt = write(m_fd, k.string(), keyLen+1); if (amt != keyLen+1) { m_status = errno; diff --git a/libs/utils/BackupHelpers.cpp b/libs/utils/BackupHelpers.cpp index aee7ff089..882bf71b5 100644 --- a/libs/utils/BackupHelpers.cpp +++ b/libs/utils/BackupHelpers.cpp @@ -628,7 +628,7 @@ int write_tarfile(const String8& packageName, const String8& domain, // [ 329 : 8 ] and [ 337 : 8 ] devmajor/devminor, not used - LOGI(" Name: %s", fullname.string()); + ALOGI(" Name: %s", fullname.string()); // If we're using a pax extended header, build & write that here; lengths are // already preflighted diff --git a/libs/utils/ObbFile.cpp b/libs/utils/ObbFile.cpp index 2907b5666..11fe1e973 100644 --- a/libs/utils/ObbFile.cpp +++ b/libs/utils/ObbFile.cpp @@ -179,14 +179,14 @@ bool ObbFile::parseObbFile(int fd) actual = TEMP_FAILURE_RETRY(read(fd, scanBuf, footerSize)); // readAmount is guaranteed to be less than kMaxBufSize if (actual != (ssize_t)footerSize) { - LOGI("couldn't read ObbFile footer: %s\n", strerror(errno)); + ALOGI("couldn't read ObbFile footer: %s\n", strerror(errno)); free(scanBuf); return false; } #ifdef DEBUG for (int i = 0; i < footerSize; ++i) { - LOGI("char: 0x%02x\n", scanBuf[i]); + ALOGI("char: 0x%02x\n", scanBuf[i]); } #endif @@ -217,7 +217,7 @@ bool ObbFile::parseObbFile(int fd) free(scanBuf); #ifdef DEBUG - LOGI("Obb scan succeeded: packageName=%s, version=%d\n", mPackageName.string(), mVersion); + ALOGI("Obb scan succeeded: packageName=%s, version=%d\n", mPackageName.string(), mVersion); #endif return true; diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp index 559afd8b1..3569e32d7 100644 --- a/libs/utils/ResourceTypes.cpp +++ b/libs/utils/ResourceTypes.cpp @@ -1164,7 +1164,7 @@ ResXMLTree::ResXMLTree() : ResXMLParser(*this) , mError(NO_INIT), mOwnedData(NULL) { - //LOGI("Creating ResXMLTree %p #%d\n", this, android_atomic_inc(&gCount)+1); + //ALOGI("Creating ResXMLTree %p #%d\n", this, android_atomic_inc(&gCount)+1); restart(); } @@ -1172,13 +1172,13 @@ ResXMLTree::ResXMLTree(const void* data, size_t size, bool copyData) : ResXMLParser(*this) , mError(NO_INIT), mOwnedData(NULL) { - //LOGI("Creating ResXMLTree %p #%d\n", this, android_atomic_inc(&gCount)+1); + //ALOGI("Creating ResXMLTree %p #%d\n", this, android_atomic_inc(&gCount)+1); setTo(data, size, copyData); } ResXMLTree::~ResXMLTree() { - //LOGI("Destroying ResXMLTree in %p #%d\n", this, android_atomic_dec(&gCount)-1); + //ALOGI("Destroying ResXMLTree in %p #%d\n", this, android_atomic_dec(&gCount)-1); uninit(); } @@ -1631,7 +1631,7 @@ status_t ResTable::Theme::applyStyle(uint32_t resID, bool force) mTable.unlock(); - //LOGI("Applying style 0x%08x (force=%d) theme %p...\n", resID, force, this); + //ALOGI("Applying style 0x%08x (force=%d) theme %p...\n", resID, force, this); //dumpToLog(); return NO_ERROR; @@ -1639,7 +1639,7 @@ status_t ResTable::Theme::applyStyle(uint32_t resID, bool force) status_t ResTable::Theme::setTo(const Theme& other) { - //LOGI("Setting theme %p from theme %p...\n", this, &other); + //ALOGI("Setting theme %p from theme %p...\n", this, &other); //dumpToLog(); //other.dumpToLog(); @@ -1670,7 +1670,7 @@ status_t ResTable::Theme::setTo(const Theme& other) } } - //LOGI("Final theme:"); + //ALOGI("Final theme:"); //dumpToLog(); return NO_ERROR; @@ -1752,21 +1752,21 @@ ssize_t ResTable::Theme::resolveAttributeReference(Res_value* inOutValue, void ResTable::Theme::dumpToLog() const { - LOGI("Theme %p:\n", this); + ALOGI("Theme %p:\n", this); for (size_t i=0; inumTypes; j++) { type_info& ti = pi->types[j]; if (ti.numEntries == 0) continue; - LOGI(" Type #0x%02x:\n", (int)(j+1)); + ALOGI(" Type #0x%02x:\n", (int)(j+1)); for (size_t k=0; kheader = (const ResTable_header*)data; header->size = dtohl(header->header->header.size); - //LOGI("Got size 0x%x, again size 0x%x, raw size 0x%x\n", header->size, + //ALOGI("Got size 0x%x, again size 0x%x, raw size 0x%x\n", header->size, // dtohl(header->header->header.size), header->header->header.size); LOAD_TABLE_NOISY(LOGV("Loading ResTable @%p:\n", header->header)); LOAD_TABLE_NOISY(printHexData(2, header->header, header->size < 256 ? header->size : 256, @@ -2350,7 +2350,7 @@ ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag, *outTypeSpecFlags = set->typeSpecFlags; } *outBag = (bag_entry*)(set+1); - //LOGI("Found existing bag for: %p\n", (void*)resID); + //ALOGI("Found existing bag for: %p\n", (void*)resID); return set->numAttrs; } LOGW("Attempt to retrieve bag 0x%08x which is invalid or in a cycle.", @@ -4273,7 +4273,7 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg, TABLE_GETENTRY( ResTable_config thisConfig; thisConfig.copyFromDtoH(type->config); - LOGI("Adding config to type %d: imsi:%d/%d lang:%c%c cnt:%c%c " + ALOGI("Adding config to type %d: imsi:%d/%d lang:%c%c cnt:%c%c " "orien:%d touch:%d density:%d key:%d inp:%d nav:%d w:%d h:%d " "swdp:%d wdp:%d hdp:%d\n", type->id, diff --git a/libs/utils/Static.cpp b/libs/utils/Static.cpp index ceca43552..bfcb2da45 100644 --- a/libs/utils/Static.cpp +++ b/libs/utils/Static.cpp @@ -57,8 +57,8 @@ protected: virtual status_t writeLines(const struct iovec& vec, size_t N) { //android_writevLog(&vec, N); <-- this is now a no-op - if (N != 1) LOGI("WARNING: writeLines N=%d\n", N); - LOGI("%.*s", vec.iov_len, (const char*) vec.iov_base); + if (N != 1) ALOGI("WARNING: writeLines N=%d\n", N); + ALOGI("%.*s", vec.iov_len, (const char*) vec.iov_base); return NO_ERROR; } }; diff --git a/libs/utils/ZipFileRO.cpp b/libs/utils/ZipFileRO.cpp index 306935271..6ca9a28ba 100644 --- a/libs/utils/ZipFileRO.cpp +++ b/libs/utils/ZipFileRO.cpp @@ -210,7 +210,7 @@ bool ZipFileRO::mapCentralDirectory(void) ssize_t actual = TEMP_FAILURE_RETRY(read(mFd, scanBuf, sizeof(int32_t))); if (actual != (ssize_t) sizeof(int32_t)) { - LOGI("couldn't read first signature from zip archive: %s", strerror(errno)); + ALOGI("couldn't read first signature from zip archive: %s", strerror(errno)); free(scanBuf); return false; } @@ -218,7 +218,7 @@ bool ZipFileRO::mapCentralDirectory(void) { unsigned int header = get4LE(scanBuf); if (header == kEOCDSignature) { - LOGI("Found Zip archive, but it looks empty\n"); + ALOGI("Found Zip archive, but it looks empty\n"); free(scanBuf); return false; } else if (header != kLFHSignature) { @@ -761,7 +761,7 @@ bool ZipFileRO::uncompressEntry(ZipEntryRO entry, int fd) const (ZD_TYPE) actual, (ZD_TYPE) uncompLen); goto unmap; } else { - LOGI("+++ successful write\n"); + ALOGI("+++ successful write\n"); } } else { if (!inflateBuffer(fd, ptr, uncompLen, compLen)) From 61d341b8d3d771f4ef3dd54df0502b19b7a2ab4d Mon Sep 17 00:00:00 2001 From: Steve Block Date: Thu, 5 Jan 2012 23:22:43 +0000 Subject: [PATCH 383/541] Rename (IF_)LOGW(_IF) to (IF_)ALOGW(_IF) DO NOT MERGE See https://android-git.corp.google.com/g/157065 Bug: 5449033 Change-Id: I00a4b904f9449e6f93b7fd35eac28640d7929e69 --- libs/utils/Asset.cpp | 6 +- libs/utils/AssetManager.cpp | 34 +++---- libs/utils/BackupHelpers.cpp | 28 +++--- libs/utils/BlobCache.cpp | 4 +- libs/utils/FileMap.cpp | 2 +- libs/utils/Looper.cpp | 12 +-- libs/utils/ObbFile.cpp | 48 +++++----- libs/utils/PropertyMap.cpp | 4 +- libs/utils/ResourceTypes.cpp | 176 +++++++++++++++++------------------ libs/utils/SystemClock.cpp | 6 +- libs/utils/Threads.cpp | 4 +- libs/utils/ZipFileRO.cpp | 54 +++++------ libs/utils/ZipUtils.cpp | 4 +- 13 files changed, 191 insertions(+), 191 deletions(-) diff --git a/libs/utils/Asset.cpp b/libs/utils/Asset.cpp index 07693bbd5..22af816fe 100644 --- a/libs/utils/Asset.cpp +++ b/libs/utils/Asset.cpp @@ -327,14 +327,14 @@ off64_t Asset::handleSeek(off64_t offset, int whence, off64_t curPosn, off64_t m newOffset = maxPosn + offset; break; default: - LOGW("unexpected whence %d\n", whence); + ALOGW("unexpected whence %d\n", whence); // this was happening due to an off64_t size mismatch assert(false); return (off64_t) -1; } if (newOffset < 0 || newOffset > maxPosn) { - LOGW("seek out of range: want %ld, end=%ld\n", + ALOGW("seek out of range: want %ld, end=%ld\n", (long) newOffset, (long) maxPosn); return (off64_t) -1; } @@ -855,7 +855,7 @@ const void* _CompressedAsset::getBuffer(bool wordAligned) */ buf = new unsigned char[mUncompressedLen]; if (buf == NULL) { - LOGW("alloc %ld bytes failed\n", (long) mUncompressedLen); + ALOGW("alloc %ld bytes failed\n", (long) mUncompressedLen); goto bail; } diff --git a/libs/utils/AssetManager.cpp b/libs/utils/AssetManager.cpp index 77db3d488..8a8551f78 100644 --- a/libs/utils/AssetManager.cpp +++ b/libs/utils/AssetManager.cpp @@ -151,7 +151,7 @@ bool AssetManager::addAssetPath(const String8& path, void** cookie) ap.path = path; ap.type = ::getFileType(path.string()); if (ap.type != kFileTypeDirectory && ap.type != kFileTypeRegular) { - LOGW("Asset path %s is neither a directory nor file (type=%d).", + ALOGW("Asset path %s is neither a directory nor file (type=%d).", path.string(), (int)ap.type); return false; } @@ -200,7 +200,7 @@ bool AssetManager::addAssetPath(const String8& path, void** cookie) if (addOverlay) { mAssetPaths.add(oap); } else { - LOGW("failed to add overlay package %s\n", overlayPath.string()); + ALOGW("failed to add overlay package %s\n", overlayPath.string()); } } } @@ -216,17 +216,17 @@ bool AssetManager::isIdmapStaleLocked(const String8& originalPath, const String8 if (errno == ENOENT) { return true; // non-existing idmap is always stale } else { - LOGW("failed to stat file %s: %s\n", idmapPath.string(), strerror(errno)); + ALOGW("failed to stat file %s: %s\n", idmapPath.string(), strerror(errno)); return false; } } if (st.st_size < ResTable::IDMAP_HEADER_SIZE_BYTES) { - LOGW("file %s has unexpectedly small size=%zd\n", idmapPath.string(), (size_t)st.st_size); + ALOGW("file %s has unexpectedly small size=%zd\n", idmapPath.string(), (size_t)st.st_size); return false; } int fd = TEMP_FAILURE_RETRY(::open(idmapPath.string(), O_RDONLY)); if (fd == -1) { - LOGW("failed to open file %s: %s\n", idmapPath.string(), strerror(errno)); + ALOGW("failed to open file %s: %s\n", idmapPath.string(), strerror(errno)); return false; } char buf[ResTable::IDMAP_HEADER_SIZE_BYTES]; @@ -300,24 +300,24 @@ bool AssetManager::createIdmapFileLocked(const String8& originalPath, const Stri ap.path = *paths[i]; Asset* ass = openNonAssetInPathLocked("resources.arsc", Asset::ACCESS_BUFFER, ap); if (ass == NULL) { - LOGW("failed to find resources.arsc in %s\n", ap.path.string()); + ALOGW("failed to find resources.arsc in %s\n", ap.path.string()); goto error; } tables[i].add(ass, (void*)1, false); } if (!getZipEntryCrcLocked(originalPath, "resources.arsc", &originalCrc)) { - LOGW("failed to retrieve crc for resources.arsc in %s\n", originalPath.string()); + ALOGW("failed to retrieve crc for resources.arsc in %s\n", originalPath.string()); goto error; } if (!getZipEntryCrcLocked(overlayPath, "resources.arsc", &overlayCrc)) { - LOGW("failed to retrieve crc for resources.arsc in %s\n", overlayPath.string()); + ALOGW("failed to retrieve crc for resources.arsc in %s\n", overlayPath.string()); goto error; } if (tables[0].createIdmap(tables[1], originalCrc, overlayCrc, (void**)&data, &size) != NO_ERROR) { - LOGW("failed to generate idmap data for file %s\n", idmapPath.string()); + ALOGW("failed to generate idmap data for file %s\n", idmapPath.string()); goto error; } @@ -326,13 +326,13 @@ bool AssetManager::createIdmapFileLocked(const String8& originalPath, const Stri // installd). fd = TEMP_FAILURE_RETRY(::open(idmapPath.string(), O_WRONLY | O_CREAT | O_TRUNC, 0644)); if (fd == -1) { - LOGW("failed to write idmap file %s (open: %s)\n", idmapPath.string(), strerror(errno)); + ALOGW("failed to write idmap file %s (open: %s)\n", idmapPath.string(), strerror(errno)); goto error_free; } for (;;) { ssize_t written = TEMP_FAILURE_RETRY(write(fd, data + offset, size)); if (written < 0) { - LOGW("failed to write idmap file %s (write: %s)\n", idmapPath.string(), + ALOGW("failed to write idmap file %s (write: %s)\n", idmapPath.string(), strerror(errno)); goto error_close; } @@ -686,7 +686,7 @@ const ResTable* AssetManager::getResTable(bool required) const } } - if (required && !rt) LOGW("Unable to find resources file resources.arsc"); + if (required && !rt) ALOGW("Unable to find resources file resources.arsc"); if (!rt) { mResources = rt = new ResTable(); } @@ -727,7 +727,7 @@ Asset* AssetManager::openIdmapLocked(const struct asset_path& ap) const if (ass) { ALOGV("loading idmap %s\n", ap.idmap.string()); } else { - LOGW("failed to load idmap %s\n", ap.idmap.string()); + ALOGW("failed to load idmap %s\n", ap.idmap.string()); } } return ass; @@ -1074,13 +1074,13 @@ Asset* AssetManager::openAssetFromZipLocked(const ZipFileRO* pZipFile, if (!pZipFile->getEntryInfo(entry, &method, &uncompressedLen, NULL, NULL, NULL, NULL)) { - LOGW("getEntryInfo failed\n"); + ALOGW("getEntryInfo failed\n"); return NULL; } FileMap* dataMap = pZipFile->createEntryFileMap(entry); if (dataMap == NULL) { - LOGW("create map from entry failed\n"); + ALOGW("create map from entry failed\n"); return NULL; } @@ -1096,7 +1096,7 @@ Asset* AssetManager::openAssetFromZipLocked(const ZipFileRO* pZipFile, } if (pAsset == NULL) { /* unexpected */ - LOGW("create from segment failed\n"); + ALOGW("create from segment failed\n"); } return pAsset; @@ -1427,7 +1427,7 @@ bool AssetManager::scanAndMergeZipLocked(SortedVector* pMerg pZip = mZipSet.getZip(ap.path); if (pZip == NULL) { - LOGW("Failure opening zip %s\n", ap.path.string()); + ALOGW("Failure opening zip %s\n", ap.path.string()); return false; } diff --git a/libs/utils/BackupHelpers.cpp b/libs/utils/BackupHelpers.cpp index 882bf71b5..04b2e7132 100644 --- a/libs/utils/BackupHelpers.cpp +++ b/libs/utils/BackupHelpers.cpp @@ -100,7 +100,7 @@ read_snapshot_file(int fd, KeyedVector* snapshot) bytesRead += amt; if (header.magic0 != MAGIC0 || header.magic1 != MAGIC1) { - LOGW("read_snapshot_file header.magic0=0x%08x magic1=0x%08x", header.magic0, header.magic1); + ALOGW("read_snapshot_file header.magic0=0x%08x magic1=0x%08x", header.magic0, header.magic1); return 1; } @@ -110,7 +110,7 @@ read_snapshot_file(int fd, KeyedVector* snapshot) amt = read(fd, &file, sizeof(FileState)); if (amt != sizeof(FileState)) { - LOGW("read_snapshot_file FileState truncated/error with read at %d bytes\n", bytesRead); + ALOGW("read_snapshot_file FileState truncated/error with read at %d bytes\n", bytesRead); return 1; } bytesRead += amt; @@ -129,13 +129,13 @@ read_snapshot_file(int fd, KeyedVector* snapshot) free(filename); } if (amt != nameBufSize) { - LOGW("read_snapshot_file filename truncated/error with read at %d bytes\n", bytesRead); + ALOGW("read_snapshot_file filename truncated/error with read at %d bytes\n", bytesRead); return 1; } } if (header.totalSize != bytesRead) { - LOGW("read_snapshot_file length mismatch: header.totalSize=%d bytesRead=%d\n", + ALOGW("read_snapshot_file length mismatch: header.totalSize=%d bytesRead=%d\n", header.totalSize, bytesRead); return 1; } @@ -166,7 +166,7 @@ write_snapshot_file(int fd, const KeyedVector& snapshot) amt = write(fd, &header, sizeof(header)); if (amt != sizeof(header)) { - LOGW("write_snapshot_file error writing header %s", strerror(errno)); + ALOGW("write_snapshot_file error writing header %s", strerror(errno)); return errno; } @@ -178,14 +178,14 @@ write_snapshot_file(int fd, const KeyedVector& snapshot) amt = write(fd, &r.s, sizeof(FileState)); if (amt != sizeof(FileState)) { - LOGW("write_snapshot_file error writing header %s", strerror(errno)); + ALOGW("write_snapshot_file error writing header %s", strerror(errno)); return 1; } // filename is not NULL terminated, but it is padded amt = write(fd, name.string(), nameLen); if (amt != nameLen) { - LOGW("write_snapshot_file error writing filename %s", strerror(errno)); + ALOGW("write_snapshot_file error writing filename %s", strerror(errno)); return 1; } int paddingLen = ROUND_UP[nameLen % 4]; @@ -193,7 +193,7 @@ write_snapshot_file(int fd, const KeyedVector& snapshot) int padding = 0xabababab; amt = write(fd, &padding, paddingLen); if (amt != paddingLen) { - LOGW("write_snapshot_file error writing %d bytes of filename padding %s", + ALOGW("write_snapshot_file error writing %d bytes of filename padding %s", paddingLen, strerror(errno)); return 1; } @@ -591,7 +591,7 @@ int write_tarfile(const String8& packageName, const String8& domain, } else if (S_ISREG(s.st_mode)) { type = '0'; // tar magic: '0' == normal file } else { - LOGW("Error: unknown file mode 0%o [%s]", s.st_mode, filepath.string()); + ALOGW("Error: unknown file mode 0%o [%s]", s.st_mode, filepath.string()); goto cleanup; } buf[156] = type; @@ -759,7 +759,7 @@ RestoreHelperBase::WriteFile(const String8& filename, BackupDataReader* in) file_metadata_v1 metadata; amt = in->ReadEntityData(&metadata, sizeof(metadata)); if (amt != sizeof(metadata)) { - LOGW("Could not read metadata for %s -- %ld / %s", filename.string(), + ALOGW("Could not read metadata for %s -- %ld / %s", filename.string(), (long)amt, strerror(errno)); return EIO; } @@ -768,7 +768,7 @@ RestoreHelperBase::WriteFile(const String8& filename, BackupDataReader* in) if (metadata.version > CURRENT_METADATA_VERSION) { if (!m_loggedUnknownMetadata) { m_loggedUnknownMetadata = true; - LOGW("Restoring file with unsupported metadata version %d (currently %d)", + ALOGW("Restoring file with unsupported metadata version %d (currently %d)", metadata.version, CURRENT_METADATA_VERSION); } } @@ -778,7 +778,7 @@ RestoreHelperBase::WriteFile(const String8& filename, BackupDataReader* in) crc = crc32(0L, Z_NULL, 0); fd = open(filename.string(), O_CREAT|O_RDWR|O_TRUNC, mode); if (fd == -1) { - LOGW("Could not open file %s -- %s", filename.string(), strerror(errno)); + ALOGW("Could not open file %s -- %s", filename.string(), strerror(errno)); return errno; } @@ -786,7 +786,7 @@ RestoreHelperBase::WriteFile(const String8& filename, BackupDataReader* in) err = write(fd, buf, amt); if (err != amt) { close(fd); - LOGW("Error '%s' writing '%s'", strerror(errno), filename.string()); + ALOGW("Error '%s' writing '%s'", strerror(errno), filename.string()); return errno; } crc = crc32(crc, (Bytef*)buf, amt); @@ -797,7 +797,7 @@ RestoreHelperBase::WriteFile(const String8& filename, BackupDataReader* in) // Record for the snapshot err = stat(filename.string(), &st); if (err != 0) { - LOGW("Error stating file that we just created %s", filename.string()); + ALOGW("Error stating file that we just created %s", filename.string()); return errno; } diff --git a/libs/utils/BlobCache.cpp b/libs/utils/BlobCache.cpp index 497082869..0011d29d2 100644 --- a/libs/utils/BlobCache.cpp +++ b/libs/utils/BlobCache.cpp @@ -69,11 +69,11 @@ void BlobCache::set(const void* key, size_t keySize, const void* value, return; } if (keySize == 0) { - LOGW("set: not caching because keySize is 0"); + ALOGW("set: not caching because keySize is 0"); return; } if (valueSize <= 0) { - LOGW("set: not caching because valueSize is 0"); + ALOGW("set: not caching because valueSize is 0"); return; } diff --git a/libs/utils/FileMap.cpp b/libs/utils/FileMap.cpp index b2a61f122..c9a423e7d 100644 --- a/libs/utils/FileMap.cpp +++ b/libs/utils/FileMap.cpp @@ -217,7 +217,7 @@ int FileMap::advise(MapAdvice advice) cc = madvise(mBasePtr, mBaseLength, sysAdvice); if (cc != 0) - LOGW("madvise(%d) failed: %s\n", sysAdvice, strerror(errno)); + ALOGW("madvise(%d) failed: %s\n", sysAdvice, strerror(errno)); return cc; #else return -1; diff --git a/libs/utils/Looper.cpp b/libs/utils/Looper.cpp index 1bc92cf88..28ed0e850 100644 --- a/libs/utils/Looper.cpp +++ b/libs/utils/Looper.cpp @@ -163,7 +163,7 @@ sp Looper::prepare(int opts) { Looper::setForThread(looper); } if (looper->getAllowNonCallbacks() != allowNonCallbacks) { - LOGW("Looper already prepared for this thread with a different value for the " + ALOGW("Looper already prepared for this thread with a different value for the " "ALOOPER_PREPARE_ALLOW_NON_CALLBACKS option."); } return looper; @@ -262,7 +262,7 @@ int Looper::pollInner(int timeoutMillis) { if (errno == EINTR) { goto Done; } - LOGW("Poll failed with an unexpected error, errno=%d", errno); + ALOGW("Poll failed with an unexpected error, errno=%d", errno); result = ALOOPER_POLL_ERROR; goto Done; } @@ -289,7 +289,7 @@ int Looper::pollInner(int timeoutMillis) { if (epollEvents & EPOLLIN) { awoken(); } else { - LOGW("Ignoring unexpected epoll events 0x%x on wake read pipe.", epollEvents); + ALOGW("Ignoring unexpected epoll events 0x%x on wake read pipe.", epollEvents); } } else { ssize_t requestIndex = mRequests.indexOfKey(fd); @@ -301,7 +301,7 @@ int Looper::pollInner(int timeoutMillis) { if (epollEvents & EPOLLHUP) events |= ALOOPER_EVENT_HANGUP; pushResponse(events, mRequests.valueAt(requestIndex)); } else { - LOGW("Ignoring unexpected epoll events 0x%x on fd %d that is " + ALOGW("Ignoring unexpected epoll events 0x%x on fd %d that is " "no longer registered.", epollEvents, fd); } } @@ -317,7 +317,7 @@ Done: ; if (pollEvents & POLLIN) { awoken(); } else { - LOGW("Ignoring unexpected poll events 0x%x on wake read pipe.", pollEvents); + ALOGW("Ignoring unexpected poll events 0x%x on wake read pipe.", pollEvents); } } else { int events = 0; @@ -468,7 +468,7 @@ void Looper::wake() { if (nWrite != 1) { if (errno != EAGAIN) { - LOGW("Could not write wake signal, errno=%d", errno); + ALOGW("Could not write wake signal, errno=%d", errno); } } } diff --git a/libs/utils/ObbFile.cpp b/libs/utils/ObbFile.cpp index 11fe1e973..ddf59914b 100644 --- a/libs/utils/ObbFile.cpp +++ b/libs/utils/ObbFile.cpp @@ -90,14 +90,14 @@ bool ObbFile::readFrom(const char* filename) fd = ::open(filename, O_RDONLY); if (fd < 0) { - LOGW("couldn't open file %s: %s", filename, strerror(errno)); + ALOGW("couldn't open file %s: %s", filename, strerror(errno)); goto out; } success = readFrom(fd); close(fd); if (!success) { - LOGW("failed to read from %s (fd=%d)\n", filename, fd); + ALOGW("failed to read from %s (fd=%d)\n", filename, fd); } out: @@ -107,7 +107,7 @@ out: bool ObbFile::readFrom(int fd) { if (fd < 0) { - LOGW("attempt to read from invalid fd\n"); + ALOGW("attempt to read from invalid fd\n"); return false; } @@ -120,9 +120,9 @@ bool ObbFile::parseObbFile(int fd) if (fileLength < kFooterMinSize) { if (fileLength < 0) { - LOGW("error seeking in ObbFile: %s\n", strerror(errno)); + ALOGW("error seeking in ObbFile: %s\n", strerror(errno)); } else { - LOGW("file is only %lld (less than %d minimum)\n", fileLength, kFooterMinSize); + ALOGW("file is only %lld (less than %d minimum)\n", fileLength, kFooterMinSize); } return false; } @@ -136,13 +136,13 @@ bool ObbFile::parseObbFile(int fd) char *footer = new char[kFooterTagSize]; actual = TEMP_FAILURE_RETRY(read(fd, footer, kFooterTagSize)); if (actual != kFooterTagSize) { - LOGW("couldn't read footer signature: %s\n", strerror(errno)); + ALOGW("couldn't read footer signature: %s\n", strerror(errno)); return false; } unsigned int fileSig = get4LE((unsigned char*)footer + sizeof(int32_t)); if (fileSig != kSignature) { - LOGW("footer didn't match magic string (expected 0x%08x; got 0x%08x)\n", + ALOGW("footer didn't match magic string (expected 0x%08x; got 0x%08x)\n", kSignature, fileSig); return false; } @@ -150,13 +150,13 @@ bool ObbFile::parseObbFile(int fd) footerSize = get4LE((unsigned char*)footer); if (footerSize > (size_t)fileLength - kFooterTagSize || footerSize > kMaxBufSize) { - LOGW("claimed footer size is too large (0x%08zx; file size is 0x%08llx)\n", + ALOGW("claimed footer size is too large (0x%08zx; file size is 0x%08llx)\n", footerSize, fileLength); return false; } if (footerSize < (kFooterMinSize - kFooterTagSize)) { - LOGW("claimed footer size is too small (0x%zx; minimum size is 0x%x)\n", + ALOGW("claimed footer size is too small (0x%zx; minimum size is 0x%x)\n", footerSize, kFooterMinSize - kFooterTagSize); return false; } @@ -164,7 +164,7 @@ bool ObbFile::parseObbFile(int fd) off64_t fileOffset = fileLength - footerSize - kFooterTagSize; if (lseek64(fd, fileOffset, SEEK_SET) != fileOffset) { - LOGW("seek %lld failed: %s\n", fileOffset, strerror(errno)); + ALOGW("seek %lld failed: %s\n", fileOffset, strerror(errno)); return false; } @@ -172,7 +172,7 @@ bool ObbFile::parseObbFile(int fd) char* scanBuf = (char*)malloc(footerSize); if (scanBuf == NULL) { - LOGW("couldn't allocate scanBuf: %s\n", strerror(errno)); + ALOGW("couldn't allocate scanBuf: %s\n", strerror(errno)); return false; } @@ -192,7 +192,7 @@ bool ObbFile::parseObbFile(int fd) uint32_t sigVersion = get4LE((unsigned char*)scanBuf); if (sigVersion != kSigVersion) { - LOGW("Unsupported ObbFile version %d\n", sigVersion); + ALOGW("Unsupported ObbFile version %d\n", sigVersion); free(scanBuf); return false; } @@ -205,7 +205,7 @@ bool ObbFile::parseObbFile(int fd) size_t packageNameLen = get4LE((unsigned char*)scanBuf + kPackageNameLenOffset); if (packageNameLen == 0 || packageNameLen > (footerSize - kPackageNameOffset)) { - LOGW("bad ObbFile package name length (0x%04zx; 0x%04zx possible)\n", + ALOGW("bad ObbFile package name length (0x%04zx; 0x%04zx possible)\n", packageNameLen, footerSize - kPackageNameOffset); free(scanBuf); return false; @@ -237,7 +237,7 @@ bool ObbFile::writeTo(const char* filename) out: if (!success) { - LOGW("failed to write to %s: %s\n", filename, strerror(errno)); + ALOGW("failed to write to %s: %s\n", filename, strerror(errno)); } return success; } @@ -251,7 +251,7 @@ bool ObbFile::writeTo(int fd) lseek64(fd, 0, SEEK_END); if (mPackageName.size() == 0 || mVersion == -1) { - LOGW("tried to write uninitialized ObbFile data\n"); + ALOGW("tried to write uninitialized ObbFile data\n"); return false; } @@ -260,48 +260,48 @@ bool ObbFile::writeTo(int fd) put4LE(intBuf, kSigVersion); if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) { - LOGW("couldn't write signature version: %s\n", strerror(errno)); + ALOGW("couldn't write signature version: %s\n", strerror(errno)); return false; } put4LE(intBuf, mVersion); if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) { - LOGW("couldn't write package version\n"); + ALOGW("couldn't write package version\n"); return false; } put4LE(intBuf, mFlags); if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) { - LOGW("couldn't write package version\n"); + ALOGW("couldn't write package version\n"); return false; } if (write(fd, mSalt, sizeof(mSalt)) != (ssize_t)sizeof(mSalt)) { - LOGW("couldn't write salt: %s\n", strerror(errno)); + ALOGW("couldn't write salt: %s\n", strerror(errno)); return false; } size_t packageNameLen = mPackageName.size(); put4LE(intBuf, packageNameLen); if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) { - LOGW("couldn't write package name length: %s\n", strerror(errno)); + ALOGW("couldn't write package name length: %s\n", strerror(errno)); return false; } if (write(fd, mPackageName.string(), packageNameLen) != (ssize_t)packageNameLen) { - LOGW("couldn't write package name: %s\n", strerror(errno)); + ALOGW("couldn't write package name: %s\n", strerror(errno)); return false; } put4LE(intBuf, kPackageNameOffset + packageNameLen); if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) { - LOGW("couldn't write footer size: %s\n", strerror(errno)); + ALOGW("couldn't write footer size: %s\n", strerror(errno)); return false; } put4LE(intBuf, kSignature); if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) { - LOGW("couldn't write footer magic signature: %s\n", strerror(errno)); + ALOGW("couldn't write footer magic signature: %s\n", strerror(errno)); return false; } @@ -322,7 +322,7 @@ bool ObbFile::removeFrom(const char* filename) out: if (!success) { - LOGW("failed to remove signature from %s: %s\n", filename, strerror(errno)); + ALOGW("failed to remove signature from %s: %s\n", filename, strerror(errno)); } return success; } diff --git a/libs/utils/PropertyMap.cpp b/libs/utils/PropertyMap.cpp index 99603abca..d801609a9 100644 --- a/libs/utils/PropertyMap.cpp +++ b/libs/utils/PropertyMap.cpp @@ -84,7 +84,7 @@ bool PropertyMap::tryGetProperty(const String8& key, int32_t& outValue) const { char* end; int value = strtol(stringValue.string(), & end, 10); if (*end != '\0') { - LOGW("Property key '%s' has invalid value '%s'. Expected an integer.", + ALOGW("Property key '%s' has invalid value '%s'. Expected an integer.", key.string(), stringValue.string()); return false; } @@ -101,7 +101,7 @@ bool PropertyMap::tryGetProperty(const String8& key, float& outValue) const { char* end; float value = strtof(stringValue.string(), & end); if (*end != '\0') { - LOGW("Property key '%s' has invalid value '%s'. Expected a float.", + ALOGW("Property key '%s' has invalid value '%s'. Expected a float.", key.string(), stringValue.string()); return false; } diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp index 3569e32d7..9a8816fc6 100644 --- a/libs/utils/ResourceTypes.cpp +++ b/libs/utils/ResourceTypes.cpp @@ -107,20 +107,20 @@ static status_t validate_chunk(const ResChunk_header* chunk, if ((ssize_t)size <= (dataEnd-((const uint8_t*)chunk))) { return NO_ERROR; } - LOGW("%s data size %p extends beyond resource end %p.", + ALOGW("%s data size %p extends beyond resource end %p.", name, (void*)size, (void*)(dataEnd-((const uint8_t*)chunk))); return BAD_TYPE; } - LOGW("%s size 0x%x or headerSize 0x%x is not on an integer boundary.", + ALOGW("%s size 0x%x or headerSize 0x%x is not on an integer boundary.", name, (int)size, (int)headerSize); return BAD_TYPE; } - LOGW("%s size %p is smaller than header size %p.", + ALOGW("%s size %p is smaller than header size %p.", name, (void*)size, (void*)(int)headerSize); return BAD_TYPE; } - LOGW("%s header size %p is too small.", + ALOGW("%s header size %p is too small.", name, (void*)(int)headerSize); return BAD_TYPE; } @@ -221,11 +221,11 @@ static void deserializeInternal(const void* inData, Res_png_9patch* outData) { static bool assertIdmapHeader(const uint32_t* map, size_t sizeBytes) { if (sizeBytes < ResTable::IDMAP_HEADER_SIZE_BYTES) { - LOGW("idmap assertion failed: size=%d bytes\n", sizeBytes); + ALOGW("idmap assertion failed: size=%d bytes\n", sizeBytes); return false; } if (*map != htodl(IDMAP_MAGIC)) { // htodl: map data expected to be in correct endianess - LOGW("idmap assertion failed: invalid magic found (is 0x%08x, expected 0x%08x)\n", + ALOGW("idmap assertion failed: invalid magic found (is 0x%08x, expected 0x%08x)\n", *map, htodl(IDMAP_MAGIC)); return false; } @@ -246,11 +246,11 @@ static status_t idmapLookup(const uint32_t* map, size_t sizeBytes, uint32_t key, const uint32_t typeCount = *map; if (type > typeCount) { - LOGW("Resource ID map: type=%d exceeds number of types=%d\n", type, typeCount); + ALOGW("Resource ID map: type=%d exceeds number of types=%d\n", type, typeCount); return UNKNOWN_ERROR; } if (typeCount > size) { - LOGW("Resource ID map: number of types=%d exceeds size of map=%d\n", typeCount, size); + ALOGW("Resource ID map: number of types=%d exceeds size of map=%d\n", typeCount, size); return UNKNOWN_ERROR; } const uint32_t typeOffset = map[type]; @@ -259,7 +259,7 @@ static status_t idmapLookup(const uint32_t* map, size_t sizeBytes, uint32_t key, return NO_ERROR; } if (typeOffset + 1 > size) { - LOGW("Resource ID map: type offset=%d exceeds reasonable value, size of map=%d\n", + ALOGW("Resource ID map: type offset=%d exceeds reasonable value, size of map=%d\n", typeOffset, size); return UNKNOWN_ERROR; } @@ -271,7 +271,7 @@ static status_t idmapLookup(const uint32_t* map, size_t sizeBytes, uint32_t key, } const uint32_t index = typeOffset + 2 + entry - entryOffset; if (index > size) { - LOGW("Resource ID map: entry index=%d exceeds size of map=%d\n", index, size); + ALOGW("Resource ID map: entry index=%d exceeds size of map=%d\n", index, size); *outValue = 0; return NO_ERROR; } @@ -358,7 +358,7 @@ status_t ResStringPool::setTo(const void* data, size_t size, bool copyData) if (mHeader->header.headerSize > mHeader->header.size || mHeader->header.size > size) { - LOGW("Bad string block: header size %d or total size %d is larger than data size %d\n", + ALOGW("Bad string block: header size %d or total size %d is larger than data size %d\n", (int)mHeader->header.headerSize, (int)mHeader->header.size, (int)size); return (mError=BAD_TYPE); } @@ -370,7 +370,7 @@ status_t ResStringPool::setTo(const void* data, size_t size, bool copyData) if ((mHeader->stringCount*sizeof(uint32_t) < mHeader->stringCount) // uint32 overflow? || (mHeader->header.headerSize+(mHeader->stringCount*sizeof(uint32_t))) > size) { - LOGW("Bad string block: entry of %d items extends past data size %d\n", + ALOGW("Bad string block: entry of %d items extends past data size %d\n", (int)(mHeader->header.headerSize+(mHeader->stringCount*sizeof(uint32_t))), (int)size); return (mError=BAD_TYPE); @@ -388,7 +388,7 @@ status_t ResStringPool::setTo(const void* data, size_t size, bool copyData) mStrings = (const void*) (((const uint8_t*)data)+mHeader->stringsStart); if (mHeader->stringsStart >= (mHeader->header.size-sizeof(uint16_t))) { - LOGW("Bad string block: string pool starts at %d, after total size %d\n", + ALOGW("Bad string block: string pool starts at %d, after total size %d\n", (int)mHeader->stringsStart, (int)mHeader->header.size); return (mError=BAD_TYPE); } @@ -398,13 +398,13 @@ status_t ResStringPool::setTo(const void* data, size_t size, bool copyData) } else { // check invariant: styles starts before end of data if (mHeader->stylesStart >= (mHeader->header.size-sizeof(uint16_t))) { - LOGW("Bad style block: style block starts at %d past data size of %d\n", + ALOGW("Bad style block: style block starts at %d past data size of %d\n", (int)mHeader->stylesStart, (int)mHeader->header.size); return (mError=BAD_TYPE); } // check invariant: styles follow the strings if (mHeader->stylesStart <= mHeader->stringsStart) { - LOGW("Bad style block: style block starts at %d, before strings at %d\n", + ALOGW("Bad style block: style block starts at %d, before strings at %d\n", (int)mHeader->stylesStart, (int)mHeader->stringsStart); return (mError=BAD_TYPE); } @@ -414,7 +414,7 @@ status_t ResStringPool::setTo(const void* data, size_t size, bool copyData) // check invariant: stringCount > 0 requires a string pool to exist if (mStringPoolSize == 0) { - LOGW("Bad string block: stringCount is %d but pool size is 0\n", (int)mHeader->stringCount); + ALOGW("Bad string block: stringCount is %d but pool size is 0\n", (int)mHeader->stringCount); return (mError=BAD_TYPE); } @@ -437,7 +437,7 @@ status_t ResStringPool::setTo(const void* data, size_t size, bool copyData) ((uint8_t*)mStrings)[mStringPoolSize-1] != 0) || (!mHeader->flags&ResStringPool_header::UTF8_FLAG && ((char16_t*)mStrings)[mStringPoolSize-1] != 0)) { - LOGW("Bad string block: last string is not 0-terminated\n"); + ALOGW("Bad string block: last string is not 0-terminated\n"); return (mError=BAD_TYPE); } } else { @@ -449,12 +449,12 @@ status_t ResStringPool::setTo(const void* data, size_t size, bool copyData) mEntryStyles = mEntries + mHeader->stringCount; // invariant: integer overflow in calculating mEntryStyles if (mEntryStyles < mEntries) { - LOGW("Bad string block: integer overflow finding styles\n"); + ALOGW("Bad string block: integer overflow finding styles\n"); return (mError=BAD_TYPE); } if (((const uint8_t*)mEntryStyles-(const uint8_t*)mHeader) > (int)size) { - LOGW("Bad string block: entry of %d styles extends past data size %d\n", + ALOGW("Bad string block: entry of %d styles extends past data size %d\n", (int)((const uint8_t*)mEntryStyles-(const uint8_t*)mHeader), (int)size); return (mError=BAD_TYPE); @@ -462,7 +462,7 @@ status_t ResStringPool::setTo(const void* data, size_t size, bool copyData) mStyles = (const uint32_t*) (((const uint8_t*)data)+mHeader->stylesStart); if (mHeader->stylesStart >= mHeader->header.size) { - LOGW("Bad string block: style pool starts %d, after total size %d\n", + ALOGW("Bad string block: style pool starts %d, after total size %d\n", (int)mHeader->stylesStart, (int)mHeader->header.size); return (mError=BAD_TYPE); } @@ -487,7 +487,7 @@ status_t ResStringPool::setTo(const void* data, size_t size, bool copyData) }; if (memcmp(&mStyles[mStylePoolSize-(sizeof(endSpan)/sizeof(uint32_t))], &endSpan, sizeof(endSpan)) != 0) { - LOGW("Bad string block: last style is not 0xFFFFFFFF-terminated\n"); + ALOGW("Bad string block: last style is not 0xFFFFFFFF-terminated\n"); return (mError=BAD_TYPE); } } else { @@ -581,7 +581,7 @@ const uint16_t* ResStringPool::stringAt(size_t idx, size_t* u16len) const if ((uint32_t)(str+*u16len-strings) < mStringPoolSize) { return str; } else { - LOGW("Bad string block: string #%d extends to %d, past end at %d\n", + ALOGW("Bad string block: string #%d extends to %d, past end at %d\n", (int)idx, (int)(str+*u16len-strings), (int)mStringPoolSize); } } else { @@ -601,7 +601,7 @@ const uint16_t* ResStringPool::stringAt(size_t idx, size_t* u16len) const ssize_t actualLen = utf8_to_utf16_length(u8str, u8len); if (actualLen < 0 || (size_t)actualLen != *u16len) { - LOGW("Bad string block: string #%lld decoded length is not correct " + ALOGW("Bad string block: string #%lld decoded length is not correct " "%lld vs %llu\n", (long long)idx, (long long)actualLen, (long long)*u16len); return NULL; @@ -609,7 +609,7 @@ const uint16_t* ResStringPool::stringAt(size_t idx, size_t* u16len) const char16_t *u16str = (char16_t *)calloc(*u16len+1, sizeof(char16_t)); if (!u16str) { - LOGW("No memory when trying to allocate decode cache for string #%d\n", + ALOGW("No memory when trying to allocate decode cache for string #%d\n", (int)idx); return NULL; } @@ -618,13 +618,13 @@ const uint16_t* ResStringPool::stringAt(size_t idx, size_t* u16len) const mCache[idx] = u16str; return u16str; } else { - LOGW("Bad string block: string #%lld extends to %lld, past end at %lld\n", + ALOGW("Bad string block: string #%lld extends to %lld, past end at %lld\n", (long long)idx, (long long)(u8str+u8len-strings), (long long)mStringPoolSize); } } } else { - LOGW("Bad string block: string #%d entry is at %d, past end at %d\n", + ALOGW("Bad string block: string #%d entry is at %d, past end at %d\n", (int)idx, (int)(off*sizeof(uint16_t)), (int)(mStringPoolSize*sizeof(uint16_t))); } @@ -646,12 +646,12 @@ const char* ResStringPool::string8At(size_t idx, size_t* outLen) const if ((uint32_t)(str+encLen-strings) < mStringPoolSize) { return (const char*)str; } else { - LOGW("Bad string block: string #%d extends to %d, past end at %d\n", + ALOGW("Bad string block: string #%d extends to %d, past end at %d\n", (int)idx, (int)(str+encLen-strings), (int)mStringPoolSize); } } } else { - LOGW("Bad string block: string #%d entry is at %d, past end at %d\n", + ALOGW("Bad string block: string #%d entry is at %d, past end at %d\n", (int)idx, (int)(off*sizeof(uint16_t)), (int)(mStringPoolSize*sizeof(uint16_t))); } @@ -671,7 +671,7 @@ const ResStringPool_span* ResStringPool::styleAt(size_t idx) const if (off < mStylePoolSize) { return (const ResStringPool_span*)(mStyles+off); } else { - LOGW("Bad string block: style #%d entry is at %d, past end at %d\n", + ALOGW("Bad string block: style #%d entry is at %d, past end at %d\n", (int)idx, (int)(off*sizeof(uint32_t)), (int)(mStylePoolSize*sizeof(uint32_t))); } @@ -1087,7 +1087,7 @@ ResXMLParser::event_code_t ResXMLParser::nextNode() do { const ResXMLTree_node* next = (const ResXMLTree_node*) (((const uint8_t*)mCurNode) + dtohl(mCurNode->header.size)); - //LOGW("Next node: prev=%p, next=%p\n", mCurNode, next); + //ALOGW("Next node: prev=%p, next=%p\n", mCurNode, next); if (((const uint8_t*)next) >= mTree.mDataEnd) { mCurNode = NULL; @@ -1120,14 +1120,14 @@ ResXMLParser::event_code_t ResXMLParser::nextNode() minExtSize = sizeof(ResXMLTree_cdataExt); break; default: - LOGW("Unknown XML block: header type %d in node at %d\n", + ALOGW("Unknown XML block: header type %d in node at %d\n", (int)dtohs(next->header.type), (int)(((const uint8_t*)next)-((const uint8_t*)mTree.mHeader))); continue; } if ((totalSize-headerSize) < minExtSize) { - LOGW("Bad XML block: header type 0x%x in node at 0x%x has size %d, need %d\n", + ALOGW("Bad XML block: header type 0x%x in node at 0x%x has size %d, need %d\n", (int)dtohs(next->header.type), (int)(((const uint8_t*)next)-((const uint8_t*)mTree.mHeader)), (int)(totalSize-headerSize), (int)minExtSize); @@ -1199,7 +1199,7 @@ status_t ResXMLTree::setTo(const void* data, size_t size, bool copyData) mHeader = (const ResXMLTree_header*)data; mSize = dtohl(mHeader->header.size); if (dtohs(mHeader->header.headerSize) > mSize || mSize > size) { - LOGW("Bad XML block: header size %d or total size %d is larger than data size %d\n", + ALOGW("Bad XML block: header size %d or total size %d is larger than data size %d\n", (int)dtohs(mHeader->header.headerSize), (int)dtohl(mHeader->header.size), (int)size); mError = BAD_TYPE; @@ -1259,7 +1259,7 @@ status_t ResXMLTree::setTo(const void* data, size_t size, bool copyData) } if (mRootNode == NULL) { - LOGW("Bad XML block: no root element node found\n"); + ALOGW("Bad XML block: no root element node found\n"); mError = BAD_TYPE; goto done; } @@ -1313,12 +1313,12 @@ status_t ResXMLTree::validateNode(const ResXMLTree_node* node) const if ((dtohs(attrExt->attributeStart)+attrSize) <= (size-headerSize)) { return NO_ERROR; } - LOGW("Bad XML block: node attributes use 0x%x bytes, only have 0x%x bytes\n", + ALOGW("Bad XML block: node attributes use 0x%x bytes, only have 0x%x bytes\n", (unsigned int)(dtohs(attrExt->attributeStart)+attrSize), (unsigned int)(size-headerSize)); } else { - LOGW("Bad XML start block: node header size 0x%x, size 0x%x\n", + ALOGW("Bad XML start block: node header size 0x%x, size 0x%x\n", (unsigned int)headerSize, (unsigned int)size); } return BAD_TYPE; @@ -1342,21 +1342,21 @@ status_t ResXMLTree::validateNode(const ResXMLTree_node* node) const <= (size-headerSize)) { return NO_ERROR; } - LOGW("Bad XML block: node attributes use 0x%x bytes, only have 0x%x bytes\n", + ALOGW("Bad XML block: node attributes use 0x%x bytes, only have 0x%x bytes\n", ((int)dtohs(node->attributeSize))*dtohs(node->attributeCount), (int)(size-headerSize)); return BAD_TYPE; } - LOGW("Bad XML block: node at 0x%x extends beyond data end 0x%x\n", + ALOGW("Bad XML block: node at 0x%x extends beyond data end 0x%x\n", (int)(((const uint8_t*)node)-((const uint8_t*)mHeader)), (int)mSize); return BAD_TYPE; } - LOGW("Bad XML block: node at 0x%x header size 0x%x smaller than total size 0x%x\n", + ALOGW("Bad XML block: node at 0x%x header size 0x%x smaller than total size 0x%x\n", (int)(((const uint8_t*)node)-((const uint8_t*)mHeader)), (int)headerSize, (int)size); return BAD_TYPE; } - LOGW("Bad XML block: node at 0x%x header size 0x%x too small\n", + ALOGW("Bad XML block: node at 0x%x header size 0x%x too small\n", (int)(((const uint8_t*)node)-((const uint8_t*)mHeader)), (int)headerSize); return BAD_TYPE; @@ -1712,7 +1712,7 @@ ssize_t ResTable::Theme::getAttribute(uint32_t resID, Res_value* outValue, resID = te.value.data; continue; } - LOGW("Too many attribute references, stopped at: 0x%08x\n", resID); + ALOGW("Too many attribute references, stopped at: 0x%08x\n", resID); return BAD_INDEX; } else if (type != Res_value::TYPE_NULL) { *outValue = te.value; @@ -1813,7 +1813,7 @@ status_t ResTable::add(Asset* asset, void* cookie, bool copyData, const void* id { const void* data = asset->getBuffer(true); if (data == NULL) { - LOGW("Unable to get buffer of resource asset file"); + ALOGW("Unable to get buffer of resource asset file"); return UNKNOWN_ERROR; } size_t size = (size_t)asset->getLength(); @@ -1888,13 +1888,13 @@ status_t ResTable::add(const void* data, size_t size, void* cookie, 16, 16, 0, false, printToLogFunc)); if (dtohs(header->header->header.headerSize) > header->size || header->size > size) { - LOGW("Bad resource table: header size 0x%x or total size 0x%x is larger than data size 0x%x\n", + ALOGW("Bad resource table: header size 0x%x or total size 0x%x is larger than data size 0x%x\n", (int)dtohs(header->header->header.headerSize), (int)header->size, (int)size); return (mError=BAD_TYPE); } if (((dtohs(header->header->header.headerSize)|header->size)&0x3) != 0) { - LOGW("Bad resource table: header size 0x%x or total size 0x%x is not on an integer boundary\n", + ALOGW("Bad resource table: header size 0x%x or total size 0x%x is not on an integer boundary\n", (int)dtohs(header->header->header.headerSize), (int)header->size); return (mError=BAD_TYPE); @@ -1927,11 +1927,11 @@ status_t ResTable::add(const void* data, size_t size, void* cookie, return (mError=err); } } else { - LOGW("Multiple string chunks found in resource table."); + ALOGW("Multiple string chunks found in resource table."); } } else if (ctype == RES_TABLE_PACKAGE_TYPE) { if (curPackage >= dtohl(header->header->packageCount)) { - LOGW("More package chunks were found than the %d declared in the header.", + ALOGW("More package chunks were found than the %d declared in the header.", dtohl(header->header->packageCount)); return (mError=BAD_TYPE); } @@ -1949,7 +1949,7 @@ status_t ResTable::add(const void* data, size_t size, void* cookie, } curPackage++; } else { - LOGW("Unknown chunk type %p in table at %p.\n", + ALOGW("Unknown chunk type %p in table at %p.\n", (void*)(int)(ctype), (void*)(((const uint8_t*)chunk) - ((const uint8_t*)header->header))); } @@ -1958,13 +1958,13 @@ status_t ResTable::add(const void* data, size_t size, void* cookie, } if (curPackage < dtohl(header->header->packageCount)) { - LOGW("Fewer package chunks (%d) were found than the %d declared in the header.", + ALOGW("Fewer package chunks (%d) were found than the %d declared in the header.", (int)curPackage, dtohl(header->header->packageCount)); return (mError=BAD_TYPE); } mError = header->values.getError(); if (mError != NO_ERROR) { - LOGW("No string values found in resource table!"); + ALOGW("No string values found in resource table!"); } TABLE_NOISY(LOGV("Returning from add with mError=%d\n", mError)); @@ -2011,20 +2011,20 @@ bool ResTable::getResourceName(uint32_t resID, resource_name* outName) const if (p < 0) { if (Res_GETPACKAGE(resID)+1 == 0) { - LOGW("No package identifier when getting name for resource number 0x%08x", resID); + ALOGW("No package identifier when getting name for resource number 0x%08x", resID); } else { - LOGW("No known package when getting name for resource number 0x%08x", resID); + ALOGW("No known package when getting name for resource number 0x%08x", resID); } return false; } if (t < 0) { - LOGW("No type identifier when getting name for resource number 0x%08x", resID); + ALOGW("No type identifier when getting name for resource number 0x%08x", resID); return false; } const PackageGroup* const grp = mPackageGroups[p]; if (grp == NULL) { - LOGW("Bad identifier when getting name for resource number 0x%08x", resID); + ALOGW("Bad identifier when getting name for resource number 0x%08x", resID); return false; } if (grp->packages.size() > 0) { @@ -2067,14 +2067,14 @@ ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag if (p < 0) { if (Res_GETPACKAGE(resID)+1 == 0) { - LOGW("No package identifier when getting value for resource number 0x%08x", resID); + ALOGW("No package identifier when getting value for resource number 0x%08x", resID); } else { - LOGW("No known package when getting value for resource number 0x%08x", resID); + ALOGW("No known package when getting value for resource number 0x%08x", resID); } return BAD_INDEX; } if (t < 0) { - LOGW("No type identifier when getting value for resource number 0x%08x", resID); + ALOGW("No type identifier when getting value for resource number 0x%08x", resID); return BAD_INDEX; } @@ -2089,7 +2089,7 @@ ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag // recently added. const PackageGroup* const grp = mPackageGroups[p]; if (grp == NULL) { - LOGW("Bad identifier when getting value for resource number 0x%08x", resID); + ALOGW("Bad identifier when getting value for resource number 0x%08x", resID); return BAD_INDEX; } @@ -2141,7 +2141,7 @@ ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag // overlay package did not specify a default. // Non-overlay packages are still required to provide a default. if (offset < 0 && ip == 0) { - LOGW("Failure getting entry for 0x%08x (t=%d e=%d) in package %zd (error %d)\n", + ALOGW("Failure getting entry for 0x%08x (t=%d e=%d) in package %zd (error %d)\n", resID, T, E, ip, (int)offset); rc = offset; goto out; @@ -2151,7 +2151,7 @@ ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag if ((dtohs(entry->flags)&entry->FLAG_COMPLEX) != 0) { if (!mayBeBag) { - LOGW("Requesting resource %p failed because it is complex\n", + ALOGW("Requesting resource %p failed because it is complex\n", (void*)resID); } continue; @@ -2161,7 +2161,7 @@ ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag << HexDump(type, dtohl(type->header.size)) << endl); if ((size_t)offset > (dtohl(type->header.size)-sizeof(Res_value))) { - LOGW("ResTable_item at %d is beyond type chunk data %d", + ALOGW("ResTable_item at %d is beyond type chunk data %d", (int)offset, dtohl(type->header.size)); rc = BAD_TYPE; goto out; @@ -2307,23 +2307,23 @@ ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag, const int e = Res_GETENTRY(resID); if (p < 0) { - LOGW("Invalid package identifier when getting bag for resource number 0x%08x", resID); + ALOGW("Invalid package identifier when getting bag for resource number 0x%08x", resID); return BAD_INDEX; } if (t < 0) { - LOGW("No type identifier when getting bag for resource number 0x%08x", resID); + ALOGW("No type identifier when getting bag for resource number 0x%08x", resID); return BAD_INDEX; } //printf("Get bag: id=0x%08x, p=%d, t=%d\n", resID, p, t); PackageGroup* const grp = mPackageGroups[p]; if (grp == NULL) { - LOGW("Bad identifier when getting bag for resource number 0x%08x", resID); + ALOGW("Bad identifier when getting bag for resource number 0x%08x", resID); return false; } if (t >= (int)grp->typeCount) { - LOGW("Type identifier 0x%x is larger than type count 0x%x", + ALOGW("Type identifier 0x%x is larger than type count 0x%x", t+1, (int)grp->typeCount); return BAD_INDEX; } @@ -2334,7 +2334,7 @@ ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag, const size_t NENTRY = typeConfigs->entryCount; if (e >= (int)NENTRY) { - LOGW("Entry identifier 0x%x is larger than entry count 0x%x", + ALOGW("Entry identifier 0x%x is larger than entry count 0x%x", e, (int)typeConfigs->entryCount); return BAD_INDEX; } @@ -2353,7 +2353,7 @@ ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag, //ALOGI("Found existing bag for: %p\n", (void*)resID); return set->numAttrs; } - LOGW("Attempt to retrieve bag 0x%08x which is invalid or in a cycle.", + ALOGW("Attempt to retrieve bag 0x%08x which is invalid or in a cycle.", resID); return BAD_INDEX; } @@ -2429,7 +2429,7 @@ ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag, } if ((dtohs(entry->flags)&entry->FLAG_COMPLEX) == 0) { - LOGW("Skipping entry %p in package table %d because it is not complex!\n", + ALOGW("Skipping entry %p in package table %d because it is not complex!\n", (void*)resID, (int)ip); continue; } @@ -2505,7 +2505,7 @@ ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag, TABLE_NOISY(printf("Now at %p\n", (void*)curOff)); if ((size_t)curOff > (dtohl(type->header.size)-sizeof(ResTable_map))) { - LOGW("ResTable_map at %d is beyond type chunk data %d", + ALOGW("ResTable_map at %d is beyond type chunk data %d", (int)curOff, dtohl(type->header.size)); return BAD_TYPE; } @@ -2676,7 +2676,7 @@ nope: && name[6] == '_') { int index = atoi(String8(name + 7, nameLen - 7).string()); if (Res_CHECKID(index)) { - LOGW("Array resource index: %d is too large.", + ALOGW("Array resource index: %d is too large.", index); return 0; } @@ -2792,12 +2792,12 @@ nope: offset += typeOffset; if (offset > (dtohl(ty->header.size)-sizeof(ResTable_entry))) { - LOGW("ResTable_entry at %d is beyond type chunk data %d", + ALOGW("ResTable_entry at %d is beyond type chunk data %d", offset, dtohl(ty->header.size)); return 0; } if ((offset&0x3) != 0) { - LOGW("ResTable_entry at %d (pkg=%d type=%d ent=%d) is not on an integer boundary when looking for %s:%s/%s", + ALOGW("ResTable_entry at %d (pkg=%d type=%d ent=%d) is not on an integer boundary when looking for %s:%s/%s", (int)offset, (int)group->id, (int)ti+1, (int)i, String8(package, packageLen).string(), String8(type, typeLen).string(), @@ -2808,7 +2808,7 @@ nope: const ResTable_entry* const entry = (const ResTable_entry*) (((const uint8_t*)ty) + offset); if (dtohs(entry->size) < sizeof(*entry)) { - LOGW("ResTable_entry size %d is too small", dtohs(entry->size)); + ALOGW("ResTable_entry size %d is too small", dtohs(entry->size)); return BAD_TYPE; } @@ -3935,7 +3935,7 @@ ssize_t ResTable::getEntry( } if ((size_t)entryIndex >= allTypes->entryCount) { - LOGW("getEntry failing because entryIndex %d is beyond type entryCount %d", + ALOGW("getEntry failing because entryIndex %d is beyond type entryCount %d", entryIndex, (int)allTypes->entryCount); return BAD_TYPE; } @@ -4039,12 +4039,12 @@ ssize_t ResTable::getEntry( << ", offset=" << (void*)offset << endl); if (offset > (dtohl(type->header.size)-sizeof(ResTable_entry))) { - LOGW("ResTable_entry at 0x%x is beyond type chunk data 0x%x", + ALOGW("ResTable_entry at 0x%x is beyond type chunk data 0x%x", offset, dtohl(type->header.size)); return BAD_TYPE; } if ((offset&0x3) != 0) { - LOGW("ResTable_entry at 0x%x is not on an integer boundary", + ALOGW("ResTable_entry at 0x%x is not on an integer boundary", offset); return BAD_TYPE; } @@ -4052,7 +4052,7 @@ ssize_t ResTable::getEntry( const ResTable_entry* const entry = (const ResTable_entry*) (((const uint8_t*)type) + offset); if (dtohs(entry->size) < sizeof(*entry)) { - LOGW("ResTable_entry size 0x%x is too small", dtohs(entry->size)); + ALOGW("ResTable_entry size 0x%x is too small", dtohs(entry->size)); return BAD_TYPE; } @@ -4077,22 +4077,22 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg, const size_t pkgSize = dtohl(pkg->header.size); if (dtohl(pkg->typeStrings) >= pkgSize) { - LOGW("ResTable_package type strings at %p are past chunk size %p.", + ALOGW("ResTable_package type strings at %p are past chunk size %p.", (void*)dtohl(pkg->typeStrings), (void*)pkgSize); return (mError=BAD_TYPE); } if ((dtohl(pkg->typeStrings)&0x3) != 0) { - LOGW("ResTable_package type strings at %p is not on an integer boundary.", + ALOGW("ResTable_package type strings at %p is not on an integer boundary.", (void*)dtohl(pkg->typeStrings)); return (mError=BAD_TYPE); } if (dtohl(pkg->keyStrings) >= pkgSize) { - LOGW("ResTable_package key strings at %p are past chunk size %p.", + ALOGW("ResTable_package key strings at %p are past chunk size %p.", (void*)dtohl(pkg->keyStrings), (void*)pkgSize); return (mError=BAD_TYPE); } if ((dtohl(pkg->keyStrings)&0x3) != 0) { - LOGW("ResTable_package key strings at %p is not on an integer boundary.", + ALOGW("ResTable_package key strings at %p is not on an integer boundary.", (void*)dtohl(pkg->keyStrings)); return (mError=BAD_TYPE); } @@ -4195,7 +4195,7 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg, if ((dtohl(typeSpec->entryCount) > (INT32_MAX/sizeof(uint32_t)) || dtohs(typeSpec->header.headerSize)+(sizeof(uint32_t)*dtohl(typeSpec->entryCount)) > typeSpecSize)) { - LOGW("ResTable_typeSpec entry index to %p extends beyond chunk end %p.", + ALOGW("ResTable_typeSpec entry index to %p extends beyond chunk end %p.", (void*)(dtohs(typeSpec->header.headerSize) +(sizeof(uint32_t)*dtohl(typeSpec->entryCount))), (void*)typeSpecSize); @@ -4203,7 +4203,7 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg, } if (typeSpec->id == 0) { - LOGW("ResTable_type has an id of 0."); + ALOGW("ResTable_type has an id of 0."); return (mError=BAD_TYPE); } @@ -4215,7 +4215,7 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg, t = new Type(header, package, dtohl(typeSpec->entryCount)); package->types.editItemAt(typeSpec->id-1) = t; } else if (dtohl(typeSpec->entryCount) != t->entryCount) { - LOGW("ResTable_typeSpec entry count inconsistent: given %d, previously %d", + ALOGW("ResTable_typeSpec entry count inconsistent: given %d, previously %d", (int)dtohl(typeSpec->entryCount), (int)t->entryCount); return (mError=BAD_TYPE); } @@ -4240,7 +4240,7 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg, (void*)typeSize)); if (dtohs(type->header.headerSize)+(sizeof(uint32_t)*dtohl(type->entryCount)) > typeSize) { - LOGW("ResTable_type entry index to %p extends beyond chunk end %p.", + ALOGW("ResTable_type entry index to %p extends beyond chunk end %p.", (void*)(dtohs(type->header.headerSize) +(sizeof(uint32_t)*dtohl(type->entryCount))), (void*)typeSize); @@ -4248,12 +4248,12 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg, } if (dtohl(type->entryCount) != 0 && dtohl(type->entriesStart) > (typeSize-sizeof(ResTable_entry))) { - LOGW("ResTable_type entriesStart at %p extends beyond chunk end %p.", + ALOGW("ResTable_type entriesStart at %p extends beyond chunk end %p.", (void*)dtohl(type->entriesStart), (void*)typeSize); return (mError=BAD_TYPE); } if (type->id == 0) { - LOGW("ResTable_type has an id of 0."); + ALOGW("ResTable_type has an id of 0."); return (mError=BAD_TYPE); } @@ -4265,7 +4265,7 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg, t = new Type(header, package, dtohl(type->entryCount)); package->types.editItemAt(type->id-1) = t; } else if (dtohl(type->entryCount) != t->entryCount) { - LOGW("ResTable_type entry count inconsistent: given %d, previously %d", + ALOGW("ResTable_type entry count inconsistent: given %d, previously %d", (int)dtohl(type->entryCount), (int)t->entryCount); return (mError=BAD_TYPE); } @@ -4346,7 +4346,7 @@ status_t ResTable::createIdmap(const ResTable& overlay, uint32_t originalCrc, ui | (0x0000ffff & (entryIndex)); resource_name resName; if (!this->getResourceName(resID, &resName)) { - LOGW("idmap: resource 0x%08x has spec but lacks values, skipping\n", resID); + ALOGW("idmap: resource 0x%08x has spec but lacks values, skipping\n", resID); continue; } diff --git a/libs/utils/SystemClock.cpp b/libs/utils/SystemClock.cpp index 89a052fb9..8b8ac1031 100644 --- a/libs/utils/SystemClock.cpp +++ b/libs/utils/SystemClock.cpp @@ -69,20 +69,20 @@ int setCurrentTimeMillis(int64_t millis) #ifdef HAVE_ANDROID_OS fd = open("/dev/alarm", O_RDWR); if(fd < 0) { - LOGW("Unable to open alarm driver: %s\n", strerror(errno)); + ALOGW("Unable to open alarm driver: %s\n", strerror(errno)); return -1; } ts.tv_sec = tv.tv_sec; ts.tv_nsec = tv.tv_usec * 1000; res = ioctl(fd, ANDROID_ALARM_SET_RTC, &ts); if(res < 0) { - LOGW("Unable to set rtc to %ld: %s\n", tv.tv_sec, strerror(errno)); + ALOGW("Unable to set rtc to %ld: %s\n", tv.tv_sec, strerror(errno)); ret = -1; } close(fd); #else if (settimeofday(&tv, NULL) != 0) { - LOGW("Unable to set clock to %d.%d: %s\n", + ALOGW("Unable to set clock to %d.%d: %s\n", (int) tv.tv_sec, (int) tv.tv_usec, strerror(errno)); ret = -1; } diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp index fe4b8e62b..fb52d7c74 100644 --- a/libs/utils/Threads.cpp +++ b/libs/utils/Threads.cpp @@ -870,7 +870,7 @@ status_t Thread::requestExitAndWait() { Mutex::Autolock _l(mLock); if (mThread == getThreadId()) { - LOGW( + ALOGW( "Thread (this=%p): don't call waitForExit() from this " "Thread object's thread. It's a guaranteed deadlock!", this); @@ -894,7 +894,7 @@ status_t Thread::join() { Mutex::Autolock _l(mLock); if (mThread == getThreadId()) { - LOGW( + ALOGW( "Thread (this=%p): don't call join() from this " "Thread object's thread. It's a guaranteed deadlock!", this); diff --git a/libs/utils/ZipFileRO.cpp b/libs/utils/ZipFileRO.cpp index 6ca9a28ba..a6cce7e32 100644 --- a/libs/utils/ZipFileRO.cpp +++ b/libs/utils/ZipFileRO.cpp @@ -120,7 +120,7 @@ int ZipFileRO::entryToIndex(const ZipEntryRO entry) const { long ent = ((long) entry) - kZipEntryAdj; if (ent < 0 || ent >= mHashTableSize || mHashTable[ent].name == NULL) { - LOGW("Invalid ZipEntryRO %p (%ld)\n", entry, ent); + ALOGW("Invalid ZipEntryRO %p (%ld)\n", entry, ent); return -1; } return ent; @@ -142,7 +142,7 @@ status_t ZipFileRO::open(const char* zipFileName) */ fd = ::open(zipFileName, O_RDONLY | O_BINARY); if (fd < 0) { - LOGW("Unable to open zip '%s': %s\n", zipFileName, strerror(errno)); + ALOGW("Unable to open zip '%s': %s\n", zipFileName, strerror(errno)); return NAME_NOT_FOUND; } @@ -194,7 +194,7 @@ bool ZipFileRO::mapCentralDirectory(void) unsigned char* scanBuf = (unsigned char*) malloc(readAmount); if (scanBuf == NULL) { - LOGW("couldn't allocate scanBuf: %s", strerror(errno)); + ALOGW("couldn't allocate scanBuf: %s", strerror(errno)); free(scanBuf); return false; } @@ -203,7 +203,7 @@ bool ZipFileRO::mapCentralDirectory(void) * Make sure this is a Zip archive. */ if (lseek64(mFd, 0, SEEK_SET) != 0) { - LOGW("seek to start failed: %s", strerror(errno)); + ALOGW("seek to start failed: %s", strerror(errno)); free(scanBuf); return false; } @@ -243,13 +243,13 @@ bool ZipFileRO::mapCentralDirectory(void) off64_t searchStart = mFileLength - readAmount; if (lseek64(mFd, searchStart, SEEK_SET) != searchStart) { - LOGW("seek %ld failed: %s\n", (long) searchStart, strerror(errno)); + ALOGW("seek %ld failed: %s\n", (long) searchStart, strerror(errno)); free(scanBuf); return false; } actual = TEMP_FAILURE_RETRY(read(mFd, scanBuf, readAmount)); if (actual != (ssize_t) readAmount) { - LOGW("Zip: read " ZD ", expected " ZD ". Failed: %s\n", + ALOGW("Zip: read " ZD ", expected " ZD ". Failed: %s\n", (ZD_TYPE) actual, (ZD_TYPE) readAmount, strerror(errno)); free(scanBuf); return false; @@ -290,12 +290,12 @@ bool ZipFileRO::mapCentralDirectory(void) // Verify that they look reasonable. if ((long long) dirOffset + (long long) dirSize > (long long) eocdOffset) { - LOGW("bad offsets (dir %ld, size %u, eocd %ld)\n", + ALOGW("bad offsets (dir %ld, size %u, eocd %ld)\n", (long) dirOffset, dirSize, (long) eocdOffset); return false; } if (numEntries == 0) { - LOGW("empty archive?\n"); + ALOGW("empty archive?\n"); return false; } @@ -304,12 +304,12 @@ bool ZipFileRO::mapCentralDirectory(void) mDirectoryMap = new FileMap(); if (mDirectoryMap == NULL) { - LOGW("Unable to create directory map: %s", strerror(errno)); + ALOGW("Unable to create directory map: %s", strerror(errno)); return false; } if (!mDirectoryMap->create(mFileName, mFd, dirOffset, dirSize, true)) { - LOGW("Unable to map '%s' (" ZD " to " ZD "): %s\n", mFileName, + ALOGW("Unable to map '%s' (" ZD " to " ZD "): %s\n", mFileName, (ZD_TYPE) dirOffset, (ZD_TYPE) (dirOffset + dirSize), strerror(errno)); return false; } @@ -341,17 +341,17 @@ bool ZipFileRO::parseZipArchive(void) const unsigned char* ptr = cdPtr; for (int i = 0; i < numEntries; i++) { if (get4LE(ptr) != kCDESignature) { - LOGW("Missed a central dir sig (at %d)\n", i); + ALOGW("Missed a central dir sig (at %d)\n", i); goto bail; } if (ptr + kCDELen > cdPtr + cdLength) { - LOGW("Ran off the end (at %d)\n", i); + ALOGW("Ran off the end (at %d)\n", i); goto bail; } long localHdrOffset = (long) get4LE(ptr + kCDELocalOffset); if (localHdrOffset >= mDirectoryOffset) { - LOGW("bad LFH offset %ld at entry %d\n", localHdrOffset, i); + ALOGW("bad LFH offset %ld at entry %d\n", localHdrOffset, i); goto bail; } @@ -367,7 +367,7 @@ bool ZipFileRO::parseZipArchive(void) ptr += kCDELen + fileNameLen + extraLen + commentLen; if ((size_t)(ptr - cdPtr) > cdLength) { - LOGW("bad CD advance (%d vs " ZD ") at entry %d\n", + ALOGW("bad CD advance (%d vs " ZD ") at entry %d\n", (int) (ptr - cdPtr), (ZD_TYPE) cdLength, i); goto bail; } @@ -452,7 +452,7 @@ ZipEntryRO ZipFileRO::findEntryByName(const char* fileName) const ZipEntryRO ZipFileRO::findEntryByIndex(int idx) const { if (idx < 0 || idx >= mNumEntries) { - LOGW("Invalid index %d\n", idx); + ALOGW("Invalid index %d\n", idx); return NULL; } @@ -544,12 +544,12 @@ bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, size_t* pUncompLen, TEMP_FAILURE_RETRY(pread64(mFd, lfhBuf, sizeof(lfhBuf), localHdrOffset)); if (actual != sizeof(lfhBuf)) { - LOGW("failed reading lfh from offset %ld\n", localHdrOffset); + ALOGW("failed reading lfh from offset %ld\n", localHdrOffset); return false; } if (get4LE(lfhBuf) != kLFHSignature) { - LOGW("didn't find signature at start of lfh; wanted: offset=%ld data=0x%08x; " + ALOGW("didn't find signature at start of lfh; wanted: offset=%ld data=0x%08x; " "got: data=0x%08lx\n", localHdrOffset, kLFHSignature, get4LE(lfhBuf)); return false; @@ -567,20 +567,20 @@ bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, size_t* pUncompLen, AutoMutex _l(mFdLock); if (lseek64(mFd, localHdrOffset, SEEK_SET) != localHdrOffset) { - LOGW("failed seeking to lfh at offset %ld\n", localHdrOffset); + ALOGW("failed seeking to lfh at offset %ld\n", localHdrOffset); return false; } ssize_t actual = TEMP_FAILURE_RETRY(read(mFd, lfhBuf, sizeof(lfhBuf))); if (actual != sizeof(lfhBuf)) { - LOGW("failed reading lfh from offset %ld\n", localHdrOffset); + ALOGW("failed reading lfh from offset %ld\n", localHdrOffset); return false; } if (get4LE(lfhBuf) != kLFHSignature) { off64_t actualOffset = lseek64(mFd, 0, SEEK_CUR); - LOGW("didn't find signature at start of lfh; wanted: offset=%ld data=0x%08x; " + ALOGW("didn't find signature at start of lfh; wanted: offset=%ld data=0x%08x; " "got: offset=" ZD " data=0x%08lx\n", localHdrOffset, kLFHSignature, (ZD_TYPE) actualOffset, get4LE(lfhBuf)); return false; @@ -591,13 +591,13 @@ bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, size_t* pUncompLen, off64_t dataOffset = localHdrOffset + kLFHLen + get2LE(lfhBuf + kLFHNameLen) + get2LE(lfhBuf + kLFHExtraLen); if (dataOffset >= cdOffset) { - LOGW("bad data offset %ld in zip\n", (long) dataOffset); + ALOGW("bad data offset %ld in zip\n", (long) dataOffset); return false; } /* check lengths */ if ((off64_t)(dataOffset + compLen) > cdOffset) { - LOGW("bad compressed length in zip (%ld + " ZD " > %ld)\n", + ALOGW("bad compressed length in zip (%ld + " ZD " > %ld)\n", (long) dataOffset, (ZD_TYPE) compLen, (long) cdOffset); return false; } @@ -819,7 +819,7 @@ bail: */ zerr = inflate(&zstream, Z_FINISH); if (zerr != Z_STREAM_END) { - LOGW("Zip inflate failed, zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)\n", + ALOGW("Zip inflate failed, zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)\n", zerr, zstream.next_in, zstream.avail_in, zstream.next_out, zstream.avail_out); goto z_bail; @@ -827,7 +827,7 @@ bail: /* paranoia */ if (zstream.total_out != uncompLen) { - LOGW("Size mismatch on inflated file (%ld vs " ZD ")\n", + ALOGW("Size mismatch on inflated file (%ld vs " ZD ")\n", zstream.total_out, (ZD_TYPE) uncompLen); goto z_bail; } @@ -890,7 +890,7 @@ bail: */ zerr = inflate(&zstream, Z_NO_FLUSH); if (zerr != Z_OK && zerr != Z_STREAM_END) { - LOGW("zlib inflate: zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)\n", + ALOGW("zlib inflate: zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)\n", zerr, zstream.next_in, zstream.avail_in, zstream.next_out, zstream.avail_out); goto z_bail; @@ -903,7 +903,7 @@ bail: long writeSize = zstream.next_out - writeBuf; int cc = write(fd, writeBuf, writeSize); if (cc != (int) writeSize) { - LOGW("write failed in inflate (%d vs %ld)\n", cc, writeSize); + ALOGW("write failed in inflate (%d vs %ld)\n", cc, writeSize); goto z_bail; } @@ -916,7 +916,7 @@ bail: /* paranoia */ if (zstream.total_out != uncompLen) { - LOGW("Size mismatch on inflated file (%ld vs " ZD ")\n", + ALOGW("Size mismatch on inflated file (%ld vs " ZD ")\n", zstream.total_out, (ZD_TYPE) uncompLen); goto z_bail; } diff --git a/libs/utils/ZipUtils.cpp b/libs/utils/ZipUtils.cpp index cc5c68a65..0fe1a7bfa 100644 --- a/libs/utils/ZipUtils.cpp +++ b/libs/utils/ZipUtils.cpp @@ -124,7 +124,7 @@ using namespace android; assert(zerr == Z_STREAM_END); /* other errors should've been caught */ if ((long) zstream.total_out != uncompressedLen) { - LOGW("Size mismatch on inflated file (%ld vs %ld)\n", + ALOGW("Size mismatch on inflated file (%ld vs %ld)\n", zstream.total_out, uncompressedLen); goto z_bail; } @@ -236,7 +236,7 @@ bail: assert(zerr == Z_STREAM_END); /* other errors should've been caught */ if ((long) zstream.total_out != uncompressedLen) { - LOGW("Size mismatch on inflated file (%ld vs %ld)\n", + ALOGW("Size mismatch on inflated file (%ld vs %ld)\n", zstream.total_out, uncompressedLen); goto z_bail; } From 1b781ab0e0e8d59a7a8d1140bf6dee96a48a160c Mon Sep 17 00:00:00 2001 From: Steve Block Date: Fri, 6 Jan 2012 19:20:56 +0000 Subject: [PATCH 384/541] Rename (IF_)LOGE(_IF) to (IF_)ALOGE(_IF) DO NOT MERGE See https://android-git.corp.google.com/g/#/c/157220 Bug: 5449033 Change-Id: Ic9c19d30693bd56755f55906127cd6bd7126096c --- include/utils/GenerationCache.h | 2 +- libs/utils/Asset.cpp | 8 ++++---- libs/utils/AssetManager.cpp | 2 +- libs/utils/BackupHelpers.cpp | 14 +++++++------- libs/utils/BlobCache.cpp | 16 ++++++++-------- libs/utils/FileMap.cpp | 8 ++++---- libs/utils/Looper.cpp | 10 +++++----- libs/utils/PropertyMap.cpp | 14 +++++++------- libs/utils/RefBase.cpp | 10 +++++----- libs/utils/ResourceTypes.cpp | 10 +++++----- libs/utils/StreamingZipInflater.cpp | 6 +++--- libs/utils/Threads.cpp | 2 +- libs/utils/Tokenizer.cpp | 6 +++--- libs/utils/ZipFileRO.cpp | 16 ++++++++-------- libs/utils/ZipUtils.cpp | 8 ++++---- 15 files changed, 66 insertions(+), 66 deletions(-) diff --git a/include/utils/GenerationCache.h b/include/utils/GenerationCache.h index da85a9aeb..40722d11e 100644 --- a/include/utils/GenerationCache.h +++ b/include/utils/GenerationCache.h @@ -205,7 +205,7 @@ bool GenerationCache::removeOldest() { removeAt(index); return true; } - LOGE("GenerationCache: removeOldest failed to find the item in the cache " + ALOGE("GenerationCache: removeOldest failed to find the item in the cache " "with the given key, but we know it must be in there. " "Is the key comparator kaput?"); } diff --git a/libs/utils/Asset.cpp b/libs/utils/Asset.cpp index 22af816fe..50e701aa8 100644 --- a/libs/utils/Asset.cpp +++ b/libs/utils/Asset.cpp @@ -473,7 +473,7 @@ ssize_t _FileAsset::read(void* buf, size_t count) /* read from the file */ //printf("file read\n"); if (ftell(mFp) != mStart + mOffset) { - LOGE("Hosed: %ld != %ld+%ld\n", + ALOGE("Hosed: %ld != %ld+%ld\n", ftell(mFp), (long) mStart, (long) mOffset); assert(false); } @@ -581,7 +581,7 @@ const void* _FileAsset::getBuffer(bool wordAligned) buf = new unsigned char[allocLen]; if (buf == NULL) { - LOGE("alloc of %ld bytes failed\n", (long) allocLen); + ALOGE("alloc of %ld bytes failed\n", (long) allocLen); return NULL; } @@ -590,7 +590,7 @@ const void* _FileAsset::getBuffer(bool wordAligned) long oldPosn = ftell(mFp); fseek(mFp, mStart, SEEK_SET); if (fread(buf, 1, mLength, mFp) != (size_t) mLength) { - LOGE("failed reading %ld bytes\n", (long) mLength); + ALOGE("failed reading %ld bytes\n", (long) mLength); delete[] buf; return NULL; } @@ -658,7 +658,7 @@ const void* _FileAsset::ensureAlignment(FileMap* map) getAssetSource(), (int)mLength); unsigned char* buf = new unsigned char[mLength]; if (buf == NULL) { - LOGE("alloc of %ld bytes failed\n", (long) mLength); + ALOGE("alloc of %ld bytes failed\n", (long) mLength); return NULL; } memcpy(buf, data, mLength); diff --git a/libs/utils/AssetManager.cpp b/libs/utils/AssetManager.cpp index 8a8551f78..47a2b9953 100644 --- a/libs/utils/AssetManager.cpp +++ b/libs/utils/AssetManager.cpp @@ -1461,7 +1461,7 @@ bool AssetManager::scanAndMergeZipLocked(SortedVector* pMerg entry = pZip->findEntryByIndex(i); if (pZip->getEntryFileName(entry, nameBuf, sizeof(nameBuf)) != 0) { // TODO: fix this if we expect to have long names - LOGE("ARGH: name too long?\n"); + ALOGE("ARGH: name too long?\n"); continue; } //printf("Comparing %s in %s?\n", nameBuf, dirName.string()); diff --git a/libs/utils/BackupHelpers.cpp b/libs/utils/BackupHelpers.cpp index 04b2e7132..f77a8917c 100644 --- a/libs/utils/BackupHelpers.cpp +++ b/libs/utils/BackupHelpers.cpp @@ -232,7 +232,7 @@ write_update_file(BackupDataWriter* dataStream, int fd, int mode, const String8& lseek(fd, 0, SEEK_SET); if (sizeof(metadata) != 16) { - LOGE("ERROR: metadata block is the wrong size!"); + ALOGE("ERROR: metadata block is the wrong size!"); } bytesLeft = fileSize + sizeof(metadata); @@ -280,7 +280,7 @@ write_update_file(BackupDataWriter* dataStream, int fd, int mode, const String8& } } } - LOGE("write_update_file size mismatch for %s. expected=%d actual=%d." + ALOGE("write_update_file size mismatch for %s. expected=%d actual=%d." " You aren't doing proper locking!", realFilename, fileSize, fileSize-bytesLeft); } @@ -525,7 +525,7 @@ int write_tarfile(const String8& packageName, const String8& domain, struct stat64 s; if (lstat64(filepath.string(), &s) != 0) { err = errno; - LOGE("Error %d (%s) from lstat64(%s)", err, strerror(err), filepath.string()); + ALOGE("Error %d (%s) from lstat64(%s)", err, strerror(err), filepath.string()); return err; } @@ -540,7 +540,7 @@ int write_tarfile(const String8& packageName, const String8& domain, int fd = open(filepath.string(), O_RDONLY); if (fd < 0) { err = errno; - LOGE("Error %d (%s) from open(%s)", err, strerror(err), filepath.string()); + ALOGE("Error %d (%s) from open(%s)", err, strerror(err), filepath.string()); return err; } @@ -551,7 +551,7 @@ int write_tarfile(const String8& packageName, const String8& domain, char* paxData = buf + 1024; if (buf == NULL) { - LOGE("Out of mem allocating transfer buffer"); + ALOGE("Out of mem allocating transfer buffer"); err = ENOMEM; goto cleanup; } @@ -688,11 +688,11 @@ int write_tarfile(const String8& packageName, const String8& domain, ssize_t nRead = read(fd, buf, toRead); if (nRead < 0) { err = errno; - LOGE("Unable to read file [%s], err=%d (%s)", filepath.string(), + ALOGE("Unable to read file [%s], err=%d (%s)", filepath.string(), err, strerror(err)); break; } else if (nRead == 0) { - LOGE("EOF but expect %lld more bytes in [%s]", (long long) toWrite, + ALOGE("EOF but expect %lld more bytes in [%s]", (long long) toWrite, filepath.string()); err = EIO; break; diff --git a/libs/utils/BlobCache.cpp b/libs/utils/BlobCache.cpp index 0011d29d2..e52cf2f84 100644 --- a/libs/utils/BlobCache.cpp +++ b/libs/utils/BlobCache.cpp @@ -183,13 +183,13 @@ size_t BlobCache::getFdCount() const { status_t BlobCache::flatten(void* buffer, size_t size, int fds[], size_t count) const { if (count != 0) { - LOGE("flatten: nonzero fd count: %d", count); + ALOGE("flatten: nonzero fd count: %d", count); return BAD_VALUE; } // Write the cache header if (size < sizeof(Header)) { - LOGE("flatten: not enough room for cache header"); + ALOGE("flatten: not enough room for cache header"); return BAD_VALUE; } Header* header = reinterpret_cast(buffer); @@ -210,7 +210,7 @@ status_t BlobCache::flatten(void* buffer, size_t size, int fds[], size_t count) size_t entrySize = sizeof(EntryHeader) + keySize + valueSize; if (byteOffset + entrySize > size) { - LOGE("flatten: not enough room for cache entries"); + ALOGE("flatten: not enough room for cache entries"); return BAD_VALUE; } @@ -234,18 +234,18 @@ status_t BlobCache::unflatten(void const* buffer, size_t size, int fds[], mCacheEntries.clear(); if (count != 0) { - LOGE("unflatten: nonzero fd count: %d", count); + ALOGE("unflatten: nonzero fd count: %d", count); return BAD_VALUE; } // Read the cache header if (size < sizeof(Header)) { - LOGE("unflatten: not enough room for cache header"); + ALOGE("unflatten: not enough room for cache header"); return BAD_VALUE; } const Header* header = reinterpret_cast(buffer); if (header->mMagicNumber != blobCacheMagic) { - LOGE("unflatten: bad magic number: %d", header->mMagicNumber); + ALOGE("unflatten: bad magic number: %d", header->mMagicNumber); return BAD_VALUE; } if (header->mBlobCacheVersion != blobCacheVersion || @@ -261,7 +261,7 @@ status_t BlobCache::unflatten(void const* buffer, size_t size, int fds[], for (size_t i = 0; i < numEntries; i++) { if (byteOffset + sizeof(EntryHeader) > size) { mCacheEntries.clear(); - LOGE("unflatten: not enough room for cache entry headers"); + ALOGE("unflatten: not enough room for cache entry headers"); return BAD_VALUE; } @@ -273,7 +273,7 @@ status_t BlobCache::unflatten(void const* buffer, size_t size, int fds[], if (byteOffset + entrySize > size) { mCacheEntries.clear(); - LOGE("unflatten: not enough room for cache entry headers"); + ALOGE("unflatten: not enough room for cache entry headers"); return BAD_VALUE; } diff --git a/libs/utils/FileMap.cpp b/libs/utils/FileMap.cpp index c9a423e7d..9ce370e0e 100644 --- a/libs/utils/FileMap.cpp +++ b/libs/utils/FileMap.cpp @@ -108,7 +108,7 @@ bool FileMap::create(const char* origFileName, int fd, off64_t offset, size_t le mFileHandle = (HANDLE) _get_osfhandle(fd); mFileMapping = CreateFileMapping( mFileHandle, NULL, protect, 0, 0, NULL); if (mFileMapping == NULL) { - LOGE("CreateFileMapping(%p, %lx) failed with error %ld\n", + ALOGE("CreateFileMapping(%p, %lx) failed with error %ld\n", mFileHandle, protect, GetLastError() ); return false; } @@ -123,7 +123,7 @@ bool FileMap::create(const char* origFileName, int fd, off64_t offset, size_t le (DWORD)(adjOffset), adjLength ); if (mBasePtr == NULL) { - LOGE("MapViewOfFile(%ld, %ld) failed with error %ld\n", + ALOGE("MapViewOfFile(%ld, %ld) failed with error %ld\n", adjOffset, adjLength, GetLastError() ); CloseHandle(mFileMapping); mFileMapping = INVALID_HANDLE_VALUE; @@ -147,7 +147,7 @@ bool FileMap::create(const char* origFileName, int fd, off64_t offset, size_t le #if NOT_USING_KLIBC mPageSize = sysconf(_SC_PAGESIZE); if (mPageSize == -1) { - LOGE("could not get _SC_PAGESIZE\n"); + ALOGE("could not get _SC_PAGESIZE\n"); return false; } #else @@ -175,7 +175,7 @@ try_again: goto try_again; } - LOGE("mmap(%ld,%ld) failed: %s\n", + ALOGE("mmap(%ld,%ld) failed: %s\n", (long) adjOffset, (long) adjLength, strerror(errno)); return false; } diff --git a/libs/utils/Looper.cpp b/libs/utils/Looper.cpp index 28ed0e850..d1aa664bc 100644 --- a/libs/utils/Looper.cpp +++ b/libs/utils/Looper.cpp @@ -520,12 +520,12 @@ int Looper::addFd(int fd, int ident, int events, ALooper_callbackFunc callback, if (! callback) { if (! mAllowNonCallbacks) { - LOGE("Invalid attempt to set NULL callback but not allowed for this looper."); + ALOGE("Invalid attempt to set NULL callback but not allowed for this looper."); return -1; } if (ident < 0) { - LOGE("Invalid attempt to set NULL callback with ident <= 0."); + ALOGE("Invalid attempt to set NULL callback with ident <= 0."); return -1; } } @@ -553,14 +553,14 @@ int Looper::addFd(int fd, int ident, int events, ALooper_callbackFunc callback, if (requestIndex < 0) { int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, & eventItem); if (epollResult < 0) { - LOGE("Error adding epoll events for fd %d, errno=%d", fd, errno); + ALOGE("Error adding epoll events for fd %d, errno=%d", fd, errno); return -1; } mRequests.add(fd, request); } else { int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_MOD, fd, & eventItem); if (epollResult < 0) { - LOGE("Error modifying epoll events for fd %d, errno=%d", fd, errno); + ALOGE("Error modifying epoll events for fd %d, errno=%d", fd, errno); return -1; } mRequests.replaceValueAt(requestIndex, request); @@ -611,7 +611,7 @@ int Looper::removeFd(int fd) { int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_DEL, fd, NULL); if (epollResult < 0) { - LOGE("Error removing epoll events for fd %d, errno=%d", fd, errno); + ALOGE("Error removing epoll events for fd %d, errno=%d", fd, errno); return -1; } diff --git a/libs/utils/PropertyMap.cpp b/libs/utils/PropertyMap.cpp index d801609a9..55207027b 100644 --- a/libs/utils/PropertyMap.cpp +++ b/libs/utils/PropertyMap.cpp @@ -121,11 +121,11 @@ status_t PropertyMap::load(const String8& filename, PropertyMap** outMap) { Tokenizer* tokenizer; status_t status = Tokenizer::open(filename, &tokenizer); if (status) { - LOGE("Error %d opening property file %s.", status, filename.string()); + ALOGE("Error %d opening property file %s.", status, filename.string()); } else { PropertyMap* map = new PropertyMap(); if (!map) { - LOGE("Error allocating property map."); + ALOGE("Error allocating property map."); status = NO_MEMORY; } else { #if DEBUG_PARSER_PERFORMANCE @@ -172,14 +172,14 @@ status_t PropertyMap::Parser::parse() { if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') { String8 keyToken = mTokenizer->nextToken(WHITESPACE_OR_PROPERTY_DELIMITER); if (keyToken.isEmpty()) { - LOGE("%s: Expected non-empty property key.", mTokenizer->getLocation().string()); + ALOGE("%s: Expected non-empty property key.", mTokenizer->getLocation().string()); return BAD_VALUE; } mTokenizer->skipDelimiters(WHITESPACE); if (mTokenizer->nextChar() != '=') { - LOGE("%s: Expected '=' between property key and value.", + ALOGE("%s: Expected '=' between property key and value.", mTokenizer->getLocation().string()); return BAD_VALUE; } @@ -188,21 +188,21 @@ status_t PropertyMap::Parser::parse() { String8 valueToken = mTokenizer->nextToken(WHITESPACE); if (valueToken.find("\\", 0) >= 0 || valueToken.find("\"", 0) >= 0) { - LOGE("%s: Found reserved character '\\' or '\"' in property value.", + ALOGE("%s: Found reserved character '\\' or '\"' in property value.", mTokenizer->getLocation().string()); return BAD_VALUE; } mTokenizer->skipDelimiters(WHITESPACE); if (!mTokenizer->isEol()) { - LOGE("%s: Expected end of line, got '%s'.", + ALOGE("%s: Expected end of line, got '%s'.", mTokenizer->getLocation().string(), mTokenizer->peekRemainderOfLine().string()); return BAD_VALUE; } if (mMap->hasProperty(keyToken)) { - LOGE("%s: Duplicate property value for key '%s'.", + ALOGE("%s: Duplicate property value for key '%s'.", mTokenizer->getLocation().string(), keyToken.string()); return BAD_VALUE; } diff --git a/libs/utils/RefBase.cpp b/libs/utils/RefBase.cpp index 0b7dd92e3..ad0939e57 100644 --- a/libs/utils/RefBase.cpp +++ b/libs/utils/RefBase.cpp @@ -98,7 +98,7 @@ public: #if DEBUG_REFS_FATAL_SANITY_CHECKS LOG_ALWAYS_FATAL("Strong references remain!"); #else - LOGE("Strong references remain:"); + ALOGE("Strong references remain:"); #endif ref_entry* refs = mStrongRefs; while (refs) { @@ -116,7 +116,7 @@ public: #if DEBUG_REFS_FATAL_SANITY_CHECKS LOG_ALWAYS_FATAL("Weak references remain:"); #else - LOGE("Weak references remain!"); + ALOGE("Weak references remain!"); #endif ref_entry* refs = mWeakRefs; while (refs) { @@ -129,7 +129,7 @@ public: } } if (dumpStack) { - LOGE("above errors at:"); + ALOGE("above errors at:"); CallStack stack; stack.update(); stack.dump(); @@ -205,7 +205,7 @@ public: close(rc); ALOGD("STACK TRACE for %p saved in %s", this, name); } - else LOGE("FAILED TO PRINT STACK TRACE for %p in %s: %s", this, + else ALOGE("FAILED TO PRINT STACK TRACE for %p in %s: %s", this, name, strerror(errno)); } } @@ -263,7 +263,7 @@ private: id, mBase, this); #endif - LOGE("RefBase: removing id %p on RefBase %p" + ALOGE("RefBase: removing id %p on RefBase %p" "(weakref_type %p) that doesn't exist!", id, mBase, this); diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp index 9a8816fc6..15b83bbd2 100644 --- a/libs/utils/ResourceTypes.cpp +++ b/libs/utils/ResourceTypes.cpp @@ -296,7 +296,7 @@ static status_t getIdmapPackageId(const uint32_t* map, size_t mapSize, uint32_t Res_png_9patch* Res_png_9patch::deserialize(const void* inData) { if (sizeof(void*) != sizeof(int32_t)) { - LOGE("Cannot deserialize on non 32-bit system\n"); + ALOGE("Cannot deserialize on non 32-bit system\n"); return NULL; } deserializeInternal(inData, (Res_png_9patch*) inData); @@ -1574,7 +1574,7 @@ status_t ResTable::Theme::applyStyle(uint32_t resID, bool force) if (curPackage != p) { const ssize_t pidx = mTable.getResourcePackageIndex(attrRes); if (pidx < 0) { - LOGE("Style contains key with bad package: 0x%08x\n", attrRes); + ALOGE("Style contains key with bad package: 0x%08x\n", attrRes); bag++; continue; } @@ -1594,7 +1594,7 @@ status_t ResTable::Theme::applyStyle(uint32_t resID, bool force) } if (curType != t) { if (t >= curPI->numTypes) { - LOGE("Style contains key with bad type: 0x%08x\n", attrRes); + ALOGE("Style contains key with bad type: 0x%08x\n", attrRes); bag++; continue; } @@ -1612,7 +1612,7 @@ status_t ResTable::Theme::applyStyle(uint32_t resID, bool force) numEntries = curPI->types[t].numEntries; } if (e >= numEntries) { - LOGE("Style contains key with bad entry: 0x%08x\n", attrRes); + ALOGE("Style contains key with bad entry: 0x%08x\n", attrRes); bag++; continue; } @@ -2099,7 +2099,7 @@ ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag if (density > 0) { overrideConfig = (ResTable_config*) malloc(sizeof(ResTable_config)); if (overrideConfig == NULL) { - LOGE("Couldn't malloc ResTable_config for overrides: %s", strerror(errno)); + ALOGE("Couldn't malloc ResTable_config for overrides: %s", strerror(errno)); return BAD_INDEX; } memcpy(overrideConfig, &mParams, sizeof(ResTable_config)); diff --git a/libs/utils/StreamingZipInflater.cpp b/libs/utils/StreamingZipInflater.cpp index 59a46f977..8512170ae 100644 --- a/libs/utils/StreamingZipInflater.cpp +++ b/libs/utils/StreamingZipInflater.cpp @@ -138,7 +138,7 @@ ssize_t StreamingZipInflater::read(void* outBuf, size_t count) { if (mInflateState.avail_in == 0) { int err = readNextChunk(); if (err < 0) { - LOGE("Unable to access asset data: %d", err); + ALOGE("Unable to access asset data: %d", err); if (!mStreamNeedsInit) { ::inflateEnd(&mInflateState); initInflateState(); @@ -165,7 +165,7 @@ ssize_t StreamingZipInflater::read(void* outBuf, size_t count) { if (result == Z_OK) result = ::inflate(&mInflateState, Z_SYNC_FLUSH); if (result < 0) { // Whoops, inflation failed - LOGE("Error inflating asset: %d", result); + ALOGE("Error inflating asset: %d", result); ::inflateEnd(&mInflateState); initInflateState(); return -1; @@ -195,7 +195,7 @@ int StreamingZipInflater::readNextChunk() { //ALOGV("Reading input chunk, size %08x didread %08x", toRead, didRead); if (didRead < 0) { // TODO: error - LOGE("Error reading asset data"); + ALOGE("Error reading asset data"); return didRead; } else { mInNextChunkOffset += didRead; diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp index fb52d7c74..e343c6235 100644 --- a/libs/utils/Threads.cpp +++ b/libs/utils/Threads.cpp @@ -163,7 +163,7 @@ int androidCreateRawThreadEtc(android_thread_func_t entryFunction, (android_pthread_entry)entryFunction, userData); pthread_attr_destroy(&attr); if (result != 0) { - LOGE("androidCreateRawThreadEtc failed (entry=%p, res=%d, errno=%d)\n" + ALOGE("androidCreateRawThreadEtc failed (entry=%p, res=%d, errno=%d)\n" "(android threadPriority=%d)", entryFunction, result, errno, threadPriority); return 0; diff --git a/libs/utils/Tokenizer.cpp b/libs/utils/Tokenizer.cpp index 68752b412..efda2bfff 100644 --- a/libs/utils/Tokenizer.cpp +++ b/libs/utils/Tokenizer.cpp @@ -55,12 +55,12 @@ status_t Tokenizer::open(const String8& filename, Tokenizer** outTokenizer) { int fd = ::open(filename.string(), O_RDONLY); if (fd < 0) { result = -errno; - LOGE("Error opening file '%s', %s.", filename.string(), strerror(errno)); + ALOGE("Error opening file '%s', %s.", filename.string(), strerror(errno)); } else { struct stat stat; if (fstat(fd, &stat)) { result = -errno; - LOGE("Error getting size of file '%s', %s.", filename.string(), strerror(errno)); + ALOGE("Error getting size of file '%s', %s.", filename.string(), strerror(errno)); } else { size_t length = size_t(stat.st_size); @@ -80,7 +80,7 @@ status_t Tokenizer::open(const String8& filename, Tokenizer** outTokenizer) { ssize_t nrd = read(fd, buffer, length); if (nrd < 0) { result = -errno; - LOGE("Error reading file '%s', %s.", filename.string(), strerror(errno)); + ALOGE("Error reading file '%s', %s.", filename.string(), strerror(errno)); delete[] buffer; buffer = NULL; } else { diff --git a/libs/utils/ZipFileRO.cpp b/libs/utils/ZipFileRO.cpp index a6cce7e32..1498aac07 100644 --- a/libs/utils/ZipFileRO.cpp +++ b/libs/utils/ZipFileRO.cpp @@ -527,7 +527,7 @@ bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, size_t* pUncompLen, if (pOffset != NULL) { long localHdrOffset = get4LE(ptr + kCDELocalOffset); if (localHdrOffset + kLFHLen >= cdOffset) { - LOGE("ERROR: bad local hdr offset in zip\n"); + ALOGE("ERROR: bad local hdr offset in zip\n"); return false; } @@ -605,7 +605,7 @@ bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, size_t* pUncompLen, if (method == kCompressStored && (off64_t)(dataOffset + uncompLen) > cdOffset) { - LOGE("ERROR: bad uncompressed length in zip (%ld + " ZD " > %ld)\n", + ALOGE("ERROR: bad uncompressed length in zip (%ld + " ZD " > %ld)\n", (long) dataOffset, (ZD_TYPE) uncompLen, (long) cdOffset); return false; } @@ -754,10 +754,10 @@ bool ZipFileRO::uncompressEntry(ZipEntryRO entry, int fd) const if (method == kCompressStored) { ssize_t actual = write(fd, ptr, uncompLen); if (actual < 0) { - LOGE("Write failed: %s\n", strerror(errno)); + ALOGE("Write failed: %s\n", strerror(errno)); goto unmap; } else if ((size_t) actual != uncompLen) { - LOGE("Partial write during uncompress (" ZD " of " ZD ")\n", + ALOGE("Partial write during uncompress (" ZD " of " ZD ")\n", (ZD_TYPE) actual, (ZD_TYPE) uncompLen); goto unmap; } else { @@ -806,10 +806,10 @@ bail: zerr = inflateInit2(&zstream, -MAX_WBITS); if (zerr != Z_OK) { if (zerr == Z_VERSION_ERROR) { - LOGE("Installed zlib is not compatible with linked version (%s)\n", + ALOGE("Installed zlib is not compatible with linked version (%s)\n", ZLIB_VERSION); } else { - LOGE("Call to inflateInit2 failed (zerr=%d)\n", zerr); + ALOGE("Call to inflateInit2 failed (zerr=%d)\n", zerr); } goto bail; } @@ -873,10 +873,10 @@ bail: zerr = inflateInit2(&zstream, -MAX_WBITS); if (zerr != Z_OK) { if (zerr == Z_VERSION_ERROR) { - LOGE("Installed zlib is not compatible with linked version (%s)\n", + ALOGE("Installed zlib is not compatible with linked version (%s)\n", ZLIB_VERSION); } else { - LOGE("Call to inflateInit2 failed (zerr=%d)\n", zerr); + ALOGE("Call to inflateInit2 failed (zerr=%d)\n", zerr); } goto bail; } diff --git a/libs/utils/ZipUtils.cpp b/libs/utils/ZipUtils.cpp index 0fe1a7bfa..2dbdc1d38 100644 --- a/libs/utils/ZipUtils.cpp +++ b/libs/utils/ZipUtils.cpp @@ -77,10 +77,10 @@ using namespace android; zerr = inflateInit2(&zstream, -MAX_WBITS); if (zerr != Z_OK) { if (zerr == Z_VERSION_ERROR) { - LOGE("Installed zlib is not compatible with linked version (%s)\n", + ALOGE("Installed zlib is not compatible with linked version (%s)\n", ZLIB_VERSION); } else { - LOGE("Call to inflateInit2 failed (zerr=%d)\n", zerr); + ALOGE("Call to inflateInit2 failed (zerr=%d)\n", zerr); } goto bail; } @@ -189,10 +189,10 @@ bail: zerr = inflateInit2(&zstream, -MAX_WBITS); if (zerr != Z_OK) { if (zerr == Z_VERSION_ERROR) { - LOGE("Installed zlib is not compatible with linked version (%s)\n", + ALOGE("Installed zlib is not compatible with linked version (%s)\n", ZLIB_VERSION); } else { - LOGE("Call to inflateInit2 failed (zerr=%d)\n", zerr); + ALOGE("Call to inflateInit2 failed (zerr=%d)\n", zerr); } goto bail; } From ae07445e9793724324b93bb593fe20be2a386707 Mon Sep 17 00:00:00 2001 From: Steve Block Date: Mon, 9 Jan 2012 18:35:44 +0000 Subject: [PATCH 385/541] Rename LOG_ASSERT to ALOG_ASSERT DO NOT MERGE See https://android-git.corp.google.com/g/157519 Bug: 5449033 Change-Id: I8ceb2dba1b031a0fd68d15d146960d9ced62bbf3 --- libs/utils/RefBase.cpp | 14 +++++++------- libs/utils/String16.cpp | 4 ++-- libs/utils/String8.cpp | 6 +++--- libs/utils/VectorImpl.cpp | 16 ++++++++-------- 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/libs/utils/RefBase.cpp b/libs/utils/RefBase.cpp index ad0939e57..e80a795b7 100644 --- a/libs/utils/RefBase.cpp +++ b/libs/utils/RefBase.cpp @@ -332,7 +332,7 @@ void RefBase::incStrong(const void* id) const refs->addStrongRef(id); const int32_t c = android_atomic_inc(&refs->mStrong); - LOG_ASSERT(c > 0, "incStrong() called on %p after last strong ref", refs); + ALOG_ASSERT(c > 0, "incStrong() called on %p after last strong ref", refs); #if PRINT_REFS ALOGD("incStrong of %p from %p: cnt=%d\n", this, id, c); #endif @@ -352,7 +352,7 @@ void RefBase::decStrong(const void* id) const #if PRINT_REFS ALOGD("decStrong of %p from %p: cnt=%d\n", this, id, c); #endif - LOG_ASSERT(c >= 1, "decStrong() called on %p too many times", refs); + ALOG_ASSERT(c >= 1, "decStrong() called on %p too many times", refs); if (c == 1) { refs->mBase->onLastStrongRef(id); if ((refs->mFlags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_STRONG) { @@ -369,7 +369,7 @@ void RefBase::forceIncStrong(const void* id) const refs->addStrongRef(id); const int32_t c = android_atomic_inc(&refs->mStrong); - LOG_ASSERT(c >= 0, "forceIncStrong called on %p after ref count underflow", + ALOG_ASSERT(c >= 0, "forceIncStrong called on %p after ref count underflow", refs); #if PRINT_REFS ALOGD("forceIncStrong of %p from %p: cnt=%d\n", this, id, c); @@ -399,7 +399,7 @@ void RefBase::weakref_type::incWeak(const void* id) weakref_impl* const impl = static_cast(this); impl->addWeakRef(id); const int32_t c = android_atomic_inc(&impl->mWeak); - LOG_ASSERT(c >= 0, "incWeak called on %p after last weak ref", this); + ALOG_ASSERT(c >= 0, "incWeak called on %p after last weak ref", this); } @@ -408,7 +408,7 @@ void RefBase::weakref_type::decWeak(const void* id) weakref_impl* const impl = static_cast(this); impl->removeWeakRef(id); const int32_t c = android_atomic_dec(&impl->mWeak); - LOG_ASSERT(c >= 1, "decWeak called on %p too many times", this); + ALOG_ASSERT(c >= 1, "decWeak called on %p too many times", this); if (c != 1) return; if ((impl->mFlags&OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_STRONG) { @@ -442,7 +442,7 @@ bool RefBase::weakref_type::attemptIncStrong(const void* id) weakref_impl* const impl = static_cast(this); int32_t curCount = impl->mStrong; - LOG_ASSERT(curCount >= 0, "attemptIncStrong called on %p after underflow", + ALOG_ASSERT(curCount >= 0, "attemptIncStrong called on %p after underflow", this); while (curCount > 0 && curCount != INITIAL_STRONG_VALUE) { if (android_atomic_cmpxchg(curCount, curCount+1, &impl->mStrong) == 0) { @@ -503,7 +503,7 @@ bool RefBase::weakref_type::attemptIncWeak(const void* id) weakref_impl* const impl = static_cast(this); int32_t curCount = impl->mWeak; - LOG_ASSERT(curCount >= 0, "attemptIncWeak called on %p after underflow", + ALOG_ASSERT(curCount >= 0, "attemptIncWeak called on %p after underflow", this); while (curCount > 0) { if (android_atomic_cmpxchg(curCount, curCount+1, &impl->mWeak) == 0) { diff --git a/libs/utils/String16.cpp b/libs/utils/String16.cpp index 4ce166426..94e072fce 100644 --- a/libs/utils/String16.cpp +++ b/libs/utils/String16.cpp @@ -112,7 +112,7 @@ String16::String16(const char16_t* o) { size_t len = strlen16(o); SharedBuffer* buf = SharedBuffer::alloc((len+1)*sizeof(char16_t)); - LOG_ASSERT(buf, "Unable to allocate shared buffer"); + ALOG_ASSERT(buf, "Unable to allocate shared buffer"); if (buf) { char16_t* str = (char16_t*)buf->data(); strcpy16(str, o); @@ -126,7 +126,7 @@ String16::String16(const char16_t* o) String16::String16(const char16_t* o, size_t len) { SharedBuffer* buf = SharedBuffer::alloc((len+1)*sizeof(char16_t)); - LOG_ASSERT(buf, "Unable to allocate shared buffer"); + ALOG_ASSERT(buf, "Unable to allocate shared buffer"); if (buf) { char16_t* str = (char16_t*)buf->data(); memcpy(str, o, len*sizeof(char16_t)); diff --git a/libs/utils/String8.cpp b/libs/utils/String8.cpp index 0bc5aff22..562f026f6 100644 --- a/libs/utils/String8.cpp +++ b/libs/utils/String8.cpp @@ -80,7 +80,7 @@ static char* allocFromUTF8(const char* in, size_t len) { if (len > 0) { SharedBuffer* buf = SharedBuffer::alloc(len+1); - LOG_ASSERT(buf, "Unable to allocate shared buffer"); + ALOG_ASSERT(buf, "Unable to allocate shared buffer"); if (buf) { char* str = (char*)buf->data(); memcpy(str, in, len); @@ -103,7 +103,7 @@ static char* allocFromUTF16(const char16_t* in, size_t len) } SharedBuffer* buf = SharedBuffer::alloc(bytes+1); - LOG_ASSERT(buf, "Unable to allocate shared buffer"); + ALOG_ASSERT(buf, "Unable to allocate shared buffer"); if (!buf) { return getEmptyString(); } @@ -125,7 +125,7 @@ static char* allocFromUTF32(const char32_t* in, size_t len) } SharedBuffer* buf = SharedBuffer::alloc(bytes+1); - LOG_ASSERT(buf, "Unable to allocate shared buffer"); + ALOG_ASSERT(buf, "Unable to allocate shared buffer"); if (!buf) { return getEmptyString(); } diff --git a/libs/utils/VectorImpl.cpp b/libs/utils/VectorImpl.cpp index 4a9029671..220ae3ea6 100644 --- a/libs/utils/VectorImpl.cpp +++ b/libs/utils/VectorImpl.cpp @@ -56,7 +56,7 @@ VectorImpl::VectorImpl(const VectorImpl& rhs) VectorImpl::~VectorImpl() { - LOG_ASSERT(!mCount, + ALOG_ASSERT(!mCount, "[%p] " "subclasses of VectorImpl must call finish_vector()" " in their destructor. Leaking %d bytes.", @@ -66,7 +66,7 @@ VectorImpl::~VectorImpl() VectorImpl& VectorImpl::operator = (const VectorImpl& rhs) { - LOG_ASSERT(mItemSize == rhs.mItemSize, + ALOG_ASSERT(mItemSize == rhs.mItemSize, "Vector<> have different types (this=%p, rhs=%p)", this, &rhs); if (this != &rhs) { release_storage(); @@ -248,7 +248,7 @@ ssize_t VectorImpl::replaceAt(size_t index) ssize_t VectorImpl::replaceAt(const void* prototype, size_t index) { - LOG_ASSERT(index Date: Fri, 13 Jan 2012 15:33:54 +0400 Subject: [PATCH 386/541] Fix compilation with Clang. warning: extraneous template parameter list in template specialization template<> Change-Id: If3e3e1d06a018343b40ac0984dfd9ecc12629f8d --- include/utils/TypeHelpers.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/include/utils/TypeHelpers.h b/include/utils/TypeHelpers.h index 7b4fb70ba..1f2c2d5fd 100644 --- a/include/utils/TypeHelpers.h +++ b/include/utils/TypeHelpers.h @@ -233,19 +233,15 @@ struct key_value_pair_t { } }; -template<> template struct trait_trivial_ctor< key_value_pair_t > { enum { value = aggregate_traits::has_trivial_ctor }; }; -template<> template struct trait_trivial_dtor< key_value_pair_t > { enum { value = aggregate_traits::has_trivial_dtor }; }; -template<> template struct trait_trivial_copy< key_value_pair_t > { enum { value = aggregate_traits::has_trivial_copy }; }; -template<> template struct trait_trivial_move< key_value_pair_t > { enum { value = aggregate_traits::has_trivial_move }; }; From d731f07cc9ca7e8fc327c7910de7bea7e22c2fd3 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Mon, 11 Jul 2011 15:59:22 -0700 Subject: [PATCH 387/541] Thread::getTid returns pid_t gettid() after run This is needed when the parent or any other thread besides the child needs access to the child's kernel tid. Change-Id: Ib148505913eb78314cfd76657c30d7b20663dffd --- include/utils/threads.h | 12 ++++++++++-- libs/utils/Android.mk | 8 ++++++-- libs/utils/Threads.cpp | 38 +++++++++++++++++++++++++++++++++++--- 3 files changed, 51 insertions(+), 7 deletions(-) diff --git a/include/utils/threads.h b/include/utils/threads.h index ab3e8cdb6..b4a8b7c0e 100644 --- a/include/utils/threads.h +++ b/include/utils/threads.h @@ -526,6 +526,12 @@ public: // Do not call from this object's thread; will return WOULD_BLOCK in that case. status_t join(); +#ifdef HAVE_ANDROID_OS + // Return the thread's kernel ID, same as the thread itself calling gettid() or + // androidGetTid(), or -1 if the thread is not running. + pid_t getTid() const; +#endif + protected: // exitPending() returns true if requestExit() has been called. bool exitPending() const; @@ -551,8 +557,10 @@ private: volatile bool mExitPending; volatile bool mRunning; sp mHoldSelf; -#if HAVE_ANDROID_OS - int mTid; +#ifdef HAVE_ANDROID_OS + // legacy for debugging, not used by getTid() as it is set by the child thread + // and so is not initialized until the child reaches that point + pid_t mTid; #endif }; diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk index 544ab744e..24cf5048f 100644 --- a/libs/utils/Android.mk +++ b/libs/utils/Android.mk @@ -98,7 +98,8 @@ endif LOCAL_C_INCLUDES += \ external/zlib \ - external/icu4c/common + external/icu4c/common \ + bionic/libc/private LOCAL_LDLIBS += -lpthread @@ -114,7 +115,10 @@ include $(BUILD_SHARED_LIBRARY) ifeq ($(TARGET_OS),linux) include $(CLEAR_VARS) -LOCAL_C_INCLUDES += external/zlib external/icu4c/common +LOCAL_C_INCLUDES += \ + external/zlib \ + external/icu4c/common \ + bionic/libc/private LOCAL_LDLIBS := -lrt -ldl -lpthread LOCAL_MODULE := libutils LOCAL_SRC_FILES := $(commonSources) BackupData.cpp BackupHelpers.cpp diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp index e343c6235..ab207f565 100644 --- a/libs/utils/Threads.cpp +++ b/libs/utils/Threads.cpp @@ -34,6 +34,9 @@ # include # include # include +#ifdef HAVE_ANDROID_OS +# include +#endif #elif defined(HAVE_WIN32_THREADS) # include # include @@ -86,7 +89,7 @@ struct thread_data_t { char * threadName; // we use this trampoline when we need to set the priority with - // nice/setpriority. + // nice/setpriority, and name with prctl. static int trampoline(const thread_data_t* t) { thread_func_t f = t->entryFunction; void* u = t->userData; @@ -141,8 +144,13 @@ int androidCreateRawThreadEtc(android_thread_func_t entryFunction, #ifdef HAVE_ANDROID_OS /* valgrind is rejecting RT-priority create reqs */ if (threadPriority != PRIORITY_DEFAULT || threadName != NULL) { - // We could avoid the trampoline if there was a way to get to the - // android_thread_id_t (pid) from pthread_t + // Now that the pthread_t has a method to find the associated + // android_thread_id_t (pid) from pthread_t, it would be possible to avoid + // this trampoline in some cases as the parent could set the properties + // for the child. However, there would be a race condition because the + // child becomes ready immediately, and it doesn't work for the name. + // prctl(PR_SET_NAME) only works for self; prctl(PR_SET_THREAD_NAME) was + // proposed but not yet accepted. thread_data_t* t = new thread_data_t; t->priority = threadPriority; t->threadName = threadName ? strdup(threadName) : NULL; @@ -178,6 +186,13 @@ int androidCreateRawThreadEtc(android_thread_func_t entryFunction, return 1; } +#ifdef HAVE_ANDROID_OS +static pthread_t android_thread_id_t_to_pthread(android_thread_id_t thread) +{ + return (pthread_t) thread; +} +#endif + android_thread_id_t androidGetThreadId() { return (android_thread_id_t)pthread_self(); @@ -909,6 +924,23 @@ status_t Thread::join() return mStatus; } +#ifdef HAVE_ANDROID_OS +pid_t Thread::getTid() const +{ + // mTid is not defined until the child initializes it, and the caller may need it earlier + Mutex::Autolock _l(mLock); + pid_t tid; + if (mRunning) { + pthread_t pthread = android_thread_id_t_to_pthread(mThread); + tid = __pthread_gettid(pthread); + } else { + ALOGW("Thread (this=%p): getTid() is undefined before run()", this); + tid = -1; + } + return tid; +} +#endif + bool Thread::exitPending() const { Mutex::Autolock _l(mLock); From e956c5c1973e574e5ac2f90a905d773fb398b1e2 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Wed, 25 Jan 2012 15:12:23 -0800 Subject: [PATCH 388/541] Add xxhdpi; fix ActivityManager.getLauncherLargeIconSize() etc. Change-Id: I519d6cdc527a402d93b98df17a64fc1da52ad598 --- include/utils/ResourceTypes.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h index 46420c131..b741ed614 100644 --- a/include/utils/ResourceTypes.h +++ b/include/utils/ResourceTypes.h @@ -843,6 +843,8 @@ struct ResTable_config DENSITY_MEDIUM = ACONFIGURATION_DENSITY_MEDIUM, DENSITY_TV = ACONFIGURATION_DENSITY_TV, DENSITY_HIGH = ACONFIGURATION_DENSITY_HIGH, + DENSITY_XHIGH = ACONFIGURATION_DENSITY_XHIGH, + DENSITY_XXHIGH = ACONFIGURATION_DENSITY_XXHIGH, DENSITY_NONE = ACONFIGURATION_DENSITY_NONE }; From 78eb46c8e8776278b16ea0cef460884685b852b4 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Tue, 31 Jan 2012 11:27:43 -0800 Subject: [PATCH 389/541] aapt now sorts the strings in the resource string pool. In our current environment with very many translations, this can save a lot of RAM -- for example over 200K in Gmail just by sorting the strings in the Gmail .apk (not the framework). Also add a new aapt command to print the contents of the resource table string pool. Change-Id: I1da037b3e2c377b890833ff57ab158965314ac48 --- include/utils/ResourceTypes.h | 603 +---------------- libs/utils/ResourceTypes.cpp | 1145 ++++++++++++++++++++++++++------- 2 files changed, 935 insertions(+), 813 deletions(-) diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h index b741ed614..c496da6ba 100644 --- a/include/utils/ResourceTypes.h +++ b/include/utils/ResourceTypes.h @@ -444,23 +444,31 @@ public: void uninit(); + // Return string entry as UTF16; if the pool is UTF8, the string will + // be converted before returning. inline const char16_t* stringAt(const ResStringPool_ref& ref, size_t* outLen) const { return stringAt(ref.index, outLen); } const char16_t* stringAt(size_t idx, size_t* outLen) const; + // Note: returns null if the string pool is not UTF8. const char* string8At(size_t idx, size_t* outLen) const; + // Return string whether the pool is UTF8 or UTF16. Does not allow you + // to distinguish null. + const String8 string8ObjectAt(size_t idx) const; + const ResStringPool_span* styleAt(const ResStringPool_ref& ref) const; const ResStringPool_span* styleAt(size_t idx) const; ssize_t indexOfString(const char16_t* str, size_t strLen) const; size_t size() const; + size_t styleCount() const; + size_t bytes() const; -#ifndef HAVE_ANDROID_OS + bool isSorted() const; bool isUTF8() const; -#endif private: status_t mError; @@ -746,7 +754,9 @@ private: /** * Header for a resource table. Its data contains a series of * additional chunks: - * * A ResStringPool_header containing all table values. + * * A ResStringPool_header containing all table values. This string pool + * contains all of the string values in the entire resource table (not + * the names of entries or type identifiers however). * * One or more ResTable_package chunks. * * Specific entries within a resource table can be uniquely identified @@ -984,68 +994,15 @@ struct ResTable_config uint32_t screenSizeDp; }; - inline void copyFromDeviceNoSwap(const ResTable_config& o) { - const size_t size = dtohl(o.size); - if (size >= sizeof(ResTable_config)) { - *this = o; - } else { - memcpy(this, &o, size); - memset(((uint8_t*)this)+size, 0, sizeof(ResTable_config)-size); - } - } + void copyFromDeviceNoSwap(const ResTable_config& o); - inline void copyFromDtoH(const ResTable_config& o) { - copyFromDeviceNoSwap(o); - size = sizeof(ResTable_config); - mcc = dtohs(mcc); - mnc = dtohs(mnc); - density = dtohs(density); - screenWidth = dtohs(screenWidth); - screenHeight = dtohs(screenHeight); - sdkVersion = dtohs(sdkVersion); - minorVersion = dtohs(minorVersion); - smallestScreenWidthDp = dtohs(smallestScreenWidthDp); - screenWidthDp = dtohs(screenWidthDp); - screenHeightDp = dtohs(screenHeightDp); - } - - inline void swapHtoD() { - size = htodl(size); - mcc = htods(mcc); - mnc = htods(mnc); - density = htods(density); - screenWidth = htods(screenWidth); - screenHeight = htods(screenHeight); - sdkVersion = htods(sdkVersion); - minorVersion = htods(minorVersion); - smallestScreenWidthDp = htods(smallestScreenWidthDp); - screenWidthDp = htods(screenWidthDp); - screenHeightDp = htods(screenHeightDp); - } - - inline int compare(const ResTable_config& o) const { - int32_t diff = (int32_t)(imsi - o.imsi); - if (diff != 0) return diff; - diff = (int32_t)(locale - o.locale); - if (diff != 0) return diff; - diff = (int32_t)(screenType - o.screenType); - if (diff != 0) return diff; - diff = (int32_t)(input - o.input); - if (diff != 0) return diff; - diff = (int32_t)(screenSize - o.screenSize); - if (diff != 0) return diff; - diff = (int32_t)(version - o.version); - if (diff != 0) return diff; - diff = (int32_t)(screenLayout - o.screenLayout); - if (diff != 0) return diff; - diff = (int32_t)(uiMode - o.uiMode); - if (diff != 0) return diff; - diff = (int32_t)(smallestScreenWidthDp - o.smallestScreenWidthDp); - if (diff != 0) return diff; - diff = (int32_t)(screenSizeDp - o.screenSizeDp); - return (int)diff; - } + void copyFromDtoH(const ResTable_config& o); + void swapHtoD(); + + int compare(const ResTable_config& o) const; + int compareLogical(const ResTable_config& o) const; + // Flags indicating a set of config values. These flag constants must // match the corresponding ones in android.content.pm.ActivityInfo and // attrs_manifest.xml. @@ -1068,158 +1025,10 @@ struct ResTable_config // Compare two configuration, returning CONFIG_* flags set for each value // that is different. - inline int diff(const ResTable_config& o) const { - int diffs = 0; - if (mcc != o.mcc) diffs |= CONFIG_MCC; - if (mnc != o.mnc) diffs |= CONFIG_MNC; - if (locale != o.locale) diffs |= CONFIG_LOCALE; - if (orientation != o.orientation) diffs |= CONFIG_ORIENTATION; - if (density != o.density) diffs |= CONFIG_DENSITY; - if (touchscreen != o.touchscreen) diffs |= CONFIG_TOUCHSCREEN; - if (((inputFlags^o.inputFlags)&(MASK_KEYSHIDDEN|MASK_NAVHIDDEN)) != 0) - diffs |= CONFIG_KEYBOARD_HIDDEN; - if (keyboard != o.keyboard) diffs |= CONFIG_KEYBOARD; - if (navigation != o.navigation) diffs |= CONFIG_NAVIGATION; - if (screenSize != o.screenSize) diffs |= CONFIG_SCREEN_SIZE; - if (version != o.version) diffs |= CONFIG_VERSION; - if (screenLayout != o.screenLayout) diffs |= CONFIG_SCREEN_LAYOUT; - if (uiMode != o.uiMode) diffs |= CONFIG_UI_MODE; - if (smallestScreenWidthDp != o.smallestScreenWidthDp) diffs |= CONFIG_SMALLEST_SCREEN_SIZE; - if (screenSizeDp != o.screenSizeDp) diffs |= CONFIG_SCREEN_SIZE; - return diffs; - } + int diff(const ResTable_config& o) const; // Return true if 'this' is more specific than 'o'. - inline bool - isMoreSpecificThan(const ResTable_config& o) const { - // The order of the following tests defines the importance of one - // configuration parameter over another. Those tests first are more - // important, trumping any values in those following them. - if (imsi || o.imsi) { - if (mcc != o.mcc) { - if (!mcc) return false; - if (!o.mcc) return true; - } - - if (mnc != o.mnc) { - if (!mnc) return false; - if (!o.mnc) return true; - } - } - - if (locale || o.locale) { - if (language[0] != o.language[0]) { - if (!language[0]) return false; - if (!o.language[0]) return true; - } - - if (country[0] != o.country[0]) { - if (!country[0]) return false; - if (!o.country[0]) return true; - } - } - - if (smallestScreenWidthDp || o.smallestScreenWidthDp) { - if (smallestScreenWidthDp != o.smallestScreenWidthDp) { - if (!smallestScreenWidthDp) return false; - if (!o.smallestScreenWidthDp) return true; - } - } - - if (screenSizeDp || o.screenSizeDp) { - if (screenWidthDp != o.screenWidthDp) { - if (!screenWidthDp) return false; - if (!o.screenWidthDp) return true; - } - - if (screenHeightDp != o.screenHeightDp) { - if (!screenHeightDp) return false; - if (!o.screenHeightDp) return true; - } - } - - if (screenLayout || o.screenLayout) { - if (((screenLayout^o.screenLayout) & MASK_SCREENSIZE) != 0) { - if (!(screenLayout & MASK_SCREENSIZE)) return false; - if (!(o.screenLayout & MASK_SCREENSIZE)) return true; - } - if (((screenLayout^o.screenLayout) & MASK_SCREENLONG) != 0) { - if (!(screenLayout & MASK_SCREENLONG)) return false; - if (!(o.screenLayout & MASK_SCREENLONG)) return true; - } - } - - if (orientation != o.orientation) { - if (!orientation) return false; - if (!o.orientation) return true; - } - - if (uiMode || o.uiMode) { - if (((uiMode^o.uiMode) & MASK_UI_MODE_TYPE) != 0) { - if (!(uiMode & MASK_UI_MODE_TYPE)) return false; - if (!(o.uiMode & MASK_UI_MODE_TYPE)) return true; - } - if (((uiMode^o.uiMode) & MASK_UI_MODE_NIGHT) != 0) { - if (!(uiMode & MASK_UI_MODE_NIGHT)) return false; - if (!(o.uiMode & MASK_UI_MODE_NIGHT)) return true; - } - } - - // density is never 'more specific' - // as the default just equals 160 - - if (touchscreen != o.touchscreen) { - if (!touchscreen) return false; - if (!o.touchscreen) return true; - } - - if (input || o.input) { - if (((inputFlags^o.inputFlags) & MASK_KEYSHIDDEN) != 0) { - if (!(inputFlags & MASK_KEYSHIDDEN)) return false; - if (!(o.inputFlags & MASK_KEYSHIDDEN)) return true; - } - - if (((inputFlags^o.inputFlags) & MASK_NAVHIDDEN) != 0) { - if (!(inputFlags & MASK_NAVHIDDEN)) return false; - if (!(o.inputFlags & MASK_NAVHIDDEN)) return true; - } - - if (keyboard != o.keyboard) { - if (!keyboard) return false; - if (!o.keyboard) return true; - } - - if (navigation != o.navigation) { - if (!navigation) return false; - if (!o.navigation) return true; - } - } - - if (screenSize || o.screenSize) { - if (screenWidth != o.screenWidth) { - if (!screenWidth) return false; - if (!o.screenWidth) return true; - } - - if (screenHeight != o.screenHeight) { - if (!screenHeight) return false; - if (!o.screenHeight) return true; - } - } - - if (version || o.version) { - if (sdkVersion != o.sdkVersion) { - if (!sdkVersion) return false; - if (!o.sdkVersion) return true; - } - - if (minorVersion != o.minorVersion) { - if (!minorVersion) return false; - if (!o.minorVersion) return true; - } - } - return false; - } + bool isMoreSpecificThan(const ResTable_config& o) const; // Return true if 'this' is a better match than 'o' for the 'requested' // configuration. This assumes that match() has already been used to @@ -1231,222 +1040,7 @@ struct ResTable_config // they are not equal then one must be generic because only generic and // '==requested' will pass the match() call. So if this is not generic, // it wins. If this IS generic, o wins (return false). - inline bool - isBetterThan(const ResTable_config& o, - const ResTable_config* requested) const { - if (requested) { - if (imsi || o.imsi) { - if ((mcc != o.mcc) && requested->mcc) { - return (mcc); - } - - if ((mnc != o.mnc) && requested->mnc) { - return (mnc); - } - } - - if (locale || o.locale) { - if ((language[0] != o.language[0]) && requested->language[0]) { - return (language[0]); - } - - if ((country[0] != o.country[0]) && requested->country[0]) { - return (country[0]); - } - } - - if (smallestScreenWidthDp || o.smallestScreenWidthDp) { - // The configuration closest to the actual size is best. - // We assume that larger configs have already been filtered - // out at this point. That means we just want the largest one. - return smallestScreenWidthDp >= o.smallestScreenWidthDp; - } - - if (screenSizeDp || o.screenSizeDp) { - // "Better" is based on the sum of the difference between both - // width and height from the requested dimensions. We are - // assuming the invalid configs (with smaller dimens) have - // already been filtered. Note that if a particular dimension - // is unspecified, we will end up with a large value (the - // difference between 0 and the requested dimension), which is - // good since we will prefer a config that has specified a - // dimension value. - int myDelta = 0, otherDelta = 0; - if (requested->screenWidthDp) { - myDelta += requested->screenWidthDp - screenWidthDp; - otherDelta += requested->screenWidthDp - o.screenWidthDp; - } - if (requested->screenHeightDp) { - myDelta += requested->screenHeightDp - screenHeightDp; - otherDelta += requested->screenHeightDp - o.screenHeightDp; - } - //ALOGI("Comparing this %dx%d to other %dx%d in %dx%d: myDelta=%d otherDelta=%d", - // screenWidthDp, screenHeightDp, o.screenWidthDp, o.screenHeightDp, - // requested->screenWidthDp, requested->screenHeightDp, myDelta, otherDelta); - return (myDelta <= otherDelta); - } - - if (screenLayout || o.screenLayout) { - if (((screenLayout^o.screenLayout) & MASK_SCREENSIZE) != 0 - && (requested->screenLayout & MASK_SCREENSIZE)) { - // A little backwards compatibility here: undefined is - // considered equivalent to normal. But only if the - // requested size is at least normal; otherwise, small - // is better than the default. - int mySL = (screenLayout & MASK_SCREENSIZE); - int oSL = (o.screenLayout & MASK_SCREENSIZE); - int fixedMySL = mySL; - int fixedOSL = oSL; - if ((requested->screenLayout & MASK_SCREENSIZE) >= SCREENSIZE_NORMAL) { - if (fixedMySL == 0) fixedMySL = SCREENSIZE_NORMAL; - if (fixedOSL == 0) fixedOSL = SCREENSIZE_NORMAL; - } - // For screen size, the best match is the one that is - // closest to the requested screen size, but not over - // (the not over part is dealt with in match() below). - if (fixedMySL == fixedOSL) { - // If the two are the same, but 'this' is actually - // undefined, then the other is really a better match. - if (mySL == 0) return false; - return true; - } - return fixedMySL >= fixedOSL; - } - if (((screenLayout^o.screenLayout) & MASK_SCREENLONG) != 0 - && (requested->screenLayout & MASK_SCREENLONG)) { - return (screenLayout & MASK_SCREENLONG); - } - } - - if ((orientation != o.orientation) && requested->orientation) { - return (orientation); - } - - if (uiMode || o.uiMode) { - if (((uiMode^o.uiMode) & MASK_UI_MODE_TYPE) != 0 - && (requested->uiMode & MASK_UI_MODE_TYPE)) { - return (uiMode & MASK_UI_MODE_TYPE); - } - if (((uiMode^o.uiMode) & MASK_UI_MODE_NIGHT) != 0 - && (requested->uiMode & MASK_UI_MODE_NIGHT)) { - return (uiMode & MASK_UI_MODE_NIGHT); - } - } - - if (screenType || o.screenType) { - if (density != o.density) { - // density is tough. Any density is potentially useful - // because the system will scale it. Scaling down - // is generally better than scaling up. - // Default density counts as 160dpi (the system default) - // TODO - remove 160 constants - int h = (density?density:160); - int l = (o.density?o.density:160); - bool bImBigger = true; - if (l > h) { - int t = h; - h = l; - l = t; - bImBigger = false; - } - - int reqValue = (requested->density?requested->density:160); - if (reqValue >= h) { - // requested value higher than both l and h, give h - return bImBigger; - } - if (l >= reqValue) { - // requested value lower than both l and h, give l - return !bImBigger; - } - // saying that scaling down is 2x better than up - if (((2 * l) - reqValue) * h > reqValue * reqValue) { - return !bImBigger; - } else { - return bImBigger; - } - } - - if ((touchscreen != o.touchscreen) && requested->touchscreen) { - return (touchscreen); - } - } - - if (input || o.input) { - const int keysHidden = inputFlags & MASK_KEYSHIDDEN; - const int oKeysHidden = o.inputFlags & MASK_KEYSHIDDEN; - if (keysHidden != oKeysHidden) { - const int reqKeysHidden = - requested->inputFlags & MASK_KEYSHIDDEN; - if (reqKeysHidden) { - - if (!keysHidden) return false; - if (!oKeysHidden) return true; - // For compatibility, we count KEYSHIDDEN_NO as being - // the same as KEYSHIDDEN_SOFT. Here we disambiguate - // these by making an exact match more specific. - if (reqKeysHidden == keysHidden) return true; - if (reqKeysHidden == oKeysHidden) return false; - } - } - - const int navHidden = inputFlags & MASK_NAVHIDDEN; - const int oNavHidden = o.inputFlags & MASK_NAVHIDDEN; - if (navHidden != oNavHidden) { - const int reqNavHidden = - requested->inputFlags & MASK_NAVHIDDEN; - if (reqNavHidden) { - - if (!navHidden) return false; - if (!oNavHidden) return true; - } - } - - if ((keyboard != o.keyboard) && requested->keyboard) { - return (keyboard); - } - - if ((navigation != o.navigation) && requested->navigation) { - return (navigation); - } - } - - if (screenSize || o.screenSize) { - // "Better" is based on the sum of the difference between both - // width and height from the requested dimensions. We are - // assuming the invalid configs (with smaller sizes) have - // already been filtered. Note that if a particular dimension - // is unspecified, we will end up with a large value (the - // difference between 0 and the requested dimension), which is - // good since we will prefer a config that has specified a - // size value. - int myDelta = 0, otherDelta = 0; - if (requested->screenWidth) { - myDelta += requested->screenWidth - screenWidth; - otherDelta += requested->screenWidth - o.screenWidth; - } - if (requested->screenHeight) { - myDelta += requested->screenHeight - screenHeight; - otherDelta += requested->screenHeight - o.screenHeight; - } - return (myDelta <= otherDelta); - } - - if (version || o.version) { - if ((sdkVersion != o.sdkVersion) && requested->sdkVersion) { - return (sdkVersion > o.sdkVersion); - } - - if ((minorVersion != o.minorVersion) && - requested->minorVersion) { - return (minorVersion); - } - } - - return false; - } - return isMoreSpecificThan(o); - } + bool isBetterThan(const ResTable_config& o, const ResTable_config* requested) const; // Return true if 'this' can be considered a match for the parameters in // 'settings'. @@ -1454,150 +1048,11 @@ struct ResTable_config // but a request for the default should not match odd specifics // (ie, request with no mcc should not match a particular mcc's data) // settings is the requested settings - inline bool match(const ResTable_config& settings) const { - if (imsi != 0) { - if (mcc != 0 && mcc != settings.mcc) { - return false; - } - if (mnc != 0 && mnc != settings.mnc) { - return false; - } - } - if (locale != 0) { - if (language[0] != 0 - && (language[0] != settings.language[0] - || language[1] != settings.language[1])) { - return false; - } - if (country[0] != 0 - && (country[0] != settings.country[0] - || country[1] != settings.country[1])) { - return false; - } - } - if (screenConfig != 0) { - const int screenSize = screenLayout&MASK_SCREENSIZE; - const int setScreenSize = settings.screenLayout&MASK_SCREENSIZE; - // Any screen sizes for larger screens than the setting do not - // match. - if (screenSize != 0 && screenSize > setScreenSize) { - return false; - } - - const int screenLong = screenLayout&MASK_SCREENLONG; - const int setScreenLong = settings.screenLayout&MASK_SCREENLONG; - if (screenLong != 0 && screenLong != setScreenLong) { - return false; - } + bool match(const ResTable_config& settings) const; - const int uiModeType = uiMode&MASK_UI_MODE_TYPE; - const int setUiModeType = settings.uiMode&MASK_UI_MODE_TYPE; - if (uiModeType != 0 && uiModeType != setUiModeType) { - return false; - } + void getLocale(char str[6]) const; - const int uiModeNight = uiMode&MASK_UI_MODE_NIGHT; - const int setUiModeNight = settings.uiMode&MASK_UI_MODE_NIGHT; - if (uiModeNight != 0 && uiModeNight != setUiModeNight) { - return false; - } - - if (smallestScreenWidthDp != 0 - && smallestScreenWidthDp > settings.smallestScreenWidthDp) { - return false; - } - } - if (screenSizeDp != 0) { - if (screenWidthDp != 0 && screenWidthDp > settings.screenWidthDp) { - //ALOGI("Filtering out width %d in requested %d", screenWidthDp, settings.screenWidthDp); - return false; - } - if (screenHeightDp != 0 && screenHeightDp > settings.screenHeightDp) { - //ALOGI("Filtering out height %d in requested %d", screenHeightDp, settings.screenHeightDp); - return false; - } - } - if (screenType != 0) { - if (orientation != 0 && orientation != settings.orientation) { - return false; - } - // density always matches - we can scale it. See isBetterThan - if (touchscreen != 0 && touchscreen != settings.touchscreen) { - return false; - } - } - if (input != 0) { - const int keysHidden = inputFlags&MASK_KEYSHIDDEN; - const int setKeysHidden = settings.inputFlags&MASK_KEYSHIDDEN; - if (keysHidden != 0 && keysHidden != setKeysHidden) { - // For compatibility, we count a request for KEYSHIDDEN_NO as also - // matching the more recent KEYSHIDDEN_SOFT. Basically - // KEYSHIDDEN_NO means there is some kind of keyboard available. - //ALOGI("Matching keysHidden: have=%d, config=%d\n", keysHidden, setKeysHidden); - if (keysHidden != KEYSHIDDEN_NO || setKeysHidden != KEYSHIDDEN_SOFT) { - //ALOGI("No match!"); - return false; - } - } - const int navHidden = inputFlags&MASK_NAVHIDDEN; - const int setNavHidden = settings.inputFlags&MASK_NAVHIDDEN; - if (navHidden != 0 && navHidden != setNavHidden) { - return false; - } - if (keyboard != 0 && keyboard != settings.keyboard) { - return false; - } - if (navigation != 0 && navigation != settings.navigation) { - return false; - } - } - if (screenSize != 0) { - if (screenWidth != 0 && screenWidth > settings.screenWidth) { - return false; - } - if (screenHeight != 0 && screenHeight > settings.screenHeight) { - return false; - } - } - if (version != 0) { - if (sdkVersion != 0 && sdkVersion > settings.sdkVersion) { - return false; - } - if (minorVersion != 0 && minorVersion != settings.minorVersion) { - return false; - } - } - return true; - } - - void getLocale(char str[6]) const { - memset(str, 0, 6); - if (language[0]) { - str[0] = language[0]; - str[1] = language[1]; - if (country[0]) { - str[2] = '_'; - str[3] = country[0]; - str[4] = country[1]; - } - } - } - - String8 toString() const { - char buf[200]; - sprintf(buf, "imsi=%d/%d lang=%c%c reg=%c%c orient=%d touch=%d dens=%d " - "kbd=%d nav=%d input=%d ssz=%dx%d sw%ddp w%ddp h%ddp sz=%d long=%d " - "ui=%d night=%d vers=%d.%d", - mcc, mnc, - language[0] ? language[0] : '-', language[1] ? language[1] : '-', - country[0] ? country[0] : '-', country[1] ? country[1] : '-', - orientation, touchscreen, density, keyboard, navigation, inputFlags, - screenWidth, screenHeight, smallestScreenWidthDp, screenWidthDp, screenHeightDp, - screenLayout&MASK_SCREENSIZE, screenLayout&MASK_SCREENLONG, - uiMode&MASK_UI_MODE_TYPE, uiMode&MASK_UI_MODE_NIGHT, - sdkVersion, minorVersion); - return String8(buf); - } + String8 toString() const; }; /** @@ -2056,8 +1511,14 @@ public: const char16_t* getBasePackageName(size_t idx) const; uint32_t getBasePackageId(size_t idx) const; + // Return the number of resource tables that the object contains. size_t getTableCount() const; + // Return the values string pool for the resource table at the given + // index. This string pool contains all of the strings for values + // contained in the resource table -- that is the item values themselves, + // but not the names their entries or types. const ResStringPool* getTableStringBlock(size_t index) const; + // Return unique cookie identifier for the given resource table. void* getTableCookie(size_t index) const; // Return the configurations (ResTable_config) that we know about diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp index 15b83bbd2..3fa562ec6 100644 --- a/libs/utils/ResourceTypes.cpp +++ b/libs/utils/ResourceTypes.cpp @@ -221,7 +221,7 @@ static void deserializeInternal(const void* inData, Res_png_9patch* outData) { static bool assertIdmapHeader(const uint32_t* map, size_t sizeBytes) { if (sizeBytes < ResTable::IDMAP_HEADER_SIZE_BYTES) { - ALOGW("idmap assertion failed: size=%d bytes\n", sizeBytes); + ALOGW("idmap assertion failed: size=%d bytes\n", (int)sizeBytes); return false; } if (*map != htodl(IDMAP_MAGIC)) { // htodl: map data expected to be in correct endianess @@ -250,7 +250,7 @@ static status_t idmapLookup(const uint32_t* map, size_t sizeBytes, uint32_t key, return UNKNOWN_ERROR; } if (typeCount > size) { - ALOGW("Resource ID map: number of types=%d exceeds size of map=%d\n", typeCount, size); + ALOGW("Resource ID map: number of types=%d exceeds size of map=%d\n", typeCount, (int)size); return UNKNOWN_ERROR; } const uint32_t typeOffset = map[type]; @@ -260,7 +260,7 @@ static status_t idmapLookup(const uint32_t* map, size_t sizeBytes, uint32_t key, } if (typeOffset + 1 > size) { ALOGW("Resource ID map: type offset=%d exceeds reasonable value, size of map=%d\n", - typeOffset, size); + typeOffset, (int)size); return UNKNOWN_ERROR; } const uint32_t entryCount = map[typeOffset]; @@ -271,7 +271,7 @@ static status_t idmapLookup(const uint32_t* map, size_t sizeBytes, uint32_t key, } const uint32_t index = typeOffset + 2 + entry - entryOffset; if (index > size) { - ALOGW("Resource ID map: entry index=%d exceeds size of map=%d\n", index, size); + ALOGW("Resource ID map: entry index=%d exceeds size of map=%d\n", index, (int)size); *outValue = 0; return NO_ERROR; } @@ -659,6 +659,16 @@ const char* ResStringPool::string8At(size_t idx, size_t* outLen) const return NULL; } +const String8 ResStringPool::string8ObjectAt(size_t idx) const +{ + size_t len; + const char *str = (const char*)string8At(idx, &len); + if (str != NULL) { + return String8(str); + } + return String8(stringAt(idx, &len)); +} + const ResStringPool_span* ResStringPool::styleAt(const ResStringPool_ref& ref) const { return styleAt(ref.index); @@ -738,12 +748,25 @@ size_t ResStringPool::size() const return (mError == NO_ERROR) ? mHeader->stringCount : 0; } -#ifndef HAVE_ANDROID_OS +size_t ResStringPool::styleCount() const +{ + return (mError == NO_ERROR) ? mHeader->styleCount : 0; +} + +size_t ResStringPool::bytes() const +{ + return (mError == NO_ERROR) ? mHeader->header.size : 0; +} + +bool ResStringPool::isSorted() const +{ + return (mHeader->flags&ResStringPool_header::SORTED_FLAG)!=0; +} + bool ResStringPool::isUTF8() const { return (mHeader->flags&ResStringPool_header::UTF8_FLAG)!=0; } -#endif // -------------------------------------------------------------------- // -------------------------------------------------------------------- @@ -1367,6 +1390,873 @@ status_t ResXMLTree::validateNode(const ResXMLTree_node* node) const // -------------------------------------------------------------------- // -------------------------------------------------------------------- +void ResTable_config::copyFromDeviceNoSwap(const ResTable_config& o) { + const size_t size = dtohl(o.size); + if (size >= sizeof(ResTable_config)) { + *this = o; + } else { + memcpy(this, &o, size); + memset(((uint8_t*)this)+size, 0, sizeof(ResTable_config)-size); + } +} + +void ResTable_config::copyFromDtoH(const ResTable_config& o) { + copyFromDeviceNoSwap(o); + size = sizeof(ResTable_config); + mcc = dtohs(mcc); + mnc = dtohs(mnc); + density = dtohs(density); + screenWidth = dtohs(screenWidth); + screenHeight = dtohs(screenHeight); + sdkVersion = dtohs(sdkVersion); + minorVersion = dtohs(minorVersion); + smallestScreenWidthDp = dtohs(smallestScreenWidthDp); + screenWidthDp = dtohs(screenWidthDp); + screenHeightDp = dtohs(screenHeightDp); +} + +void ResTable_config::swapHtoD() { + size = htodl(size); + mcc = htods(mcc); + mnc = htods(mnc); + density = htods(density); + screenWidth = htods(screenWidth); + screenHeight = htods(screenHeight); + sdkVersion = htods(sdkVersion); + minorVersion = htods(minorVersion); + smallestScreenWidthDp = htods(smallestScreenWidthDp); + screenWidthDp = htods(screenWidthDp); + screenHeightDp = htods(screenHeightDp); +} + +int ResTable_config::compare(const ResTable_config& o) const { + int32_t diff = (int32_t)(imsi - o.imsi); + if (diff != 0) return diff; + diff = (int32_t)(locale - o.locale); + if (diff != 0) return diff; + diff = (int32_t)(screenType - o.screenType); + if (diff != 0) return diff; + diff = (int32_t)(input - o.input); + if (diff != 0) return diff; + diff = (int32_t)(screenSize - o.screenSize); + if (diff != 0) return diff; + diff = (int32_t)(version - o.version); + if (diff != 0) return diff; + diff = (int32_t)(screenLayout - o.screenLayout); + if (diff != 0) return diff; + diff = (int32_t)(uiMode - o.uiMode); + if (diff != 0) return diff; + diff = (int32_t)(smallestScreenWidthDp - o.smallestScreenWidthDp); + if (diff != 0) return diff; + diff = (int32_t)(screenSizeDp - o.screenSizeDp); + return (int)diff; +} + +int ResTable_config::compareLogical(const ResTable_config& o) const { + if (mcc != o.mcc) { + return mcc < o.mcc ? -1 : 1; + } + if (mnc != o.mnc) { + return mnc < o.mnc ? -1 : 1; + } + if (language[0] != o.language[0]) { + return language[0] < o.language[0] ? -1 : 1; + } + if (language[1] != o.language[1]) { + return language[1] < o.language[1] ? -1 : 1; + } + if (country[0] != o.country[0]) { + return country[0] < o.country[0] ? -1 : 1; + } + if (country[1] != o.country[1]) { + return country[1] < o.country[1] ? -1 : 1; + } + if (smallestScreenWidthDp != o.smallestScreenWidthDp) { + return smallestScreenWidthDp < o.smallestScreenWidthDp ? -1 : 1; + } + if (screenWidthDp != o.screenWidthDp) { + return screenWidthDp < o.screenWidthDp ? -1 : 1; + } + if (screenHeightDp != o.screenHeightDp) { + return screenHeightDp < o.screenHeightDp ? -1 : 1; + } + if (screenWidth != o.screenWidth) { + return screenWidth < o.screenWidth ? -1 : 1; + } + if (screenHeight != o.screenHeight) { + return screenHeight < o.screenHeight ? -1 : 1; + } + if (density != o.density) { + return density < o.density ? -1 : 1; + } + if (orientation != o.orientation) { + return orientation < o.orientation ? -1 : 1; + } + if (touchscreen != o.touchscreen) { + return touchscreen < o.touchscreen ? -1 : 1; + } + if (input != o.input) { + return input < o.input ? -1 : 1; + } + if (screenLayout != o.screenLayout) { + return screenLayout < o.screenLayout ? -1 : 1; + } + if (uiMode != o.uiMode) { + return uiMode < o.uiMode ? -1 : 1; + } + if (version != o.version) { + return version < o.version ? -1 : 1; + } + return 0; +} + +int ResTable_config::diff(const ResTable_config& o) const { + int diffs = 0; + if (mcc != o.mcc) diffs |= CONFIG_MCC; + if (mnc != o.mnc) diffs |= CONFIG_MNC; + if (locale != o.locale) diffs |= CONFIG_LOCALE; + if (orientation != o.orientation) diffs |= CONFIG_ORIENTATION; + if (density != o.density) diffs |= CONFIG_DENSITY; + if (touchscreen != o.touchscreen) diffs |= CONFIG_TOUCHSCREEN; + if (((inputFlags^o.inputFlags)&(MASK_KEYSHIDDEN|MASK_NAVHIDDEN)) != 0) + diffs |= CONFIG_KEYBOARD_HIDDEN; + if (keyboard != o.keyboard) diffs |= CONFIG_KEYBOARD; + if (navigation != o.navigation) diffs |= CONFIG_NAVIGATION; + if (screenSize != o.screenSize) diffs |= CONFIG_SCREEN_SIZE; + if (version != o.version) diffs |= CONFIG_VERSION; + if (screenLayout != o.screenLayout) diffs |= CONFIG_SCREEN_LAYOUT; + if (uiMode != o.uiMode) diffs |= CONFIG_UI_MODE; + if (smallestScreenWidthDp != o.smallestScreenWidthDp) diffs |= CONFIG_SMALLEST_SCREEN_SIZE; + if (screenSizeDp != o.screenSizeDp) diffs |= CONFIG_SCREEN_SIZE; + return diffs; +} + +bool ResTable_config::isMoreSpecificThan(const ResTable_config& o) const { + // The order of the following tests defines the importance of one + // configuration parameter over another. Those tests first are more + // important, trumping any values in those following them. + if (imsi || o.imsi) { + if (mcc != o.mcc) { + if (!mcc) return false; + if (!o.mcc) return true; + } + + if (mnc != o.mnc) { + if (!mnc) return false; + if (!o.mnc) return true; + } + } + + if (locale || o.locale) { + if (language[0] != o.language[0]) { + if (!language[0]) return false; + if (!o.language[0]) return true; + } + + if (country[0] != o.country[0]) { + if (!country[0]) return false; + if (!o.country[0]) return true; + } + } + + if (smallestScreenWidthDp || o.smallestScreenWidthDp) { + if (smallestScreenWidthDp != o.smallestScreenWidthDp) { + if (!smallestScreenWidthDp) return false; + if (!o.smallestScreenWidthDp) return true; + } + } + + if (screenSizeDp || o.screenSizeDp) { + if (screenWidthDp != o.screenWidthDp) { + if (!screenWidthDp) return false; + if (!o.screenWidthDp) return true; + } + + if (screenHeightDp != o.screenHeightDp) { + if (!screenHeightDp) return false; + if (!o.screenHeightDp) return true; + } + } + + if (screenLayout || o.screenLayout) { + if (((screenLayout^o.screenLayout) & MASK_SCREENSIZE) != 0) { + if (!(screenLayout & MASK_SCREENSIZE)) return false; + if (!(o.screenLayout & MASK_SCREENSIZE)) return true; + } + if (((screenLayout^o.screenLayout) & MASK_SCREENLONG) != 0) { + if (!(screenLayout & MASK_SCREENLONG)) return false; + if (!(o.screenLayout & MASK_SCREENLONG)) return true; + } + } + + if (orientation != o.orientation) { + if (!orientation) return false; + if (!o.orientation) return true; + } + + if (uiMode || o.uiMode) { + if (((uiMode^o.uiMode) & MASK_UI_MODE_TYPE) != 0) { + if (!(uiMode & MASK_UI_MODE_TYPE)) return false; + if (!(o.uiMode & MASK_UI_MODE_TYPE)) return true; + } + if (((uiMode^o.uiMode) & MASK_UI_MODE_NIGHT) != 0) { + if (!(uiMode & MASK_UI_MODE_NIGHT)) return false; + if (!(o.uiMode & MASK_UI_MODE_NIGHT)) return true; + } + } + + // density is never 'more specific' + // as the default just equals 160 + + if (touchscreen != o.touchscreen) { + if (!touchscreen) return false; + if (!o.touchscreen) return true; + } + + if (input || o.input) { + if (((inputFlags^o.inputFlags) & MASK_KEYSHIDDEN) != 0) { + if (!(inputFlags & MASK_KEYSHIDDEN)) return false; + if (!(o.inputFlags & MASK_KEYSHIDDEN)) return true; + } + + if (((inputFlags^o.inputFlags) & MASK_NAVHIDDEN) != 0) { + if (!(inputFlags & MASK_NAVHIDDEN)) return false; + if (!(o.inputFlags & MASK_NAVHIDDEN)) return true; + } + + if (keyboard != o.keyboard) { + if (!keyboard) return false; + if (!o.keyboard) return true; + } + + if (navigation != o.navigation) { + if (!navigation) return false; + if (!o.navigation) return true; + } + } + + if (screenSize || o.screenSize) { + if (screenWidth != o.screenWidth) { + if (!screenWidth) return false; + if (!o.screenWidth) return true; + } + + if (screenHeight != o.screenHeight) { + if (!screenHeight) return false; + if (!o.screenHeight) return true; + } + } + + if (version || o.version) { + if (sdkVersion != o.sdkVersion) { + if (!sdkVersion) return false; + if (!o.sdkVersion) return true; + } + + if (minorVersion != o.minorVersion) { + if (!minorVersion) return false; + if (!o.minorVersion) return true; + } + } + return false; +} + +bool ResTable_config::isBetterThan(const ResTable_config& o, + const ResTable_config* requested) const { + if (requested) { + if (imsi || o.imsi) { + if ((mcc != o.mcc) && requested->mcc) { + return (mcc); + } + + if ((mnc != o.mnc) && requested->mnc) { + return (mnc); + } + } + + if (locale || o.locale) { + if ((language[0] != o.language[0]) && requested->language[0]) { + return (language[0]); + } + + if ((country[0] != o.country[0]) && requested->country[0]) { + return (country[0]); + } + } + + if (smallestScreenWidthDp || o.smallestScreenWidthDp) { + // The configuration closest to the actual size is best. + // We assume that larger configs have already been filtered + // out at this point. That means we just want the largest one. + return smallestScreenWidthDp >= o.smallestScreenWidthDp; + } + + if (screenSizeDp || o.screenSizeDp) { + // "Better" is based on the sum of the difference between both + // width and height from the requested dimensions. We are + // assuming the invalid configs (with smaller dimens) have + // already been filtered. Note that if a particular dimension + // is unspecified, we will end up with a large value (the + // difference between 0 and the requested dimension), which is + // good since we will prefer a config that has specified a + // dimension value. + int myDelta = 0, otherDelta = 0; + if (requested->screenWidthDp) { + myDelta += requested->screenWidthDp - screenWidthDp; + otherDelta += requested->screenWidthDp - o.screenWidthDp; + } + if (requested->screenHeightDp) { + myDelta += requested->screenHeightDp - screenHeightDp; + otherDelta += requested->screenHeightDp - o.screenHeightDp; + } + //ALOGI("Comparing this %dx%d to other %dx%d in %dx%d: myDelta=%d otherDelta=%d", + // screenWidthDp, screenHeightDp, o.screenWidthDp, o.screenHeightDp, + // requested->screenWidthDp, requested->screenHeightDp, myDelta, otherDelta); + return (myDelta <= otherDelta); + } + + if (screenLayout || o.screenLayout) { + if (((screenLayout^o.screenLayout) & MASK_SCREENSIZE) != 0 + && (requested->screenLayout & MASK_SCREENSIZE)) { + // A little backwards compatibility here: undefined is + // considered equivalent to normal. But only if the + // requested size is at least normal; otherwise, small + // is better than the default. + int mySL = (screenLayout & MASK_SCREENSIZE); + int oSL = (o.screenLayout & MASK_SCREENSIZE); + int fixedMySL = mySL; + int fixedOSL = oSL; + if ((requested->screenLayout & MASK_SCREENSIZE) >= SCREENSIZE_NORMAL) { + if (fixedMySL == 0) fixedMySL = SCREENSIZE_NORMAL; + if (fixedOSL == 0) fixedOSL = SCREENSIZE_NORMAL; + } + // For screen size, the best match is the one that is + // closest to the requested screen size, but not over + // (the not over part is dealt with in match() below). + if (fixedMySL == fixedOSL) { + // If the two are the same, but 'this' is actually + // undefined, then the other is really a better match. + if (mySL == 0) return false; + return true; + } + return fixedMySL >= fixedOSL; + } + if (((screenLayout^o.screenLayout) & MASK_SCREENLONG) != 0 + && (requested->screenLayout & MASK_SCREENLONG)) { + return (screenLayout & MASK_SCREENLONG); + } + } + + if ((orientation != o.orientation) && requested->orientation) { + return (orientation); + } + + if (uiMode || o.uiMode) { + if (((uiMode^o.uiMode) & MASK_UI_MODE_TYPE) != 0 + && (requested->uiMode & MASK_UI_MODE_TYPE)) { + return (uiMode & MASK_UI_MODE_TYPE); + } + if (((uiMode^o.uiMode) & MASK_UI_MODE_NIGHT) != 0 + && (requested->uiMode & MASK_UI_MODE_NIGHT)) { + return (uiMode & MASK_UI_MODE_NIGHT); + } + } + + if (screenType || o.screenType) { + if (density != o.density) { + // density is tough. Any density is potentially useful + // because the system will scale it. Scaling down + // is generally better than scaling up. + // Default density counts as 160dpi (the system default) + // TODO - remove 160 constants + int h = (density?density:160); + int l = (o.density?o.density:160); + bool bImBigger = true; + if (l > h) { + int t = h; + h = l; + l = t; + bImBigger = false; + } + + int reqValue = (requested->density?requested->density:160); + if (reqValue >= h) { + // requested value higher than both l and h, give h + return bImBigger; + } + if (l >= reqValue) { + // requested value lower than both l and h, give l + return !bImBigger; + } + // saying that scaling down is 2x better than up + if (((2 * l) - reqValue) * h > reqValue * reqValue) { + return !bImBigger; + } else { + return bImBigger; + } + } + + if ((touchscreen != o.touchscreen) && requested->touchscreen) { + return (touchscreen); + } + } + + if (input || o.input) { + const int keysHidden = inputFlags & MASK_KEYSHIDDEN; + const int oKeysHidden = o.inputFlags & MASK_KEYSHIDDEN; + if (keysHidden != oKeysHidden) { + const int reqKeysHidden = + requested->inputFlags & MASK_KEYSHIDDEN; + if (reqKeysHidden) { + + if (!keysHidden) return false; + if (!oKeysHidden) return true; + // For compatibility, we count KEYSHIDDEN_NO as being + // the same as KEYSHIDDEN_SOFT. Here we disambiguate + // these by making an exact match more specific. + if (reqKeysHidden == keysHidden) return true; + if (reqKeysHidden == oKeysHidden) return false; + } + } + + const int navHidden = inputFlags & MASK_NAVHIDDEN; + const int oNavHidden = o.inputFlags & MASK_NAVHIDDEN; + if (navHidden != oNavHidden) { + const int reqNavHidden = + requested->inputFlags & MASK_NAVHIDDEN; + if (reqNavHidden) { + + if (!navHidden) return false; + if (!oNavHidden) return true; + } + } + + if ((keyboard != o.keyboard) && requested->keyboard) { + return (keyboard); + } + + if ((navigation != o.navigation) && requested->navigation) { + return (navigation); + } + } + + if (screenSize || o.screenSize) { + // "Better" is based on the sum of the difference between both + // width and height from the requested dimensions. We are + // assuming the invalid configs (with smaller sizes) have + // already been filtered. Note that if a particular dimension + // is unspecified, we will end up with a large value (the + // difference between 0 and the requested dimension), which is + // good since we will prefer a config that has specified a + // size value. + int myDelta = 0, otherDelta = 0; + if (requested->screenWidth) { + myDelta += requested->screenWidth - screenWidth; + otherDelta += requested->screenWidth - o.screenWidth; + } + if (requested->screenHeight) { + myDelta += requested->screenHeight - screenHeight; + otherDelta += requested->screenHeight - o.screenHeight; + } + return (myDelta <= otherDelta); + } + + if (version || o.version) { + if ((sdkVersion != o.sdkVersion) && requested->sdkVersion) { + return (sdkVersion > o.sdkVersion); + } + + if ((minorVersion != o.minorVersion) && + requested->minorVersion) { + return (minorVersion); + } + } + + return false; + } + return isMoreSpecificThan(o); +} + +bool ResTable_config::match(const ResTable_config& settings) const { + if (imsi != 0) { + if (mcc != 0 && mcc != settings.mcc) { + return false; + } + if (mnc != 0 && mnc != settings.mnc) { + return false; + } + } + if (locale != 0) { + if (language[0] != 0 + && (language[0] != settings.language[0] + || language[1] != settings.language[1])) { + return false; + } + if (country[0] != 0 + && (country[0] != settings.country[0] + || country[1] != settings.country[1])) { + return false; + } + } + if (screenConfig != 0) { + const int screenSize = screenLayout&MASK_SCREENSIZE; + const int setScreenSize = settings.screenLayout&MASK_SCREENSIZE; + // Any screen sizes for larger screens than the setting do not + // match. + if (screenSize != 0 && screenSize > setScreenSize) { + return false; + } + + const int screenLong = screenLayout&MASK_SCREENLONG; + const int setScreenLong = settings.screenLayout&MASK_SCREENLONG; + if (screenLong != 0 && screenLong != setScreenLong) { + return false; + } + + const int uiModeType = uiMode&MASK_UI_MODE_TYPE; + const int setUiModeType = settings.uiMode&MASK_UI_MODE_TYPE; + if (uiModeType != 0 && uiModeType != setUiModeType) { + return false; + } + + const int uiModeNight = uiMode&MASK_UI_MODE_NIGHT; + const int setUiModeNight = settings.uiMode&MASK_UI_MODE_NIGHT; + if (uiModeNight != 0 && uiModeNight != setUiModeNight) { + return false; + } + + if (smallestScreenWidthDp != 0 + && smallestScreenWidthDp > settings.smallestScreenWidthDp) { + return false; + } + } + if (screenSizeDp != 0) { + if (screenWidthDp != 0 && screenWidthDp > settings.screenWidthDp) { + //ALOGI("Filtering out width %d in requested %d", screenWidthDp, settings.screenWidthDp); + return false; + } + if (screenHeightDp != 0 && screenHeightDp > settings.screenHeightDp) { + //ALOGI("Filtering out height %d in requested %d", screenHeightDp, settings.screenHeightDp); + return false; + } + } + if (screenType != 0) { + if (orientation != 0 && orientation != settings.orientation) { + return false; + } + // density always matches - we can scale it. See isBetterThan + if (touchscreen != 0 && touchscreen != settings.touchscreen) { + return false; + } + } + if (input != 0) { + const int keysHidden = inputFlags&MASK_KEYSHIDDEN; + const int setKeysHidden = settings.inputFlags&MASK_KEYSHIDDEN; + if (keysHidden != 0 && keysHidden != setKeysHidden) { + // For compatibility, we count a request for KEYSHIDDEN_NO as also + // matching the more recent KEYSHIDDEN_SOFT. Basically + // KEYSHIDDEN_NO means there is some kind of keyboard available. + //ALOGI("Matching keysHidden: have=%d, config=%d\n", keysHidden, setKeysHidden); + if (keysHidden != KEYSHIDDEN_NO || setKeysHidden != KEYSHIDDEN_SOFT) { + //ALOGI("No match!"); + return false; + } + } + const int navHidden = inputFlags&MASK_NAVHIDDEN; + const int setNavHidden = settings.inputFlags&MASK_NAVHIDDEN; + if (navHidden != 0 && navHidden != setNavHidden) { + return false; + } + if (keyboard != 0 && keyboard != settings.keyboard) { + return false; + } + if (navigation != 0 && navigation != settings.navigation) { + return false; + } + } + if (screenSize != 0) { + if (screenWidth != 0 && screenWidth > settings.screenWidth) { + return false; + } + if (screenHeight != 0 && screenHeight > settings.screenHeight) { + return false; + } + } + if (version != 0) { + if (sdkVersion != 0 && sdkVersion > settings.sdkVersion) { + return false; + } + if (minorVersion != 0 && minorVersion != settings.minorVersion) { + return false; + } + } + return true; +} + +void ResTable_config::getLocale(char str[6]) const { + memset(str, 0, 6); + if (language[0]) { + str[0] = language[0]; + str[1] = language[1]; + if (country[0]) { + str[2] = '_'; + str[3] = country[0]; + str[4] = country[1]; + } + } +} + +String8 ResTable_config::toString() const { + String8 res; + + if (mcc != 0) { + if (res.size() > 0) res.append("-"); + res.appendFormat("%dmcc", dtohs(mcc)); + } + if (mnc != 0) { + if (res.size() > 0) res.append("-"); + res.appendFormat("%dmnc", dtohs(mnc)); + } + if (language[0] != 0) { + if (res.size() > 0) res.append("-"); + res.append(language, 2); + } + if (country[0] != 0) { + if (res.size() > 0) res.append("-"); + res.append(country, 2); + } + if (smallestScreenWidthDp != 0) { + if (res.size() > 0) res.append("-"); + res.appendFormat("sw%ddp", dtohs(smallestScreenWidthDp)); + } + if (screenWidthDp != 0) { + if (res.size() > 0) res.append("-"); + res.appendFormat("w%ddp", dtohs(screenWidthDp)); + } + if (screenHeightDp != 0) { + if (res.size() > 0) res.append("-"); + res.appendFormat("h%ddp", dtohs(screenHeightDp)); + } + if ((screenLayout&MASK_SCREENSIZE) != SCREENSIZE_ANY) { + if (res.size() > 0) res.append("-"); + switch (screenLayout&ResTable_config::MASK_SCREENSIZE) { + case ResTable_config::SCREENSIZE_SMALL: + res.append("small"); + break; + case ResTable_config::SCREENSIZE_NORMAL: + res.append("normal"); + break; + case ResTable_config::SCREENSIZE_LARGE: + res.append("large"); + break; + case ResTable_config::SCREENSIZE_XLARGE: + res.append("xlarge"); + break; + default: + res.appendFormat("screenLayoutSize=%d", + dtohs(screenLayout&ResTable_config::MASK_SCREENSIZE)); + break; + } + } + if ((screenLayout&MASK_SCREENLONG) != 0) { + if (res.size() > 0) res.append("-"); + switch (screenLayout&ResTable_config::MASK_SCREENLONG) { + case ResTable_config::SCREENLONG_NO: + res.append("notlong"); + break; + case ResTable_config::SCREENLONG_YES: + res.append("long"); + break; + default: + res.appendFormat("screenLayoutLong=%d", + dtohs(screenLayout&ResTable_config::MASK_SCREENLONG)); + break; + } + } + if (orientation != ORIENTATION_ANY) { + if (res.size() > 0) res.append("-"); + switch (orientation) { + case ResTable_config::ORIENTATION_PORT: + res.append("port"); + break; + case ResTable_config::ORIENTATION_LAND: + res.append("land"); + break; + case ResTable_config::ORIENTATION_SQUARE: + res.append("square"); + break; + default: + res.appendFormat("orientation=%d", dtohs(orientation)); + break; + } + } + if ((uiMode&MASK_UI_MODE_TYPE) != UI_MODE_TYPE_ANY) { + if (res.size() > 0) res.append("-"); + switch (uiMode&ResTable_config::MASK_UI_MODE_TYPE) { + case ResTable_config::UI_MODE_TYPE_DESK: + res.append("desk"); + break; + case ResTable_config::UI_MODE_TYPE_CAR: + res.append("car"); + break; + case ResTable_config::UI_MODE_TYPE_TELEVISION: + res.append("television"); + break; + case ResTable_config::UI_MODE_TYPE_APPLIANCE: + res.append("appliance"); + break; + default: + res.appendFormat("uiModeType=%d", + dtohs(screenLayout&ResTable_config::MASK_UI_MODE_TYPE)); + break; + } + } + if ((uiMode&MASK_UI_MODE_NIGHT) != 0) { + if (res.size() > 0) res.append("-"); + switch (uiMode&ResTable_config::MASK_UI_MODE_NIGHT) { + case ResTable_config::UI_MODE_NIGHT_NO: + res.append("notnight"); + break; + case ResTable_config::UI_MODE_NIGHT_YES: + res.append("night"); + break; + default: + res.appendFormat("uiModeNight=%d", + dtohs(uiMode&MASK_UI_MODE_NIGHT)); + break; + } + } + if (density != DENSITY_DEFAULT) { + if (res.size() > 0) res.append("-"); + switch (density) { + case ResTable_config::DENSITY_LOW: + res.append("ldpi"); + break; + case ResTable_config::DENSITY_MEDIUM: + res.append("mdpi"); + break; + case ResTable_config::DENSITY_TV: + res.append("tvdpi"); + break; + case ResTable_config::DENSITY_HIGH: + res.append("hdpi"); + break; + case ResTable_config::DENSITY_XHIGH: + res.append("xhdpi"); + break; + case ResTable_config::DENSITY_XXHIGH: + res.append("xxhdpi"); + break; + case ResTable_config::DENSITY_NONE: + res.append("nodpi"); + break; + default: + res.appendFormat("density=%d", dtohs(density)); + break; + } + } + if (touchscreen != TOUCHSCREEN_ANY) { + if (res.size() > 0) res.append("-"); + switch (touchscreen) { + case ResTable_config::TOUCHSCREEN_NOTOUCH: + res.append("notouch"); + break; + case ResTable_config::TOUCHSCREEN_FINGER: + res.append("finger"); + break; + case ResTable_config::TOUCHSCREEN_STYLUS: + res.append("stylus"); + break; + default: + res.appendFormat("touchscreen=%d", dtohs(touchscreen)); + break; + } + } + if (keyboard != KEYBOARD_ANY) { + if (res.size() > 0) res.append("-"); + switch (keyboard) { + case ResTable_config::KEYBOARD_NOKEYS: + res.append("nokeys"); + break; + case ResTable_config::KEYBOARD_QWERTY: + res.append("qwerty"); + break; + case ResTable_config::KEYBOARD_12KEY: + res.append("12key"); + break; + default: + res.appendFormat("keyboard=%d", dtohs(keyboard)); + break; + } + } + if ((inputFlags&MASK_KEYSHIDDEN) != 0) { + if (res.size() > 0) res.append("-"); + switch (inputFlags&MASK_KEYSHIDDEN) { + case ResTable_config::KEYSHIDDEN_NO: + res.append("keysexposed"); + break; + case ResTable_config::KEYSHIDDEN_YES: + res.append("keyshidden"); + break; + case ResTable_config::KEYSHIDDEN_SOFT: + res.append("keyssoft"); + break; + } + } + if (navigation != NAVIGATION_ANY) { + if (res.size() > 0) res.append("-"); + switch (navigation) { + case ResTable_config::NAVIGATION_NONAV: + res.append("nonav"); + break; + case ResTable_config::NAVIGATION_DPAD: + res.append("dpad"); + break; + case ResTable_config::NAVIGATION_TRACKBALL: + res.append("trackball"); + break; + case ResTable_config::NAVIGATION_WHEEL: + res.append("wheel"); + break; + default: + res.appendFormat("navigation=%d", dtohs(navigation)); + break; + } + } + if ((inputFlags&MASK_NAVHIDDEN) != 0) { + if (res.size() > 0) res.append("-"); + switch (inputFlags&MASK_NAVHIDDEN) { + case ResTable_config::NAVHIDDEN_NO: + res.append("navsexposed"); + break; + case ResTable_config::NAVHIDDEN_YES: + res.append("navhidden"); + break; + default: + res.appendFormat("inputFlagsNavHidden=%d", + dtohs(inputFlags&MASK_NAVHIDDEN)); + break; + } + } + if (screenSize != 0) { + if (res.size() > 0) res.append("-"); + res.appendFormat("%dx%d", dtohs(screenWidth), dtohs(screenHeight)); + } + if (version != 0) { + if (res.size() > 0) res.append("-"); + res.appendFormat("v%d", dtohs(sdkVersion)); + if (minorVersion != 0) { + res.appendFormat(".%d", dtohs(minorVersion)); + } + } + + return res; +} + +// -------------------------------------------------------------------- +// -------------------------------------------------------------------- +// -------------------------------------------------------------------- + struct ResTable::Header { Header(ResTable* _owner) : owner(_owner), ownedData(NULL), header(NULL), @@ -3953,43 +4843,9 @@ ssize_t ResTable::getEntry( ResTable_config thisConfig; thisConfig.copyFromDtoH(thisType->config); - TABLE_GETENTRY(LOGI("Match entry 0x%x in type 0x%x (sz 0x%x): imsi:%d/%d=%d/%d " - "lang:%c%c=%c%c cnt:%c%c=%c%c orien:%d=%d touch:%d=%d " - "density:%d=%d key:%d=%d inp:%d=%d nav:%d=%d w:%d=%d h:%d=%d " - "swdp:%d=%d wdp:%d=%d hdp:%d=%d\n", + TABLE_GETENTRY(LOGI("Match entry 0x%x in type 0x%x (sz 0x%x): %s\n", entryIndex, typeIndex+1, dtohl(thisType->config.size), - thisConfig.mcc, thisConfig.mnc, - config ? config->mcc : 0, config ? config->mnc : 0, - thisConfig.language[0] ? thisConfig.language[0] : '-', - thisConfig.language[1] ? thisConfig.language[1] : '-', - config && config->language[0] ? config->language[0] : '-', - config && config->language[1] ? config->language[1] : '-', - thisConfig.country[0] ? thisConfig.country[0] : '-', - thisConfig.country[1] ? thisConfig.country[1] : '-', - config && config->country[0] ? config->country[0] : '-', - config && config->country[1] ? config->country[1] : '-', - thisConfig.orientation, - config ? config->orientation : 0, - thisConfig.touchscreen, - config ? config->touchscreen : 0, - thisConfig.density, - config ? config->density : 0, - thisConfig.keyboard, - config ? config->keyboard : 0, - thisConfig.inputFlags, - config ? config->inputFlags : 0, - thisConfig.navigation, - config ? config->navigation : 0, - thisConfig.screenWidth, - config ? config->screenWidth : 0, - thisConfig.screenHeight, - config ? config->screenHeight : 0, - thisConfig.smallestScreenWidthDp, - config ? config->smallestScreenWidthDp : 0, - thisConfig.screenWidthDp, - config ? config->screenWidthDp : 0, - thisConfig.screenHeightDp, - config ? config->screenHeightDp : 0)); + thisConfig.toString().string())); // Check to make sure this one is valid for the current parameters. if (config && !thisConfig.match(*config)) { @@ -4273,26 +5129,8 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg, TABLE_GETENTRY( ResTable_config thisConfig; thisConfig.copyFromDtoH(type->config); - ALOGI("Adding config to type %d: imsi:%d/%d lang:%c%c cnt:%c%c " - "orien:%d touch:%d density:%d key:%d inp:%d nav:%d w:%d h:%d " - "swdp:%d wdp:%d hdp:%d\n", - type->id, - thisConfig.mcc, thisConfig.mnc, - thisConfig.language[0] ? thisConfig.language[0] : '-', - thisConfig.language[1] ? thisConfig.language[1] : '-', - thisConfig.country[0] ? thisConfig.country[0] : '-', - thisConfig.country[1] ? thisConfig.country[1] : '-', - thisConfig.orientation, - thisConfig.touchscreen, - thisConfig.density, - thisConfig.keyboard, - thisConfig.inputFlags, - thisConfig.navigation, - thisConfig.screenWidth, - thisConfig.screenHeight, - thisConfig.smallestScreenWidthDp, - thisConfig.screenWidthDp, - thisConfig.screenHeightDp)); + ALOGI("Adding config to type %d: %s\n", + type->id, thisConfig.toString().string())); t->configs.add(type); } else { status_t err = validate_chunk(chunk, sizeof(ResChunk_header), @@ -4622,186 +5460,9 @@ void ResTable::print(bool inclValues) const printf(" NON-INTEGER ResTable_type ADDRESS: %p\n", type); continue; } - char density[16]; - uint16_t dval = dtohs(type->config.density); - if (dval == ResTable_config::DENSITY_DEFAULT) { - strcpy(density, "def"); - } else if (dval == ResTable_config::DENSITY_NONE) { - strcpy(density, "no"); - } else { - sprintf(density, "%d", (int)dval); - } - printf(" config %d", (int)configIndex); - if (type->config.mcc != 0) { - printf(" mcc=%d", dtohs(type->config.mcc)); - } - if (type->config.mnc != 0) { - printf(" mnc=%d", dtohs(type->config.mnc)); - } - if (type->config.locale != 0) { - printf(" lang=%c%c cnt=%c%c", - type->config.language[0] ? type->config.language[0] : '-', - type->config.language[1] ? type->config.language[1] : '-', - type->config.country[0] ? type->config.country[0] : '-', - type->config.country[1] ? type->config.country[1] : '-'); - } - if (type->config.screenLayout != 0) { - printf(" sz=%d", - type->config.screenLayout&ResTable_config::MASK_SCREENSIZE); - switch (type->config.screenLayout&ResTable_config::MASK_SCREENSIZE) { - case ResTable_config::SCREENSIZE_SMALL: - printf(" (small)"); - break; - case ResTable_config::SCREENSIZE_NORMAL: - printf(" (normal)"); - break; - case ResTable_config::SCREENSIZE_LARGE: - printf(" (large)"); - break; - case ResTable_config::SCREENSIZE_XLARGE: - printf(" (xlarge)"); - break; - } - printf(" lng=%d", - type->config.screenLayout&ResTable_config::MASK_SCREENLONG); - switch (type->config.screenLayout&ResTable_config::MASK_SCREENLONG) { - case ResTable_config::SCREENLONG_NO: - printf(" (notlong)"); - break; - case ResTable_config::SCREENLONG_YES: - printf(" (long)"); - break; - } - } - if (type->config.orientation != 0) { - printf(" orient=%d", type->config.orientation); - switch (type->config.orientation) { - case ResTable_config::ORIENTATION_PORT: - printf(" (port)"); - break; - case ResTable_config::ORIENTATION_LAND: - printf(" (land)"); - break; - case ResTable_config::ORIENTATION_SQUARE: - printf(" (square)"); - break; - } - } - if (type->config.uiMode != 0) { - printf(" type=%d", - type->config.uiMode&ResTable_config::MASK_UI_MODE_TYPE); - switch (type->config.uiMode&ResTable_config::MASK_UI_MODE_TYPE) { - case ResTable_config::UI_MODE_TYPE_NORMAL: - printf(" (normal)"); - break; - case ResTable_config::UI_MODE_TYPE_CAR: - printf(" (car)"); - break; - } - printf(" night=%d", - type->config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT); - switch (type->config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT) { - case ResTable_config::UI_MODE_NIGHT_NO: - printf(" (no)"); - break; - case ResTable_config::UI_MODE_NIGHT_YES: - printf(" (yes)"); - break; - } - } - if (dval != 0) { - printf(" density=%s", density); - } - if (type->config.touchscreen != 0) { - printf(" touch=%d", type->config.touchscreen); - switch (type->config.touchscreen) { - case ResTable_config::TOUCHSCREEN_NOTOUCH: - printf(" (notouch)"); - break; - case ResTable_config::TOUCHSCREEN_STYLUS: - printf(" (stylus)"); - break; - case ResTable_config::TOUCHSCREEN_FINGER: - printf(" (finger)"); - break; - } - } - if (type->config.inputFlags != 0) { - printf(" keyhid=%d", type->config.inputFlags&ResTable_config::MASK_KEYSHIDDEN); - switch (type->config.inputFlags&ResTable_config::MASK_KEYSHIDDEN) { - case ResTable_config::KEYSHIDDEN_NO: - printf(" (no)"); - break; - case ResTable_config::KEYSHIDDEN_YES: - printf(" (yes)"); - break; - case ResTable_config::KEYSHIDDEN_SOFT: - printf(" (soft)"); - break; - } - printf(" navhid=%d", type->config.inputFlags&ResTable_config::MASK_NAVHIDDEN); - switch (type->config.inputFlags&ResTable_config::MASK_NAVHIDDEN) { - case ResTable_config::NAVHIDDEN_NO: - printf(" (no)"); - break; - case ResTable_config::NAVHIDDEN_YES: - printf(" (yes)"); - break; - } - } - if (type->config.keyboard != 0) { - printf(" kbd=%d", type->config.keyboard); - switch (type->config.keyboard) { - case ResTable_config::KEYBOARD_NOKEYS: - printf(" (nokeys)"); - break; - case ResTable_config::KEYBOARD_QWERTY: - printf(" (qwerty)"); - break; - case ResTable_config::KEYBOARD_12KEY: - printf(" (12key)"); - break; - } - } - if (type->config.navigation != 0) { - printf(" nav=%d", type->config.navigation); - switch (type->config.navigation) { - case ResTable_config::NAVIGATION_NONAV: - printf(" (nonav)"); - break; - case ResTable_config::NAVIGATION_DPAD: - printf(" (dpad)"); - break; - case ResTable_config::NAVIGATION_TRACKBALL: - printf(" (trackball)"); - break; - case ResTable_config::NAVIGATION_WHEEL: - printf(" (wheel)"); - break; - } - } - if (type->config.screenWidth != 0) { - printf(" w=%d", dtohs(type->config.screenWidth)); - } - if (type->config.screenHeight != 0) { - printf(" h=%d", dtohs(type->config.screenHeight)); - } - if (type->config.smallestScreenWidthDp != 0) { - printf(" swdp=%d", dtohs(type->config.smallestScreenWidthDp)); - } - if (type->config.screenWidthDp != 0) { - printf(" wdp=%d", dtohs(type->config.screenWidthDp)); - } - if (type->config.screenHeightDp != 0) { - printf(" hdp=%d", dtohs(type->config.screenHeightDp)); - } - if (type->config.sdkVersion != 0) { - printf(" sdk=%d", dtohs(type->config.sdkVersion)); - } - if (type->config.minorVersion != 0) { - printf(" mver=%d", dtohs(type->config.minorVersion)); - } - printf("\n"); + String8 configStr = type->config.toString(); + printf(" config %s:\n", configStr.size() > 0 + ? configStr.string() : "(default)"); size_t entryCount = dtohl(type->entryCount); uint32_t entriesStart = dtohl(type->entriesStart); if ((entriesStart&0x3) != 0) { From 81beedda90443e42692d6842169ab953c227902a Mon Sep 17 00:00:00 2001 From: Ramanan Rajeswaran Date: Mon, 13 Feb 2012 13:53:39 -0800 Subject: [PATCH 390/541] DO NOT MERGE Revert "Add xxhdpi; fix ActivityManager.getLauncherLargeIconSize() etc." This reverts commit c1496d2d9a496e4aba817a58ecb9e07fe55cdba5. Change-Id: If60df742b19c925cc99b20d01108b84415b124ad --- include/utils/ResourceTypes.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h index c496da6ba..70bb6b9b3 100644 --- a/include/utils/ResourceTypes.h +++ b/include/utils/ResourceTypes.h @@ -853,8 +853,6 @@ struct ResTable_config DENSITY_MEDIUM = ACONFIGURATION_DENSITY_MEDIUM, DENSITY_TV = ACONFIGURATION_DENSITY_TV, DENSITY_HIGH = ACONFIGURATION_DENSITY_HIGH, - DENSITY_XHIGH = ACONFIGURATION_DENSITY_XHIGH, - DENSITY_XXHIGH = ACONFIGURATION_DENSITY_XXHIGH, DENSITY_NONE = ACONFIGURATION_DENSITY_NONE }; From 413022bc90a4248a6947fbb5a3cbee26a42cec31 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Wed, 25 Jan 2012 15:12:23 -0800 Subject: [PATCH 391/541] Add xxhdpi; fix ActivityManager.getLauncherLargeIconSize() etc. Change-Id: I519d6cdc527a402d93b98df17a64fc1da52ad598 --- include/utils/ResourceTypes.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h index 70bb6b9b3..c496da6ba 100644 --- a/include/utils/ResourceTypes.h +++ b/include/utils/ResourceTypes.h @@ -853,6 +853,8 @@ struct ResTable_config DENSITY_MEDIUM = ACONFIGURATION_DENSITY_MEDIUM, DENSITY_TV = ACONFIGURATION_DENSITY_TV, DENSITY_HIGH = ACONFIGURATION_DENSITY_HIGH, + DENSITY_XHIGH = ACONFIGURATION_DENSITY_XHIGH, + DENSITY_XXHIGH = ACONFIGURATION_DENSITY_XXHIGH, DENSITY_NONE = ACONFIGURATION_DENSITY_NONE }; From 5db7269c89b546e57cfc6715405004c2416a264e Mon Sep 17 00:00:00 2001 From: Ramanan Rajeswaran Date: Mon, 13 Feb 2012 13:53:39 -0800 Subject: [PATCH 392/541] DO NOT MERGE Revert "Add xxhdpi; fix ActivityManager.getLauncherLargeIconSize() etc." This reverts commit c1496d2d9a496e4aba817a58ecb9e07fe55cdba5. Change-Id: If60df742b19c925cc99b20d01108b84415b124ad --- include/utils/ResourceTypes.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h index c496da6ba..70bb6b9b3 100644 --- a/include/utils/ResourceTypes.h +++ b/include/utils/ResourceTypes.h @@ -853,8 +853,6 @@ struct ResTable_config DENSITY_MEDIUM = ACONFIGURATION_DENSITY_MEDIUM, DENSITY_TV = ACONFIGURATION_DENSITY_TV, DENSITY_HIGH = ACONFIGURATION_DENSITY_HIGH, - DENSITY_XHIGH = ACONFIGURATION_DENSITY_XHIGH, - DENSITY_XXHIGH = ACONFIGURATION_DENSITY_XXHIGH, DENSITY_NONE = ACONFIGURATION_DENSITY_NONE }; From 73e263e5d698fdd267168dff497e1f5facb9348d Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Thu, 19 Jan 2012 08:59:58 -0800 Subject: [PATCH 393/541] Update comments We no longer put the filename at start of file. Change-Id: Ic435b159a23105681e3d4a6cb1ac097bc853302e --- include/utils/KeyedVector.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/utils/KeyedVector.h b/include/utils/KeyedVector.h index 6bcdea4ff..fcc3bcfa0 100644 --- a/include/utils/KeyedVector.h +++ b/include/utils/KeyedVector.h @@ -66,7 +66,7 @@ public: ssize_t indexOfKey(const KEY& key) const; /*! - * modifing the array + * modifying the array */ VALUE& editValueFor(const KEY& key); From f21c255fde25e8ffcab62760716009e7182a6510 Mon Sep 17 00:00:00 2001 From: Iliyan Malchev Date: Fri, 17 Feb 2012 12:15:58 -0800 Subject: [PATCH 394/541] libs/utils: replace malloc() + memset() to zero with calloc() Change-Id: I8bdf4360147e51e35c162856c9a859aed6acac34 Signed-off-by: Iliyan Malchev --- libs/utils/BackupHelpers.cpp | 5 +---- libs/utils/ResourceTypes.cpp | 9 +++------ 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/libs/utils/BackupHelpers.cpp b/libs/utils/BackupHelpers.cpp index f77a8917c..214f6d7e9 100644 --- a/libs/utils/BackupHelpers.cpp +++ b/libs/utils/BackupHelpers.cpp @@ -546,7 +546,7 @@ int write_tarfile(const String8& packageName, const String8& domain, // read/write up to this much at a time. const size_t BUFSIZE = 32 * 1024; - char* buf = new char[BUFSIZE]; + char* buf = (char *)calloc(1,BUFSIZE); char* paxHeader = buf + 512; // use a different chunk of it as separate scratch char* paxData = buf + 1024; @@ -556,9 +556,6 @@ int write_tarfile(const String8& packageName, const String8& domain, goto cleanup; } - // Good to go -- first construct the standard tar header at the start of the buffer - memset(buf, 0, BUFSIZE); - // Magic fields for the ustar file format strcat(buf + 257, "ustar"); strcat(buf + 263, "00"); diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp index 3fa562ec6..af2f3a9cc 100644 --- a/libs/utils/ResourceTypes.cpp +++ b/libs/utils/ResourceTypes.cpp @@ -379,8 +379,7 @@ status_t ResStringPool::setTo(const void* data, size_t size, bool copyData) size_t charSize; if (mHeader->flags&ResStringPool_header::UTF8_FLAG) { charSize = sizeof(uint8_t); - mCache = (char16_t**)malloc(sizeof(char16_t**)*mHeader->stringCount); - memset(mCache, 0, sizeof(char16_t**)*mHeader->stringCount); + mCache = (char16_t**)calloc(mHeader->stringCount, sizeof(char16_t**)); } else { charSize = sizeof(char16_t); } @@ -3252,16 +3251,14 @@ ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag, // Bag not found, we need to compute it! if (!grp->bags) { - grp->bags = (bag_set***)malloc(sizeof(bag_set*)*grp->typeCount); + grp->bags = (bag_set***)calloc(grp->typeCount, sizeof(bag_set*)); if (!grp->bags) return NO_MEMORY; - memset(grp->bags, 0, sizeof(bag_set*)*grp->typeCount); } bag_set** typeSet = grp->bags[t]; if (!typeSet) { - typeSet = (bag_set**)malloc(sizeof(bag_set*)*NENTRY); + typeSet = (bag_set**)calloc(NENTRY, sizeof(bag_set*)); if (!typeSet) return NO_MEMORY; - memset(typeSet, 0, sizeof(bag_set*)*NENTRY); grp->bags[t] = typeSet; } From c2b7fb1f9118c1b9c1b9ac2d872dd0fb06ea6502 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Fri, 17 Feb 2012 18:27:36 -0800 Subject: [PATCH 395/541] frameworks/base refactoring. step 2: move libutils headers to their new home: androidfw Change-Id: I14624ba23db92a81f2cb929f104386e1fab293ef --- include/utils/Asset.h | 322 ------ include/utils/AssetDir.h | 145 --- include/utils/AssetManager.h | 373 ------ include/utils/BackupHelpers.h | 169 --- include/utils/ObbFile.h | 145 --- include/utils/ResourceTypes.h | 1590 -------------------------- include/utils/StreamingZipInflater.h | 84 -- include/utils/ZipFileCRO.h | 61 - include/utils/ZipFileRO.h | 262 ----- include/utils/ZipUtils.h | 67 -- libs/utils/Asset.cpp | 18 +- libs/utils/AssetDir.cpp | 2 +- libs/utils/AssetManager.cpp | 20 +- libs/utils/BackupData.cpp | 2 +- libs/utils/BackupHelpers.cpp | 2 +- libs/utils/ObbFile.cpp | 2 +- libs/utils/ResourceTypes.cpp | 4 +- libs/utils/StreamingZipInflater.cpp | 2 +- libs/utils/ZipFileCRO.cpp | 4 +- libs/utils/ZipFileRO.cpp | 2 +- libs/utils/ZipUtils.cpp | 4 +- libs/utils/tests/ObbFile_test.cpp | 2 +- libs/utils/tests/ZipFileRO_test.cpp | 2 +- 23 files changed, 33 insertions(+), 3251 deletions(-) delete mode 100644 include/utils/Asset.h delete mode 100644 include/utils/AssetDir.h delete mode 100644 include/utils/AssetManager.h delete mode 100644 include/utils/BackupHelpers.h delete mode 100644 include/utils/ObbFile.h delete mode 100644 include/utils/ResourceTypes.h delete mode 100644 include/utils/StreamingZipInflater.h delete mode 100644 include/utils/ZipFileCRO.h delete mode 100644 include/utils/ZipFileRO.h delete mode 100644 include/utils/ZipUtils.h diff --git a/include/utils/Asset.h b/include/utils/Asset.h deleted file mode 100644 index 1fe0e063c..000000000 --- a/include/utils/Asset.h +++ /dev/null @@ -1,322 +0,0 @@ -/* - * Copyright (C) 2006 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. - */ - -// -// Class providing access to a read-only asset. Asset objects are NOT -// thread-safe, and should not be shared across threads. -// -#ifndef __LIBS_ASSET_H -#define __LIBS_ASSET_H - -#include -#include - -#include -#include -#include -#include - -namespace android { - -/* - * Instances of this class provide read-only operations on a byte stream. - * - * Access may be optimized for streaming, random, or whole buffer modes. All - * operations are supported regardless of how the file was opened, but some - * things will be less efficient. [pass that in??] - * - * "Asset" is the base class for all types of assets. The classes below - * provide most of the implementation. The AssetManager uses one of the - * static "create" functions defined here to create a new instance. - */ -class Asset { -public: - virtual ~Asset(void); - - static int32_t getGlobalCount(); - static String8 getAssetAllocations(); - - /* used when opening an asset */ - typedef enum AccessMode { - ACCESS_UNKNOWN = 0, - - /* read chunks, and seek forward and backward */ - ACCESS_RANDOM, - - /* read sequentially, with an occasional forward seek */ - ACCESS_STREAMING, - - /* caller plans to ask for a read-only buffer with all data */ - ACCESS_BUFFER, - } AccessMode; - - /* - * Read data from the current offset. Returns the actual number of - * bytes read, 0 on EOF, or -1 on error. - */ - virtual ssize_t read(void* buf, size_t count) = 0; - - /* - * Seek to the specified offset. "whence" uses the same values as - * lseek/fseek. Returns the new position on success, or (off64_t) -1 - * on failure. - */ - virtual off64_t seek(off64_t offset, int whence) = 0; - - /* - * Close the asset, freeing all associated resources. - */ - virtual void close(void) = 0; - - /* - * Get a pointer to a buffer with the entire contents of the file. - */ - virtual const void* getBuffer(bool wordAligned) = 0; - - /* - * Get the total amount of data that can be read. - */ - virtual off64_t getLength(void) const = 0; - - /* - * Get the total amount of data that can be read from the current position. - */ - virtual off64_t getRemainingLength(void) const = 0; - - /* - * Open a new file descriptor that can be used to read this asset. - * Returns -1 if you can not use the file descriptor (for example if the - * asset is compressed). - */ - virtual int openFileDescriptor(off64_t* outStart, off64_t* outLength) const = 0; - - /* - * Return whether this asset's buffer is allocated in RAM (not mmapped). - * Note: not virtual so it is safe to call even when being destroyed. - */ - virtual bool isAllocated(void) const { return false; } - - /* - * Get a string identifying the asset's source. This might be a full - * path, it might be a colon-separated list of identifiers. - * - * This is NOT intended to be used for anything except debug output. - * DO NOT try to parse this or use it to open a file. - */ - const char* getAssetSource(void) const { return mAssetSource.string(); } - -protected: - Asset(void); // constructor; only invoked indirectly - - /* handle common seek() housekeeping */ - off64_t handleSeek(off64_t offset, int whence, off64_t curPosn, off64_t maxPosn); - - /* set the asset source string */ - void setAssetSource(const String8& path) { mAssetSource = path; } - - AccessMode getAccessMode(void) const { return mAccessMode; } - -private: - /* these operations are not implemented */ - Asset(const Asset& src); - Asset& operator=(const Asset& src); - - /* AssetManager needs access to our "create" functions */ - friend class AssetManager; - - /* - * Create the asset from a named file on disk. - */ - static Asset* createFromFile(const char* fileName, AccessMode mode); - - /* - * Create the asset from a named, compressed file on disk (e.g. ".gz"). - */ - static Asset* createFromCompressedFile(const char* fileName, - AccessMode mode); - -#if 0 - /* - * Create the asset from a segment of an open file. This will fail - * if "offset" and "length" don't fit within the bounds of the file. - * - * The asset takes ownership of the file descriptor. - */ - static Asset* createFromFileSegment(int fd, off64_t offset, size_t length, - AccessMode mode); - - /* - * Create from compressed data. "fd" should be seeked to the start of - * the compressed data. This could be inside a gzip file or part of a - * Zip archive. - * - * The asset takes ownership of the file descriptor. - * - * This may not verify the validity of the compressed data until first - * use. - */ - static Asset* createFromCompressedData(int fd, off64_t offset, - int compressionMethod, size_t compressedLength, - size_t uncompressedLength, AccessMode mode); -#endif - - /* - * Create the asset from a memory-mapped file segment. - * - * The asset takes ownership of the FileMap. - */ - static Asset* createFromUncompressedMap(FileMap* dataMap, AccessMode mode); - - /* - * Create the asset from a memory-mapped file segment with compressed - * data. "method" is a Zip archive compression method constant. - * - * The asset takes ownership of the FileMap. - */ - static Asset* createFromCompressedMap(FileMap* dataMap, int method, - size_t uncompressedLen, AccessMode mode); - - - /* - * Create from a reference-counted chunk of shared memory. - */ - // TODO - - AccessMode mAccessMode; // how the asset was opened - String8 mAssetSource; // debug string - - Asset* mNext; // linked list. - Asset* mPrev; -}; - - -/* - * =========================================================================== - * - * Innards follow. Do not use these classes directly. - */ - -/* - * An asset based on an uncompressed file on disk. It may encompass the - * entire file or just a piece of it. Access is through fread/fseek. - */ -class _FileAsset : public Asset { -public: - _FileAsset(void); - virtual ~_FileAsset(void); - - /* - * Use a piece of an already-open file. - * - * On success, the object takes ownership of "fd". - */ - status_t openChunk(const char* fileName, int fd, off64_t offset, size_t length); - - /* - * Use a memory-mapped region. - * - * On success, the object takes ownership of "dataMap". - */ - status_t openChunk(FileMap* dataMap); - - /* - * Standard Asset interfaces. - */ - virtual ssize_t read(void* buf, size_t count); - virtual off64_t seek(off64_t offset, int whence); - virtual void close(void); - virtual const void* getBuffer(bool wordAligned); - virtual off64_t getLength(void) const { return mLength; } - virtual off64_t getRemainingLength(void) const { return mLength-mOffset; } - virtual int openFileDescriptor(off64_t* outStart, off64_t* outLength) const; - virtual bool isAllocated(void) const { return mBuf != NULL; } - -private: - off64_t mStart; // absolute file offset of start of chunk - off64_t mLength; // length of the chunk - off64_t mOffset; // current local offset, 0 == mStart - FILE* mFp; // for read/seek - char* mFileName; // for opening - - /* - * To support getBuffer() we either need to read the entire thing into - * a buffer or memory-map it. For small files it's probably best to - * just read them in. - */ - enum { kReadVsMapThreshold = 4096 }; - - FileMap* mMap; // for memory map - unsigned char* mBuf; // for read - - const void* ensureAlignment(FileMap* map); -}; - - -/* - * An asset based on compressed data in a file. - */ -class _CompressedAsset : public Asset { -public: - _CompressedAsset(void); - virtual ~_CompressedAsset(void); - - /* - * Use a piece of an already-open file. - * - * On success, the object takes ownership of "fd". - */ - status_t openChunk(int fd, off64_t offset, int compressionMethod, - size_t uncompressedLen, size_t compressedLen); - - /* - * Use a memory-mapped region. - * - * On success, the object takes ownership of "fd". - */ - status_t openChunk(FileMap* dataMap, int compressionMethod, - size_t uncompressedLen); - - /* - * Standard Asset interfaces. - */ - virtual ssize_t read(void* buf, size_t count); - virtual off64_t seek(off64_t offset, int whence); - virtual void close(void); - virtual const void* getBuffer(bool wordAligned); - virtual off64_t getLength(void) const { return mUncompressedLen; } - virtual off64_t getRemainingLength(void) const { return mUncompressedLen-mOffset; } - virtual int openFileDescriptor(off64_t* outStart, off64_t* outLength) const { return -1; } - virtual bool isAllocated(void) const { return mBuf != NULL; } - -private: - off64_t mStart; // offset to start of compressed data - off64_t mCompressedLen; // length of the compressed data - off64_t mUncompressedLen; // length of the uncompressed data - off64_t mOffset; // current offset, 0 == start of uncomp data - - FileMap* mMap; // for memory-mapped input - int mFd; // for file input - - class StreamingZipInflater* mZipInflater; // for streaming large compressed assets - - unsigned char* mBuf; // for getBuffer() -}; - -// need: shared mmap version? - -}; // namespace android - -#endif // __LIBS_ASSET_H diff --git a/include/utils/AssetDir.h b/include/utils/AssetDir.h deleted file mode 100644 index abf8a3595..000000000 --- a/include/utils/AssetDir.h +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright (C) 2006 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. - */ - -// -// Access a chunk of the asset hierarchy as if it were a single directory. -// -#ifndef __LIBS_ASSETDIR_H -#define __LIBS_ASSETDIR_H - -#include -#include -#include -#include -#include - -namespace android { - -/* - * This provides vector-style access to a directory. We do this rather - * than modeling opendir/readdir access because it's simpler and the - * nature of the operation requires us to have all data on hand anyway. - * - * The list of files will be sorted in ascending order by ASCII value. - * - * The contents are populated by our friend, the AssetManager. - */ -class AssetDir { -public: - AssetDir(void) - : mFileInfo(NULL) - {} - virtual ~AssetDir(void) { - delete mFileInfo; - } - - /* - * Vector-style access. - */ - size_t getFileCount(void) { return mFileInfo->size(); } - const String8& getFileName(int idx) { - return mFileInfo->itemAt(idx).getFileName(); - } - const String8& getSourceName(int idx) { - return mFileInfo->itemAt(idx).getSourceName(); - } - - /* - * Get the type of a file (usually regular or directory). - */ - FileType getFileType(int idx) { - return mFileInfo->itemAt(idx).getFileType(); - } - -private: - /* these operations are not implemented */ - AssetDir(const AssetDir& src); - const AssetDir& operator=(const AssetDir& src); - - friend class AssetManager; - - /* - * This holds information about files in the asset hierarchy. - */ - class FileInfo { - public: - FileInfo(void) {} - FileInfo(const String8& path) // useful for e.g. svect.indexOf - : mFileName(path), mFileType(kFileTypeUnknown) - {} - ~FileInfo(void) {} - FileInfo(const FileInfo& src) { - copyMembers(src); - } - const FileInfo& operator= (const FileInfo& src) { - if (this != &src) - copyMembers(src); - return *this; - } - - void copyMembers(const FileInfo& src) { - mFileName = src.mFileName; - mFileType = src.mFileType; - mSourceName = src.mSourceName; - } - - /* need this for SortedVector; must compare only on file name */ - bool operator< (const FileInfo& rhs) const { - return mFileName < rhs.mFileName; - } - - /* used by AssetManager */ - bool operator== (const FileInfo& rhs) const { - return mFileName == rhs.mFileName; - } - - void set(const String8& path, FileType type) { - mFileName = path; - mFileType = type; - } - - const String8& getFileName(void) const { return mFileName; } - void setFileName(const String8& path) { mFileName = path; } - - FileType getFileType(void) const { return mFileType; } - void setFileType(FileType type) { mFileType = type; } - - const String8& getSourceName(void) const { return mSourceName; } - void setSourceName(const String8& path) { mSourceName = path; } - - /* - * Handy utility for finding an entry in a sorted vector of FileInfo. - * Returns the index of the matching entry, or -1 if none found. - */ - static int findEntry(const SortedVector* pVector, - const String8& fileName); - - private: - String8 mFileName; // filename only - FileType mFileType; // regular, directory, etc - - String8 mSourceName; // currently debug-only - }; - - /* AssetManager uses this to initialize us */ - void setFileList(SortedVector* list) { mFileInfo = list; } - - SortedVector* mFileInfo; -}; - -}; // namespace android - -#endif // __LIBS_ASSETDIR_H diff --git a/include/utils/AssetManager.h b/include/utils/AssetManager.h deleted file mode 100644 index a8c7ddbd7..000000000 --- a/include/utils/AssetManager.h +++ /dev/null @@ -1,373 +0,0 @@ -/* - * Copyright (C) 2006 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. - */ - -// -// Asset management class. AssetManager objects are thread-safe. -// -#ifndef __LIBS_ASSETMANAGER_H -#define __LIBS_ASSETMANAGER_H - -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * Native-app access is via the opaque typedef struct AAssetManager in the C namespace. - */ -#ifdef __cplusplus -extern "C" { -#endif - -struct AAssetManager { }; - -#ifdef __cplusplus -}; -#endif - - -/* - * Now the proper C++ android-namespace definitions - */ - -namespace android { - -class Asset; // fwd decl for things that include Asset.h first -class ResTable; -struct ResTable_config; - -/* - * Every application that uses assets needs one instance of this. A - * single instance may be shared across multiple threads, and a single - * thread may have more than one instance (the latter is discouraged). - * - * The purpose of the AssetManager is to create Asset objects. To do - * this efficiently it may cache information about the locations of - * files it has seen. This can be controlled with the "cacheMode" - * argument. - * - * The asset hierarchy may be examined like a filesystem, using - * AssetDir objects to peruse a single directory. - */ -class AssetManager : public AAssetManager { -public: - typedef enum CacheMode { - CACHE_UNKNOWN = 0, - CACHE_OFF, // don't try to cache file locations - CACHE_DEFER, // construct cache as pieces are needed - //CACHE_SCAN, // scan full(!) asset hierarchy at init() time - } CacheMode; - - AssetManager(CacheMode cacheMode = CACHE_OFF); - virtual ~AssetManager(void); - - static int32_t getGlobalCount(); - - /* - * Add a new source for assets. This can be called multiple times to - * look in multiple places for assets. It can be either a directory (for - * finding assets as raw files on the disk) or a ZIP file. This newly - * added asset path will be examined first when searching for assets, - * before any that were previously added. - * - * Returns "true" on success, "false" on failure. If 'cookie' is non-NULL, - * then on success, *cookie is set to the value corresponding to the - * newly-added asset source. - */ - bool addAssetPath(const String8& path, void** cookie); - - /* - * Convenience for adding the standard system assets. Uses the - * ANDROID_ROOT environment variable to find them. - */ - bool addDefaultAssets(); - - /* - * Iterate over the asset paths in this manager. (Previously - * added via addAssetPath() and addDefaultAssets().) On first call, - * 'cookie' must be NULL, resulting in the first cookie being returned. - * Each next cookie will be returned there-after, until NULL indicating - * the end has been reached. - */ - void* nextAssetPath(void* cookie) const; - - /* - * Return an asset path in the manager. 'which' must be between 0 and - * countAssetPaths(). - */ - String8 getAssetPath(void* cookie) const; - - /* - * Set the current locale and vendor. The locale can change during - * the lifetime of an AssetManager if the user updates the device's - * language setting. The vendor is less likely to change. - * - * Pass in NULL to indicate no preference. - */ - void setLocale(const char* locale); - void setVendor(const char* vendor); - - /* - * Choose screen orientation for resources values returned. - */ - void setConfiguration(const ResTable_config& config, const char* locale = NULL); - - void getConfiguration(ResTable_config* outConfig) const; - - typedef Asset::AccessMode AccessMode; // typing shortcut - - /* - * Open an asset. - * - * This will search through locale-specific and vendor-specific - * directories and packages to find the file. - * - * The object returned does not depend on the AssetManager. It should - * be freed by calling Asset::close(). - */ - Asset* open(const char* fileName, AccessMode mode); - - /* - * Open a non-asset file as an asset. - * - * This is for opening files that are included in an asset package - * but aren't assets. These sit outside the usual "locale/vendor" - * path hierarchy, and will not be seen by "AssetDir" or included - * in our filename cache. - */ - Asset* openNonAsset(const char* fileName, AccessMode mode); - - /* - * Explicit non-asset file. The file explicitly named by the cookie (the - * resource set to look in) and fileName will be opened and returned. - */ - Asset* openNonAsset(void* cookie, const char* fileName, AccessMode mode); - - /* - * Open a directory within the asset hierarchy. - * - * The contents of the directory are an amalgam of vendor-specific, - * locale-specific, and generic assets stored loosely or in asset - * packages. Depending on the cache setting and previous accesses, - * this call may incur significant disk overhead. - * - * To open the top-level directory, pass in "". - */ - AssetDir* openDir(const char* dirName); - - /* - * Open a directory within a particular path of the asset manager. - * - * The contents of the directory are an amalgam of vendor-specific, - * locale-specific, and generic assets stored loosely or in asset - * packages. Depending on the cache setting and previous accesses, - * this call may incur significant disk overhead. - * - * To open the top-level directory, pass in "". - */ - AssetDir* openNonAssetDir(void* cookie, const char* dirName); - - /* - * Get the type of a file in the asset hierarchy. They will either - * be "regular" or "directory". [Currently only works for "regular".] - * - * Can also be used as a quick test for existence of a file. - */ - FileType getFileType(const char* fileName); - - /* - * Return the complete resource table to find things in the package. - */ - const ResTable& getResources(bool required = true) const; - - /* - * Discard cached filename information. This only needs to be called - * if somebody has updated the set of "loose" files, and we want to - * discard our cached notion of what's where. - */ - void purge(void) { purgeFileNameCacheLocked(); } - - /* - * Return true if the files this AssetManager references are all - * up-to-date (have not been changed since it was created). If false - * is returned, you will need to create a new AssetManager to get - * the current data. - */ - bool isUpToDate(); - - /** - * Get the known locales for this asset manager object. - */ - void getLocales(Vector* locales) const; - -private: - struct asset_path - { - String8 path; - FileType type; - String8 idmap; - }; - - Asset* openInPathLocked(const char* fileName, AccessMode mode, - const asset_path& path); - Asset* openNonAssetInPathLocked(const char* fileName, AccessMode mode, - const asset_path& path); - Asset* openInLocaleVendorLocked(const char* fileName, AccessMode mode, - const asset_path& path, const char* locale, const char* vendor); - String8 createPathNameLocked(const asset_path& path, const char* locale, - const char* vendor); - String8 createPathNameLocked(const asset_path& path, const char* rootDir); - String8 createZipSourceNameLocked(const String8& zipFileName, - const String8& dirName, const String8& fileName); - - ZipFileRO* getZipFileLocked(const asset_path& path); - Asset* openAssetFromFileLocked(const String8& fileName, AccessMode mode); - Asset* openAssetFromZipLocked(const ZipFileRO* pZipFile, - const ZipEntryRO entry, AccessMode mode, const String8& entryName); - - bool scanAndMergeDirLocked(SortedVector* pMergedInfo, - const asset_path& path, const char* rootDir, const char* dirName); - SortedVector* scanDirLocked(const String8& path); - bool scanAndMergeZipLocked(SortedVector* pMergedInfo, - const asset_path& path, const char* rootDir, const char* dirName); - void mergeInfoLocked(SortedVector* pMergedInfo, - const SortedVector* pContents); - - void loadFileNameCacheLocked(void); - void fncScanLocked(SortedVector* pMergedInfo, - const char* dirName); - bool fncScanAndMergeDirLocked( - SortedVector* pMergedInfo, - const asset_path& path, const char* locale, const char* vendor, - const char* dirName); - void purgeFileNameCacheLocked(void); - - const ResTable* getResTable(bool required = true) const; - void setLocaleLocked(const char* locale); - void updateResourceParamsLocked() const; - - bool createIdmapFileLocked(const String8& originalPath, const String8& overlayPath, - const String8& idmapPath); - - bool isIdmapStaleLocked(const String8& originalPath, const String8& overlayPath, - const String8& idmapPath); - - Asset* openIdmapLocked(const struct asset_path& ap) const; - - bool getZipEntryCrcLocked(const String8& zipPath, const char* entryFilename, uint32_t* pCrc); - - class SharedZip : public RefBase { - public: - static sp get(const String8& path); - - ZipFileRO* getZip(); - - Asset* getResourceTableAsset(); - Asset* setResourceTableAsset(Asset* asset); - - ResTable* getResourceTable(); - ResTable* setResourceTable(ResTable* res); - - bool isUpToDate(); - - protected: - ~SharedZip(); - - private: - SharedZip(const String8& path, time_t modWhen); - SharedZip(); // <-- not implemented - - String8 mPath; - ZipFileRO* mZipFile; - time_t mModWhen; - - Asset* mResourceTableAsset; - ResTable* mResourceTable; - - static Mutex gLock; - static DefaultKeyedVector > gOpen; - }; - - /* - * Manage a set of Zip files. For each file we need a pointer to the - * ZipFile and a time_t with the file's modification date. - * - * We currently only have two zip files (current app, "common" app). - * (This was originally written for 8, based on app/locale/vendor.) - */ - class ZipSet { - public: - ZipSet(void); - ~ZipSet(void); - - /* - * Return a ZipFileRO structure for a ZipFileRO with the specified - * parameters. - */ - ZipFileRO* getZip(const String8& path); - - Asset* getZipResourceTableAsset(const String8& path); - Asset* setZipResourceTableAsset(const String8& path, Asset* asset); - - ResTable* getZipResourceTable(const String8& path); - ResTable* setZipResourceTable(const String8& path, ResTable* res); - - // generate path, e.g. "common/en-US-noogle.zip" - static String8 getPathName(const char* path); - - bool isUpToDate(); - - private: - void closeZip(int idx); - - int getIndex(const String8& zip) const; - mutable Vector mZipPath; - mutable Vector > mZipFile; - }; - - // Protect all internal state. - mutable Mutex mLock; - - ZipSet mZipSet; - - Vector mAssetPaths; - char* mLocale; - char* mVendor; - - mutable ResTable* mResources; - ResTable_config* mConfig; - - /* - * Cached data for "loose" files. This lets us avoid poking at the - * filesystem when searching for loose assets. Each entry is the - * "extended partial" path, e.g. "default/default/foo/bar.txt". The - * full set of files is present, including ".EXCLUDE" entries. - * - * We do not cache directory names. We don't retain the ".gz", - * because to our clients "foo" and "foo.gz" both look like "foo". - */ - CacheMode mCacheMode; // is the cache enabled? - bool mCacheValid; // clear when locale or vendor changes - SortedVector mCache; -}; - -}; // namespace android - -#endif // __LIBS_ASSETMANAGER_H diff --git a/include/utils/BackupHelpers.h b/include/utils/BackupHelpers.h deleted file mode 100644 index 1bb04a712..000000000 --- a/include/utils/BackupHelpers.h +++ /dev/null @@ -1,169 +0,0 @@ -/* - * 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 _UTILS_BACKUP_HELPERS_H -#define _UTILS_BACKUP_HELPERS_H - -#include -#include -#include - -namespace android { - -enum { - BACKUP_HEADER_ENTITY_V1 = 0x61746144, // Data (little endian) -}; - -typedef struct { - int type; // BACKUP_HEADER_ENTITY_V1 - int keyLen; // length of the key name, not including the null terminator - int dataSize; // size of the data, not including the padding, -1 means delete -} entity_header_v1; - -struct SnapshotHeader { - int magic0; - int fileCount; - int magic1; - int totalSize; -}; - -struct FileState { - int modTime_sec; - int modTime_nsec; - int mode; - int size; - int crc32; - int nameLen; -}; - -struct FileRec { - String8 file; - bool deleted; - FileState s; -}; - - -/** - * Writes the data. - * - * If an error occurs, it poisons this object and all write calls will fail - * with the error that occurred. - */ -class BackupDataWriter -{ -public: - BackupDataWriter(int fd); - // does not close fd - ~BackupDataWriter(); - - status_t WriteEntityHeader(const String8& key, size_t dataSize); - - /* Note: WriteEntityData will write arbitrary data into the file without - * validation or a previously-supplied header. The full backup implementation - * uses it this way to generate a controlled binary stream that is not - * entity-structured. If the implementation here is changed, either this - * use case must remain valid, or the full backup implementation should be - * adjusted to use some other appropriate mechanism. - */ - status_t WriteEntityData(const void* data, size_t size); - - void SetKeyPrefix(const String8& keyPrefix); - -private: - explicit BackupDataWriter(); - status_t write_padding_for(int n); - - int m_fd; - status_t m_status; - ssize_t m_pos; - int m_entityCount; - String8 m_keyPrefix; -}; - -/** - * Reads the data. - * - * If an error occurs, it poisons this object and all write calls will fail - * with the error that occurred. - */ -class BackupDataReader -{ -public: - BackupDataReader(int fd); - // does not close fd - ~BackupDataReader(); - - status_t Status(); - status_t ReadNextHeader(bool* done, int* type); - - bool HasEntities(); - status_t ReadEntityHeader(String8* key, size_t* dataSize); - status_t SkipEntityData(); // must be called with the pointer at the beginning of the data. - ssize_t ReadEntityData(void* data, size_t size); - -private: - explicit BackupDataReader(); - status_t skip_padding(); - - int m_fd; - bool m_done; - status_t m_status; - ssize_t m_pos; - ssize_t m_dataEndPos; - int m_entityCount; - union { - int type; - entity_header_v1 entity; - } m_header; - String8 m_key; -}; - -int back_up_files(int oldSnapshotFD, BackupDataWriter* dataStream, int newSnapshotFD, - char const* const* files, char const* const *keys, int fileCount); - -int write_tarfile(const String8& packageName, const String8& domain, - const String8& rootPath, const String8& filePath, BackupDataWriter* outputStream); - -class RestoreHelperBase -{ -public: - RestoreHelperBase(); - ~RestoreHelperBase(); - - status_t WriteFile(const String8& filename, BackupDataReader* in); - status_t WriteSnapshot(int fd); - -private: - void* m_buf; - bool m_loggedUnknownMetadata; - KeyedVector m_files; -}; - -#define TEST_BACKUP_HELPERS 1 - -#if TEST_BACKUP_HELPERS -int backup_helper_test_empty(); -int backup_helper_test_four(); -int backup_helper_test_files(); -int backup_helper_test_null_base(); -int backup_helper_test_missing_file(); -int backup_helper_test_data_writer(); -int backup_helper_test_data_reader(); -#endif - -} // namespace android - -#endif // _UTILS_BACKUP_HELPERS_H diff --git a/include/utils/ObbFile.h b/include/utils/ObbFile.h deleted file mode 100644 index 47559cdd0..000000000 --- a/include/utils/ObbFile.h +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright (C) 2010 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 OBBFILE_H_ -#define OBBFILE_H_ - -#include -#include - -#include -#include - -namespace android { - -// OBB flags (bit 0) -#define OBB_OVERLAY (1 << 0) -#define OBB_SALTED (1 << 1) - -class ObbFile : public RefBase { -protected: - virtual ~ObbFile(); - -public: - ObbFile(); - - bool readFrom(const char* filename); - bool readFrom(int fd); - bool writeTo(const char* filename); - bool writeTo(int fd); - bool removeFrom(const char* filename); - bool removeFrom(int fd); - - const char* getFileName() const { - return mFileName; - } - - const String8 getPackageName() const { - return mPackageName; - } - - void setPackageName(String8 packageName) { - mPackageName = packageName; - } - - int32_t getVersion() const { - return mVersion; - } - - void setVersion(int32_t version) { - mVersion = version; - } - - int32_t getFlags() const { - return mFlags; - } - - void setFlags(int32_t flags) { - mFlags = flags; - } - - const unsigned char* getSalt(size_t* length) const { - if ((mFlags & OBB_SALTED) == 0) { - *length = 0; - return NULL; - } - - *length = sizeof(mSalt); - return mSalt; - } - - bool setSalt(const unsigned char* salt, size_t length) { - if (length != sizeof(mSalt)) { - return false; - } - - memcpy(mSalt, salt, sizeof(mSalt)); - mFlags |= OBB_SALTED; - return true; - } - - bool isOverlay() { - return (mFlags & OBB_OVERLAY) == OBB_OVERLAY; - } - - void setOverlay(bool overlay) { - if (overlay) { - mFlags |= OBB_OVERLAY; - } else { - mFlags &= ~OBB_OVERLAY; - } - } - - static inline uint32_t get4LE(const unsigned char* buf) { - return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); - } - - static inline void put4LE(unsigned char* buf, uint32_t val) { - buf[0] = val & 0xFF; - buf[1] = (val >> 8) & 0xFF; - buf[2] = (val >> 16) & 0xFF; - buf[3] = (val >> 24) & 0xFF; - } - -private: - /* Package name this ObbFile is associated with */ - String8 mPackageName; - - /* Package version this ObbFile is associated with */ - int32_t mVersion; - - /* Flags for this OBB type. */ - int32_t mFlags; - - /* Whether the file is salted. */ - bool mSalted; - - /* The encryption salt. */ - unsigned char mSalt[8]; - - const char* mFileName; - - size_t mFileSize; - - size_t mFooterStart; - - unsigned char* mReadBuf; - - bool parseObbFile(int fd); -}; - -} -#endif /* OBBFILE_H_ */ diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h deleted file mode 100644 index 70bb6b9b3..000000000 --- a/include/utils/ResourceTypes.h +++ /dev/null @@ -1,1590 +0,0 @@ -/* - * Copyright (C) 2005 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. - */ - -// -// Definitions of resource data structures. -// -#ifndef _LIBS_UTILS_RESOURCE_TYPES_H -#define _LIBS_UTILS_RESOURCE_TYPES_H - -#include -#include -#include -#include -#include - -#include - -#include -#include - -#include - -namespace android { - -/** ******************************************************************** - * PNG Extensions - * - * New private chunks that may be placed in PNG images. - * - *********************************************************************** */ - -/** - * This chunk specifies how to split an image into segments for - * scaling. - * - * There are J horizontal and K vertical segments. These segments divide - * the image into J*K regions as follows (where J=4 and K=3): - * - * F0 S0 F1 S1 - * +-----+----+------+-------+ - * S2| 0 | 1 | 2 | 3 | - * +-----+----+------+-------+ - * | | | | | - * | | | | | - * F2| 4 | 5 | 6 | 7 | - * | | | | | - * | | | | | - * +-----+----+------+-------+ - * S3| 8 | 9 | 10 | 11 | - * +-----+----+------+-------+ - * - * Each horizontal and vertical segment is considered to by either - * stretchable (marked by the Sx labels) or fixed (marked by the Fy - * labels), in the horizontal or vertical axis, respectively. In the - * above example, the first is horizontal segment (F0) is fixed, the - * next is stretchable and then they continue to alternate. Note that - * the segment list for each axis can begin or end with a stretchable - * or fixed segment. - * - * The relative sizes of the stretchy segments indicates the relative - * amount of stretchiness of the regions bordered by the segments. For - * example, regions 3, 7 and 11 above will take up more horizontal space - * than regions 1, 5 and 9 since the horizontal segment associated with - * the first set of regions is larger than the other set of regions. The - * ratios of the amount of horizontal (or vertical) space taken by any - * two stretchable slices is exactly the ratio of their corresponding - * segment lengths. - * - * xDivs and yDivs point to arrays of horizontal and vertical pixel - * indices. The first pair of Divs (in either array) indicate the - * starting and ending points of the first stretchable segment in that - * axis. The next pair specifies the next stretchable segment, etc. So - * in the above example xDiv[0] and xDiv[1] specify the horizontal - * coordinates for the regions labeled 1, 5 and 9. xDiv[2] and - * xDiv[3] specify the coordinates for regions 3, 7 and 11. Note that - * the leftmost slices always start at x=0 and the rightmost slices - * always end at the end of the image. So, for example, the regions 0, - * 4 and 8 (which are fixed along the X axis) start at x value 0 and - * go to xDiv[0] and slices 2, 6 and 10 start at xDiv[1] and end at - * xDiv[2]. - * - * The array pointed to by the colors field lists contains hints for - * each of the regions. They are ordered according left-to-right and - * top-to-bottom as indicated above. For each segment that is a solid - * color the array entry will contain that color value; otherwise it - * will contain NO_COLOR. Segments that are completely transparent - * will always have the value TRANSPARENT_COLOR. - * - * The PNG chunk type is "npTc". - */ -struct Res_png_9patch -{ - Res_png_9patch() : wasDeserialized(false), xDivs(NULL), - yDivs(NULL), colors(NULL) { } - - int8_t wasDeserialized; - int8_t numXDivs; - int8_t numYDivs; - int8_t numColors; - - // These tell where the next section of a patch starts. - // For example, the first patch includes the pixels from - // 0 to xDivs[0]-1 and the second patch includes the pixels - // from xDivs[0] to xDivs[1]-1. - // Note: allocation/free of these pointers is left to the caller. - int32_t* xDivs; - int32_t* yDivs; - - int32_t paddingLeft, paddingRight; - int32_t paddingTop, paddingBottom; - - enum { - // The 9 patch segment is not a solid color. - NO_COLOR = 0x00000001, - - // The 9 patch segment is completely transparent. - TRANSPARENT_COLOR = 0x00000000 - }; - // Note: allocation/free of this pointer is left to the caller. - uint32_t* colors; - - // Convert data from device representation to PNG file representation. - void deviceToFile(); - // Convert data from PNG file representation to device representation. - void fileToDevice(); - // Serialize/Marshall the patch data into a newly malloc-ed block - void* serialize(); - // Serialize/Marshall the patch data - void serialize(void* outData); - // Deserialize/Unmarshall the patch data - static Res_png_9patch* deserialize(const void* data); - // Compute the size of the serialized data structure - size_t serializedSize(); -}; - -/** ******************************************************************** - * Base Types - * - * These are standard types that are shared between multiple specific - * resource types. - * - *********************************************************************** */ - -/** - * Header that appears at the front of every data chunk in a resource. - */ -struct ResChunk_header -{ - // Type identifier for this chunk. The meaning of this value depends - // on the containing chunk. - uint16_t type; - - // Size of the chunk header (in bytes). Adding this value to - // the address of the chunk allows you to find its associated data - // (if any). - uint16_t headerSize; - - // Total size of this chunk (in bytes). This is the chunkSize plus - // the size of any data associated with the chunk. Adding this value - // to the chunk allows you to completely skip its contents (including - // any child chunks). If this value is the same as chunkSize, there is - // no data associated with the chunk. - uint32_t size; -}; - -enum { - RES_NULL_TYPE = 0x0000, - RES_STRING_POOL_TYPE = 0x0001, - RES_TABLE_TYPE = 0x0002, - RES_XML_TYPE = 0x0003, - - // Chunk types in RES_XML_TYPE - RES_XML_FIRST_CHUNK_TYPE = 0x0100, - RES_XML_START_NAMESPACE_TYPE= 0x0100, - RES_XML_END_NAMESPACE_TYPE = 0x0101, - RES_XML_START_ELEMENT_TYPE = 0x0102, - RES_XML_END_ELEMENT_TYPE = 0x0103, - RES_XML_CDATA_TYPE = 0x0104, - RES_XML_LAST_CHUNK_TYPE = 0x017f, - // This contains a uint32_t array mapping strings in the string - // pool back to resource identifiers. It is optional. - RES_XML_RESOURCE_MAP_TYPE = 0x0180, - - // Chunk types in RES_TABLE_TYPE - RES_TABLE_PACKAGE_TYPE = 0x0200, - RES_TABLE_TYPE_TYPE = 0x0201, - RES_TABLE_TYPE_SPEC_TYPE = 0x0202 -}; - -/** - * Macros for building/splitting resource identifiers. - */ -#define Res_VALIDID(resid) (resid != 0) -#define Res_CHECKID(resid) ((resid&0xFFFF0000) != 0) -#define Res_MAKEID(package, type, entry) \ - (((package+1)<<24) | (((type+1)&0xFF)<<16) | (entry&0xFFFF)) -#define Res_GETPACKAGE(id) ((id>>24)-1) -#define Res_GETTYPE(id) (((id>>16)&0xFF)-1) -#define Res_GETENTRY(id) (id&0xFFFF) - -#define Res_INTERNALID(resid) ((resid&0xFFFF0000) != 0 && (resid&0xFF0000) == 0) -#define Res_MAKEINTERNAL(entry) (0x01000000 | (entry&0xFFFF)) -#define Res_MAKEARRAY(entry) (0x02000000 | (entry&0xFFFF)) - -#define Res_MAXPACKAGE 255 - -/** - * Representation of a value in a resource, supplying type - * information. - */ -struct Res_value -{ - // Number of bytes in this structure. - uint16_t size; - - // Always set to 0. - uint8_t res0; - - // Type of the data value. - enum { - // Contains no data. - TYPE_NULL = 0x00, - // The 'data' holds a ResTable_ref, a reference to another resource - // table entry. - TYPE_REFERENCE = 0x01, - // The 'data' holds an attribute resource identifier. - TYPE_ATTRIBUTE = 0x02, - // The 'data' holds an index into the containing resource table's - // global value string pool. - TYPE_STRING = 0x03, - // The 'data' holds a single-precision floating point number. - TYPE_FLOAT = 0x04, - // The 'data' holds a complex number encoding a dimension value, - // such as "100in". - TYPE_DIMENSION = 0x05, - // The 'data' holds a complex number encoding a fraction of a - // container. - TYPE_FRACTION = 0x06, - - // Beginning of integer flavors... - TYPE_FIRST_INT = 0x10, - - // The 'data' is a raw integer value of the form n..n. - TYPE_INT_DEC = 0x10, - // The 'data' is a raw integer value of the form 0xn..n. - TYPE_INT_HEX = 0x11, - // The 'data' is either 0 or 1, for input "false" or "true" respectively. - TYPE_INT_BOOLEAN = 0x12, - - // Beginning of color integer flavors... - TYPE_FIRST_COLOR_INT = 0x1c, - - // The 'data' is a raw integer value of the form #aarrggbb. - TYPE_INT_COLOR_ARGB8 = 0x1c, - // The 'data' is a raw integer value of the form #rrggbb. - TYPE_INT_COLOR_RGB8 = 0x1d, - // The 'data' is a raw integer value of the form #argb. - TYPE_INT_COLOR_ARGB4 = 0x1e, - // The 'data' is a raw integer value of the form #rgb. - TYPE_INT_COLOR_RGB4 = 0x1f, - - // ...end of integer flavors. - TYPE_LAST_COLOR_INT = 0x1f, - - // ...end of integer flavors. - TYPE_LAST_INT = 0x1f - }; - uint8_t dataType; - - // Structure of complex data values (TYPE_UNIT and TYPE_FRACTION) - enum { - // Where the unit type information is. This gives us 16 possible - // types, as defined below. - COMPLEX_UNIT_SHIFT = 0, - COMPLEX_UNIT_MASK = 0xf, - - // TYPE_DIMENSION: Value is raw pixels. - COMPLEX_UNIT_PX = 0, - // TYPE_DIMENSION: Value is Device Independent Pixels. - COMPLEX_UNIT_DIP = 1, - // TYPE_DIMENSION: Value is a Scaled device independent Pixels. - COMPLEX_UNIT_SP = 2, - // TYPE_DIMENSION: Value is in points. - COMPLEX_UNIT_PT = 3, - // TYPE_DIMENSION: Value is in inches. - COMPLEX_UNIT_IN = 4, - // TYPE_DIMENSION: Value is in millimeters. - COMPLEX_UNIT_MM = 5, - - // TYPE_FRACTION: A basic fraction of the overall size. - COMPLEX_UNIT_FRACTION = 0, - // TYPE_FRACTION: A fraction of the parent size. - COMPLEX_UNIT_FRACTION_PARENT = 1, - - // Where the radix information is, telling where the decimal place - // appears in the mantissa. This give us 4 possible fixed point - // representations as defined below. - COMPLEX_RADIX_SHIFT = 4, - COMPLEX_RADIX_MASK = 0x3, - - // The mantissa is an integral number -- i.e., 0xnnnnnn.0 - COMPLEX_RADIX_23p0 = 0, - // The mantissa magnitude is 16 bits -- i.e, 0xnnnn.nn - COMPLEX_RADIX_16p7 = 1, - // The mantissa magnitude is 8 bits -- i.e, 0xnn.nnnn - COMPLEX_RADIX_8p15 = 2, - // The mantissa magnitude is 0 bits -- i.e, 0x0.nnnnnn - COMPLEX_RADIX_0p23 = 3, - - // Where the actual value is. This gives us 23 bits of - // precision. The top bit is the sign. - COMPLEX_MANTISSA_SHIFT = 8, - COMPLEX_MANTISSA_MASK = 0xffffff - }; - - // The data for this item, as interpreted according to dataType. - uint32_t data; - - void copyFrom_dtoh(const Res_value& src); -}; - -/** - * This is a reference to a unique entry (a ResTable_entry structure) - * in a resource table. The value is structured as: 0xpptteeee, - * where pp is the package index, tt is the type index in that - * package, and eeee is the entry index in that type. The package - * and type values start at 1 for the first item, to help catch cases - * where they have not been supplied. - */ -struct ResTable_ref -{ - uint32_t ident; -}; - -/** - * Reference to a string in a string pool. - */ -struct ResStringPool_ref -{ - // Index into the string pool table (uint32_t-offset from the indices - // immediately after ResStringPool_header) at which to find the location - // of the string data in the pool. - uint32_t index; -}; - -/** ******************************************************************** - * String Pool - * - * A set of strings that can be references by others through a - * ResStringPool_ref. - * - *********************************************************************** */ - -/** - * Definition for a pool of strings. The data of this chunk is an - * array of uint32_t providing indices into the pool, relative to - * stringsStart. At stringsStart are all of the UTF-16 strings - * concatenated together; each starts with a uint16_t of the string's - * length and each ends with a 0x0000 terminator. If a string is > - * 32767 characters, the high bit of the length is set meaning to take - * those 15 bits as a high word and it will be followed by another - * uint16_t containing the low word. - * - * If styleCount is not zero, then immediately following the array of - * uint32_t indices into the string table is another array of indices - * into a style table starting at stylesStart. Each entry in the - * style table is an array of ResStringPool_span structures. - */ -struct ResStringPool_header -{ - struct ResChunk_header header; - - // Number of strings in this pool (number of uint32_t indices that follow - // in the data). - uint32_t stringCount; - - // Number of style span arrays in the pool (number of uint32_t indices - // follow the string indices). - uint32_t styleCount; - - // Flags. - enum { - // If set, the string index is sorted by the string values (based - // on strcmp16()). - SORTED_FLAG = 1<<0, - - // String pool is encoded in UTF-8 - UTF8_FLAG = 1<<8 - }; - uint32_t flags; - - // Index from header of the string data. - uint32_t stringsStart; - - // Index from header of the style data. - uint32_t stylesStart; -}; - -/** - * This structure defines a span of style information associated with - * a string in the pool. - */ -struct ResStringPool_span -{ - enum { - END = 0xFFFFFFFF - }; - - // This is the name of the span -- that is, the name of the XML - // tag that defined it. The special value END (0xFFFFFFFF) indicates - // the end of an array of spans. - ResStringPool_ref name; - - // The range of characters in the string that this span applies to. - uint32_t firstChar, lastChar; -}; - -/** - * Convenience class for accessing data in a ResStringPool resource. - */ -class ResStringPool -{ -public: - ResStringPool(); - ResStringPool(const void* data, size_t size, bool copyData=false); - ~ResStringPool(); - - status_t setTo(const void* data, size_t size, bool copyData=false); - - status_t getError() const; - - void uninit(); - - // Return string entry as UTF16; if the pool is UTF8, the string will - // be converted before returning. - inline const char16_t* stringAt(const ResStringPool_ref& ref, size_t* outLen) const { - return stringAt(ref.index, outLen); - } - const char16_t* stringAt(size_t idx, size_t* outLen) const; - - // Note: returns null if the string pool is not UTF8. - const char* string8At(size_t idx, size_t* outLen) const; - - // Return string whether the pool is UTF8 or UTF16. Does not allow you - // to distinguish null. - const String8 string8ObjectAt(size_t idx) const; - - const ResStringPool_span* styleAt(const ResStringPool_ref& ref) const; - const ResStringPool_span* styleAt(size_t idx) const; - - ssize_t indexOfString(const char16_t* str, size_t strLen) const; - - size_t size() const; - size_t styleCount() const; - size_t bytes() const; - - bool isSorted() const; - bool isUTF8() const; - -private: - status_t mError; - void* mOwnedData; - const ResStringPool_header* mHeader; - size_t mSize; - mutable Mutex mDecodeLock; - const uint32_t* mEntries; - const uint32_t* mEntryStyles; - const void* mStrings; - char16_t** mCache; - uint32_t mStringPoolSize; // number of uint16_t - const uint32_t* mStyles; - uint32_t mStylePoolSize; // number of uint32_t -}; - -/** ******************************************************************** - * XML Tree - * - * Binary representation of an XML document. This is designed to - * express everything in an XML document, in a form that is much - * easier to parse on the device. - * - *********************************************************************** */ - -/** - * XML tree header. This appears at the front of an XML tree, - * describing its content. It is followed by a flat array of - * ResXMLTree_node structures; the hierarchy of the XML document - * is described by the occurrance of RES_XML_START_ELEMENT_TYPE - * and corresponding RES_XML_END_ELEMENT_TYPE nodes in the array. - */ -struct ResXMLTree_header -{ - struct ResChunk_header header; -}; - -/** - * Basic XML tree node. A single item in the XML document. Extended info - * about the node can be found after header.headerSize. - */ -struct ResXMLTree_node -{ - struct ResChunk_header header; - - // Line number in original source file at which this element appeared. - uint32_t lineNumber; - - // Optional XML comment that was associated with this element; -1 if none. - struct ResStringPool_ref comment; -}; - -/** - * Extended XML tree node for CDATA tags -- includes the CDATA string. - * Appears header.headerSize bytes after a ResXMLTree_node. - */ -struct ResXMLTree_cdataExt -{ - // The raw CDATA character data. - struct ResStringPool_ref data; - - // The typed value of the character data if this is a CDATA node. - struct Res_value typedData; -}; - -/** - * Extended XML tree node for namespace start/end nodes. - * Appears header.headerSize bytes after a ResXMLTree_node. - */ -struct ResXMLTree_namespaceExt -{ - // The prefix of the namespace. - struct ResStringPool_ref prefix; - - // The URI of the namespace. - struct ResStringPool_ref uri; -}; - -/** - * Extended XML tree node for element start/end nodes. - * Appears header.headerSize bytes after a ResXMLTree_node. - */ -struct ResXMLTree_endElementExt -{ - // String of the full namespace of this element. - struct ResStringPool_ref ns; - - // String name of this node if it is an ELEMENT; the raw - // character data if this is a CDATA node. - struct ResStringPool_ref name; -}; - -/** - * Extended XML tree node for start tags -- includes attribute - * information. - * Appears header.headerSize bytes after a ResXMLTree_node. - */ -struct ResXMLTree_attrExt -{ - // String of the full namespace of this element. - struct ResStringPool_ref ns; - - // String name of this node if it is an ELEMENT; the raw - // character data if this is a CDATA node. - struct ResStringPool_ref name; - - // Byte offset from the start of this structure where the attributes start. - uint16_t attributeStart; - - // Size of the ResXMLTree_attribute structures that follow. - uint16_t attributeSize; - - // Number of attributes associated with an ELEMENT. These are - // available as an array of ResXMLTree_attribute structures - // immediately following this node. - uint16_t attributeCount; - - // Index (1-based) of the "id" attribute. 0 if none. - uint16_t idIndex; - - // Index (1-based) of the "class" attribute. 0 if none. - uint16_t classIndex; - - // Index (1-based) of the "style" attribute. 0 if none. - uint16_t styleIndex; -}; - -struct ResXMLTree_attribute -{ - // Namespace of this attribute. - struct ResStringPool_ref ns; - - // Name of this attribute. - struct ResStringPool_ref name; - - // The original raw string value of this attribute. - struct ResStringPool_ref rawValue; - - // Processesd typed value of this attribute. - struct Res_value typedValue; -}; - -class ResXMLTree; - -class ResXMLParser -{ -public: - ResXMLParser(const ResXMLTree& tree); - - enum event_code_t { - BAD_DOCUMENT = -1, - START_DOCUMENT = 0, - END_DOCUMENT = 1, - - FIRST_CHUNK_CODE = RES_XML_FIRST_CHUNK_TYPE, - - START_NAMESPACE = RES_XML_START_NAMESPACE_TYPE, - END_NAMESPACE = RES_XML_END_NAMESPACE_TYPE, - START_TAG = RES_XML_START_ELEMENT_TYPE, - END_TAG = RES_XML_END_ELEMENT_TYPE, - TEXT = RES_XML_CDATA_TYPE - }; - - struct ResXMLPosition - { - event_code_t eventCode; - const ResXMLTree_node* curNode; - const void* curExt; - }; - - void restart(); - - const ResStringPool& getStrings() const; - - event_code_t getEventType() const; - // Note, unlike XmlPullParser, the first call to next() will return - // START_TAG of the first element. - event_code_t next(); - - // These are available for all nodes: - int32_t getCommentID() const; - const uint16_t* getComment(size_t* outLen) const; - uint32_t getLineNumber() const; - - // This is available for TEXT: - int32_t getTextID() const; - const uint16_t* getText(size_t* outLen) const; - ssize_t getTextValue(Res_value* outValue) const; - - // These are available for START_NAMESPACE and END_NAMESPACE: - int32_t getNamespacePrefixID() const; - const uint16_t* getNamespacePrefix(size_t* outLen) const; - int32_t getNamespaceUriID() const; - const uint16_t* getNamespaceUri(size_t* outLen) const; - - // These are available for START_TAG and END_TAG: - int32_t getElementNamespaceID() const; - const uint16_t* getElementNamespace(size_t* outLen) const; - int32_t getElementNameID() const; - const uint16_t* getElementName(size_t* outLen) const; - - // Remaining methods are for retrieving information about attributes - // associated with a START_TAG: - - size_t getAttributeCount() const; - - // Returns -1 if no namespace, -2 if idx out of range. - int32_t getAttributeNamespaceID(size_t idx) const; - const uint16_t* getAttributeNamespace(size_t idx, size_t* outLen) const; - - int32_t getAttributeNameID(size_t idx) const; - const uint16_t* getAttributeName(size_t idx, size_t* outLen) const; - uint32_t getAttributeNameResID(size_t idx) const; - - int32_t getAttributeValueStringID(size_t idx) const; - const uint16_t* getAttributeStringValue(size_t idx, size_t* outLen) const; - - int32_t getAttributeDataType(size_t idx) const; - int32_t getAttributeData(size_t idx) const; - ssize_t getAttributeValue(size_t idx, Res_value* outValue) const; - - ssize_t indexOfAttribute(const char* ns, const char* attr) const; - ssize_t indexOfAttribute(const char16_t* ns, size_t nsLen, - const char16_t* attr, size_t attrLen) const; - - ssize_t indexOfID() const; - ssize_t indexOfClass() const; - ssize_t indexOfStyle() const; - - void getPosition(ResXMLPosition* pos) const; - void setPosition(const ResXMLPosition& pos); - -private: - friend class ResXMLTree; - - event_code_t nextNode(); - - const ResXMLTree& mTree; - event_code_t mEventCode; - const ResXMLTree_node* mCurNode; - const void* mCurExt; -}; - -/** - * Convenience class for accessing data in a ResXMLTree resource. - */ -class ResXMLTree : public ResXMLParser -{ -public: - ResXMLTree(); - ResXMLTree(const void* data, size_t size, bool copyData=false); - ~ResXMLTree(); - - status_t setTo(const void* data, size_t size, bool copyData=false); - - status_t getError() const; - - void uninit(); - -private: - friend class ResXMLParser; - - status_t validateNode(const ResXMLTree_node* node) const; - - status_t mError; - void* mOwnedData; - const ResXMLTree_header* mHeader; - size_t mSize; - const uint8_t* mDataEnd; - ResStringPool mStrings; - const uint32_t* mResIds; - size_t mNumResIds; - const ResXMLTree_node* mRootNode; - const void* mRootExt; - event_code_t mRootCode; -}; - -/** ******************************************************************** - * RESOURCE TABLE - * - *********************************************************************** */ - -/** - * Header for a resource table. Its data contains a series of - * additional chunks: - * * A ResStringPool_header containing all table values. This string pool - * contains all of the string values in the entire resource table (not - * the names of entries or type identifiers however). - * * One or more ResTable_package chunks. - * - * Specific entries within a resource table can be uniquely identified - * with a single integer as defined by the ResTable_ref structure. - */ -struct ResTable_header -{ - struct ResChunk_header header; - - // The number of ResTable_package structures. - uint32_t packageCount; -}; - -/** - * A collection of resource data types within a package. Followed by - * one or more ResTable_type and ResTable_typeSpec structures containing the - * entry values for each resource type. - */ -struct ResTable_package -{ - struct ResChunk_header header; - - // If this is a base package, its ID. Package IDs start - // at 1 (corresponding to the value of the package bits in a - // resource identifier). 0 means this is not a base package. - uint32_t id; - - // Actual name of this package, \0-terminated. - char16_t name[128]; - - // Offset to a ResStringPool_header defining the resource - // type symbol table. If zero, this package is inheriting from - // another base package (overriding specific values in it). - uint32_t typeStrings; - - // Last index into typeStrings that is for public use by others. - uint32_t lastPublicType; - - // Offset to a ResStringPool_header defining the resource - // key symbol table. If zero, this package is inheriting from - // another base package (overriding specific values in it). - uint32_t keyStrings; - - // Last index into keyStrings that is for public use by others. - uint32_t lastPublicKey; -}; - -/** - * Describes a particular resource configuration. - */ -struct ResTable_config -{ - // Number of bytes in this structure. - uint32_t size; - - union { - struct { - // Mobile country code (from SIM). 0 means "any". - uint16_t mcc; - // Mobile network code (from SIM). 0 means "any". - uint16_t mnc; - }; - uint32_t imsi; - }; - - union { - struct { - // \0\0 means "any". Otherwise, en, fr, etc. - char language[2]; - - // \0\0 means "any". Otherwise, US, CA, etc. - char country[2]; - }; - uint32_t locale; - }; - - enum { - ORIENTATION_ANY = ACONFIGURATION_ORIENTATION_ANY, - ORIENTATION_PORT = ACONFIGURATION_ORIENTATION_PORT, - ORIENTATION_LAND = ACONFIGURATION_ORIENTATION_LAND, - ORIENTATION_SQUARE = ACONFIGURATION_ORIENTATION_SQUARE, - }; - - enum { - TOUCHSCREEN_ANY = ACONFIGURATION_TOUCHSCREEN_ANY, - TOUCHSCREEN_NOTOUCH = ACONFIGURATION_TOUCHSCREEN_NOTOUCH, - TOUCHSCREEN_STYLUS = ACONFIGURATION_TOUCHSCREEN_STYLUS, - TOUCHSCREEN_FINGER = ACONFIGURATION_TOUCHSCREEN_FINGER, - }; - - enum { - DENSITY_DEFAULT = ACONFIGURATION_DENSITY_DEFAULT, - DENSITY_LOW = ACONFIGURATION_DENSITY_LOW, - DENSITY_MEDIUM = ACONFIGURATION_DENSITY_MEDIUM, - DENSITY_TV = ACONFIGURATION_DENSITY_TV, - DENSITY_HIGH = ACONFIGURATION_DENSITY_HIGH, - DENSITY_NONE = ACONFIGURATION_DENSITY_NONE - }; - - union { - struct { - uint8_t orientation; - uint8_t touchscreen; - uint16_t density; - }; - uint32_t screenType; - }; - - enum { - KEYBOARD_ANY = ACONFIGURATION_KEYBOARD_ANY, - KEYBOARD_NOKEYS = ACONFIGURATION_KEYBOARD_NOKEYS, - KEYBOARD_QWERTY = ACONFIGURATION_KEYBOARD_QWERTY, - KEYBOARD_12KEY = ACONFIGURATION_KEYBOARD_12KEY, - }; - - enum { - NAVIGATION_ANY = ACONFIGURATION_NAVIGATION_ANY, - NAVIGATION_NONAV = ACONFIGURATION_NAVIGATION_NONAV, - NAVIGATION_DPAD = ACONFIGURATION_NAVIGATION_DPAD, - NAVIGATION_TRACKBALL = ACONFIGURATION_NAVIGATION_TRACKBALL, - NAVIGATION_WHEEL = ACONFIGURATION_NAVIGATION_WHEEL, - }; - - enum { - MASK_KEYSHIDDEN = 0x0003, - KEYSHIDDEN_ANY = ACONFIGURATION_KEYSHIDDEN_ANY, - KEYSHIDDEN_NO = ACONFIGURATION_KEYSHIDDEN_NO, - KEYSHIDDEN_YES = ACONFIGURATION_KEYSHIDDEN_YES, - KEYSHIDDEN_SOFT = ACONFIGURATION_KEYSHIDDEN_SOFT, - }; - - enum { - MASK_NAVHIDDEN = 0x000c, - SHIFT_NAVHIDDEN = 2, - NAVHIDDEN_ANY = ACONFIGURATION_NAVHIDDEN_ANY << SHIFT_NAVHIDDEN, - NAVHIDDEN_NO = ACONFIGURATION_NAVHIDDEN_NO << SHIFT_NAVHIDDEN, - NAVHIDDEN_YES = ACONFIGURATION_NAVHIDDEN_YES << SHIFT_NAVHIDDEN, - }; - - union { - struct { - uint8_t keyboard; - uint8_t navigation; - uint8_t inputFlags; - uint8_t inputPad0; - }; - uint32_t input; - }; - - enum { - SCREENWIDTH_ANY = 0 - }; - - enum { - SCREENHEIGHT_ANY = 0 - }; - - union { - struct { - uint16_t screenWidth; - uint16_t screenHeight; - }; - uint32_t screenSize; - }; - - enum { - SDKVERSION_ANY = 0 - }; - - enum { - MINORVERSION_ANY = 0 - }; - - union { - struct { - uint16_t sdkVersion; - // For now minorVersion must always be 0!!! Its meaning - // is currently undefined. - uint16_t minorVersion; - }; - uint32_t version; - }; - - enum { - // screenLayout bits for screen size class. - MASK_SCREENSIZE = 0x0f, - SCREENSIZE_ANY = ACONFIGURATION_SCREENSIZE_ANY, - SCREENSIZE_SMALL = ACONFIGURATION_SCREENSIZE_SMALL, - SCREENSIZE_NORMAL = ACONFIGURATION_SCREENSIZE_NORMAL, - SCREENSIZE_LARGE = ACONFIGURATION_SCREENSIZE_LARGE, - SCREENSIZE_XLARGE = ACONFIGURATION_SCREENSIZE_XLARGE, - - // screenLayout bits for wide/long screen variation. - MASK_SCREENLONG = 0x30, - SHIFT_SCREENLONG = 4, - SCREENLONG_ANY = ACONFIGURATION_SCREENLONG_ANY << SHIFT_SCREENLONG, - SCREENLONG_NO = ACONFIGURATION_SCREENLONG_NO << SHIFT_SCREENLONG, - SCREENLONG_YES = ACONFIGURATION_SCREENLONG_YES << SHIFT_SCREENLONG, - }; - - enum { - // uiMode bits for the mode type. - MASK_UI_MODE_TYPE = 0x0f, - UI_MODE_TYPE_ANY = ACONFIGURATION_UI_MODE_TYPE_ANY, - UI_MODE_TYPE_NORMAL = ACONFIGURATION_UI_MODE_TYPE_NORMAL, - UI_MODE_TYPE_DESK = ACONFIGURATION_UI_MODE_TYPE_DESK, - UI_MODE_TYPE_CAR = ACONFIGURATION_UI_MODE_TYPE_CAR, - UI_MODE_TYPE_TELEVISION = ACONFIGURATION_UI_MODE_TYPE_TELEVISION, - UI_MODE_TYPE_APPLIANCE = ACONFIGURATION_UI_MODE_TYPE_APPLIANCE, - - // uiMode bits for the night switch. - MASK_UI_MODE_NIGHT = 0x30, - SHIFT_UI_MODE_NIGHT = 4, - UI_MODE_NIGHT_ANY = ACONFIGURATION_UI_MODE_NIGHT_ANY << SHIFT_UI_MODE_NIGHT, - UI_MODE_NIGHT_NO = ACONFIGURATION_UI_MODE_NIGHT_NO << SHIFT_UI_MODE_NIGHT, - UI_MODE_NIGHT_YES = ACONFIGURATION_UI_MODE_NIGHT_YES << SHIFT_UI_MODE_NIGHT, - }; - - union { - struct { - uint8_t screenLayout; - uint8_t uiMode; - uint16_t smallestScreenWidthDp; - }; - uint32_t screenConfig; - }; - - union { - struct { - uint16_t screenWidthDp; - uint16_t screenHeightDp; - }; - uint32_t screenSizeDp; - }; - - void copyFromDeviceNoSwap(const ResTable_config& o); - - void copyFromDtoH(const ResTable_config& o); - - void swapHtoD(); - - int compare(const ResTable_config& o) const; - int compareLogical(const ResTable_config& o) const; - - // Flags indicating a set of config values. These flag constants must - // match the corresponding ones in android.content.pm.ActivityInfo and - // attrs_manifest.xml. - enum { - CONFIG_MCC = ACONFIGURATION_MCC, - CONFIG_MNC = ACONFIGURATION_MCC, - CONFIG_LOCALE = ACONFIGURATION_LOCALE, - CONFIG_TOUCHSCREEN = ACONFIGURATION_TOUCHSCREEN, - CONFIG_KEYBOARD = ACONFIGURATION_KEYBOARD, - CONFIG_KEYBOARD_HIDDEN = ACONFIGURATION_KEYBOARD_HIDDEN, - CONFIG_NAVIGATION = ACONFIGURATION_NAVIGATION, - CONFIG_ORIENTATION = ACONFIGURATION_ORIENTATION, - CONFIG_DENSITY = ACONFIGURATION_DENSITY, - CONFIG_SCREEN_SIZE = ACONFIGURATION_SCREEN_SIZE, - CONFIG_SMALLEST_SCREEN_SIZE = ACONFIGURATION_SMALLEST_SCREEN_SIZE, - CONFIG_VERSION = ACONFIGURATION_VERSION, - CONFIG_SCREEN_LAYOUT = ACONFIGURATION_SCREEN_LAYOUT, - CONFIG_UI_MODE = ACONFIGURATION_UI_MODE - }; - - // Compare two configuration, returning CONFIG_* flags set for each value - // that is different. - int diff(const ResTable_config& o) const; - - // Return true if 'this' is more specific than 'o'. - bool isMoreSpecificThan(const ResTable_config& o) const; - - // Return true if 'this' is a better match than 'o' for the 'requested' - // configuration. This assumes that match() has already been used to - // remove any configurations that don't match the requested configuration - // at all; if they are not first filtered, non-matching results can be - // considered better than matching ones. - // The general rule per attribute: if the request cares about an attribute - // (it normally does), if the two (this and o) are equal it's a tie. If - // they are not equal then one must be generic because only generic and - // '==requested' will pass the match() call. So if this is not generic, - // it wins. If this IS generic, o wins (return false). - bool isBetterThan(const ResTable_config& o, const ResTable_config* requested) const; - - // Return true if 'this' can be considered a match for the parameters in - // 'settings'. - // Note this is asymetric. A default piece of data will match every request - // but a request for the default should not match odd specifics - // (ie, request with no mcc should not match a particular mcc's data) - // settings is the requested settings - bool match(const ResTable_config& settings) const; - - void getLocale(char str[6]) const; - - String8 toString() const; -}; - -/** - * A specification of the resources defined by a particular type. - * - * There should be one of these chunks for each resource type. - * - * This structure is followed by an array of integers providing the set of - * configuation change flags (ResTable_config::CONFIG_*) that have multiple - * resources for that configuration. In addition, the high bit is set if that - * resource has been made public. - */ -struct ResTable_typeSpec -{ - struct ResChunk_header header; - - // The type identifier this chunk is holding. Type IDs start - // at 1 (corresponding to the value of the type bits in a - // resource identifier). 0 is invalid. - uint8_t id; - - // Must be 0. - uint8_t res0; - // Must be 0. - uint16_t res1; - - // Number of uint32_t entry configuration masks that follow. - uint32_t entryCount; - - enum { - // Additional flag indicating an entry is public. - SPEC_PUBLIC = 0x40000000 - }; -}; - -/** - * A collection of resource entries for a particular resource data - * type. Followed by an array of uint32_t defining the resource - * values, corresponding to the array of type strings in the - * ResTable_package::typeStrings string block. Each of these hold an - * index from entriesStart; a value of NO_ENTRY means that entry is - * not defined. - * - * There may be multiple of these chunks for a particular resource type, - * supply different configuration variations for the resource values of - * that type. - * - * It would be nice to have an additional ordered index of entries, so - * we can do a binary search if trying to find a resource by string name. - */ -struct ResTable_type -{ - struct ResChunk_header header; - - enum { - NO_ENTRY = 0xFFFFFFFF - }; - - // The type identifier this chunk is holding. Type IDs start - // at 1 (corresponding to the value of the type bits in a - // resource identifier). 0 is invalid. - uint8_t id; - - // Must be 0. - uint8_t res0; - // Must be 0. - uint16_t res1; - - // Number of uint32_t entry indices that follow. - uint32_t entryCount; - - // Offset from header where ResTable_entry data starts. - uint32_t entriesStart; - - // Configuration this collection of entries is designed for. - ResTable_config config; -}; - -/** - * 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 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. - */ -struct ResTable_entry -{ - // Number of bytes in this structure. - uint16_t size; - - enum { - // If set, this is a complex entry, holding a set of name/value - // mappings. It is followed by an array of ResTable_map structures. - FLAG_COMPLEX = 0x0001, - // If set, this resource has been declared public, so libraries - // are allowed to reference it. - FLAG_PUBLIC = 0x0002 - }; - uint16_t flags; - - // Reference into ResTable_package::keyStrings identifying this entry. - struct ResStringPool_ref key; -}; - -/** - * Extended form of a ResTable_entry for map entries, defining a parent map - * resource from which to inherit values. - */ -struct ResTable_map_entry : public ResTable_entry -{ - // Resource identifier of the parent mapping, or 0 if there is none. - ResTable_ref parent; - // Number of name/value pairs that follow for FLAG_COMPLEX. - uint32_t count; -}; - -/** - * A single name/value mapping that is part of a complex resource - * entry. - */ -struct ResTable_map -{ - // The resource identifier defining this mapping's name. For attribute - // resources, 'name' can be one of the following special resource types - // to supply meta-data about the attribute; for all other resource types - // it must be an attribute resource. - ResTable_ref name; - - // Special values for 'name' when defining attribute resources. - enum { - // This entry holds the attribute's type code. - ATTR_TYPE = Res_MAKEINTERNAL(0), - - // For integral attributes, this is the minimum value it can hold. - ATTR_MIN = Res_MAKEINTERNAL(1), - - // For integral attributes, this is the maximum value it can hold. - ATTR_MAX = Res_MAKEINTERNAL(2), - - // Localization of this resource is can be encouraged or required with - // an aapt flag if this is set - ATTR_L10N = Res_MAKEINTERNAL(3), - - // for plural support, see android.content.res.PluralRules#attrForQuantity(int) - ATTR_OTHER = Res_MAKEINTERNAL(4), - ATTR_ZERO = Res_MAKEINTERNAL(5), - ATTR_ONE = Res_MAKEINTERNAL(6), - ATTR_TWO = Res_MAKEINTERNAL(7), - ATTR_FEW = Res_MAKEINTERNAL(8), - ATTR_MANY = Res_MAKEINTERNAL(9) - - }; - - // Bit mask of allowed types, for use with ATTR_TYPE. - enum { - // No type has been defined for this attribute, use generic - // type handling. The low 16 bits are for types that can be - // handled generically; the upper 16 require additional information - // in the bag so can not be handled generically for TYPE_ANY. - TYPE_ANY = 0x0000FFFF, - - // Attribute holds a references to another resource. - TYPE_REFERENCE = 1<<0, - - // Attribute holds a generic string. - TYPE_STRING = 1<<1, - - // Attribute holds an integer value. ATTR_MIN and ATTR_MIN can - // optionally specify a constrained range of possible integer values. - TYPE_INTEGER = 1<<2, - - // Attribute holds a boolean integer. - TYPE_BOOLEAN = 1<<3, - - // Attribute holds a color value. - TYPE_COLOR = 1<<4, - - // Attribute holds a floating point value. - TYPE_FLOAT = 1<<5, - - // Attribute holds a dimension value, such as "20px". - TYPE_DIMENSION = 1<<6, - - // Attribute holds a fraction value, such as "20%". - TYPE_FRACTION = 1<<7, - - // Attribute holds an enumeration. The enumeration values are - // supplied as additional entries in the map. - TYPE_ENUM = 1<<16, - - // Attribute holds a bitmaks of flags. The flag bit values are - // supplied as additional entries in the map. - TYPE_FLAGS = 1<<17 - }; - - // Enum of localization modes, for use with ATTR_L10N. - enum { - L10N_NOT_REQUIRED = 0, - L10N_SUGGESTED = 1 - }; - - // This mapping's value. - Res_value value; -}; - -/** - * Convenience class for accessing data in a ResTable resource. - */ -class ResTable -{ -public: - ResTable(); - ResTable(const void* data, size_t size, void* cookie, - bool copyData=false); - ~ResTable(); - - status_t add(const void* data, size_t size, void* cookie, - bool copyData=false, const void* idmap = NULL); - status_t add(Asset* asset, void* cookie, - bool copyData=false, const void* idmap = NULL); - status_t add(ResTable* src); - - status_t getError() const; - - void uninit(); - - struct resource_name - { - const char16_t* package; - size_t packageLen; - const char16_t* type; - size_t typeLen; - const char16_t* name; - size_t nameLen; - }; - - bool getResourceName(uint32_t resID, resource_name* outName) const; - - /** - * Retrieve the value of a resource. If the resource is found, returns a - * value >= 0 indicating the table it is in (for use with - * getTableStringBlock() and getTableCookie()) and fills in 'outValue'. If - * not found, returns a negative error code. - * - * Note that this function does not do reference traversal. If you want - * to follow references to other resources to get the "real" value to - * use, you need to call resolveReference() after this function. - * - * @param resID The desired resoruce identifier. - * @param outValue Filled in with the resource data that was found. - * - * @return ssize_t Either a >= 0 table index or a negative error code. - */ - ssize_t getResource(uint32_t resID, Res_value* outValue, bool mayBeBag = false, - uint16_t density = 0, - uint32_t* outSpecFlags = NULL, - ResTable_config* outConfig = NULL) const; - - inline ssize_t getResource(const ResTable_ref& res, Res_value* outValue, - uint32_t* outSpecFlags=NULL) const { - return getResource(res.ident, outValue, false, 0, outSpecFlags, NULL); - } - - ssize_t resolveReference(Res_value* inOutValue, - ssize_t blockIndex, - uint32_t* outLastRef = NULL, - uint32_t* inoutTypeSpecFlags = NULL, - ResTable_config* outConfig = NULL) const; - - enum { - TMP_BUFFER_SIZE = 16 - }; - const char16_t* valueToString(const Res_value* value, size_t stringBlock, - char16_t tmpBuffer[TMP_BUFFER_SIZE], - size_t* outLen); - - struct bag_entry { - ssize_t stringBlock; - ResTable_map map; - }; - - /** - * Retrieve the bag of a resource. If the resoruce is found, returns the - * number of bags it contains and 'outBag' points to an array of their - * values. If not found, a negative error code is returned. - * - * Note that this function -does- do reference traversal of the bag data. - * - * @param resID The desired resource identifier. - * @param outBag Filled inm with a pointer to the bag mappings. - * - * @return ssize_t Either a >= 0 bag count of negative error code. - */ - ssize_t lockBag(uint32_t resID, const bag_entry** outBag) const; - - void unlockBag(const bag_entry* bag) const; - - void lock() const; - - ssize_t getBagLocked(uint32_t resID, const bag_entry** outBag, - uint32_t* outTypeSpecFlags=NULL) const; - - void unlock() const; - - class Theme { - public: - Theme(const ResTable& table); - ~Theme(); - - inline const ResTable& getResTable() const { return mTable; } - - status_t applyStyle(uint32_t resID, bool force=false); - status_t setTo(const Theme& other); - - /** - * Retrieve a value in the theme. If the theme defines this - * value, returns a value >= 0 indicating the table it is in - * (for use with getTableStringBlock() and getTableCookie) and - * fills in 'outValue'. If not found, returns a negative error - * code. - * - * Note that this function does not do reference traversal. If you want - * to follow references to other resources to get the "real" value to - * use, you need to call resolveReference() after this function. - * - * @param resID A resource identifier naming the desired theme - * attribute. - * @param outValue Filled in with the theme value that was - * found. - * - * @return ssize_t Either a >= 0 table index or a negative error code. - */ - ssize_t getAttribute(uint32_t resID, Res_value* outValue, - uint32_t* outTypeSpecFlags = NULL) const; - - /** - * This is like ResTable::resolveReference(), but also takes - * care of resolving attribute references to the theme. - */ - ssize_t resolveAttributeReference(Res_value* inOutValue, - ssize_t blockIndex, uint32_t* outLastRef = NULL, - uint32_t* inoutTypeSpecFlags = NULL, - ResTable_config* inoutConfig = NULL) const; - - void dumpToLog() const; - - private: - Theme(const Theme&); - Theme& operator=(const Theme&); - - struct theme_entry { - ssize_t stringBlock; - uint32_t typeSpecFlags; - Res_value value; - }; - struct type_info { - size_t numEntries; - theme_entry* entries; - }; - struct package_info { - size_t numTypes; - type_info types[]; - }; - - void free_package(package_info* pi); - package_info* copy_package(package_info* pi); - - const ResTable& mTable; - package_info* mPackages[Res_MAXPACKAGE]; - }; - - void setParameters(const ResTable_config* params); - void getParameters(ResTable_config* params) const; - - // Retrieve an identifier (which can be passed to getResource) - // for a given resource name. The 'name' can be fully qualified - // (:.) or the package or type components - // can be dropped if default values are supplied here. - // - // Returns 0 if no such resource was found, else a valid resource ID. - uint32_t identifierForName(const char16_t* name, size_t nameLen, - const char16_t* type = 0, size_t typeLen = 0, - const char16_t* defPackage = 0, - size_t defPackageLen = 0, - uint32_t* outTypeSpecFlags = NULL) const; - - static bool expandResourceRef(const uint16_t* refStr, size_t refLen, - String16* outPackage, - String16* outType, - String16* outName, - const String16* defType = NULL, - const String16* defPackage = NULL, - const char** outErrorMsg = NULL, - bool* outPublicOnly = NULL); - - static bool stringToInt(const char16_t* s, size_t len, Res_value* outValue); - static bool stringToFloat(const char16_t* s, size_t len, Res_value* outValue); - - // Used with stringToValue. - class Accessor - { - public: - inline virtual ~Accessor() { } - - virtual uint32_t getCustomResource(const String16& package, - const String16& type, - const String16& name) const = 0; - virtual uint32_t getCustomResourceWithCreation(const String16& package, - const String16& type, - const String16& name, - const bool createIfNeeded = false) = 0; - virtual uint32_t getRemappedPackage(uint32_t origPackage) const = 0; - virtual bool getAttributeType(uint32_t attrID, uint32_t* outType) = 0; - virtual bool getAttributeMin(uint32_t attrID, uint32_t* outMin) = 0; - virtual bool getAttributeMax(uint32_t attrID, uint32_t* outMax) = 0; - virtual bool getAttributeEnum(uint32_t attrID, - const char16_t* name, size_t nameLen, - Res_value* outValue) = 0; - virtual bool getAttributeFlags(uint32_t attrID, - const char16_t* name, size_t nameLen, - Res_value* outValue) = 0; - virtual uint32_t getAttributeL10N(uint32_t attrID) = 0; - virtual bool getLocalizationSetting() = 0; - virtual void reportError(void* accessorCookie, const char* fmt, ...) = 0; - }; - - // Convert a string to a resource value. Handles standard "@res", - // "#color", "123", and "0x1bd" types; performs escaping of strings. - // The resulting value is placed in 'outValue'; if it is a string type, - // 'outString' receives the string. If 'attrID' is supplied, the value is - // type checked against this attribute and it is used to perform enum - // evaluation. If 'acccessor' is supplied, it will be used to attempt to - // resolve resources that do not exist in this ResTable. If 'attrType' is - // supplied, the value will be type checked for this format if 'attrID' - // is not supplied or found. - bool stringToValue(Res_value* outValue, String16* outString, - const char16_t* s, size_t len, - bool preserveSpaces, bool coerceType, - uint32_t attrID = 0, - const String16* defType = NULL, - const String16* defPackage = NULL, - Accessor* accessor = NULL, - void* accessorCookie = NULL, - uint32_t attrType = ResTable_map::TYPE_ANY, - bool enforcePrivate = true) const; - - // Perform processing of escapes and quotes in a string. - static bool collectString(String16* outString, - const char16_t* s, size_t len, - bool preserveSpaces, - const char** outErrorMsg = NULL, - bool append = false); - - size_t getBasePackageCount() const; - const char16_t* getBasePackageName(size_t idx) const; - uint32_t getBasePackageId(size_t idx) const; - - // Return the number of resource tables that the object contains. - size_t getTableCount() const; - // Return the values string pool for the resource table at the given - // index. This string pool contains all of the strings for values - // contained in the resource table -- that is the item values themselves, - // but not the names their entries or types. - const ResStringPool* getTableStringBlock(size_t index) const; - // Return unique cookie identifier for the given resource table. - void* getTableCookie(size_t index) const; - - // Return the configurations (ResTable_config) that we know about - void getConfigurations(Vector* configs) const; - - void getLocales(Vector* locales) const; - - // Generate an idmap. - // - // Return value: on success: NO_ERROR; caller is responsible for free-ing - // outData (using free(3)). On failure, any status_t value other than - // NO_ERROR; the caller should not free outData. - status_t createIdmap(const ResTable& overlay, uint32_t originalCrc, uint32_t overlayCrc, - void** outData, size_t* outSize) const; - - enum { - IDMAP_HEADER_SIZE_BYTES = 3 * sizeof(uint32_t), - }; - // Retrieve idmap meta-data. - // - // This function only requires the idmap header (the first - // IDMAP_HEADER_SIZE_BYTES) bytes of an idmap file. - static bool getIdmapInfo(const void* idmap, size_t size, - uint32_t* pOriginalCrc, uint32_t* pOverlayCrc); - -#ifndef HAVE_ANDROID_OS - void print(bool inclValues) const; - static String8 normalizeForOutput(const char* input); -#endif - -private: - struct Header; - struct Type; - struct Package; - struct PackageGroup; - struct bag_set; - - status_t add(const void* data, size_t size, void* cookie, - Asset* asset, bool copyData, const Asset* idmap); - - ssize_t getResourcePackageIndex(uint32_t resID) const; - ssize_t getEntry( - const Package* package, int typeIndex, int entryIndex, - const ResTable_config* config, - const ResTable_type** outType, const ResTable_entry** outEntry, - const Type** outTypeClass) const; - status_t parsePackage( - const ResTable_package* const pkg, const Header* const header, uint32_t idmap_id); - - void print_value(const Package* pkg, const Res_value& value) const; - - mutable Mutex mLock; - - status_t mError; - - ResTable_config mParams; - - // Array of all resource tables. - Vector mHeaders; - - // Array of packages in all resource tables. - Vector mPackageGroups; - - // Mapping from resource package IDs to indices into the internal - // package array. - uint8_t mPackageMap[256]; -}; - -} // namespace android - -#endif // _LIBS_UTILS_RESOURCE_TYPES_H diff --git a/include/utils/StreamingZipInflater.h b/include/utils/StreamingZipInflater.h deleted file mode 100644 index 3ace5d5a8..000000000 --- a/include/utils/StreamingZipInflater.h +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (C) 2010 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 __LIBS_STREAMINGZIPINFLATER_H -#define __LIBS_STREAMINGZIPINFLATER_H - -#include -#include -#include - -#include - -namespace android { - -class StreamingZipInflater { -public: - static const size_t INPUT_CHUNK_SIZE = 64 * 1024; - static const size_t OUTPUT_CHUNK_SIZE = 64 * 1024; - - // Flavor that pages in the compressed data from a fd - StreamingZipInflater(int fd, off64_t compDataStart, size_t uncompSize, size_t compSize); - - // Flavor that gets the compressed data from an in-memory buffer - StreamingZipInflater(class FileMap* dataMap, size_t uncompSize); - - ~StreamingZipInflater(); - - // read 'count' bytes of uncompressed data from the current position. outBuf may - // be NULL, in which case the data is consumed and discarded. - ssize_t read(void* outBuf, size_t count); - - // seeking backwards requires uncompressing fom the beginning, so is very - // expensive. seeking forwards only requires uncompressing from the current - // position to the destination. - off64_t seekAbsolute(off64_t absoluteInputPosition); - -private: - void initInflateState(); - int readNextChunk(); - - // where to find the uncompressed data - int mFd; - off64_t mInFileStart; // where the compressed data lives in the file - class FileMap* mDataMap; - - z_stream mInflateState; - bool mStreamNeedsInit; - - // output invariants for this asset - uint8_t* mOutBuf; // output buf for decompressed bytes - size_t mOutBufSize; // allocated size of mOutBuf - size_t mOutTotalSize; // total uncompressed size of the blob - - // current output state bookkeeping - off64_t mOutCurPosition; // current position in total offset - size_t mOutLastDecoded; // last decoded byte + 1 in mOutbuf - size_t mOutDeliverable; // next undelivered byte of decoded output in mOutBuf - - // input invariants - uint8_t* mInBuf; - size_t mInBufSize; // allocated size of mInBuf; - size_t mInTotalSize; // total size of compressed data for this blob - - // input state bookkeeping - size_t mInNextChunkOffset; // offset from start of blob at which the next input chunk lies - // the z_stream contains state about input block consumption -}; - -} - -#endif diff --git a/include/utils/ZipFileCRO.h b/include/utils/ZipFileCRO.h deleted file mode 100644 index 3e42a958b..000000000 --- a/include/utils/ZipFileCRO.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// -// C API for ead-only access to Zip archives, with minimal heap allocation. -// -#ifndef __LIBS_ZIPFILECRO_H -#define __LIBS_ZIPFILECRO_H - -#include -#include -#include - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/* - * Trivial typedef to ensure that ZipFileCRO is not treated as a simple integer. - */ -typedef void* ZipFileCRO; - -/* - * Trivial typedef to ensure that ZipEntryCRO is not treated as a simple - * integer. We use NULL to indicate an invalid value. - */ -typedef void* ZipEntryCRO; - -extern ZipFileCRO ZipFileXRO_open(const char* path); - -extern void ZipFileCRO_destroy(ZipFileCRO zip); - -extern ZipEntryCRO ZipFileCRO_findEntryByName(ZipFileCRO zip, - const char* fileName); - -extern bool ZipFileCRO_getEntryInfo(ZipFileCRO zip, ZipEntryCRO entry, - int* pMethod, size_t* pUncompLen, - size_t* pCompLen, off64_t* pOffset, long* pModWhen, long* pCrc32); - -extern bool ZipFileCRO_uncompressEntry(ZipFileCRO zip, ZipEntryCRO entry, int fd); - -#ifdef __cplusplus -} -#endif - -#endif /*__LIBS_ZIPFILECRO_H*/ diff --git a/include/utils/ZipFileRO.h b/include/utils/ZipFileRO.h deleted file mode 100644 index 547e36a09..000000000 --- a/include/utils/ZipFileRO.h +++ /dev/null @@ -1,262 +0,0 @@ -/* - * 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. - */ - -/* - * Read-only access to Zip archives, with minimal heap allocation. - * - * This is similar to the more-complete ZipFile class, but no attempt - * has been made to make them interchangeable. This class operates under - * a very different set of assumptions and constraints. - * - * One such assumption is that if you're getting file descriptors for - * use with this class as a child of a fork() operation, you must be on - * a pread() to guarantee correct operation. This is because pread() can - * atomically read at a file offset without worrying about a lock around an - * lseek() + read() pair. - */ -#ifndef __LIBS_ZIPFILERO_H -#define __LIBS_ZIPFILERO_H - -#include -#include -#include -#include - -#include -#include -#include -#include - -namespace android { - -/* - * Trivial typedef to ensure that ZipEntryRO is not treated as a simple - * integer. We use NULL to indicate an invalid value. - */ -typedef void* ZipEntryRO; - -/* - * Open a Zip archive for reading. - * - * We want "open" and "find entry by name" to be fast operations, and we - * want to use as little memory as possible. We memory-map the file, - * and load a hash table with pointers to the filenames (which aren't - * null-terminated). The other fields are at a fixed offset from the - * filename, so we don't need to extract those (but we do need to byte-read - * and endian-swap them every time we want them). - * - * To speed comparisons when doing a lookup by name, we could make the mapping - * "private" (copy-on-write) and null-terminate the filenames after verifying - * the record structure. However, this requires a private mapping of - * every page that the Central Directory touches. Easier to tuck a copy - * of the string length into the hash table entry. - * - * NOTE: If this is used on file descriptors inherited from a fork() operation, - * you must be on a platform that implements pread() to guarantee correctness - * on the shared file descriptors. - */ -class ZipFileRO { -public: - ZipFileRO() - : mFd(-1), mFileName(NULL), mFileLength(-1), - mDirectoryMap(NULL), - mNumEntries(-1), mDirectoryOffset(-1), - mHashTableSize(-1), mHashTable(NULL) - {} - - ~ZipFileRO(); - - /* - * Open an archive. - */ - status_t open(const char* zipFileName); - - /* - * Find an entry, by name. Returns the entry identifier, or NULL if - * not found. - * - * If two entries have the same name, one will be chosen at semi-random. - */ - ZipEntryRO findEntryByName(const char* fileName) const; - - /* - * Return the #of entries in the Zip archive. - */ - int getNumEntries(void) const { - return mNumEntries; - } - - /* - * Return the Nth entry. Zip file entries are not stored in sorted - * order, and updated entries may appear at the end, so anyone walking - * the archive needs to avoid making ordering assumptions. We take - * that further by returning the Nth non-empty entry in the hash table - * rather than the Nth entry in the archive. - * - * Valid values are [0..numEntries). - * - * [This is currently O(n). If it needs to be fast we can allocate an - * additional data structure or provide an iterator interface.] - */ - ZipEntryRO findEntryByIndex(int idx) const; - - /* - * Copy the filename into the supplied buffer. Returns 0 on success, - * -1 if "entry" is invalid, or the filename length if it didn't fit. The - * length, and the returned string, include the null-termination. - */ - int getEntryFileName(ZipEntryRO entry, char* buffer, int bufLen) const; - - /* - * Get the vital stats for an entry. Pass in NULL pointers for anything - * you don't need. - * - * "*pOffset" holds the Zip file offset of the entry's data. - * - * Returns "false" if "entry" is bogus or if the data in the Zip file - * appears to be bad. - */ - bool getEntryInfo(ZipEntryRO entry, int* pMethod, size_t* pUncompLen, - size_t* pCompLen, off64_t* pOffset, long* pModWhen, long* pCrc32) const; - - /* - * Create a new FileMap object that maps a subset of the archive. For - * an uncompressed entry this effectively provides a pointer to the - * actual data, for a compressed entry this provides the input buffer - * for inflate(). - */ - FileMap* createEntryFileMap(ZipEntryRO entry) const; - - /* - * Uncompress the data into a buffer. Depending on the compression - * format, this is either an "inflate" operation or a memcpy. - * - * Use "uncompLen" from getEntryInfo() to determine the required - * buffer size. - * - * Returns "true" on success. - */ - bool uncompressEntry(ZipEntryRO entry, void* buffer) const; - - /* - * Uncompress the data to an open file descriptor. - */ - bool uncompressEntry(ZipEntryRO entry, int fd) const; - - /* Zip compression methods we support */ - enum { - kCompressStored = 0, // no compression - kCompressDeflated = 8, // standard deflate - }; - - /* - * Utility function: uncompress deflated data, buffer to buffer. - */ - static bool inflateBuffer(void* outBuf, const void* inBuf, - size_t uncompLen, size_t compLen); - - /* - * Utility function: uncompress deflated data, buffer to fd. - */ - static bool inflateBuffer(int fd, const void* inBuf, - size_t uncompLen, size_t compLen); - - /* - * Utility function to convert ZIP's time format to a timespec struct. - */ - static inline void zipTimeToTimespec(long when, struct tm* timespec) { - const long date = when >> 16; - timespec->tm_year = ((date >> 9) & 0x7F) + 80; // Zip is years since 1980 - timespec->tm_mon = (date >> 5) & 0x0F; - timespec->tm_mday = date & 0x1F; - - timespec->tm_hour = (when >> 11) & 0x1F; - timespec->tm_min = (when >> 5) & 0x3F; - timespec->tm_sec = (when & 0x1F) << 1; - } - - /* - * Some basic functions for raw data manipulation. "LE" means - * Little Endian. - */ - static inline unsigned short get2LE(const unsigned char* buf) { - return buf[0] | (buf[1] << 8); - } - static inline unsigned long get4LE(const unsigned char* buf) { - return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); - } - -private: - /* these are private and not defined */ - ZipFileRO(const ZipFileRO& src); - ZipFileRO& operator=(const ZipFileRO& src); - - /* locate and parse the central directory */ - bool mapCentralDirectory(void); - - /* parse the archive, prepping internal structures */ - bool parseZipArchive(void); - - /* add a new entry to the hash table */ - void addToHash(const char* str, int strLen, unsigned int hash); - - /* compute string hash code */ - static unsigned int computeHash(const char* str, int len); - - /* convert a ZipEntryRO back to a hash table index */ - int entryToIndex(const ZipEntryRO entry) const; - - /* - * One entry in the hash table. - */ - typedef struct HashEntry { - const char* name; - unsigned short nameLen; - //unsigned int hash; - } HashEntry; - - /* open Zip archive */ - int mFd; - - /* Lock for handling the file descriptor (seeks, etc) */ - mutable Mutex mFdLock; - - /* zip file name */ - char* mFileName; - - /* length of file */ - size_t mFileLength; - - /* mapped file */ - FileMap* mDirectoryMap; - - /* number of entries in the Zip archive */ - int mNumEntries; - - /* CD directory offset in the Zip archive */ - off64_t mDirectoryOffset; - - /* - * We know how many entries are in the Zip archive, so we have a - * fixed-size hash table. We probe for an empty slot. - */ - int mHashTableSize; - HashEntry* mHashTable; -}; - -}; // namespace android - -#endif /*__LIBS_ZIPFILERO_H*/ diff --git a/include/utils/ZipUtils.h b/include/utils/ZipUtils.h deleted file mode 100644 index 42c42b6c0..000000000 --- a/include/utils/ZipUtils.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * 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. - */ - -// -// Miscellaneous zip/gzip utility functions. -// -#ifndef __LIBS_ZIPUTILS_H -#define __LIBS_ZIPUTILS_H - -#include - -namespace android { - -/* - * Container class for utility functions, primarily for namespace reasons. - */ -class ZipUtils { -public: - /* - * General utility function for uncompressing "deflate" data from a file - * to a buffer. - */ - static bool inflateToBuffer(int fd, void* buf, long uncompressedLen, - long compressedLen); - static bool inflateToBuffer(FILE* fp, void* buf, long uncompressedLen, - long compressedLen); - - /* - * Someday we might want to make this generic and handle bzip2 ".bz2" - * files too. - * - * We could declare gzip to be a sub-class of zip that has exactly - * one always-compressed entry, but we currently want to treat Zip - * and gzip as distinct, so there's no value. - * - * The zlib library has some gzip utilities, but it has no interface - * for extracting the uncompressed length of the file (you do *not* - * want to gzseek to the end). - * - * Pass in a seeked file pointer for the gzip file. If this is a gzip - * file, we set our return values appropriately and return "true" with - * the file seeked to the start of the compressed data. - */ - static bool examineGzip(FILE* fp, int* pCompressionMethod, - long* pUncompressedLen, long* pCompressedLen, unsigned long* pCRC32); - -private: - ZipUtils() {} - ~ZipUtils() {} -}; - -}; // namespace android - -#endif /*__LIBS_ZIPUTILS_H*/ diff --git a/libs/utils/Asset.cpp b/libs/utils/Asset.cpp index 50e701aa8..cb7628d1d 100644 --- a/libs/utils/Asset.cpp +++ b/libs/utils/Asset.cpp @@ -21,23 +21,23 @@ #define LOG_TAG "asset" //#define NDEBUG 0 -#include +#include +#include +#include +#include #include #include -#include -#include -#include #include #include -#include -#include -#include -#include #include -#include +#include +#include +#include +#include #include #include +#include using namespace android; diff --git a/libs/utils/AssetDir.cpp b/libs/utils/AssetDir.cpp index c5f664ecc..475f521c1 100644 --- a/libs/utils/AssetDir.cpp +++ b/libs/utils/AssetDir.cpp @@ -19,7 +19,7 @@ // implementation is in the header file or in friend functions in // AssetManager. // -#include +#include using namespace android; diff --git a/libs/utils/AssetManager.cpp b/libs/utils/AssetManager.cpp index 47a2b9953..4829addec 100644 --- a/libs/utils/AssetManager.cpp +++ b/libs/utils/AssetManager.cpp @@ -21,23 +21,23 @@ #define LOG_TAG "asset" //#define LOG_NDEBUG 0 -#include -#include -#include +#include +#include +#include +#include +#include #include -#include -#include -#include -#include #include -#include +#include +#include #include +#include +#include #include #include -#include -#include #include +#include #include #include diff --git a/libs/utils/BackupData.cpp b/libs/utils/BackupData.cpp index f95630613..7b1bcba2d 100644 --- a/libs/utils/BackupData.cpp +++ b/libs/utils/BackupData.cpp @@ -16,7 +16,7 @@ #define LOG_TAG "backup_data" -#include +#include #include #include diff --git a/libs/utils/BackupHelpers.cpp b/libs/utils/BackupHelpers.cpp index 214f6d7e9..7a817a789 100644 --- a/libs/utils/BackupHelpers.cpp +++ b/libs/utils/BackupHelpers.cpp @@ -16,7 +16,7 @@ #define LOG_TAG "file_backup_helper" -#include +#include #include #include diff --git a/libs/utils/ObbFile.cpp b/libs/utils/ObbFile.cpp index ddf59914b..21e06c8db 100644 --- a/libs/utils/ObbFile.cpp +++ b/libs/utils/ObbFile.cpp @@ -23,9 +23,9 @@ #define LOG_TAG "ObbFile" +#include #include #include -#include //#define DEBUG 1 diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp index af2f3a9cc..07f3b1624 100644 --- a/libs/utils/ResourceTypes.cpp +++ b/libs/utils/ResourceTypes.cpp @@ -17,14 +17,14 @@ #define LOG_TAG "ResourceType" //#define LOG_NDEBUG 0 +#include #include #include #include -#include +#include #include #include #include -#include #include #include diff --git a/libs/utils/StreamingZipInflater.cpp b/libs/utils/StreamingZipInflater.cpp index 8512170ae..d3fb98d57 100644 --- a/libs/utils/StreamingZipInflater.cpp +++ b/libs/utils/StreamingZipInflater.cpp @@ -18,8 +18,8 @@ #define LOG_TAG "szipinf" #include +#include #include -#include #include #include #include diff --git a/libs/utils/ZipFileCRO.cpp b/libs/utils/ZipFileCRO.cpp index 55dfd9f87..c8df8453d 100644 --- a/libs/utils/ZipFileCRO.cpp +++ b/libs/utils/ZipFileCRO.cpp @@ -14,8 +14,8 @@ * limitations under the License. */ -#include -#include +#include +#include using namespace android; diff --git a/libs/utils/ZipFileRO.cpp b/libs/utils/ZipFileRO.cpp index 1498aac07..4b7f1e73f 100644 --- a/libs/utils/ZipFileRO.cpp +++ b/libs/utils/ZipFileRO.cpp @@ -19,7 +19,7 @@ // #define LOG_TAG "zipro" //#define LOG_NDEBUG 0 -#include +#include #include #include #include diff --git a/libs/utils/ZipUtils.cpp b/libs/utils/ZipUtils.cpp index 2dbdc1d38..db3479d33 100644 --- a/libs/utils/ZipUtils.cpp +++ b/libs/utils/ZipUtils.cpp @@ -20,8 +20,8 @@ #define LOG_TAG "ziputil" -#include -#include +#include +#include #include #include diff --git a/libs/utils/tests/ObbFile_test.cpp b/libs/utils/tests/ObbFile_test.cpp index 46b30c2a6..09d4d7d98 100644 --- a/libs/utils/tests/ObbFile_test.cpp +++ b/libs/utils/tests/ObbFile_test.cpp @@ -15,8 +15,8 @@ */ #define LOG_TAG "ObbFile_test" +#include #include -#include #include #include diff --git a/libs/utils/tests/ZipFileRO_test.cpp b/libs/utils/tests/ZipFileRO_test.cpp index 7a1d0bd95..344f97416 100644 --- a/libs/utils/tests/ZipFileRO_test.cpp +++ b/libs/utils/tests/ZipFileRO_test.cpp @@ -15,8 +15,8 @@ */ #define LOG_TAG "ZipFileRO_test" +#include #include -#include #include From 6c865b58aabe34d2fdcac6ce66a4a0db1e1196f5 Mon Sep 17 00:00:00 2001 From: Al Sutton Date: Sun, 19 Feb 2012 08:31:19 +0000 Subject: [PATCH 396/541] Xcode 4.3 compatibility checkin The update compiler in Xcode 4.3 (and 4.4) requires lookups into dependant bases of class templates to be qualified. This checkin fixes the issues raised by the compiler by implementing the this-> recommendation from the llvm page at http://clang.llvm.org/compatibility.html#dep_lookup_bases Signed-off-by: Al Sutton --- include/utils/KeyedVector.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/utils/KeyedVector.h b/include/utils/KeyedVector.h index fcc3bcfa0..85535bd61 100644 --- a/include/utils/KeyedVector.h +++ b/include/utils/KeyedVector.h @@ -122,7 +122,7 @@ ssize_t KeyedVector::indexOfKey(const KEY& key) const { template inline const VALUE& KeyedVector::valueFor(const KEY& key) const { - ssize_t i = indexOfKey(key); + ssize_t i = this->indexOfKey(key); assert(i>=0); return mVector.itemAt(i).value; } @@ -139,7 +139,7 @@ const KEY& KeyedVector::keyAt(size_t index) const { template inline VALUE& KeyedVector::editValueFor(const KEY& key) { - ssize_t i = indexOfKey(key); + ssize_t i = this->indexOfKey(key); assert(i>=0); return mVector.editItemAt(i).value; } @@ -190,7 +190,7 @@ DefaultKeyedVector::DefaultKeyedVector(const VALUE& defValue) template inline const VALUE& DefaultKeyedVector::valueFor(const KEY& key) const { - ssize_t i = indexOfKey(key); + ssize_t i = this->indexOfKey(key); return i >= 0 ? KeyedVector::valueAt(i) : mDefault; } From eb63cf29dbd3b6cdffffcd7cf661e581bddb61d3 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Mon, 20 Feb 2012 16:58:20 -0800 Subject: [PATCH 397/541] frameworks/base refactoring create the new libandroidfw from parts of libui and libutils Change-Id: I1584995616fff5d527a2aba63921b682a6194d58 --- libs/utils/Android.mk | 29 - libs/utils/Asset.cpp | 897 ----- libs/utils/AssetDir.cpp | 66 - libs/utils/AssetManager.cpp | 2001 ---------- libs/utils/BackupData.cpp | 381 -- libs/utils/BackupHelpers.cpp | 1591 -------- libs/utils/ObbFile.cpp | 345 -- libs/utils/ResourceTypes.cpp | 5580 --------------------------- libs/utils/StreamingZipInflater.cpp | 226 -- libs/utils/ZipFileCRO.cpp | 54 - libs/utils/ZipFileRO.cpp | 931 ----- libs/utils/ZipUtils.cpp | 343 -- libs/utils/tests/Android.mk | 2 - libs/utils/tests/ObbFile_test.cpp | 100 - libs/utils/tests/ZipFileRO_test.cpp | 64 - 15 files changed, 12610 deletions(-) delete mode 100644 libs/utils/Asset.cpp delete mode 100644 libs/utils/AssetDir.cpp delete mode 100644 libs/utils/AssetManager.cpp delete mode 100644 libs/utils/BackupData.cpp delete mode 100644 libs/utils/BackupHelpers.cpp delete mode 100644 libs/utils/ObbFile.cpp delete mode 100644 libs/utils/ResourceTypes.cpp delete mode 100644 libs/utils/StreamingZipInflater.cpp delete mode 100644 libs/utils/ZipFileCRO.cpp delete mode 100644 libs/utils/ZipFileRO.cpp delete mode 100644 libs/utils/ZipUtils.cpp delete mode 100644 libs/utils/tests/ObbFile_test.cpp delete mode 100644 libs/utils/tests/ZipFileRO_test.cpp diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk index 24cf5048f..a96c8e604 100644 --- a/libs/utils/Android.mk +++ b/libs/utils/Android.mk @@ -18,9 +18,6 @@ LOCAL_PATH:= $(call my-dir) # and once for the device. commonSources:= \ - Asset.cpp \ - AssetDir.cpp \ - AssetManager.cpp \ BasicHashtable.cpp \ BlobCache.cpp \ BufferedTextOutput.cpp \ @@ -29,14 +26,11 @@ commonSources:= \ FileMap.cpp \ Flattenable.cpp \ LinearTransform.cpp \ - ObbFile.cpp \ PropertyMap.cpp \ RefBase.cpp \ - ResourceTypes.cpp \ SharedBuffer.cpp \ Static.cpp \ StopWatch.cpp \ - StreamingZipInflater.cpp \ String8.cpp \ String16.cpp \ StringArray.cpp \ @@ -47,9 +41,6 @@ commonSources:= \ Tokenizer.cpp \ Unicode.cpp \ VectorImpl.cpp \ - ZipFileCRO.cpp \ - ZipFileRO.cpp \ - ZipUtils.cpp \ misc.cpp @@ -63,7 +54,6 @@ LOCAL_SRC_FILES:= $(commonSources) LOCAL_MODULE:= libutils LOCAL_CFLAGS += -DLIBUTILS_NATIVE=1 $(TOOL_CFLAGS) -LOCAL_C_INCLUDES += external/zlib ifeq ($(HOST_OS),windows) ifeq ($(strip $(USE_CYGWIN),),) @@ -79,7 +69,6 @@ endif include $(BUILD_HOST_STATIC_LIBRARY) - # For the device # ===================================================== include $(CLEAR_VARS) @@ -88,8 +77,6 @@ include $(CLEAR_VARS) # we have the common sources, plus some device-specific stuff LOCAL_SRC_FILES:= \ $(commonSources) \ - BackupData.cpp \ - BackupHelpers.cpp \ Looper.cpp ifeq ($(TARGET_OS),linux) @@ -97,14 +84,11 @@ LOCAL_LDLIBS += -lrt -ldl endif LOCAL_C_INCLUDES += \ - external/zlib \ - external/icu4c/common \ bionic/libc/private LOCAL_LDLIBS += -lpthread LOCAL_SHARED_LIBRARIES := \ - libz \ liblog \ libcutils \ libdl \ @@ -113,19 +97,6 @@ LOCAL_SHARED_LIBRARIES := \ LOCAL_MODULE:= libutils include $(BUILD_SHARED_LIBRARY) -ifeq ($(TARGET_OS),linux) -include $(CLEAR_VARS) -LOCAL_C_INCLUDES += \ - external/zlib \ - external/icu4c/common \ - bionic/libc/private -LOCAL_LDLIBS := -lrt -ldl -lpthread -LOCAL_MODULE := libutils -LOCAL_SRC_FILES := $(commonSources) BackupData.cpp BackupHelpers.cpp -include $(BUILD_STATIC_LIBRARY) -endif - - # Include subdirectory makefiles # ============================================================ diff --git a/libs/utils/Asset.cpp b/libs/utils/Asset.cpp deleted file mode 100644 index cb7628d1d..000000000 --- a/libs/utils/Asset.cpp +++ /dev/null @@ -1,897 +0,0 @@ -/* - * Copyright (C) 2006 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. - */ - -// -// Provide access to a read-only asset. -// - -#define LOG_TAG "asset" -//#define NDEBUG 0 - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace android; - -#ifndef O_BINARY -# define O_BINARY 0 -#endif - -static Mutex gAssetLock; -static int32_t gCount = 0; -static Asset* gHead = NULL; -static Asset* gTail = NULL; - -int32_t Asset::getGlobalCount() -{ - AutoMutex _l(gAssetLock); - return gCount; -} - -String8 Asset::getAssetAllocations() -{ - AutoMutex _l(gAssetLock); - String8 res; - Asset* cur = gHead; - while (cur != NULL) { - if (cur->isAllocated()) { - res.append(" "); - res.append(cur->getAssetSource()); - off64_t size = (cur->getLength()+512)/1024; - char buf[64]; - sprintf(buf, ": %dK\n", (int)size); - res.append(buf); - } - cur = cur->mNext; - } - - return res; -} - -Asset::Asset(void) - : mAccessMode(ACCESS_UNKNOWN) -{ - AutoMutex _l(gAssetLock); - gCount++; - mNext = mPrev = NULL; - if (gTail == NULL) { - gHead = gTail = this; - } else { - mPrev = gTail; - gTail->mNext = this; - gTail = this; - } - //ALOGI("Creating Asset %p #%d\n", this, gCount); -} - -Asset::~Asset(void) -{ - AutoMutex _l(gAssetLock); - gCount--; - if (gHead == this) { - gHead = mNext; - } - if (gTail == this) { - gTail = mPrev; - } - if (mNext != NULL) { - mNext->mPrev = mPrev; - } - if (mPrev != NULL) { - mPrev->mNext = mNext; - } - mNext = mPrev = NULL; - //ALOGI("Destroying Asset in %p #%d\n", this, gCount); -} - -/* - * Create a new Asset from a file on disk. There is a fair chance that - * the file doesn't actually exist. - * - * We can use "mode" to decide how we want to go about it. - */ -/*static*/ Asset* Asset::createFromFile(const char* fileName, AccessMode mode) -{ - _FileAsset* pAsset; - status_t result; - off64_t length; - int fd; - - fd = open(fileName, O_RDONLY | O_BINARY); - if (fd < 0) - return NULL; - - /* - * Under Linux, the lseek fails if we actually opened a directory. To - * be correct we should test the file type explicitly, but since we - * always open things read-only it doesn't really matter, so there's - * no value in incurring the extra overhead of an fstat() call. - */ - // TODO(kroot): replace this with fstat despite the plea above. -#if 1 - length = lseek64(fd, 0, SEEK_END); - if (length < 0) { - ::close(fd); - return NULL; - } - (void) lseek64(fd, 0, SEEK_SET); -#else - struct stat st; - if (fstat(fd, &st) < 0) { - ::close(fd); - return NULL; - } - - if (!S_ISREG(st.st_mode)) { - ::close(fd); - return NULL; - } -#endif - - pAsset = new _FileAsset; - result = pAsset->openChunk(fileName, fd, 0, length); - if (result != NO_ERROR) { - delete pAsset; - return NULL; - } - - pAsset->mAccessMode = mode; - return pAsset; -} - - -/* - * Create a new Asset from a compressed file on disk. There is a fair chance - * that the file doesn't actually exist. - * - * We currently support gzip files. We might want to handle .bz2 someday. - */ -/*static*/ Asset* Asset::createFromCompressedFile(const char* fileName, - AccessMode mode) -{ - _CompressedAsset* pAsset; - status_t result; - off64_t fileLen; - bool scanResult; - long offset; - int method; - long uncompressedLen, compressedLen; - int fd; - - fd = open(fileName, O_RDONLY | O_BINARY); - if (fd < 0) - return NULL; - - fileLen = lseek(fd, 0, SEEK_END); - if (fileLen < 0) { - ::close(fd); - return NULL; - } - (void) lseek(fd, 0, SEEK_SET); - - /* want buffered I/O for the file scan; must dup so fclose() is safe */ - FILE* fp = fdopen(dup(fd), "rb"); - if (fp == NULL) { - ::close(fd); - return NULL; - } - - unsigned long crc32; - scanResult = ZipUtils::examineGzip(fp, &method, &uncompressedLen, - &compressedLen, &crc32); - offset = ftell(fp); - fclose(fp); - if (!scanResult) { - ALOGD("File '%s' is not in gzip format\n", fileName); - ::close(fd); - return NULL; - } - - pAsset = new _CompressedAsset; - result = pAsset->openChunk(fd, offset, method, uncompressedLen, - compressedLen); - if (result != NO_ERROR) { - delete pAsset; - return NULL; - } - - pAsset->mAccessMode = mode; - return pAsset; -} - - -#if 0 -/* - * Create a new Asset from part of an open file. - */ -/*static*/ Asset* Asset::createFromFileSegment(int fd, off64_t offset, - size_t length, AccessMode mode) -{ - _FileAsset* pAsset; - status_t result; - - pAsset = new _FileAsset; - result = pAsset->openChunk(NULL, fd, offset, length); - if (result != NO_ERROR) - return NULL; - - pAsset->mAccessMode = mode; - return pAsset; -} - -/* - * Create a new Asset from compressed data in an open file. - */ -/*static*/ Asset* Asset::createFromCompressedData(int fd, off64_t offset, - int compressionMethod, size_t uncompressedLen, size_t compressedLen, - AccessMode mode) -{ - _CompressedAsset* pAsset; - status_t result; - - pAsset = new _CompressedAsset; - result = pAsset->openChunk(fd, offset, compressionMethod, - uncompressedLen, compressedLen); - if (result != NO_ERROR) - return NULL; - - pAsset->mAccessMode = mode; - return pAsset; -} -#endif - -/* - * Create a new Asset from a memory mapping. - */ -/*static*/ Asset* Asset::createFromUncompressedMap(FileMap* dataMap, - AccessMode mode) -{ - _FileAsset* pAsset; - status_t result; - - pAsset = new _FileAsset; - result = pAsset->openChunk(dataMap); - if (result != NO_ERROR) - return NULL; - - pAsset->mAccessMode = mode; - return pAsset; -} - -/* - * Create a new Asset from compressed data in a memory mapping. - */ -/*static*/ Asset* Asset::createFromCompressedMap(FileMap* dataMap, - int method, size_t uncompressedLen, AccessMode mode) -{ - _CompressedAsset* pAsset; - status_t result; - - pAsset = new _CompressedAsset; - result = pAsset->openChunk(dataMap, method, uncompressedLen); - if (result != NO_ERROR) - return NULL; - - pAsset->mAccessMode = mode; - return pAsset; -} - - -/* - * Do generic seek() housekeeping. Pass in the offset/whence values from - * the seek request, along with the current chunk offset and the chunk - * length. - * - * Returns the new chunk offset, or -1 if the seek is illegal. - */ -off64_t Asset::handleSeek(off64_t offset, int whence, off64_t curPosn, off64_t maxPosn) -{ - off64_t newOffset; - - switch (whence) { - case SEEK_SET: - newOffset = offset; - break; - case SEEK_CUR: - newOffset = curPosn + offset; - break; - case SEEK_END: - newOffset = maxPosn + offset; - break; - default: - ALOGW("unexpected whence %d\n", whence); - // this was happening due to an off64_t size mismatch - assert(false); - return (off64_t) -1; - } - - if (newOffset < 0 || newOffset > maxPosn) { - ALOGW("seek out of range: want %ld, end=%ld\n", - (long) newOffset, (long) maxPosn); - return (off64_t) -1; - } - - return newOffset; -} - - -/* - * =========================================================================== - * _FileAsset - * =========================================================================== - */ - -/* - * Constructor. - */ -_FileAsset::_FileAsset(void) - : mStart(0), mLength(0), mOffset(0), mFp(NULL), mFileName(NULL), mMap(NULL), mBuf(NULL) -{ -} - -/* - * Destructor. Release resources. - */ -_FileAsset::~_FileAsset(void) -{ - close(); -} - -/* - * Operate on a chunk of an uncompressed file. - * - * Zero-length chunks are allowed. - */ -status_t _FileAsset::openChunk(const char* fileName, int fd, off64_t offset, size_t length) -{ - assert(mFp == NULL); // no reopen - assert(mMap == NULL); - assert(fd >= 0); - assert(offset >= 0); - - /* - * Seek to end to get file length. - */ - off64_t fileLength; - fileLength = lseek64(fd, 0, SEEK_END); - if (fileLength == (off64_t) -1) { - // probably a bad file descriptor - ALOGD("failed lseek (errno=%d)\n", errno); - return UNKNOWN_ERROR; - } - - if ((off64_t) (offset + length) > fileLength) { - ALOGD("start (%ld) + len (%ld) > end (%ld)\n", - (long) offset, (long) length, (long) fileLength); - return BAD_INDEX; - } - - /* after fdopen, the fd will be closed on fclose() */ - mFp = fdopen(fd, "rb"); - if (mFp == NULL) - return UNKNOWN_ERROR; - - mStart = offset; - mLength = length; - assert(mOffset == 0); - - /* seek the FILE* to the start of chunk */ - if (fseek(mFp, mStart, SEEK_SET) != 0) { - assert(false); - } - - mFileName = fileName != NULL ? strdup(fileName) : NULL; - - return NO_ERROR; -} - -/* - * Create the chunk from the map. - */ -status_t _FileAsset::openChunk(FileMap* dataMap) -{ - assert(mFp == NULL); // no reopen - assert(mMap == NULL); - assert(dataMap != NULL); - - mMap = dataMap; - mStart = -1; // not used - mLength = dataMap->getDataLength(); - assert(mOffset == 0); - - return NO_ERROR; -} - -/* - * Read a chunk of data. - */ -ssize_t _FileAsset::read(void* buf, size_t count) -{ - size_t maxLen; - size_t actual; - - assert(mOffset >= 0 && mOffset <= mLength); - - if (getAccessMode() == ACCESS_BUFFER) { - /* - * On first access, read or map the entire file. The caller has - * requested buffer access, either because they're going to be - * using the buffer or because what they're doing has appropriate - * performance needs and access patterns. - */ - if (mBuf == NULL) - getBuffer(false); - } - - /* adjust count if we're near EOF */ - maxLen = mLength - mOffset; - if (count > maxLen) - count = maxLen; - - if (!count) - return 0; - - if (mMap != NULL) { - /* copy from mapped area */ - //printf("map read\n"); - memcpy(buf, (char*)mMap->getDataPtr() + mOffset, count); - actual = count; - } else if (mBuf != NULL) { - /* copy from buffer */ - //printf("buf read\n"); - memcpy(buf, (char*)mBuf + mOffset, count); - actual = count; - } else { - /* read from the file */ - //printf("file read\n"); - if (ftell(mFp) != mStart + mOffset) { - ALOGE("Hosed: %ld != %ld+%ld\n", - ftell(mFp), (long) mStart, (long) mOffset); - assert(false); - } - - /* - * This returns 0 on error or eof. We need to use ferror() or feof() - * to tell the difference, but we don't currently have those on the - * device. However, we know how much data is *supposed* to be in the - * file, so if we don't read the full amount we know something is - * hosed. - */ - actual = fread(buf, 1, count, mFp); - if (actual == 0) // something failed -- I/O error? - return -1; - - assert(actual == count); - } - - mOffset += actual; - return actual; -} - -/* - * Seek to a new position. - */ -off64_t _FileAsset::seek(off64_t offset, int whence) -{ - off64_t newPosn; - off64_t actualOffset; - - // compute new position within chunk - newPosn = handleSeek(offset, whence, mOffset, mLength); - if (newPosn == (off64_t) -1) - return newPosn; - - actualOffset = mStart + newPosn; - - if (mFp != NULL) { - if (fseek(mFp, (long) actualOffset, SEEK_SET) != 0) - return (off64_t) -1; - } - - mOffset = actualOffset - mStart; - return mOffset; -} - -/* - * Close the asset. - */ -void _FileAsset::close(void) -{ - if (mMap != NULL) { - mMap->release(); - mMap = NULL; - } - if (mBuf != NULL) { - delete[] mBuf; - mBuf = NULL; - } - - if (mFileName != NULL) { - free(mFileName); - mFileName = NULL; - } - - if (mFp != NULL) { - // can only be NULL when called from destructor - // (otherwise we would never return this object) - fclose(mFp); - mFp = NULL; - } -} - -/* - * Return a read-only pointer to a buffer. - * - * We can either read the whole thing in or map the relevant piece of - * the source file. Ideally a map would be established at a higher - * level and we'd be using a different object, but we didn't, so we - * deal with it here. - */ -const void* _FileAsset::getBuffer(bool wordAligned) -{ - /* subsequent requests just use what we did previously */ - if (mBuf != NULL) - return mBuf; - if (mMap != NULL) { - if (!wordAligned) { - return mMap->getDataPtr(); - } - return ensureAlignment(mMap); - } - - assert(mFp != NULL); - - if (mLength < kReadVsMapThreshold) { - unsigned char* buf; - long allocLen; - - /* zero-length files are allowed; not sure about zero-len allocs */ - /* (works fine with gcc + x86linux) */ - allocLen = mLength; - if (mLength == 0) - allocLen = 1; - - buf = new unsigned char[allocLen]; - if (buf == NULL) { - ALOGE("alloc of %ld bytes failed\n", (long) allocLen); - return NULL; - } - - ALOGV("Asset %p allocating buffer size %d (smaller than threshold)", this, (int)allocLen); - if (mLength > 0) { - long oldPosn = ftell(mFp); - fseek(mFp, mStart, SEEK_SET); - if (fread(buf, 1, mLength, mFp) != (size_t) mLength) { - ALOGE("failed reading %ld bytes\n", (long) mLength); - delete[] buf; - return NULL; - } - fseek(mFp, oldPosn, SEEK_SET); - } - - ALOGV(" getBuffer: loaded into buffer\n"); - - mBuf = buf; - return mBuf; - } else { - FileMap* map; - - map = new FileMap; - if (!map->create(NULL, fileno(mFp), mStart, mLength, true)) { - map->release(); - return NULL; - } - - ALOGV(" getBuffer: mapped\n"); - - mMap = map; - if (!wordAligned) { - return mMap->getDataPtr(); - } - return ensureAlignment(mMap); - } -} - -int _FileAsset::openFileDescriptor(off64_t* outStart, off64_t* outLength) const -{ - if (mMap != NULL) { - const char* fname = mMap->getFileName(); - if (fname == NULL) { - fname = mFileName; - } - if (fname == NULL) { - return -1; - } - *outStart = mMap->getDataOffset(); - *outLength = mMap->getDataLength(); - return open(fname, O_RDONLY | O_BINARY); - } - if (mFileName == NULL) { - return -1; - } - *outStart = mStart; - *outLength = mLength; - return open(mFileName, O_RDONLY | O_BINARY); -} - -const void* _FileAsset::ensureAlignment(FileMap* map) -{ - void* data = map->getDataPtr(); - if ((((size_t)data)&0x3) == 0) { - // We can return this directly if it is aligned on a word - // boundary. - ALOGV("Returning aligned FileAsset %p (%s).", this, - getAssetSource()); - return data; - } - // If not aligned on a word boundary, then we need to copy it into - // our own buffer. - ALOGV("Copying FileAsset %p (%s) to buffer size %d to make it aligned.", this, - getAssetSource(), (int)mLength); - unsigned char* buf = new unsigned char[mLength]; - if (buf == NULL) { - ALOGE("alloc of %ld bytes failed\n", (long) mLength); - return NULL; - } - memcpy(buf, data, mLength); - mBuf = buf; - return buf; -} - -/* - * =========================================================================== - * _CompressedAsset - * =========================================================================== - */ - -/* - * Constructor. - */ -_CompressedAsset::_CompressedAsset(void) - : mStart(0), mCompressedLen(0), mUncompressedLen(0), mOffset(0), - mMap(NULL), mFd(-1), mZipInflater(NULL), mBuf(NULL) -{ -} - -/* - * Destructor. Release resources. - */ -_CompressedAsset::~_CompressedAsset(void) -{ - close(); -} - -/* - * Open a chunk of compressed data inside a file. - * - * This currently just sets up some values and returns. On the first - * read, we expand the entire file into a buffer and return data from it. - */ -status_t _CompressedAsset::openChunk(int fd, off64_t offset, - int compressionMethod, size_t uncompressedLen, size_t compressedLen) -{ - assert(mFd < 0); // no re-open - assert(mMap == NULL); - assert(fd >= 0); - assert(offset >= 0); - assert(compressedLen > 0); - - if (compressionMethod != ZipFileRO::kCompressDeflated) { - assert(false); - return UNKNOWN_ERROR; - } - - mStart = offset; - mCompressedLen = compressedLen; - mUncompressedLen = uncompressedLen; - assert(mOffset == 0); - mFd = fd; - assert(mBuf == NULL); - - if (uncompressedLen > StreamingZipInflater::OUTPUT_CHUNK_SIZE) { - mZipInflater = new StreamingZipInflater(mFd, offset, uncompressedLen, compressedLen); - } - - return NO_ERROR; -} - -/* - * Open a chunk of compressed data in a mapped region. - * - * Nothing is expanded until the first read call. - */ -status_t _CompressedAsset::openChunk(FileMap* dataMap, int compressionMethod, - size_t uncompressedLen) -{ - assert(mFd < 0); // no re-open - assert(mMap == NULL); - assert(dataMap != NULL); - - if (compressionMethod != ZipFileRO::kCompressDeflated) { - assert(false); - return UNKNOWN_ERROR; - } - - mMap = dataMap; - mStart = -1; // not used - mCompressedLen = dataMap->getDataLength(); - mUncompressedLen = uncompressedLen; - assert(mOffset == 0); - - if (uncompressedLen > StreamingZipInflater::OUTPUT_CHUNK_SIZE) { - mZipInflater = new StreamingZipInflater(dataMap, uncompressedLen); - } - return NO_ERROR; -} - -/* - * Read data from a chunk of compressed data. - * - * [For now, that's just copying data out of a buffer.] - */ -ssize_t _CompressedAsset::read(void* buf, size_t count) -{ - size_t maxLen; - size_t actual; - - assert(mOffset >= 0 && mOffset <= mUncompressedLen); - - /* If we're relying on a streaming inflater, go through that */ - if (mZipInflater) { - actual = mZipInflater->read(buf, count); - } else { - if (mBuf == NULL) { - if (getBuffer(false) == NULL) - return -1; - } - assert(mBuf != NULL); - - /* adjust count if we're near EOF */ - maxLen = mUncompressedLen - mOffset; - if (count > maxLen) - count = maxLen; - - if (!count) - return 0; - - /* copy from buffer */ - //printf("comp buf read\n"); - memcpy(buf, (char*)mBuf + mOffset, count); - actual = count; - } - - mOffset += actual; - return actual; -} - -/* - * Handle a seek request. - * - * If we're working in a streaming mode, this is going to be fairly - * expensive, because it requires plowing through a bunch of compressed - * data. - */ -off64_t _CompressedAsset::seek(off64_t offset, int whence) -{ - off64_t newPosn; - - // compute new position within chunk - newPosn = handleSeek(offset, whence, mOffset, mUncompressedLen); - if (newPosn == (off64_t) -1) - return newPosn; - - if (mZipInflater) { - mZipInflater->seekAbsolute(newPosn); - } - mOffset = newPosn; - return mOffset; -} - -/* - * Close the asset. - */ -void _CompressedAsset::close(void) -{ - if (mMap != NULL) { - mMap->release(); - mMap = NULL; - } - - delete[] mBuf; - mBuf = NULL; - - delete mZipInflater; - mZipInflater = NULL; - - if (mFd > 0) { - ::close(mFd); - mFd = -1; - } -} - -/* - * Get a pointer to a read-only buffer of data. - * - * The first time this is called, we expand the compressed data into a - * buffer. - */ -const void* _CompressedAsset::getBuffer(bool wordAligned) -{ - unsigned char* buf = NULL; - - if (mBuf != NULL) - return mBuf; - - /* - * Allocate a buffer and read the file into it. - */ - buf = new unsigned char[mUncompressedLen]; - if (buf == NULL) { - ALOGW("alloc %ld bytes failed\n", (long) mUncompressedLen); - goto bail; - } - - if (mMap != NULL) { - if (!ZipFileRO::inflateBuffer(buf, mMap->getDataPtr(), - mUncompressedLen, mCompressedLen)) - goto bail; - } else { - assert(mFd >= 0); - - /* - * Seek to the start of the compressed data. - */ - if (lseek(mFd, mStart, SEEK_SET) != mStart) - goto bail; - - /* - * Expand the data into it. - */ - if (!ZipUtils::inflateToBuffer(mFd, buf, mUncompressedLen, - mCompressedLen)) - goto bail; - } - - /* - * Success - now that we have the full asset in RAM we - * no longer need the streaming inflater - */ - delete mZipInflater; - mZipInflater = NULL; - - mBuf = buf; - buf = NULL; - -bail: - delete[] buf; - return mBuf; -} - diff --git a/libs/utils/AssetDir.cpp b/libs/utils/AssetDir.cpp deleted file mode 100644 index 475f521c1..000000000 --- a/libs/utils/AssetDir.cpp +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2006 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. - */ - -// -// Provide access to a virtual directory in "asset space". Most of the -// implementation is in the header file or in friend functions in -// AssetManager. -// -#include - -using namespace android; - - -/* - * Find a matching entry in a vector of FileInfo. Because it's sorted, we - * can use a binary search. - * - * Assumes the vector is sorted in ascending order. - */ -/*static*/ int AssetDir::FileInfo::findEntry(const SortedVector* pVector, - const String8& fileName) -{ - FileInfo tmpInfo; - - tmpInfo.setFileName(fileName); - return pVector->indexOf(tmpInfo); - -#if 0 // don't need this after all (uses 1/2 compares of SortedVector though) - int lo, hi, cur; - - lo = 0; - hi = pVector->size() -1; - while (lo <= hi) { - int cmp; - - cur = (hi + lo) / 2; - cmp = strcmp(pVector->itemAt(cur).getFileName(), fileName); - if (cmp == 0) { - /* match, bail */ - return cur; - } else if (cmp < 0) { - /* too low */ - lo = cur + 1; - } else { - /* too high */ - hi = cur -1; - } - } - - return -1; -#endif -} - diff --git a/libs/utils/AssetManager.cpp b/libs/utils/AssetManager.cpp deleted file mode 100644 index 4829addec..000000000 --- a/libs/utils/AssetManager.cpp +++ /dev/null @@ -1,2001 +0,0 @@ -/* - * Copyright (C) 2006 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. - */ - -// -// Provide access to read-only assets. -// - -#define LOG_TAG "asset" -//#define LOG_NDEBUG 0 - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#ifndef TEMP_FAILURE_RETRY -/* Used to retry syscalls that can return EINTR. */ -#define TEMP_FAILURE_RETRY(exp) ({ \ - typeof (exp) _rc; \ - do { \ - _rc = (exp); \ - } while (_rc == -1 && errno == EINTR); \ - _rc; }) -#endif - -using namespace android; - -/* - * Names for default app, locale, and vendor. We might want to change - * these to be an actual locale, e.g. always use en-US as the default. - */ -static const char* kDefaultLocale = "default"; -static const char* kDefaultVendor = "default"; -static const char* kAssetsRoot = "assets"; -static const char* kAppZipName = NULL; //"classes.jar"; -static const char* kSystemAssets = "framework/framework-res.apk"; -static const char* kIdmapCacheDir = "resource-cache"; - -static const char* kExcludeExtension = ".EXCLUDE"; - -static Asset* const kExcludedAsset = (Asset*) 0xd000000d; - -static volatile int32_t gCount = 0; - -namespace { - // Transform string /a/b/c.apk to /data/resource-cache/a@b@c.apk@idmap - String8 idmapPathForPackagePath(const String8& pkgPath) - { - const char* root = getenv("ANDROID_DATA"); - LOG_ALWAYS_FATAL_IF(root == NULL, "ANDROID_DATA not set"); - String8 path(root); - path.appendPath(kIdmapCacheDir); - - char buf[256]; // 256 chars should be enough for anyone... - strncpy(buf, pkgPath.string(), 255); - buf[255] = '\0'; - char* filename = buf; - while (*filename && *filename == '/') { - ++filename; - } - char* p = filename; - while (*p) { - if (*p == '/') { - *p = '@'; - } - ++p; - } - path.appendPath(filename); - path.append("@idmap"); - - return path; - } -} - -/* - * =========================================================================== - * AssetManager - * =========================================================================== - */ - -int32_t AssetManager::getGlobalCount() -{ - return gCount; -} - -AssetManager::AssetManager(CacheMode cacheMode) - : mLocale(NULL), mVendor(NULL), - mResources(NULL), mConfig(new ResTable_config), - mCacheMode(cacheMode), mCacheValid(false) -{ - int count = android_atomic_inc(&gCount)+1; - //ALOGI("Creating AssetManager %p #%d\n", this, count); - memset(mConfig, 0, sizeof(ResTable_config)); -} - -AssetManager::~AssetManager(void) -{ - int count = android_atomic_dec(&gCount); - //ALOGI("Destroying AssetManager in %p #%d\n", this, count); - - delete mConfig; - delete mResources; - - // don't have a String class yet, so make sure we clean up - delete[] mLocale; - delete[] mVendor; -} - -bool AssetManager::addAssetPath(const String8& path, void** cookie) -{ - AutoMutex _l(mLock); - - asset_path ap; - - String8 realPath(path); - if (kAppZipName) { - realPath.appendPath(kAppZipName); - } - ap.type = ::getFileType(realPath.string()); - if (ap.type == kFileTypeRegular) { - ap.path = realPath; - } else { - ap.path = path; - ap.type = ::getFileType(path.string()); - if (ap.type != kFileTypeDirectory && ap.type != kFileTypeRegular) { - ALOGW("Asset path %s is neither a directory nor file (type=%d).", - path.string(), (int)ap.type); - return false; - } - } - - // Skip if we have it already. - for (size_t i=0; ifindEntryByName(entryFilename); - if (entry == NULL) { - return false; - } - if (!zip->getEntryInfo(entry, NULL, NULL, NULL, NULL, NULL, (long*)pCrc)) { - return false; - } - return true; -} - -bool AssetManager::createIdmapFileLocked(const String8& originalPath, const String8& overlayPath, - const String8& idmapPath) -{ - ALOGD("%s: originalPath=%s overlayPath=%s idmapPath=%s\n", - __FUNCTION__, originalPath.string(), overlayPath.string(), idmapPath.string()); - ResTable tables[2]; - const String8* paths[2] = { &originalPath, &overlayPath }; - uint32_t originalCrc, overlayCrc; - bool retval = false; - ssize_t offset = 0; - int fd = 0; - uint32_t* data = NULL; - size_t size; - - for (int i = 0; i < 2; ++i) { - asset_path ap; - ap.type = kFileTypeRegular; - ap.path = *paths[i]; - Asset* ass = openNonAssetInPathLocked("resources.arsc", Asset::ACCESS_BUFFER, ap); - if (ass == NULL) { - ALOGW("failed to find resources.arsc in %s\n", ap.path.string()); - goto error; - } - tables[i].add(ass, (void*)1, false); - } - - if (!getZipEntryCrcLocked(originalPath, "resources.arsc", &originalCrc)) { - ALOGW("failed to retrieve crc for resources.arsc in %s\n", originalPath.string()); - goto error; - } - if (!getZipEntryCrcLocked(overlayPath, "resources.arsc", &overlayCrc)) { - ALOGW("failed to retrieve crc for resources.arsc in %s\n", overlayPath.string()); - goto error; - } - - if (tables[0].createIdmap(tables[1], originalCrc, overlayCrc, - (void**)&data, &size) != NO_ERROR) { - ALOGW("failed to generate idmap data for file %s\n", idmapPath.string()); - goto error; - } - - // This should be abstracted (eg replaced by a stand-alone - // application like dexopt, triggered by something equivalent to - // installd). - fd = TEMP_FAILURE_RETRY(::open(idmapPath.string(), O_WRONLY | O_CREAT | O_TRUNC, 0644)); - if (fd == -1) { - ALOGW("failed to write idmap file %s (open: %s)\n", idmapPath.string(), strerror(errno)); - goto error_free; - } - for (;;) { - ssize_t written = TEMP_FAILURE_RETRY(write(fd, data + offset, size)); - if (written < 0) { - ALOGW("failed to write idmap file %s (write: %s)\n", idmapPath.string(), - strerror(errno)); - goto error_close; - } - size -= (size_t)written; - offset += written; - if (size == 0) { - break; - } - } - - retval = true; -error_close: - TEMP_FAILURE_RETRY(close(fd)); -error_free: - free(data); -error: - return retval; -} - -bool AssetManager::addDefaultAssets() -{ - const char* root = getenv("ANDROID_ROOT"); - LOG_ALWAYS_FATAL_IF(root == NULL, "ANDROID_ROOT not set"); - - String8 path(root); - path.appendPath(kSystemAssets); - - return addAssetPath(path, NULL); -} - -void* AssetManager::nextAssetPath(void* cookie) const -{ - AutoMutex _l(mLock); - size_t next = ((size_t)cookie)+1; - return next > mAssetPaths.size() ? NULL : (void*)next; -} - -String8 AssetManager::getAssetPath(void* cookie) const -{ - AutoMutex _l(mLock); - const size_t which = ((size_t)cookie)-1; - if (which < mAssetPaths.size()) { - return mAssetPaths[which].path; - } - return String8(); -} - -/* - * Set the current locale. Use NULL to indicate no locale. - * - * Close and reopen Zip archives as appropriate, and reset cached - * information in the locale-specific sections of the tree. - */ -void AssetManager::setLocale(const char* locale) -{ - AutoMutex _l(mLock); - setLocaleLocked(locale); -} - -void AssetManager::setLocaleLocked(const char* locale) -{ - if (mLocale != NULL) { - /* previously set, purge cached data */ - purgeFileNameCacheLocked(); - //mZipSet.purgeLocale(); - delete[] mLocale; - } - mLocale = strdupNew(locale); - - updateResourceParamsLocked(); -} - -/* - * Set the current vendor. Use NULL to indicate no vendor. - * - * Close and reopen Zip archives as appropriate, and reset cached - * information in the vendor-specific sections of the tree. - */ -void AssetManager::setVendor(const char* vendor) -{ - AutoMutex _l(mLock); - - if (mVendor != NULL) { - /* previously set, purge cached data */ - purgeFileNameCacheLocked(); - //mZipSet.purgeVendor(); - delete[] mVendor; - } - mVendor = strdupNew(vendor); -} - -void AssetManager::setConfiguration(const ResTable_config& config, const char* locale) -{ - AutoMutex _l(mLock); - *mConfig = config; - if (locale) { - setLocaleLocked(locale); - } else if (config.language[0] != 0) { - char spec[9]; - spec[0] = config.language[0]; - spec[1] = config.language[1]; - if (config.country[0] != 0) { - spec[2] = '_'; - spec[3] = config.country[0]; - spec[4] = config.country[1]; - spec[5] = 0; - } else { - spec[3] = 0; - } - setLocaleLocked(spec); - } else { - updateResourceParamsLocked(); - } -} - -void AssetManager::getConfiguration(ResTable_config* outConfig) const -{ - AutoMutex _l(mLock); - *outConfig = *mConfig; -} - -/* - * Open an asset. - * - * The data could be; - * - In a file on disk (assetBase + fileName). - * - In a compressed file on disk (assetBase + fileName.gz). - * - In a Zip archive, uncompressed or compressed. - * - * It can be in a number of different directories and Zip archives. - * The search order is: - * - [appname] - * - locale + vendor - * - "default" + vendor - * - locale + "default" - * - "default + "default" - * - "common" - * - (same as above) - * - * To find a particular file, we have to try up to eight paths with - * all three forms of data. - * - * We should probably reject requests for "illegal" filenames, e.g. those - * with illegal characters or "../" backward relative paths. - */ -Asset* AssetManager::open(const char* fileName, AccessMode mode) -{ - AutoMutex _l(mLock); - - LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager"); - - - if (mCacheMode != CACHE_OFF && !mCacheValid) - loadFileNameCacheLocked(); - - String8 assetName(kAssetsRoot); - assetName.appendPath(fileName); - - /* - * For each top-level asset path, search for the asset. - */ - - size_t i = mAssetPaths.size(); - while (i > 0) { - i--; - ALOGV("Looking for asset '%s' in '%s'\n", - assetName.string(), mAssetPaths.itemAt(i).path.string()); - Asset* pAsset = openNonAssetInPathLocked(assetName.string(), mode, mAssetPaths.itemAt(i)); - if (pAsset != NULL) { - return pAsset != kExcludedAsset ? pAsset : NULL; - } - } - - return NULL; -} - -/* - * Open a non-asset file as if it were an asset. - * - * The "fileName" is the partial path starting from the application - * name. - */ -Asset* AssetManager::openNonAsset(const char* fileName, AccessMode mode) -{ - AutoMutex _l(mLock); - - LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager"); - - - if (mCacheMode != CACHE_OFF && !mCacheValid) - loadFileNameCacheLocked(); - - /* - * For each top-level asset path, search for the asset. - */ - - size_t i = mAssetPaths.size(); - while (i > 0) { - i--; - ALOGV("Looking for non-asset '%s' in '%s'\n", fileName, mAssetPaths.itemAt(i).path.string()); - Asset* pAsset = openNonAssetInPathLocked( - fileName, mode, mAssetPaths.itemAt(i)); - if (pAsset != NULL) { - return pAsset != kExcludedAsset ? pAsset : NULL; - } - } - - return NULL; -} - -Asset* AssetManager::openNonAsset(void* cookie, const char* fileName, AccessMode mode) -{ - const size_t which = ((size_t)cookie)-1; - - AutoMutex _l(mLock); - - LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager"); - - - if (mCacheMode != CACHE_OFF && !mCacheValid) - loadFileNameCacheLocked(); - - if (which < mAssetPaths.size()) { - ALOGV("Looking for non-asset '%s' in '%s'\n", fileName, - mAssetPaths.itemAt(which).path.string()); - Asset* pAsset = openNonAssetInPathLocked( - fileName, mode, mAssetPaths.itemAt(which)); - if (pAsset != NULL) { - return pAsset != kExcludedAsset ? pAsset : NULL; - } - } - - return NULL; -} - -/* - * Get the type of a file in the asset namespace. - * - * This currently only works for regular files. All others (including - * directories) will return kFileTypeNonexistent. - */ -FileType AssetManager::getFileType(const char* fileName) -{ - Asset* pAsset = NULL; - - /* - * Open the asset. This is less efficient than simply finding the - * file, but it's not too bad (we don't uncompress or mmap data until - * the first read() call). - */ - pAsset = open(fileName, Asset::ACCESS_STREAMING); - delete pAsset; - - if (pAsset == NULL) - return kFileTypeNonexistent; - else - return kFileTypeRegular; -} - -const ResTable* AssetManager::getResTable(bool required) const -{ - ResTable* rt = mResources; - if (rt) { - return rt; - } - - // Iterate through all asset packages, collecting resources from each. - - AutoMutex _l(mLock); - - if (mResources != NULL) { - return mResources; - } - - if (required) { - LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager"); - } - - if (mCacheMode != CACHE_OFF && !mCacheValid) - const_cast(this)->loadFileNameCacheLocked(); - - const size_t N = mAssetPaths.size(); - for (size_t i=0; i(this)-> - mZipSet.getZipResourceTable(ap.path); - } - if (sharedRes == NULL) { - ass = const_cast(this)-> - mZipSet.getZipResourceTableAsset(ap.path); - if (ass == NULL) { - ALOGV("loading resource table %s\n", ap.path.string()); - ass = const_cast(this)-> - openNonAssetInPathLocked("resources.arsc", - Asset::ACCESS_BUFFER, - ap); - if (ass != NULL && ass != kExcludedAsset) { - ass = const_cast(this)-> - mZipSet.setZipResourceTableAsset(ap.path, ass); - } - } - - if (i == 0 && ass != NULL) { - // If this is the first resource table in the asset - // manager, then we are going to cache it so that we - // can quickly copy it out for others. - ALOGV("Creating shared resources for %s", ap.path.string()); - sharedRes = new ResTable(); - sharedRes->add(ass, (void*)(i+1), false, idmap); - sharedRes = const_cast(this)-> - mZipSet.setZipResourceTable(ap.path, sharedRes); - } - } - } else { - ALOGV("loading resource table %s\n", ap.path.string()); - Asset* ass = const_cast(this)-> - openNonAssetInPathLocked("resources.arsc", - Asset::ACCESS_BUFFER, - ap); - shared = false; - } - if ((ass != NULL || sharedRes != NULL) && ass != kExcludedAsset) { - if (rt == NULL) { - mResources = rt = new ResTable(); - updateResourceParamsLocked(); - } - ALOGV("Installing resource asset %p in to table %p\n", ass, mResources); - if (sharedRes != NULL) { - ALOGV("Copying existing resources for %s", ap.path.string()); - rt->add(sharedRes); - } else { - ALOGV("Parsing resources for %s", ap.path.string()); - rt->add(ass, (void*)(i+1), !shared, idmap); - } - - if (!shared) { - delete ass; - } - } - if (idmap != NULL) { - delete idmap; - } - } - - if (required && !rt) ALOGW("Unable to find resources file resources.arsc"); - if (!rt) { - mResources = rt = new ResTable(); - } - return rt; -} - -void AssetManager::updateResourceParamsLocked() const -{ - ResTable* res = mResources; - if (!res) { - return; - } - - size_t llen = mLocale ? strlen(mLocale) : 0; - mConfig->language[0] = 0; - mConfig->language[1] = 0; - mConfig->country[0] = 0; - mConfig->country[1] = 0; - if (llen >= 2) { - mConfig->language[0] = mLocale[0]; - mConfig->language[1] = mLocale[1]; - } - if (llen >= 5) { - mConfig->country[0] = mLocale[3]; - mConfig->country[1] = mLocale[4]; - } - mConfig->size = sizeof(*mConfig); - - res->setParameters(mConfig); -} - -Asset* AssetManager::openIdmapLocked(const struct asset_path& ap) const -{ - Asset* ass = NULL; - if (ap.idmap.size() != 0) { - ass = const_cast(this)-> - openAssetFromFileLocked(ap.idmap, Asset::ACCESS_BUFFER); - if (ass) { - ALOGV("loading idmap %s\n", ap.idmap.string()); - } else { - ALOGW("failed to load idmap %s\n", ap.idmap.string()); - } - } - return ass; -} - -const ResTable& AssetManager::getResources(bool required) const -{ - const ResTable* rt = getResTable(required); - return *rt; -} - -bool AssetManager::isUpToDate() -{ - AutoMutex _l(mLock); - return mZipSet.isUpToDate(); -} - -void AssetManager::getLocales(Vector* locales) const -{ - ResTable* res = mResources; - if (res != NULL) { - res->getLocales(locales); - } -} - -/* - * Open a non-asset file as if it were an asset, searching for it in the - * specified app. - * - * Pass in a NULL values for "appName" if the common app directory should - * be used. - */ -Asset* AssetManager::openNonAssetInPathLocked(const char* fileName, AccessMode mode, - const asset_path& ap) -{ - Asset* pAsset = NULL; - - /* look at the filesystem on disk */ - if (ap.type == kFileTypeDirectory) { - String8 path(ap.path); - path.appendPath(fileName); - - pAsset = openAssetFromFileLocked(path, mode); - - if (pAsset == NULL) { - /* try again, this time with ".gz" */ - path.append(".gz"); - pAsset = openAssetFromFileLocked(path, mode); - } - - if (pAsset != NULL) { - //printf("FOUND NA '%s' on disk\n", fileName); - pAsset->setAssetSource(path); - } - - /* look inside the zip file */ - } else { - String8 path(fileName); - - /* check the appropriate Zip file */ - ZipFileRO* pZip; - ZipEntryRO entry; - - pZip = getZipFileLocked(ap); - if (pZip != NULL) { - //printf("GOT zip, checking NA '%s'\n", (const char*) path); - entry = pZip->findEntryByName(path.string()); - if (entry != NULL) { - //printf("FOUND NA in Zip file for %s\n", appName ? appName : kAppCommon); - pAsset = openAssetFromZipLocked(pZip, entry, mode, path); - } - } - - if (pAsset != NULL) { - /* create a "source" name, for debug/display */ - pAsset->setAssetSource( - createZipSourceNameLocked(ZipSet::getPathName(ap.path.string()), String8(""), - String8(fileName))); - } - } - - return pAsset; -} - -/* - * Open an asset, searching for it in the directory hierarchy for the - * specified app. - * - * Pass in a NULL values for "appName" if the common app directory should - * be used. - */ -Asset* AssetManager::openInPathLocked(const char* fileName, AccessMode mode, - const asset_path& ap) -{ - Asset* pAsset = NULL; - - /* - * Try various combinations of locale and vendor. - */ - if (mLocale != NULL && mVendor != NULL) - pAsset = openInLocaleVendorLocked(fileName, mode, ap, mLocale, mVendor); - if (pAsset == NULL && mVendor != NULL) - pAsset = openInLocaleVendorLocked(fileName, mode, ap, NULL, mVendor); - if (pAsset == NULL && mLocale != NULL) - pAsset = openInLocaleVendorLocked(fileName, mode, ap, mLocale, NULL); - if (pAsset == NULL) - pAsset = openInLocaleVendorLocked(fileName, mode, ap, NULL, NULL); - - return pAsset; -} - -/* - * Open an asset, searching for it in the directory hierarchy for the - * specified locale and vendor. - * - * We also search in "app.jar". - * - * Pass in NULL values for "appName", "locale", and "vendor" if the - * defaults should be used. - */ -Asset* AssetManager::openInLocaleVendorLocked(const char* fileName, AccessMode mode, - const asset_path& ap, const char* locale, const char* vendor) -{ - Asset* pAsset = NULL; - - if (ap.type == kFileTypeDirectory) { - if (mCacheMode == CACHE_OFF) { - /* look at the filesystem on disk */ - String8 path(createPathNameLocked(ap, locale, vendor)); - path.appendPath(fileName); - - String8 excludeName(path); - excludeName.append(kExcludeExtension); - if (::getFileType(excludeName.string()) != kFileTypeNonexistent) { - /* say no more */ - //printf("+++ excluding '%s'\n", (const char*) excludeName); - return kExcludedAsset; - } - - pAsset = openAssetFromFileLocked(path, mode); - - if (pAsset == NULL) { - /* try again, this time with ".gz" */ - path.append(".gz"); - pAsset = openAssetFromFileLocked(path, mode); - } - - if (pAsset != NULL) - pAsset->setAssetSource(path); - } else { - /* find in cache */ - String8 path(createPathNameLocked(ap, locale, vendor)); - path.appendPath(fileName); - - AssetDir::FileInfo tmpInfo; - bool found = false; - - String8 excludeName(path); - excludeName.append(kExcludeExtension); - - if (mCache.indexOf(excludeName) != NAME_NOT_FOUND) { - /* go no farther */ - //printf("+++ Excluding '%s'\n", (const char*) excludeName); - return kExcludedAsset; - } - - /* - * File compression extensions (".gz") don't get stored in the - * name cache, so we have to try both here. - */ - if (mCache.indexOf(path) != NAME_NOT_FOUND) { - found = true; - pAsset = openAssetFromFileLocked(path, mode); - if (pAsset == NULL) { - /* try again, this time with ".gz" */ - path.append(".gz"); - pAsset = openAssetFromFileLocked(path, mode); - } - } - - if (pAsset != NULL) - pAsset->setAssetSource(path); - - /* - * Don't continue the search into the Zip files. Our cached info - * said it was a file on disk; to be consistent with openDir() - * we want to return the loose asset. If the cached file gets - * removed, we fail. - * - * The alternative is to update our cache when files get deleted, - * or make some sort of "best effort" promise, but for now I'm - * taking the hard line. - */ - if (found) { - if (pAsset == NULL) - ALOGD("Expected file not found: '%s'\n", path.string()); - return pAsset; - } - } - } - - /* - * Either it wasn't found on disk or on the cached view of the disk. - * Dig through the currently-opened set of Zip files. If caching - * is disabled, the Zip file may get reopened. - */ - if (pAsset == NULL && ap.type == kFileTypeRegular) { - String8 path; - - path.appendPath((locale != NULL) ? locale : kDefaultLocale); - path.appendPath((vendor != NULL) ? vendor : kDefaultVendor); - path.appendPath(fileName); - - /* check the appropriate Zip file */ - ZipFileRO* pZip; - ZipEntryRO entry; - - pZip = getZipFileLocked(ap); - if (pZip != NULL) { - //printf("GOT zip, checking '%s'\n", (const char*) path); - entry = pZip->findEntryByName(path.string()); - if (entry != NULL) { - //printf("FOUND in Zip file for %s/%s-%s\n", - // appName, locale, vendor); - pAsset = openAssetFromZipLocked(pZip, entry, mode, path); - } - } - - if (pAsset != NULL) { - /* create a "source" name, for debug/display */ - pAsset->setAssetSource(createZipSourceNameLocked(ZipSet::getPathName(ap.path.string()), - String8(""), String8(fileName))); - } - } - - return pAsset; -} - -/* - * Create a "source name" for a file from a Zip archive. - */ -String8 AssetManager::createZipSourceNameLocked(const String8& zipFileName, - const String8& dirName, const String8& fileName) -{ - String8 sourceName("zip:"); - sourceName.append(zipFileName); - sourceName.append(":"); - if (dirName.length() > 0) { - sourceName.appendPath(dirName); - } - sourceName.appendPath(fileName); - return sourceName; -} - -/* - * Create a path to a loose asset (asset-base/app/locale/vendor). - */ -String8 AssetManager::createPathNameLocked(const asset_path& ap, const char* locale, - const char* vendor) -{ - String8 path(ap.path); - path.appendPath((locale != NULL) ? locale : kDefaultLocale); - path.appendPath((vendor != NULL) ? vendor : kDefaultVendor); - return path; -} - -/* - * Create a path to a loose asset (asset-base/app/rootDir). - */ -String8 AssetManager::createPathNameLocked(const asset_path& ap, const char* rootDir) -{ - String8 path(ap.path); - if (rootDir != NULL) path.appendPath(rootDir); - return path; -} - -/* - * Return a pointer to one of our open Zip archives. Returns NULL if no - * matching Zip file exists. - * - * Right now we have 2 possible Zip files (1 each in app/"common"). - * - * If caching is set to CACHE_OFF, to get the expected behavior we - * need to reopen the Zip file on every request. That would be silly - * and expensive, so instead we just check the file modification date. - * - * Pass in NULL values for "appName", "locale", and "vendor" if the - * generics should be used. - */ -ZipFileRO* AssetManager::getZipFileLocked(const asset_path& ap) -{ - ALOGV("getZipFileLocked() in %p\n", this); - - return mZipSet.getZip(ap.path); -} - -/* - * Try to open an asset from a file on disk. - * - * If the file is compressed with gzip, we seek to the start of the - * deflated data and pass that in (just like we would for a Zip archive). - * - * For uncompressed data, we may already have an mmap()ed version sitting - * around. If so, we want to hand that to the Asset instead. - * - * This returns NULL if the file doesn't exist, couldn't be opened, or - * claims to be a ".gz" but isn't. - */ -Asset* AssetManager::openAssetFromFileLocked(const String8& pathName, - AccessMode mode) -{ - Asset* pAsset = NULL; - - if (strcasecmp(pathName.getPathExtension().string(), ".gz") == 0) { - //printf("TRYING '%s'\n", (const char*) pathName); - pAsset = Asset::createFromCompressedFile(pathName.string(), mode); - } else { - //printf("TRYING '%s'\n", (const char*) pathName); - pAsset = Asset::createFromFile(pathName.string(), mode); - } - - return pAsset; -} - -/* - * Given an entry in a Zip archive, create a new Asset object. - * - * If the entry is uncompressed, we may want to create or share a - * slice of shared memory. - */ -Asset* AssetManager::openAssetFromZipLocked(const ZipFileRO* pZipFile, - const ZipEntryRO entry, AccessMode mode, const String8& entryName) -{ - Asset* pAsset = NULL; - - // TODO: look for previously-created shared memory slice? - int method; - size_t uncompressedLen; - - //printf("USING Zip '%s'\n", pEntry->getFileName()); - - //pZipFile->getEntryInfo(entry, &method, &uncompressedLen, &compressedLen, - // &offset); - if (!pZipFile->getEntryInfo(entry, &method, &uncompressedLen, NULL, NULL, - NULL, NULL)) - { - ALOGW("getEntryInfo failed\n"); - return NULL; - } - - FileMap* dataMap = pZipFile->createEntryFileMap(entry); - if (dataMap == NULL) { - ALOGW("create map from entry failed\n"); - return NULL; - } - - if (method == ZipFileRO::kCompressStored) { - pAsset = Asset::createFromUncompressedMap(dataMap, mode); - ALOGV("Opened uncompressed entry %s in zip %s mode %d: %p", entryName.string(), - dataMap->getFileName(), mode, pAsset); - } else { - pAsset = Asset::createFromCompressedMap(dataMap, method, - uncompressedLen, mode); - ALOGV("Opened compressed entry %s in zip %s mode %d: %p", entryName.string(), - dataMap->getFileName(), mode, pAsset); - } - if (pAsset == NULL) { - /* unexpected */ - ALOGW("create from segment failed\n"); - } - - return pAsset; -} - - - -/* - * Open a directory in the asset namespace. - * - * An "asset directory" is simply the combination of all files in all - * locations, with ".gz" stripped for loose files. With app, locale, and - * vendor defined, we have 8 directories and 2 Zip archives to scan. - * - * Pass in "" for the root dir. - */ -AssetDir* AssetManager::openDir(const char* dirName) -{ - AutoMutex _l(mLock); - - AssetDir* pDir = NULL; - SortedVector* pMergedInfo = NULL; - - LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager"); - assert(dirName != NULL); - - //printf("+++ openDir(%s) in '%s'\n", dirName, (const char*) mAssetBase); - - if (mCacheMode != CACHE_OFF && !mCacheValid) - loadFileNameCacheLocked(); - - pDir = new AssetDir; - - /* - * Scan the various directories, merging what we find into a single - * vector. We want to scan them in reverse priority order so that - * the ".EXCLUDE" processing works correctly. Also, if we decide we - * want to remember where the file is coming from, we'll get the right - * version. - * - * We start with Zip archives, then do loose files. - */ - pMergedInfo = new SortedVector; - - size_t i = mAssetPaths.size(); - while (i > 0) { - i--; - const asset_path& ap = mAssetPaths.itemAt(i); - if (ap.type == kFileTypeRegular) { - ALOGV("Adding directory %s from zip %s", dirName, ap.path.string()); - scanAndMergeZipLocked(pMergedInfo, ap, kAssetsRoot, dirName); - } else { - ALOGV("Adding directory %s from dir %s", dirName, ap.path.string()); - scanAndMergeDirLocked(pMergedInfo, ap, kAssetsRoot, dirName); - } - } - -#if 0 - printf("FILE LIST:\n"); - for (i = 0; i < (size_t) pMergedInfo->size(); i++) { - printf(" %d: (%d) '%s'\n", i, - pMergedInfo->itemAt(i).getFileType(), - (const char*) pMergedInfo->itemAt(i).getFileName()); - } -#endif - - pDir->setFileList(pMergedInfo); - return pDir; -} - -/* - * Open a directory in the non-asset namespace. - * - * An "asset directory" is simply the combination of all files in all - * locations, with ".gz" stripped for loose files. With app, locale, and - * vendor defined, we have 8 directories and 2 Zip archives to scan. - * - * Pass in "" for the root dir. - */ -AssetDir* AssetManager::openNonAssetDir(void* cookie, const char* dirName) -{ - AutoMutex _l(mLock); - - AssetDir* pDir = NULL; - SortedVector* pMergedInfo = NULL; - - LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager"); - assert(dirName != NULL); - - //printf("+++ openDir(%s) in '%s'\n", dirName, (const char*) mAssetBase); - - if (mCacheMode != CACHE_OFF && !mCacheValid) - loadFileNameCacheLocked(); - - pDir = new AssetDir; - - pMergedInfo = new SortedVector; - - const size_t which = ((size_t)cookie)-1; - - if (which < mAssetPaths.size()) { - const asset_path& ap = mAssetPaths.itemAt(which); - if (ap.type == kFileTypeRegular) { - ALOGV("Adding directory %s from zip %s", dirName, ap.path.string()); - scanAndMergeZipLocked(pMergedInfo, ap, NULL, dirName); - } else { - ALOGV("Adding directory %s from dir %s", dirName, ap.path.string()); - scanAndMergeDirLocked(pMergedInfo, ap, NULL, dirName); - } - } - -#if 0 - printf("FILE LIST:\n"); - for (i = 0; i < (size_t) pMergedInfo->size(); i++) { - printf(" %d: (%d) '%s'\n", i, - pMergedInfo->itemAt(i).getFileType(), - (const char*) pMergedInfo->itemAt(i).getFileName()); - } -#endif - - pDir->setFileList(pMergedInfo); - return pDir; -} - -/* - * Scan the contents of the specified directory and merge them into the - * "pMergedInfo" vector, removing previous entries if we find "exclude" - * directives. - * - * Returns "false" if we found nothing to contribute. - */ -bool AssetManager::scanAndMergeDirLocked(SortedVector* pMergedInfo, - const asset_path& ap, const char* rootDir, const char* dirName) -{ - SortedVector* pContents; - String8 path; - - assert(pMergedInfo != NULL); - - //printf("scanAndMergeDir: %s %s %s %s\n", appName, locale, vendor,dirName); - - if (mCacheValid) { - int i, start, count; - - pContents = new SortedVector; - - /* - * Get the basic partial path and find it in the cache. That's - * the start point for the search. - */ - path = createPathNameLocked(ap, rootDir); - if (dirName[0] != '\0') - path.appendPath(dirName); - - start = mCache.indexOf(path); - if (start == NAME_NOT_FOUND) { - //printf("+++ not found in cache: dir '%s'\n", (const char*) path); - delete pContents; - return false; - } - - /* - * The match string looks like "common/default/default/foo/bar/". - * The '/' on the end ensures that we don't match on the directory - * itself or on ".../foo/barfy/". - */ - path.append("/"); - - count = mCache.size(); - - /* - * Pick out the stuff in the current dir by examining the pathname. - * It needs to match the partial pathname prefix, and not have a '/' - * (fssep) anywhere after the prefix. - */ - for (i = start+1; i < count; i++) { - if (mCache[i].getFileName().length() > path.length() && - strncmp(mCache[i].getFileName().string(), path.string(), path.length()) == 0) - { - const char* name = mCache[i].getFileName().string(); - // XXX THIS IS BROKEN! Looks like we need to store the full - // path prefix separately from the file path. - if (strchr(name + path.length(), '/') == NULL) { - /* grab it, reducing path to just the filename component */ - AssetDir::FileInfo tmp = mCache[i]; - tmp.setFileName(tmp.getFileName().getPathLeaf()); - pContents->add(tmp); - } - } else { - /* no longer in the dir or its subdirs */ - break; - } - - } - } else { - path = createPathNameLocked(ap, rootDir); - if (dirName[0] != '\0') - path.appendPath(dirName); - pContents = scanDirLocked(path); - if (pContents == NULL) - return false; - } - - // if we wanted to do an incremental cache fill, we would do it here - - /* - * Process "exclude" directives. If we find a filename that ends with - * ".EXCLUDE", we look for a matching entry in the "merged" set, and - * remove it if we find it. We also delete the "exclude" entry. - */ - int i, count, exclExtLen; - - count = pContents->size(); - exclExtLen = strlen(kExcludeExtension); - for (i = 0; i < count; i++) { - const char* name; - int nameLen; - - name = pContents->itemAt(i).getFileName().string(); - nameLen = strlen(name); - if (nameLen > exclExtLen && - strcmp(name + (nameLen - exclExtLen), kExcludeExtension) == 0) - { - String8 match(name, nameLen - exclExtLen); - int matchIdx; - - matchIdx = AssetDir::FileInfo::findEntry(pMergedInfo, match); - if (matchIdx > 0) { - ALOGV("Excluding '%s' [%s]\n", - pMergedInfo->itemAt(matchIdx).getFileName().string(), - pMergedInfo->itemAt(matchIdx).getSourceName().string()); - pMergedInfo->removeAt(matchIdx); - } else { - //printf("+++ no match on '%s'\n", (const char*) match); - } - - ALOGD("HEY: size=%d removing %d\n", (int)pContents->size(), i); - pContents->removeAt(i); - i--; // adjust "for" loop - count--; // and loop limit - } - } - - mergeInfoLocked(pMergedInfo, pContents); - - delete pContents; - - return true; -} - -/* - * Scan the contents of the specified directory, and stuff what we find - * into a newly-allocated vector. - * - * Files ending in ".gz" will have their extensions removed. - * - * We should probably think about skipping files with "illegal" names, - * e.g. illegal characters (/\:) or excessive length. - * - * Returns NULL if the specified directory doesn't exist. - */ -SortedVector* AssetManager::scanDirLocked(const String8& path) -{ - SortedVector* pContents = NULL; - DIR* dir; - struct dirent* entry; - FileType fileType; - - ALOGV("Scanning dir '%s'\n", path.string()); - - dir = opendir(path.string()); - if (dir == NULL) - return NULL; - - pContents = new SortedVector; - - while (1) { - entry = readdir(dir); - if (entry == NULL) - break; - - if (strcmp(entry->d_name, ".") == 0 || - strcmp(entry->d_name, "..") == 0) - continue; - -#ifdef _DIRENT_HAVE_D_TYPE - if (entry->d_type == DT_REG) - fileType = kFileTypeRegular; - else if (entry->d_type == DT_DIR) - fileType = kFileTypeDirectory; - else - fileType = kFileTypeUnknown; -#else - // stat the file - fileType = ::getFileType(path.appendPathCopy(entry->d_name).string()); -#endif - - if (fileType != kFileTypeRegular && fileType != kFileTypeDirectory) - continue; - - AssetDir::FileInfo info; - info.set(String8(entry->d_name), fileType); - if (strcasecmp(info.getFileName().getPathExtension().string(), ".gz") == 0) - info.setFileName(info.getFileName().getBasePath()); - info.setSourceName(path.appendPathCopy(info.getFileName())); - pContents->add(info); - } - - closedir(dir); - return pContents; -} - -/* - * Scan the contents out of the specified Zip archive, and merge what we - * find into "pMergedInfo". If the Zip archive in question doesn't exist, - * we return immediately. - * - * Returns "false" if we found nothing to contribute. - */ -bool AssetManager::scanAndMergeZipLocked(SortedVector* pMergedInfo, - const asset_path& ap, const char* rootDir, const char* baseDirName) -{ - ZipFileRO* pZip; - Vector dirs; - AssetDir::FileInfo info; - SortedVector contents; - String8 sourceName, zipName, dirName; - - pZip = mZipSet.getZip(ap.path); - if (pZip == NULL) { - ALOGW("Failure opening zip %s\n", ap.path.string()); - return false; - } - - zipName = ZipSet::getPathName(ap.path.string()); - - /* convert "sounds" to "rootDir/sounds" */ - if (rootDir != NULL) dirName = rootDir; - dirName.appendPath(baseDirName); - - /* - * Scan through the list of files, looking for a match. The files in - * the Zip table of contents are not in sorted order, so we have to - * process the entire list. We're looking for a string that begins - * with the characters in "dirName", is followed by a '/', and has no - * subsequent '/' in the stuff that follows. - * - * What makes this especially fun is that directories are not stored - * explicitly in Zip archives, so we have to infer them from context. - * When we see "sounds/foo.wav" we have to leave a note to ourselves - * to insert a directory called "sounds" into the list. We store - * these in temporary vector so that we only return each one once. - * - * Name comparisons are case-sensitive to match UNIX filesystem - * semantics. - */ - int dirNameLen = dirName.length(); - for (int i = 0; i < pZip->getNumEntries(); i++) { - ZipEntryRO entry; - char nameBuf[256]; - - entry = pZip->findEntryByIndex(i); - if (pZip->getEntryFileName(entry, nameBuf, sizeof(nameBuf)) != 0) { - // TODO: fix this if we expect to have long names - ALOGE("ARGH: name too long?\n"); - continue; - } - //printf("Comparing %s in %s?\n", nameBuf, dirName.string()); - if (dirNameLen == 0 || - (strncmp(nameBuf, dirName.string(), dirNameLen) == 0 && - nameBuf[dirNameLen] == '/')) - { - const char* cp; - const char* nextSlash; - - cp = nameBuf + dirNameLen; - if (dirNameLen != 0) - cp++; // advance past the '/' - - nextSlash = strchr(cp, '/'); -//xxx this may break if there are bare directory entries - if (nextSlash == NULL) { - /* this is a file in the requested directory */ - - info.set(String8(nameBuf).getPathLeaf(), kFileTypeRegular); - - info.setSourceName( - createZipSourceNameLocked(zipName, dirName, info.getFileName())); - - contents.add(info); - //printf("FOUND: file '%s'\n", info.getFileName().string()); - } else { - /* this is a subdir; add it if we don't already have it*/ - String8 subdirName(cp, nextSlash - cp); - size_t j; - size_t N = dirs.size(); - - for (j = 0; j < N; j++) { - if (subdirName == dirs[j]) { - break; - } - } - if (j == N) { - dirs.add(subdirName); - } - - //printf("FOUND: dir '%s'\n", subdirName.string()); - } - } - } - - /* - * Add the set of unique directories. - */ - for (int i = 0; i < (int) dirs.size(); i++) { - info.set(dirs[i], kFileTypeDirectory); - info.setSourceName( - createZipSourceNameLocked(zipName, dirName, info.getFileName())); - contents.add(info); - } - - mergeInfoLocked(pMergedInfo, &contents); - - return true; -} - - -/* - * Merge two vectors of FileInfo. - * - * The merged contents will be stuffed into *pMergedInfo. - * - * If an entry for a file exists in both "pMergedInfo" and "pContents", - * we use the newer "pContents" entry. - */ -void AssetManager::mergeInfoLocked(SortedVector* pMergedInfo, - const SortedVector* pContents) -{ - /* - * Merge what we found in this directory with what we found in - * other places. - * - * Two basic approaches: - * (1) Create a new array that holds the unique values of the two - * arrays. - * (2) Take the elements from pContents and shove them into pMergedInfo. - * - * Because these are vectors of complex objects, moving elements around - * inside the vector requires constructing new objects and allocating - * storage for members. With approach #1, we're always adding to the - * end, whereas with #2 we could be inserting multiple elements at the - * front of the vector. Approach #1 requires a full copy of the - * contents of pMergedInfo, but approach #2 requires the same copy for - * every insertion at the front of pMergedInfo. - * - * (We should probably use a SortedVector interface that allows us to - * just stuff items in, trusting us to maintain the sort order.) - */ - SortedVector* pNewSorted; - int mergeMax, contMax; - int mergeIdx, contIdx; - - pNewSorted = new SortedVector; - mergeMax = pMergedInfo->size(); - contMax = pContents->size(); - mergeIdx = contIdx = 0; - - while (mergeIdx < mergeMax || contIdx < contMax) { - if (mergeIdx == mergeMax) { - /* hit end of "merge" list, copy rest of "contents" */ - pNewSorted->add(pContents->itemAt(contIdx)); - contIdx++; - } else if (contIdx == contMax) { - /* hit end of "cont" list, copy rest of "merge" */ - pNewSorted->add(pMergedInfo->itemAt(mergeIdx)); - mergeIdx++; - } else if (pMergedInfo->itemAt(mergeIdx) == pContents->itemAt(contIdx)) - { - /* items are identical, add newer and advance both indices */ - pNewSorted->add(pContents->itemAt(contIdx)); - mergeIdx++; - contIdx++; - } else if (pMergedInfo->itemAt(mergeIdx) < pContents->itemAt(contIdx)) - { - /* "merge" is lower, add that one */ - pNewSorted->add(pMergedInfo->itemAt(mergeIdx)); - mergeIdx++; - } else { - /* "cont" is lower, add that one */ - assert(pContents->itemAt(contIdx) < pMergedInfo->itemAt(mergeIdx)); - pNewSorted->add(pContents->itemAt(contIdx)); - contIdx++; - } - } - - /* - * Overwrite the "merged" list with the new stuff. - */ - *pMergedInfo = *pNewSorted; - delete pNewSorted; - -#if 0 // for Vector, rather than SortedVector - int i, j; - for (i = pContents->size() -1; i >= 0; i--) { - bool add = true; - - for (j = pMergedInfo->size() -1; j >= 0; j--) { - /* case-sensitive comparisons, to behave like UNIX fs */ - if (strcmp(pContents->itemAt(i).mFileName, - pMergedInfo->itemAt(j).mFileName) == 0) - { - /* match, don't add this entry */ - add = false; - break; - } - } - - if (add) - pMergedInfo->add(pContents->itemAt(i)); - } -#endif -} - - -/* - * Load all files into the file name cache. We want to do this across - * all combinations of { appname, locale, vendor }, performing a recursive - * directory traversal. - * - * This is not the most efficient data structure. Also, gathering the - * information as we needed it (file-by-file or directory-by-directory) - * would be faster. However, on the actual device, 99% of the files will - * live in Zip archives, so this list will be very small. The trouble - * is that we have to check the "loose" files first, so it's important - * that we don't beat the filesystem silly looking for files that aren't - * there. - * - * Note on thread safety: this is the only function that causes updates - * to mCache, and anybody who tries to use it will call here if !mCacheValid, - * so we need to employ a mutex here. - */ -void AssetManager::loadFileNameCacheLocked(void) -{ - assert(!mCacheValid); - assert(mCache.size() == 0); - -#ifdef DO_TIMINGS // need to link against -lrt for this now - DurationTimer timer; - timer.start(); -#endif - - fncScanLocked(&mCache, ""); - -#ifdef DO_TIMINGS - timer.stop(); - ALOGD("Cache scan took %.3fms\n", - timer.durationUsecs() / 1000.0); -#endif - -#if 0 - int i; - printf("CACHED FILE LIST (%d entries):\n", mCache.size()); - for (i = 0; i < (int) mCache.size(); i++) { - printf(" %d: (%d) '%s'\n", i, - mCache.itemAt(i).getFileType(), - (const char*) mCache.itemAt(i).getFileName()); - } -#endif - - mCacheValid = true; -} - -/* - * Scan up to 8 versions of the specified directory. - */ -void AssetManager::fncScanLocked(SortedVector* pMergedInfo, - const char* dirName) -{ - size_t i = mAssetPaths.size(); - while (i > 0) { - i--; - const asset_path& ap = mAssetPaths.itemAt(i); - fncScanAndMergeDirLocked(pMergedInfo, ap, NULL, NULL, dirName); - if (mLocale != NULL) - fncScanAndMergeDirLocked(pMergedInfo, ap, mLocale, NULL, dirName); - if (mVendor != NULL) - fncScanAndMergeDirLocked(pMergedInfo, ap, NULL, mVendor, dirName); - if (mLocale != NULL && mVendor != NULL) - fncScanAndMergeDirLocked(pMergedInfo, ap, mLocale, mVendor, dirName); - } -} - -/* - * Recursively scan this directory and all subdirs. - * - * This is similar to scanAndMergeDir, but we don't remove the .EXCLUDE - * files, and we prepend the extended partial path to the filenames. - */ -bool AssetManager::fncScanAndMergeDirLocked( - SortedVector* pMergedInfo, - const asset_path& ap, const char* locale, const char* vendor, - const char* dirName) -{ - SortedVector* pContents; - String8 partialPath; - String8 fullPath; - - // XXX This is broken -- the filename cache needs to hold the base - // asset path separately from its filename. - - partialPath = createPathNameLocked(ap, locale, vendor); - if (dirName[0] != '\0') { - partialPath.appendPath(dirName); - } - - fullPath = partialPath; - pContents = scanDirLocked(fullPath); - if (pContents == NULL) { - return false; // directory did not exist - } - - /* - * Scan all subdirectories of the current dir, merging what we find - * into "pMergedInfo". - */ - for (int i = 0; i < (int) pContents->size(); i++) { - if (pContents->itemAt(i).getFileType() == kFileTypeDirectory) { - String8 subdir(dirName); - subdir.appendPath(pContents->itemAt(i).getFileName()); - - fncScanAndMergeDirLocked(pMergedInfo, ap, locale, vendor, subdir.string()); - } - } - - /* - * To be consistent, we want entries for the root directory. If - * we're the root, add one now. - */ - if (dirName[0] == '\0') { - AssetDir::FileInfo tmpInfo; - - tmpInfo.set(String8(""), kFileTypeDirectory); - tmpInfo.setSourceName(createPathNameLocked(ap, locale, vendor)); - pContents->add(tmpInfo); - } - - /* - * We want to prepend the extended partial path to every entry in - * "pContents". It's the same value for each entry, so this will - * not change the sorting order of the vector contents. - */ - for (int i = 0; i < (int) pContents->size(); i++) { - const AssetDir::FileInfo& info = pContents->itemAt(i); - pContents->editItemAt(i).setFileName(partialPath.appendPathCopy(info.getFileName())); - } - - mergeInfoLocked(pMergedInfo, pContents); - return true; -} - -/* - * Trash the cache. - */ -void AssetManager::purgeFileNameCacheLocked(void) -{ - mCacheValid = false; - mCache.clear(); -} - -/* - * =========================================================================== - * AssetManager::SharedZip - * =========================================================================== - */ - - -Mutex AssetManager::SharedZip::gLock; -DefaultKeyedVector > AssetManager::SharedZip::gOpen; - -AssetManager::SharedZip::SharedZip(const String8& path, time_t modWhen) - : mPath(path), mZipFile(NULL), mModWhen(modWhen), - mResourceTableAsset(NULL), mResourceTable(NULL) -{ - //ALOGI("Creating SharedZip %p %s\n", this, (const char*)mPath); - mZipFile = new ZipFileRO; - ALOGV("+++ opening zip '%s'\n", mPath.string()); - if (mZipFile->open(mPath.string()) != NO_ERROR) { - ALOGD("failed to open Zip archive '%s'\n", mPath.string()); - delete mZipFile; - mZipFile = NULL; - } -} - -sp AssetManager::SharedZip::get(const String8& path) -{ - AutoMutex _l(gLock); - time_t modWhen = getFileModDate(path); - sp zip = gOpen.valueFor(path).promote(); - if (zip != NULL && zip->mModWhen == modWhen) { - return zip; - } - zip = new SharedZip(path, modWhen); - gOpen.add(path, zip); - return zip; - -} - -ZipFileRO* AssetManager::SharedZip::getZip() -{ - return mZipFile; -} - -Asset* AssetManager::SharedZip::getResourceTableAsset() -{ - ALOGV("Getting from SharedZip %p resource asset %p\n", this, mResourceTableAsset); - return mResourceTableAsset; -} - -Asset* AssetManager::SharedZip::setResourceTableAsset(Asset* asset) -{ - { - AutoMutex _l(gLock); - if (mResourceTableAsset == NULL) { - mResourceTableAsset = asset; - // This is not thread safe the first time it is called, so - // do it here with the global lock held. - asset->getBuffer(true); - return asset; - } - } - delete asset; - return mResourceTableAsset; -} - -ResTable* AssetManager::SharedZip::getResourceTable() -{ - ALOGV("Getting from SharedZip %p resource table %p\n", this, mResourceTable); - return mResourceTable; -} - -ResTable* AssetManager::SharedZip::setResourceTable(ResTable* res) -{ - { - AutoMutex _l(gLock); - if (mResourceTable == NULL) { - mResourceTable = res; - return res; - } - } - delete res; - return mResourceTable; -} - -bool AssetManager::SharedZip::isUpToDate() -{ - time_t modWhen = getFileModDate(mPath.string()); - return mModWhen == modWhen; -} - -AssetManager::SharedZip::~SharedZip() -{ - //ALOGI("Destroying SharedZip %p %s\n", this, (const char*)mPath); - if (mResourceTable != NULL) { - delete mResourceTable; - } - if (mResourceTableAsset != NULL) { - delete mResourceTableAsset; - } - if (mZipFile != NULL) { - delete mZipFile; - ALOGV("Closed '%s'\n", mPath.string()); - } -} - -/* - * =========================================================================== - * AssetManager::ZipSet - * =========================================================================== - */ - -/* - * Constructor. - */ -AssetManager::ZipSet::ZipSet(void) -{ -} - -/* - * Destructor. Close any open archives. - */ -AssetManager::ZipSet::~ZipSet(void) -{ - size_t N = mZipFile.size(); - for (size_t i = 0; i < N; i++) - closeZip(i); -} - -/* - * Close a Zip file and reset the entry. - */ -void AssetManager::ZipSet::closeZip(int idx) -{ - mZipFile.editItemAt(idx) = NULL; -} - - -/* - * Retrieve the appropriate Zip file from the set. - */ -ZipFileRO* AssetManager::ZipSet::getZip(const String8& path) -{ - int idx = getIndex(path); - sp zip = mZipFile[idx]; - if (zip == NULL) { - zip = SharedZip::get(path); - mZipFile.editItemAt(idx) = zip; - } - return zip->getZip(); -} - -Asset* AssetManager::ZipSet::getZipResourceTableAsset(const String8& path) -{ - int idx = getIndex(path); - sp zip = mZipFile[idx]; - if (zip == NULL) { - zip = SharedZip::get(path); - mZipFile.editItemAt(idx) = zip; - } - return zip->getResourceTableAsset(); -} - -Asset* AssetManager::ZipSet::setZipResourceTableAsset(const String8& path, - Asset* asset) -{ - int idx = getIndex(path); - sp zip = mZipFile[idx]; - // doesn't make sense to call before previously accessing. - return zip->setResourceTableAsset(asset); -} - -ResTable* AssetManager::ZipSet::getZipResourceTable(const String8& path) -{ - int idx = getIndex(path); - sp zip = mZipFile[idx]; - if (zip == NULL) { - zip = SharedZip::get(path); - mZipFile.editItemAt(idx) = zip; - } - return zip->getResourceTable(); -} - -ResTable* AssetManager::ZipSet::setZipResourceTable(const String8& path, - ResTable* res) -{ - int idx = getIndex(path); - sp zip = mZipFile[idx]; - // doesn't make sense to call before previously accessing. - return zip->setResourceTable(res); -} - -/* - * Generate the partial pathname for the specified archive. The caller - * gets to prepend the asset root directory. - * - * Returns something like "common/en-US-noogle.jar". - */ -/*static*/ String8 AssetManager::ZipSet::getPathName(const char* zipPath) -{ - return String8(zipPath); -} - -bool AssetManager::ZipSet::isUpToDate() -{ - const size_t N = mZipFile.size(); - for (size_t i=0; iisUpToDate()) { - return false; - } - } - return true; -} - -/* - * Compute the zip file's index. - * - * "appName", "locale", and "vendor" should be set to NULL to indicate the - * default directory. - */ -int AssetManager::ZipSet::getIndex(const String8& zip) const -{ - const size_t N = mZipPath.size(); - for (size_t i=0; i -#include - -#include -#include -#include - -#include - -namespace android { - -static const bool DEBUG = false; - -/* - * File Format (v1): - * - * All ints are stored little-endian. - * - * - An app_header_v1 struct. - * - The name of the package, utf-8, null terminated, padded to 4-byte boundary. - * - A sequence of zero or more key/value paires (entities), each with - * - A entity_header_v1 struct - * - The key, utf-8, null terminated, padded to 4-byte boundary. - * - The value, padded to 4 byte boundary - */ - -const static int ROUND_UP[4] = { 0, 3, 2, 1 }; - -static inline size_t -round_up(size_t n) -{ - return n + ROUND_UP[n % 4]; -} - -static inline size_t -padding_extra(size_t n) -{ - return ROUND_UP[n % 4]; -} - -BackupDataWriter::BackupDataWriter(int fd) - :m_fd(fd), - m_status(NO_ERROR), - m_pos(0), - m_entityCount(0) -{ -} - -BackupDataWriter::~BackupDataWriter() -{ -} - -// Pad out anything they've previously written to the next 4 byte boundary. -status_t -BackupDataWriter::write_padding_for(int n) -{ - ssize_t amt; - ssize_t paddingSize; - - paddingSize = padding_extra(n); - if (paddingSize > 0) { - uint32_t padding = 0xbcbcbcbc; - if (DEBUG) ALOGI("writing %d padding bytes for %d", paddingSize, n); - amt = write(m_fd, &padding, paddingSize); - if (amt != paddingSize) { - m_status = errno; - return m_status; - } - m_pos += amt; - } - return NO_ERROR; -} - -status_t -BackupDataWriter::WriteEntityHeader(const String8& key, size_t dataSize) -{ - if (m_status != NO_ERROR) { - return m_status; - } - - ssize_t amt; - - amt = write_padding_for(m_pos); - if (amt != 0) { - return amt; - } - - String8 k; - if (m_keyPrefix.length() > 0) { - k = m_keyPrefix; - k += ":"; - k += key; - } else { - k = key; - } - if (DEBUG) { - ALOGD("Writing header: prefix='%s' key='%s' dataSize=%d", m_keyPrefix.string(), - key.string(), dataSize); - } - - entity_header_v1 header; - ssize_t keyLen; - - keyLen = k.length(); - - header.type = tolel(BACKUP_HEADER_ENTITY_V1); - header.keyLen = tolel(keyLen); - header.dataSize = tolel(dataSize); - - if (DEBUG) ALOGI("writing entity header, %d bytes", sizeof(entity_header_v1)); - amt = write(m_fd, &header, sizeof(entity_header_v1)); - if (amt != sizeof(entity_header_v1)) { - m_status = errno; - return m_status; - } - m_pos += amt; - - if (DEBUG) ALOGI("writing entity header key, %d bytes", keyLen+1); - amt = write(m_fd, k.string(), keyLen+1); - if (amt != keyLen+1) { - m_status = errno; - return m_status; - } - m_pos += amt; - - amt = write_padding_for(keyLen+1); - - m_entityCount++; - - return amt; -} - -status_t -BackupDataWriter::WriteEntityData(const void* data, size_t size) -{ - if (DEBUG) ALOGD("Writing data: size=%lu", (unsigned long) size); - - if (m_status != NO_ERROR) { - if (DEBUG) { - ALOGD("Not writing data - stream in error state %d (%s)", m_status, strerror(m_status)); - } - return m_status; - } - - // We don't write padding here, because they're allowed to call this several - // times with smaller buffers. We write it at the end of WriteEntityHeader - // instead. - ssize_t amt = write(m_fd, data, size); - if (amt != (ssize_t)size) { - m_status = errno; - if (DEBUG) ALOGD("write returned error %d (%s)", m_status, strerror(m_status)); - return m_status; - } - m_pos += amt; - return NO_ERROR; -} - -void -BackupDataWriter::SetKeyPrefix(const String8& keyPrefix) -{ - m_keyPrefix = keyPrefix; -} - - -BackupDataReader::BackupDataReader(int fd) - :m_fd(fd), - m_done(false), - m_status(NO_ERROR), - m_pos(0), - m_entityCount(0) -{ - memset(&m_header, 0, sizeof(m_header)); -} - -BackupDataReader::~BackupDataReader() -{ -} - -status_t -BackupDataReader::Status() -{ - return m_status; -} - -#define CHECK_SIZE(actual, expected) \ - do { \ - if ((actual) != (expected)) { \ - if ((actual) == 0) { \ - m_status = EIO; \ - m_done = true; \ - } else { \ - m_status = errno; \ - ALOGD("CHECK_SIZE(a=%ld e=%ld) failed at line %d m_status='%s'", \ - long(actual), long(expected), __LINE__, strerror(m_status)); \ - } \ - return m_status; \ - } \ - } while(0) -#define SKIP_PADDING() \ - do { \ - status_t err = skip_padding(); \ - if (err != NO_ERROR) { \ - ALOGD("SKIP_PADDING FAILED at line %d", __LINE__); \ - m_status = err; \ - return err; \ - } \ - } while(0) - -status_t -BackupDataReader::ReadNextHeader(bool* done, int* type) -{ - *done = m_done; - if (m_status != NO_ERROR) { - return m_status; - } - - int amt; - - amt = skip_padding(); - if (amt == EIO) { - *done = m_done = true; - return NO_ERROR; - } - else if (amt != NO_ERROR) { - return amt; - } - amt = read(m_fd, &m_header, sizeof(m_header)); - *done = m_done = (amt == 0); - if (*done) { - return NO_ERROR; - } - CHECK_SIZE(amt, sizeof(m_header)); - m_pos += sizeof(m_header); - if (type) { - *type = m_header.type; - } - - // validate and fix up the fields. - m_header.type = fromlel(m_header.type); - switch (m_header.type) - { - case BACKUP_HEADER_ENTITY_V1: - { - m_header.entity.keyLen = fromlel(m_header.entity.keyLen); - if (m_header.entity.keyLen <= 0) { - ALOGD("Entity header at %d has keyLen<=0: 0x%08x\n", (int)m_pos, - (int)m_header.entity.keyLen); - m_status = EINVAL; - } - m_header.entity.dataSize = fromlel(m_header.entity.dataSize); - m_entityCount++; - - // read the rest of the header (filename) - size_t size = m_header.entity.keyLen; - char* buf = m_key.lockBuffer(size); - if (buf == NULL) { - m_status = ENOMEM; - return m_status; - } - int amt = read(m_fd, buf, size+1); - CHECK_SIZE(amt, (int)size+1); - m_key.unlockBuffer(size); - m_pos += size+1; - SKIP_PADDING(); - m_dataEndPos = m_pos + m_header.entity.dataSize; - - break; - } - default: - ALOGD("Chunk header at %d has invalid type: 0x%08x", - (int)(m_pos - sizeof(m_header)), (int)m_header.type); - m_status = EINVAL; - } - - return m_status; -} - -bool -BackupDataReader::HasEntities() -{ - return m_status == NO_ERROR && m_header.type == BACKUP_HEADER_ENTITY_V1; -} - -status_t -BackupDataReader::ReadEntityHeader(String8* key, size_t* dataSize) -{ - if (m_status != NO_ERROR) { - return m_status; - } - if (m_header.type != BACKUP_HEADER_ENTITY_V1) { - return EINVAL; - } - *key = m_key; - *dataSize = m_header.entity.dataSize; - return NO_ERROR; -} - -status_t -BackupDataReader::SkipEntityData() -{ - if (m_status != NO_ERROR) { - return m_status; - } - if (m_header.type != BACKUP_HEADER_ENTITY_V1) { - return EINVAL; - } - if (m_header.entity.dataSize > 0) { - int pos = lseek(m_fd, m_dataEndPos, SEEK_SET); - if (pos == -1) { - return errno; - } - } - SKIP_PADDING(); - return NO_ERROR; -} - -ssize_t -BackupDataReader::ReadEntityData(void* data, size_t size) -{ - if (m_status != NO_ERROR) { - return -1; - } - int remaining = m_dataEndPos - m_pos; - //ALOGD("ReadEntityData size=%d m_pos=0x%x m_dataEndPos=0x%x remaining=%d\n", - // size, m_pos, m_dataEndPos, remaining); - if (remaining <= 0) { - return 0; - } - if (((int)size) > remaining) { - size = remaining; - } - //ALOGD(" reading %d bytes", size); - int amt = read(m_fd, data, size); - if (amt < 0) { - m_status = errno; - return -1; - } - if (amt == 0) { - m_status = EIO; - m_done = true; - } - m_pos += amt; - return amt; -} - -status_t -BackupDataReader::skip_padding() -{ - ssize_t amt; - ssize_t paddingSize; - - paddingSize = padding_extra(m_pos); - if (paddingSize > 0) { - uint32_t padding; - amt = read(m_fd, &padding, paddingSize); - CHECK_SIZE(amt, paddingSize); - m_pos += amt; - } - return NO_ERROR; -} - - -} // namespace android diff --git a/libs/utils/BackupHelpers.cpp b/libs/utils/BackupHelpers.cpp deleted file mode 100644 index 7a817a789..000000000 --- a/libs/utils/BackupHelpers.cpp +++ /dev/null @@ -1,1591 +0,0 @@ -/* - * 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. - */ - -#define LOG_TAG "file_backup_helper" - -#include - -#include -#include -#include - -#include -#include -#include -#include -#include // for utimes -#include -#include -#include -#include -#include -#include - -#include - -namespace android { - -#define MAGIC0 0x70616e53 // Snap -#define MAGIC1 0x656c6946 // File - -/* - * File entity data format (v1): - * - * - 4-byte version number of the metadata, little endian (0x00000001 for v1) - * - 12 bytes of metadata - * - the file data itself - * - * i.e. a 16-byte metadata header followed by the raw file data. If the - * restore code does not recognize the metadata version, it can still - * interpret the file data itself correctly. - * - * file_metadata_v1: - * - * - 4 byte version number === 0x00000001 (little endian) - * - 4-byte access mode (little-endian) - * - undefined (8 bytes) - */ - -struct file_metadata_v1 { - int version; - int mode; - int undefined_1; - int undefined_2; -}; - -const static int CURRENT_METADATA_VERSION = 1; - -#if 1 -#define LOGP(f, x...) -#else -#if TEST_BACKUP_HELPERS -#define LOGP(f, x...) printf(f "\n", x) -#else -#define LOGP(x...) ALOGD(x) -#endif -#endif - -const static int ROUND_UP[4] = { 0, 3, 2, 1 }; - -static inline int -round_up(int n) -{ - return n + ROUND_UP[n % 4]; -} - -static int -read_snapshot_file(int fd, KeyedVector* snapshot) -{ - int bytesRead = 0; - int amt; - SnapshotHeader header; - - amt = read(fd, &header, sizeof(header)); - if (amt != sizeof(header)) { - return errno; - } - bytesRead += amt; - - if (header.magic0 != MAGIC0 || header.magic1 != MAGIC1) { - ALOGW("read_snapshot_file header.magic0=0x%08x magic1=0x%08x", header.magic0, header.magic1); - return 1; - } - - for (int i=0; iadd(String8(filename, file.nameLen), file); - } - bytesRead += amt; - if (filename != filenameBuf) { - free(filename); - } - if (amt != nameBufSize) { - ALOGW("read_snapshot_file filename truncated/error with read at %d bytes\n", bytesRead); - return 1; - } - } - - if (header.totalSize != bytesRead) { - ALOGW("read_snapshot_file length mismatch: header.totalSize=%d bytesRead=%d\n", - header.totalSize, bytesRead); - return 1; - } - - return 0; -} - -static int -write_snapshot_file(int fd, const KeyedVector& snapshot) -{ - int fileCount = 0; - int bytesWritten = sizeof(SnapshotHeader); - // preflight size - const int N = snapshot.size(); - for (int i=0; iWriteEntityHeader(key, -1); -} - -static int -write_update_file(BackupDataWriter* dataStream, int fd, int mode, const String8& key, - char const* realFilename) -{ - LOGP("write_update_file %s (%s) : mode 0%o\n", realFilename, key.string(), mode); - - const int bufsize = 4*1024; - int err; - int amt; - int fileSize; - int bytesLeft; - file_metadata_v1 metadata; - - char* buf = (char*)malloc(bufsize); - int crc = crc32(0L, Z_NULL, 0); - - - fileSize = lseek(fd, 0, SEEK_END); - lseek(fd, 0, SEEK_SET); - - if (sizeof(metadata) != 16) { - ALOGE("ERROR: metadata block is the wrong size!"); - } - - bytesLeft = fileSize + sizeof(metadata); - err = dataStream->WriteEntityHeader(key, bytesLeft); - if (err != 0) { - free(buf); - return err; - } - - // store the file metadata first - metadata.version = tolel(CURRENT_METADATA_VERSION); - metadata.mode = tolel(mode); - metadata.undefined_1 = metadata.undefined_2 = 0; - err = dataStream->WriteEntityData(&metadata, sizeof(metadata)); - if (err != 0) { - free(buf); - return err; - } - bytesLeft -= sizeof(metadata); // bytesLeft should == fileSize now - - // now store the file content - while ((amt = read(fd, buf, bufsize)) != 0 && bytesLeft > 0) { - bytesLeft -= amt; - if (bytesLeft < 0) { - amt += bytesLeft; // Plus a negative is minus. Don't write more than we promised. - } - err = dataStream->WriteEntityData(buf, amt); - if (err != 0) { - free(buf); - return err; - } - } - if (bytesLeft != 0) { - if (bytesLeft > 0) { - // Pad out the space we promised in the buffer. We can't corrupt the buffer, - // even though the data we're sending is probably bad. - memset(buf, 0, bufsize); - while (bytesLeft > 0) { - amt = bytesLeft < bufsize ? bytesLeft : bufsize; - bytesLeft -= amt; - err = dataStream->WriteEntityData(buf, amt); - if (err != 0) { - free(buf); - return err; - } - } - } - ALOGE("write_update_file size mismatch for %s. expected=%d actual=%d." - " You aren't doing proper locking!", realFilename, fileSize, fileSize-bytesLeft); - } - - free(buf); - return NO_ERROR; -} - -static int -write_update_file(BackupDataWriter* dataStream, const String8& key, char const* realFilename) -{ - int err; - struct stat st; - - err = stat(realFilename, &st); - if (err < 0) { - return errno; - } - - int fd = open(realFilename, O_RDONLY); - if (fd == -1) { - return errno; - } - - err = write_update_file(dataStream, fd, st.st_mode, key, realFilename); - close(fd); - return err; -} - -static int -compute_crc32(int fd) -{ - const int bufsize = 4*1024; - int amt; - - char* buf = (char*)malloc(bufsize); - int crc = crc32(0L, Z_NULL, 0); - - lseek(fd, 0, SEEK_SET); - - while ((amt = read(fd, buf, bufsize)) != 0) { - crc = crc32(crc, (Bytef*)buf, amt); - } - - free(buf); - return crc; -} - -int -back_up_files(int oldSnapshotFD, BackupDataWriter* dataStream, int newSnapshotFD, - char const* const* files, char const* const* keys, int fileCount) -{ - int err; - KeyedVector oldSnapshot; - KeyedVector newSnapshot; - - if (oldSnapshotFD != -1) { - err = read_snapshot_file(oldSnapshotFD, &oldSnapshot); - if (err != 0) { - // On an error, treat this as a full backup. - oldSnapshot.clear(); - } - } - - for (int i=0; i= 0) { - LOGP("back_up_files key already in use '%s'", key.string()); - return -1; - } - } - newSnapshot.add(key, r); - } - - int n = 0; - int N = oldSnapshot.size(); - int m = 0; - - while (nWriteEntityHeader(p, -1); - n++; - } - else if (cmp > 0) { - // file added - LOGP("file added: %s", g.file.string()); - write_update_file(dataStream, q, g.file.string()); - m++; - } - else { - // both files exist, check them - const FileState& f = oldSnapshot.valueAt(n); - - int fd = open(g.file.string(), O_RDONLY); - if (fd < 0) { - // We can't open the file. Don't report it as a delete either. Let the - // server keep the old version. Maybe they'll be able to deal with it - // on restore. - LOGP("Unable to open file %s - skipping", g.file.string()); - } else { - g.s.crc32 = compute_crc32(fd); - - LOGP("%s", q.string()); - LOGP(" new: modTime=%d,%d mode=%04o size=%-3d crc32=0x%08x", - f.modTime_sec, f.modTime_nsec, f.mode, f.size, f.crc32); - LOGP(" old: modTime=%d,%d mode=%04o size=%-3d crc32=0x%08x", - g.s.modTime_sec, g.s.modTime_nsec, g.s.mode, g.s.size, g.s.crc32); - if (f.modTime_sec != g.s.modTime_sec || f.modTime_nsec != g.s.modTime_nsec - || f.mode != g.s.mode || f.size != g.s.size || f.crc32 != g.s.crc32) { - write_update_file(dataStream, fd, g.s.mode, p, g.file.string()); - } - - close(fd); - } - n++; - m++; - } - } - - // these were deleted - while (nWriteEntityHeader(oldSnapshot.keyAt(n), -1); - n++; - } - - // these were added - while (m 9) len++; - if (len > 99) len++; - if (len > 999) len++; - // since PATH_MAX is 4096 we don't expect to have to generate any single - // header entry longer than 9999 characters - - return sprintf(buf, "%d %s=%s\n", len, key, value); -} - -// Wire format to the backup manager service is chunked: each chunk is prefixed by -// a 4-byte count of its size. A chunk size of zero (four zero bytes) indicates EOD. -void send_tarfile_chunk(BackupDataWriter* writer, const char* buffer, size_t size) { - uint32_t chunk_size_no = htonl(size); - writer->WriteEntityData(&chunk_size_no, 4); - if (size != 0) writer->WriteEntityData(buffer, size); -} - -int write_tarfile(const String8& packageName, const String8& domain, - const String8& rootpath, const String8& filepath, BackupDataWriter* writer) -{ - // In the output stream everything is stored relative to the root - const char* relstart = filepath.string() + rootpath.length(); - if (*relstart == '/') relstart++; // won't be true when path == rootpath - String8 relpath(relstart); - - // If relpath is empty, it means this is the top of one of the standard named - // domain directories, so we should just skip it - if (relpath.length() == 0) { - return 0; - } - - // Too long a name for the ustar format? - // "apps/" + packagename + '/' + domainpath < 155 chars - // relpath < 100 chars - bool needExtended = false; - if ((5 + packageName.length() + 1 + domain.length() >= 155) || (relpath.length() >= 100)) { - needExtended = true; - } - - // Non-7bit-clean path also means needing pax extended format - if (!needExtended) { - for (size_t i = 0; i < filepath.length(); i++) { - if ((filepath[i] & 0x80) != 0) { - needExtended = true; - break; - } - } - } - - int err = 0; - struct stat64 s; - if (lstat64(filepath.string(), &s) != 0) { - err = errno; - ALOGE("Error %d (%s) from lstat64(%s)", err, strerror(err), filepath.string()); - return err; - } - - String8 fullname; // for pax later on - String8 prefix; - - const int isdir = S_ISDIR(s.st_mode); - if (isdir) s.st_size = 0; // directories get no actual data in the tar stream - - // !!! TODO: use mmap when possible to avoid churning the buffer cache - // !!! TODO: this will break with symlinks; need to use readlink(2) - int fd = open(filepath.string(), O_RDONLY); - if (fd < 0) { - err = errno; - ALOGE("Error %d (%s) from open(%s)", err, strerror(err), filepath.string()); - return err; - } - - // read/write up to this much at a time. - const size_t BUFSIZE = 32 * 1024; - char* buf = (char *)calloc(1,BUFSIZE); - char* paxHeader = buf + 512; // use a different chunk of it as separate scratch - char* paxData = buf + 1024; - - if (buf == NULL) { - ALOGE("Out of mem allocating transfer buffer"); - err = ENOMEM; - goto cleanup; - } - - // Magic fields for the ustar file format - strcat(buf + 257, "ustar"); - strcat(buf + 263, "00"); - - // [ 265 : 32 ] user name, ignored on restore - // [ 297 : 32 ] group name, ignored on restore - - // [ 100 : 8 ] file mode - snprintf(buf + 100, 8, "%06o ", s.st_mode & ~S_IFMT); - - // [ 108 : 8 ] uid -- ignored in Android format; uids are remapped at restore time - // [ 116 : 8 ] gid -- ignored in Android format - snprintf(buf + 108, 8, "0%lo", s.st_uid); - snprintf(buf + 116, 8, "0%lo", s.st_gid); - - // [ 124 : 12 ] file size in bytes - if (s.st_size > 077777777777LL) { - // very large files need a pax extended size header - needExtended = true; - } - snprintf(buf + 124, 12, "%011llo", (isdir) ? 0LL : s.st_size); - - // [ 136 : 12 ] last mod time as a UTC time_t - snprintf(buf + 136, 12, "%0lo", s.st_mtime); - - // [ 156 : 1 ] link/file type - uint8_t type; - if (isdir) { - type = '5'; // tar magic: '5' == directory - } else if (S_ISREG(s.st_mode)) { - type = '0'; // tar magic: '0' == normal file - } else { - ALOGW("Error: unknown file mode 0%o [%s]", s.st_mode, filepath.string()); - goto cleanup; - } - buf[156] = type; - - // [ 157 : 100 ] name of linked file [not implemented] - - { - // Prefix and main relative path. Path lengths have been preflighted. - if (packageName.length() > 0) { - prefix = "apps/"; - prefix += packageName; - } - if (domain.length() > 0) { - prefix.appendPath(domain); - } - - // pax extended means we don't put in a prefix field, and put a different - // string in the basic name field. We can also construct the full path name - // out of the substrings we've now built. - fullname = prefix; - fullname.appendPath(relpath); - - // ustar: - // [ 0 : 100 ]; file name/path - // [ 345 : 155 ] filename path prefix - // We only use the prefix area if fullname won't fit in the path - if (fullname.length() > 100) { - strncpy(buf, relpath.string(), 100); - strncpy(buf + 345, prefix.string(), 155); - } else { - strncpy(buf, fullname.string(), 100); - } - } - - // [ 329 : 8 ] and [ 337 : 8 ] devmajor/devminor, not used - - ALOGI(" Name: %s", fullname.string()); - - // If we're using a pax extended header, build & write that here; lengths are - // already preflighted - if (needExtended) { - char sizeStr[32]; // big enough for a 64-bit unsigned value in decimal - char* p = paxData; - - // construct the pax extended header data block - memset(paxData, 0, BUFSIZE - (paxData - buf)); - int len; - - // size header -- calc len in digits by actually rendering the number - // to a string - brute force but simple - snprintf(sizeStr, sizeof(sizeStr), "%lld", s.st_size); - p += write_pax_header_entry(p, "size", sizeStr); - - // fullname was generated above with the ustar paths - p += write_pax_header_entry(p, "path", fullname.string()); - - // Now we know how big the pax data is - int paxLen = p - paxData; - - // Now build the pax *header* templated on the ustar header - memcpy(paxHeader, buf, 512); - - String8 leaf = fullname.getPathLeaf(); - memset(paxHeader, 0, 100); // rewrite the name area - snprintf(paxHeader, 100, "PaxHeader/%s", leaf.string()); - memset(paxHeader + 345, 0, 155); // rewrite the prefix area - strncpy(paxHeader + 345, prefix.string(), 155); - - paxHeader[156] = 'x'; // mark it as a pax extended header - - // [ 124 : 12 ] size of pax extended header data - memset(paxHeader + 124, 0, 12); - snprintf(paxHeader + 124, 12, "%011o", p - paxData); - - // Checksum and write the pax block header - calc_tar_checksum(paxHeader); - send_tarfile_chunk(writer, paxHeader, 512); - - // Now write the pax data itself - int paxblocks = (paxLen + 511) / 512; - send_tarfile_chunk(writer, paxData, 512 * paxblocks); - } - - // Checksum and write the 512-byte ustar file header block to the output - calc_tar_checksum(buf); - send_tarfile_chunk(writer, buf, 512); - - // Now write the file data itself, for real files. We honor tar's convention that - // only full 512-byte blocks are sent to write(). - if (!isdir) { - off64_t toWrite = s.st_size; - while (toWrite > 0) { - size_t toRead = (toWrite < BUFSIZE) ? toWrite : BUFSIZE; - ssize_t nRead = read(fd, buf, toRead); - if (nRead < 0) { - err = errno; - ALOGE("Unable to read file [%s], err=%d (%s)", filepath.string(), - err, strerror(err)); - break; - } else if (nRead == 0) { - ALOGE("EOF but expect %lld more bytes in [%s]", (long long) toWrite, - filepath.string()); - err = EIO; - break; - } - - // At EOF we might have a short block; NUL-pad that to a 512-byte multiple. This - // depends on the OS guarantee that for ordinary files, read() will never return - // less than the number of bytes requested. - ssize_t partial = (nRead+512) % 512; - if (partial > 0) { - ssize_t remainder = 512 - partial; - memset(buf + nRead, 0, remainder); - nRead += remainder; - } - send_tarfile_chunk(writer, buf, nRead); - toWrite -= nRead; - } - } - -cleanup: - delete [] buf; -done: - close(fd); - return err; -} -// end tarfile - - - -#define RESTORE_BUF_SIZE (8*1024) - -RestoreHelperBase::RestoreHelperBase() -{ - m_buf = malloc(RESTORE_BUF_SIZE); - m_loggedUnknownMetadata = false; -} - -RestoreHelperBase::~RestoreHelperBase() -{ - free(m_buf); -} - -status_t -RestoreHelperBase::WriteFile(const String8& filename, BackupDataReader* in) -{ - ssize_t err; - size_t dataSize; - String8 key; - int fd; - void* buf = m_buf; - ssize_t amt; - int mode; - int crc; - struct stat st; - FileRec r; - - err = in->ReadEntityHeader(&key, &dataSize); - if (err != NO_ERROR) { - return err; - } - - // Get the metadata block off the head of the file entity and use that to - // set up the output file - file_metadata_v1 metadata; - amt = in->ReadEntityData(&metadata, sizeof(metadata)); - if (amt != sizeof(metadata)) { - ALOGW("Could not read metadata for %s -- %ld / %s", filename.string(), - (long)amt, strerror(errno)); - return EIO; - } - metadata.version = fromlel(metadata.version); - metadata.mode = fromlel(metadata.mode); - if (metadata.version > CURRENT_METADATA_VERSION) { - if (!m_loggedUnknownMetadata) { - m_loggedUnknownMetadata = true; - ALOGW("Restoring file with unsupported metadata version %d (currently %d)", - metadata.version, CURRENT_METADATA_VERSION); - } - } - mode = metadata.mode; - - // Write the file and compute the crc - crc = crc32(0L, Z_NULL, 0); - fd = open(filename.string(), O_CREAT|O_RDWR|O_TRUNC, mode); - if (fd == -1) { - ALOGW("Could not open file %s -- %s", filename.string(), strerror(errno)); - return errno; - } - - while ((amt = in->ReadEntityData(buf, RESTORE_BUF_SIZE)) > 0) { - err = write(fd, buf, amt); - if (err != amt) { - close(fd); - ALOGW("Error '%s' writing '%s'", strerror(errno), filename.string()); - return errno; - } - crc = crc32(crc, (Bytef*)buf, amt); - } - - close(fd); - - // Record for the snapshot - err = stat(filename.string(), &st); - if (err != 0) { - ALOGW("Error stating file that we just created %s", filename.string()); - return errno; - } - - r.file = filename; - r.deleted = false; - r.s.modTime_sec = st.st_mtime; - r.s.modTime_nsec = 0; // workaround sim breakage - //r.s.modTime_nsec = st.st_mtime_nsec; - r.s.mode = st.st_mode; - r.s.size = st.st_size; - r.s.crc32 = crc; - - m_files.add(key, r); - - return NO_ERROR; -} - -status_t -RestoreHelperBase::WriteSnapshot(int fd) -{ - return write_snapshot_file(fd, m_files);; -} - -#if TEST_BACKUP_HELPERS - -#define SCRATCH_DIR "/data/backup_helper_test/" - -static int -write_text_file(const char* path, const char* data) -{ - int amt; - int fd; - int len; - - fd = creat(path, 0666); - if (fd == -1) { - fprintf(stderr, "creat %s failed\n", path); - return errno; - } - - len = strlen(data); - amt = write(fd, data, len); - if (amt != len) { - fprintf(stderr, "error (%s) writing to file %s\n", strerror(errno), path); - return errno; - } - - close(fd); - - return 0; -} - -static int -compare_file(const char* path, const unsigned char* data, int len) -{ - int fd; - int amt; - - fd = open(path, O_RDONLY); - if (fd == -1) { - fprintf(stderr, "compare_file error (%s) opening %s\n", strerror(errno), path); - return errno; - } - - unsigned char* contents = (unsigned char*)malloc(len); - if (contents == NULL) { - fprintf(stderr, "malloc(%d) failed\n", len); - return ENOMEM; - } - - bool sizesMatch = true; - amt = lseek(fd, 0, SEEK_END); - if (amt != len) { - fprintf(stderr, "compare_file file length should be %d, was %d\n", len, amt); - sizesMatch = false; - } - lseek(fd, 0, SEEK_SET); - - int readLen = amt < len ? amt : len; - amt = read(fd, contents, readLen); - if (amt != readLen) { - fprintf(stderr, "compare_file read expected %d bytes but got %d\n", len, amt); - } - - bool contentsMatch = true; - for (int i=0; i snapshot; - const char* filename = SCRATCH_DIR "backup_helper_test_empty.snap"; - - system("rm -r " SCRATCH_DIR); - mkdir(SCRATCH_DIR, 0777); - - // write - fd = creat(filename, 0666); - if (fd == -1) { - fprintf(stderr, "error creating %s\n", filename); - return 1; - } - - err = write_snapshot_file(fd, snapshot); - - close(fd); - - if (err != 0) { - fprintf(stderr, "write_snapshot_file reported error %d (%s)\n", err, strerror(err)); - return err; - } - - static const unsigned char correct_data[] = { - 0x53, 0x6e, 0x61, 0x70, 0x00, 0x00, 0x00, 0x00, - 0x46, 0x69, 0x6c, 0x65, 0x10, 0x00, 0x00, 0x00 - }; - - err = compare_file(filename, correct_data, sizeof(correct_data)); - if (err != 0) { - return err; - } - - // read - fd = open(filename, O_RDONLY); - if (fd == -1) { - fprintf(stderr, "error opening for read %s\n", filename); - return 1; - } - - KeyedVector readSnapshot; - err = read_snapshot_file(fd, &readSnapshot); - if (err != 0) { - fprintf(stderr, "read_snapshot_file failed %d\n", err); - return err; - } - - if (readSnapshot.size() != 0) { - fprintf(stderr, "readSnapshot should be length 0\n"); - return 1; - } - - return 0; -} - -int -backup_helper_test_four() -{ - int err; - int fd; - KeyedVector snapshot; - const char* filename = SCRATCH_DIR "backup_helper_test_four.snap"; - - system("rm -r " SCRATCH_DIR); - mkdir(SCRATCH_DIR, 0777); - - // write - fd = creat(filename, 0666); - if (fd == -1) { - fprintf(stderr, "error opening %s\n", filename); - return 1; - } - - String8 filenames[4]; - FileState states[4]; - FileRec r; - r.deleted = false; - - states[0].modTime_sec = 0xfedcba98; - states[0].modTime_nsec = 0xdeadbeef; - states[0].mode = 0777; // decimal 511, hex 0x000001ff - states[0].size = 0xababbcbc; - states[0].crc32 = 0x12345678; - states[0].nameLen = -12; - r.s = states[0]; - filenames[0] = String8("bytes_of_padding"); - snapshot.add(filenames[0], r); - - states[1].modTime_sec = 0x93400031; - states[1].modTime_nsec = 0xdeadbeef; - states[1].mode = 0666; // decimal 438, hex 0x000001b6 - states[1].size = 0x88557766; - states[1].crc32 = 0x22334422; - states[1].nameLen = -1; - r.s = states[1]; - filenames[1] = String8("bytes_of_padding3"); - snapshot.add(filenames[1], r); - - states[2].modTime_sec = 0x33221144; - states[2].modTime_nsec = 0xdeadbeef; - states[2].mode = 0744; // decimal 484, hex 0x000001e4 - states[2].size = 0x11223344; - states[2].crc32 = 0x01122334; - states[2].nameLen = 0; - r.s = states[2]; - filenames[2] = String8("bytes_of_padding_2"); - snapshot.add(filenames[2], r); - - states[3].modTime_sec = 0x33221144; - states[3].modTime_nsec = 0xdeadbeef; - states[3].mode = 0755; // decimal 493, hex 0x000001ed - states[3].size = 0x11223344; - states[3].crc32 = 0x01122334; - states[3].nameLen = 0; - r.s = states[3]; - filenames[3] = String8("bytes_of_padding__1"); - snapshot.add(filenames[3], r); - - err = write_snapshot_file(fd, snapshot); - - close(fd); - - if (err != 0) { - fprintf(stderr, "write_snapshot_file reported error %d (%s)\n", err, strerror(err)); - return err; - } - - static const unsigned char correct_data[] = { - // header - 0x53, 0x6e, 0x61, 0x70, 0x04, 0x00, 0x00, 0x00, - 0x46, 0x69, 0x6c, 0x65, 0xbc, 0x00, 0x00, 0x00, - - // bytes_of_padding - 0x98, 0xba, 0xdc, 0xfe, 0xef, 0xbe, 0xad, 0xde, - 0xff, 0x01, 0x00, 0x00, 0xbc, 0xbc, 0xab, 0xab, - 0x78, 0x56, 0x34, 0x12, 0x10, 0x00, 0x00, 0x00, - 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x6f, 0x66, - 0x5f, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, - - // bytes_of_padding3 - 0x31, 0x00, 0x40, 0x93, 0xef, 0xbe, 0xad, 0xde, - 0xb6, 0x01, 0x00, 0x00, 0x66, 0x77, 0x55, 0x88, - 0x22, 0x44, 0x33, 0x22, 0x11, 0x00, 0x00, 0x00, - 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x6f, 0x66, - 0x5f, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, - 0x33, 0xab, 0xab, 0xab, - - // bytes of padding2 - 0x44, 0x11, 0x22, 0x33, 0xef, 0xbe, 0xad, 0xde, - 0xe4, 0x01, 0x00, 0x00, 0x44, 0x33, 0x22, 0x11, - 0x34, 0x23, 0x12, 0x01, 0x12, 0x00, 0x00, 0x00, - 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x6f, 0x66, - 0x5f, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, - 0x5f, 0x32, 0xab, 0xab, - - // bytes of padding3 - 0x44, 0x11, 0x22, 0x33, 0xef, 0xbe, 0xad, 0xde, - 0xed, 0x01, 0x00, 0x00, 0x44, 0x33, 0x22, 0x11, - 0x34, 0x23, 0x12, 0x01, 0x13, 0x00, 0x00, 0x00, - 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x6f, 0x66, - 0x5f, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, - 0x5f, 0x5f, 0x31, 0xab - }; - - err = compare_file(filename, correct_data, sizeof(correct_data)); - if (err != 0) { - return err; - } - - // read - fd = open(filename, O_RDONLY); - if (fd == -1) { - fprintf(stderr, "error opening for read %s\n", filename); - return 1; - } - - - KeyedVector readSnapshot; - err = read_snapshot_file(fd, &readSnapshot); - if (err != 0) { - fprintf(stderr, "read_snapshot_file failed %d\n", err); - return err; - } - - if (readSnapshot.size() != 4) { - fprintf(stderr, "readSnapshot should be length 4 is %d\n", readSnapshot.size()); - return 1; - } - - bool matched = true; - for (size_t i=0; i -#include -#include -#include -#include -#include - -#define LOG_TAG "ObbFile" - -#include -#include -#include - -//#define DEBUG 1 - -#define kFooterTagSize 8 /* last two 32-bit integers */ - -#define kFooterMinSize 33 /* 32-bit signature version (4 bytes) - * 32-bit package version (4 bytes) - * 32-bit flags (4 bytes) - * 64-bit salt (8 bytes) - * 32-bit package name size (4 bytes) - * >=1-character package name (1 byte) - * 32-bit footer size (4 bytes) - * 32-bit footer marker (4 bytes) - */ - -#define kMaxBufSize 32768 /* Maximum file read buffer */ - -#define kSignature 0x01059983U /* ObbFile signature */ - -#define kSigVersion 1 /* We only know about signature version 1 */ - -/* offsets in version 1 of the header */ -#define kPackageVersionOffset 4 -#define kFlagsOffset 8 -#define kSaltOffset 12 -#define kPackageNameLenOffset 20 -#define kPackageNameOffset 24 - -/* - * TEMP_FAILURE_RETRY is defined by some, but not all, versions of - * . (Alas, it is not as standard as we'd hoped!) So, if it's - * not already defined, then define it here. - */ -#ifndef TEMP_FAILURE_RETRY -/* Used to retry syscalls that can return EINTR. */ -#define TEMP_FAILURE_RETRY(exp) ({ \ - typeof (exp) _rc; \ - do { \ - _rc = (exp); \ - } while (_rc == -1 && errno == EINTR); \ - _rc; }) -#endif - - -namespace android { - -ObbFile::ObbFile() - : mPackageName("") - , mVersion(-1) - , mFlags(0) -{ - memset(mSalt, 0, sizeof(mSalt)); -} - -ObbFile::~ObbFile() { -} - -bool ObbFile::readFrom(const char* filename) -{ - int fd; - bool success = false; - - fd = ::open(filename, O_RDONLY); - if (fd < 0) { - ALOGW("couldn't open file %s: %s", filename, strerror(errno)); - goto out; - } - success = readFrom(fd); - close(fd); - - if (!success) { - ALOGW("failed to read from %s (fd=%d)\n", filename, fd); - } - -out: - return success; -} - -bool ObbFile::readFrom(int fd) -{ - if (fd < 0) { - ALOGW("attempt to read from invalid fd\n"); - return false; - } - - return parseObbFile(fd); -} - -bool ObbFile::parseObbFile(int fd) -{ - off64_t fileLength = lseek64(fd, 0, SEEK_END); - - if (fileLength < kFooterMinSize) { - if (fileLength < 0) { - ALOGW("error seeking in ObbFile: %s\n", strerror(errno)); - } else { - ALOGW("file is only %lld (less than %d minimum)\n", fileLength, kFooterMinSize); - } - return false; - } - - ssize_t actual; - size_t footerSize; - - { - lseek64(fd, fileLength - kFooterTagSize, SEEK_SET); - - char *footer = new char[kFooterTagSize]; - actual = TEMP_FAILURE_RETRY(read(fd, footer, kFooterTagSize)); - if (actual != kFooterTagSize) { - ALOGW("couldn't read footer signature: %s\n", strerror(errno)); - return false; - } - - unsigned int fileSig = get4LE((unsigned char*)footer + sizeof(int32_t)); - if (fileSig != kSignature) { - ALOGW("footer didn't match magic string (expected 0x%08x; got 0x%08x)\n", - kSignature, fileSig); - return false; - } - - footerSize = get4LE((unsigned char*)footer); - if (footerSize > (size_t)fileLength - kFooterTagSize - || footerSize > kMaxBufSize) { - ALOGW("claimed footer size is too large (0x%08zx; file size is 0x%08llx)\n", - footerSize, fileLength); - return false; - } - - if (footerSize < (kFooterMinSize - kFooterTagSize)) { - ALOGW("claimed footer size is too small (0x%zx; minimum size is 0x%x)\n", - footerSize, kFooterMinSize - kFooterTagSize); - return false; - } - } - - off64_t fileOffset = fileLength - footerSize - kFooterTagSize; - if (lseek64(fd, fileOffset, SEEK_SET) != fileOffset) { - ALOGW("seek %lld failed: %s\n", fileOffset, strerror(errno)); - return false; - } - - mFooterStart = fileOffset; - - char* scanBuf = (char*)malloc(footerSize); - if (scanBuf == NULL) { - ALOGW("couldn't allocate scanBuf: %s\n", strerror(errno)); - return false; - } - - actual = TEMP_FAILURE_RETRY(read(fd, scanBuf, footerSize)); - // readAmount is guaranteed to be less than kMaxBufSize - if (actual != (ssize_t)footerSize) { - ALOGI("couldn't read ObbFile footer: %s\n", strerror(errno)); - free(scanBuf); - return false; - } - -#ifdef DEBUG - for (int i = 0; i < footerSize; ++i) { - ALOGI("char: 0x%02x\n", scanBuf[i]); - } -#endif - - uint32_t sigVersion = get4LE((unsigned char*)scanBuf); - if (sigVersion != kSigVersion) { - ALOGW("Unsupported ObbFile version %d\n", sigVersion); - free(scanBuf); - return false; - } - - mVersion = (int32_t) get4LE((unsigned char*)scanBuf + kPackageVersionOffset); - mFlags = (int32_t) get4LE((unsigned char*)scanBuf + kFlagsOffset); - - memcpy(&mSalt, (unsigned char*)scanBuf + kSaltOffset, sizeof(mSalt)); - - size_t packageNameLen = get4LE((unsigned char*)scanBuf + kPackageNameLenOffset); - if (packageNameLen == 0 - || packageNameLen > (footerSize - kPackageNameOffset)) { - ALOGW("bad ObbFile package name length (0x%04zx; 0x%04zx possible)\n", - packageNameLen, footerSize - kPackageNameOffset); - free(scanBuf); - return false; - } - - char* packageName = reinterpret_cast(scanBuf + kPackageNameOffset); - mPackageName = String8(const_cast(packageName), packageNameLen); - - free(scanBuf); - -#ifdef DEBUG - ALOGI("Obb scan succeeded: packageName=%s, version=%d\n", mPackageName.string(), mVersion); -#endif - - return true; -} - -bool ObbFile::writeTo(const char* filename) -{ - int fd; - bool success = false; - - fd = ::open(filename, O_WRONLY); - if (fd < 0) { - goto out; - } - success = writeTo(fd); - close(fd); - -out: - if (!success) { - ALOGW("failed to write to %s: %s\n", filename, strerror(errno)); - } - return success; -} - -bool ObbFile::writeTo(int fd) -{ - if (fd < 0) { - return false; - } - - lseek64(fd, 0, SEEK_END); - - if (mPackageName.size() == 0 || mVersion == -1) { - ALOGW("tried to write uninitialized ObbFile data\n"); - return false; - } - - unsigned char intBuf[sizeof(uint32_t)+1]; - memset(&intBuf, 0, sizeof(intBuf)); - - put4LE(intBuf, kSigVersion); - if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) { - ALOGW("couldn't write signature version: %s\n", strerror(errno)); - return false; - } - - put4LE(intBuf, mVersion); - if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) { - ALOGW("couldn't write package version\n"); - return false; - } - - put4LE(intBuf, mFlags); - if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) { - ALOGW("couldn't write package version\n"); - return false; - } - - if (write(fd, mSalt, sizeof(mSalt)) != (ssize_t)sizeof(mSalt)) { - ALOGW("couldn't write salt: %s\n", strerror(errno)); - return false; - } - - size_t packageNameLen = mPackageName.size(); - put4LE(intBuf, packageNameLen); - if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) { - ALOGW("couldn't write package name length: %s\n", strerror(errno)); - return false; - } - - if (write(fd, mPackageName.string(), packageNameLen) != (ssize_t)packageNameLen) { - ALOGW("couldn't write package name: %s\n", strerror(errno)); - return false; - } - - put4LE(intBuf, kPackageNameOffset + packageNameLen); - if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) { - ALOGW("couldn't write footer size: %s\n", strerror(errno)); - return false; - } - - put4LE(intBuf, kSignature); - if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) { - ALOGW("couldn't write footer magic signature: %s\n", strerror(errno)); - return false; - } - - return true; -} - -bool ObbFile::removeFrom(const char* filename) -{ - int fd; - bool success = false; - - fd = ::open(filename, O_RDWR); - if (fd < 0) { - goto out; - } - success = removeFrom(fd); - close(fd); - -out: - if (!success) { - ALOGW("failed to remove signature from %s: %s\n", filename, strerror(errno)); - } - return success; -} - -bool ObbFile::removeFrom(int fd) -{ - if (fd < 0) { - return false; - } - - if (!readFrom(fd)) { - return false; - } - - ftruncate(fd, mFooterStart); - - return true; -} - -} diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp deleted file mode 100644 index 07f3b1624..000000000 --- a/libs/utils/ResourceTypes.cpp +++ /dev/null @@ -1,5580 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "ResourceType" -//#define LOG_NDEBUG 0 - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#ifndef INT32_MAX -#define INT32_MAX ((int32_t)(2147483647)) -#endif - -#define POOL_NOISY(x) //x -#define XML_NOISY(x) //x -#define TABLE_NOISY(x) //x -#define TABLE_GETENTRY(x) //x -#define TABLE_SUPER_NOISY(x) //x -#define LOAD_TABLE_NOISY(x) //x -#define TABLE_THEME(x) //x - -namespace android { - -#ifdef HAVE_WINSOCK -#undef nhtol -#undef htonl - -#ifdef HAVE_LITTLE_ENDIAN -#define ntohl(x) ( ((x) << 24) | (((x) >> 24) & 255) | (((x) << 8) & 0xff0000) | (((x) >> 8) & 0xff00) ) -#define htonl(x) ntohl(x) -#define ntohs(x) ( (((x) << 8) & 0xff00) | (((x) >> 8) & 255) ) -#define htons(x) ntohs(x) -#else -#define ntohl(x) (x) -#define htonl(x) (x) -#define ntohs(x) (x) -#define htons(x) (x) -#endif -#endif - -#define IDMAP_MAGIC 0x706d6469 -// size measured in sizeof(uint32_t) -#define IDMAP_HEADER_SIZE (ResTable::IDMAP_HEADER_SIZE_BYTES / sizeof(uint32_t)) - -static void printToLogFunc(void* cookie, const char* txt) -{ - ALOGV("%s", txt); -} - -// Standard C isspace() is only required to look at the low byte of its input, so -// produces incorrect results for UTF-16 characters. For safety's sake, assume that -// any high-byte UTF-16 code point is not whitespace. -inline int isspace16(char16_t c) { - return (c < 0x0080 && isspace(c)); -} - -// range checked; guaranteed to NUL-terminate within the stated number of available slots -// NOTE: if this truncates the dst string due to running out of space, no attempt is -// made to avoid splitting surrogate pairs. -static void strcpy16_dtoh(uint16_t* dst, const uint16_t* src, size_t avail) -{ - uint16_t* last = dst + avail - 1; - while (*src && (dst < last)) { - char16_t s = dtohs(*src); - *dst++ = s; - src++; - } - *dst = 0; -} - -static status_t validate_chunk(const ResChunk_header* chunk, - size_t minSize, - const uint8_t* dataEnd, - const char* name) -{ - const uint16_t headerSize = dtohs(chunk->headerSize); - const uint32_t size = dtohl(chunk->size); - - if (headerSize >= minSize) { - if (headerSize <= size) { - if (((headerSize|size)&0x3) == 0) { - if ((ssize_t)size <= (dataEnd-((const uint8_t*)chunk))) { - return NO_ERROR; - } - ALOGW("%s data size %p extends beyond resource end %p.", - name, (void*)size, - (void*)(dataEnd-((const uint8_t*)chunk))); - return BAD_TYPE; - } - ALOGW("%s size 0x%x or headerSize 0x%x is not on an integer boundary.", - name, (int)size, (int)headerSize); - return BAD_TYPE; - } - ALOGW("%s size %p is smaller than header size %p.", - name, (void*)size, (void*)(int)headerSize); - return BAD_TYPE; - } - ALOGW("%s header size %p is too small.", - name, (void*)(int)headerSize); - return BAD_TYPE; -} - -inline void Res_value::copyFrom_dtoh(const Res_value& src) -{ - size = dtohs(src.size); - res0 = src.res0; - dataType = src.dataType; - data = dtohl(src.data); -} - -void Res_png_9patch::deviceToFile() -{ - for (int i = 0; i < numXDivs; i++) { - xDivs[i] = htonl(xDivs[i]); - } - for (int i = 0; i < numYDivs; i++) { - yDivs[i] = htonl(yDivs[i]); - } - paddingLeft = htonl(paddingLeft); - paddingRight = htonl(paddingRight); - paddingTop = htonl(paddingTop); - paddingBottom = htonl(paddingBottom); - for (int i=0; ixDivs, numXDivs * sizeof(int32_t)); - data += numXDivs * sizeof(int32_t); - memmove(data, this->yDivs, numYDivs * sizeof(int32_t)); - data += numYDivs * sizeof(int32_t); - memmove(data, this->colors, numColors * sizeof(uint32_t)); -} - -static void deserializeInternal(const void* inData, Res_png_9patch* outData) { - char* patch = (char*) inData; - if (inData != outData) { - memmove(&outData->wasDeserialized, patch, 4); // copy wasDeserialized, numXDivs, numYDivs, numColors - memmove(&outData->paddingLeft, patch + 12, 4); // copy wasDeserialized, numXDivs, numYDivs, numColors - } - outData->wasDeserialized = true; - char* data = (char*)outData; - data += sizeof(Res_png_9patch); - outData->xDivs = (int32_t*) data; - data += outData->numXDivs * sizeof(int32_t); - outData->yDivs = (int32_t*) data; - data += outData->numYDivs * sizeof(int32_t); - outData->colors = (uint32_t*) data; -} - -static bool assertIdmapHeader(const uint32_t* map, size_t sizeBytes) -{ - if (sizeBytes < ResTable::IDMAP_HEADER_SIZE_BYTES) { - ALOGW("idmap assertion failed: size=%d bytes\n", (int)sizeBytes); - return false; - } - if (*map != htodl(IDMAP_MAGIC)) { // htodl: map data expected to be in correct endianess - ALOGW("idmap assertion failed: invalid magic found (is 0x%08x, expected 0x%08x)\n", - *map, htodl(IDMAP_MAGIC)); - return false; - } - return true; -} - -static status_t idmapLookup(const uint32_t* map, size_t sizeBytes, uint32_t key, uint32_t* outValue) -{ - // see README for details on the format of map - if (!assertIdmapHeader(map, sizeBytes)) { - return UNKNOWN_ERROR; - } - map = map + IDMAP_HEADER_SIZE; // skip ahead to data segment - // size of data block, in uint32_t - const size_t size = (sizeBytes - ResTable::IDMAP_HEADER_SIZE_BYTES) / sizeof(uint32_t); - const uint32_t type = Res_GETTYPE(key) + 1; // add one, idmap stores "public" type id - const uint32_t entry = Res_GETENTRY(key); - const uint32_t typeCount = *map; - - if (type > typeCount) { - ALOGW("Resource ID map: type=%d exceeds number of types=%d\n", type, typeCount); - return UNKNOWN_ERROR; - } - if (typeCount > size) { - ALOGW("Resource ID map: number of types=%d exceeds size of map=%d\n", typeCount, (int)size); - return UNKNOWN_ERROR; - } - const uint32_t typeOffset = map[type]; - if (typeOffset == 0) { - *outValue = 0; - return NO_ERROR; - } - if (typeOffset + 1 > size) { - ALOGW("Resource ID map: type offset=%d exceeds reasonable value, size of map=%d\n", - typeOffset, (int)size); - return UNKNOWN_ERROR; - } - const uint32_t entryCount = map[typeOffset]; - const uint32_t entryOffset = map[typeOffset + 1]; - if (entryCount == 0 || entry < entryOffset || entry - entryOffset > entryCount - 1) { - *outValue = 0; - return NO_ERROR; - } - const uint32_t index = typeOffset + 2 + entry - entryOffset; - if (index > size) { - ALOGW("Resource ID map: entry index=%d exceeds size of map=%d\n", index, (int)size); - *outValue = 0; - return NO_ERROR; - } - *outValue = map[index]; - - return NO_ERROR; -} - -static status_t getIdmapPackageId(const uint32_t* map, size_t mapSize, uint32_t *outId) -{ - if (!assertIdmapHeader(map, mapSize)) { - return UNKNOWN_ERROR; - } - const uint32_t* p = map + IDMAP_HEADER_SIZE + 1; - while (*p == 0) { - ++p; - } - *outId = (map[*p + IDMAP_HEADER_SIZE + 2] >> 24) & 0x000000ff; - return NO_ERROR; -} - -Res_png_9patch* Res_png_9patch::deserialize(const void* inData) -{ - if (sizeof(void*) != sizeof(int32_t)) { - ALOGE("Cannot deserialize on non 32-bit system\n"); - return NULL; - } - deserializeInternal(inData, (Res_png_9patch*) inData); - return (Res_png_9patch*) inData; -} - -// -------------------------------------------------------------------- -// -------------------------------------------------------------------- -// -------------------------------------------------------------------- - -ResStringPool::ResStringPool() - : mError(NO_INIT), mOwnedData(NULL), mHeader(NULL), mCache(NULL) -{ -} - -ResStringPool::ResStringPool(const void* data, size_t size, bool copyData) - : mError(NO_INIT), mOwnedData(NULL), mHeader(NULL), mCache(NULL) -{ - setTo(data, size, copyData); -} - -ResStringPool::~ResStringPool() -{ - uninit(); -} - -status_t ResStringPool::setTo(const void* data, size_t size, bool copyData) -{ - if (!data || !size) { - return (mError=BAD_TYPE); - } - - uninit(); - - const bool notDeviceEndian = htods(0xf0) != 0xf0; - - if (copyData || notDeviceEndian) { - mOwnedData = malloc(size); - if (mOwnedData == NULL) { - return (mError=NO_MEMORY); - } - memcpy(mOwnedData, data, size); - data = mOwnedData; - } - - mHeader = (const ResStringPool_header*)data; - - if (notDeviceEndian) { - ResStringPool_header* h = const_cast(mHeader); - h->header.headerSize = dtohs(mHeader->header.headerSize); - h->header.type = dtohs(mHeader->header.type); - h->header.size = dtohl(mHeader->header.size); - h->stringCount = dtohl(mHeader->stringCount); - h->styleCount = dtohl(mHeader->styleCount); - h->flags = dtohl(mHeader->flags); - h->stringsStart = dtohl(mHeader->stringsStart); - h->stylesStart = dtohl(mHeader->stylesStart); - } - - if (mHeader->header.headerSize > mHeader->header.size - || mHeader->header.size > size) { - ALOGW("Bad string block: header size %d or total size %d is larger than data size %d\n", - (int)mHeader->header.headerSize, (int)mHeader->header.size, (int)size); - return (mError=BAD_TYPE); - } - mSize = mHeader->header.size; - mEntries = (const uint32_t*) - (((const uint8_t*)data)+mHeader->header.headerSize); - - if (mHeader->stringCount > 0) { - if ((mHeader->stringCount*sizeof(uint32_t) < mHeader->stringCount) // uint32 overflow? - || (mHeader->header.headerSize+(mHeader->stringCount*sizeof(uint32_t))) - > size) { - ALOGW("Bad string block: entry of %d items extends past data size %d\n", - (int)(mHeader->header.headerSize+(mHeader->stringCount*sizeof(uint32_t))), - (int)size); - return (mError=BAD_TYPE); - } - - size_t charSize; - if (mHeader->flags&ResStringPool_header::UTF8_FLAG) { - charSize = sizeof(uint8_t); - mCache = (char16_t**)calloc(mHeader->stringCount, sizeof(char16_t**)); - } else { - charSize = sizeof(char16_t); - } - - mStrings = (const void*) - (((const uint8_t*)data)+mHeader->stringsStart); - if (mHeader->stringsStart >= (mHeader->header.size-sizeof(uint16_t))) { - ALOGW("Bad string block: string pool starts at %d, after total size %d\n", - (int)mHeader->stringsStart, (int)mHeader->header.size); - return (mError=BAD_TYPE); - } - if (mHeader->styleCount == 0) { - mStringPoolSize = - (mHeader->header.size-mHeader->stringsStart)/charSize; - } else { - // check invariant: styles starts before end of data - if (mHeader->stylesStart >= (mHeader->header.size-sizeof(uint16_t))) { - ALOGW("Bad style block: style block starts at %d past data size of %d\n", - (int)mHeader->stylesStart, (int)mHeader->header.size); - return (mError=BAD_TYPE); - } - // check invariant: styles follow the strings - if (mHeader->stylesStart <= mHeader->stringsStart) { - ALOGW("Bad style block: style block starts at %d, before strings at %d\n", - (int)mHeader->stylesStart, (int)mHeader->stringsStart); - return (mError=BAD_TYPE); - } - mStringPoolSize = - (mHeader->stylesStart-mHeader->stringsStart)/charSize; - } - - // check invariant: stringCount > 0 requires a string pool to exist - if (mStringPoolSize == 0) { - ALOGW("Bad string block: stringCount is %d but pool size is 0\n", (int)mHeader->stringCount); - return (mError=BAD_TYPE); - } - - if (notDeviceEndian) { - size_t i; - uint32_t* e = const_cast(mEntries); - for (i=0; istringCount; i++) { - e[i] = dtohl(mEntries[i]); - } - if (!(mHeader->flags&ResStringPool_header::UTF8_FLAG)) { - const char16_t* strings = (const char16_t*)mStrings; - char16_t* s = const_cast(strings); - for (i=0; iflags&ResStringPool_header::UTF8_FLAG && - ((uint8_t*)mStrings)[mStringPoolSize-1] != 0) || - (!mHeader->flags&ResStringPool_header::UTF8_FLAG && - ((char16_t*)mStrings)[mStringPoolSize-1] != 0)) { - ALOGW("Bad string block: last string is not 0-terminated\n"); - return (mError=BAD_TYPE); - } - } else { - mStrings = NULL; - mStringPoolSize = 0; - } - - if (mHeader->styleCount > 0) { - mEntryStyles = mEntries + mHeader->stringCount; - // invariant: integer overflow in calculating mEntryStyles - if (mEntryStyles < mEntries) { - ALOGW("Bad string block: integer overflow finding styles\n"); - return (mError=BAD_TYPE); - } - - if (((const uint8_t*)mEntryStyles-(const uint8_t*)mHeader) > (int)size) { - ALOGW("Bad string block: entry of %d styles extends past data size %d\n", - (int)((const uint8_t*)mEntryStyles-(const uint8_t*)mHeader), - (int)size); - return (mError=BAD_TYPE); - } - mStyles = (const uint32_t*) - (((const uint8_t*)data)+mHeader->stylesStart); - if (mHeader->stylesStart >= mHeader->header.size) { - ALOGW("Bad string block: style pool starts %d, after total size %d\n", - (int)mHeader->stylesStart, (int)mHeader->header.size); - return (mError=BAD_TYPE); - } - mStylePoolSize = - (mHeader->header.size-mHeader->stylesStart)/sizeof(uint32_t); - - if (notDeviceEndian) { - size_t i; - uint32_t* e = const_cast(mEntryStyles); - for (i=0; istyleCount; i++) { - e[i] = dtohl(mEntryStyles[i]); - } - uint32_t* s = const_cast(mStyles); - for (i=0; istringCount; x++) { - if (mCache[x] != NULL) { - free(mCache[x]); - mCache[x] = NULL; - } - } - free(mCache); - mCache = NULL; - } -} - -/** - * Strings in UTF-16 format have length indicated by a length encoded in the - * stored data. It is either 1 or 2 characters of length data. This allows a - * maximum length of 0x7FFFFFF (2147483647 bytes), but if you're storing that - * much data in a string, you're abusing them. - * - * If the high bit is set, then there are two characters or 4 bytes of length - * data encoded. In that case, drop the high bit of the first character and - * add it together with the next character. - */ -static inline size_t -decodeLength(const char16_t** str) -{ - size_t len = **str; - if ((len & 0x8000) != 0) { - (*str)++; - len = ((len & 0x7FFF) << 16) | **str; - } - (*str)++; - return len; -} - -/** - * Strings in UTF-8 format have length indicated by a length encoded in the - * stored data. It is either 1 or 2 characters of length data. This allows a - * maximum length of 0x7FFF (32767 bytes), but you should consider storing - * text in another way if you're using that much data in a single string. - * - * If the high bit is set, then there are two characters or 2 bytes of length - * data encoded. In that case, drop the high bit of the first character and - * add it together with the next character. - */ -static inline size_t -decodeLength(const uint8_t** str) -{ - size_t len = **str; - if ((len & 0x80) != 0) { - (*str)++; - len = ((len & 0x7F) << 8) | **str; - } - (*str)++; - return len; -} - -const uint16_t* ResStringPool::stringAt(size_t idx, size_t* u16len) const -{ - if (mError == NO_ERROR && idx < mHeader->stringCount) { - const bool isUTF8 = (mHeader->flags&ResStringPool_header::UTF8_FLAG) != 0; - const uint32_t off = mEntries[idx]/(isUTF8?sizeof(char):sizeof(char16_t)); - if (off < (mStringPoolSize-1)) { - if (!isUTF8) { - const char16_t* strings = (char16_t*)mStrings; - const char16_t* str = strings+off; - - *u16len = decodeLength(&str); - if ((uint32_t)(str+*u16len-strings) < mStringPoolSize) { - return str; - } else { - ALOGW("Bad string block: string #%d extends to %d, past end at %d\n", - (int)idx, (int)(str+*u16len-strings), (int)mStringPoolSize); - } - } else { - const uint8_t* strings = (uint8_t*)mStrings; - const uint8_t* u8str = strings+off; - - *u16len = decodeLength(&u8str); - size_t u8len = decodeLength(&u8str); - - // encLen must be less than 0x7FFF due to encoding. - if ((uint32_t)(u8str+u8len-strings) < mStringPoolSize) { - AutoMutex lock(mDecodeLock); - - if (mCache[idx] != NULL) { - return mCache[idx]; - } - - ssize_t actualLen = utf8_to_utf16_length(u8str, u8len); - if (actualLen < 0 || (size_t)actualLen != *u16len) { - ALOGW("Bad string block: string #%lld decoded length is not correct " - "%lld vs %llu\n", - (long long)idx, (long long)actualLen, (long long)*u16len); - return NULL; - } - - char16_t *u16str = (char16_t *)calloc(*u16len+1, sizeof(char16_t)); - if (!u16str) { - ALOGW("No memory when trying to allocate decode cache for string #%d\n", - (int)idx); - return NULL; - } - - utf8_to_utf16(u8str, u8len, u16str); - mCache[idx] = u16str; - return u16str; - } else { - ALOGW("Bad string block: string #%lld extends to %lld, past end at %lld\n", - (long long)idx, (long long)(u8str+u8len-strings), - (long long)mStringPoolSize); - } - } - } else { - ALOGW("Bad string block: string #%d entry is at %d, past end at %d\n", - (int)idx, (int)(off*sizeof(uint16_t)), - (int)(mStringPoolSize*sizeof(uint16_t))); - } - } - return NULL; -} - -const char* ResStringPool::string8At(size_t idx, size_t* outLen) const -{ - if (mError == NO_ERROR && idx < mHeader->stringCount) { - const bool isUTF8 = (mHeader->flags&ResStringPool_header::UTF8_FLAG) != 0; - const uint32_t off = mEntries[idx]/(isUTF8?sizeof(char):sizeof(char16_t)); - if (off < (mStringPoolSize-1)) { - if (isUTF8) { - const uint8_t* strings = (uint8_t*)mStrings; - const uint8_t* str = strings+off; - *outLen = decodeLength(&str); - size_t encLen = decodeLength(&str); - if ((uint32_t)(str+encLen-strings) < mStringPoolSize) { - return (const char*)str; - } else { - ALOGW("Bad string block: string #%d extends to %d, past end at %d\n", - (int)idx, (int)(str+encLen-strings), (int)mStringPoolSize); - } - } - } else { - ALOGW("Bad string block: string #%d entry is at %d, past end at %d\n", - (int)idx, (int)(off*sizeof(uint16_t)), - (int)(mStringPoolSize*sizeof(uint16_t))); - } - } - return NULL; -} - -const String8 ResStringPool::string8ObjectAt(size_t idx) const -{ - size_t len; - const char *str = (const char*)string8At(idx, &len); - if (str != NULL) { - return String8(str); - } - return String8(stringAt(idx, &len)); -} - -const ResStringPool_span* ResStringPool::styleAt(const ResStringPool_ref& ref) const -{ - return styleAt(ref.index); -} - -const ResStringPool_span* ResStringPool::styleAt(size_t idx) const -{ - if (mError == NO_ERROR && idx < mHeader->styleCount) { - const uint32_t off = (mEntryStyles[idx]/sizeof(uint32_t)); - if (off < mStylePoolSize) { - return (const ResStringPool_span*)(mStyles+off); - } else { - ALOGW("Bad string block: style #%d entry is at %d, past end at %d\n", - (int)idx, (int)(off*sizeof(uint32_t)), - (int)(mStylePoolSize*sizeof(uint32_t))); - } - } - return NULL; -} - -ssize_t ResStringPool::indexOfString(const char16_t* str, size_t strLen) const -{ - if (mError != NO_ERROR) { - return mError; - } - - size_t len; - - // TODO optimize searching for UTF-8 strings taking into account - // the cache fill to determine when to convert the searched-for - // string key to UTF-8. - - if (mHeader->flags&ResStringPool_header::SORTED_FLAG) { - // Do a binary search for the string... - ssize_t l = 0; - ssize_t h = mHeader->stringCount-1; - - ssize_t mid; - while (l <= h) { - mid = l + (h - l)/2; - const char16_t* s = stringAt(mid, &len); - int c = s ? strzcmp16(s, len, str, strLen) : -1; - POOL_NOISY(printf("Looking for %s, at %s, cmp=%d, l/mid/h=%d/%d/%d\n", - String8(str).string(), - String8(s).string(), - c, (int)l, (int)mid, (int)h)); - if (c == 0) { - return mid; - } else if (c < 0) { - l = mid + 1; - } else { - h = mid - 1; - } - } - } else { - // It is unusual to get the ID from an unsorted string block... - // most often this happens because we want to get IDs for style - // span tags; since those always appear at the end of the string - // block, start searching at the back. - for (int i=mHeader->stringCount-1; i>=0; i--) { - const char16_t* s = stringAt(i, &len); - POOL_NOISY(printf("Looking for %s, at %s, i=%d\n", - String8(str, strLen).string(), - String8(s).string(), - i)); - if (s && strzcmp16(s, len, str, strLen) == 0) { - return i; - } - } - } - - return NAME_NOT_FOUND; -} - -size_t ResStringPool::size() const -{ - return (mError == NO_ERROR) ? mHeader->stringCount : 0; -} - -size_t ResStringPool::styleCount() const -{ - return (mError == NO_ERROR) ? mHeader->styleCount : 0; -} - -size_t ResStringPool::bytes() const -{ - return (mError == NO_ERROR) ? mHeader->header.size : 0; -} - -bool ResStringPool::isSorted() const -{ - return (mHeader->flags&ResStringPool_header::SORTED_FLAG)!=0; -} - -bool ResStringPool::isUTF8() const -{ - return (mHeader->flags&ResStringPool_header::UTF8_FLAG)!=0; -} - -// -------------------------------------------------------------------- -// -------------------------------------------------------------------- -// -------------------------------------------------------------------- - -ResXMLParser::ResXMLParser(const ResXMLTree& tree) - : mTree(tree), mEventCode(BAD_DOCUMENT) -{ -} - -void ResXMLParser::restart() -{ - mCurNode = NULL; - mEventCode = mTree.mError == NO_ERROR ? START_DOCUMENT : BAD_DOCUMENT; -} -const ResStringPool& ResXMLParser::getStrings() const -{ - return mTree.mStrings; -} - -ResXMLParser::event_code_t ResXMLParser::getEventType() const -{ - return mEventCode; -} - -ResXMLParser::event_code_t ResXMLParser::next() -{ - if (mEventCode == START_DOCUMENT) { - mCurNode = mTree.mRootNode; - mCurExt = mTree.mRootExt; - return (mEventCode=mTree.mRootCode); - } else if (mEventCode >= FIRST_CHUNK_CODE) { - return nextNode(); - } - return mEventCode; -} - -int32_t ResXMLParser::getCommentID() const -{ - return mCurNode != NULL ? dtohl(mCurNode->comment.index) : -1; -} - -const uint16_t* ResXMLParser::getComment(size_t* outLen) const -{ - int32_t id = getCommentID(); - return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; -} - -uint32_t ResXMLParser::getLineNumber() const -{ - return mCurNode != NULL ? dtohl(mCurNode->lineNumber) : -1; -} - -int32_t ResXMLParser::getTextID() const -{ - if (mEventCode == TEXT) { - return dtohl(((const ResXMLTree_cdataExt*)mCurExt)->data.index); - } - return -1; -} - -const uint16_t* ResXMLParser::getText(size_t* outLen) const -{ - int32_t id = getTextID(); - return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; -} - -ssize_t ResXMLParser::getTextValue(Res_value* outValue) const -{ - if (mEventCode == TEXT) { - outValue->copyFrom_dtoh(((const ResXMLTree_cdataExt*)mCurExt)->typedData); - return sizeof(Res_value); - } - return BAD_TYPE; -} - -int32_t ResXMLParser::getNamespacePrefixID() const -{ - if (mEventCode == START_NAMESPACE || mEventCode == END_NAMESPACE) { - return dtohl(((const ResXMLTree_namespaceExt*)mCurExt)->prefix.index); - } - return -1; -} - -const uint16_t* ResXMLParser::getNamespacePrefix(size_t* outLen) const -{ - int32_t id = getNamespacePrefixID(); - //printf("prefix=%d event=%p\n", id, mEventCode); - return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; -} - -int32_t ResXMLParser::getNamespaceUriID() const -{ - if (mEventCode == START_NAMESPACE || mEventCode == END_NAMESPACE) { - return dtohl(((const ResXMLTree_namespaceExt*)mCurExt)->uri.index); - } - return -1; -} - -const uint16_t* ResXMLParser::getNamespaceUri(size_t* outLen) const -{ - int32_t id = getNamespaceUriID(); - //printf("uri=%d event=%p\n", id, mEventCode); - return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; -} - -int32_t ResXMLParser::getElementNamespaceID() const -{ - if (mEventCode == START_TAG) { - return dtohl(((const ResXMLTree_attrExt*)mCurExt)->ns.index); - } - if (mEventCode == END_TAG) { - return dtohl(((const ResXMLTree_endElementExt*)mCurExt)->ns.index); - } - return -1; -} - -const uint16_t* ResXMLParser::getElementNamespace(size_t* outLen) const -{ - int32_t id = getElementNamespaceID(); - return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; -} - -int32_t ResXMLParser::getElementNameID() const -{ - if (mEventCode == START_TAG) { - return dtohl(((const ResXMLTree_attrExt*)mCurExt)->name.index); - } - if (mEventCode == END_TAG) { - return dtohl(((const ResXMLTree_endElementExt*)mCurExt)->name.index); - } - return -1; -} - -const uint16_t* ResXMLParser::getElementName(size_t* outLen) const -{ - int32_t id = getElementNameID(); - return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; -} - -size_t ResXMLParser::getAttributeCount() const -{ - if (mEventCode == START_TAG) { - return dtohs(((const ResXMLTree_attrExt*)mCurExt)->attributeCount); - } - return 0; -} - -int32_t ResXMLParser::getAttributeNamespaceID(size_t idx) const -{ - if (mEventCode == START_TAG) { - const ResXMLTree_attrExt* tag = (const ResXMLTree_attrExt*)mCurExt; - if (idx < dtohs(tag->attributeCount)) { - const ResXMLTree_attribute* attr = (const ResXMLTree_attribute*) - (((const uint8_t*)tag) - + dtohs(tag->attributeStart) - + (dtohs(tag->attributeSize)*idx)); - return dtohl(attr->ns.index); - } - } - return -2; -} - -const uint16_t* ResXMLParser::getAttributeNamespace(size_t idx, size_t* outLen) const -{ - int32_t id = getAttributeNamespaceID(idx); - //printf("attribute namespace=%d idx=%d event=%p\n", id, idx, mEventCode); - //XML_NOISY(printf("getAttributeNamespace 0x%x=0x%x\n", idx, id)); - return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; -} - -int32_t ResXMLParser::getAttributeNameID(size_t idx) const -{ - if (mEventCode == START_TAG) { - const ResXMLTree_attrExt* tag = (const ResXMLTree_attrExt*)mCurExt; - if (idx < dtohs(tag->attributeCount)) { - const ResXMLTree_attribute* attr = (const ResXMLTree_attribute*) - (((const uint8_t*)tag) - + dtohs(tag->attributeStart) - + (dtohs(tag->attributeSize)*idx)); - return dtohl(attr->name.index); - } - } - return -1; -} - -const uint16_t* ResXMLParser::getAttributeName(size_t idx, size_t* outLen) const -{ - int32_t id = getAttributeNameID(idx); - //printf("attribute name=%d idx=%d event=%p\n", id, idx, mEventCode); - //XML_NOISY(printf("getAttributeName 0x%x=0x%x\n", idx, id)); - return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; -} - -uint32_t ResXMLParser::getAttributeNameResID(size_t idx) const -{ - int32_t id = getAttributeNameID(idx); - if (id >= 0 && (size_t)id < mTree.mNumResIds) { - return dtohl(mTree.mResIds[id]); - } - return 0; -} - -int32_t ResXMLParser::getAttributeValueStringID(size_t idx) const -{ - if (mEventCode == START_TAG) { - const ResXMLTree_attrExt* tag = (const ResXMLTree_attrExt*)mCurExt; - if (idx < dtohs(tag->attributeCount)) { - const ResXMLTree_attribute* attr = (const ResXMLTree_attribute*) - (((const uint8_t*)tag) - + dtohs(tag->attributeStart) - + (dtohs(tag->attributeSize)*idx)); - return dtohl(attr->rawValue.index); - } - } - return -1; -} - -const uint16_t* ResXMLParser::getAttributeStringValue(size_t idx, size_t* outLen) const -{ - int32_t id = getAttributeValueStringID(idx); - //XML_NOISY(printf("getAttributeValue 0x%x=0x%x\n", idx, id)); - return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; -} - -int32_t ResXMLParser::getAttributeDataType(size_t idx) const -{ - if (mEventCode == START_TAG) { - const ResXMLTree_attrExt* tag = (const ResXMLTree_attrExt*)mCurExt; - if (idx < dtohs(tag->attributeCount)) { - const ResXMLTree_attribute* attr = (const ResXMLTree_attribute*) - (((const uint8_t*)tag) - + dtohs(tag->attributeStart) - + (dtohs(tag->attributeSize)*idx)); - return attr->typedValue.dataType; - } - } - return Res_value::TYPE_NULL; -} - -int32_t ResXMLParser::getAttributeData(size_t idx) const -{ - if (mEventCode == START_TAG) { - const ResXMLTree_attrExt* tag = (const ResXMLTree_attrExt*)mCurExt; - if (idx < dtohs(tag->attributeCount)) { - const ResXMLTree_attribute* attr = (const ResXMLTree_attribute*) - (((const uint8_t*)tag) - + dtohs(tag->attributeStart) - + (dtohs(tag->attributeSize)*idx)); - return dtohl(attr->typedValue.data); - } - } - return 0; -} - -ssize_t ResXMLParser::getAttributeValue(size_t idx, Res_value* outValue) const -{ - if (mEventCode == START_TAG) { - const ResXMLTree_attrExt* tag = (const ResXMLTree_attrExt*)mCurExt; - if (idx < dtohs(tag->attributeCount)) { - const ResXMLTree_attribute* attr = (const ResXMLTree_attribute*) - (((const uint8_t*)tag) - + dtohs(tag->attributeStart) - + (dtohs(tag->attributeSize)*idx)); - outValue->copyFrom_dtoh(attr->typedValue); - return sizeof(Res_value); - } - } - return BAD_TYPE; -} - -ssize_t ResXMLParser::indexOfAttribute(const char* ns, const char* attr) const -{ - String16 nsStr(ns != NULL ? ns : ""); - String16 attrStr(attr); - return indexOfAttribute(ns ? nsStr.string() : NULL, ns ? nsStr.size() : 0, - attrStr.string(), attrStr.size()); -} - -ssize_t ResXMLParser::indexOfAttribute(const char16_t* ns, size_t nsLen, - const char16_t* attr, size_t attrLen) const -{ - if (mEventCode == START_TAG) { - const size_t N = getAttributeCount(); - for (size_t i=0; i attr=%s, curAttr=%s\n", - // String8(attr).string(), String8(curAttr).string()); - if (attr && curAttr && (strzcmp16(attr, attrLen, curAttr, curAttrLen) == 0)) { - if (ns == NULL) { - if (curNs == NULL) return i; - } else if (curNs != NULL) { - //printf(" --> ns=%s, curNs=%s\n", - // String8(ns).string(), String8(curNs).string()); - if (strzcmp16(ns, nsLen, curNs, curNsLen) == 0) return i; - } - } - } - } - - return NAME_NOT_FOUND; -} - -ssize_t ResXMLParser::indexOfID() const -{ - if (mEventCode == START_TAG) { - const ssize_t idx = dtohs(((const ResXMLTree_attrExt*)mCurExt)->idIndex); - if (idx > 0) return (idx-1); - } - return NAME_NOT_FOUND; -} - -ssize_t ResXMLParser::indexOfClass() const -{ - if (mEventCode == START_TAG) { - const ssize_t idx = dtohs(((const ResXMLTree_attrExt*)mCurExt)->classIndex); - if (idx > 0) return (idx-1); - } - return NAME_NOT_FOUND; -} - -ssize_t ResXMLParser::indexOfStyle() const -{ - if (mEventCode == START_TAG) { - const ssize_t idx = dtohs(((const ResXMLTree_attrExt*)mCurExt)->styleIndex); - if (idx > 0) return (idx-1); - } - return NAME_NOT_FOUND; -} - -ResXMLParser::event_code_t ResXMLParser::nextNode() -{ - if (mEventCode < 0) { - return mEventCode; - } - - do { - const ResXMLTree_node* next = (const ResXMLTree_node*) - (((const uint8_t*)mCurNode) + dtohl(mCurNode->header.size)); - //ALOGW("Next node: prev=%p, next=%p\n", mCurNode, next); - - if (((const uint8_t*)next) >= mTree.mDataEnd) { - mCurNode = NULL; - return (mEventCode=END_DOCUMENT); - } - - if (mTree.validateNode(next) != NO_ERROR) { - mCurNode = NULL; - return (mEventCode=BAD_DOCUMENT); - } - - mCurNode = next; - const uint16_t headerSize = dtohs(next->header.headerSize); - const uint32_t totalSize = dtohl(next->header.size); - mCurExt = ((const uint8_t*)next) + headerSize; - size_t minExtSize = 0; - event_code_t eventCode = (event_code_t)dtohs(next->header.type); - switch ((mEventCode=eventCode)) { - case RES_XML_START_NAMESPACE_TYPE: - case RES_XML_END_NAMESPACE_TYPE: - minExtSize = sizeof(ResXMLTree_namespaceExt); - break; - case RES_XML_START_ELEMENT_TYPE: - minExtSize = sizeof(ResXMLTree_attrExt); - break; - case RES_XML_END_ELEMENT_TYPE: - minExtSize = sizeof(ResXMLTree_endElementExt); - break; - case RES_XML_CDATA_TYPE: - minExtSize = sizeof(ResXMLTree_cdataExt); - break; - default: - ALOGW("Unknown XML block: header type %d in node at %d\n", - (int)dtohs(next->header.type), - (int)(((const uint8_t*)next)-((const uint8_t*)mTree.mHeader))); - continue; - } - - if ((totalSize-headerSize) < minExtSize) { - ALOGW("Bad XML block: header type 0x%x in node at 0x%x has size %d, need %d\n", - (int)dtohs(next->header.type), - (int)(((const uint8_t*)next)-((const uint8_t*)mTree.mHeader)), - (int)(totalSize-headerSize), (int)minExtSize); - return (mEventCode=BAD_DOCUMENT); - } - - //printf("CurNode=%p, CurExt=%p, headerSize=%d, minExtSize=%d\n", - // mCurNode, mCurExt, headerSize, minExtSize); - - return eventCode; - } while (true); -} - -void ResXMLParser::getPosition(ResXMLParser::ResXMLPosition* pos) const -{ - pos->eventCode = mEventCode; - pos->curNode = mCurNode; - pos->curExt = mCurExt; -} - -void ResXMLParser::setPosition(const ResXMLParser::ResXMLPosition& pos) -{ - mEventCode = pos.eventCode; - mCurNode = pos.curNode; - mCurExt = pos.curExt; -} - - -// -------------------------------------------------------------------- - -static volatile int32_t gCount = 0; - -ResXMLTree::ResXMLTree() - : ResXMLParser(*this) - , mError(NO_INIT), mOwnedData(NULL) -{ - //ALOGI("Creating ResXMLTree %p #%d\n", this, android_atomic_inc(&gCount)+1); - restart(); -} - -ResXMLTree::ResXMLTree(const void* data, size_t size, bool copyData) - : ResXMLParser(*this) - , mError(NO_INIT), mOwnedData(NULL) -{ - //ALOGI("Creating ResXMLTree %p #%d\n", this, android_atomic_inc(&gCount)+1); - setTo(data, size, copyData); -} - -ResXMLTree::~ResXMLTree() -{ - //ALOGI("Destroying ResXMLTree in %p #%d\n", this, android_atomic_dec(&gCount)-1); - uninit(); -} - -status_t ResXMLTree::setTo(const void* data, size_t size, bool copyData) -{ - uninit(); - mEventCode = START_DOCUMENT; - - if (copyData) { - mOwnedData = malloc(size); - if (mOwnedData == NULL) { - return (mError=NO_MEMORY); - } - memcpy(mOwnedData, data, size); - data = mOwnedData; - } - - mHeader = (const ResXMLTree_header*)data; - mSize = dtohl(mHeader->header.size); - if (dtohs(mHeader->header.headerSize) > mSize || mSize > size) { - ALOGW("Bad XML block: header size %d or total size %d is larger than data size %d\n", - (int)dtohs(mHeader->header.headerSize), - (int)dtohl(mHeader->header.size), (int)size); - mError = BAD_TYPE; - restart(); - return mError; - } - mDataEnd = ((const uint8_t*)mHeader) + mSize; - - mStrings.uninit(); - mRootNode = NULL; - mResIds = NULL; - mNumResIds = 0; - - // First look for a couple interesting chunks: the string block - // and first XML node. - const ResChunk_header* chunk = - (const ResChunk_header*)(((const uint8_t*)mHeader) + dtohs(mHeader->header.headerSize)); - const ResChunk_header* lastChunk = chunk; - while (((const uint8_t*)chunk) < (mDataEnd-sizeof(ResChunk_header)) && - ((const uint8_t*)chunk) < (mDataEnd-dtohl(chunk->size))) { - status_t err = validate_chunk(chunk, sizeof(ResChunk_header), mDataEnd, "XML"); - if (err != NO_ERROR) { - mError = err; - goto done; - } - const uint16_t type = dtohs(chunk->type); - const size_t size = dtohl(chunk->size); - XML_NOISY(printf("Scanning @ %p: type=0x%x, size=0x%x\n", - (void*)(((uint32_t)chunk)-((uint32_t)mHeader)), type, size)); - if (type == RES_STRING_POOL_TYPE) { - mStrings.setTo(chunk, size); - } else if (type == RES_XML_RESOURCE_MAP_TYPE) { - mResIds = (const uint32_t*) - (((const uint8_t*)chunk)+dtohs(chunk->headerSize)); - mNumResIds = (dtohl(chunk->size)-dtohs(chunk->headerSize))/sizeof(uint32_t); - } else if (type >= RES_XML_FIRST_CHUNK_TYPE - && type <= RES_XML_LAST_CHUNK_TYPE) { - if (validateNode((const ResXMLTree_node*)chunk) != NO_ERROR) { - mError = BAD_TYPE; - goto done; - } - mCurNode = (const ResXMLTree_node*)lastChunk; - if (nextNode() == BAD_DOCUMENT) { - mError = BAD_TYPE; - goto done; - } - mRootNode = mCurNode; - mRootExt = mCurExt; - mRootCode = mEventCode; - break; - } else { - XML_NOISY(printf("Skipping unknown chunk!\n")); - } - lastChunk = chunk; - chunk = (const ResChunk_header*) - (((const uint8_t*)chunk) + size); - } - - if (mRootNode == NULL) { - ALOGW("Bad XML block: no root element node found\n"); - mError = BAD_TYPE; - goto done; - } - - mError = mStrings.getError(); - -done: - restart(); - return mError; -} - -status_t ResXMLTree::getError() const -{ - return mError; -} - -void ResXMLTree::uninit() -{ - mError = NO_INIT; - mStrings.uninit(); - if (mOwnedData) { - free(mOwnedData); - mOwnedData = NULL; - } - restart(); -} - -status_t ResXMLTree::validateNode(const ResXMLTree_node* node) const -{ - const uint16_t eventCode = dtohs(node->header.type); - - status_t err = validate_chunk( - &node->header, sizeof(ResXMLTree_node), - mDataEnd, "ResXMLTree_node"); - - if (err >= NO_ERROR) { - // Only perform additional validation on START nodes - if (eventCode != RES_XML_START_ELEMENT_TYPE) { - return NO_ERROR; - } - - const uint16_t headerSize = dtohs(node->header.headerSize); - const uint32_t size = dtohl(node->header.size); - const ResXMLTree_attrExt* attrExt = (const ResXMLTree_attrExt*) - (((const uint8_t*)node) + headerSize); - // check for sensical values pulled out of the stream so far... - if ((size >= headerSize + sizeof(ResXMLTree_attrExt)) - && ((void*)attrExt > (void*)node)) { - const size_t attrSize = ((size_t)dtohs(attrExt->attributeSize)) - * dtohs(attrExt->attributeCount); - if ((dtohs(attrExt->attributeStart)+attrSize) <= (size-headerSize)) { - return NO_ERROR; - } - ALOGW("Bad XML block: node attributes use 0x%x bytes, only have 0x%x bytes\n", - (unsigned int)(dtohs(attrExt->attributeStart)+attrSize), - (unsigned int)(size-headerSize)); - } - else { - ALOGW("Bad XML start block: node header size 0x%x, size 0x%x\n", - (unsigned int)headerSize, (unsigned int)size); - } - return BAD_TYPE; - } - - return err; - -#if 0 - const bool isStart = dtohs(node->header.type) == RES_XML_START_ELEMENT_TYPE; - - const uint16_t headerSize = dtohs(node->header.headerSize); - const uint32_t size = dtohl(node->header.size); - - if (headerSize >= (isStart ? sizeof(ResXMLTree_attrNode) : sizeof(ResXMLTree_node))) { - if (size >= headerSize) { - if (((const uint8_t*)node) <= (mDataEnd-size)) { - if (!isStart) { - return NO_ERROR; - } - if ((((size_t)dtohs(node->attributeSize))*dtohs(node->attributeCount)) - <= (size-headerSize)) { - return NO_ERROR; - } - ALOGW("Bad XML block: node attributes use 0x%x bytes, only have 0x%x bytes\n", - ((int)dtohs(node->attributeSize))*dtohs(node->attributeCount), - (int)(size-headerSize)); - return BAD_TYPE; - } - ALOGW("Bad XML block: node at 0x%x extends beyond data end 0x%x\n", - (int)(((const uint8_t*)node)-((const uint8_t*)mHeader)), (int)mSize); - return BAD_TYPE; - } - ALOGW("Bad XML block: node at 0x%x header size 0x%x smaller than total size 0x%x\n", - (int)(((const uint8_t*)node)-((const uint8_t*)mHeader)), - (int)headerSize, (int)size); - return BAD_TYPE; - } - ALOGW("Bad XML block: node at 0x%x header size 0x%x too small\n", - (int)(((const uint8_t*)node)-((const uint8_t*)mHeader)), - (int)headerSize); - return BAD_TYPE; -#endif -} - -// -------------------------------------------------------------------- -// -------------------------------------------------------------------- -// -------------------------------------------------------------------- - -void ResTable_config::copyFromDeviceNoSwap(const ResTable_config& o) { - const size_t size = dtohl(o.size); - if (size >= sizeof(ResTable_config)) { - *this = o; - } else { - memcpy(this, &o, size); - memset(((uint8_t*)this)+size, 0, sizeof(ResTable_config)-size); - } -} - -void ResTable_config::copyFromDtoH(const ResTable_config& o) { - copyFromDeviceNoSwap(o); - size = sizeof(ResTable_config); - mcc = dtohs(mcc); - mnc = dtohs(mnc); - density = dtohs(density); - screenWidth = dtohs(screenWidth); - screenHeight = dtohs(screenHeight); - sdkVersion = dtohs(sdkVersion); - minorVersion = dtohs(minorVersion); - smallestScreenWidthDp = dtohs(smallestScreenWidthDp); - screenWidthDp = dtohs(screenWidthDp); - screenHeightDp = dtohs(screenHeightDp); -} - -void ResTable_config::swapHtoD() { - size = htodl(size); - mcc = htods(mcc); - mnc = htods(mnc); - density = htods(density); - screenWidth = htods(screenWidth); - screenHeight = htods(screenHeight); - sdkVersion = htods(sdkVersion); - minorVersion = htods(minorVersion); - smallestScreenWidthDp = htods(smallestScreenWidthDp); - screenWidthDp = htods(screenWidthDp); - screenHeightDp = htods(screenHeightDp); -} - -int ResTable_config::compare(const ResTable_config& o) const { - int32_t diff = (int32_t)(imsi - o.imsi); - if (diff != 0) return diff; - diff = (int32_t)(locale - o.locale); - if (diff != 0) return diff; - diff = (int32_t)(screenType - o.screenType); - if (diff != 0) return diff; - diff = (int32_t)(input - o.input); - if (diff != 0) return diff; - diff = (int32_t)(screenSize - o.screenSize); - if (diff != 0) return diff; - diff = (int32_t)(version - o.version); - if (diff != 0) return diff; - diff = (int32_t)(screenLayout - o.screenLayout); - if (diff != 0) return diff; - diff = (int32_t)(uiMode - o.uiMode); - if (diff != 0) return diff; - diff = (int32_t)(smallestScreenWidthDp - o.smallestScreenWidthDp); - if (diff != 0) return diff; - diff = (int32_t)(screenSizeDp - o.screenSizeDp); - return (int)diff; -} - -int ResTable_config::compareLogical(const ResTable_config& o) const { - if (mcc != o.mcc) { - return mcc < o.mcc ? -1 : 1; - } - if (mnc != o.mnc) { - return mnc < o.mnc ? -1 : 1; - } - if (language[0] != o.language[0]) { - return language[0] < o.language[0] ? -1 : 1; - } - if (language[1] != o.language[1]) { - return language[1] < o.language[1] ? -1 : 1; - } - if (country[0] != o.country[0]) { - return country[0] < o.country[0] ? -1 : 1; - } - if (country[1] != o.country[1]) { - return country[1] < o.country[1] ? -1 : 1; - } - if (smallestScreenWidthDp != o.smallestScreenWidthDp) { - return smallestScreenWidthDp < o.smallestScreenWidthDp ? -1 : 1; - } - if (screenWidthDp != o.screenWidthDp) { - return screenWidthDp < o.screenWidthDp ? -1 : 1; - } - if (screenHeightDp != o.screenHeightDp) { - return screenHeightDp < o.screenHeightDp ? -1 : 1; - } - if (screenWidth != o.screenWidth) { - return screenWidth < o.screenWidth ? -1 : 1; - } - if (screenHeight != o.screenHeight) { - return screenHeight < o.screenHeight ? -1 : 1; - } - if (density != o.density) { - return density < o.density ? -1 : 1; - } - if (orientation != o.orientation) { - return orientation < o.orientation ? -1 : 1; - } - if (touchscreen != o.touchscreen) { - return touchscreen < o.touchscreen ? -1 : 1; - } - if (input != o.input) { - return input < o.input ? -1 : 1; - } - if (screenLayout != o.screenLayout) { - return screenLayout < o.screenLayout ? -1 : 1; - } - if (uiMode != o.uiMode) { - return uiMode < o.uiMode ? -1 : 1; - } - if (version != o.version) { - return version < o.version ? -1 : 1; - } - return 0; -} - -int ResTable_config::diff(const ResTable_config& o) const { - int diffs = 0; - if (mcc != o.mcc) diffs |= CONFIG_MCC; - if (mnc != o.mnc) diffs |= CONFIG_MNC; - if (locale != o.locale) diffs |= CONFIG_LOCALE; - if (orientation != o.orientation) diffs |= CONFIG_ORIENTATION; - if (density != o.density) diffs |= CONFIG_DENSITY; - if (touchscreen != o.touchscreen) diffs |= CONFIG_TOUCHSCREEN; - if (((inputFlags^o.inputFlags)&(MASK_KEYSHIDDEN|MASK_NAVHIDDEN)) != 0) - diffs |= CONFIG_KEYBOARD_HIDDEN; - if (keyboard != o.keyboard) diffs |= CONFIG_KEYBOARD; - if (navigation != o.navigation) diffs |= CONFIG_NAVIGATION; - if (screenSize != o.screenSize) diffs |= CONFIG_SCREEN_SIZE; - if (version != o.version) diffs |= CONFIG_VERSION; - if (screenLayout != o.screenLayout) diffs |= CONFIG_SCREEN_LAYOUT; - if (uiMode != o.uiMode) diffs |= CONFIG_UI_MODE; - if (smallestScreenWidthDp != o.smallestScreenWidthDp) diffs |= CONFIG_SMALLEST_SCREEN_SIZE; - if (screenSizeDp != o.screenSizeDp) diffs |= CONFIG_SCREEN_SIZE; - return diffs; -} - -bool ResTable_config::isMoreSpecificThan(const ResTable_config& o) const { - // The order of the following tests defines the importance of one - // configuration parameter over another. Those tests first are more - // important, trumping any values in those following them. - if (imsi || o.imsi) { - if (mcc != o.mcc) { - if (!mcc) return false; - if (!o.mcc) return true; - } - - if (mnc != o.mnc) { - if (!mnc) return false; - if (!o.mnc) return true; - } - } - - if (locale || o.locale) { - if (language[0] != o.language[0]) { - if (!language[0]) return false; - if (!o.language[0]) return true; - } - - if (country[0] != o.country[0]) { - if (!country[0]) return false; - if (!o.country[0]) return true; - } - } - - if (smallestScreenWidthDp || o.smallestScreenWidthDp) { - if (smallestScreenWidthDp != o.smallestScreenWidthDp) { - if (!smallestScreenWidthDp) return false; - if (!o.smallestScreenWidthDp) return true; - } - } - - if (screenSizeDp || o.screenSizeDp) { - if (screenWidthDp != o.screenWidthDp) { - if (!screenWidthDp) return false; - if (!o.screenWidthDp) return true; - } - - if (screenHeightDp != o.screenHeightDp) { - if (!screenHeightDp) return false; - if (!o.screenHeightDp) return true; - } - } - - if (screenLayout || o.screenLayout) { - if (((screenLayout^o.screenLayout) & MASK_SCREENSIZE) != 0) { - if (!(screenLayout & MASK_SCREENSIZE)) return false; - if (!(o.screenLayout & MASK_SCREENSIZE)) return true; - } - if (((screenLayout^o.screenLayout) & MASK_SCREENLONG) != 0) { - if (!(screenLayout & MASK_SCREENLONG)) return false; - if (!(o.screenLayout & MASK_SCREENLONG)) return true; - } - } - - if (orientation != o.orientation) { - if (!orientation) return false; - if (!o.orientation) return true; - } - - if (uiMode || o.uiMode) { - if (((uiMode^o.uiMode) & MASK_UI_MODE_TYPE) != 0) { - if (!(uiMode & MASK_UI_MODE_TYPE)) return false; - if (!(o.uiMode & MASK_UI_MODE_TYPE)) return true; - } - if (((uiMode^o.uiMode) & MASK_UI_MODE_NIGHT) != 0) { - if (!(uiMode & MASK_UI_MODE_NIGHT)) return false; - if (!(o.uiMode & MASK_UI_MODE_NIGHT)) return true; - } - } - - // density is never 'more specific' - // as the default just equals 160 - - if (touchscreen != o.touchscreen) { - if (!touchscreen) return false; - if (!o.touchscreen) return true; - } - - if (input || o.input) { - if (((inputFlags^o.inputFlags) & MASK_KEYSHIDDEN) != 0) { - if (!(inputFlags & MASK_KEYSHIDDEN)) return false; - if (!(o.inputFlags & MASK_KEYSHIDDEN)) return true; - } - - if (((inputFlags^o.inputFlags) & MASK_NAVHIDDEN) != 0) { - if (!(inputFlags & MASK_NAVHIDDEN)) return false; - if (!(o.inputFlags & MASK_NAVHIDDEN)) return true; - } - - if (keyboard != o.keyboard) { - if (!keyboard) return false; - if (!o.keyboard) return true; - } - - if (navigation != o.navigation) { - if (!navigation) return false; - if (!o.navigation) return true; - } - } - - if (screenSize || o.screenSize) { - if (screenWidth != o.screenWidth) { - if (!screenWidth) return false; - if (!o.screenWidth) return true; - } - - if (screenHeight != o.screenHeight) { - if (!screenHeight) return false; - if (!o.screenHeight) return true; - } - } - - if (version || o.version) { - if (sdkVersion != o.sdkVersion) { - if (!sdkVersion) return false; - if (!o.sdkVersion) return true; - } - - if (minorVersion != o.minorVersion) { - if (!minorVersion) return false; - if (!o.minorVersion) return true; - } - } - return false; -} - -bool ResTable_config::isBetterThan(const ResTable_config& o, - const ResTable_config* requested) const { - if (requested) { - if (imsi || o.imsi) { - if ((mcc != o.mcc) && requested->mcc) { - return (mcc); - } - - if ((mnc != o.mnc) && requested->mnc) { - return (mnc); - } - } - - if (locale || o.locale) { - if ((language[0] != o.language[0]) && requested->language[0]) { - return (language[0]); - } - - if ((country[0] != o.country[0]) && requested->country[0]) { - return (country[0]); - } - } - - if (smallestScreenWidthDp || o.smallestScreenWidthDp) { - // The configuration closest to the actual size is best. - // We assume that larger configs have already been filtered - // out at this point. That means we just want the largest one. - return smallestScreenWidthDp >= o.smallestScreenWidthDp; - } - - if (screenSizeDp || o.screenSizeDp) { - // "Better" is based on the sum of the difference between both - // width and height from the requested dimensions. We are - // assuming the invalid configs (with smaller dimens) have - // already been filtered. Note that if a particular dimension - // is unspecified, we will end up with a large value (the - // difference between 0 and the requested dimension), which is - // good since we will prefer a config that has specified a - // dimension value. - int myDelta = 0, otherDelta = 0; - if (requested->screenWidthDp) { - myDelta += requested->screenWidthDp - screenWidthDp; - otherDelta += requested->screenWidthDp - o.screenWidthDp; - } - if (requested->screenHeightDp) { - myDelta += requested->screenHeightDp - screenHeightDp; - otherDelta += requested->screenHeightDp - o.screenHeightDp; - } - //ALOGI("Comparing this %dx%d to other %dx%d in %dx%d: myDelta=%d otherDelta=%d", - // screenWidthDp, screenHeightDp, o.screenWidthDp, o.screenHeightDp, - // requested->screenWidthDp, requested->screenHeightDp, myDelta, otherDelta); - return (myDelta <= otherDelta); - } - - if (screenLayout || o.screenLayout) { - if (((screenLayout^o.screenLayout) & MASK_SCREENSIZE) != 0 - && (requested->screenLayout & MASK_SCREENSIZE)) { - // A little backwards compatibility here: undefined is - // considered equivalent to normal. But only if the - // requested size is at least normal; otherwise, small - // is better than the default. - int mySL = (screenLayout & MASK_SCREENSIZE); - int oSL = (o.screenLayout & MASK_SCREENSIZE); - int fixedMySL = mySL; - int fixedOSL = oSL; - if ((requested->screenLayout & MASK_SCREENSIZE) >= SCREENSIZE_NORMAL) { - if (fixedMySL == 0) fixedMySL = SCREENSIZE_NORMAL; - if (fixedOSL == 0) fixedOSL = SCREENSIZE_NORMAL; - } - // For screen size, the best match is the one that is - // closest to the requested screen size, but not over - // (the not over part is dealt with in match() below). - if (fixedMySL == fixedOSL) { - // If the two are the same, but 'this' is actually - // undefined, then the other is really a better match. - if (mySL == 0) return false; - return true; - } - return fixedMySL >= fixedOSL; - } - if (((screenLayout^o.screenLayout) & MASK_SCREENLONG) != 0 - && (requested->screenLayout & MASK_SCREENLONG)) { - return (screenLayout & MASK_SCREENLONG); - } - } - - if ((orientation != o.orientation) && requested->orientation) { - return (orientation); - } - - if (uiMode || o.uiMode) { - if (((uiMode^o.uiMode) & MASK_UI_MODE_TYPE) != 0 - && (requested->uiMode & MASK_UI_MODE_TYPE)) { - return (uiMode & MASK_UI_MODE_TYPE); - } - if (((uiMode^o.uiMode) & MASK_UI_MODE_NIGHT) != 0 - && (requested->uiMode & MASK_UI_MODE_NIGHT)) { - return (uiMode & MASK_UI_MODE_NIGHT); - } - } - - if (screenType || o.screenType) { - if (density != o.density) { - // density is tough. Any density is potentially useful - // because the system will scale it. Scaling down - // is generally better than scaling up. - // Default density counts as 160dpi (the system default) - // TODO - remove 160 constants - int h = (density?density:160); - int l = (o.density?o.density:160); - bool bImBigger = true; - if (l > h) { - int t = h; - h = l; - l = t; - bImBigger = false; - } - - int reqValue = (requested->density?requested->density:160); - if (reqValue >= h) { - // requested value higher than both l and h, give h - return bImBigger; - } - if (l >= reqValue) { - // requested value lower than both l and h, give l - return !bImBigger; - } - // saying that scaling down is 2x better than up - if (((2 * l) - reqValue) * h > reqValue * reqValue) { - return !bImBigger; - } else { - return bImBigger; - } - } - - if ((touchscreen != o.touchscreen) && requested->touchscreen) { - return (touchscreen); - } - } - - if (input || o.input) { - const int keysHidden = inputFlags & MASK_KEYSHIDDEN; - const int oKeysHidden = o.inputFlags & MASK_KEYSHIDDEN; - if (keysHidden != oKeysHidden) { - const int reqKeysHidden = - requested->inputFlags & MASK_KEYSHIDDEN; - if (reqKeysHidden) { - - if (!keysHidden) return false; - if (!oKeysHidden) return true; - // For compatibility, we count KEYSHIDDEN_NO as being - // the same as KEYSHIDDEN_SOFT. Here we disambiguate - // these by making an exact match more specific. - if (reqKeysHidden == keysHidden) return true; - if (reqKeysHidden == oKeysHidden) return false; - } - } - - const int navHidden = inputFlags & MASK_NAVHIDDEN; - const int oNavHidden = o.inputFlags & MASK_NAVHIDDEN; - if (navHidden != oNavHidden) { - const int reqNavHidden = - requested->inputFlags & MASK_NAVHIDDEN; - if (reqNavHidden) { - - if (!navHidden) return false; - if (!oNavHidden) return true; - } - } - - if ((keyboard != o.keyboard) && requested->keyboard) { - return (keyboard); - } - - if ((navigation != o.navigation) && requested->navigation) { - return (navigation); - } - } - - if (screenSize || o.screenSize) { - // "Better" is based on the sum of the difference between both - // width and height from the requested dimensions. We are - // assuming the invalid configs (with smaller sizes) have - // already been filtered. Note that if a particular dimension - // is unspecified, we will end up with a large value (the - // difference between 0 and the requested dimension), which is - // good since we will prefer a config that has specified a - // size value. - int myDelta = 0, otherDelta = 0; - if (requested->screenWidth) { - myDelta += requested->screenWidth - screenWidth; - otherDelta += requested->screenWidth - o.screenWidth; - } - if (requested->screenHeight) { - myDelta += requested->screenHeight - screenHeight; - otherDelta += requested->screenHeight - o.screenHeight; - } - return (myDelta <= otherDelta); - } - - if (version || o.version) { - if ((sdkVersion != o.sdkVersion) && requested->sdkVersion) { - return (sdkVersion > o.sdkVersion); - } - - if ((minorVersion != o.minorVersion) && - requested->minorVersion) { - return (minorVersion); - } - } - - return false; - } - return isMoreSpecificThan(o); -} - -bool ResTable_config::match(const ResTable_config& settings) const { - if (imsi != 0) { - if (mcc != 0 && mcc != settings.mcc) { - return false; - } - if (mnc != 0 && mnc != settings.mnc) { - return false; - } - } - if (locale != 0) { - if (language[0] != 0 - && (language[0] != settings.language[0] - || language[1] != settings.language[1])) { - return false; - } - if (country[0] != 0 - && (country[0] != settings.country[0] - || country[1] != settings.country[1])) { - return false; - } - } - if (screenConfig != 0) { - const int screenSize = screenLayout&MASK_SCREENSIZE; - const int setScreenSize = settings.screenLayout&MASK_SCREENSIZE; - // Any screen sizes for larger screens than the setting do not - // match. - if (screenSize != 0 && screenSize > setScreenSize) { - return false; - } - - const int screenLong = screenLayout&MASK_SCREENLONG; - const int setScreenLong = settings.screenLayout&MASK_SCREENLONG; - if (screenLong != 0 && screenLong != setScreenLong) { - return false; - } - - const int uiModeType = uiMode&MASK_UI_MODE_TYPE; - const int setUiModeType = settings.uiMode&MASK_UI_MODE_TYPE; - if (uiModeType != 0 && uiModeType != setUiModeType) { - return false; - } - - const int uiModeNight = uiMode&MASK_UI_MODE_NIGHT; - const int setUiModeNight = settings.uiMode&MASK_UI_MODE_NIGHT; - if (uiModeNight != 0 && uiModeNight != setUiModeNight) { - return false; - } - - if (smallestScreenWidthDp != 0 - && smallestScreenWidthDp > settings.smallestScreenWidthDp) { - return false; - } - } - if (screenSizeDp != 0) { - if (screenWidthDp != 0 && screenWidthDp > settings.screenWidthDp) { - //ALOGI("Filtering out width %d in requested %d", screenWidthDp, settings.screenWidthDp); - return false; - } - if (screenHeightDp != 0 && screenHeightDp > settings.screenHeightDp) { - //ALOGI("Filtering out height %d in requested %d", screenHeightDp, settings.screenHeightDp); - return false; - } - } - if (screenType != 0) { - if (orientation != 0 && orientation != settings.orientation) { - return false; - } - // density always matches - we can scale it. See isBetterThan - if (touchscreen != 0 && touchscreen != settings.touchscreen) { - return false; - } - } - if (input != 0) { - const int keysHidden = inputFlags&MASK_KEYSHIDDEN; - const int setKeysHidden = settings.inputFlags&MASK_KEYSHIDDEN; - if (keysHidden != 0 && keysHidden != setKeysHidden) { - // For compatibility, we count a request for KEYSHIDDEN_NO as also - // matching the more recent KEYSHIDDEN_SOFT. Basically - // KEYSHIDDEN_NO means there is some kind of keyboard available. - //ALOGI("Matching keysHidden: have=%d, config=%d\n", keysHidden, setKeysHidden); - if (keysHidden != KEYSHIDDEN_NO || setKeysHidden != KEYSHIDDEN_SOFT) { - //ALOGI("No match!"); - return false; - } - } - const int navHidden = inputFlags&MASK_NAVHIDDEN; - const int setNavHidden = settings.inputFlags&MASK_NAVHIDDEN; - if (navHidden != 0 && navHidden != setNavHidden) { - return false; - } - if (keyboard != 0 && keyboard != settings.keyboard) { - return false; - } - if (navigation != 0 && navigation != settings.navigation) { - return false; - } - } - if (screenSize != 0) { - if (screenWidth != 0 && screenWidth > settings.screenWidth) { - return false; - } - if (screenHeight != 0 && screenHeight > settings.screenHeight) { - return false; - } - } - if (version != 0) { - if (sdkVersion != 0 && sdkVersion > settings.sdkVersion) { - return false; - } - if (minorVersion != 0 && minorVersion != settings.minorVersion) { - return false; - } - } - return true; -} - -void ResTable_config::getLocale(char str[6]) const { - memset(str, 0, 6); - if (language[0]) { - str[0] = language[0]; - str[1] = language[1]; - if (country[0]) { - str[2] = '_'; - str[3] = country[0]; - str[4] = country[1]; - } - } -} - -String8 ResTable_config::toString() const { - String8 res; - - if (mcc != 0) { - if (res.size() > 0) res.append("-"); - res.appendFormat("%dmcc", dtohs(mcc)); - } - if (mnc != 0) { - if (res.size() > 0) res.append("-"); - res.appendFormat("%dmnc", dtohs(mnc)); - } - if (language[0] != 0) { - if (res.size() > 0) res.append("-"); - res.append(language, 2); - } - if (country[0] != 0) { - if (res.size() > 0) res.append("-"); - res.append(country, 2); - } - if (smallestScreenWidthDp != 0) { - if (res.size() > 0) res.append("-"); - res.appendFormat("sw%ddp", dtohs(smallestScreenWidthDp)); - } - if (screenWidthDp != 0) { - if (res.size() > 0) res.append("-"); - res.appendFormat("w%ddp", dtohs(screenWidthDp)); - } - if (screenHeightDp != 0) { - if (res.size() > 0) res.append("-"); - res.appendFormat("h%ddp", dtohs(screenHeightDp)); - } - if ((screenLayout&MASK_SCREENSIZE) != SCREENSIZE_ANY) { - if (res.size() > 0) res.append("-"); - switch (screenLayout&ResTable_config::MASK_SCREENSIZE) { - case ResTable_config::SCREENSIZE_SMALL: - res.append("small"); - break; - case ResTable_config::SCREENSIZE_NORMAL: - res.append("normal"); - break; - case ResTable_config::SCREENSIZE_LARGE: - res.append("large"); - break; - case ResTable_config::SCREENSIZE_XLARGE: - res.append("xlarge"); - break; - default: - res.appendFormat("screenLayoutSize=%d", - dtohs(screenLayout&ResTable_config::MASK_SCREENSIZE)); - break; - } - } - if ((screenLayout&MASK_SCREENLONG) != 0) { - if (res.size() > 0) res.append("-"); - switch (screenLayout&ResTable_config::MASK_SCREENLONG) { - case ResTable_config::SCREENLONG_NO: - res.append("notlong"); - break; - case ResTable_config::SCREENLONG_YES: - res.append("long"); - break; - default: - res.appendFormat("screenLayoutLong=%d", - dtohs(screenLayout&ResTable_config::MASK_SCREENLONG)); - break; - } - } - if (orientation != ORIENTATION_ANY) { - if (res.size() > 0) res.append("-"); - switch (orientation) { - case ResTable_config::ORIENTATION_PORT: - res.append("port"); - break; - case ResTable_config::ORIENTATION_LAND: - res.append("land"); - break; - case ResTable_config::ORIENTATION_SQUARE: - res.append("square"); - break; - default: - res.appendFormat("orientation=%d", dtohs(orientation)); - break; - } - } - if ((uiMode&MASK_UI_MODE_TYPE) != UI_MODE_TYPE_ANY) { - if (res.size() > 0) res.append("-"); - switch (uiMode&ResTable_config::MASK_UI_MODE_TYPE) { - case ResTable_config::UI_MODE_TYPE_DESK: - res.append("desk"); - break; - case ResTable_config::UI_MODE_TYPE_CAR: - res.append("car"); - break; - case ResTable_config::UI_MODE_TYPE_TELEVISION: - res.append("television"); - break; - case ResTable_config::UI_MODE_TYPE_APPLIANCE: - res.append("appliance"); - break; - default: - res.appendFormat("uiModeType=%d", - dtohs(screenLayout&ResTable_config::MASK_UI_MODE_TYPE)); - break; - } - } - if ((uiMode&MASK_UI_MODE_NIGHT) != 0) { - if (res.size() > 0) res.append("-"); - switch (uiMode&ResTable_config::MASK_UI_MODE_NIGHT) { - case ResTable_config::UI_MODE_NIGHT_NO: - res.append("notnight"); - break; - case ResTable_config::UI_MODE_NIGHT_YES: - res.append("night"); - break; - default: - res.appendFormat("uiModeNight=%d", - dtohs(uiMode&MASK_UI_MODE_NIGHT)); - break; - } - } - if (density != DENSITY_DEFAULT) { - if (res.size() > 0) res.append("-"); - switch (density) { - case ResTable_config::DENSITY_LOW: - res.append("ldpi"); - break; - case ResTable_config::DENSITY_MEDIUM: - res.append("mdpi"); - break; - case ResTable_config::DENSITY_TV: - res.append("tvdpi"); - break; - case ResTable_config::DENSITY_HIGH: - res.append("hdpi"); - break; - case ResTable_config::DENSITY_XHIGH: - res.append("xhdpi"); - break; - case ResTable_config::DENSITY_XXHIGH: - res.append("xxhdpi"); - break; - case ResTable_config::DENSITY_NONE: - res.append("nodpi"); - break; - default: - res.appendFormat("density=%d", dtohs(density)); - break; - } - } - if (touchscreen != TOUCHSCREEN_ANY) { - if (res.size() > 0) res.append("-"); - switch (touchscreen) { - case ResTable_config::TOUCHSCREEN_NOTOUCH: - res.append("notouch"); - break; - case ResTable_config::TOUCHSCREEN_FINGER: - res.append("finger"); - break; - case ResTable_config::TOUCHSCREEN_STYLUS: - res.append("stylus"); - break; - default: - res.appendFormat("touchscreen=%d", dtohs(touchscreen)); - break; - } - } - if (keyboard != KEYBOARD_ANY) { - if (res.size() > 0) res.append("-"); - switch (keyboard) { - case ResTable_config::KEYBOARD_NOKEYS: - res.append("nokeys"); - break; - case ResTable_config::KEYBOARD_QWERTY: - res.append("qwerty"); - break; - case ResTable_config::KEYBOARD_12KEY: - res.append("12key"); - break; - default: - res.appendFormat("keyboard=%d", dtohs(keyboard)); - break; - } - } - if ((inputFlags&MASK_KEYSHIDDEN) != 0) { - if (res.size() > 0) res.append("-"); - switch (inputFlags&MASK_KEYSHIDDEN) { - case ResTable_config::KEYSHIDDEN_NO: - res.append("keysexposed"); - break; - case ResTable_config::KEYSHIDDEN_YES: - res.append("keyshidden"); - break; - case ResTable_config::KEYSHIDDEN_SOFT: - res.append("keyssoft"); - break; - } - } - if (navigation != NAVIGATION_ANY) { - if (res.size() > 0) res.append("-"); - switch (navigation) { - case ResTable_config::NAVIGATION_NONAV: - res.append("nonav"); - break; - case ResTable_config::NAVIGATION_DPAD: - res.append("dpad"); - break; - case ResTable_config::NAVIGATION_TRACKBALL: - res.append("trackball"); - break; - case ResTable_config::NAVIGATION_WHEEL: - res.append("wheel"); - break; - default: - res.appendFormat("navigation=%d", dtohs(navigation)); - break; - } - } - if ((inputFlags&MASK_NAVHIDDEN) != 0) { - if (res.size() > 0) res.append("-"); - switch (inputFlags&MASK_NAVHIDDEN) { - case ResTable_config::NAVHIDDEN_NO: - res.append("navsexposed"); - break; - case ResTable_config::NAVHIDDEN_YES: - res.append("navhidden"); - break; - default: - res.appendFormat("inputFlagsNavHidden=%d", - dtohs(inputFlags&MASK_NAVHIDDEN)); - break; - } - } - if (screenSize != 0) { - if (res.size() > 0) res.append("-"); - res.appendFormat("%dx%d", dtohs(screenWidth), dtohs(screenHeight)); - } - if (version != 0) { - if (res.size() > 0) res.append("-"); - res.appendFormat("v%d", dtohs(sdkVersion)); - if (minorVersion != 0) { - res.appendFormat(".%d", dtohs(minorVersion)); - } - } - - return res; -} - -// -------------------------------------------------------------------- -// -------------------------------------------------------------------- -// -------------------------------------------------------------------- - -struct ResTable::Header -{ - Header(ResTable* _owner) : owner(_owner), ownedData(NULL), header(NULL), - resourceIDMap(NULL), resourceIDMapSize(0) { } - - ~Header() - { - free(resourceIDMap); - } - - ResTable* const owner; - void* ownedData; - const ResTable_header* header; - size_t size; - const uint8_t* dataEnd; - size_t index; - void* cookie; - - ResStringPool values; - uint32_t* resourceIDMap; - size_t resourceIDMapSize; -}; - -struct ResTable::Type -{ - Type(const Header* _header, const Package* _package, size_t count) - : header(_header), package(_package), entryCount(count), - typeSpec(NULL), typeSpecFlags(NULL) { } - const Header* const header; - const Package* const package; - const size_t entryCount; - const ResTable_typeSpec* typeSpec; - const uint32_t* typeSpecFlags; - Vector configs; -}; - -struct ResTable::Package -{ - Package(ResTable* _owner, const Header* _header, const ResTable_package* _package) - : owner(_owner), header(_header), package(_package) { } - ~Package() - { - size_t i = types.size(); - while (i > 0) { - i--; - delete types[i]; - } - } - - ResTable* const owner; - const Header* const header; - const ResTable_package* const package; - Vector types; - - ResStringPool typeStrings; - ResStringPool keyStrings; - - const Type* getType(size_t idx) const { - return idx < types.size() ? types[idx] : NULL; - } -}; - -// A group of objects describing a particular resource package. -// The first in 'package' is always the root object (from the resource -// table that defined the package); the ones after are skins on top of it. -struct ResTable::PackageGroup -{ - PackageGroup(ResTable* _owner, const String16& _name, uint32_t _id) - : owner(_owner), name(_name), id(_id), typeCount(0), bags(NULL) { } - ~PackageGroup() { - clearBagCache(); - const size_t N = packages.size(); - for (size_t i=0; iowner == owner) { - delete pkg; - } - } - } - - void clearBagCache() { - if (bags) { - TABLE_NOISY(printf("bags=%p\n", bags)); - Package* pkg = packages[0]; - TABLE_NOISY(printf("typeCount=%x\n", typeCount)); - for (size_t i=0; igetType(i); - if (type != NULL) { - bag_set** typeBags = bags[i]; - TABLE_NOISY(printf("typeBags=%p\n", typeBags)); - if (typeBags) { - TABLE_NOISY(printf("type->entryCount=%x\n", type->entryCount)); - const size_t N = type->entryCount; - for (size_t j=0; j packages; - - // This is for finding typeStrings and other common package stuff. - Package* basePackage; - - // For quick access. - size_t typeCount; - - // Computed attribute bags, first indexed by the type and second - // by the entry in that type. - bag_set*** bags; -}; - -struct ResTable::bag_set -{ - size_t numAttrs; // number in array - size_t availAttrs; // total space in array - uint32_t typeSpecFlags; - // Followed by 'numAttr' bag_entry structures. -}; - -ResTable::Theme::Theme(const ResTable& table) - : mTable(table) -{ - memset(mPackages, 0, sizeof(mPackages)); -} - -ResTable::Theme::~Theme() -{ - for (size_t i=0; inumTypes; j++) { - theme_entry* te = pi->types[j].entries; - if (te != NULL) { - free(te); - } - } - free(pi); -} - -ResTable::Theme::package_info* ResTable::Theme::copy_package(package_info* pi) -{ - package_info* newpi = (package_info*)malloc( - sizeof(package_info) + (pi->numTypes*sizeof(type_info))); - newpi->numTypes = pi->numTypes; - for (size_t j=0; jnumTypes; j++) { - size_t cnt = pi->types[j].numEntries; - newpi->types[j].numEntries = cnt; - theme_entry* te = pi->types[j].entries; - if (te != NULL) { - theme_entry* newte = (theme_entry*)malloc(cnt*sizeof(theme_entry)); - newpi->types[j].entries = newte; - memcpy(newte, te, cnt*sizeof(theme_entry)); - } else { - newpi->types[j].entries = NULL; - } - } - return newpi; -} - -status_t ResTable::Theme::applyStyle(uint32_t resID, bool force) -{ - const bag_entry* bag; - uint32_t bagTypeSpecFlags = 0; - mTable.lock(); - const ssize_t N = mTable.getBagLocked(resID, &bag, &bagTypeSpecFlags); - TABLE_NOISY(LOGV("Applying style 0x%08x to theme %p, count=%d", resID, this, N)); - if (N < 0) { - mTable.unlock(); - return N; - } - - uint32_t curPackage = 0xffffffff; - ssize_t curPackageIndex = 0; - package_info* curPI = NULL; - uint32_t curType = 0xffffffff; - size_t numEntries = 0; - theme_entry* curEntries = NULL; - - const bag_entry* end = bag + N; - while (bag < end) { - const uint32_t attrRes = bag->map.name.ident; - const uint32_t p = Res_GETPACKAGE(attrRes); - const uint32_t t = Res_GETTYPE(attrRes); - const uint32_t e = Res_GETENTRY(attrRes); - - if (curPackage != p) { - const ssize_t pidx = mTable.getResourcePackageIndex(attrRes); - if (pidx < 0) { - ALOGE("Style contains key with bad package: 0x%08x\n", attrRes); - bag++; - continue; - } - curPackage = p; - curPackageIndex = pidx; - curPI = mPackages[pidx]; - if (curPI == NULL) { - PackageGroup* const grp = mTable.mPackageGroups[pidx]; - int cnt = grp->typeCount; - curPI = (package_info*)malloc( - sizeof(package_info) + (cnt*sizeof(type_info))); - curPI->numTypes = cnt; - memset(curPI->types, 0, cnt*sizeof(type_info)); - mPackages[pidx] = curPI; - } - curType = 0xffffffff; - } - if (curType != t) { - if (t >= curPI->numTypes) { - ALOGE("Style contains key with bad type: 0x%08x\n", attrRes); - bag++; - continue; - } - curType = t; - curEntries = curPI->types[t].entries; - if (curEntries == NULL) { - PackageGroup* const grp = mTable.mPackageGroups[curPackageIndex]; - const Type* type = grp->packages[0]->getType(t); - int cnt = type != NULL ? type->entryCount : 0; - curEntries = (theme_entry*)malloc(cnt*sizeof(theme_entry)); - memset(curEntries, Res_value::TYPE_NULL, cnt*sizeof(theme_entry)); - curPI->types[t].numEntries = cnt; - curPI->types[t].entries = curEntries; - } - numEntries = curPI->types[t].numEntries; - } - if (e >= numEntries) { - ALOGE("Style contains key with bad entry: 0x%08x\n", attrRes); - bag++; - continue; - } - theme_entry* curEntry = curEntries + e; - TABLE_NOISY(LOGV("Attr 0x%08x: type=0x%x, data=0x%08x; curType=0x%x", - attrRes, bag->map.value.dataType, bag->map.value.data, - curEntry->value.dataType)); - if (force || curEntry->value.dataType == Res_value::TYPE_NULL) { - curEntry->stringBlock = bag->stringBlock; - curEntry->typeSpecFlags |= bagTypeSpecFlags; - curEntry->value = bag->map.value; - } - - bag++; - } - - mTable.unlock(); - - //ALOGI("Applying style 0x%08x (force=%d) theme %p...\n", resID, force, this); - //dumpToLog(); - - return NO_ERROR; -} - -status_t ResTable::Theme::setTo(const Theme& other) -{ - //ALOGI("Setting theme %p from theme %p...\n", this, &other); - //dumpToLog(); - //other.dumpToLog(); - - if (&mTable == &other.mTable) { - for (size_t i=0; i= 0) { - const package_info* const pi = mPackages[p]; - TABLE_THEME(LOGI("Found package: %p", pi)); - if (pi != NULL) { - TABLE_THEME(LOGI("Desired type index is %ld in avail %d", t, pi->numTypes)); - if (t < pi->numTypes) { - const type_info& ti = pi->types[t]; - TABLE_THEME(LOGI("Desired entry index is %ld in avail %d", e, ti.numEntries)); - if (e < ti.numEntries) { - const theme_entry& te = ti.entries[e]; - if (outTypeSpecFlags != NULL) { - *outTypeSpecFlags |= te.typeSpecFlags; - } - TABLE_THEME(LOGI("Theme value: type=0x%x, data=0x%08x", - te.value.dataType, te.value.data)); - const uint8_t type = te.value.dataType; - if (type == Res_value::TYPE_ATTRIBUTE) { - if (cnt > 0) { - cnt--; - resID = te.value.data; - continue; - } - ALOGW("Too many attribute references, stopped at: 0x%08x\n", resID); - return BAD_INDEX; - } else if (type != Res_value::TYPE_NULL) { - *outValue = te.value; - return te.stringBlock; - } - return BAD_INDEX; - } - } - } - } - break; - - } while (true); - - return BAD_INDEX; -} - -ssize_t ResTable::Theme::resolveAttributeReference(Res_value* inOutValue, - ssize_t blockIndex, uint32_t* outLastRef, - uint32_t* inoutTypeSpecFlags, ResTable_config* inoutConfig) const -{ - //printf("Resolving type=0x%x\n", inOutValue->dataType); - if (inOutValue->dataType == Res_value::TYPE_ATTRIBUTE) { - uint32_t newTypeSpecFlags; - blockIndex = getAttribute(inOutValue->data, inOutValue, &newTypeSpecFlags); - TABLE_THEME(LOGI("Resolving attr reference: blockIndex=%d, type=0x%x, data=%p\n", - (int)blockIndex, (int)inOutValue->dataType, (void*)inOutValue->data)); - if (inoutTypeSpecFlags != NULL) *inoutTypeSpecFlags |= newTypeSpecFlags; - //printf("Retrieved attribute new type=0x%x\n", inOutValue->dataType); - if (blockIndex < 0) { - return blockIndex; - } - } - return mTable.resolveReference(inOutValue, blockIndex, outLastRef, - inoutTypeSpecFlags, inoutConfig); -} - -void ResTable::Theme::dumpToLog() const -{ - ALOGI("Theme %p:\n", this); - for (size_t i=0; inumTypes; j++) { - type_info& ti = pi->types[j]; - if (ti.numEntries == 0) continue; - - ALOGI(" Type #0x%02x:\n", (int)(j+1)); - for (size_t k=0; k(idmap)); -} - -status_t ResTable::add(Asset* asset, void* cookie, bool copyData, const void* idmap) -{ - const void* data = asset->getBuffer(true); - if (data == NULL) { - ALOGW("Unable to get buffer of resource asset file"); - return UNKNOWN_ERROR; - } - size_t size = (size_t)asset->getLength(); - return add(data, size, cookie, asset, copyData, reinterpret_cast(idmap)); -} - -status_t ResTable::add(ResTable* src) -{ - mError = src->mError; - - for (size_t i=0; imHeaders.size(); i++) { - mHeaders.add(src->mHeaders[i]); - } - - for (size_t i=0; imPackageGroups.size(); i++) { - PackageGroup* srcPg = src->mPackageGroups[i]; - PackageGroup* pg = new PackageGroup(this, srcPg->name, srcPg->id); - for (size_t j=0; jpackages.size(); j++) { - pg->packages.add(srcPg->packages[j]); - } - pg->basePackage = srcPg->basePackage; - pg->typeCount = srcPg->typeCount; - mPackageGroups.add(pg); - } - - memcpy(mPackageMap, src->mPackageMap, sizeof(mPackageMap)); - - return mError; -} - -status_t ResTable::add(const void* data, size_t size, void* cookie, - Asset* asset, bool copyData, const Asset* idmap) -{ - if (!data) return NO_ERROR; - Header* header = new Header(this); - header->index = mHeaders.size(); - header->cookie = cookie; - if (idmap != NULL) { - const size_t idmap_size = idmap->getLength(); - const void* idmap_data = const_cast(idmap)->getBuffer(true); - header->resourceIDMap = (uint32_t*)malloc(idmap_size); - if (header->resourceIDMap == NULL) { - delete header; - return (mError = NO_MEMORY); - } - memcpy((void*)header->resourceIDMap, idmap_data, idmap_size); - header->resourceIDMapSize = idmap_size; - } - mHeaders.add(header); - - const bool notDeviceEndian = htods(0xf0) != 0xf0; - - LOAD_TABLE_NOISY( - ALOGV("Adding resources to ResTable: data=%p, size=0x%x, cookie=%p, asset=%p, copy=%d " - "idmap=%p\n", data, size, cookie, asset, copyData, idmap)); - - if (copyData || notDeviceEndian) { - header->ownedData = malloc(size); - if (header->ownedData == NULL) { - return (mError=NO_MEMORY); - } - memcpy(header->ownedData, data, size); - data = header->ownedData; - } - - header->header = (const ResTable_header*)data; - header->size = dtohl(header->header->header.size); - //ALOGI("Got size 0x%x, again size 0x%x, raw size 0x%x\n", header->size, - // dtohl(header->header->header.size), header->header->header.size); - LOAD_TABLE_NOISY(LOGV("Loading ResTable @%p:\n", header->header)); - LOAD_TABLE_NOISY(printHexData(2, header->header, header->size < 256 ? header->size : 256, - 16, 16, 0, false, printToLogFunc)); - if (dtohs(header->header->header.headerSize) > header->size - || header->size > size) { - ALOGW("Bad resource table: header size 0x%x or total size 0x%x is larger than data size 0x%x\n", - (int)dtohs(header->header->header.headerSize), - (int)header->size, (int)size); - return (mError=BAD_TYPE); - } - if (((dtohs(header->header->header.headerSize)|header->size)&0x3) != 0) { - ALOGW("Bad resource table: header size 0x%x or total size 0x%x is not on an integer boundary\n", - (int)dtohs(header->header->header.headerSize), - (int)header->size); - return (mError=BAD_TYPE); - } - header->dataEnd = ((const uint8_t*)header->header) + header->size; - - // Iterate through all chunks. - size_t curPackage = 0; - - const ResChunk_header* chunk = - (const ResChunk_header*)(((const uint8_t*)header->header) - + dtohs(header->header->header.headerSize)); - while (((const uint8_t*)chunk) <= (header->dataEnd-sizeof(ResChunk_header)) && - ((const uint8_t*)chunk) <= (header->dataEnd-dtohl(chunk->size))) { - status_t err = validate_chunk(chunk, sizeof(ResChunk_header), header->dataEnd, "ResTable"); - if (err != NO_ERROR) { - return (mError=err); - } - TABLE_NOISY(LOGV("Chunk: type=0x%x, headerSize=0x%x, size=0x%x, pos=%p\n", - dtohs(chunk->type), dtohs(chunk->headerSize), dtohl(chunk->size), - (void*)(((const uint8_t*)chunk) - ((const uint8_t*)header->header)))); - const size_t csize = dtohl(chunk->size); - const uint16_t ctype = dtohs(chunk->type); - if (ctype == RES_STRING_POOL_TYPE) { - if (header->values.getError() != NO_ERROR) { - // Only use the first string chunk; ignore any others that - // may appear. - status_t err = header->values.setTo(chunk, csize); - if (err != NO_ERROR) { - return (mError=err); - } - } else { - ALOGW("Multiple string chunks found in resource table."); - } - } else if (ctype == RES_TABLE_PACKAGE_TYPE) { - if (curPackage >= dtohl(header->header->packageCount)) { - ALOGW("More package chunks were found than the %d declared in the header.", - dtohl(header->header->packageCount)); - return (mError=BAD_TYPE); - } - uint32_t idmap_id = 0; - if (idmap != NULL) { - uint32_t tmp; - if (getIdmapPackageId(header->resourceIDMap, - header->resourceIDMapSize, - &tmp) == NO_ERROR) { - idmap_id = tmp; - } - } - if (parsePackage((ResTable_package*)chunk, header, idmap_id) != NO_ERROR) { - return mError; - } - curPackage++; - } else { - ALOGW("Unknown chunk type %p in table at %p.\n", - (void*)(int)(ctype), - (void*)(((const uint8_t*)chunk) - ((const uint8_t*)header->header))); - } - chunk = (const ResChunk_header*) - (((const uint8_t*)chunk) + csize); - } - - if (curPackage < dtohl(header->header->packageCount)) { - ALOGW("Fewer package chunks (%d) were found than the %d declared in the header.", - (int)curPackage, dtohl(header->header->packageCount)); - return (mError=BAD_TYPE); - } - mError = header->values.getError(); - if (mError != NO_ERROR) { - ALOGW("No string values found in resource table!"); - } - - TABLE_NOISY(LOGV("Returning from add with mError=%d\n", mError)); - return mError; -} - -status_t ResTable::getError() const -{ - return mError; -} - -void ResTable::uninit() -{ - mError = NO_INIT; - size_t N = mPackageGroups.size(); - for (size_t i=0; iowner == this) { - if (header->ownedData) { - free(header->ownedData); - } - delete header; - } - } - - mPackageGroups.clear(); - mHeaders.clear(); -} - -bool ResTable::getResourceName(uint32_t resID, resource_name* outName) const -{ - if (mError != NO_ERROR) { - return false; - } - - const ssize_t p = getResourcePackageIndex(resID); - const int t = Res_GETTYPE(resID); - const int e = Res_GETENTRY(resID); - - if (p < 0) { - if (Res_GETPACKAGE(resID)+1 == 0) { - ALOGW("No package identifier when getting name for resource number 0x%08x", resID); - } else { - ALOGW("No known package when getting name for resource number 0x%08x", resID); - } - return false; - } - if (t < 0) { - ALOGW("No type identifier when getting name for resource number 0x%08x", resID); - return false; - } - - const PackageGroup* const grp = mPackageGroups[p]; - if (grp == NULL) { - ALOGW("Bad identifier when getting name for resource number 0x%08x", resID); - return false; - } - if (grp->packages.size() > 0) { - const Package* const package = grp->packages[0]; - - const ResTable_type* type; - const ResTable_entry* entry; - ssize_t offset = getEntry(package, t, e, NULL, &type, &entry, NULL); - if (offset <= 0) { - return false; - } - - outName->package = grp->name.string(); - outName->packageLen = grp->name.size(); - outName->type = grp->basePackage->typeStrings.stringAt(t, &outName->typeLen); - outName->name = grp->basePackage->keyStrings.stringAt( - dtohl(entry->key.index), &outName->nameLen); - - // If we have a bad index for some reason, we should abort. - if (outName->type == NULL || outName->name == NULL) { - return false; - } - - return true; - } - - return false; -} - -ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag, uint16_t density, - uint32_t* outSpecFlags, ResTable_config* outConfig) const -{ - if (mError != NO_ERROR) { - return mError; - } - - const ssize_t p = getResourcePackageIndex(resID); - const int t = Res_GETTYPE(resID); - const int e = Res_GETENTRY(resID); - - if (p < 0) { - if (Res_GETPACKAGE(resID)+1 == 0) { - ALOGW("No package identifier when getting value for resource number 0x%08x", resID); - } else { - ALOGW("No known package when getting value for resource number 0x%08x", resID); - } - return BAD_INDEX; - } - if (t < 0) { - ALOGW("No type identifier when getting value for resource number 0x%08x", resID); - return BAD_INDEX; - } - - const Res_value* bestValue = NULL; - const Package* bestPackage = NULL; - ResTable_config bestItem; - memset(&bestItem, 0, sizeof(bestItem)); // make the compiler shut up - - if (outSpecFlags != NULL) *outSpecFlags = 0; - - // Look through all resource packages, starting with the most - // recently added. - const PackageGroup* const grp = mPackageGroups[p]; - if (grp == NULL) { - ALOGW("Bad identifier when getting value for resource number 0x%08x", resID); - return BAD_INDEX; - } - - // Allow overriding density - const ResTable_config* desiredConfig = &mParams; - ResTable_config* overrideConfig = NULL; - if (density > 0) { - overrideConfig = (ResTable_config*) malloc(sizeof(ResTable_config)); - if (overrideConfig == NULL) { - ALOGE("Couldn't malloc ResTable_config for overrides: %s", strerror(errno)); - return BAD_INDEX; - } - memcpy(overrideConfig, &mParams, sizeof(ResTable_config)); - overrideConfig->density = density; - desiredConfig = overrideConfig; - } - - ssize_t rc = BAD_VALUE; - size_t ip = grp->packages.size(); - while (ip > 0) { - ip--; - int T = t; - int E = e; - - const Package* const package = grp->packages[ip]; - if (package->header->resourceIDMap) { - uint32_t overlayResID = 0x0; - status_t retval = idmapLookup(package->header->resourceIDMap, - package->header->resourceIDMapSize, - resID, &overlayResID); - if (retval == NO_ERROR && overlayResID != 0x0) { - // for this loop iteration, this is the type and entry we really want - ALOGV("resource map 0x%08x -> 0x%08x\n", resID, overlayResID); - T = Res_GETTYPE(overlayResID); - E = Res_GETENTRY(overlayResID); - } else { - // resource not present in overlay package, continue with the next package - continue; - } - } - - const ResTable_type* type; - const ResTable_entry* entry; - const Type* typeClass; - ssize_t offset = getEntry(package, T, E, desiredConfig, &type, &entry, &typeClass); - if (offset <= 0) { - // No {entry, appropriate config} pair found in package. If this - // package is an overlay package (ip != 0), this simply means the - // overlay package did not specify a default. - // Non-overlay packages are still required to provide a default. - if (offset < 0 && ip == 0) { - ALOGW("Failure getting entry for 0x%08x (t=%d e=%d) in package %zd (error %d)\n", - resID, T, E, ip, (int)offset); - rc = offset; - goto out; - } - continue; - } - - if ((dtohs(entry->flags)&entry->FLAG_COMPLEX) != 0) { - if (!mayBeBag) { - ALOGW("Requesting resource %p failed because it is complex\n", - (void*)resID); - } - continue; - } - - TABLE_NOISY(aout << "Resource type data: " - << HexDump(type, dtohl(type->header.size)) << endl); - - if ((size_t)offset > (dtohl(type->header.size)-sizeof(Res_value))) { - ALOGW("ResTable_item at %d is beyond type chunk data %d", - (int)offset, dtohl(type->header.size)); - rc = BAD_TYPE; - goto out; - } - - const Res_value* item = - (const Res_value*)(((const uint8_t*)type) + offset); - ResTable_config thisConfig; - thisConfig.copyFromDtoH(type->config); - - if (outSpecFlags != NULL) { - if (typeClass->typeSpecFlags != NULL) { - *outSpecFlags |= dtohl(typeClass->typeSpecFlags[E]); - } else { - *outSpecFlags = -1; - } - } - - if (bestPackage != NULL && - (bestItem.isMoreSpecificThan(thisConfig) || bestItem.diff(thisConfig) == 0)) { - // Discard thisConfig not only if bestItem is more specific, but also if the two configs - // are identical (diff == 0), or overlay packages will not take effect. - continue; - } - - bestItem = thisConfig; - bestValue = item; - bestPackage = package; - } - - TABLE_NOISY(printf("Found result: package %p\n", bestPackage)); - - if (bestValue) { - outValue->size = dtohs(bestValue->size); - outValue->res0 = bestValue->res0; - outValue->dataType = bestValue->dataType; - outValue->data = dtohl(bestValue->data); - if (outConfig != NULL) { - *outConfig = bestItem; - } - TABLE_NOISY(size_t len; - printf("Found value: pkg=%d, type=%d, str=%s, int=%d\n", - bestPackage->header->index, - outValue->dataType, - outValue->dataType == bestValue->TYPE_STRING - ? String8(bestPackage->header->values.stringAt( - outValue->data, &len)).string() - : "", - outValue->data)); - rc = bestPackage->header->index; - goto out; - } - -out: - if (overrideConfig != NULL) { - free(overrideConfig); - } - - return rc; -} - -ssize_t ResTable::resolveReference(Res_value* value, ssize_t blockIndex, - uint32_t* outLastRef, uint32_t* inoutTypeSpecFlags, - ResTable_config* outConfig) const -{ - int count=0; - while (blockIndex >= 0 && value->dataType == value->TYPE_REFERENCE - && value->data != 0 && count < 20) { - if (outLastRef) *outLastRef = value->data; - uint32_t lastRef = value->data; - uint32_t newFlags = 0; - const ssize_t newIndex = getResource(value->data, value, true, 0, &newFlags, - outConfig); - if (newIndex == BAD_INDEX) { - return BAD_INDEX; - } - TABLE_THEME(LOGI("Resolving reference %p: newIndex=%d, type=0x%x, data=%p\n", - (void*)lastRef, (int)newIndex, (int)value->dataType, (void*)value->data)); - //printf("Getting reference 0x%08x: newIndex=%d\n", value->data, newIndex); - if (inoutTypeSpecFlags != NULL) *inoutTypeSpecFlags |= newFlags; - if (newIndex < 0) { - // This can fail if the resource being referenced is a style... - // in this case, just return the reference, and expect the - // caller to deal with. - return blockIndex; - } - blockIndex = newIndex; - count++; - } - return blockIndex; -} - -const char16_t* ResTable::valueToString( - const Res_value* value, size_t stringBlock, - char16_t tmpBuffer[TMP_BUFFER_SIZE], size_t* outLen) -{ - if (!value) { - return NULL; - } - if (value->dataType == value->TYPE_STRING) { - return getTableStringBlock(stringBlock)->stringAt(value->data, outLen); - } - // XXX do int to string conversions. - return NULL; -} - -ssize_t ResTable::lockBag(uint32_t resID, const bag_entry** outBag) const -{ - mLock.lock(); - ssize_t err = getBagLocked(resID, outBag); - if (err < NO_ERROR) { - //printf("*** get failed! unlocking\n"); - mLock.unlock(); - } - return err; -} - -void ResTable::unlockBag(const bag_entry* bag) const -{ - //printf("<<< unlockBag %p\n", this); - mLock.unlock(); -} - -void ResTable::lock() const -{ - mLock.lock(); -} - -void ResTable::unlock() const -{ - mLock.unlock(); -} - -ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag, - uint32_t* outTypeSpecFlags) const -{ - if (mError != NO_ERROR) { - return mError; - } - - const ssize_t p = getResourcePackageIndex(resID); - const int t = Res_GETTYPE(resID); - const int e = Res_GETENTRY(resID); - - if (p < 0) { - ALOGW("Invalid package identifier when getting bag for resource number 0x%08x", resID); - return BAD_INDEX; - } - if (t < 0) { - ALOGW("No type identifier when getting bag for resource number 0x%08x", resID); - return BAD_INDEX; - } - - //printf("Get bag: id=0x%08x, p=%d, t=%d\n", resID, p, t); - PackageGroup* const grp = mPackageGroups[p]; - if (grp == NULL) { - ALOGW("Bad identifier when getting bag for resource number 0x%08x", resID); - return false; - } - - if (t >= (int)grp->typeCount) { - ALOGW("Type identifier 0x%x is larger than type count 0x%x", - t+1, (int)grp->typeCount); - return BAD_INDEX; - } - - const Package* const basePackage = grp->packages[0]; - - const Type* const typeConfigs = basePackage->getType(t); - - const size_t NENTRY = typeConfigs->entryCount; - if (e >= (int)NENTRY) { - ALOGW("Entry identifier 0x%x is larger than entry count 0x%x", - e, (int)typeConfigs->entryCount); - return BAD_INDEX; - } - - // First see if we've already computed this bag... - if (grp->bags) { - bag_set** typeSet = grp->bags[t]; - if (typeSet) { - bag_set* set = typeSet[e]; - if (set) { - if (set != (bag_set*)0xFFFFFFFF) { - if (outTypeSpecFlags != NULL) { - *outTypeSpecFlags = set->typeSpecFlags; - } - *outBag = (bag_entry*)(set+1); - //ALOGI("Found existing bag for: %p\n", (void*)resID); - return set->numAttrs; - } - ALOGW("Attempt to retrieve bag 0x%08x which is invalid or in a cycle.", - resID); - return BAD_INDEX; - } - } - } - - // Bag not found, we need to compute it! - if (!grp->bags) { - grp->bags = (bag_set***)calloc(grp->typeCount, sizeof(bag_set*)); - if (!grp->bags) return NO_MEMORY; - } - - bag_set** typeSet = grp->bags[t]; - if (!typeSet) { - typeSet = (bag_set**)calloc(NENTRY, sizeof(bag_set*)); - if (!typeSet) return NO_MEMORY; - grp->bags[t] = typeSet; - } - - // Mark that we are currently working on this one. - typeSet[e] = (bag_set*)0xFFFFFFFF; - - // This is what we are building. - bag_set* set = NULL; - - TABLE_NOISY(LOGI("Building bag: %p\n", (void*)resID)); - - ResTable_config bestConfig; - memset(&bestConfig, 0, sizeof(bestConfig)); - - // Now collect all bag attributes from all packages. - size_t ip = grp->packages.size(); - while (ip > 0) { - ip--; - int T = t; - int E = e; - - const Package* const package = grp->packages[ip]; - if (package->header->resourceIDMap) { - uint32_t overlayResID = 0x0; - status_t retval = idmapLookup(package->header->resourceIDMap, - package->header->resourceIDMapSize, - resID, &overlayResID); - if (retval == NO_ERROR && overlayResID != 0x0) { - // for this loop iteration, this is the type and entry we really want - ALOGV("resource map 0x%08x -> 0x%08x\n", resID, overlayResID); - T = Res_GETTYPE(overlayResID); - E = Res_GETENTRY(overlayResID); - } else { - // resource not present in overlay package, continue with the next package - continue; - } - } - - const ResTable_type* type; - const ResTable_entry* entry; - const Type* typeClass; - ALOGV("Getting entry pkg=%p, t=%d, e=%d\n", package, T, E); - ssize_t offset = getEntry(package, T, E, &mParams, &type, &entry, &typeClass); - ALOGV("Resulting offset=%d\n", offset); - if (offset <= 0) { - // No {entry, appropriate config} pair found in package. If this - // package is an overlay package (ip != 0), this simply means the - // overlay package did not specify a default. - // Non-overlay packages are still required to provide a default. - if (offset < 0 && ip == 0) { - if (set) free(set); - return offset; - } - continue; - } - - if ((dtohs(entry->flags)&entry->FLAG_COMPLEX) == 0) { - ALOGW("Skipping entry %p in package table %d because it is not complex!\n", - (void*)resID, (int)ip); - continue; - } - - if (set != NULL && !type->config.isBetterThan(bestConfig, NULL)) { - continue; - } - bestConfig = type->config; - if (set) { - free(set); - set = NULL; - } - - const uint16_t entrySize = dtohs(entry->size); - const uint32_t parent = entrySize >= sizeof(ResTable_map_entry) - ? dtohl(((const ResTable_map_entry*)entry)->parent.ident) : 0; - const uint32_t count = entrySize >= sizeof(ResTable_map_entry) - ? dtohl(((const ResTable_map_entry*)entry)->count) : 0; - - size_t N = count; - - TABLE_NOISY(LOGI("Found map: size=%p parent=%p count=%d\n", - entrySize, parent, count)); - - // If this map inherits from another, we need to start - // with its parent's values. Otherwise start out empty. - TABLE_NOISY(printf("Creating new bag, entrySize=0x%08x, parent=0x%08x\n", - entrySize, parent)); - if (parent) { - const bag_entry* parentBag; - uint32_t parentTypeSpecFlags = 0; - const ssize_t NP = getBagLocked(parent, &parentBag, &parentTypeSpecFlags); - const size_t NT = ((NP >= 0) ? NP : 0) + N; - set = (bag_set*)malloc(sizeof(bag_set)+sizeof(bag_entry)*NT); - if (set == NULL) { - return NO_MEMORY; - } - if (NP > 0) { - memcpy(set+1, parentBag, NP*sizeof(bag_entry)); - set->numAttrs = NP; - TABLE_NOISY(LOGI("Initialized new bag with %d inherited attributes.\n", NP)); - } else { - TABLE_NOISY(LOGI("Initialized new bag with no inherited attributes.\n")); - set->numAttrs = 0; - } - set->availAttrs = NT; - set->typeSpecFlags = parentTypeSpecFlags; - } else { - set = (bag_set*)malloc(sizeof(bag_set)+sizeof(bag_entry)*N); - if (set == NULL) { - return NO_MEMORY; - } - set->numAttrs = 0; - set->availAttrs = N; - set->typeSpecFlags = 0; - } - - if (typeClass->typeSpecFlags != NULL) { - set->typeSpecFlags |= dtohl(typeClass->typeSpecFlags[E]); - } else { - set->typeSpecFlags = -1; - } - - // Now merge in the new attributes... - ssize_t curOff = offset; - const ResTable_map* map; - bag_entry* entries = (bag_entry*)(set+1); - size_t curEntry = 0; - uint32_t pos = 0; - TABLE_NOISY(LOGI("Starting with set %p, entries=%p, avail=%d\n", - set, entries, set->availAttrs)); - while (pos < count) { - TABLE_NOISY(printf("Now at %p\n", (void*)curOff)); - - if ((size_t)curOff > (dtohl(type->header.size)-sizeof(ResTable_map))) { - ALOGW("ResTable_map at %d is beyond type chunk data %d", - (int)curOff, dtohl(type->header.size)); - return BAD_TYPE; - } - map = (const ResTable_map*)(((const uint8_t*)type) + curOff); - N++; - - const uint32_t newName = htodl(map->name.ident); - bool isInside; - uint32_t oldName = 0; - while ((isInside=(curEntry < set->numAttrs)) - && (oldName=entries[curEntry].map.name.ident) < newName) { - TABLE_NOISY(printf("#%d: Keeping existing attribute: 0x%08x\n", - curEntry, entries[curEntry].map.name.ident)); - curEntry++; - } - - if ((!isInside) || oldName != newName) { - // This is a new attribute... figure out what to do with it. - if (set->numAttrs >= set->availAttrs) { - // Need to alloc more memory... - const size_t newAvail = set->availAttrs+N; - set = (bag_set*)realloc(set, - sizeof(bag_set) - + sizeof(bag_entry)*newAvail); - if (set == NULL) { - return NO_MEMORY; - } - set->availAttrs = newAvail; - entries = (bag_entry*)(set+1); - TABLE_NOISY(printf("Reallocated set %p, entries=%p, avail=%d\n", - set, entries, set->availAttrs)); - } - if (isInside) { - // Going in the middle, need to make space. - memmove(entries+curEntry+1, entries+curEntry, - sizeof(bag_entry)*(set->numAttrs-curEntry)); - set->numAttrs++; - } - TABLE_NOISY(printf("#%d: Inserting new attribute: 0x%08x\n", - curEntry, newName)); - } else { - TABLE_NOISY(printf("#%d: Replacing existing attribute: 0x%08x\n", - curEntry, oldName)); - } - - bag_entry* cur = entries+curEntry; - - cur->stringBlock = package->header->index; - cur->map.name.ident = newName; - cur->map.value.copyFrom_dtoh(map->value); - TABLE_NOISY(printf("Setting entry #%d %p: block=%d, name=0x%08x, type=%d, data=0x%08x\n", - curEntry, cur, cur->stringBlock, cur->map.name.ident, - cur->map.value.dataType, cur->map.value.data)); - - // On to the next! - curEntry++; - pos++; - const size_t size = dtohs(map->value.size); - curOff += size + sizeof(*map)-sizeof(map->value); - }; - if (curEntry > set->numAttrs) { - set->numAttrs = curEntry; - } - } - - // And this is it... - typeSet[e] = set; - if (set) { - if (outTypeSpecFlags != NULL) { - *outTypeSpecFlags = set->typeSpecFlags; - } - *outBag = (bag_entry*)(set+1); - TABLE_NOISY(LOGI("Returning %d attrs\n", set->numAttrs)); - return set->numAttrs; - } - return BAD_INDEX; -} - -void ResTable::setParameters(const ResTable_config* params) -{ - mLock.lock(); - TABLE_GETENTRY(LOGI("Setting parameters: imsi:%d/%d lang:%c%c cnt:%c%c " - "orien:%d touch:%d density:%d key:%d inp:%d nav:%d sz:%dx%d sw%ddp w%ddp h%ddp\n", - params->mcc, params->mnc, - params->language[0] ? params->language[0] : '-', - params->language[1] ? params->language[1] : '-', - params->country[0] ? params->country[0] : '-', - params->country[1] ? params->country[1] : '-', - params->orientation, - params->touchscreen, - params->density, - params->keyboard, - params->inputFlags, - params->navigation, - params->screenWidth, - params->screenHeight, - params->smallestScreenWidthDp, - params->screenWidthDp, - params->screenHeightDp)); - mParams = *params; - for (size_t i=0; iclearBagCache(); - } - mLock.unlock(); -} - -void ResTable::getParameters(ResTable_config* params) const -{ - mLock.lock(); - *params = mParams; - mLock.unlock(); -} - -struct id_name_map { - uint32_t id; - size_t len; - char16_t name[6]; -}; - -const static id_name_map ID_NAMES[] = { - { ResTable_map::ATTR_TYPE, 5, { '^', 't', 'y', 'p', 'e' } }, - { ResTable_map::ATTR_L10N, 5, { '^', 'l', '1', '0', 'n' } }, - { ResTable_map::ATTR_MIN, 4, { '^', 'm', 'i', 'n' } }, - { ResTable_map::ATTR_MAX, 4, { '^', 'm', 'a', 'x' } }, - { ResTable_map::ATTR_OTHER, 6, { '^', 'o', 't', 'h', 'e', 'r' } }, - { ResTable_map::ATTR_ZERO, 5, { '^', 'z', 'e', 'r', 'o' } }, - { ResTable_map::ATTR_ONE, 4, { '^', 'o', 'n', 'e' } }, - { ResTable_map::ATTR_TWO, 4, { '^', 't', 'w', 'o' } }, - { ResTable_map::ATTR_FEW, 4, { '^', 'f', 'e', 'w' } }, - { ResTable_map::ATTR_MANY, 5, { '^', 'm', 'a', 'n', 'y' } }, -}; - -uint32_t ResTable::identifierForName(const char16_t* name, size_t nameLen, - const char16_t* type, size_t typeLen, - const char16_t* package, - size_t packageLen, - uint32_t* outTypeSpecFlags) const -{ - TABLE_SUPER_NOISY(printf("Identifier for name: error=%d\n", mError)); - - // Check for internal resource identifier as the very first thing, so - // that we will always find them even when there are no resources. - if (name[0] == '^') { - const int N = (sizeof(ID_NAMES)/sizeof(ID_NAMES[0])); - size_t len; - for (int i=0; ilen; - if (len != nameLen) { - continue; - } - for (size_t j=1; jname[j] != name[j]) { - goto nope; - } - } - if (outTypeSpecFlags) { - *outTypeSpecFlags = ResTable_typeSpec::SPEC_PUBLIC; - } - return m->id; -nope: - ; - } - if (nameLen > 7) { - if (name[1] == 'i' && name[2] == 'n' - && name[3] == 'd' && name[4] == 'e' && name[5] == 'x' - && name[6] == '_') { - int index = atoi(String8(name + 7, nameLen - 7).string()); - if (Res_CHECKID(index)) { - ALOGW("Array resource index: %d is too large.", - index); - return 0; - } - if (outTypeSpecFlags) { - *outTypeSpecFlags = ResTable_typeSpec::SPEC_PUBLIC; - } - return Res_MAKEARRAY(index); - } - } - return 0; - } - - if (mError != NO_ERROR) { - return 0; - } - - bool fakePublic = false; - - // Figure out the package and type we are looking in... - - const char16_t* packageEnd = NULL; - const char16_t* typeEnd = NULL; - const char16_t* const nameEnd = name+nameLen; - const char16_t* p = name; - while (p < nameEnd) { - if (*p == ':') packageEnd = p; - else if (*p == '/') typeEnd = p; - p++; - } - if (*name == '@') { - name++; - if (*name == '*') { - fakePublic = true; - name++; - } - } - if (name >= nameEnd) { - return 0; - } - - if (packageEnd) { - package = name; - packageLen = packageEnd-name; - name = packageEnd+1; - } else if (!package) { - return 0; - } - - if (typeEnd) { - type = name; - typeLen = typeEnd-name; - name = typeEnd+1; - } else if (!type) { - return 0; - } - - if (name >= nameEnd) { - return 0; - } - nameLen = nameEnd-name; - - TABLE_NOISY(printf("Looking for identifier: type=%s, name=%s, package=%s\n", - String8(type, typeLen).string(), - String8(name, nameLen).string(), - String8(package, packageLen).string())); - - const size_t NG = mPackageGroups.size(); - for (size_t ig=0; igname.string(), group->name.size())) { - TABLE_NOISY(printf("Skipping package group: %s\n", String8(group->name).string())); - continue; - } - - const ssize_t ti = group->basePackage->typeStrings.indexOfString(type, typeLen); - if (ti < 0) { - TABLE_NOISY(printf("Type not found in package %s\n", String8(group->name).string())); - continue; - } - - const ssize_t ei = group->basePackage->keyStrings.indexOfString(name, nameLen); - if (ei < 0) { - TABLE_NOISY(printf("Name not found in package %s\n", String8(group->name).string())); - continue; - } - - TABLE_NOISY(printf("Search indices: type=%d, name=%d\n", ti, ei)); - - const Type* const typeConfigs = group->packages[0]->getType(ti); - if (typeConfigs == NULL || typeConfigs->configs.size() <= 0) { - TABLE_NOISY(printf("Expected type structure not found in package %s for idnex %d\n", - String8(group->name).string(), ti)); - } - - size_t NTC = typeConfigs->configs.size(); - for (size_t tci=0; tciconfigs[tci]; - const uint32_t typeOffset = dtohl(ty->entriesStart); - - const uint8_t* const end = ((const uint8_t*)ty) + dtohl(ty->header.size); - const uint32_t* const eindex = (const uint32_t*) - (((const uint8_t*)ty) + dtohs(ty->header.headerSize)); - - const size_t NE = dtohl(ty->entryCount); - for (size_t i=0; i (dtohl(ty->header.size)-sizeof(ResTable_entry))) { - ALOGW("ResTable_entry at %d is beyond type chunk data %d", - offset, dtohl(ty->header.size)); - return 0; - } - if ((offset&0x3) != 0) { - ALOGW("ResTable_entry at %d (pkg=%d type=%d ent=%d) is not on an integer boundary when looking for %s:%s/%s", - (int)offset, (int)group->id, (int)ti+1, (int)i, - String8(package, packageLen).string(), - String8(type, typeLen).string(), - String8(name, nameLen).string()); - return 0; - } - - const ResTable_entry* const entry = (const ResTable_entry*) - (((const uint8_t*)ty) + offset); - if (dtohs(entry->size) < sizeof(*entry)) { - ALOGW("ResTable_entry size %d is too small", dtohs(entry->size)); - return BAD_TYPE; - } - - TABLE_SUPER_NOISY(printf("Looking at entry #%d: want str %d, have %d\n", - i, ei, dtohl(entry->key.index))); - if (dtohl(entry->key.index) == (size_t)ei) { - if (outTypeSpecFlags) { - *outTypeSpecFlags = typeConfigs->typeSpecFlags[i]; - if (fakePublic) { - *outTypeSpecFlags |= ResTable_typeSpec::SPEC_PUBLIC; - } - } - return Res_MAKEID(group->id-1, ti, i); - } - } - } - } - - return 0; -} - -bool ResTable::expandResourceRef(const uint16_t* refStr, size_t refLen, - String16* outPackage, - String16* outType, - String16* outName, - const String16* defType, - const String16* defPackage, - const char** outErrorMsg, - bool* outPublicOnly) -{ - const char16_t* packageEnd = NULL; - const char16_t* typeEnd = NULL; - const char16_t* p = refStr; - const char16_t* const end = p + refLen; - while (p < end) { - if (*p == ':') packageEnd = p; - else if (*p == '/') { - typeEnd = p; - break; - } - p++; - } - p = refStr; - if (*p == '@') p++; - - if (outPublicOnly != NULL) { - *outPublicOnly = true; - } - if (*p == '*') { - p++; - if (outPublicOnly != NULL) { - *outPublicOnly = false; - } - } - - if (packageEnd) { - *outPackage = String16(p, packageEnd-p); - p = packageEnd+1; - } else { - if (!defPackage) { - if (outErrorMsg) { - *outErrorMsg = "No resource package specified"; - } - return false; - } - *outPackage = *defPackage; - } - if (typeEnd) { - *outType = String16(p, typeEnd-p); - p = typeEnd+1; - } else { - if (!defType) { - if (outErrorMsg) { - *outErrorMsg = "No resource type specified"; - } - return false; - } - *outType = *defType; - } - *outName = String16(p, end-p); - if(**outPackage == 0) { - if(outErrorMsg) { - *outErrorMsg = "Resource package cannot be an empty string"; - } - return false; - } - if(**outType == 0) { - if(outErrorMsg) { - *outErrorMsg = "Resource type cannot be an empty string"; - } - return false; - } - if(**outName == 0) { - if(outErrorMsg) { - *outErrorMsg = "Resource id cannot be an empty string"; - } - return false; - } - return true; -} - -static uint32_t get_hex(char c, bool* outError) -{ - if (c >= '0' && c <= '9') { - return c - '0'; - } else if (c >= 'a' && c <= 'f') { - return c - 'a' + 0xa; - } else if (c >= 'A' && c <= 'F') { - return c - 'A' + 0xa; - } - *outError = true; - return 0; -} - -struct unit_entry -{ - const char* name; - size_t len; - uint8_t type; - uint32_t unit; - float scale; -}; - -static const unit_entry unitNames[] = { - { "px", strlen("px"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_PX, 1.0f }, - { "dip", strlen("dip"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_DIP, 1.0f }, - { "dp", strlen("dp"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_DIP, 1.0f }, - { "sp", strlen("sp"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_SP, 1.0f }, - { "pt", strlen("pt"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_PT, 1.0f }, - { "in", strlen("in"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_IN, 1.0f }, - { "mm", strlen("mm"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_MM, 1.0f }, - { "%", strlen("%"), Res_value::TYPE_FRACTION, Res_value::COMPLEX_UNIT_FRACTION, 1.0f/100 }, - { "%p", strlen("%p"), Res_value::TYPE_FRACTION, Res_value::COMPLEX_UNIT_FRACTION_PARENT, 1.0f/100 }, - { NULL, 0, 0, 0, 0 } -}; - -static bool parse_unit(const char* str, Res_value* outValue, - float* outScale, const char** outEnd) -{ - const char* end = str; - while (*end != 0 && !isspace((unsigned char)*end)) { - end++; - } - const size_t len = end-str; - - const char* realEnd = end; - while (*realEnd != 0 && isspace((unsigned char)*realEnd)) { - realEnd++; - } - if (*realEnd != 0) { - return false; - } - - const unit_entry* cur = unitNames; - while (cur->name) { - if (len == cur->len && strncmp(cur->name, str, len) == 0) { - outValue->dataType = cur->type; - outValue->data = cur->unit << Res_value::COMPLEX_UNIT_SHIFT; - *outScale = cur->scale; - *outEnd = end; - //printf("Found unit %s for %s\n", cur->name, str); - return true; - } - cur++; - } - - return false; -} - - -bool ResTable::stringToInt(const char16_t* s, size_t len, Res_value* outValue) -{ - while (len > 0 && isspace16(*s)) { - s++; - len--; - } - - if (len <= 0) { - return false; - } - - size_t i = 0; - int32_t val = 0; - bool neg = false; - - if (*s == '-') { - neg = true; - i++; - } - - if (s[i] < '0' || s[i] > '9') { - return false; - } - - // Decimal or hex? - if (s[i] == '0' && s[i+1] == 'x') { - if (outValue) - outValue->dataType = outValue->TYPE_INT_HEX; - i += 2; - bool error = false; - while (i < len && !error) { - val = (val*16) + get_hex(s[i], &error); - i++; - } - if (error) { - return false; - } - } else { - if (outValue) - outValue->dataType = outValue->TYPE_INT_DEC; - while (i < len) { - if (s[i] < '0' || s[i] > '9') { - return false; - } - val = (val*10) + s[i]-'0'; - i++; - } - } - - if (neg) val = -val; - - while (i < len && isspace16(s[i])) { - i++; - } - - if (i == len) { - if (outValue) - outValue->data = val; - return true; - } - - return false; -} - -bool ResTable::stringToFloat(const char16_t* s, size_t len, Res_value* outValue) -{ - while (len > 0 && isspace16(*s)) { - s++; - len--; - } - - if (len <= 0) { - return false; - } - - char buf[128]; - int i=0; - while (len > 0 && *s != 0 && i < 126) { - if (*s > 255) { - return false; - } - buf[i++] = *s++; - len--; - } - - if (len > 0) { - return false; - } - if (buf[0] < '0' && buf[0] > '9' && buf[0] != '.') { - return false; - } - - buf[i] = 0; - const char* end; - float f = strtof(buf, (char**)&end); - - if (*end != 0 && !isspace((unsigned char)*end)) { - // Might be a unit... - float scale; - if (parse_unit(end, outValue, &scale, &end)) { - f *= scale; - const bool neg = f < 0; - if (neg) f = -f; - uint64_t bits = (uint64_t)(f*(1<<23)+.5f); - uint32_t radix; - uint32_t shift; - if ((bits&0x7fffff) == 0) { - // Always use 23p0 if there is no fraction, just to make - // things easier to read. - radix = Res_value::COMPLEX_RADIX_23p0; - shift = 23; - } else if ((bits&0xffffffffff800000LL) == 0) { - // Magnitude is zero -- can fit in 0 bits of precision. - radix = Res_value::COMPLEX_RADIX_0p23; - shift = 0; - } else if ((bits&0xffffffff80000000LL) == 0) { - // Magnitude can fit in 8 bits of precision. - radix = Res_value::COMPLEX_RADIX_8p15; - shift = 8; - } else if ((bits&0xffffff8000000000LL) == 0) { - // Magnitude can fit in 16 bits of precision. - radix = Res_value::COMPLEX_RADIX_16p7; - shift = 16; - } else { - // Magnitude needs entire range, so no fractional part. - radix = Res_value::COMPLEX_RADIX_23p0; - shift = 23; - } - int32_t mantissa = (int32_t)( - (bits>>shift) & Res_value::COMPLEX_MANTISSA_MASK); - if (neg) { - mantissa = (-mantissa) & Res_value::COMPLEX_MANTISSA_MASK; - } - outValue->data |= - (radix<data); - return true; - } - return false; - } - - while (*end != 0 && isspace((unsigned char)*end)) { - end++; - } - - if (*end == 0) { - if (outValue) { - outValue->dataType = outValue->TYPE_FLOAT; - *(float*)(&outValue->data) = f; - return true; - } - } - - return false; -} - -bool ResTable::stringToValue(Res_value* outValue, String16* outString, - const char16_t* s, size_t len, - bool preserveSpaces, bool coerceType, - uint32_t attrID, - const String16* defType, - const String16* defPackage, - Accessor* accessor, - void* accessorCookie, - uint32_t attrType, - bool enforcePrivate) const -{ - bool localizationSetting = accessor != NULL && accessor->getLocalizationSetting(); - const char* errorMsg = NULL; - - outValue->size = sizeof(Res_value); - outValue->res0 = 0; - - // First strip leading/trailing whitespace. Do this before handling - // escapes, so they can be used to force whitespace into the string. - if (!preserveSpaces) { - while (len > 0 && isspace16(*s)) { - s++; - len--; - } - while (len > 0 && isspace16(s[len-1])) { - len--; - } - // If the string ends with '\', then we keep the space after it. - if (len > 0 && s[len-1] == '\\' && s[len] != 0) { - len++; - } - } - - //printf("Value for: %s\n", String8(s, len).string()); - - uint32_t l10nReq = ResTable_map::L10N_NOT_REQUIRED; - uint32_t attrMin = 0x80000000, attrMax = 0x7fffffff; - bool fromAccessor = false; - if (attrID != 0 && !Res_INTERNALID(attrID)) { - const ssize_t p = getResourcePackageIndex(attrID); - const bag_entry* bag; - ssize_t cnt = p >= 0 ? lockBag(attrID, &bag) : -1; - //printf("For attr 0x%08x got bag of %d\n", attrID, cnt); - if (cnt >= 0) { - while (cnt > 0) { - //printf("Entry 0x%08x = 0x%08x\n", bag->map.name.ident, bag->map.value.data); - switch (bag->map.name.ident) { - case ResTable_map::ATTR_TYPE: - attrType = bag->map.value.data; - break; - case ResTable_map::ATTR_MIN: - attrMin = bag->map.value.data; - break; - case ResTable_map::ATTR_MAX: - attrMax = bag->map.value.data; - break; - case ResTable_map::ATTR_L10N: - l10nReq = bag->map.value.data; - break; - } - bag++; - cnt--; - } - unlockBag(bag); - } else if (accessor && accessor->getAttributeType(attrID, &attrType)) { - fromAccessor = true; - if (attrType == ResTable_map::TYPE_ENUM - || attrType == ResTable_map::TYPE_FLAGS - || attrType == ResTable_map::TYPE_INTEGER) { - accessor->getAttributeMin(attrID, &attrMin); - accessor->getAttributeMax(attrID, &attrMax); - } - if (localizationSetting) { - l10nReq = accessor->getAttributeL10N(attrID); - } - } - } - - const bool canStringCoerce = - coerceType && (attrType&ResTable_map::TYPE_STRING) != 0; - - if (*s == '@') { - outValue->dataType = outValue->TYPE_REFERENCE; - - // Note: we don't check attrType here because the reference can - // be to any other type; we just need to count on the client making - // sure the referenced type is correct. - - //printf("Looking up ref: %s\n", String8(s, len).string()); - - // It's a reference! - if (len == 5 && s[1]=='n' && s[2]=='u' && s[3]=='l' && s[4]=='l') { - outValue->data = 0; - return true; - } else { - bool createIfNotFound = false; - const char16_t* resourceRefName; - int resourceNameLen; - if (len > 2 && s[1] == '+') { - createIfNotFound = true; - resourceRefName = s + 2; - resourceNameLen = len - 2; - } else if (len > 2 && s[1] == '*') { - enforcePrivate = false; - resourceRefName = s + 2; - resourceNameLen = len - 2; - } else { - createIfNotFound = false; - resourceRefName = s + 1; - resourceNameLen = len - 1; - } - String16 package, type, name; - if (!expandResourceRef(resourceRefName,resourceNameLen, &package, &type, &name, - defType, defPackage, &errorMsg)) { - if (accessor != NULL) { - accessor->reportError(accessorCookie, errorMsg); - } - return false; - } - - uint32_t specFlags = 0; - uint32_t rid = identifierForName(name.string(), name.size(), type.string(), - type.size(), package.string(), package.size(), &specFlags); - if (rid != 0) { - if (enforcePrivate) { - if ((specFlags&ResTable_typeSpec::SPEC_PUBLIC) == 0) { - if (accessor != NULL) { - accessor->reportError(accessorCookie, "Resource is not public."); - } - return false; - } - } - if (!accessor) { - outValue->data = rid; - return true; - } - rid = Res_MAKEID( - accessor->getRemappedPackage(Res_GETPACKAGE(rid)), - Res_GETTYPE(rid), Res_GETENTRY(rid)); - TABLE_NOISY(printf("Incl %s:%s/%s: 0x%08x\n", - String8(package).string(), String8(type).string(), - String8(name).string(), rid)); - outValue->data = rid; - return true; - } - - if (accessor) { - uint32_t rid = accessor->getCustomResourceWithCreation(package, type, name, - createIfNotFound); - if (rid != 0) { - TABLE_NOISY(printf("Pckg %s:%s/%s: 0x%08x\n", - String8(package).string(), String8(type).string(), - String8(name).string(), rid)); - outValue->data = rid; - return true; - } - } - } - - if (accessor != NULL) { - accessor->reportError(accessorCookie, "No resource found that matches the given name"); - } - return false; - } - - // if we got to here, and localization is required and it's not a reference, - // complain and bail. - if (l10nReq == ResTable_map::L10N_SUGGESTED) { - if (localizationSetting) { - if (accessor != NULL) { - accessor->reportError(accessorCookie, "This attribute must be localized."); - } - } - } - - if (*s == '#') { - // It's a color! Convert to an integer of the form 0xaarrggbb. - uint32_t color = 0; - bool error = false; - if (len == 4) { - outValue->dataType = outValue->TYPE_INT_COLOR_RGB4; - color |= 0xFF000000; - color |= get_hex(s[1], &error) << 20; - color |= get_hex(s[1], &error) << 16; - color |= get_hex(s[2], &error) << 12; - color |= get_hex(s[2], &error) << 8; - color |= get_hex(s[3], &error) << 4; - color |= get_hex(s[3], &error); - } else if (len == 5) { - outValue->dataType = outValue->TYPE_INT_COLOR_ARGB4; - color |= get_hex(s[1], &error) << 28; - color |= get_hex(s[1], &error) << 24; - color |= get_hex(s[2], &error) << 20; - color |= get_hex(s[2], &error) << 16; - color |= get_hex(s[3], &error) << 12; - color |= get_hex(s[3], &error) << 8; - color |= get_hex(s[4], &error) << 4; - color |= get_hex(s[4], &error); - } else if (len == 7) { - outValue->dataType = outValue->TYPE_INT_COLOR_RGB8; - color |= 0xFF000000; - color |= get_hex(s[1], &error) << 20; - color |= get_hex(s[2], &error) << 16; - color |= get_hex(s[3], &error) << 12; - color |= get_hex(s[4], &error) << 8; - color |= get_hex(s[5], &error) << 4; - color |= get_hex(s[6], &error); - } else if (len == 9) { - outValue->dataType = outValue->TYPE_INT_COLOR_ARGB8; - color |= get_hex(s[1], &error) << 28; - color |= get_hex(s[2], &error) << 24; - color |= get_hex(s[3], &error) << 20; - color |= get_hex(s[4], &error) << 16; - color |= get_hex(s[5], &error) << 12; - color |= get_hex(s[6], &error) << 8; - color |= get_hex(s[7], &error) << 4; - color |= get_hex(s[8], &error); - } else { - error = true; - } - if (!error) { - if ((attrType&ResTable_map::TYPE_COLOR) == 0) { - if (!canStringCoerce) { - if (accessor != NULL) { - accessor->reportError(accessorCookie, - "Color types not allowed"); - } - return false; - } - } else { - outValue->data = color; - //printf("Color input=%s, output=0x%x\n", String8(s, len).string(), color); - return true; - } - } else { - if ((attrType&ResTable_map::TYPE_COLOR) != 0) { - if (accessor != NULL) { - accessor->reportError(accessorCookie, "Color value not valid --" - " must be #rgb, #argb, #rrggbb, or #aarrggbb"); - } - #if 0 - fprintf(stderr, "%s: Color ID %s value %s is not valid\n", - "Resource File", //(const char*)in->getPrintableSource(), - String8(*curTag).string(), - String8(s, len).string()); - #endif - return false; - } - } - } - - if (*s == '?') { - outValue->dataType = outValue->TYPE_ATTRIBUTE; - - // Note: we don't check attrType here because the reference can - // be to any other type; we just need to count on the client making - // sure the referenced type is correct. - - //printf("Looking up attr: %s\n", String8(s, len).string()); - - static const String16 attr16("attr"); - String16 package, type, name; - if (!expandResourceRef(s+1, len-1, &package, &type, &name, - &attr16, defPackage, &errorMsg)) { - if (accessor != NULL) { - accessor->reportError(accessorCookie, errorMsg); - } - return false; - } - - //printf("Pkg: %s, Type: %s, Name: %s\n", - // String8(package).string(), String8(type).string(), - // String8(name).string()); - uint32_t specFlags = 0; - uint32_t rid = - identifierForName(name.string(), name.size(), - type.string(), type.size(), - package.string(), package.size(), &specFlags); - if (rid != 0) { - if (enforcePrivate) { - if ((specFlags&ResTable_typeSpec::SPEC_PUBLIC) == 0) { - if (accessor != NULL) { - accessor->reportError(accessorCookie, "Attribute is not public."); - } - return false; - } - } - if (!accessor) { - outValue->data = rid; - return true; - } - rid = Res_MAKEID( - accessor->getRemappedPackage(Res_GETPACKAGE(rid)), - Res_GETTYPE(rid), Res_GETENTRY(rid)); - //printf("Incl %s:%s/%s: 0x%08x\n", - // String8(package).string(), String8(type).string(), - // String8(name).string(), rid); - outValue->data = rid; - return true; - } - - if (accessor) { - uint32_t rid = accessor->getCustomResource(package, type, name); - if (rid != 0) { - //printf("Mine %s:%s/%s: 0x%08x\n", - // String8(package).string(), String8(type).string(), - // String8(name).string(), rid); - outValue->data = rid; - return true; - } - } - - if (accessor != NULL) { - accessor->reportError(accessorCookie, "No resource found that matches the given name"); - } - return false; - } - - if (stringToInt(s, len, outValue)) { - if ((attrType&ResTable_map::TYPE_INTEGER) == 0) { - // If this type does not allow integers, but does allow floats, - // fall through on this error case because the float type should - // be able to accept any integer value. - if (!canStringCoerce && (attrType&ResTable_map::TYPE_FLOAT) == 0) { - if (accessor != NULL) { - accessor->reportError(accessorCookie, "Integer types not allowed"); - } - return false; - } - } else { - if (((int32_t)outValue->data) < ((int32_t)attrMin) - || ((int32_t)outValue->data) > ((int32_t)attrMax)) { - if (accessor != NULL) { - accessor->reportError(accessorCookie, "Integer value out of range"); - } - return false; - } - return true; - } - } - - if (stringToFloat(s, len, outValue)) { - if (outValue->dataType == Res_value::TYPE_DIMENSION) { - if ((attrType&ResTable_map::TYPE_DIMENSION) != 0) { - return true; - } - if (!canStringCoerce) { - if (accessor != NULL) { - accessor->reportError(accessorCookie, "Dimension types not allowed"); - } - return false; - } - } else if (outValue->dataType == Res_value::TYPE_FRACTION) { - if ((attrType&ResTable_map::TYPE_FRACTION) != 0) { - return true; - } - if (!canStringCoerce) { - if (accessor != NULL) { - accessor->reportError(accessorCookie, "Fraction types not allowed"); - } - return false; - } - } else if ((attrType&ResTable_map::TYPE_FLOAT) == 0) { - if (!canStringCoerce) { - if (accessor != NULL) { - accessor->reportError(accessorCookie, "Float types not allowed"); - } - return false; - } - } else { - return true; - } - } - - if (len == 4) { - if ((s[0] == 't' || s[0] == 'T') && - (s[1] == 'r' || s[1] == 'R') && - (s[2] == 'u' || s[2] == 'U') && - (s[3] == 'e' || s[3] == 'E')) { - if ((attrType&ResTable_map::TYPE_BOOLEAN) == 0) { - if (!canStringCoerce) { - if (accessor != NULL) { - accessor->reportError(accessorCookie, "Boolean types not allowed"); - } - return false; - } - } else { - outValue->dataType = outValue->TYPE_INT_BOOLEAN; - outValue->data = (uint32_t)-1; - return true; - } - } - } - - if (len == 5) { - if ((s[0] == 'f' || s[0] == 'F') && - (s[1] == 'a' || s[1] == 'A') && - (s[2] == 'l' || s[2] == 'L') && - (s[3] == 's' || s[3] == 'S') && - (s[4] == 'e' || s[4] == 'E')) { - if ((attrType&ResTable_map::TYPE_BOOLEAN) == 0) { - if (!canStringCoerce) { - if (accessor != NULL) { - accessor->reportError(accessorCookie, "Boolean types not allowed"); - } - return false; - } - } else { - outValue->dataType = outValue->TYPE_INT_BOOLEAN; - outValue->data = 0; - return true; - } - } - } - - if ((attrType&ResTable_map::TYPE_ENUM) != 0) { - const ssize_t p = getResourcePackageIndex(attrID); - const bag_entry* bag; - ssize_t cnt = p >= 0 ? lockBag(attrID, &bag) : -1; - //printf("Got %d for enum\n", cnt); - if (cnt >= 0) { - resource_name rname; - while (cnt > 0) { - if (!Res_INTERNALID(bag->map.name.ident)) { - //printf("Trying attr #%08x\n", bag->map.name.ident); - if (getResourceName(bag->map.name.ident, &rname)) { - #if 0 - printf("Matching %s against %s (0x%08x)\n", - String8(s, len).string(), - String8(rname.name, rname.nameLen).string(), - bag->map.name.ident); - #endif - if (strzcmp16(s, len, rname.name, rname.nameLen) == 0) { - outValue->dataType = bag->map.value.dataType; - outValue->data = bag->map.value.data; - unlockBag(bag); - return true; - } - } - - } - bag++; - cnt--; - } - unlockBag(bag); - } - - if (fromAccessor) { - if (accessor->getAttributeEnum(attrID, s, len, outValue)) { - return true; - } - } - } - - if ((attrType&ResTable_map::TYPE_FLAGS) != 0) { - const ssize_t p = getResourcePackageIndex(attrID); - const bag_entry* bag; - ssize_t cnt = p >= 0 ? lockBag(attrID, &bag) : -1; - //printf("Got %d for flags\n", cnt); - if (cnt >= 0) { - bool failed = false; - resource_name rname; - outValue->dataType = Res_value::TYPE_INT_HEX; - outValue->data = 0; - const char16_t* end = s + len; - const char16_t* pos = s; - while (pos < end && !failed) { - const char16_t* start = pos; - pos++; - while (pos < end && *pos != '|') { - pos++; - } - //printf("Looking for: %s\n", String8(start, pos-start).string()); - const bag_entry* bagi = bag; - ssize_t i; - for (i=0; imap.name.ident)) { - //printf("Trying attr #%08x\n", bagi->map.name.ident); - if (getResourceName(bagi->map.name.ident, &rname)) { - #if 0 - printf("Matching %s against %s (0x%08x)\n", - String8(start,pos-start).string(), - String8(rname.name, rname.nameLen).string(), - bagi->map.name.ident); - #endif - if (strzcmp16(start, pos-start, rname.name, rname.nameLen) == 0) { - outValue->data |= bagi->map.value.data; - break; - } - } - } - } - if (i >= cnt) { - // Didn't find this flag identifier. - failed = true; - } - if (pos < end) { - pos++; - } - } - unlockBag(bag); - if (!failed) { - //printf("Final flag value: 0x%lx\n", outValue->data); - return true; - } - } - - - if (fromAccessor) { - if (accessor->getAttributeFlags(attrID, s, len, outValue)) { - //printf("Final flag value: 0x%lx\n", outValue->data); - return true; - } - } - } - - if ((attrType&ResTable_map::TYPE_STRING) == 0) { - if (accessor != NULL) { - accessor->reportError(accessorCookie, "String types not allowed"); - } - return false; - } - - // Generic string handling... - outValue->dataType = outValue->TYPE_STRING; - if (outString) { - bool failed = collectString(outString, s, len, preserveSpaces, &errorMsg); - if (accessor != NULL) { - accessor->reportError(accessorCookie, errorMsg); - } - return failed; - } - - return true; -} - -bool ResTable::collectString(String16* outString, - const char16_t* s, size_t len, - bool preserveSpaces, - const char** outErrorMsg, - bool append) -{ - String16 tmp; - - char quoted = 0; - const char16_t* p = s; - while (p < (s+len)) { - while (p < (s+len)) { - const char16_t c = *p; - if (c == '\\') { - break; - } - if (!preserveSpaces) { - if (quoted == 0 && isspace16(c) - && (c != ' ' || isspace16(*(p+1)))) { - break; - } - if (c == '"' && (quoted == 0 || quoted == '"')) { - break; - } - if (c == '\'' && (quoted == 0 || quoted == '\'')) { - /* - * In practice, when people write ' instead of \' - * in a string, they are doing it by accident - * instead of really meaning to use ' as a quoting - * character. Warn them so they don't lose it. - */ - if (outErrorMsg) { - *outErrorMsg = "Apostrophe not preceded by \\"; - } - return false; - } - } - p++; - } - if (p < (s+len)) { - if (p > s) { - tmp.append(String16(s, p-s)); - } - if (!preserveSpaces && (*p == '"' || *p == '\'')) { - if (quoted == 0) { - quoted = *p; - } else { - quoted = 0; - } - p++; - } else if (!preserveSpaces && isspace16(*p)) { - // Space outside of a quote -- consume all spaces and - // leave a single plain space char. - tmp.append(String16(" ")); - p++; - while (p < (s+len) && isspace16(*p)) { - p++; - } - } else if (*p == '\\') { - p++; - if (p < (s+len)) { - switch (*p) { - case 't': - tmp.append(String16("\t")); - break; - case 'n': - tmp.append(String16("\n")); - break; - case '#': - tmp.append(String16("#")); - break; - case '@': - tmp.append(String16("@")); - break; - case '?': - tmp.append(String16("?")); - break; - case '"': - tmp.append(String16("\"")); - break; - case '\'': - tmp.append(String16("'")); - break; - case '\\': - tmp.append(String16("\\")); - break; - case 'u': - { - char16_t chr = 0; - int i = 0; - while (i < 4 && p[1] != 0) { - p++; - i++; - int c; - if (*p >= '0' && *p <= '9') { - c = *p - '0'; - } else if (*p >= 'a' && *p <= 'f') { - c = *p - 'a' + 10; - } else if (*p >= 'A' && *p <= 'F') { - c = *p - 'A' + 10; - } else { - if (outErrorMsg) { - *outErrorMsg = "Bad character in \\u unicode escape sequence"; - } - return false; - } - chr = (chr<<4) | c; - } - tmp.append(String16(&chr, 1)); - } break; - default: - // ignore unknown escape chars. - break; - } - p++; - } - } - len -= (p-s); - s = p; - } - } - - if (tmp.size() != 0) { - if (len > 0) { - tmp.append(String16(s, len)); - } - if (append) { - outString->append(tmp); - } else { - outString->setTo(tmp); - } - } else { - if (append) { - outString->append(String16(s, len)); - } else { - outString->setTo(s, len); - } - } - - return true; -} - -size_t ResTable::getBasePackageCount() const -{ - if (mError != NO_ERROR) { - return 0; - } - return mPackageGroups.size(); -} - -const char16_t* ResTable::getBasePackageName(size_t idx) const -{ - if (mError != NO_ERROR) { - return 0; - } - LOG_FATAL_IF(idx >= mPackageGroups.size(), - "Requested package index %d past package count %d", - (int)idx, (int)mPackageGroups.size()); - return mPackageGroups[idx]->name.string(); -} - -uint32_t ResTable::getBasePackageId(size_t idx) const -{ - if (mError != NO_ERROR) { - return 0; - } - LOG_FATAL_IF(idx >= mPackageGroups.size(), - "Requested package index %d past package count %d", - (int)idx, (int)mPackageGroups.size()); - return mPackageGroups[idx]->id; -} - -size_t ResTable::getTableCount() const -{ - return mHeaders.size(); -} - -const ResStringPool* ResTable::getTableStringBlock(size_t index) const -{ - return &mHeaders[index]->values; -} - -void* ResTable::getTableCookie(size_t index) const -{ - return mHeaders[index]->cookie; -} - -void ResTable::getConfigurations(Vector* configs) const -{ - const size_t I = mPackageGroups.size(); - for (size_t i=0; ipackages.size(); - for (size_t j=0; jpackages[j]; - const size_t K = package->types.size(); - for (size_t k=0; ktypes[k]; - if (type == NULL) continue; - const size_t L = type->configs.size(); - for (size_t l=0; lconfigs[l]; - const ResTable_config* cfg = &config->config; - // only insert unique - const size_t M = configs->size(); - size_t m; - for (m=0; madd(*cfg); - } - } - } - } - } -} - -void ResTable::getLocales(Vector* locales) const -{ - Vector configs; - ALOGV("calling getConfigurations"); - getConfigurations(&configs); - ALOGV("called getConfigurations size=%d", (int)configs.size()); - const size_t I = configs.size(); - for (size_t i=0; isize(); - size_t j; - for (j=0; jadd(String8(locale)); - } - } -} - -ssize_t ResTable::getEntry( - const Package* package, int typeIndex, int entryIndex, - const ResTable_config* config, - const ResTable_type** outType, const ResTable_entry** outEntry, - const Type** outTypeClass) const -{ - ALOGV("Getting entry from package %p\n", package); - const ResTable_package* const pkg = package->package; - - const Type* allTypes = package->getType(typeIndex); - ALOGV("allTypes=%p\n", allTypes); - if (allTypes == NULL) { - ALOGV("Skipping entry type index 0x%02x because type is NULL!\n", typeIndex); - return 0; - } - - if ((size_t)entryIndex >= allTypes->entryCount) { - ALOGW("getEntry failing because entryIndex %d is beyond type entryCount %d", - entryIndex, (int)allTypes->entryCount); - return BAD_TYPE; - } - - const ResTable_type* type = NULL; - uint32_t offset = ResTable_type::NO_ENTRY; - ResTable_config bestConfig; - memset(&bestConfig, 0, sizeof(bestConfig)); // make the compiler shut up - - const size_t NT = allTypes->configs.size(); - for (size_t i=0; iconfigs[i]; - if (thisType == NULL) continue; - - ResTable_config thisConfig; - thisConfig.copyFromDtoH(thisType->config); - - TABLE_GETENTRY(LOGI("Match entry 0x%x in type 0x%x (sz 0x%x): %s\n", - entryIndex, typeIndex+1, dtohl(thisType->config.size), - thisConfig.toString().string())); - - // Check to make sure this one is valid for the current parameters. - if (config && !thisConfig.match(*config)) { - TABLE_GETENTRY(LOGI("Does not match config!\n")); - continue; - } - - // Check if there is the desired entry in this type. - - const uint8_t* const end = ((const uint8_t*)thisType) - + dtohl(thisType->header.size); - const uint32_t* const eindex = (const uint32_t*) - (((const uint8_t*)thisType) + dtohs(thisType->header.headerSize)); - - uint32_t thisOffset = dtohl(eindex[entryIndex]); - if (thisOffset == ResTable_type::NO_ENTRY) { - TABLE_GETENTRY(LOGI("Skipping because it is not defined!\n")); - continue; - } - - if (type != NULL) { - // Check if this one is less specific than the last found. If so, - // we will skip it. We check starting with things we most care - // about to those we least care about. - if (!thisConfig.isBetterThan(bestConfig, config)) { - TABLE_GETENTRY(LOGI("This config is worse than last!\n")); - continue; - } - } - - type = thisType; - offset = thisOffset; - bestConfig = thisConfig; - TABLE_GETENTRY(LOGI("Best entry so far -- using it!\n")); - if (!config) break; - } - - if (type == NULL) { - TABLE_GETENTRY(LOGI("No value found for requested entry!\n")); - return BAD_INDEX; - } - - offset += dtohl(type->entriesStart); - TABLE_NOISY(aout << "Looking in resource table " << package->header->header - << ", typeOff=" - << (void*)(((const char*)type)-((const char*)package->header->header)) - << ", offset=" << (void*)offset << endl); - - if (offset > (dtohl(type->header.size)-sizeof(ResTable_entry))) { - ALOGW("ResTable_entry at 0x%x is beyond type chunk data 0x%x", - offset, dtohl(type->header.size)); - return BAD_TYPE; - } - if ((offset&0x3) != 0) { - ALOGW("ResTable_entry at 0x%x is not on an integer boundary", - offset); - return BAD_TYPE; - } - - const ResTable_entry* const entry = (const ResTable_entry*) - (((const uint8_t*)type) + offset); - if (dtohs(entry->size) < sizeof(*entry)) { - ALOGW("ResTable_entry size 0x%x is too small", dtohs(entry->size)); - return BAD_TYPE; - } - - *outType = type; - *outEntry = entry; - if (outTypeClass != NULL) { - *outTypeClass = allTypes; - } - return offset + dtohs(entry->size); -} - -status_t ResTable::parsePackage(const ResTable_package* const pkg, - const Header* const header, uint32_t idmap_id) -{ - const uint8_t* base = (const uint8_t*)pkg; - status_t err = validate_chunk(&pkg->header, sizeof(*pkg), - header->dataEnd, "ResTable_package"); - if (err != NO_ERROR) { - return (mError=err); - } - - const size_t pkgSize = dtohl(pkg->header.size); - - if (dtohl(pkg->typeStrings) >= pkgSize) { - ALOGW("ResTable_package type strings at %p are past chunk size %p.", - (void*)dtohl(pkg->typeStrings), (void*)pkgSize); - return (mError=BAD_TYPE); - } - if ((dtohl(pkg->typeStrings)&0x3) != 0) { - ALOGW("ResTable_package type strings at %p is not on an integer boundary.", - (void*)dtohl(pkg->typeStrings)); - return (mError=BAD_TYPE); - } - if (dtohl(pkg->keyStrings) >= pkgSize) { - ALOGW("ResTable_package key strings at %p are past chunk size %p.", - (void*)dtohl(pkg->keyStrings), (void*)pkgSize); - return (mError=BAD_TYPE); - } - if ((dtohl(pkg->keyStrings)&0x3) != 0) { - ALOGW("ResTable_package key strings at %p is not on an integer boundary.", - (void*)dtohl(pkg->keyStrings)); - return (mError=BAD_TYPE); - } - - Package* package = NULL; - PackageGroup* group = NULL; - uint32_t id = idmap_id != 0 ? idmap_id : dtohl(pkg->id); - // If at this point id == 0, pkg is an overlay package without a - // corresponding idmap. During regular usage, overlay packages are - // always loaded alongside their idmaps, but during idmap creation - // the package is temporarily loaded by itself. - if (id < 256) { - - package = new Package(this, header, pkg); - if (package == NULL) { - return (mError=NO_MEMORY); - } - - size_t idx = mPackageMap[id]; - if (idx == 0) { - idx = mPackageGroups.size()+1; - - char16_t tmpName[sizeof(pkg->name)/sizeof(char16_t)]; - strcpy16_dtoh(tmpName, pkg->name, sizeof(pkg->name)/sizeof(char16_t)); - group = new PackageGroup(this, String16(tmpName), id); - if (group == NULL) { - delete package; - return (mError=NO_MEMORY); - } - - err = package->typeStrings.setTo(base+dtohl(pkg->typeStrings), - header->dataEnd-(base+dtohl(pkg->typeStrings))); - if (err != NO_ERROR) { - delete group; - delete package; - return (mError=err); - } - err = package->keyStrings.setTo(base+dtohl(pkg->keyStrings), - header->dataEnd-(base+dtohl(pkg->keyStrings))); - if (err != NO_ERROR) { - delete group; - delete package; - return (mError=err); - } - - //printf("Adding new package id %d at index %d\n", id, idx); - err = mPackageGroups.add(group); - if (err < NO_ERROR) { - return (mError=err); - } - group->basePackage = package; - - mPackageMap[id] = (uint8_t)idx; - } else { - group = mPackageGroups.itemAt(idx-1); - if (group == NULL) { - return (mError=UNKNOWN_ERROR); - } - } - err = group->packages.add(package); - if (err < NO_ERROR) { - return (mError=err); - } - } else { - LOG_ALWAYS_FATAL("Package id out of range"); - return NO_ERROR; - } - - - // Iterate through all chunks. - size_t curPackage = 0; - - const ResChunk_header* chunk = - (const ResChunk_header*)(((const uint8_t*)pkg) - + dtohs(pkg->header.headerSize)); - const uint8_t* endPos = ((const uint8_t*)pkg) + dtohs(pkg->header.size); - while (((const uint8_t*)chunk) <= (endPos-sizeof(ResChunk_header)) && - ((const uint8_t*)chunk) <= (endPos-dtohl(chunk->size))) { - TABLE_NOISY(LOGV("PackageChunk: type=0x%x, headerSize=0x%x, size=0x%x, pos=%p\n", - dtohs(chunk->type), dtohs(chunk->headerSize), dtohl(chunk->size), - (void*)(((const uint8_t*)chunk) - ((const uint8_t*)header->header)))); - const size_t csize = dtohl(chunk->size); - const uint16_t ctype = dtohs(chunk->type); - if (ctype == RES_TABLE_TYPE_SPEC_TYPE) { - const ResTable_typeSpec* typeSpec = (const ResTable_typeSpec*)(chunk); - err = validate_chunk(&typeSpec->header, sizeof(*typeSpec), - endPos, "ResTable_typeSpec"); - if (err != NO_ERROR) { - return (mError=err); - } - - const size_t typeSpecSize = dtohl(typeSpec->header.size); - - LOAD_TABLE_NOISY(printf("TypeSpec off %p: type=0x%x, headerSize=0x%x, size=%p\n", - (void*)(base-(const uint8_t*)chunk), - dtohs(typeSpec->header.type), - dtohs(typeSpec->header.headerSize), - (void*)typeSize)); - // look for block overrun or int overflow when multiplying by 4 - if ((dtohl(typeSpec->entryCount) > (INT32_MAX/sizeof(uint32_t)) - || dtohs(typeSpec->header.headerSize)+(sizeof(uint32_t)*dtohl(typeSpec->entryCount)) - > typeSpecSize)) { - ALOGW("ResTable_typeSpec entry index to %p extends beyond chunk end %p.", - (void*)(dtohs(typeSpec->header.headerSize) - +(sizeof(uint32_t)*dtohl(typeSpec->entryCount))), - (void*)typeSpecSize); - return (mError=BAD_TYPE); - } - - if (typeSpec->id == 0) { - ALOGW("ResTable_type has an id of 0."); - return (mError=BAD_TYPE); - } - - while (package->types.size() < typeSpec->id) { - package->types.add(NULL); - } - Type* t = package->types[typeSpec->id-1]; - if (t == NULL) { - t = new Type(header, package, dtohl(typeSpec->entryCount)); - package->types.editItemAt(typeSpec->id-1) = t; - } else if (dtohl(typeSpec->entryCount) != t->entryCount) { - ALOGW("ResTable_typeSpec entry count inconsistent: given %d, previously %d", - (int)dtohl(typeSpec->entryCount), (int)t->entryCount); - return (mError=BAD_TYPE); - } - t->typeSpecFlags = (const uint32_t*)( - ((const uint8_t*)typeSpec) + dtohs(typeSpec->header.headerSize)); - t->typeSpec = typeSpec; - - } else if (ctype == RES_TABLE_TYPE_TYPE) { - const ResTable_type* type = (const ResTable_type*)(chunk); - err = validate_chunk(&type->header, sizeof(*type)-sizeof(ResTable_config)+4, - endPos, "ResTable_type"); - if (err != NO_ERROR) { - return (mError=err); - } - - const size_t typeSize = dtohl(type->header.size); - - LOAD_TABLE_NOISY(printf("Type off %p: type=0x%x, headerSize=0x%x, size=%p\n", - (void*)(base-(const uint8_t*)chunk), - dtohs(type->header.type), - dtohs(type->header.headerSize), - (void*)typeSize)); - if (dtohs(type->header.headerSize)+(sizeof(uint32_t)*dtohl(type->entryCount)) - > typeSize) { - ALOGW("ResTable_type entry index to %p extends beyond chunk end %p.", - (void*)(dtohs(type->header.headerSize) - +(sizeof(uint32_t)*dtohl(type->entryCount))), - (void*)typeSize); - return (mError=BAD_TYPE); - } - if (dtohl(type->entryCount) != 0 - && dtohl(type->entriesStart) > (typeSize-sizeof(ResTable_entry))) { - ALOGW("ResTable_type entriesStart at %p extends beyond chunk end %p.", - (void*)dtohl(type->entriesStart), (void*)typeSize); - return (mError=BAD_TYPE); - } - if (type->id == 0) { - ALOGW("ResTable_type has an id of 0."); - return (mError=BAD_TYPE); - } - - while (package->types.size() < type->id) { - package->types.add(NULL); - } - Type* t = package->types[type->id-1]; - if (t == NULL) { - t = new Type(header, package, dtohl(type->entryCount)); - package->types.editItemAt(type->id-1) = t; - } else if (dtohl(type->entryCount) != t->entryCount) { - ALOGW("ResTable_type entry count inconsistent: given %d, previously %d", - (int)dtohl(type->entryCount), (int)t->entryCount); - return (mError=BAD_TYPE); - } - - TABLE_GETENTRY( - ResTable_config thisConfig; - thisConfig.copyFromDtoH(type->config); - ALOGI("Adding config to type %d: %s\n", - type->id, thisConfig.toString().string())); - t->configs.add(type); - } else { - status_t err = validate_chunk(chunk, sizeof(ResChunk_header), - endPos, "ResTable_package:unknown"); - if (err != NO_ERROR) { - return (mError=err); - } - } - chunk = (const ResChunk_header*) - (((const uint8_t*)chunk) + csize); - } - - if (group->typeCount == 0) { - group->typeCount = package->types.size(); - } - - return NO_ERROR; -} - -status_t ResTable::createIdmap(const ResTable& overlay, uint32_t originalCrc, uint32_t overlayCrc, - void** outData, size_t* outSize) const -{ - // see README for details on the format of map - if (mPackageGroups.size() == 0) { - return UNKNOWN_ERROR; - } - if (mPackageGroups[0]->packages.size() == 0) { - return UNKNOWN_ERROR; - } - - Vector > map; - const PackageGroup* pg = mPackageGroups[0]; - const Package* pkg = pg->packages[0]; - size_t typeCount = pkg->types.size(); - // starting size is header + first item (number of types in map) - *outSize = (IDMAP_HEADER_SIZE + 1) * sizeof(uint32_t); - const String16 overlayPackage(overlay.mPackageGroups[0]->packages[0]->package->name); - const uint32_t pkg_id = pkg->package->id << 24; - - for (size_t typeIndex = 0; typeIndex < typeCount; ++typeIndex) { - ssize_t offset = -1; - const Type* typeConfigs = pkg->getType(typeIndex); - ssize_t mapIndex = map.add(); - if (mapIndex < 0) { - return NO_MEMORY; - } - Vector& vector = map.editItemAt(mapIndex); - for (size_t entryIndex = 0; entryIndex < typeConfigs->entryCount; ++entryIndex) { - uint32_t resID = (0xff000000 & ((pkg->package->id)<<24)) - | (0x00ff0000 & ((typeIndex+1)<<16)) - | (0x0000ffff & (entryIndex)); - resource_name resName; - if (!this->getResourceName(resID, &resName)) { - ALOGW("idmap: resource 0x%08x has spec but lacks values, skipping\n", resID); - continue; - } - - const String16 overlayType(resName.type, resName.typeLen); - const String16 overlayName(resName.name, resName.nameLen); - uint32_t overlayResID = overlay.identifierForName(overlayName.string(), - overlayName.size(), - overlayType.string(), - overlayType.size(), - overlayPackage.string(), - overlayPackage.size()); - if (overlayResID != 0) { - // overlay package has package ID == 0, use original package's ID instead - overlayResID |= pkg_id; - } - vector.push(overlayResID); - if (overlayResID != 0 && offset == -1) { - offset = Res_GETENTRY(resID); - } -#if 0 - if (overlayResID != 0) { - ALOGD("%s/%s 0x%08x -> 0x%08x\n", - String8(String16(resName.type)).string(), - String8(String16(resName.name)).string(), - resID, overlayResID); - } -#endif - } - - if (offset != -1) { - // shave off leading and trailing entries which lack overlay values - vector.removeItemsAt(0, offset); - vector.insertAt((uint32_t)offset, 0, 1); - while (vector.top() == 0) { - vector.pop(); - } - // reserve space for number and offset of entries, and the actual entries - *outSize += (2 + vector.size()) * sizeof(uint32_t); - } else { - // no entries of current type defined in overlay package - vector.clear(); - // reserve space for type offset - *outSize += 1 * sizeof(uint32_t); - } - } - - if ((*outData = malloc(*outSize)) == NULL) { - return NO_MEMORY; - } - uint32_t* data = (uint32_t*)*outData; - *data++ = htodl(IDMAP_MAGIC); - *data++ = htodl(originalCrc); - *data++ = htodl(overlayCrc); - const size_t mapSize = map.size(); - *data++ = htodl(mapSize); - size_t offset = mapSize; - for (size_t i = 0; i < mapSize; ++i) { - const Vector& vector = map.itemAt(i); - const size_t N = vector.size(); - if (N == 0) { - *data++ = htodl(0); - } else { - offset++; - *data++ = htodl(offset); - offset += N; - } - } - for (size_t i = 0; i < mapSize; ++i) { - const Vector& vector = map.itemAt(i); - const size_t N = vector.size(); - if (N == 0) { - continue; - } - *data++ = htodl(N - 1); // do not count the offset (which is vector's first element) - for (size_t j = 0; j < N; ++j) { - const uint32_t& overlayResID = vector.itemAt(j); - *data++ = htodl(overlayResID); - } - } - - return NO_ERROR; -} - -bool ResTable::getIdmapInfo(const void* idmap, size_t sizeBytes, - uint32_t* pOriginalCrc, uint32_t* pOverlayCrc) -{ - const uint32_t* map = (const uint32_t*)idmap; - if (!assertIdmapHeader(map, sizeBytes)) { - return false; - } - *pOriginalCrc = map[1]; - *pOverlayCrc = map[2]; - return true; -} - - -#ifndef HAVE_ANDROID_OS -#define CHAR16_TO_CSTR(c16, len) (String8(String16(c16,len)).string()) - -#define CHAR16_ARRAY_EQ(constant, var, len) \ - ((len == (sizeof(constant)/sizeof(constant[0]))) && (0 == memcmp((var), (constant), (len)))) - -void print_complex(uint32_t complex, bool isFraction) -{ - const float MANTISSA_MULT = - 1.0f / (1<>Res_value::COMPLEX_RADIX_SHIFT) - & Res_value::COMPLEX_RADIX_MASK]; - printf("%f", value); - - 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; - case Res_value::COMPLEX_UNIT_SP: printf("sp"); break; - case Res_value::COMPLEX_UNIT_PT: printf("pt"); break; - case Res_value::COMPLEX_UNIT_IN: printf("in"); break; - case Res_value::COMPLEX_UNIT_MM: printf("mm"); break; - default: printf(" (unknown unit)"); break; - } - } else { - switch ((complex>>Res_value::COMPLEX_UNIT_SHIFT)&Res_value::COMPLEX_UNIT_MASK) { - case Res_value::COMPLEX_UNIT_FRACTION: printf("%%"); break; - case Res_value::COMPLEX_UNIT_FRACTION_PARENT: printf("%%p"); break; - default: printf(" (unknown unit)"); break; - } - } -} - -// Normalize a string for output -String8 ResTable::normalizeForOutput( const char *input ) -{ - String8 ret; - char buff[2]; - buff[1] = '\0'; - - while (*input != '\0') { - switch (*input) { - // All interesting characters are in the ASCII zone, so we are making our own lives - // easier by scanning the string one byte at a time. - case '\\': - ret += "\\\\"; - break; - case '\n': - ret += "\\n"; - break; - case '"': - ret += "\\\""; - break; - default: - buff[0] = *input; - ret += buff; - break; - } - - input++; - } - - return ret; -} - -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 char* str8 = pkg->header->values.string8At( - value.data, &len); - if (str8 != NULL) { - printf("(string8) \"%s\"\n", normalizeForOutput(str8).string()); - } else { - const char16_t* str16 = pkg->header->values.stringAt( - value.data, &len); - if (str16 != NULL) { - printf("(string16) \"%s\"\n", - normalizeForOutput(String8(str16, len).string()).string()); - } else { - printf("(string) null\n"); - } - } - } 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) { - printf("mError=0x%x (%s)\n", mError, strerror(mError)); - } -#if 0 - printf("mParams=%c%c-%c%c,\n", - mParams.language[0], mParams.language[1], - mParams.country[0], mParams.country[1]); -#endif - size_t pgCount = mPackageGroups.size(); - printf("Package Groups (%d)\n", (int)pgCount); - for (size_t pgIndex=0; pgIndexid, (int)pg->packages.size(), - String8(pg->name).string()); - - size_t pkgCount = pg->packages.size(); - for (size_t pkgIndex=0; pkgIndexpackages[pkgIndex]; - size_t typeCount = pkg->types.size(); - printf(" Package %d id=%d name=%s typeCount=%d\n", (int)pkgIndex, - pkg->package->id, String8(String16(pkg->package->name)).string(), - (int)typeCount); - for (size_t typeIndex=0; typeIndexgetType(typeIndex); - if (typeConfigs == NULL) { - printf(" type %d NULL\n", (int)typeIndex); - continue; - } - const size_t NTC = typeConfigs->configs.size(); - printf(" type %d configCount=%d entryCount=%d\n", - (int)typeIndex, (int)NTC, (int)typeConfigs->entryCount); - if (typeConfigs->typeSpecFlags != NULL) { - for (size_t entryIndex=0; entryIndexentryCount; entryIndex++) { - uint32_t resID = (0xff000000 & ((pkg->package->id)<<24)) - | (0x00ff0000 & ((typeIndex+1)<<16)) - | (0x0000ffff & (entryIndex)); - resource_name resName; - if (this->getResourceName(resID, &resName)) { - printf(" spec resource 0x%08x %s:%s/%s: flags=0x%08x\n", - resID, - CHAR16_TO_CSTR(resName.package, resName.packageLen), - CHAR16_TO_CSTR(resName.type, resName.typeLen), - CHAR16_TO_CSTR(resName.name, resName.nameLen), - dtohl(typeConfigs->typeSpecFlags[entryIndex])); - } else { - printf(" INVALID TYPE CONFIG FOR RESOURCE 0x%08x\n", resID); - } - } - } - for (size_t configIndex=0; configIndexconfigs[configIndex]; - if ((((uint64_t)type)&0x3) != 0) { - printf(" NON-INTEGER ResTable_type ADDRESS: %p\n", type); - continue; - } - String8 configStr = type->config.toString(); - printf(" config %s:\n", configStr.size() > 0 - ? configStr.string() : "(default)"); - size_t entryCount = dtohl(type->entryCount); - uint32_t entriesStart = dtohl(type->entriesStart); - if ((entriesStart&0x3) != 0) { - printf(" NON-INTEGER ResTable_type entriesStart OFFSET: %p\n", (void*)entriesStart); - continue; - } - uint32_t typeSize = dtohl(type->header.size); - if ((typeSize&0x3) != 0) { - printf(" NON-INTEGER ResTable_type header.size: %p\n", (void*)typeSize); - continue; - } - for (size_t entryIndex=0; entryIndexheader.size); - const uint32_t* const eindex = (const uint32_t*) - (((const uint8_t*)type) + dtohs(type->header.headerSize)); - - uint32_t thisOffset = dtohl(eindex[entryIndex]); - if (thisOffset == ResTable_type::NO_ENTRY) { - continue; - } - - uint32_t resID = (0xff000000 & ((pkg->package->id)<<24)) - | (0x00ff0000 & ((typeIndex+1)<<16)) - | (0x0000ffff & (entryIndex)); - resource_name resName; - if (this->getResourceName(resID, &resName)) { - printf(" resource 0x%08x %s:%s/%s: ", resID, - CHAR16_TO_CSTR(resName.package, resName.packageLen), - CHAR16_TO_CSTR(resName.type, resName.typeLen), - CHAR16_TO_CSTR(resName.name, resName.nameLen)); - } else { - printf(" INVALID RESOURCE 0x%08x: ", resID); - } - if ((thisOffset&0x3) != 0) { - printf("NON-INTEGER OFFSET: %p\n", (void*)thisOffset); - continue; - } - if ((thisOffset+sizeof(ResTable_entry)) > typeSize) { - printf("OFFSET OUT OF BOUNDS: %p+%p (size is %p)\n", - (void*)entriesStart, (void*)thisOffset, - (void*)typeSize); - continue; - } - - const ResTable_entry* ent = (const ResTable_entry*) - (((const uint8_t*)type) + entriesStart + thisOffset); - if (((entriesStart + thisOffset)&0x3) != 0) { - printf("NON-INTEGER ResTable_entry OFFSET: %p\n", - (void*)(entriesStart + thisOffset)); - continue; - } - - 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(""); - bagPtr = (const ResTable_map_entry*)ent; - } else { - 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)value.data, - (int)value.size, (int)value.res0); - } - - if ((dtohs(ent->flags)&ResTable_entry::FLAG_PUBLIC) != 0) { - printf(" (PUBLIC)"); - } - printf("\n"); - - if (inclValues) { - if (valuePtr != NULL) { - printf(" "); - print_value(pkg, value); - } else if (bagPtr != NULL) { - const int N = dtohl(bagPtr->count); - const uint8_t* baseMapPtr = (const uint8_t*)ent; - size_t mapOffset = esize; - const ResTable_map* mapPtr = (ResTable_map*)(baseMapPtr+mapOffset); - printf(" Parent=0x%08x, Count=%d\n", - dtohl(bagPtr->parent.ident), N); - for (int i=0; iname.ident)); - value.copyFrom_dtoh(mapPtr->value); - print_value(pkg, value); - const size_t size = dtohs(mapPtr->value.size); - mapOffset += size + sizeof(*mapPtr)-sizeof(mapPtr->value); - mapPtr = (ResTable_map*)(baseMapPtr+mapOffset); - } - } - } - } - } - } - } - } -} - -#endif // HAVE_ANDROID_OS - -} // namespace android diff --git a/libs/utils/StreamingZipInflater.cpp b/libs/utils/StreamingZipInflater.cpp deleted file mode 100644 index d3fb98d57..000000000 --- a/libs/utils/StreamingZipInflater.cpp +++ /dev/null @@ -1,226 +0,0 @@ -/* - * Copyright (C) 2010 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_NDEBUG 0 -#define LOG_TAG "szipinf" -#include - -#include -#include -#include -#include -#include - -static inline size_t min_of(size_t a, size_t b) { return (a < b) ? a : b; } - -using namespace android; - -/* - * Streaming access to compressed asset data in an open fd - */ -StreamingZipInflater::StreamingZipInflater(int fd, off64_t compDataStart, - size_t uncompSize, size_t compSize) { - mFd = fd; - mDataMap = NULL; - mInFileStart = compDataStart; - mOutTotalSize = uncompSize; - mInTotalSize = compSize; - - mInBufSize = StreamingZipInflater::INPUT_CHUNK_SIZE; - mInBuf = new uint8_t[mInBufSize]; - - mOutBufSize = StreamingZipInflater::OUTPUT_CHUNK_SIZE; - mOutBuf = new uint8_t[mOutBufSize]; - - initInflateState(); -} - -/* - * Streaming access to compressed data held in an mmapped region of memory - */ -StreamingZipInflater::StreamingZipInflater(FileMap* dataMap, size_t uncompSize) { - mFd = -1; - mDataMap = dataMap; - mOutTotalSize = uncompSize; - mInTotalSize = dataMap->getDataLength(); - - mInBuf = (uint8_t*) dataMap->getDataPtr(); - mInBufSize = mInTotalSize; - - mOutBufSize = StreamingZipInflater::OUTPUT_CHUNK_SIZE; - mOutBuf = new uint8_t[mOutBufSize]; - - initInflateState(); -} - -StreamingZipInflater::~StreamingZipInflater() { - // tear down the in-flight zip state just in case - ::inflateEnd(&mInflateState); - - if (mDataMap == NULL) { - delete [] mInBuf; - } - delete [] mOutBuf; -} - -void StreamingZipInflater::initInflateState() { - ALOGV("Initializing inflate state"); - - memset(&mInflateState, 0, sizeof(mInflateState)); - mInflateState.zalloc = Z_NULL; - mInflateState.zfree = Z_NULL; - mInflateState.opaque = Z_NULL; - mInflateState.next_in = (Bytef*)mInBuf; - mInflateState.next_out = (Bytef*) mOutBuf; - mInflateState.avail_out = mOutBufSize; - mInflateState.data_type = Z_UNKNOWN; - - mOutLastDecoded = mOutDeliverable = mOutCurPosition = 0; - mInNextChunkOffset = 0; - mStreamNeedsInit = true; - - if (mDataMap == NULL) { - ::lseek(mFd, mInFileStart, SEEK_SET); - mInflateState.avail_in = 0; // set when a chunk is read in - } else { - mInflateState.avail_in = mInBufSize; - } -} - -/* - * Basic approach: - * - * 1. If we have undelivered uncompressed data, send it. At this point - * either we've satisfied the request, or we've exhausted the available - * output data in mOutBuf. - * - * 2. While we haven't sent enough data to satisfy the request: - * 0. if the request is for more data than exists, bail. - * a. if there is no input data to decode, read some into the input buffer - * and readjust the z_stream input pointers - * b. point the output to the start of the output buffer and decode what we can - * c. deliver whatever output data we can - */ -ssize_t StreamingZipInflater::read(void* outBuf, size_t count) { - uint8_t* dest = (uint8_t*) outBuf; - size_t bytesRead = 0; - size_t toRead = min_of(count, size_t(mOutTotalSize - mOutCurPosition)); - while (toRead > 0) { - // First, write from whatever we already have decoded and ready to go - size_t deliverable = min_of(toRead, mOutLastDecoded - mOutDeliverable); - if (deliverable > 0) { - if (outBuf != NULL) memcpy(dest, mOutBuf + mOutDeliverable, deliverable); - mOutDeliverable += deliverable; - mOutCurPosition += deliverable; - dest += deliverable; - bytesRead += deliverable; - toRead -= deliverable; - } - - // need more data? time to decode some. - if (toRead > 0) { - // if we don't have any data to decode, read some in. If we're working - // from mmapped data this won't happen, because the clipping to total size - // will prevent reading off the end of the mapped input chunk. - if (mInflateState.avail_in == 0) { - int err = readNextChunk(); - if (err < 0) { - ALOGE("Unable to access asset data: %d", err); - if (!mStreamNeedsInit) { - ::inflateEnd(&mInflateState); - initInflateState(); - } - return -1; - } - } - // we know we've drained whatever is in the out buffer now, so just - // start from scratch there, reading all the input we have at present. - mInflateState.next_out = (Bytef*) mOutBuf; - mInflateState.avail_out = mOutBufSize; - - /* - ALOGV("Inflating to outbuf: avail_in=%u avail_out=%u next_in=%p next_out=%p", - mInflateState.avail_in, mInflateState.avail_out, - mInflateState.next_in, mInflateState.next_out); - */ - int result = Z_OK; - if (mStreamNeedsInit) { - ALOGV("Initializing zlib to inflate"); - result = inflateInit2(&mInflateState, -MAX_WBITS); - mStreamNeedsInit = false; - } - if (result == Z_OK) result = ::inflate(&mInflateState, Z_SYNC_FLUSH); - if (result < 0) { - // Whoops, inflation failed - ALOGE("Error inflating asset: %d", result); - ::inflateEnd(&mInflateState); - initInflateState(); - return -1; - } else { - if (result == Z_STREAM_END) { - // we know we have to have reached the target size here and will - // not try to read any further, so just wind things up. - ::inflateEnd(&mInflateState); - } - - // Note how much data we got, and off we go - mOutDeliverable = 0; - mOutLastDecoded = mOutBufSize - mInflateState.avail_out; - } - } - } - return bytesRead; -} - -int StreamingZipInflater::readNextChunk() { - assert(mDataMap == NULL); - - if (mInNextChunkOffset < mInTotalSize) { - size_t toRead = min_of(mInBufSize, mInTotalSize - mInNextChunkOffset); - if (toRead > 0) { - ssize_t didRead = ::read(mFd, mInBuf, toRead); - //ALOGV("Reading input chunk, size %08x didread %08x", toRead, didRead); - if (didRead < 0) { - // TODO: error - ALOGE("Error reading asset data"); - return didRead; - } else { - mInNextChunkOffset += didRead; - mInflateState.next_in = (Bytef*) mInBuf; - mInflateState.avail_in = didRead; - } - } - } - return 0; -} - -// seeking backwards requires uncompressing fom the beginning, so is very -// expensive. seeking forwards only requires uncompressing from the current -// position to the destination. -off64_t StreamingZipInflater::seekAbsolute(off64_t absoluteInputPosition) { - if (absoluteInputPosition < mOutCurPosition) { - // rewind and reprocess the data from the beginning - if (!mStreamNeedsInit) { - ::inflateEnd(&mInflateState); - } - initInflateState(); - read(NULL, absoluteInputPosition); - } else if (absoluteInputPosition > mOutCurPosition) { - read(NULL, absoluteInputPosition - mOutCurPosition); - } - // else if the target position *is* our current position, do nothing - return absoluteInputPosition; -} diff --git a/libs/utils/ZipFileCRO.cpp b/libs/utils/ZipFileCRO.cpp deleted file mode 100644 index c8df8453d..000000000 --- a/libs/utils/ZipFileCRO.cpp +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include - -using namespace android; - -ZipFileCRO ZipFileXRO_open(const char* path) { - ZipFileRO* zip = new ZipFileRO(); - if (zip->open(path) == NO_ERROR) { - return (ZipFileCRO)zip; - } - return NULL; -} - -void ZipFileCRO_destroy(ZipFileCRO zipToken) { - ZipFileRO* zip = (ZipFileRO*)zipToken; - delete zip; -} - -ZipEntryCRO ZipFileCRO_findEntryByName(ZipFileCRO zipToken, - const char* fileName) { - ZipFileRO* zip = (ZipFileRO*)zipToken; - return (ZipEntryCRO)zip->findEntryByName(fileName); -} - -bool ZipFileCRO_getEntryInfo(ZipFileCRO zipToken, ZipEntryRO entryToken, - int* pMethod, size_t* pUncompLen, - size_t* pCompLen, off64_t* pOffset, long* pModWhen, long* pCrc32) { - ZipFileRO* zip = (ZipFileRO*)zipToken; - ZipEntryRO entry = (ZipEntryRO)entryToken; - return zip->getEntryInfo(entry, pMethod, pUncompLen, pCompLen, pOffset, - pModWhen, pCrc32); -} - -bool ZipFileCRO_uncompressEntry(ZipFileCRO zipToken, ZipEntryRO entryToken, int fd) { - ZipFileRO* zip = (ZipFileRO*)zipToken; - ZipEntryRO entry = (ZipEntryRO)entryToken; - return zip->uncompressEntry(entry, fd); -} diff --git a/libs/utils/ZipFileRO.cpp b/libs/utils/ZipFileRO.cpp deleted file mode 100644 index 4b7f1e73f..000000000 --- a/libs/utils/ZipFileRO.cpp +++ /dev/null @@ -1,931 +0,0 @@ -/* - * 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. - */ - -// -// Read-only access to Zip archives, with minimal heap allocation. -// -#define LOG_TAG "zipro" -//#define LOG_NDEBUG 0 -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include - -#if HAVE_PRINTF_ZD -# define ZD "%zd" -# define ZD_TYPE ssize_t -#else -# define ZD "%ld" -# define ZD_TYPE long -#endif - -/* - * We must open binary files using open(path, ... | O_BINARY) under Windows. - * Otherwise strange read errors will happen. - */ -#ifndef O_BINARY -# define O_BINARY 0 -#endif - -/* - * TEMP_FAILURE_RETRY is defined by some, but not all, versions of - * . (Alas, it is not as standard as we'd hoped!) So, if it's - * not already defined, then define it here. - */ -#ifndef TEMP_FAILURE_RETRY -/* Used to retry syscalls that can return EINTR. */ -#define TEMP_FAILURE_RETRY(exp) ({ \ - typeof (exp) _rc; \ - do { \ - _rc = (exp); \ - } while (_rc == -1 && errno == EINTR); \ - _rc; }) -#endif - -using namespace android; - -/* - * Zip file constants. - */ -#define kEOCDSignature 0x06054b50 -#define kEOCDLen 22 -#define kEOCDNumEntries 8 // offset to #of entries in file -#define kEOCDSize 12 // size of the central directory -#define kEOCDFileOffset 16 // offset to central directory - -#define kMaxCommentLen 65535 // longest possible in ushort -#define kMaxEOCDSearch (kMaxCommentLen + kEOCDLen) - -#define kLFHSignature 0x04034b50 -#define kLFHLen 30 // excluding variable-len fields -#define kLFHNameLen 26 // offset to filename length -#define kLFHExtraLen 28 // offset to extra length - -#define kCDESignature 0x02014b50 -#define kCDELen 46 // excluding variable-len fields -#define kCDEMethod 10 // offset to compression method -#define kCDEModWhen 12 // offset to modification timestamp -#define kCDECRC 16 // offset to entry CRC -#define kCDECompLen 20 // offset to compressed length -#define kCDEUncompLen 24 // offset to uncompressed length -#define kCDENameLen 28 // offset to filename length -#define kCDEExtraLen 30 // offset to extra length -#define kCDECommentLen 32 // offset to comment length -#define kCDELocalOffset 42 // offset to local hdr - -/* - * The values we return for ZipEntryRO use 0 as an invalid value, so we - * want to adjust the hash table index by a fixed amount. Using a large - * value helps insure that people don't mix & match arguments, e.g. to - * findEntryByIndex(). - */ -#define kZipEntryAdj 10000 - -ZipFileRO::~ZipFileRO() { - free(mHashTable); - if (mDirectoryMap) - mDirectoryMap->release(); - if (mFd >= 0) - TEMP_FAILURE_RETRY(close(mFd)); - if (mFileName) - free(mFileName); -} - -/* - * Convert a ZipEntryRO to a hash table index, verifying that it's in a - * valid range. - */ -int ZipFileRO::entryToIndex(const ZipEntryRO entry) const -{ - long ent = ((long) entry) - kZipEntryAdj; - if (ent < 0 || ent >= mHashTableSize || mHashTable[ent].name == NULL) { - ALOGW("Invalid ZipEntryRO %p (%ld)\n", entry, ent); - return -1; - } - return ent; -} - - -/* - * Open the specified file read-only. We memory-map the entire thing and - * close the file before returning. - */ -status_t ZipFileRO::open(const char* zipFileName) -{ - int fd = -1; - - assert(mDirectoryMap == NULL); - - /* - * Open and map the specified file. - */ - fd = ::open(zipFileName, O_RDONLY | O_BINARY); - if (fd < 0) { - ALOGW("Unable to open zip '%s': %s\n", zipFileName, strerror(errno)); - return NAME_NOT_FOUND; - } - - mFileLength = lseek64(fd, 0, SEEK_END); - if (mFileLength < kEOCDLen) { - TEMP_FAILURE_RETRY(close(fd)); - return UNKNOWN_ERROR; - } - - if (mFileName != NULL) { - free(mFileName); - } - mFileName = strdup(zipFileName); - - mFd = fd; - - /* - * Find the Central Directory and store its size and number of entries. - */ - if (!mapCentralDirectory()) { - goto bail; - } - - /* - * Verify Central Directory and create data structures for fast access. - */ - if (!parseZipArchive()) { - goto bail; - } - - return OK; - -bail: - free(mFileName); - mFileName = NULL; - TEMP_FAILURE_RETRY(close(fd)); - return UNKNOWN_ERROR; -} - -/* - * Parse the Zip archive, verifying its contents and initializing internal - * data structures. - */ -bool ZipFileRO::mapCentralDirectory(void) -{ - ssize_t readAmount = kMaxEOCDSearch; - if (readAmount > (ssize_t) mFileLength) - readAmount = mFileLength; - - unsigned char* scanBuf = (unsigned char*) malloc(readAmount); - if (scanBuf == NULL) { - ALOGW("couldn't allocate scanBuf: %s", strerror(errno)); - free(scanBuf); - return false; - } - - /* - * Make sure this is a Zip archive. - */ - if (lseek64(mFd, 0, SEEK_SET) != 0) { - ALOGW("seek to start failed: %s", strerror(errno)); - free(scanBuf); - return false; - } - - ssize_t actual = TEMP_FAILURE_RETRY(read(mFd, scanBuf, sizeof(int32_t))); - if (actual != (ssize_t) sizeof(int32_t)) { - ALOGI("couldn't read first signature from zip archive: %s", strerror(errno)); - free(scanBuf); - return false; - } - - { - unsigned int header = get4LE(scanBuf); - if (header == kEOCDSignature) { - ALOGI("Found Zip archive, but it looks empty\n"); - free(scanBuf); - return false; - } else if (header != kLFHSignature) { - ALOGV("Not a Zip archive (found 0x%08x)\n", header); - free(scanBuf); - return false; - } - } - - /* - * Perform the traditional EOCD snipe hunt. - * - * We're searching for the End of Central Directory magic number, - * which appears at the start of the EOCD block. It's followed by - * 18 bytes of EOCD stuff and up to 64KB of archive comment. We - * need to read the last part of the file into a buffer, dig through - * it to find the magic number, parse some values out, and use those - * to determine the extent of the CD. - * - * We start by pulling in the last part of the file. - */ - off64_t searchStart = mFileLength - readAmount; - - if (lseek64(mFd, searchStart, SEEK_SET) != searchStart) { - ALOGW("seek %ld failed: %s\n", (long) searchStart, strerror(errno)); - free(scanBuf); - return false; - } - actual = TEMP_FAILURE_RETRY(read(mFd, scanBuf, readAmount)); - if (actual != (ssize_t) readAmount) { - ALOGW("Zip: read " ZD ", expected " ZD ". Failed: %s\n", - (ZD_TYPE) actual, (ZD_TYPE) readAmount, strerror(errno)); - free(scanBuf); - return false; - } - - /* - * Scan backward for the EOCD magic. In an archive without a trailing - * comment, we'll find it on the first try. (We may want to consider - * doing an initial minimal read; if we don't find it, retry with a - * second read as above.) - */ - int i; - for (i = readAmount - kEOCDLen; i >= 0; i--) { - if (scanBuf[i] == 0x50 && get4LE(&scanBuf[i]) == kEOCDSignature) { - ALOGV("+++ Found EOCD at buf+%d\n", i); - break; - } - } - if (i < 0) { - ALOGD("Zip: EOCD not found, %s is not zip\n", mFileName); - free(scanBuf); - return false; - } - - off64_t eocdOffset = searchStart + i; - const unsigned char* eocdPtr = scanBuf + i; - - assert(eocdOffset < mFileLength); - - /* - * Grab the CD offset and size, and the number of entries in the - * archive. After that, we can release our EOCD hunt buffer. - */ - unsigned int numEntries = get2LE(eocdPtr + kEOCDNumEntries); - unsigned int dirSize = get4LE(eocdPtr + kEOCDSize); - unsigned int dirOffset = get4LE(eocdPtr + kEOCDFileOffset); - free(scanBuf); - - // Verify that they look reasonable. - if ((long long) dirOffset + (long long) dirSize > (long long) eocdOffset) { - ALOGW("bad offsets (dir %ld, size %u, eocd %ld)\n", - (long) dirOffset, dirSize, (long) eocdOffset); - return false; - } - if (numEntries == 0) { - ALOGW("empty archive?\n"); - return false; - } - - ALOGV("+++ numEntries=%d dirSize=%d dirOffset=%d\n", - numEntries, dirSize, dirOffset); - - mDirectoryMap = new FileMap(); - if (mDirectoryMap == NULL) { - ALOGW("Unable to create directory map: %s", strerror(errno)); - return false; - } - - if (!mDirectoryMap->create(mFileName, mFd, dirOffset, dirSize, true)) { - ALOGW("Unable to map '%s' (" ZD " to " ZD "): %s\n", mFileName, - (ZD_TYPE) dirOffset, (ZD_TYPE) (dirOffset + dirSize), strerror(errno)); - return false; - } - - mNumEntries = numEntries; - mDirectoryOffset = dirOffset; - - return true; -} - -bool ZipFileRO::parseZipArchive(void) -{ - bool result = false; - const unsigned char* cdPtr = (const unsigned char*) mDirectoryMap->getDataPtr(); - size_t cdLength = mDirectoryMap->getDataLength(); - int numEntries = mNumEntries; - - /* - * Create hash table. We have a minimum 75% load factor, possibly as - * low as 50% after we round off to a power of 2. - */ - mHashTableSize = roundUpPower2(1 + (numEntries * 4) / 3); - mHashTable = (HashEntry*) calloc(mHashTableSize, sizeof(HashEntry)); - - /* - * Walk through the central directory, adding entries to the hash - * table. - */ - const unsigned char* ptr = cdPtr; - for (int i = 0; i < numEntries; i++) { - if (get4LE(ptr) != kCDESignature) { - ALOGW("Missed a central dir sig (at %d)\n", i); - goto bail; - } - if (ptr + kCDELen > cdPtr + cdLength) { - ALOGW("Ran off the end (at %d)\n", i); - goto bail; - } - - long localHdrOffset = (long) get4LE(ptr + kCDELocalOffset); - if (localHdrOffset >= mDirectoryOffset) { - ALOGW("bad LFH offset %ld at entry %d\n", localHdrOffset, i); - goto bail; - } - - unsigned int fileNameLen, extraLen, commentLen, hash; - - fileNameLen = get2LE(ptr + kCDENameLen); - extraLen = get2LE(ptr + kCDEExtraLen); - commentLen = get2LE(ptr + kCDECommentLen); - - /* add the CDE filename to the hash table */ - hash = computeHash((const char*)ptr + kCDELen, fileNameLen); - addToHash((const char*)ptr + kCDELen, fileNameLen, hash); - - ptr += kCDELen + fileNameLen + extraLen + commentLen; - if ((size_t)(ptr - cdPtr) > cdLength) { - ALOGW("bad CD advance (%d vs " ZD ") at entry %d\n", - (int) (ptr - cdPtr), (ZD_TYPE) cdLength, i); - goto bail; - } - } - ALOGV("+++ zip good scan %d entries\n", numEntries); - result = true; - -bail: - return result; -} - -/* - * Simple string hash function for non-null-terminated strings. - */ -/*static*/ unsigned int ZipFileRO::computeHash(const char* str, int len) -{ - unsigned int hash = 0; - - while (len--) - hash = hash * 31 + *str++; - - return hash; -} - -/* - * Add a new entry to the hash table. - */ -void ZipFileRO::addToHash(const char* str, int strLen, unsigned int hash) -{ - int ent = hash & (mHashTableSize-1); - - /* - * We over-allocate the table, so we're guaranteed to find an empty slot. - */ - while (mHashTable[ent].name != NULL) - ent = (ent + 1) & (mHashTableSize-1); - - mHashTable[ent].name = str; - mHashTable[ent].nameLen = strLen; -} - -/* - * Find a matching entry. - * - * Returns NULL if not found. - */ -ZipEntryRO ZipFileRO::findEntryByName(const char* fileName) const -{ - /* - * If the ZipFileRO instance is not initialized, the entry number will - * end up being garbage since mHashTableSize is -1. - */ - if (mHashTableSize <= 0) { - return NULL; - } - - int nameLen = strlen(fileName); - unsigned int hash = computeHash(fileName, nameLen); - int ent = hash & (mHashTableSize-1); - - while (mHashTable[ent].name != NULL) { - if (mHashTable[ent].nameLen == nameLen && - memcmp(mHashTable[ent].name, fileName, nameLen) == 0) - { - /* match */ - return (ZipEntryRO)(long)(ent + kZipEntryAdj); - } - - ent = (ent + 1) & (mHashTableSize-1); - } - - return NULL; -} - -/* - * Find the Nth entry. - * - * This currently involves walking through the sparse hash table, counting - * non-empty entries. If we need to speed this up we can either allocate - * a parallel lookup table or (perhaps better) provide an iterator interface. - */ -ZipEntryRO ZipFileRO::findEntryByIndex(int idx) const -{ - if (idx < 0 || idx >= mNumEntries) { - ALOGW("Invalid index %d\n", idx); - return NULL; - } - - for (int ent = 0; ent < mHashTableSize; ent++) { - if (mHashTable[ent].name != NULL) { - if (idx-- == 0) - return (ZipEntryRO) (ent + kZipEntryAdj); - } - } - - return NULL; -} - -/* - * Get the useful fields from the zip entry. - * - * Returns "false" if the offsets to the fields or the contents of the fields - * appear to be bogus. - */ -bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, size_t* pUncompLen, - size_t* pCompLen, off64_t* pOffset, long* pModWhen, long* pCrc32) const -{ - bool ret = false; - - const int ent = entryToIndex(entry); - if (ent < 0) - return false; - - HashEntry hashEntry = mHashTable[ent]; - - /* - * Recover the start of the central directory entry from the filename - * pointer. The filename is the first entry past the fixed-size data, - * so we can just subtract back from that. - */ - const unsigned char* ptr = (const unsigned char*) hashEntry.name; - off64_t cdOffset = mDirectoryOffset; - - ptr -= kCDELen; - - int method = get2LE(ptr + kCDEMethod); - if (pMethod != NULL) - *pMethod = method; - - if (pModWhen != NULL) - *pModWhen = get4LE(ptr + kCDEModWhen); - if (pCrc32 != NULL) - *pCrc32 = get4LE(ptr + kCDECRC); - - size_t compLen = get4LE(ptr + kCDECompLen); - if (pCompLen != NULL) - *pCompLen = compLen; - size_t uncompLen = get4LE(ptr + kCDEUncompLen); - if (pUncompLen != NULL) - *pUncompLen = uncompLen; - - /* - * If requested, determine the offset of the start of the data. All we - * have is the offset to the Local File Header, which is variable size, - * so we have to read the contents of the struct to figure out where - * the actual data starts. - * - * We also need to make sure that the lengths are not so large that - * somebody trying to map the compressed or uncompressed data runs - * off the end of the mapped region. - * - * Note we don't verify compLen/uncompLen if they don't request the - * dataOffset, because dataOffset is expensive to determine. However, - * if they don't have the file offset, they're not likely to be doing - * anything with the contents. - */ - if (pOffset != NULL) { - long localHdrOffset = get4LE(ptr + kCDELocalOffset); - if (localHdrOffset + kLFHLen >= cdOffset) { - ALOGE("ERROR: bad local hdr offset in zip\n"); - return false; - } - - unsigned char lfhBuf[kLFHLen]; - -#ifdef HAVE_PREAD - /* - * This file descriptor might be from zygote's preloaded assets, - * so we need to do an pread64() instead of a lseek64() + read() to - * guarantee atomicity across the processes with the shared file - * descriptors. - */ - ssize_t actual = - TEMP_FAILURE_RETRY(pread64(mFd, lfhBuf, sizeof(lfhBuf), localHdrOffset)); - - if (actual != sizeof(lfhBuf)) { - ALOGW("failed reading lfh from offset %ld\n", localHdrOffset); - return false; - } - - if (get4LE(lfhBuf) != kLFHSignature) { - ALOGW("didn't find signature at start of lfh; wanted: offset=%ld data=0x%08x; " - "got: data=0x%08lx\n", - localHdrOffset, kLFHSignature, get4LE(lfhBuf)); - return false; - } -#else /* HAVE_PREAD */ - /* - * For hosts don't have pread64() we cannot guarantee atomic reads from - * an offset in a file. Android should never run on those platforms. - * File descriptors inherited from a fork() share file offsets and - * there would be nothing to protect from two different processes - * calling lseek64() concurrently. - */ - - { - AutoMutex _l(mFdLock); - - if (lseek64(mFd, localHdrOffset, SEEK_SET) != localHdrOffset) { - ALOGW("failed seeking to lfh at offset %ld\n", localHdrOffset); - return false; - } - - ssize_t actual = - TEMP_FAILURE_RETRY(read(mFd, lfhBuf, sizeof(lfhBuf))); - if (actual != sizeof(lfhBuf)) { - ALOGW("failed reading lfh from offset %ld\n", localHdrOffset); - return false; - } - - if (get4LE(lfhBuf) != kLFHSignature) { - off64_t actualOffset = lseek64(mFd, 0, SEEK_CUR); - ALOGW("didn't find signature at start of lfh; wanted: offset=%ld data=0x%08x; " - "got: offset=" ZD " data=0x%08lx\n", - localHdrOffset, kLFHSignature, (ZD_TYPE) actualOffset, get4LE(lfhBuf)); - return false; - } - } -#endif /* HAVE_PREAD */ - - off64_t dataOffset = localHdrOffset + kLFHLen - + get2LE(lfhBuf + kLFHNameLen) + get2LE(lfhBuf + kLFHExtraLen); - if (dataOffset >= cdOffset) { - ALOGW("bad data offset %ld in zip\n", (long) dataOffset); - return false; - } - - /* check lengths */ - if ((off64_t)(dataOffset + compLen) > cdOffset) { - ALOGW("bad compressed length in zip (%ld + " ZD " > %ld)\n", - (long) dataOffset, (ZD_TYPE) compLen, (long) cdOffset); - return false; - } - - if (method == kCompressStored && - (off64_t)(dataOffset + uncompLen) > cdOffset) - { - ALOGE("ERROR: bad uncompressed length in zip (%ld + " ZD " > %ld)\n", - (long) dataOffset, (ZD_TYPE) uncompLen, (long) cdOffset); - return false; - } - - *pOffset = dataOffset; - } - - return true; -} - -/* - * Copy the entry's filename to the buffer. - */ -int ZipFileRO::getEntryFileName(ZipEntryRO entry, char* buffer, int bufLen) - const -{ - int ent = entryToIndex(entry); - if (ent < 0) - return -1; - - int nameLen = mHashTable[ent].nameLen; - if (bufLen < nameLen+1) - return nameLen+1; - - memcpy(buffer, mHashTable[ent].name, nameLen); - buffer[nameLen] = '\0'; - return 0; -} - -/* - * Create a new FileMap object that spans the data in "entry". - */ -FileMap* ZipFileRO::createEntryFileMap(ZipEntryRO entry) const -{ - /* - * TODO: the efficient way to do this is to modify FileMap to allow - * sub-regions of a file to be mapped. A reference-counting scheme - * can manage the base memory mapping. For now, we just create a brand - * new mapping off of the Zip archive file descriptor. - */ - - FileMap* newMap; - size_t compLen; - off64_t offset; - - if (!getEntryInfo(entry, NULL, NULL, &compLen, &offset, NULL, NULL)) - return NULL; - - newMap = new FileMap(); - if (!newMap->create(mFileName, mFd, offset, compLen, true)) { - newMap->release(); - return NULL; - } - - return newMap; -} - -/* - * Uncompress an entry, in its entirety, into the provided output buffer. - * - * This doesn't verify the data's CRC, which might be useful for - * uncompressed data. The caller should be able to manage it. - */ -bool ZipFileRO::uncompressEntry(ZipEntryRO entry, void* buffer) const -{ - const size_t kSequentialMin = 32768; - bool result = false; - int ent = entryToIndex(entry); - if (ent < 0) - return -1; - - int method; - size_t uncompLen, compLen; - off64_t offset; - const unsigned char* ptr; - - getEntryInfo(entry, &method, &uncompLen, &compLen, &offset, NULL, NULL); - - FileMap* file = createEntryFileMap(entry); - if (file == NULL) { - goto bail; - } - - ptr = (const unsigned char*) file->getDataPtr(); - - /* - * Experiment with madvise hint. When we want to uncompress a file, - * we pull some stuff out of the central dir entry and then hit a - * bunch of compressed or uncompressed data sequentially. The CDE - * visit will cause a limited amount of read-ahead because it's at - * the end of the file. We could end up doing lots of extra disk - * access if the file we're prying open is small. Bottom line is we - * probably don't want to turn MADV_SEQUENTIAL on and leave it on. - * - * So, if the compressed size of the file is above a certain minimum - * size, temporarily boost the read-ahead in the hope that the extra - * pair of system calls are negated by a reduction in page faults. - */ - if (compLen > kSequentialMin) - file->advise(FileMap::SEQUENTIAL); - - if (method == kCompressStored) { - memcpy(buffer, ptr, uncompLen); - } else { - if (!inflateBuffer(buffer, ptr, uncompLen, compLen)) - goto unmap; - } - - if (compLen > kSequentialMin) - file->advise(FileMap::NORMAL); - - result = true; - -unmap: - file->release(); -bail: - return result; -} - -/* - * Uncompress an entry, in its entirety, to an open file descriptor. - * - * This doesn't verify the data's CRC, but probably should. - */ -bool ZipFileRO::uncompressEntry(ZipEntryRO entry, int fd) const -{ - bool result = false; - int ent = entryToIndex(entry); - if (ent < 0) - return -1; - - int method; - size_t uncompLen, compLen; - off64_t offset; - const unsigned char* ptr; - - getEntryInfo(entry, &method, &uncompLen, &compLen, &offset, NULL, NULL); - - FileMap* file = createEntryFileMap(entry); - if (file == NULL) { - goto bail; - } - - ptr = (const unsigned char*) file->getDataPtr(); - - if (method == kCompressStored) { - ssize_t actual = write(fd, ptr, uncompLen); - if (actual < 0) { - ALOGE("Write failed: %s\n", strerror(errno)); - goto unmap; - } else if ((size_t) actual != uncompLen) { - ALOGE("Partial write during uncompress (" ZD " of " ZD ")\n", - (ZD_TYPE) actual, (ZD_TYPE) uncompLen); - goto unmap; - } else { - ALOGI("+++ successful write\n"); - } - } else { - if (!inflateBuffer(fd, ptr, uncompLen, compLen)) - goto unmap; - } - - result = true; - -unmap: - file->release(); -bail: - return result; -} - -/* - * Uncompress "deflate" data from one buffer to another. - */ -/*static*/ bool ZipFileRO::inflateBuffer(void* outBuf, const void* inBuf, - size_t uncompLen, size_t compLen) -{ - bool result = false; - z_stream zstream; - int zerr; - - /* - * Initialize the zlib stream struct. - */ - memset(&zstream, 0, sizeof(zstream)); - zstream.zalloc = Z_NULL; - zstream.zfree = Z_NULL; - zstream.opaque = Z_NULL; - zstream.next_in = (Bytef*)inBuf; - zstream.avail_in = compLen; - zstream.next_out = (Bytef*) outBuf; - zstream.avail_out = uncompLen; - zstream.data_type = Z_UNKNOWN; - - /* - * Use the undocumented "negative window bits" feature to tell zlib - * that there's no zlib header waiting for it. - */ - zerr = inflateInit2(&zstream, -MAX_WBITS); - if (zerr != Z_OK) { - if (zerr == Z_VERSION_ERROR) { - ALOGE("Installed zlib is not compatible with linked version (%s)\n", - ZLIB_VERSION); - } else { - ALOGE("Call to inflateInit2 failed (zerr=%d)\n", zerr); - } - goto bail; - } - - /* - * Expand data. - */ - zerr = inflate(&zstream, Z_FINISH); - if (zerr != Z_STREAM_END) { - ALOGW("Zip inflate failed, zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)\n", - zerr, zstream.next_in, zstream.avail_in, - zstream.next_out, zstream.avail_out); - goto z_bail; - } - - /* paranoia */ - if (zstream.total_out != uncompLen) { - ALOGW("Size mismatch on inflated file (%ld vs " ZD ")\n", - zstream.total_out, (ZD_TYPE) uncompLen); - goto z_bail; - } - - result = true; - -z_bail: - inflateEnd(&zstream); /* free up any allocated structures */ - -bail: - return result; -} - -/* - * Uncompress "deflate" data from one buffer to an open file descriptor. - */ -/*static*/ bool ZipFileRO::inflateBuffer(int fd, const void* inBuf, - size_t uncompLen, size_t compLen) -{ - bool result = false; - const size_t kWriteBufSize = 32768; - unsigned char writeBuf[kWriteBufSize]; - z_stream zstream; - int zerr; - - /* - * Initialize the zlib stream struct. - */ - memset(&zstream, 0, sizeof(zstream)); - zstream.zalloc = Z_NULL; - zstream.zfree = Z_NULL; - zstream.opaque = Z_NULL; - zstream.next_in = (Bytef*)inBuf; - zstream.avail_in = compLen; - zstream.next_out = (Bytef*) writeBuf; - zstream.avail_out = sizeof(writeBuf); - zstream.data_type = Z_UNKNOWN; - - /* - * Use the undocumented "negative window bits" feature to tell zlib - * that there's no zlib header waiting for it. - */ - zerr = inflateInit2(&zstream, -MAX_WBITS); - if (zerr != Z_OK) { - if (zerr == Z_VERSION_ERROR) { - ALOGE("Installed zlib is not compatible with linked version (%s)\n", - ZLIB_VERSION); - } else { - ALOGE("Call to inflateInit2 failed (zerr=%d)\n", zerr); - } - goto bail; - } - - /* - * Loop while we have more to do. - */ - do { - /* - * Expand data. - */ - zerr = inflate(&zstream, Z_NO_FLUSH); - if (zerr != Z_OK && zerr != Z_STREAM_END) { - ALOGW("zlib inflate: zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)\n", - zerr, zstream.next_in, zstream.avail_in, - zstream.next_out, zstream.avail_out); - goto z_bail; - } - - /* write when we're full or when we're done */ - if (zstream.avail_out == 0 || - (zerr == Z_STREAM_END && zstream.avail_out != sizeof(writeBuf))) - { - long writeSize = zstream.next_out - writeBuf; - int cc = write(fd, writeBuf, writeSize); - if (cc != (int) writeSize) { - ALOGW("write failed in inflate (%d vs %ld)\n", cc, writeSize); - goto z_bail; - } - - zstream.next_out = writeBuf; - zstream.avail_out = sizeof(writeBuf); - } - } while (zerr == Z_OK); - - assert(zerr == Z_STREAM_END); /* other errors should've been caught */ - - /* paranoia */ - if (zstream.total_out != uncompLen) { - ALOGW("Size mismatch on inflated file (%ld vs " ZD ")\n", - zstream.total_out, (ZD_TYPE) uncompLen); - goto z_bail; - } - - result = true; - -z_bail: - inflateEnd(&zstream); /* free up any allocated structures */ - -bail: - return result; -} diff --git a/libs/utils/ZipUtils.cpp b/libs/utils/ZipUtils.cpp deleted file mode 100644 index db3479d33..000000000 --- a/libs/utils/ZipUtils.cpp +++ /dev/null @@ -1,343 +0,0 @@ -/* - * 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. - */ - -// -// Misc zip/gzip utility functions. -// - -#define LOG_TAG "ziputil" - -#include -#include -#include - -#include -#include -#include - -#include - -using namespace android; - -/* - * Utility function that expands zip/gzip "deflate" compressed data - * into a buffer. - * - * "fd" is an open file positioned at the start of the "deflate" data - * "buf" must hold at least "uncompressedLen" bytes. - */ -/*static*/ bool ZipUtils::inflateToBuffer(int fd, void* buf, - long uncompressedLen, long compressedLen) -{ - bool result = false; - const unsigned long kReadBufSize = 32768; - unsigned char* readBuf = NULL; - z_stream zstream; - int zerr; - unsigned long compRemaining; - - assert(uncompressedLen >= 0); - assert(compressedLen >= 0); - - readBuf = new unsigned char[kReadBufSize]; - if (readBuf == NULL) - goto bail; - compRemaining = compressedLen; - - /* - * Initialize the zlib stream. - */ - memset(&zstream, 0, sizeof(zstream)); - zstream.zalloc = Z_NULL; - zstream.zfree = Z_NULL; - zstream.opaque = Z_NULL; - zstream.next_in = NULL; - zstream.avail_in = 0; - zstream.next_out = (Bytef*) buf; - zstream.avail_out = uncompressedLen; - zstream.data_type = Z_UNKNOWN; - - /* - * Use the undocumented "negative window bits" feature to tell zlib - * that there's no zlib header waiting for it. - */ - zerr = inflateInit2(&zstream, -MAX_WBITS); - if (zerr != Z_OK) { - if (zerr == Z_VERSION_ERROR) { - ALOGE("Installed zlib is not compatible with linked version (%s)\n", - ZLIB_VERSION); - } else { - ALOGE("Call to inflateInit2 failed (zerr=%d)\n", zerr); - } - goto bail; - } - - /* - * Loop while we have data. - */ - do { - unsigned long getSize; - - /* read as much as we can */ - if (zstream.avail_in == 0) { - getSize = (compRemaining > kReadBufSize) ? - kReadBufSize : compRemaining; - ALOGV("+++ reading %ld bytes (%ld left)\n", - getSize, compRemaining); - - int cc = read(fd, readBuf, getSize); - if (cc != (int) getSize) { - ALOGD("inflate read failed (%d vs %ld)\n", - cc, getSize); - goto z_bail; - } - - compRemaining -= getSize; - - zstream.next_in = readBuf; - zstream.avail_in = getSize; - } - - /* uncompress the data */ - zerr = inflate(&zstream, Z_NO_FLUSH); - if (zerr != Z_OK && zerr != Z_STREAM_END) { - ALOGD("zlib inflate call failed (zerr=%d)\n", zerr); - goto z_bail; - } - - /* output buffer holds all, so no need to write the output */ - } while (zerr == Z_OK); - - assert(zerr == Z_STREAM_END); /* other errors should've been caught */ - - if ((long) zstream.total_out != uncompressedLen) { - ALOGW("Size mismatch on inflated file (%ld vs %ld)\n", - zstream.total_out, uncompressedLen); - goto z_bail; - } - - // success! - result = true; - -z_bail: - inflateEnd(&zstream); /* free up any allocated structures */ - -bail: - delete[] readBuf; - return result; -} - -/* - * Utility function that expands zip/gzip "deflate" compressed data - * into a buffer. - * - * (This is a clone of the previous function, but it takes a FILE* instead - * of an fd. We could pass fileno(fd) to the above, but we can run into - * trouble when "fp" has a different notion of what fd's file position is.) - * - * "fp" is an open file positioned at the start of the "deflate" data - * "buf" must hold at least "uncompressedLen" bytes. - */ -/*static*/ bool ZipUtils::inflateToBuffer(FILE* fp, void* buf, - long uncompressedLen, long compressedLen) -{ - bool result = false; - const unsigned long kReadBufSize = 32768; - unsigned char* readBuf = NULL; - z_stream zstream; - int zerr; - unsigned long compRemaining; - - assert(uncompressedLen >= 0); - assert(compressedLen >= 0); - - readBuf = new unsigned char[kReadBufSize]; - if (readBuf == NULL) - goto bail; - compRemaining = compressedLen; - - /* - * Initialize the zlib stream. - */ - memset(&zstream, 0, sizeof(zstream)); - zstream.zalloc = Z_NULL; - zstream.zfree = Z_NULL; - zstream.opaque = Z_NULL; - zstream.next_in = NULL; - zstream.avail_in = 0; - zstream.next_out = (Bytef*) buf; - zstream.avail_out = uncompressedLen; - zstream.data_type = Z_UNKNOWN; - - /* - * Use the undocumented "negative window bits" feature to tell zlib - * that there's no zlib header waiting for it. - */ - zerr = inflateInit2(&zstream, -MAX_WBITS); - if (zerr != Z_OK) { - if (zerr == Z_VERSION_ERROR) { - ALOGE("Installed zlib is not compatible with linked version (%s)\n", - ZLIB_VERSION); - } else { - ALOGE("Call to inflateInit2 failed (zerr=%d)\n", zerr); - } - goto bail; - } - - /* - * Loop while we have data. - */ - do { - unsigned long getSize; - - /* read as much as we can */ - if (zstream.avail_in == 0) { - getSize = (compRemaining > kReadBufSize) ? - kReadBufSize : compRemaining; - ALOGV("+++ reading %ld bytes (%ld left)\n", - getSize, compRemaining); - - int cc = fread(readBuf, 1, getSize, fp); - if (cc != (int) getSize) { - ALOGD("inflate read failed (%d vs %ld)\n", - cc, getSize); - goto z_bail; - } - - compRemaining -= getSize; - - zstream.next_in = readBuf; - zstream.avail_in = getSize; - } - - /* uncompress the data */ - zerr = inflate(&zstream, Z_NO_FLUSH); - if (zerr != Z_OK && zerr != Z_STREAM_END) { - ALOGD("zlib inflate call failed (zerr=%d)\n", zerr); - goto z_bail; - } - - /* output buffer holds all, so no need to write the output */ - } while (zerr == Z_OK); - - assert(zerr == Z_STREAM_END); /* other errors should've been caught */ - - if ((long) zstream.total_out != uncompressedLen) { - ALOGW("Size mismatch on inflated file (%ld vs %ld)\n", - zstream.total_out, uncompressedLen); - goto z_bail; - } - - // success! - result = true; - -z_bail: - inflateEnd(&zstream); /* free up any allocated structures */ - -bail: - delete[] readBuf; - return result; -} - -/* - * Look at the contents of a gzip archive. We want to know where the - * data starts, and how long it will be after it is uncompressed. - * - * We expect to find the CRC and length as the last 8 bytes on the file. - * This is a pretty reasonable thing to expect for locally-compressed - * files, but there's a small chance that some extra padding got thrown - * on (the man page talks about compressed data written to tape). We - * don't currently deal with that here. If "gzip -l" whines, we're going - * to fail too. - * - * On exit, "fp" is pointing at the start of the compressed data. - */ -/*static*/ bool ZipUtils::examineGzip(FILE* fp, int* pCompressionMethod, - long* pUncompressedLen, long* pCompressedLen, unsigned long* pCRC32) -{ - enum { // flags - FTEXT = 0x01, - FHCRC = 0x02, - FEXTRA = 0x04, - FNAME = 0x08, - FCOMMENT = 0x10, - }; - int ic; - int method, flags; - int i; - - ic = getc(fp); - if (ic != 0x1f || getc(fp) != 0x8b) - return false; // not gzip - method = getc(fp); - flags = getc(fp); - - /* quick sanity checks */ - if (method == EOF || flags == EOF) - return false; - if (method != ZipFileRO::kCompressDeflated) - return false; - - /* skip over 4 bytes of mod time, 1 byte XFL, 1 byte OS */ - for (i = 0; i < 6; i++) - (void) getc(fp); - /* consume "extra" field, if present */ - if ((flags & FEXTRA) != 0) { - int len; - - len = getc(fp); - len |= getc(fp) << 8; - while (len-- && getc(fp) != EOF) - ; - } - /* consume filename, if present */ - if ((flags & FNAME) != 0) { - do { - ic = getc(fp); - } while (ic != 0 && ic != EOF); - } - /* consume comment, if present */ - if ((flags & FCOMMENT) != 0) { - do { - ic = getc(fp); - } while (ic != 0 && ic != EOF); - } - /* consume 16-bit header CRC, if present */ - if ((flags & FHCRC) != 0) { - (void) getc(fp); - (void) getc(fp); - } - - if (feof(fp) || ferror(fp)) - return false; - - /* seek to the end; CRC and length are in the last 8 bytes */ - long curPosn = ftell(fp); - unsigned char buf[8]; - fseek(fp, -8, SEEK_END); - *pCompressedLen = ftell(fp) - curPosn; - - if (fread(buf, 1, 8, fp) != 8) - return false; - /* seek back to start of compressed data */ - fseek(fp, curPosn, SEEK_SET); - - *pCompressionMethod = method; - *pCRC32 = ZipFileRO::get4LE(&buf[0]); - *pUncompressedLen = ZipFileRO::get4LE(&buf[4]); - - return true; -} diff --git a/libs/utils/tests/Android.mk b/libs/utils/tests/Android.mk index 58230f429..a6811fcf8 100644 --- a/libs/utils/tests/Android.mk +++ b/libs/utils/tests/Android.mk @@ -7,10 +7,8 @@ test_src_files := \ BasicHashtable_test.cpp \ BlobCache_test.cpp \ Looper_test.cpp \ - ObbFile_test.cpp \ String8_test.cpp \ Unicode_test.cpp \ - ZipFileRO_test.cpp \ shared_libraries := \ libz \ diff --git a/libs/utils/tests/ObbFile_test.cpp b/libs/utils/tests/ObbFile_test.cpp deleted file mode 100644 index 09d4d7d98..000000000 --- a/libs/utils/tests/ObbFile_test.cpp +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (C) 2010 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 "ObbFile_test" -#include -#include -#include -#include - -#include - -#include -#include - -namespace android { - -#define TEST_FILENAME "/test.obb" - -class ObbFileTest : public testing::Test { -protected: - sp mObbFile; - char* mExternalStorage; - char* mFileName; - - virtual void SetUp() { - mObbFile = new ObbFile(); - mExternalStorage = getenv("EXTERNAL_STORAGE"); - - const int totalLen = strlen(mExternalStorage) + strlen(TEST_FILENAME) + 1; - mFileName = new char[totalLen]; - snprintf(mFileName, totalLen, "%s%s", mExternalStorage, TEST_FILENAME); - - int fd = ::open(mFileName, O_CREAT | O_TRUNC); - if (fd < 0) { - FAIL() << "Couldn't create " << mFileName << " for tests"; - } - } - - virtual void TearDown() { - } -}; - -TEST_F(ObbFileTest, ReadFailure) { - EXPECT_FALSE(mObbFile->readFrom(-1)) - << "No failure on invalid file descriptor"; -} - -TEST_F(ObbFileTest, WriteThenRead) { - const char* packageName = "com.example.obbfile"; - const int32_t versionNum = 1; - - mObbFile->setPackageName(String8(packageName)); - mObbFile->setVersion(versionNum); -#define SALT_SIZE 8 - unsigned char salt[SALT_SIZE] = {0x01, 0x10, 0x55, 0xAA, 0xFF, 0x00, 0x5A, 0xA5}; - EXPECT_TRUE(mObbFile->setSalt(salt, SALT_SIZE)) - << "Salt should be successfully set"; - - EXPECT_TRUE(mObbFile->writeTo(mFileName)) - << "couldn't write to fake .obb file"; - - mObbFile = new ObbFile(); - - EXPECT_TRUE(mObbFile->readFrom(mFileName)) - << "couldn't read from fake .obb file"; - - EXPECT_EQ(versionNum, mObbFile->getVersion()) - << "version didn't come out the same as it went in"; - const char* currentPackageName = mObbFile->getPackageName().string(); - EXPECT_STREQ(packageName, currentPackageName) - << "package name didn't come out the same as it went in"; - - size_t saltLen; - const unsigned char* newSalt = mObbFile->getSalt(&saltLen); - - EXPECT_EQ(sizeof(salt), saltLen) - << "salt sizes were not the same"; - - for (int i = 0; i < sizeof(salt); i++) { - EXPECT_EQ(salt[i], newSalt[i]) - << "salt character " << i << " should be equal"; - } - EXPECT_TRUE(memcmp(newSalt, salt, sizeof(salt)) == 0) - << "salts should be the same"; -} - -} diff --git a/libs/utils/tests/ZipFileRO_test.cpp b/libs/utils/tests/ZipFileRO_test.cpp deleted file mode 100644 index 344f97416..000000000 --- a/libs/utils/tests/ZipFileRO_test.cpp +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) 2011 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 "ZipFileRO_test" -#include -#include - -#include - -#include -#include - -namespace android { - -class ZipFileROTest : public testing::Test { -protected: - virtual void SetUp() { - } - - virtual void TearDown() { - } -}; - -TEST_F(ZipFileROTest, ZipTimeConvertSuccess) { - struct tm t; - - // 2011-06-29 14:40:40 - long when = 0x3EDD7514; - - ZipFileRO::zipTimeToTimespec(when, &t); - - EXPECT_EQ(2011, t.tm_year + 1900) - << "Year was improperly converted."; - - EXPECT_EQ(6, t.tm_mon) - << "Month was improperly converted."; - - EXPECT_EQ(29, t.tm_mday) - << "Day was improperly converted."; - - EXPECT_EQ(14, t.tm_hour) - << "Hour was improperly converted."; - - EXPECT_EQ(40, t.tm_min) - << "Minute was improperly converted."; - - EXPECT_EQ(40, t.tm_sec) - << "Second was improperly converted."; -} - -} From b11abadc247e5645832793f3ffff98fe9c6f8480 Mon Sep 17 00:00:00 2001 From: Jamie Gennis Date: Thu, 23 Feb 2012 14:16:05 -0800 Subject: [PATCH 398/541] Add the atrace utility. This change adds a new system binary to help with capturing and dumping kernel traces. Change-Id: If2fc074480f822588a4c171312dc4c04fd305356 --- atrace/Android.mk | 12 ++ atrace/MODULE_LICENSE_APACHE2 | 0 atrace/NOTICE | 190 +++++++++++++++++++++ atrace/atrace.c | 304 ++++++++++++++++++++++++++++++++++ 4 files changed, 506 insertions(+) create mode 100644 atrace/Android.mk create mode 100644 atrace/MODULE_LICENSE_APACHE2 create mode 100644 atrace/NOTICE create mode 100644 atrace/atrace.c diff --git a/atrace/Android.mk b/atrace/Android.mk new file mode 100644 index 000000000..b6c55d4fb --- /dev/null +++ b/atrace/Android.mk @@ -0,0 +1,12 @@ +# Copyright 2012 The Android Open Source Project + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= atrace.c + +LOCAL_MODULE:= atrace + +LOCAL_MODULE_TAGS:= optional + +include $(BUILD_EXECUTABLE) diff --git a/atrace/MODULE_LICENSE_APACHE2 b/atrace/MODULE_LICENSE_APACHE2 new file mode 100644 index 000000000..e69de29bb diff --git a/atrace/NOTICE b/atrace/NOTICE new file mode 100644 index 000000000..c77f135e7 --- /dev/null +++ b/atrace/NOTICE @@ -0,0 +1,190 @@ + + Copyright (c) 2012, 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. + + 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. + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + diff --git a/atrace/atrace.c b/atrace/atrace.c new file mode 100644 index 000000000..3d2a52e55 --- /dev/null +++ b/atrace/atrace.c @@ -0,0 +1,304 @@ +/* + * Copyright (C) 2012 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 +#include +#include +#include +#include +#include +#include +#include +#include + +/* Command line options */ +static int g_traceDurationSeconds = 5; +static bool g_traceSchedSwitch = false; +static bool g_traceWorkqueue = false; +static bool g_traceOverwrite = false; + +/* Global state */ +static bool g_traceAborted = false; + +/* Sys file paths */ +static const char* k_traceClockPath = + "/sys/kernel/debug/tracing/trace_clock"; + +static const char* k_tracingOverwriteEnablePath = + "/sys/kernel/debug/tracing/options/overwrite"; + +static const char* k_schedSwitchEnablePath = + "/sys/kernel/debug/tracing/events/sched/sched_switch/enable"; + +static const char* k_workqueueEnablePath = + "/sys/kernel/debug/tracing/events/workqueue/enable"; + +static const char* k_tracingOnPath = + "/sys/kernel/debug/tracing/tracing_on"; + +static const char* k_tracePath = + "/sys/kernel/debug/tracing/trace"; + +static const char* k_traceMarkerPath = + "/sys/kernel/debug/tracing/trace_marker"; + +// Write a string to a file, returning true if the write was successful. +bool writeStr(const char* filename, const char* str) +{ + int fd = open(filename, O_WRONLY); + if (fd == -1) { + fprintf(stderr, "error opening %s: %s (%d)\n", filename, + strerror(errno), errno); + return false; + } + + bool ok = true; + ssize_t len = strlen(str); + if (write(fd, str, len) != len) { + fprintf(stderr, "error writing to %s: %s (%d)\n", filename, + strerror(errno), errno); + ok = false; + } + + close(fd); + + return ok; +} + +// Enable or disable a kernel option by writing a "1" or a "0" into a /sys file. +static bool setKernelOptionEnable(const char* filename, bool enable) +{ + return writeStr(filename, enable ? "1" : "0"); +} + +// Enable or disable overwriting of the kernel trace buffers. Disabling this +// will cause tracing to stop once the trace buffers have filled up. +static bool setTraceOverwriteEnable(bool enable) +{ + return setKernelOptionEnable(k_tracingOverwriteEnablePath, enable); +} + +// Enable or disable tracing of the kernel scheduler switching. +static bool setSchedSwitchTracingEnable(bool enable) +{ + return setKernelOptionEnable(k_schedSwitchEnablePath, enable); +} + +// Enable or disable tracing of the kernel workqueues. +static bool setWorkqueueTracingEnabled(bool enable) +{ + return setKernelOptionEnable(k_workqueueEnablePath, enable); +} + +// Enable or disable kernel tracing. +static bool setTracingEnabled(bool enable) +{ + return setKernelOptionEnable(k_tracingOnPath, enable); +} + +// Clear the contents of the kernel trace. +static bool clearTrace() +{ + int traceFD = creat(k_tracePath, 0); + if (traceFD == -1) { + fprintf(stderr, "error truncating %s: %s (%d)\n", k_tracePath, + strerror(errno), errno); + return false; + } + + close(traceFD); + + return true; +} + +// Enable or disable the kernel's use of the global clock. Disabling the global +// clock will result in the kernel using a per-CPU local clock. +static bool setGlobalClockEnable(bool enable) +{ + return writeStr(k_traceClockPath, enable ? "global" : "local"); +} + +// Enable tracing in the kernel. +static bool startTrace() +{ + bool ok = true; + + // Set up the tracing options. + ok &= setTraceOverwriteEnable(g_traceOverwrite); + ok &= setSchedSwitchTracingEnable(g_traceSchedSwitch); + ok &= setWorkqueueTracingEnabled(g_traceWorkqueue); + ok &= setGlobalClockEnable(true); + + // Enable tracing. + ok &= setTracingEnabled(true); + + if (!ok) { + fprintf(stderr, "error: unable to start trace\n"); + } + + return ok; +} + +// Disable tracing in the kernel. +static void stopTrace() +{ + // Disable tracing. + setTracingEnabled(false); + + // Set the options back to their defaults. + setTraceOverwriteEnable(true); + setSchedSwitchTracingEnable(false); + setWorkqueueTracingEnabled(false); + setGlobalClockEnable(false); +} + +// Read the current kernel trace and write it to stdout. +static void dumpTrace() +{ + int traceFD = open(k_tracePath, O_RDWR); + if (traceFD == -1) { + fprintf(stderr, "error opening %s: %s (%d)\n", k_tracePath, + strerror(errno), errno); + return; + } + + ssize_t sent = 0; + while ((sent = sendfile(STDOUT_FILENO, traceFD, NULL, 64*1024*1024)) > 0); + if (sent == -1) { + fprintf(stderr, "error dumping trace: %s (%d)\n", strerror(errno), + errno); + } + + close(traceFD); +} + +// Print the command usage help to stderr. +static void showHelp(const char *cmd) +{ + fprintf(stderr, "usage: %s [options]\n", cmd); + fprintf(stderr, "options include:\n" + " -c trace into a circular buffer\n" + " -s trace the kernel scheduler switches\n" + " -t N trace for N seconds [defualt 5]\n" + " -w trace the kernel workqueue\n"); +} + +static void handleSignal(int signo) { + g_traceAborted = true; +} + +static void registerSigHandler() { + struct sigaction sa; + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + sa.sa_handler = handleSignal; + sigaction(SIGHUP, &sa, NULL); + sigaction(SIGINT, &sa, NULL); + sigaction(SIGQUIT, &sa, NULL); + sigaction(SIGTERM, &sa, NULL); +} + +int main(int argc, char **argv) +{ + if (argc == 2 && 0 == strcmp(argv[1], "--help")) { + showHelp(argv[0]); + exit(0); + } + + if (getuid() != 0) { + fprintf(stderr, "error: %s must be run as root.", argv[0]); + } + + for (;;) { + int ret; + + ret = getopt(argc, argv, "cst:w"); + + if (ret < 0) { + break; + } + + switch(ret) { + case 'c': + g_traceOverwrite = true; + break; + + case 's': + g_traceSchedSwitch = true; + break; + + case 't': + g_traceDurationSeconds = atoi(optarg); + break; + + case 'w': + g_traceWorkqueue = true; + break; + + default: + showHelp(argv[0]); + exit(-1); + break; + } + } + + registerSigHandler(); + + bool ok = startTrace(); + + if (ok) { + printf("capturing trace..."); + fflush(stdout); + + // We clear the trace after starting it because tracing gets enabled for + // each CPU individually in the kernel. Having the beginning of the trace + // contain entries from only one CPU can cause "begin" entries without a + // matching "end" entry to show up if a task gets migrated from one CPU to + // another. + ok = clearTrace(); + + if (ok) { + // Sleep to allow the trace to be captured. + struct timespec timeLeft; + timeLeft.tv_sec = g_traceDurationSeconds; + timeLeft.tv_nsec = 0; + do { + if (g_traceAborted) { + break; + } + } while (nanosleep(&timeLeft, &timeLeft) == -1 && errno == EINTR); + } + } + + // Stop the trace and restore the default settings. + stopTrace(); + + if (ok) { + if (!g_traceAborted) { + printf(" done\nTRACE:\n"); + fflush(stdout); + dumpTrace(); + } else { + printf("\ntrace aborted.\n"); + fflush(stdout); + } + clearTrace(); + } else { + fprintf(stderr, "unable to start tracing\n"); + } + + return g_traceAborted ? 1 : 0; +} From 2ccfe1a0606f59b5cefd177f9dd5c837d0ea2d0b Mon Sep 17 00:00:00 2001 From: Jamie Gennis Date: Thu, 23 Feb 2012 11:28:28 -0800 Subject: [PATCH 399/541] libutils: add a system-wide tracing utility This change adds some utility functionality for doing userland tracing into the kernel trace log. Change-Id: Id0a8cee9ea515b1d8765afd1cecf472a88b4b9e8 --- include/utils/Trace.h | 166 ++++++++++++++++++++++++++++++++++++++++++ libs/utils/Android.mk | 3 +- libs/utils/Trace.cpp | 47 ++++++++++++ 3 files changed, 215 insertions(+), 1 deletion(-) create mode 100644 include/utils/Trace.h create mode 100644 libs/utils/Trace.cpp diff --git a/include/utils/Trace.h b/include/utils/Trace.h new file mode 100644 index 000000000..f33ddf652 --- /dev/null +++ b/include/utils/Trace.h @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2012 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_TRACE_H +#define ANDROID_TRACE_H + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +// The ATRACE_TAG macro can be defined before including this header to trace +// using one of the tags defined below. It must be defined to one of the +// following ATRACE_TAG_* macros. The trace tag is used to filter tracing in +// userland to avoid some of the runtime cost of tracing when it is not desired. +// +// Defining ATRACE_TAG to be ATRACE_TAG_ALWAYS will result in the tracing always +// being enabled - this should ONLY be done for debug code, as userland tracing +// has a performance cost even when the trace is not being recorded. Defining +// ATRACE_TAG to be ATRACE_TAG_NEVER or leaving ATRACE_TAG undefined will result +// in the tracing always being disabled. +#define ATRACE_TAG_NEVER 0 // The "never" tag is never enabled. +#define ATRACE_TAG_ALWAYS (1<<0) // The "always" tag is always enabled. +#define ATRACE_TAG_GRAPHICS (1<<1) +#define ATRACE_TAG_LAST (1<<1) + +#define ATRACE_TAG_INVALID (~((ATRACE_TAG_LAST - 1) | ATRACE_TAG_LAST)) + +#ifndef ATRACE_TAG +#define ATRACE_TAG ATRACE_TAG_NEVER +#elif ATRACE_TAG > ATRACE_TAG_LAST +#error ATRACE_TAG must be defined to be one of the tags defined in utils/Trace.h +#endif + +// ATRACE_CALL traces the beginning and end of the current function. To trace +// the correct start and end times this macro should be the first line of the +// function body. +#define ATRACE_CALL() android::ScopedTrace ___tracer(ATRACE_TAG, __FUNCTION__) + +// ATRACE_INT traces a named integer value. This can be used to track how the +// value changes over time in a trace. +#define ATRACE_INT(name, value) android::Tracer::traceCounter(ATRACE_TAG, name, value) + +namespace android { + +class Tracer { + +public: + + static inline void traceCounter(uint64_t tag, const char* name, + int32_t value) { + if (!android_atomic_acquire_load(&sIsReady)) { + init(); + } + int traceFD = sTraceFD; + if (CC_UNLIKELY(tagEnabled(tag) && traceFD != -1)) { + char buf[1024]; + snprintf(buf, 1024, "C|%d|%s|%d", getpid(), name, value); + write(traceFD, buf, strlen(buf)); + } + } + + static inline void traceBegin(uint64_t tag, const char* name) { + if (CC_UNLIKELY(!android_atomic_acquire_load(&sIsReady))) { + init(); + } + int traceFD = sTraceFD; + if (CC_UNLIKELY(tagEnabled(tag) && (traceFD != -1))) { + char buf[1024]; + size_t len = snprintf(buf, 1024, "B|%d|%s", getpid(), name); + write(traceFD, buf, len); + } + } + + static inline void traceEnd(uint64_t tag) { + if (CC_UNLIKELY(!android_atomic_acquire_load(&sIsReady))) { + init(); + } + int traceFD = sTraceFD; + if (CC_UNLIKELY(tagEnabled(tag) && (traceFD != -1))) { + char buf = 'E'; + write(traceFD, &buf, 1); + } + } + +private: + + static inline bool tagEnabled(uint64_t tag) { + return !(tag & ATRACE_TAG_INVALID) && (tag & sEnabledTags); + } + + // init opens the trace marker file for writing and reads the + // atrace.tags.enableflags system property. It does this only the first + // time it is run, using sMutex for synchronization. + static void init(); + + // sIsReady is a boolean value indicating whether a call to init() has + // completed in this process. It is initialized to 0 and set to 1 when the + // first init() call completes. It is set to 1 even if a failure occurred + // in init (e.g. the trace marker file couldn't be opened). + // + // This should be checked by all tracing functions using an atomic acquire + // load operation before calling init(). This check avoids the need to lock + // a mutex each time a trace function gets called. + static volatile int32_t sIsReady; + + // sTraceFD is the file descriptor used to write to the kernel's trace + // buffer. It is initialized to -1 and set to an open file descriptor in + // init() while a lock on sMutex is held. + // + // This should only be used by a trace function after init() has + // successfully completed. + static int sTraceFD; + + // sEnabledTags is the set of tag bits for which tracing is currently + // enabled. It is initialized to 0 and set based on the + // atrace.tags.enableflags system property in init() while a lock on sMutex + // is held. + // + // This should only be used by a trace function after init() has + // successfully completed. + static uint64_t sEnabledTags; + + // sMutex is used to protect the execution of init(). + static Mutex sMutex; +}; + +class ScopedTrace { + +public: + inline ScopedTrace(uint64_t tag, const char* name) : + mTag(tag) { + Tracer::traceBegin(mTag, name); + } + + inline ~ScopedTrace() { + Tracer::traceEnd(mTag); + } + +private: + + uint64_t mTag; +}; + +}; // namespace android + +#endif // ANDROID_TRACE_H diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk index a96c8e604..57c048a3e 100644 --- a/libs/utils/Android.mk +++ b/libs/utils/Android.mk @@ -77,7 +77,8 @@ include $(CLEAR_VARS) # we have the common sources, plus some device-specific stuff LOCAL_SRC_FILES:= \ $(commonSources) \ - Looper.cpp + Looper.cpp \ + Trace.cpp ifeq ($(TARGET_OS),linux) LOCAL_LDLIBS += -lrt -ldl diff --git a/libs/utils/Trace.cpp b/libs/utils/Trace.cpp new file mode 100644 index 000000000..c49278ae4 --- /dev/null +++ b/libs/utils/Trace.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2012 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 +#include +#include + +namespace android { + +volatile int32_t Tracer::sIsReady = 0; +int Tracer::sTraceFD = -1; +uint64_t Tracer::sEnabledTags = 0; +Mutex Tracer::sMutex; + +void Tracer::init() { + Mutex::Autolock lock(sMutex); + + if (!sIsReady) { + const char* const traceFileName = + "/sys/kernel/debug/tracing/trace_marker"; + sTraceFD = open(traceFileName, O_WRONLY); + if (sTraceFD == -1) { + ALOGE("error opening trace file: %s (%d)", strerror(errno), errno); + } else { + char value[PROPERTY_VALUE_MAX]; + property_get("atrace.tags.enableflags", value, "0"); + sEnabledTags = strtoll(value, NULL, 0) | ATRACE_TAG_ALWAYS; + } + + android_atomic_release_store(1, &sIsReady); + } +} + +} // namespace andoid From 2bd99599bb9eef197e6d1ccacb9f808ebfbcc598 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Sat, 25 Feb 2012 23:02:14 -0800 Subject: [PATCH 400/541] split utils/threads.h into multiple headers AndroidDefs.h - C/C++ enums and types Mutex.h - Mutex class declaration Condition.h - Condition class declaration RWLock.h - RWLock class declaration Thread.h - Thread class declaration AndroidThreads.h - low-level thread creation functions threads.h - includes all the above for backward source compatibility Change-Id: Ia2f80c175333b59a41d1720985810bb2346e94cb Note: implementations are still in Threads.cpp --- include/utils/AndroidThreads.h | 136 ++++++++ include/utils/Condition.h | 134 ++++++++ include/utils/Mutex.h | 137 ++++++++ include/utils/RWLock.h | 126 ++++++++ include/utils/Thread.h | 113 +++++++ include/utils/ThreadDefs.h | 123 ++++++++ include/utils/threads.h | 558 +-------------------------------- 7 files changed, 781 insertions(+), 546 deletions(-) create mode 100644 include/utils/AndroidThreads.h create mode 100644 include/utils/Condition.h create mode 100644 include/utils/Mutex.h create mode 100644 include/utils/RWLock.h create mode 100644 include/utils/Thread.h create mode 100644 include/utils/ThreadDefs.h diff --git a/include/utils/AndroidThreads.h b/include/utils/AndroidThreads.h new file mode 100644 index 000000000..f9f7aa41a --- /dev/null +++ b/include/utils/AndroidThreads.h @@ -0,0 +1,136 @@ +/* + * 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. + */ + +#ifndef _LIBS_UTILS_ANDROID_THREADS_H +#define _LIBS_UTILS_ANDROID_THREADS_H + +#include +#include + +#if defined(HAVE_PTHREADS) +# include +#endif + +#include + +// --------------------------------------------------------------------------- +// C API + +#ifdef __cplusplus +extern "C" { +#endif + +// Create and run a new thread. +extern int androidCreateThread(android_thread_func_t, void *); + +// Create thread with lots of parameters +extern int androidCreateThreadEtc(android_thread_func_t entryFunction, + void *userData, + const char* threadName, + int32_t threadPriority, + size_t threadStackSize, + android_thread_id_t *threadId); + +// Get some sort of unique identifier for the current thread. +extern android_thread_id_t androidGetThreadId(); + +// Low-level thread creation -- never creates threads that can +// interact with the Java VM. +extern int androidCreateRawThreadEtc(android_thread_func_t entryFunction, + void *userData, + const char* threadName, + int32_t threadPriority, + size_t threadStackSize, + android_thread_id_t *threadId); + +// Used by the Java Runtime to control how threads are created, so that +// they can be proper and lovely Java threads. +typedef int (*android_create_thread_fn)(android_thread_func_t entryFunction, + void *userData, + const char* threadName, + int32_t threadPriority, + size_t threadStackSize, + android_thread_id_t *threadId); + +extern void androidSetCreateThreadFunc(android_create_thread_fn func); + +// ------------------------------------------------------------------ +// Extra functions working with raw pids. + +// Get pid for the current thread. +extern pid_t androidGetTid(); + +// Change the scheduling group of a particular thread. The group +// should be one of the ANDROID_TGROUP constants. Returns BAD_VALUE if +// grp is out of range, else another non-zero value with errno set if +// the operation failed. Thread ID zero means current thread. +extern int androidSetThreadSchedulingGroup(pid_t tid, int grp); + +// Change the priority AND scheduling group of a particular thread. The priority +// should be one of the ANDROID_PRIORITY constants. Returns INVALID_OPERATION +// if the priority set failed, else another value if just the group set failed; +// in either case errno is set. Thread ID zero means current thread. +extern int androidSetThreadPriority(pid_t tid, int prio); + +// Get the current priority of a particular thread. Returns one of the +// ANDROID_PRIORITY constants or a negative result in case of error. +extern int androidGetThreadPriority(pid_t tid); + +// Get the current scheduling group of a particular thread. Normally returns +// one of the ANDROID_TGROUP constants other than ANDROID_TGROUP_DEFAULT. +// Returns ANDROID_TGROUP_DEFAULT if no pthread support (e.g. on host) or if +// scheduling groups are disabled. Returns INVALID_OPERATION if unexpected error. +// Thread ID zero means current thread. +extern int androidGetThreadSchedulingGroup(pid_t tid); + +#ifdef __cplusplus +} // extern "C" +#endif + +// ---------------------------------------------------------------------------- +// C++ API +#ifdef __cplusplus +namespace android { +// ---------------------------------------------------------------------------- + +// Create and run a new thread. +inline bool createThread(thread_func_t f, void *a) { + return androidCreateThread(f, a) ? true : false; +} + +// Create thread with lots of parameters +inline bool createThreadEtc(thread_func_t entryFunction, + void *userData, + const char* threadName = "android:unnamed_thread", + int32_t threadPriority = PRIORITY_DEFAULT, + size_t threadStackSize = 0, + thread_id_t *threadId = 0) +{ + return androidCreateThreadEtc(entryFunction, userData, threadName, + threadPriority, threadStackSize, threadId) ? true : false; +} + +// Get some sort of unique identifier for the current thread. +inline thread_id_t getThreadId() { + return androidGetThreadId(); +} + +// ---------------------------------------------------------------------------- +}; // namespace android +#endif // __cplusplus +// ---------------------------------------------------------------------------- + +#endif // _LIBS_UTILS_ANDROID_THREADS_H diff --git a/include/utils/Condition.h b/include/utils/Condition.h new file mode 100644 index 000000000..8852d5333 --- /dev/null +++ b/include/utils/Condition.h @@ -0,0 +1,134 @@ +/* + * 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. + */ + +#ifndef _LIBS_UTILS_CONDITION_H +#define _LIBS_UTILS_CONDITION_H + +#include +#include +#include + +#if defined(HAVE_PTHREADS) +# include +#endif + +#include +#include +#include + +// --------------------------------------------------------------------------- +namespace android { +// --------------------------------------------------------------------------- + +/* + * Condition variable class. The implementation is system-dependent. + * + * Condition variables are paired up with mutexes. Lock the mutex, + * call wait(), then either re-wait() if things aren't quite what you want, + * or unlock the mutex and continue. All threads calling wait() must + * use the same mutex for a given Condition. + */ +class Condition { +public: + enum { + PRIVATE = 0, + SHARED = 1 + }; + + Condition(); + Condition(int type); + ~Condition(); + // Wait on the condition variable. Lock the mutex before calling. + status_t wait(Mutex& mutex); + // same with relative timeout + status_t waitRelative(Mutex& mutex, nsecs_t reltime); + // Signal the condition variable, allowing one thread to continue. + void signal(); + // Signal the condition variable, allowing all threads to continue. + void broadcast(); + +private: +#if defined(HAVE_PTHREADS) + pthread_cond_t mCond; +#else + void* mState; +#endif +}; + +// --------------------------------------------------------------------------- + +#if defined(HAVE_PTHREADS) + +inline Condition::Condition() { + pthread_cond_init(&mCond, NULL); +} +inline Condition::Condition(int type) { + if (type == SHARED) { + pthread_condattr_t attr; + pthread_condattr_init(&attr); + pthread_condattr_setpshared(&attr, PTHREAD_PROCESS_SHARED); + pthread_cond_init(&mCond, &attr); + pthread_condattr_destroy(&attr); + } else { + pthread_cond_init(&mCond, NULL); + } +} +inline Condition::~Condition() { + pthread_cond_destroy(&mCond); +} +inline status_t Condition::wait(Mutex& mutex) { + return -pthread_cond_wait(&mCond, &mutex.mMutex); +} +inline status_t Condition::waitRelative(Mutex& mutex, nsecs_t reltime) { +#if defined(HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE) + struct timespec ts; + ts.tv_sec = reltime/1000000000; + ts.tv_nsec = reltime%1000000000; + return -pthread_cond_timedwait_relative_np(&mCond, &mutex.mMutex, &ts); +#else // HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE + struct timespec ts; +#if defined(HAVE_POSIX_CLOCKS) + clock_gettime(CLOCK_REALTIME, &ts); +#else // HAVE_POSIX_CLOCKS + // we don't support the clocks here. + struct timeval t; + gettimeofday(&t, NULL); + ts.tv_sec = t.tv_sec; + ts.tv_nsec= t.tv_usec*1000; +#endif // HAVE_POSIX_CLOCKS + ts.tv_sec += reltime/1000000000; + ts.tv_nsec+= reltime%1000000000; + if (ts.tv_nsec >= 1000000000) { + ts.tv_nsec -= 1000000000; + ts.tv_sec += 1; + } + return -pthread_cond_timedwait(&mCond, &mutex.mMutex, &ts); +#endif // HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE +} +inline void Condition::signal() { + pthread_cond_signal(&mCond); +} +inline void Condition::broadcast() { + pthread_cond_broadcast(&mCond); +} + +#endif // HAVE_PTHREADS + +// --------------------------------------------------------------------------- +}; // namespace android +// --------------------------------------------------------------------------- + +#endif // _LIBS_UTILS_CONDITON_H diff --git a/include/utils/Mutex.h b/include/utils/Mutex.h new file mode 100644 index 000000000..de6fb394a --- /dev/null +++ b/include/utils/Mutex.h @@ -0,0 +1,137 @@ +/* + * 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. + */ + +#ifndef _LIBS_UTILS_MUTEX_H +#define _LIBS_UTILS_MUTEX_H + +#include +#include +#include + +#if defined(HAVE_PTHREADS) +# include +#endif + +#include + +// --------------------------------------------------------------------------- +namespace android { +// --------------------------------------------------------------------------- + +class Condition; + +/* + * Simple mutex class. The implementation is system-dependent. + * + * The mutex must be unlocked by the thread that locked it. They are not + * recursive, i.e. the same thread can't lock it multiple times. + */ +class Mutex { +public: + enum { + PRIVATE = 0, + SHARED = 1 + }; + + Mutex(); + Mutex(const char* name); + Mutex(int type, const char* name = NULL); + ~Mutex(); + + // lock or unlock the mutex + status_t lock(); + void unlock(); + + // lock if possible; returns 0 on success, error otherwise + status_t tryLock(); + + // Manages the mutex automatically. It'll be locked when Autolock is + // constructed and released when Autolock goes out of scope. + class Autolock { + public: + inline Autolock(Mutex& mutex) : mLock(mutex) { mLock.lock(); } + inline Autolock(Mutex* mutex) : mLock(*mutex) { mLock.lock(); } + inline ~Autolock() { mLock.unlock(); } + private: + Mutex& mLock; + }; + +private: + friend class Condition; + + // A mutex cannot be copied + Mutex(const Mutex&); + Mutex& operator = (const Mutex&); + +#if defined(HAVE_PTHREADS) + pthread_mutex_t mMutex; +#else + void _init(); + void* mState; +#endif +}; + +// --------------------------------------------------------------------------- + +#if defined(HAVE_PTHREADS) + +inline Mutex::Mutex() { + pthread_mutex_init(&mMutex, NULL); +} +inline Mutex::Mutex(const char* name) { + pthread_mutex_init(&mMutex, NULL); +} +inline Mutex::Mutex(int type, const char* name) { + if (type == SHARED) { + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED); + pthread_mutex_init(&mMutex, &attr); + pthread_mutexattr_destroy(&attr); + } else { + pthread_mutex_init(&mMutex, NULL); + } +} +inline Mutex::~Mutex() { + pthread_mutex_destroy(&mMutex); +} +inline status_t Mutex::lock() { + return -pthread_mutex_lock(&mMutex); +} +inline void Mutex::unlock() { + pthread_mutex_unlock(&mMutex); +} +inline status_t Mutex::tryLock() { + return -pthread_mutex_trylock(&mMutex); +} + +#endif // HAVE_PTHREADS + +// --------------------------------------------------------------------------- + +/* + * Automatic mutex. Declare one of these at the top of a function. + * When the function returns, it will go out of scope, and release the + * mutex. + */ + +typedef Mutex::Autolock AutoMutex; + +// --------------------------------------------------------------------------- +}; // namespace android +// --------------------------------------------------------------------------- + +#endif // _LIBS_UTILS_MUTEX_H diff --git a/include/utils/RWLock.h b/include/utils/RWLock.h new file mode 100644 index 000000000..a5abea2b8 --- /dev/null +++ b/include/utils/RWLock.h @@ -0,0 +1,126 @@ +/* + * 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. + */ + +#ifndef _LIBS_UTILS_RWLOCK_H +#define _LIBS_UTILS_RWLOCK_H + +#include +#include + +#if defined(HAVE_PTHREADS) +# include +#endif + +#include +#include + +// --------------------------------------------------------------------------- +namespace android { +// --------------------------------------------------------------------------- + +#if defined(HAVE_PTHREADS) + +/* + * Simple mutex class. The implementation is system-dependent. + * + * The mutex must be unlocked by the thread that locked it. They are not + * recursive, i.e. the same thread can't lock it multiple times. + */ +class RWLock { +public: + enum { + PRIVATE = 0, + SHARED = 1 + }; + + RWLock(); + RWLock(const char* name); + RWLock(int type, const char* name = NULL); + ~RWLock(); + + status_t readLock(); + status_t tryReadLock(); + status_t writeLock(); + status_t tryWriteLock(); + void unlock(); + + class AutoRLock { + public: + inline AutoRLock(RWLock& rwlock) : mLock(rwlock) { mLock.readLock(); } + inline ~AutoRLock() { mLock.unlock(); } + private: + RWLock& mLock; + }; + + class AutoWLock { + public: + inline AutoWLock(RWLock& rwlock) : mLock(rwlock) { mLock.writeLock(); } + inline ~AutoWLock() { mLock.unlock(); } + private: + RWLock& mLock; + }; + +private: + // A RWLock cannot be copied + RWLock(const RWLock&); + RWLock& operator = (const RWLock&); + + pthread_rwlock_t mRWLock; +}; + +inline RWLock::RWLock() { + pthread_rwlock_init(&mRWLock, NULL); +} +inline RWLock::RWLock(const char* name) { + pthread_rwlock_init(&mRWLock, NULL); +} +inline RWLock::RWLock(int type, const char* name) { + if (type == SHARED) { + pthread_rwlockattr_t attr; + pthread_rwlockattr_init(&attr); + pthread_rwlockattr_setpshared(&attr, PTHREAD_PROCESS_SHARED); + pthread_rwlock_init(&mRWLock, &attr); + pthread_rwlockattr_destroy(&attr); + } else { + pthread_rwlock_init(&mRWLock, NULL); + } +} +inline RWLock::~RWLock() { + pthread_rwlock_destroy(&mRWLock); +} +inline status_t RWLock::readLock() { + return -pthread_rwlock_rdlock(&mRWLock); +} +inline status_t RWLock::tryReadLock() { + return -pthread_rwlock_tryrdlock(&mRWLock); +} +inline status_t RWLock::writeLock() { + return -pthread_rwlock_wrlock(&mRWLock); +} +inline status_t RWLock::tryWriteLock() { + return -pthread_rwlock_trywrlock(&mRWLock); +} +inline void RWLock::unlock() { + pthread_rwlock_unlock(&mRWLock); +} + +#endif // HAVE_PTHREADS + +// --------------------------------------------------------------------------- +}; // namespace android +// --------------------------------------------------------------------------- + +#endif // _LIBS_UTILS_RWLOCK_H diff --git a/include/utils/Thread.h b/include/utils/Thread.h new file mode 100644 index 000000000..4a34abd78 --- /dev/null +++ b/include/utils/Thread.h @@ -0,0 +1,113 @@ +/* + * 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. + */ + +#ifndef _LIBS_UTILS_THREAD_H +#define _LIBS_UTILS_THREAD_H + +#include +#include +#include + +#if defined(HAVE_PTHREADS) +# include +#endif + +#include +#include +#include +#include +#include +#include + +// --------------------------------------------------------------------------- +namespace android { +// --------------------------------------------------------------------------- + +class Thread : virtual public RefBase +{ +public: + // Create a Thread object, but doesn't create or start the associated + // thread. See the run() method. + Thread(bool canCallJava = true); + virtual ~Thread(); + + // Start the thread in threadLoop() which needs to be implemented. + virtual status_t run( const char* name = 0, + int32_t priority = PRIORITY_DEFAULT, + size_t stack = 0); + + // Ask this object's thread to exit. This function is asynchronous, when the + // function returns the thread might still be running. Of course, this + // function can be called from a different thread. + virtual void requestExit(); + + // Good place to do one-time initializations + virtual status_t readyToRun(); + + // Call requestExit() and wait until this object's thread exits. + // BE VERY CAREFUL of deadlocks. In particular, it would be silly to call + // this function from this object's thread. Will return WOULD_BLOCK in + // that case. + status_t requestExitAndWait(); + + // Wait until this object's thread exits. Returns immediately if not yet running. + // Do not call from this object's thread; will return WOULD_BLOCK in that case. + status_t join(); + +#ifdef HAVE_ANDROID_OS + // Return the thread's kernel ID, same as the thread itself calling gettid() or + // androidGetTid(), or -1 if the thread is not running. + pid_t getTid() const; +#endif + +protected: + // exitPending() returns true if requestExit() has been called. + bool exitPending() const; + +private: + // Derived class must implement threadLoop(). The thread starts its life + // here. There are two ways of using the Thread object: + // 1) loop: if threadLoop() returns true, it will be called again if + // requestExit() wasn't called. + // 2) once: if threadLoop() returns false, the thread will exit upon return. + virtual bool threadLoop() = 0; + +private: + Thread& operator=(const Thread&); + static int _threadLoop(void* user); + const bool mCanCallJava; + // always hold mLock when reading or writing + thread_id_t mThread; + mutable Mutex mLock; + Condition mThreadExitedCondition; + status_t mStatus; + // note that all accesses of mExitPending and mRunning need to hold mLock + volatile bool mExitPending; + volatile bool mRunning; + sp mHoldSelf; +#ifdef HAVE_ANDROID_OS + // legacy for debugging, not used by getTid() as it is set by the child thread + // and so is not initialized until the child reaches that point + pid_t mTid; +#endif +}; + + +}; // namespace android + +// --------------------------------------------------------------------------- +#endif // _LIBS_UTILS_THREAD_H +// --------------------------------------------------------------------------- diff --git a/include/utils/ThreadDefs.h b/include/utils/ThreadDefs.h new file mode 100644 index 000000000..3e563734e --- /dev/null +++ b/include/utils/ThreadDefs.h @@ -0,0 +1,123 @@ +/* + * 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. + */ + +#ifndef _LIBS_UTILS_THREAD_DEFS_H +#define _LIBS_UTILS_THREAD_DEFS_H + +#include +#include +#include + +// --------------------------------------------------------------------------- +// C API + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void* android_thread_id_t; + +typedef int (*android_thread_func_t)(void*); + +enum { + /* + * *********************************************** + * ** Keep in sync with android.os.Process.java ** + * *********************************************** + * + * This maps directly to the "nice" priorities we use in Android. + * A thread priority should be chosen inverse-proportionally to + * the amount of work the thread is expected to do. The more work + * a thread will do, the less favorable priority it should get so that + * it doesn't starve the system. Threads not behaving properly might + * be "punished" by the kernel. + * Use the levels below when appropriate. Intermediate values are + * acceptable, preferably use the {MORE|LESS}_FAVORABLE constants below. + */ + ANDROID_PRIORITY_LOWEST = 19, + + /* use for background tasks */ + ANDROID_PRIORITY_BACKGROUND = 10, + + /* most threads run at normal priority */ + ANDROID_PRIORITY_NORMAL = 0, + + /* threads currently running a UI that the user is interacting with */ + ANDROID_PRIORITY_FOREGROUND = -2, + + /* the main UI thread has a slightly more favorable priority */ + ANDROID_PRIORITY_DISPLAY = -4, + + /* ui service treads might want to run at a urgent display (uncommon) */ + ANDROID_PRIORITY_URGENT_DISPLAY = HAL_PRIORITY_URGENT_DISPLAY, + + /* all normal audio threads */ + ANDROID_PRIORITY_AUDIO = -16, + + /* service audio threads (uncommon) */ + ANDROID_PRIORITY_URGENT_AUDIO = -19, + + /* should never be used in practice. regular process might not + * be allowed to use this level */ + ANDROID_PRIORITY_HIGHEST = -20, + + ANDROID_PRIORITY_DEFAULT = ANDROID_PRIORITY_NORMAL, + ANDROID_PRIORITY_MORE_FAVORABLE = -1, + ANDROID_PRIORITY_LESS_FAVORABLE = +1, +}; + +enum { + ANDROID_TGROUP_DEFAULT = 0, + ANDROID_TGROUP_BG_NONINTERACT = 1, + ANDROID_TGROUP_FG_BOOST = 2, + ANDROID_TGROUP_MAX = ANDROID_TGROUP_FG_BOOST, +}; + +#ifdef __cplusplus +} // extern "C" +#endif + +// --------------------------------------------------------------------------- +// C++ API +#ifdef __cplusplus +namespace android { +// --------------------------------------------------------------------------- + +typedef android_thread_id_t thread_id_t; +typedef android_thread_func_t thread_func_t; + +enum { + PRIORITY_LOWEST = ANDROID_PRIORITY_LOWEST, + PRIORITY_BACKGROUND = ANDROID_PRIORITY_BACKGROUND, + PRIORITY_NORMAL = ANDROID_PRIORITY_NORMAL, + PRIORITY_FOREGROUND = ANDROID_PRIORITY_FOREGROUND, + PRIORITY_DISPLAY = ANDROID_PRIORITY_DISPLAY, + PRIORITY_URGENT_DISPLAY = ANDROID_PRIORITY_URGENT_DISPLAY, + PRIORITY_AUDIO = ANDROID_PRIORITY_AUDIO, + PRIORITY_URGENT_AUDIO = ANDROID_PRIORITY_URGENT_AUDIO, + PRIORITY_HIGHEST = ANDROID_PRIORITY_HIGHEST, + PRIORITY_DEFAULT = ANDROID_PRIORITY_DEFAULT, + PRIORITY_MORE_FAVORABLE = ANDROID_PRIORITY_MORE_FAVORABLE, + PRIORITY_LESS_FAVORABLE = ANDROID_PRIORITY_LESS_FAVORABLE, +}; + +// --------------------------------------------------------------------------- +}; // namespace android +#endif // __cplusplus +// --------------------------------------------------------------------------- + + +#endif // _LIBS_UTILS_THREAD_DEFS_H diff --git a/include/utils/threads.h b/include/utils/threads.h index b4a8b7c0e..9de338211 100644 --- a/include/utils/threads.h +++ b/include/utils/threads.h @@ -17,556 +17,22 @@ #ifndef _LIBS_UTILS_THREADS_H #define _LIBS_UTILS_THREADS_H -#include -#include -#include -#include +/* + * Please, DO NOT USE! + * + * This file is here only for legacy reasons. Instead, include directly + * the headers you need below. + * + */ -#if defined(HAVE_PTHREADS) -# include -#endif - -// ------------------------------------------------------------------ -// C API +#include #ifdef __cplusplus -extern "C" { -#endif - -typedef void* android_thread_id_t; - -typedef int (*android_thread_func_t)(void*); - -enum { - /* - * *********************************************** - * ** Keep in sync with android.os.Process.java ** - * *********************************************** - * - * This maps directly to the "nice" priorities we use in Android. - * A thread priority should be chosen inverse-proportionally to - * the amount of work the thread is expected to do. The more work - * a thread will do, the less favorable priority it should get so that - * it doesn't starve the system. Threads not behaving properly might - * be "punished" by the kernel. - * Use the levels below when appropriate. Intermediate values are - * acceptable, preferably use the {MORE|LESS}_FAVORABLE constants below. - */ - ANDROID_PRIORITY_LOWEST = 19, - - /* use for background tasks */ - ANDROID_PRIORITY_BACKGROUND = 10, - - /* most threads run at normal priority */ - ANDROID_PRIORITY_NORMAL = 0, - - /* threads currently running a UI that the user is interacting with */ - ANDROID_PRIORITY_FOREGROUND = -2, - - /* the main UI thread has a slightly more favorable priority */ - ANDROID_PRIORITY_DISPLAY = -4, - - /* ui service treads might want to run at a urgent display (uncommon) */ - ANDROID_PRIORITY_URGENT_DISPLAY = HAL_PRIORITY_URGENT_DISPLAY, - - /* all normal audio threads */ - ANDROID_PRIORITY_AUDIO = -16, - - /* service audio threads (uncommon) */ - ANDROID_PRIORITY_URGENT_AUDIO = -19, - - /* should never be used in practice. regular process might not - * be allowed to use this level */ - ANDROID_PRIORITY_HIGHEST = -20, - - ANDROID_PRIORITY_DEFAULT = ANDROID_PRIORITY_NORMAL, - ANDROID_PRIORITY_MORE_FAVORABLE = -1, - ANDROID_PRIORITY_LESS_FAVORABLE = +1, -}; - -enum { - ANDROID_TGROUP_DEFAULT = 0, - ANDROID_TGROUP_BG_NONINTERACT = 1, - ANDROID_TGROUP_FG_BOOST = 2, - ANDROID_TGROUP_MAX = ANDROID_TGROUP_FG_BOOST, -}; - -// Create and run a new thread. -extern int androidCreateThread(android_thread_func_t, void *); - -// Create thread with lots of parameters -extern int androidCreateThreadEtc(android_thread_func_t entryFunction, - void *userData, - const char* threadName, - int32_t threadPriority, - size_t threadStackSize, - android_thread_id_t *threadId); - -// Get some sort of unique identifier for the current thread. -extern android_thread_id_t androidGetThreadId(); - -// Low-level thread creation -- never creates threads that can -// interact with the Java VM. -extern int androidCreateRawThreadEtc(android_thread_func_t entryFunction, - void *userData, - const char* threadName, - int32_t threadPriority, - size_t threadStackSize, - android_thread_id_t *threadId); - -// Used by the Java Runtime to control how threads are created, so that -// they can be proper and lovely Java threads. -typedef int (*android_create_thread_fn)(android_thread_func_t entryFunction, - void *userData, - const char* threadName, - int32_t threadPriority, - size_t threadStackSize, - android_thread_id_t *threadId); - -extern void androidSetCreateThreadFunc(android_create_thread_fn func); - -// ------------------------------------------------------------------ -// Extra functions working with raw pids. - -// Get pid for the current thread. -extern pid_t androidGetTid(); - -// Change the scheduling group of a particular thread. The group -// should be one of the ANDROID_TGROUP constants. Returns BAD_VALUE if -// grp is out of range, else another non-zero value with errno set if -// the operation failed. Thread ID zero means current thread. -extern int androidSetThreadSchedulingGroup(pid_t tid, int grp); - -// Change the priority AND scheduling group of a particular thread. The priority -// should be one of the ANDROID_PRIORITY constants. Returns INVALID_OPERATION -// if the priority set failed, else another value if just the group set failed; -// in either case errno is set. Thread ID zero means current thread. -extern int androidSetThreadPriority(pid_t tid, int prio); - -// Get the current priority of a particular thread. Returns one of the -// ANDROID_PRIORITY constants or a negative result in case of error. -extern int androidGetThreadPriority(pid_t tid); - -// Get the current scheduling group of a particular thread. Normally returns -// one of the ANDROID_TGROUP constants other than ANDROID_TGROUP_DEFAULT. -// Returns ANDROID_TGROUP_DEFAULT if no pthread support (e.g. on host) or if -// scheduling groups are disabled. Returns INVALID_OPERATION if unexpected error. -// Thread ID zero means current thread. -extern int androidGetThreadSchedulingGroup(pid_t tid); - -#ifdef __cplusplus -} -#endif - -// ------------------------------------------------------------------ -// C++ API - -#ifdef __cplusplus - +#include #include -#include -#include - -namespace android { - -typedef android_thread_id_t thread_id_t; - -typedef android_thread_func_t thread_func_t; - -enum { - PRIORITY_LOWEST = ANDROID_PRIORITY_LOWEST, - PRIORITY_BACKGROUND = ANDROID_PRIORITY_BACKGROUND, - PRIORITY_NORMAL = ANDROID_PRIORITY_NORMAL, - PRIORITY_FOREGROUND = ANDROID_PRIORITY_FOREGROUND, - PRIORITY_DISPLAY = ANDROID_PRIORITY_DISPLAY, - PRIORITY_URGENT_DISPLAY = ANDROID_PRIORITY_URGENT_DISPLAY, - PRIORITY_AUDIO = ANDROID_PRIORITY_AUDIO, - PRIORITY_URGENT_AUDIO = ANDROID_PRIORITY_URGENT_AUDIO, - PRIORITY_HIGHEST = ANDROID_PRIORITY_HIGHEST, - PRIORITY_DEFAULT = ANDROID_PRIORITY_DEFAULT, - PRIORITY_MORE_FAVORABLE = ANDROID_PRIORITY_MORE_FAVORABLE, - PRIORITY_LESS_FAVORABLE = ANDROID_PRIORITY_LESS_FAVORABLE, -}; - -// Create and run a new thread. -inline bool createThread(thread_func_t f, void *a) { - return androidCreateThread(f, a) ? true : false; -} - -// Create thread with lots of parameters -inline bool createThreadEtc(thread_func_t entryFunction, - void *userData, - const char* threadName = "android:unnamed_thread", - int32_t threadPriority = PRIORITY_DEFAULT, - size_t threadStackSize = 0, - thread_id_t *threadId = 0) -{ - return androidCreateThreadEtc(entryFunction, userData, threadName, - threadPriority, threadStackSize, threadId) ? true : false; -} - -// Get some sort of unique identifier for the current thread. -inline thread_id_t getThreadId() { - return androidGetThreadId(); -} - -/*****************************************************************************/ - -/* - * Simple mutex class. The implementation is system-dependent. - * - * The mutex must be unlocked by the thread that locked it. They are not - * recursive, i.e. the same thread can't lock it multiple times. - */ -class Mutex { -public: - enum { - PRIVATE = 0, - SHARED = 1 - }; - - Mutex(); - Mutex(const char* name); - Mutex(int type, const char* name = NULL); - ~Mutex(); - - // lock or unlock the mutex - status_t lock(); - void unlock(); - - // lock if possible; returns 0 on success, error otherwise - status_t tryLock(); - - // Manages the mutex automatically. It'll be locked when Autolock is - // constructed and released when Autolock goes out of scope. - class Autolock { - public: - inline Autolock(Mutex& mutex) : mLock(mutex) { mLock.lock(); } - inline Autolock(Mutex* mutex) : mLock(*mutex) { mLock.lock(); } - inline ~Autolock() { mLock.unlock(); } - private: - Mutex& mLock; - }; - -private: - friend class Condition; - - // A mutex cannot be copied - Mutex(const Mutex&); - Mutex& operator = (const Mutex&); - -#if defined(HAVE_PTHREADS) - pthread_mutex_t mMutex; -#else - void _init(); - void* mState; +#include +#include +#include #endif -}; - -#if defined(HAVE_PTHREADS) - -inline Mutex::Mutex() { - pthread_mutex_init(&mMutex, NULL); -} -inline Mutex::Mutex(const char* name) { - pthread_mutex_init(&mMutex, NULL); -} -inline Mutex::Mutex(int type, const char* name) { - if (type == SHARED) { - pthread_mutexattr_t attr; - pthread_mutexattr_init(&attr); - pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED); - pthread_mutex_init(&mMutex, &attr); - pthread_mutexattr_destroy(&attr); - } else { - pthread_mutex_init(&mMutex, NULL); - } -} -inline Mutex::~Mutex() { - pthread_mutex_destroy(&mMutex); -} -inline status_t Mutex::lock() { - return -pthread_mutex_lock(&mMutex); -} -inline void Mutex::unlock() { - pthread_mutex_unlock(&mMutex); -} -inline status_t Mutex::tryLock() { - return -pthread_mutex_trylock(&mMutex); -} - -#endif // HAVE_PTHREADS - -/* - * Automatic mutex. Declare one of these at the top of a function. - * When the function returns, it will go out of scope, and release the - * mutex. - */ - -typedef Mutex::Autolock AutoMutex; - -/*****************************************************************************/ - -#if defined(HAVE_PTHREADS) - -/* - * Simple mutex class. The implementation is system-dependent. - * - * The mutex must be unlocked by the thread that locked it. They are not - * recursive, i.e. the same thread can't lock it multiple times. - */ -class RWLock { -public: - enum { - PRIVATE = 0, - SHARED = 1 - }; - - RWLock(); - RWLock(const char* name); - RWLock(int type, const char* name = NULL); - ~RWLock(); - - status_t readLock(); - status_t tryReadLock(); - status_t writeLock(); - status_t tryWriteLock(); - void unlock(); - - class AutoRLock { - public: - inline AutoRLock(RWLock& rwlock) : mLock(rwlock) { mLock.readLock(); } - inline ~AutoRLock() { mLock.unlock(); } - private: - RWLock& mLock; - }; - - class AutoWLock { - public: - inline AutoWLock(RWLock& rwlock) : mLock(rwlock) { mLock.writeLock(); } - inline ~AutoWLock() { mLock.unlock(); } - private: - RWLock& mLock; - }; - -private: - // A RWLock cannot be copied - RWLock(const RWLock&); - RWLock& operator = (const RWLock&); - - pthread_rwlock_t mRWLock; -}; - -inline RWLock::RWLock() { - pthread_rwlock_init(&mRWLock, NULL); -} -inline RWLock::RWLock(const char* name) { - pthread_rwlock_init(&mRWLock, NULL); -} -inline RWLock::RWLock(int type, const char* name) { - if (type == SHARED) { - pthread_rwlockattr_t attr; - pthread_rwlockattr_init(&attr); - pthread_rwlockattr_setpshared(&attr, PTHREAD_PROCESS_SHARED); - pthread_rwlock_init(&mRWLock, &attr); - pthread_rwlockattr_destroy(&attr); - } else { - pthread_rwlock_init(&mRWLock, NULL); - } -} -inline RWLock::~RWLock() { - pthread_rwlock_destroy(&mRWLock); -} -inline status_t RWLock::readLock() { - return -pthread_rwlock_rdlock(&mRWLock); -} -inline status_t RWLock::tryReadLock() { - return -pthread_rwlock_tryrdlock(&mRWLock); -} -inline status_t RWLock::writeLock() { - return -pthread_rwlock_wrlock(&mRWLock); -} -inline status_t RWLock::tryWriteLock() { - return -pthread_rwlock_trywrlock(&mRWLock); -} -inline void RWLock::unlock() { - pthread_rwlock_unlock(&mRWLock); -} - -#endif // HAVE_PTHREADS - -/*****************************************************************************/ - -/* - * Condition variable class. The implementation is system-dependent. - * - * Condition variables are paired up with mutexes. Lock the mutex, - * call wait(), then either re-wait() if things aren't quite what you want, - * or unlock the mutex and continue. All threads calling wait() must - * use the same mutex for a given Condition. - */ -class Condition { -public: - enum { - PRIVATE = 0, - SHARED = 1 - }; - - Condition(); - Condition(int type); - ~Condition(); - // Wait on the condition variable. Lock the mutex before calling. - status_t wait(Mutex& mutex); - // same with relative timeout - status_t waitRelative(Mutex& mutex, nsecs_t reltime); - // Signal the condition variable, allowing one thread to continue. - void signal(); - // Signal the condition variable, allowing all threads to continue. - void broadcast(); - -private: -#if defined(HAVE_PTHREADS) - pthread_cond_t mCond; -#else - void* mState; -#endif -}; - -#if defined(HAVE_PTHREADS) - -inline Condition::Condition() { - pthread_cond_init(&mCond, NULL); -} -inline Condition::Condition(int type) { - if (type == SHARED) { - pthread_condattr_t attr; - pthread_condattr_init(&attr); - pthread_condattr_setpshared(&attr, PTHREAD_PROCESS_SHARED); - pthread_cond_init(&mCond, &attr); - pthread_condattr_destroy(&attr); - } else { - pthread_cond_init(&mCond, NULL); - } -} -inline Condition::~Condition() { - pthread_cond_destroy(&mCond); -} -inline status_t Condition::wait(Mutex& mutex) { - return -pthread_cond_wait(&mCond, &mutex.mMutex); -} -inline status_t Condition::waitRelative(Mutex& mutex, nsecs_t reltime) { -#if defined(HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE) - struct timespec ts; - ts.tv_sec = reltime/1000000000; - ts.tv_nsec = reltime%1000000000; - return -pthread_cond_timedwait_relative_np(&mCond, &mutex.mMutex, &ts); -#else // HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE - struct timespec ts; -#if defined(HAVE_POSIX_CLOCKS) - clock_gettime(CLOCK_REALTIME, &ts); -#else // HAVE_POSIX_CLOCKS - // we don't support the clocks here. - struct timeval t; - gettimeofday(&t, NULL); - ts.tv_sec = t.tv_sec; - ts.tv_nsec= t.tv_usec*1000; -#endif // HAVE_POSIX_CLOCKS - ts.tv_sec += reltime/1000000000; - ts.tv_nsec+= reltime%1000000000; - if (ts.tv_nsec >= 1000000000) { - ts.tv_nsec -= 1000000000; - ts.tv_sec += 1; - } - return -pthread_cond_timedwait(&mCond, &mutex.mMutex, &ts); -#endif // HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE -} -inline void Condition::signal() { - pthread_cond_signal(&mCond); -} -inline void Condition::broadcast() { - pthread_cond_broadcast(&mCond); -} - -#endif // HAVE_PTHREADS - -/*****************************************************************************/ - -/* - * This is our spiffy thread object! - */ - -class Thread : virtual public RefBase -{ -public: - // Create a Thread object, but doesn't create or start the associated - // thread. See the run() method. - Thread(bool canCallJava = true); - virtual ~Thread(); - - // Start the thread in threadLoop() which needs to be implemented. - virtual status_t run( const char* name = 0, - int32_t priority = PRIORITY_DEFAULT, - size_t stack = 0); - - // Ask this object's thread to exit. This function is asynchronous, when the - // function returns the thread might still be running. Of course, this - // function can be called from a different thread. - virtual void requestExit(); - - // Good place to do one-time initializations - virtual status_t readyToRun(); - - // Call requestExit() and wait until this object's thread exits. - // BE VERY CAREFUL of deadlocks. In particular, it would be silly to call - // this function from this object's thread. Will return WOULD_BLOCK in - // that case. - status_t requestExitAndWait(); - - // Wait until this object's thread exits. Returns immediately if not yet running. - // Do not call from this object's thread; will return WOULD_BLOCK in that case. - status_t join(); - -#ifdef HAVE_ANDROID_OS - // Return the thread's kernel ID, same as the thread itself calling gettid() or - // androidGetTid(), or -1 if the thread is not running. - pid_t getTid() const; -#endif - -protected: - // exitPending() returns true if requestExit() has been called. - bool exitPending() const; - -private: - // Derived class must implement threadLoop(). The thread starts its life - // here. There are two ways of using the Thread object: - // 1) loop: if threadLoop() returns true, it will be called again if - // requestExit() wasn't called. - // 2) once: if threadLoop() returns false, the thread will exit upon return. - virtual bool threadLoop() = 0; - -private: - Thread& operator=(const Thread&); - static int _threadLoop(void* user); - const bool mCanCallJava; - // always hold mLock when reading or writing - thread_id_t mThread; - mutable Mutex mLock; - Condition mThreadExitedCondition; - status_t mStatus; - // note that all accesses of mExitPending and mRunning need to hold mLock - volatile bool mExitPending; - volatile bool mRunning; - sp mHoldSelf; -#ifdef HAVE_ANDROID_OS - // legacy for debugging, not used by getTid() as it is set by the child thread - // and so is not initialized until the child reaches that point - pid_t mTid; -#endif -}; - - -}; // namespace android - -#endif // __cplusplus #endif // _LIBS_UTILS_THREADS_H From 24e57d5660a1f5daf37e36d449d31a342fbcb9cf Mon Sep 17 00:00:00 2001 From: Andrew Hsieh Date: Mon, 27 Feb 2012 18:50:55 -0800 Subject: [PATCH 401/541] Fixed several 64-bit porting issues 1. Use "%zu" instead of "%d" for size_t in printf 2. Variable precision specifier (eg. "%.*s") in printf should be of type int. (iov_len is size_t which is 64-bit when compiled with -m64) 3. Use PRId64 instead of "%lld" to print variables of type int64_t Change-Id: I2be40a6514b5dffa0038d62b9bccc3401b8756e6 --- libs/utils/BlobCache.cpp | 4 ++-- libs/utils/Debug.cpp | 2 +- libs/utils/Static.cpp | 4 ++-- libs/utils/StopWatch.cpp | 8 ++++++-- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/libs/utils/BlobCache.cpp b/libs/utils/BlobCache.cpp index e52cf2f84..be398ee4e 100644 --- a/libs/utils/BlobCache.cpp +++ b/libs/utils/BlobCache.cpp @@ -183,7 +183,7 @@ size_t BlobCache::getFdCount() const { status_t BlobCache::flatten(void* buffer, size_t size, int fds[], size_t count) const { if (count != 0) { - ALOGE("flatten: nonzero fd count: %d", count); + ALOGE("flatten: nonzero fd count: %zu", count); return BAD_VALUE; } @@ -234,7 +234,7 @@ status_t BlobCache::unflatten(void const* buffer, size_t size, int fds[], mCacheEntries.clear(); if (count != 0) { - ALOGE("unflatten: nonzero fd count: %d", count); + ALOGE("unflatten: nonzero fd count: %zu", count); return BAD_VALUE; } diff --git a/libs/utils/Debug.cpp b/libs/utils/Debug.cpp index f7988ecf1..e8ac983ea 100644 --- a/libs/utils/Debug.cpp +++ b/libs/utils/Debug.cpp @@ -199,7 +199,7 @@ void printHexData(int32_t indent, const void *buf, size_t length, if ((int32_t)length < 0) { if (singleLineBytesCutoff < 0) func(cookie, "\n"); char buf[64]; - sprintf(buf, "(bad length: %d)", length); + sprintf(buf, "(bad length: %zu)", length); func(cookie, buf); return; } diff --git a/libs/utils/Static.cpp b/libs/utils/Static.cpp index bfcb2da45..624e917ae 100644 --- a/libs/utils/Static.cpp +++ b/libs/utils/Static.cpp @@ -57,8 +57,8 @@ protected: virtual status_t writeLines(const struct iovec& vec, size_t N) { //android_writevLog(&vec, N); <-- this is now a no-op - if (N != 1) ALOGI("WARNING: writeLines N=%d\n", N); - ALOGI("%.*s", vec.iov_len, (const char*) vec.iov_base); + if (N != 1) ALOGI("WARNING: writeLines N=%zu\n", N); + ALOGI("%.*s", (int)vec.iov_len, (const char*) vec.iov_base); return NO_ERROR; } }; diff --git a/libs/utils/StopWatch.cpp b/libs/utils/StopWatch.cpp index 595aec359..b1708d62b 100644 --- a/libs/utils/StopWatch.cpp +++ b/libs/utils/StopWatch.cpp @@ -20,6 +20,10 @@ #include #include +/* for PRId64 */ +#define __STDC_FORMAT_MACROS 1 +#include + #include #include #include @@ -39,11 +43,11 @@ StopWatch::~StopWatch() { nsecs_t elapsed = elapsedTime(); const int n = mNumLaps; - ALOGD("StopWatch %s (us): %lld ", mName, ns2us(elapsed)); + ALOGD("StopWatch %s (us): %" PRId64 " ", mName, ns2us(elapsed)); for (int i=0 ; i Date: Wed, 29 Feb 2012 15:47:17 -0800 Subject: [PATCH 402/541] Instead of hardcoding OMX component names in our code, support a config file instead. Change-Id: I5835903ab9f1c4a22ccc605ca99ed966767adf57 --- include/utils/Vector.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/utils/Vector.h b/include/utils/Vector.h index b908e2ab2..5b5296bdb 100644 --- a/include/utils/Vector.h +++ b/include/utils/Vector.h @@ -186,8 +186,8 @@ public: inline const_iterator end() const { return array() + size(); } inline void reserve(size_t n) { setCapacity(n); } inline bool empty() const{ return isEmpty(); } - inline void push_back(const TYPE& item) { insertAt(item, size()); } - inline void push_front(const TYPE& item) { insertAt(item, 0); } + inline void push_back(const TYPE& item) { insertAt(item, size(), 1); } + inline void push_front(const TYPE& item) { insertAt(item, 0, 1); } inline iterator erase(iterator pos) { return begin() + removeItemsAt(pos-array()); } From 59322a36c610199417015bc4e58fa94bfc2febbc Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Fri, 9 Mar 2012 14:46:01 -0800 Subject: [PATCH 403/541] Expose the set of enabled tags. This is used by the Java trace wrappers to avoid JNI overhead when trace tags are disabled. Also added a new tag for the input subsystem and view hierarchy. Change-Id: Ia04a507e42a37b1d3bbb19859e6c07a92f4fe9aa --- include/utils/Trace.h | 54 +++++++++++++++++++++++++------------------ libs/utils/Trace.cpp | 4 +++- 2 files changed, 35 insertions(+), 23 deletions(-) diff --git a/include/utils/Trace.h b/include/utils/Trace.h index f33ddf652..a2429c0f6 100644 --- a/include/utils/Trace.h +++ b/include/utils/Trace.h @@ -38,12 +38,16 @@ // has a performance cost even when the trace is not being recorded. Defining // ATRACE_TAG to be ATRACE_TAG_NEVER or leaving ATRACE_TAG undefined will result // in the tracing always being disabled. +// +// These tags must be kept in sync with frameworks/base/core/java/android/os/Trace.java. #define ATRACE_TAG_NEVER 0 // The "never" tag is never enabled. #define ATRACE_TAG_ALWAYS (1<<0) // The "always" tag is always enabled. #define ATRACE_TAG_GRAPHICS (1<<1) -#define ATRACE_TAG_LAST (1<<1) +#define ATRACE_TAG_INPUT (1<<2) +#define ATRACE_TAG_VIEW (1<<3) +#define ATRACE_TAG_LAST ATRACE_TAG_VIEW -#define ATRACE_TAG_INVALID (~((ATRACE_TAG_LAST - 1) | ATRACE_TAG_LAST)) +#define ATRACE_TAG_VALID_MASK ((ATRACE_TAG_LAST - 1) | ATRACE_TAG_LAST) #ifndef ATRACE_TAG #define ATRACE_TAG ATRACE_TAG_NEVER @@ -60,52 +64,56 @@ // value changes over time in a trace. #define ATRACE_INT(name, value) android::Tracer::traceCounter(ATRACE_TAG, name, value) +// ATRACE_ENABLED returns true if the trace tag is enabled. It can be used as a +// guard condition around more expensive trace calculations. +#define ATRACE_ENABLED() android::Tracer::isTagEnabled(ATRACE_TAG) + namespace android { class Tracer { public: + static uint64_t getEnabledTags() { + initIfNeeded(); + return sEnabledTags; + } + + static inline bool isTagEnabled(uint64_t tag) { + initIfNeeded(); + return sEnabledTags & tag; + } + static inline void traceCounter(uint64_t tag, const char* name, int32_t value) { - if (!android_atomic_acquire_load(&sIsReady)) { - init(); - } - int traceFD = sTraceFD; - if (CC_UNLIKELY(tagEnabled(tag) && traceFD != -1)) { + if (CC_UNLIKELY(isTagEnabled(tag))) { char buf[1024]; snprintf(buf, 1024, "C|%d|%s|%d", getpid(), name, value); - write(traceFD, buf, strlen(buf)); + write(sTraceFD, buf, strlen(buf)); } } static inline void traceBegin(uint64_t tag, const char* name) { - if (CC_UNLIKELY(!android_atomic_acquire_load(&sIsReady))) { - init(); - } - int traceFD = sTraceFD; - if (CC_UNLIKELY(tagEnabled(tag) && (traceFD != -1))) { + if (CC_UNLIKELY(isTagEnabled(tag))) { char buf[1024]; size_t len = snprintf(buf, 1024, "B|%d|%s", getpid(), name); - write(traceFD, buf, len); + write(sTraceFD, buf, len); } } static inline void traceEnd(uint64_t tag) { - if (CC_UNLIKELY(!android_atomic_acquire_load(&sIsReady))) { - init(); - } - int traceFD = sTraceFD; - if (CC_UNLIKELY(tagEnabled(tag) && (traceFD != -1))) { + if (CC_UNLIKELY(isTagEnabled(tag))) { char buf = 'E'; - write(traceFD, &buf, 1); + write(sTraceFD, &buf, 1); } } private: - static inline bool tagEnabled(uint64_t tag) { - return !(tag & ATRACE_TAG_INVALID) && (tag & sEnabledTags); + static inline void initIfNeeded() { + if (!android_atomic_acquire_load(&sIsReady)) { + init(); + } } // init opens the trace marker file for writing and reads the @@ -138,6 +146,8 @@ private: // // This should only be used by a trace function after init() has // successfully completed. + // + // This value is only ever non-zero when tracing is initialized and sTraceFD is not -1. static uint64_t sEnabledTags; // sMutex is used to protect the execution of init(). diff --git a/libs/utils/Trace.cpp b/libs/utils/Trace.cpp index c49278ae4..d5322967a 100644 --- a/libs/utils/Trace.cpp +++ b/libs/utils/Trace.cpp @@ -34,10 +34,12 @@ void Tracer::init() { sTraceFD = open(traceFileName, O_WRONLY); if (sTraceFD == -1) { ALOGE("error opening trace file: %s (%d)", strerror(errno), errno); + // sEnabledTags remains zero indicating that no tracing can occur } else { char value[PROPERTY_VALUE_MAX]; property_get("atrace.tags.enableflags", value, "0"); - sEnabledTags = strtoll(value, NULL, 0) | ATRACE_TAG_ALWAYS; + sEnabledTags = (strtoll(value, NULL, 0) & ATRACE_TAG_VALID_MASK) + | ATRACE_TAG_ALWAYS; } android_atomic_release_store(1, &sIsReady); From a19d2c7e3bf4aadad064163029bccf3f03c300cb Mon Sep 17 00:00:00 2001 From: Andrew Hsieh Date: Tue, 13 Mar 2012 00:55:34 -0700 Subject: [PATCH 404/541] Added rules to build f/n/libs/utils in 64-bit: lib64utils.a It's needed to build four shared libraries in 64-bit for 64-bit emulator with "-gpu on" lib64OpenglRender.so lib64EGL_translator.so lib64GLES_CM_translator.so lib64GLES_V2_translator.so Change-Id: Ia6c05b23df1e9cd9e7f2e94e4cd5bde4be5d336b --- libs/utils/Android.mk | 36 +++++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk index 57c048a3e..87d0fb22c 100644 --- a/libs/utils/Android.mk +++ b/libs/utils/Android.mk @@ -43,29 +43,39 @@ commonSources:= \ VectorImpl.cpp \ misc.cpp - -# For the host -# ===================================================== - -include $(CLEAR_VARS) - -LOCAL_SRC_FILES:= $(commonSources) - -LOCAL_MODULE:= libutils - -LOCAL_CFLAGS += -DLIBUTILS_NATIVE=1 $(TOOL_CFLAGS) +host_commonCflags := -DLIBUTILS_NATIVE=1 $(TOOL_CFLAGS) ifeq ($(HOST_OS),windows) ifeq ($(strip $(USE_CYGWIN),),) # Under MinGW, ctype.h doesn't need multi-byte support -LOCAL_CFLAGS += -DMB_CUR_MAX=1 +host_commonCflags += -DMB_CUR_MAX=1 endif endif +host_commonLdlibs := + ifeq ($(TARGET_OS),linux) -LOCAL_LDLIBS += -lrt -ldl +host_commonLdlibs += -lrt -ldl endif + +# For the host +# ===================================================== +include $(CLEAR_VARS) +LOCAL_SRC_FILES:= $(commonSources) +LOCAL_MODULE:= libutils +LOCAL_CFLAGS += $(host_commonCflags) +LOCAL_LDLIBS += $(host_commonLdlibs) +include $(BUILD_HOST_STATIC_LIBRARY) + + +# For the host, 64-bit +# ===================================================== +include $(CLEAR_VARS) +LOCAL_SRC_FILES:= $(commonSources) +LOCAL_MODULE:= lib64utils +LOCAL_CFLAGS += $(host_commonCflags) -m64 +LOCAL_LDLIBS += $(host_commonLdlibs) include $(BUILD_HOST_STATIC_LIBRARY) From 9a0a76df1e961ef4621e81814d8bf891a09bef66 Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Fri, 16 Mar 2012 14:45:49 -0700 Subject: [PATCH 405/541] Add traits to common utils data structures. Many of our basic data structures are trivially movable using memcpy() even if they are not trivially constructable, destructable or copyable. It's worth taking advantage of this *ahem* trait. Adding trivial_move_trait to String16 reduces appt running time on frameworks/base/core/res by 40%! Change-Id: I630a1a027e2d0ded96856e4ca042ea82906289fe --- include/utils/BitSet.h | 3 +++ include/utils/KeyedVector.h | 7 +++++++ include/utils/RefBase.h | 1 + include/utils/SortedVector.h | 3 +++ include/utils/String16.h | 5 +++++ include/utils/String8.h | 5 +++++ include/utils/TypeHelpers.h | 20 ++++++++++++++++---- include/utils/Vector.h | 3 +++ 8 files changed, 43 insertions(+), 4 deletions(-) diff --git a/include/utils/BitSet.h b/include/utils/BitSet.h index 9452e86d6..e189d0c73 100644 --- a/include/utils/BitSet.h +++ b/include/utils/BitSet.h @@ -18,6 +18,7 @@ #define UTILS_BITSET_H #include +#include /* * Contains some bit manipulation helpers. @@ -102,6 +103,8 @@ struct BitSet32 { inline bool operator!= (const BitSet32& other) const { return value != other.value; } }; +ANDROID_BASIC_TYPES_TRAITS(BitSet32) + } // namespace android #endif // UTILS_BITSET_H diff --git a/include/utils/KeyedVector.h b/include/utils/KeyedVector.h index 85535bd61..20575ee3e 100644 --- a/include/utils/KeyedVector.h +++ b/include/utils/KeyedVector.h @@ -91,6 +91,13 @@ private: SortedVector< key_value_pair_t > mVector; }; +// KeyedVector can be trivially moved using memcpy() because its +// underlying SortedVector can be trivially moved. +template struct trait_trivial_move > { + enum { value = trait_trivial_move > >::value }; +}; + + // --------------------------------------------------------------------------- /** diff --git a/include/utils/RefBase.h b/include/utils/RefBase.h index 51eff5ac9..aa91f6023 100644 --- a/include/utils/RefBase.h +++ b/include/utils/RefBase.h @@ -25,6 +25,7 @@ #include #include +#include // --------------------------------------------------------------------------- namespace android { diff --git a/include/utils/SortedVector.h b/include/utils/SortedVector.h index 0e98aeb05..24455253c 100644 --- a/include/utils/SortedVector.h +++ b/include/utils/SortedVector.h @@ -133,6 +133,9 @@ protected: virtual int do_compare(const void* lhs, const void* rhs) const; }; +// SortedVector can be trivially moved using memcpy() because moving does not +// require any change to the underlying SharedBuffer contents or reference count. +template struct trait_trivial_move > { enum { value = true }; }; // --------------------------------------------------------------------------- // No user serviceable parts from here... diff --git a/include/utils/String16.h b/include/utils/String16.h index 360f407c3..fe06c576f 100644 --- a/include/utils/String16.h +++ b/include/utils/String16.h @@ -20,6 +20,7 @@ #include #include #include +#include // --------------------------------------------------------------------------- @@ -112,6 +113,10 @@ private: const char16_t* mString; }; +// String16 can be trivially moved using memcpy() because moving does not +// require any change to the underlying SharedBuffer contents or reference count. +ANDROID_TRIVIAL_MOVE_TRAIT(String16) + TextOutput& operator<<(TextOutput& to, const String16& val); // --------------------------------------------------------------------------- diff --git a/include/utils/String8.h b/include/utils/String8.h index 4163697d2..335e7f1bf 100644 --- a/include/utils/String8.h +++ b/include/utils/String8.h @@ -20,6 +20,7 @@ #include #include #include +#include #include // for strcmp #include @@ -219,6 +220,10 @@ private: const char* mString; }; +// String8 can be trivially moved using memcpy() because moving does not +// require any change to the underlying SharedBuffer contents or reference count. +ANDROID_TRIVIAL_MOVE_TRAIT(String8) + TextOutput& operator<<(TextOutput& to, const String16& val); // --------------------------------------------------------------------------- diff --git a/include/utils/TypeHelpers.h b/include/utils/TypeHelpers.h index 1f2c2d5fd..2bf33c332 100644 --- a/include/utils/TypeHelpers.h +++ b/include/utils/TypeHelpers.h @@ -68,12 +68,24 @@ struct aggregate_traits { }; }; -#define ANDROID_BASIC_TYPES_TRAITS( T ) \ - template<> struct trait_trivial_ctor< T > { enum { value = true }; }; \ - template<> struct trait_trivial_dtor< T > { enum { value = true }; }; \ - template<> struct trait_trivial_copy< T > { enum { value = true }; }; \ +#define ANDROID_TRIVIAL_CTOR_TRAIT( T ) \ + template<> struct trait_trivial_ctor< T > { enum { value = true }; }; + +#define ANDROID_TRIVIAL_DTOR_TRAIT( T ) \ + template<> struct trait_trivial_dtor< T > { enum { value = true }; }; + +#define ANDROID_TRIVIAL_COPY_TRAIT( T ) \ + template<> struct trait_trivial_copy< T > { enum { value = true }; }; + +#define ANDROID_TRIVIAL_MOVE_TRAIT( T ) \ template<> struct trait_trivial_move< T > { enum { value = true }; }; +#define ANDROID_BASIC_TYPES_TRAITS( T ) \ + ANDROID_TRIVIAL_CTOR_TRAIT( T ) \ + ANDROID_TRIVIAL_DTOR_TRAIT( T ) \ + ANDROID_TRIVIAL_COPY_TRAIT( T ) \ + ANDROID_TRIVIAL_MOVE_TRAIT( T ) + // --------------------------------------------------------------------------- /* diff --git a/include/utils/Vector.h b/include/utils/Vector.h index 5b5296bdb..e39a5b759 100644 --- a/include/utils/Vector.h +++ b/include/utils/Vector.h @@ -201,6 +201,9 @@ protected: virtual void do_move_backward(void* dest, const void* from, size_t num) const; }; +// Vector can be trivially moved using memcpy() because moving does not +// require any change to the underlying SharedBuffer contents or reference count. +template struct trait_trivial_move > { enum { value = true }; }; // --------------------------------------------------------------------------- // No user serviceable parts from here... From 27e6eaae879309d6bbfb708e1e1aa75d7431a3a4 Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Fri, 16 Mar 2012 22:18:39 -0700 Subject: [PATCH 406/541] Add a simple work queue abstraction. Makes it easy to schedule a bunch of work to happen in parallel. Change-Id: Id9c0e52fc8b6d78d2b9ed4c2ee47abce0a01775c --- include/utils/AndroidThreads.h | 2 + include/utils/WorkQueue.h | 119 +++++++++++++++++++++++ libs/utils/Android.mk | 1 + libs/utils/Threads.cpp | 2 + libs/utils/WorkQueue.cpp | 171 +++++++++++++++++++++++++++++++++ 5 files changed, 295 insertions(+) create mode 100644 include/utils/WorkQueue.h create mode 100644 libs/utils/WorkQueue.cpp diff --git a/include/utils/AndroidThreads.h b/include/utils/AndroidThreads.h index f9f7aa41a..5bda0fd63 100644 --- a/include/utils/AndroidThreads.h +++ b/include/utils/AndroidThreads.h @@ -73,6 +73,7 @@ extern void androidSetCreateThreadFunc(android_create_thread_fn func); // Get pid for the current thread. extern pid_t androidGetTid(); +#ifdef HAVE_ANDROID_OS // Change the scheduling group of a particular thread. The group // should be one of the ANDROID_TGROUP constants. Returns BAD_VALUE if // grp is out of range, else another non-zero value with errno set if @@ -95,6 +96,7 @@ extern int androidGetThreadPriority(pid_t tid); // scheduling groups are disabled. Returns INVALID_OPERATION if unexpected error. // Thread ID zero means current thread. extern int androidGetThreadSchedulingGroup(pid_t tid); +#endif #ifdef __cplusplus } // extern "C" diff --git a/include/utils/WorkQueue.h b/include/utils/WorkQueue.h new file mode 100644 index 000000000..e3c75b219 --- /dev/null +++ b/include/utils/WorkQueue.h @@ -0,0 +1,119 @@ +/*] + * Copyright (C) 2012 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 _LIBS_UTILS_WORK_QUEUE_H +#define _LIBS_UTILS_WORK_QUEUE_H + +#include +#include +#include + +namespace android { + +/* + * A threaded work queue. + * + * This class is designed to make it easy to run a bunch of isolated work + * units in parallel, using up to the specified number of threads. + * To use it, write a loop to post work units to the work queue, then synchronize + * on the queue at the end. + */ +class WorkQueue { +public: + class WorkUnit { + public: + WorkUnit() { } + virtual ~WorkUnit() { } + + /* + * Runs the work unit. + * If the result is 'true' then the work queue continues scheduling work as usual. + * If the result is 'false' then the work queue is canceled. + */ + virtual bool run() = 0; + }; + + /* Creates a work queue with the specified maximum number of work threads. */ + WorkQueue(size_t maxThreads, bool canCallJava = true); + + /* Destroys the work queue. + * Cancels pending work and waits for all remaining threads to complete. + */ + ~WorkQueue(); + + /* Posts a work unit to run later. + * If the work queue has been canceled or is already finished, returns INVALID_OPERATION + * and does not take ownership of the work unit (caller must destroy it itself). + * Otherwise, returns OK and takes ownership of the work unit (the work queue will + * destroy it automatically). + * + * For flow control, this method blocks when the size of the pending work queue is more + * 'backlog' times the number of threads. This condition reduces the rate of entry into + * the pending work queue and prevents it from growing much more rapidly than the + * work threads can actually handle. + * + * If 'backlog' is 0, then no throttle is applied. + */ + status_t schedule(WorkUnit* workUnit, size_t backlog = 2); + + /* Cancels all pending work. + * If the work queue is already finished, returns INVALID_OPERATION. + * If the work queue is already canceled, returns OK and does nothing else. + * Otherwise, returns OK, discards all pending work units and prevents additional + * work units from being scheduled. + * + * Call finish() after cancel() to wait for all remaining work to complete. + */ + status_t cancel(); + + /* Waits for all work to complete. + * If the work queue is already finished, returns INVALID_OPERATION. + * Otherwise, waits for all work to complete and returns OK. + */ + status_t finish(); + +private: + class WorkThread : public Thread { + public: + WorkThread(WorkQueue* workQueue, bool canCallJava); + virtual ~WorkThread(); + + private: + virtual bool threadLoop(); + + WorkQueue* const mWorkQueue; + }; + + status_t cancelLocked(); + bool threadLoop(); // called from each work thread + + const size_t mMaxThreads; + const bool mCanCallJava; + + Mutex mLock; + Condition mWorkChangedCondition; + Condition mWorkDequeuedCondition; + + bool mCanceled; + bool mFinished; + size_t mIdleThreads; + Vector > mWorkThreads; + Vector mWorkUnits; +}; + +}; // namespace android + +#endif // _LIBS_UTILS_WORK_QUEUE_H diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk index 87d0fb22c..b4fc994c0 100644 --- a/libs/utils/Android.mk +++ b/libs/utils/Android.mk @@ -41,6 +41,7 @@ commonSources:= \ Tokenizer.cpp \ Unicode.cpp \ VectorImpl.cpp \ + WorkQueue.cpp \ misc.cpp host_commonCflags := -DLIBUTILS_NATIVE=1 $(TOOL_CFLAGS) diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp index ab207f565..f9277de73 100644 --- a/libs/utils/Threads.cpp +++ b/libs/utils/Threads.cpp @@ -323,6 +323,7 @@ pid_t androidGetTid() #endif } +#ifdef HAVE_ANDROID_OS int androidSetThreadSchedulingGroup(pid_t tid, int grp) { if (grp > ANDROID_TGROUP_MAX || grp < 0) { @@ -425,6 +426,7 @@ int androidGetThreadSchedulingGroup(pid_t tid) return ret; } +#endif namespace android { diff --git a/libs/utils/WorkQueue.cpp b/libs/utils/WorkQueue.cpp new file mode 100644 index 000000000..3bb99a1be --- /dev/null +++ b/libs/utils/WorkQueue.cpp @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2012 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_NDEBUG 0 +#define LOG_TAG "WorkQueue" + +#include +#include + +namespace android { + +// --- WorkQueue --- + +WorkQueue::WorkQueue(size_t maxThreads, bool canCallJava) : + mMaxThreads(maxThreads), mCanCallJava(canCallJava), + mCanceled(false), mFinished(false), mIdleThreads(0) { +} + +WorkQueue::~WorkQueue() { + if (!cancel()) { + finish(); + } +} + +status_t WorkQueue::schedule(WorkUnit* workUnit, size_t backlog) { + AutoMutex _l(mLock); + + if (mFinished || mCanceled) { + return INVALID_OPERATION; + } + + if (mWorkThreads.size() < mMaxThreads + && mIdleThreads < mWorkUnits.size() + 1) { + sp workThread = new WorkThread(this, mCanCallJava); + status_t status = workThread->run("WorkQueue::WorkThread"); + if (status) { + return status; + } + mWorkThreads.add(workThread); + mIdleThreads += 1; + } else if (backlog) { + while (mWorkUnits.size() >= mMaxThreads * backlog) { + mWorkDequeuedCondition.wait(mLock); + if (mFinished || mCanceled) { + return INVALID_OPERATION; + } + } + } + + mWorkUnits.add(workUnit); + mWorkChangedCondition.broadcast(); + return OK; +} + +status_t WorkQueue::cancel() { + AutoMutex _l(mLock); + + return cancelLocked(); +} + +status_t WorkQueue::cancelLocked() { + if (mFinished) { + return INVALID_OPERATION; + } + + if (!mCanceled) { + mCanceled = true; + + size_t count = mWorkUnits.size(); + for (size_t i = 0; i < count; i++) { + delete mWorkUnits.itemAt(i); + } + mWorkUnits.clear(); + mWorkChangedCondition.broadcast(); + mWorkDequeuedCondition.broadcast(); + } + return OK; +} + +status_t WorkQueue::finish() { + { // acquire lock + AutoMutex _l(mLock); + + if (mFinished) { + return INVALID_OPERATION; + } + + mFinished = true; + mWorkChangedCondition.broadcast(); + } // release lock + + // It is not possible for the list of work threads to change once the mFinished + // flag has been set, so we can access mWorkThreads outside of the lock here. + size_t count = mWorkThreads.size(); + for (size_t i = 0; i < count; i++) { + mWorkThreads.itemAt(i)->join(); + } + mWorkThreads.clear(); + return OK; +} + +bool WorkQueue::threadLoop() { + WorkUnit* workUnit; + { // acquire lock + AutoMutex _l(mLock); + + for (;;) { + if (mCanceled) { + return false; + } + + if (!mWorkUnits.isEmpty()) { + workUnit = mWorkUnits.itemAt(0); + mWorkUnits.removeAt(0); + mIdleThreads -= 1; + mWorkDequeuedCondition.broadcast(); + break; + } + + if (mFinished) { + return false; + } + + mWorkChangedCondition.wait(mLock); + } + } // release lock + + bool shouldContinue = workUnit->run(); + delete workUnit; + + { // acquire lock + AutoMutex _l(mLock); + + mIdleThreads += 1; + + if (!shouldContinue) { + cancelLocked(); + return false; + } + } // release lock + + return true; +} + +// --- WorkQueue::WorkThread --- + +WorkQueue::WorkThread::WorkThread(WorkQueue* workQueue, bool canCallJava) : + Thread(canCallJava), mWorkQueue(workQueue) { +} + +WorkQueue::WorkThread::~WorkThread() { +} + +bool WorkQueue::WorkThread::threadLoop() { + return mWorkQueue->threadLoop(); +} + +}; // namespace android From cd19987605f46133b5df4f1c8251bc21d48f74a9 Mon Sep 17 00:00:00 2001 From: Kenny Root Date: Thu, 22 Mar 2012 16:28:11 -0700 Subject: [PATCH 407/541] Add UniquePtr.h to frameworks/native This is a copy of libcore's UniquePtr.h header which is used until we get C++11 which has std::unique_ptr which is essentially the same. Taken from libcore project at commit 3e6dd45baa0d7f9b4fa06f4ade76e088b59cc7bf Change-Id: I7537b016f9eae33bfc4c57b24f86260909719ab8 --- include/utils/UniquePtr.h | 239 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 239 insertions(+) create mode 100644 include/utils/UniquePtr.h diff --git a/include/utils/UniquePtr.h b/include/utils/UniquePtr.h new file mode 100644 index 000000000..bc62fe606 --- /dev/null +++ b/include/utils/UniquePtr.h @@ -0,0 +1,239 @@ +/* + * Copyright (C) 2010 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. + */ + +/* === NOTE === NOTE === NOTE === NOTE === NOTE === NOTE === NOTE === NOTE === + * + * THIS IS A COPY OF libcore/include/UniquePtr.h AND AS SUCH THAT IS THE + * CANONICAL SOURCE OF THIS FILE. PLEASE KEEP THEM IN SYNC. + * + * === NOTE === NOTE === NOTE === NOTE === NOTE === NOTE === NOTE === NOTE === + */ + +#ifndef UNIQUE_PTR_H_included +#define UNIQUE_PTR_H_included + +#include // For NULL. + +// Default deleter for pointer types. +template +struct DefaultDelete { + enum { type_must_be_complete = sizeof(T) }; + DefaultDelete() {} + void operator()(T* p) const { + delete p; + } +}; + +// Default deleter for array types. +template +struct DefaultDelete { + enum { type_must_be_complete = sizeof(T) }; + void operator()(T* p) const { + delete[] p; + } +}; + +// A smart pointer that deletes the given pointer on destruction. +// Equivalent to C++0x's std::unique_ptr (a combination of boost::scoped_ptr +// and boost::scoped_array). +// Named to be in keeping with Android style but also to avoid +// collision with any other implementation, until we can switch over +// to unique_ptr. +// Use thus: +// UniquePtr c(new C); +template > +class UniquePtr { +public: + // Construct a new UniquePtr, taking ownership of the given raw pointer. + explicit UniquePtr(T* ptr = NULL) : mPtr(ptr) { + } + + ~UniquePtr() { + reset(); + } + + // Accessors. + T& operator*() const { return *mPtr; } + T* operator->() const { return mPtr; } + T* get() const { return mPtr; } + + // Returns the raw pointer and hands over ownership to the caller. + // The pointer will not be deleted by UniquePtr. + T* release() __attribute__((warn_unused_result)) { + T* result = mPtr; + mPtr = NULL; + return result; + } + + // Takes ownership of the given raw pointer. + // If this smart pointer previously owned a different raw pointer, that + // raw pointer will be freed. + void reset(T* ptr = NULL) { + if (ptr != mPtr) { + D()(mPtr); + mPtr = ptr; + } + } + +private: + // The raw pointer. + T* mPtr; + + // Comparing unique pointers is probably a mistake, since they're unique. + template bool operator==(const UniquePtr& p) const; + template bool operator!=(const UniquePtr& p) const; + + // Disallow copy and assignment. + UniquePtr(const UniquePtr&); + void operator=(const UniquePtr&); +}; + +// Partial specialization for array types. Like std::unique_ptr, this removes +// operator* and operator-> but adds operator[]. +template +class UniquePtr { +public: + explicit UniquePtr(T* ptr = NULL) : mPtr(ptr) { + } + + ~UniquePtr() { + reset(); + } + + T& operator[](size_t i) const { + return mPtr[i]; + } + T* get() const { return mPtr; } + + T* release() __attribute__((warn_unused_result)) { + T* result = mPtr; + mPtr = NULL; + return result; + } + + void reset(T* ptr = NULL) { + if (ptr != mPtr) { + D()(mPtr); + mPtr = ptr; + } + } + +private: + T* mPtr; + + // Disallow copy and assignment. + UniquePtr(const UniquePtr&); + void operator=(const UniquePtr&); +}; + +#if UNIQUE_PTR_TESTS + +// Run these tests with: +// g++ -g -DUNIQUE_PTR_TESTS -x c++ UniquePtr.h && ./a.out + +#include + +static void assert(bool b) { + if (!b) { + fprintf(stderr, "FAIL\n"); + abort(); + } + fprintf(stderr, "OK\n"); +} +static int cCount = 0; +struct C { + C() { ++cCount; } + ~C() { --cCount; } +}; +static bool freed = false; +struct Freer { + void operator()(int* p) { + assert(*p == 123); + free(p); + freed = true; + } +}; + +int main(int argc, char* argv[]) { + // + // UniquePtr tests... + // + + // Can we free a single object? + { + UniquePtr c(new C); + assert(cCount == 1); + } + assert(cCount == 0); + // Does release work? + C* rawC; + { + UniquePtr c(new C); + assert(cCount == 1); + rawC = c.release(); + } + assert(cCount == 1); + delete rawC; + // Does reset work? + { + UniquePtr c(new C); + assert(cCount == 1); + c.reset(new C); + assert(cCount == 1); + } + assert(cCount == 0); + + // + // UniquePtr tests... + // + + // Can we free an array? + { + UniquePtr cs(new C[4]); + assert(cCount == 4); + } + assert(cCount == 0); + // Does release work? + { + UniquePtr c(new C[4]); + assert(cCount == 4); + rawC = c.release(); + } + assert(cCount == 4); + delete[] rawC; + // Does reset work? + { + UniquePtr c(new C[4]); + assert(cCount == 4); + c.reset(new C[2]); + assert(cCount == 2); + } + assert(cCount == 0); + + // + // Custom deleter tests... + // + assert(!freed); + { + UniquePtr i(reinterpret_cast(malloc(sizeof(int)))); + *i = 123; + } + assert(freed); + return 0; +} +#endif + +#endif // UNIQUE_PTR_H_included From 3abdea92efbe85c66d7f2dc662953d0b5ed59451 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Thu, 22 Mar 2012 18:46:44 -0700 Subject: [PATCH 408/541] frameworks/native: move Zip* from libandroidfw to libutils ZipUtils is needed by build/tools, move it from libandroidfw (frameworks/base) to libutils (frameworks/native). Change-Id: Ib8c41134ccdec6d6aa8d49860f8dddef49a24783 --- include/utils/ZipFileCRO.h | 61 ++ include/utils/ZipFileRO.h | 262 ++++++++ include/utils/ZipUtils.h | 67 ++ libs/utils/Android.mk | 9 +- libs/utils/ZipFileCRO.cpp | 54 ++ libs/utils/ZipFileRO.cpp | 931 ++++++++++++++++++++++++++++ libs/utils/ZipUtils.cpp | 343 ++++++++++ libs/utils/tests/Android.mk | 1 + libs/utils/tests/ZipFileRO_test.cpp | 64 ++ 9 files changed, 1790 insertions(+), 2 deletions(-) create mode 100644 include/utils/ZipFileCRO.h create mode 100644 include/utils/ZipFileRO.h create mode 100644 include/utils/ZipUtils.h create mode 100644 libs/utils/ZipFileCRO.cpp create mode 100644 libs/utils/ZipFileRO.cpp create mode 100644 libs/utils/ZipUtils.cpp create mode 100644 libs/utils/tests/ZipFileRO_test.cpp diff --git a/include/utils/ZipFileCRO.h b/include/utils/ZipFileCRO.h new file mode 100644 index 000000000..3e42a958b --- /dev/null +++ b/include/utils/ZipFileCRO.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// C API for ead-only access to Zip archives, with minimal heap allocation. +// +#ifndef __LIBS_ZIPFILECRO_H +#define __LIBS_ZIPFILECRO_H + +#include +#include +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Trivial typedef to ensure that ZipFileCRO is not treated as a simple integer. + */ +typedef void* ZipFileCRO; + +/* + * Trivial typedef to ensure that ZipEntryCRO is not treated as a simple + * integer. We use NULL to indicate an invalid value. + */ +typedef void* ZipEntryCRO; + +extern ZipFileCRO ZipFileXRO_open(const char* path); + +extern void ZipFileCRO_destroy(ZipFileCRO zip); + +extern ZipEntryCRO ZipFileCRO_findEntryByName(ZipFileCRO zip, + const char* fileName); + +extern bool ZipFileCRO_getEntryInfo(ZipFileCRO zip, ZipEntryCRO entry, + int* pMethod, size_t* pUncompLen, + size_t* pCompLen, off64_t* pOffset, long* pModWhen, long* pCrc32); + +extern bool ZipFileCRO_uncompressEntry(ZipFileCRO zip, ZipEntryCRO entry, int fd); + +#ifdef __cplusplus +} +#endif + +#endif /*__LIBS_ZIPFILECRO_H*/ diff --git a/include/utils/ZipFileRO.h b/include/utils/ZipFileRO.h new file mode 100644 index 000000000..547e36a09 --- /dev/null +++ b/include/utils/ZipFileRO.h @@ -0,0 +1,262 @@ +/* + * 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. + */ + +/* + * Read-only access to Zip archives, with minimal heap allocation. + * + * This is similar to the more-complete ZipFile class, but no attempt + * has been made to make them interchangeable. This class operates under + * a very different set of assumptions and constraints. + * + * One such assumption is that if you're getting file descriptors for + * use with this class as a child of a fork() operation, you must be on + * a pread() to guarantee correct operation. This is because pread() can + * atomically read at a file offset without worrying about a lock around an + * lseek() + read() pair. + */ +#ifndef __LIBS_ZIPFILERO_H +#define __LIBS_ZIPFILERO_H + +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace android { + +/* + * Trivial typedef to ensure that ZipEntryRO is not treated as a simple + * integer. We use NULL to indicate an invalid value. + */ +typedef void* ZipEntryRO; + +/* + * Open a Zip archive for reading. + * + * We want "open" and "find entry by name" to be fast operations, and we + * want to use as little memory as possible. We memory-map the file, + * and load a hash table with pointers to the filenames (which aren't + * null-terminated). The other fields are at a fixed offset from the + * filename, so we don't need to extract those (but we do need to byte-read + * and endian-swap them every time we want them). + * + * To speed comparisons when doing a lookup by name, we could make the mapping + * "private" (copy-on-write) and null-terminate the filenames after verifying + * the record structure. However, this requires a private mapping of + * every page that the Central Directory touches. Easier to tuck a copy + * of the string length into the hash table entry. + * + * NOTE: If this is used on file descriptors inherited from a fork() operation, + * you must be on a platform that implements pread() to guarantee correctness + * on the shared file descriptors. + */ +class ZipFileRO { +public: + ZipFileRO() + : mFd(-1), mFileName(NULL), mFileLength(-1), + mDirectoryMap(NULL), + mNumEntries(-1), mDirectoryOffset(-1), + mHashTableSize(-1), mHashTable(NULL) + {} + + ~ZipFileRO(); + + /* + * Open an archive. + */ + status_t open(const char* zipFileName); + + /* + * Find an entry, by name. Returns the entry identifier, or NULL if + * not found. + * + * If two entries have the same name, one will be chosen at semi-random. + */ + ZipEntryRO findEntryByName(const char* fileName) const; + + /* + * Return the #of entries in the Zip archive. + */ + int getNumEntries(void) const { + return mNumEntries; + } + + /* + * Return the Nth entry. Zip file entries are not stored in sorted + * order, and updated entries may appear at the end, so anyone walking + * the archive needs to avoid making ordering assumptions. We take + * that further by returning the Nth non-empty entry in the hash table + * rather than the Nth entry in the archive. + * + * Valid values are [0..numEntries). + * + * [This is currently O(n). If it needs to be fast we can allocate an + * additional data structure or provide an iterator interface.] + */ + ZipEntryRO findEntryByIndex(int idx) const; + + /* + * Copy the filename into the supplied buffer. Returns 0 on success, + * -1 if "entry" is invalid, or the filename length if it didn't fit. The + * length, and the returned string, include the null-termination. + */ + int getEntryFileName(ZipEntryRO entry, char* buffer, int bufLen) const; + + /* + * Get the vital stats for an entry. Pass in NULL pointers for anything + * you don't need. + * + * "*pOffset" holds the Zip file offset of the entry's data. + * + * Returns "false" if "entry" is bogus or if the data in the Zip file + * appears to be bad. + */ + bool getEntryInfo(ZipEntryRO entry, int* pMethod, size_t* pUncompLen, + size_t* pCompLen, off64_t* pOffset, long* pModWhen, long* pCrc32) const; + + /* + * Create a new FileMap object that maps a subset of the archive. For + * an uncompressed entry this effectively provides a pointer to the + * actual data, for a compressed entry this provides the input buffer + * for inflate(). + */ + FileMap* createEntryFileMap(ZipEntryRO entry) const; + + /* + * Uncompress the data into a buffer. Depending on the compression + * format, this is either an "inflate" operation or a memcpy. + * + * Use "uncompLen" from getEntryInfo() to determine the required + * buffer size. + * + * Returns "true" on success. + */ + bool uncompressEntry(ZipEntryRO entry, void* buffer) const; + + /* + * Uncompress the data to an open file descriptor. + */ + bool uncompressEntry(ZipEntryRO entry, int fd) const; + + /* Zip compression methods we support */ + enum { + kCompressStored = 0, // no compression + kCompressDeflated = 8, // standard deflate + }; + + /* + * Utility function: uncompress deflated data, buffer to buffer. + */ + static bool inflateBuffer(void* outBuf, const void* inBuf, + size_t uncompLen, size_t compLen); + + /* + * Utility function: uncompress deflated data, buffer to fd. + */ + static bool inflateBuffer(int fd, const void* inBuf, + size_t uncompLen, size_t compLen); + + /* + * Utility function to convert ZIP's time format to a timespec struct. + */ + static inline void zipTimeToTimespec(long when, struct tm* timespec) { + const long date = when >> 16; + timespec->tm_year = ((date >> 9) & 0x7F) + 80; // Zip is years since 1980 + timespec->tm_mon = (date >> 5) & 0x0F; + timespec->tm_mday = date & 0x1F; + + timespec->tm_hour = (when >> 11) & 0x1F; + timespec->tm_min = (when >> 5) & 0x3F; + timespec->tm_sec = (when & 0x1F) << 1; + } + + /* + * Some basic functions for raw data manipulation. "LE" means + * Little Endian. + */ + static inline unsigned short get2LE(const unsigned char* buf) { + return buf[0] | (buf[1] << 8); + } + static inline unsigned long get4LE(const unsigned char* buf) { + return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); + } + +private: + /* these are private and not defined */ + ZipFileRO(const ZipFileRO& src); + ZipFileRO& operator=(const ZipFileRO& src); + + /* locate and parse the central directory */ + bool mapCentralDirectory(void); + + /* parse the archive, prepping internal structures */ + bool parseZipArchive(void); + + /* add a new entry to the hash table */ + void addToHash(const char* str, int strLen, unsigned int hash); + + /* compute string hash code */ + static unsigned int computeHash(const char* str, int len); + + /* convert a ZipEntryRO back to a hash table index */ + int entryToIndex(const ZipEntryRO entry) const; + + /* + * One entry in the hash table. + */ + typedef struct HashEntry { + const char* name; + unsigned short nameLen; + //unsigned int hash; + } HashEntry; + + /* open Zip archive */ + int mFd; + + /* Lock for handling the file descriptor (seeks, etc) */ + mutable Mutex mFdLock; + + /* zip file name */ + char* mFileName; + + /* length of file */ + size_t mFileLength; + + /* mapped file */ + FileMap* mDirectoryMap; + + /* number of entries in the Zip archive */ + int mNumEntries; + + /* CD directory offset in the Zip archive */ + off64_t mDirectoryOffset; + + /* + * We know how many entries are in the Zip archive, so we have a + * fixed-size hash table. We probe for an empty slot. + */ + int mHashTableSize; + HashEntry* mHashTable; +}; + +}; // namespace android + +#endif /*__LIBS_ZIPFILERO_H*/ diff --git a/include/utils/ZipUtils.h b/include/utils/ZipUtils.h new file mode 100644 index 000000000..42c42b6c0 --- /dev/null +++ b/include/utils/ZipUtils.h @@ -0,0 +1,67 @@ +/* + * 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. + */ + +// +// Miscellaneous zip/gzip utility functions. +// +#ifndef __LIBS_ZIPUTILS_H +#define __LIBS_ZIPUTILS_H + +#include + +namespace android { + +/* + * Container class for utility functions, primarily for namespace reasons. + */ +class ZipUtils { +public: + /* + * General utility function for uncompressing "deflate" data from a file + * to a buffer. + */ + static bool inflateToBuffer(int fd, void* buf, long uncompressedLen, + long compressedLen); + static bool inflateToBuffer(FILE* fp, void* buf, long uncompressedLen, + long compressedLen); + + /* + * Someday we might want to make this generic and handle bzip2 ".bz2" + * files too. + * + * We could declare gzip to be a sub-class of zip that has exactly + * one always-compressed entry, but we currently want to treat Zip + * and gzip as distinct, so there's no value. + * + * The zlib library has some gzip utilities, but it has no interface + * for extracting the uncompressed length of the file (you do *not* + * want to gzseek to the end). + * + * Pass in a seeked file pointer for the gzip file. If this is a gzip + * file, we set our return values appropriately and return "true" with + * the file seeked to the start of the compressed data. + */ + static bool examineGzip(FILE* fp, int* pCompressionMethod, + long* pUncompressedLen, long* pCompressedLen, unsigned long* pCRC32); + +private: + ZipUtils() {} + ~ZipUtils() {} +}; + +}; // namespace android + +#endif /*__LIBS_ZIPUTILS_H*/ diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk index b4fc994c0..a5a59d8e3 100644 --- a/libs/utils/Android.mk +++ b/libs/utils/Android.mk @@ -42,6 +42,9 @@ commonSources:= \ Unicode.cpp \ VectorImpl.cpp \ WorkQueue.cpp \ + ZipFileCRO.cpp \ + ZipFileRO.cpp \ + ZipUtils.cpp \ misc.cpp host_commonCflags := -DLIBUTILS_NATIVE=1 $(TOOL_CFLAGS) @@ -96,7 +99,8 @@ LOCAL_LDLIBS += -lrt -ldl endif LOCAL_C_INCLUDES += \ - bionic/libc/private + bionic/libc/private \ + external/zlib LOCAL_LDLIBS += -lpthread @@ -104,7 +108,8 @@ LOCAL_SHARED_LIBRARIES := \ liblog \ libcutils \ libdl \ - libcorkscrew + libcorkscrew \ + libz LOCAL_MODULE:= libutils include $(BUILD_SHARED_LIBRARY) diff --git a/libs/utils/ZipFileCRO.cpp b/libs/utils/ZipFileCRO.cpp new file mode 100644 index 000000000..55dfd9f87 --- /dev/null +++ b/libs/utils/ZipFileCRO.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +using namespace android; + +ZipFileCRO ZipFileXRO_open(const char* path) { + ZipFileRO* zip = new ZipFileRO(); + if (zip->open(path) == NO_ERROR) { + return (ZipFileCRO)zip; + } + return NULL; +} + +void ZipFileCRO_destroy(ZipFileCRO zipToken) { + ZipFileRO* zip = (ZipFileRO*)zipToken; + delete zip; +} + +ZipEntryCRO ZipFileCRO_findEntryByName(ZipFileCRO zipToken, + const char* fileName) { + ZipFileRO* zip = (ZipFileRO*)zipToken; + return (ZipEntryCRO)zip->findEntryByName(fileName); +} + +bool ZipFileCRO_getEntryInfo(ZipFileCRO zipToken, ZipEntryRO entryToken, + int* pMethod, size_t* pUncompLen, + size_t* pCompLen, off64_t* pOffset, long* pModWhen, long* pCrc32) { + ZipFileRO* zip = (ZipFileRO*)zipToken; + ZipEntryRO entry = (ZipEntryRO)entryToken; + return zip->getEntryInfo(entry, pMethod, pUncompLen, pCompLen, pOffset, + pModWhen, pCrc32); +} + +bool ZipFileCRO_uncompressEntry(ZipFileCRO zipToken, ZipEntryRO entryToken, int fd) { + ZipFileRO* zip = (ZipFileRO*)zipToken; + ZipEntryRO entry = (ZipEntryRO)entryToken; + return zip->uncompressEntry(entry, fd); +} diff --git a/libs/utils/ZipFileRO.cpp b/libs/utils/ZipFileRO.cpp new file mode 100644 index 000000000..cad772075 --- /dev/null +++ b/libs/utils/ZipFileRO.cpp @@ -0,0 +1,931 @@ +/* + * 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. + */ + +// +// Read-only access to Zip archives, with minimal heap allocation. +// +#define LOG_TAG "zipro" +//#define LOG_NDEBUG 0 +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#if HAVE_PRINTF_ZD +# define ZD "%zd" +# define ZD_TYPE ssize_t +#else +# define ZD "%ld" +# define ZD_TYPE long +#endif + +/* + * We must open binary files using open(path, ... | O_BINARY) under Windows. + * Otherwise strange read errors will happen. + */ +#ifndef O_BINARY +# define O_BINARY 0 +#endif + +/* + * TEMP_FAILURE_RETRY is defined by some, but not all, versions of + * . (Alas, it is not as standard as we'd hoped!) So, if it's + * not already defined, then define it here. + */ +#ifndef TEMP_FAILURE_RETRY +/* Used to retry syscalls that can return EINTR. */ +#define TEMP_FAILURE_RETRY(exp) ({ \ + typeof (exp) _rc; \ + do { \ + _rc = (exp); \ + } while (_rc == -1 && errno == EINTR); \ + _rc; }) +#endif + +using namespace android; + +/* + * Zip file constants. + */ +#define kEOCDSignature 0x06054b50 +#define kEOCDLen 22 +#define kEOCDNumEntries 8 // offset to #of entries in file +#define kEOCDSize 12 // size of the central directory +#define kEOCDFileOffset 16 // offset to central directory + +#define kMaxCommentLen 65535 // longest possible in ushort +#define kMaxEOCDSearch (kMaxCommentLen + kEOCDLen) + +#define kLFHSignature 0x04034b50 +#define kLFHLen 30 // excluding variable-len fields +#define kLFHNameLen 26 // offset to filename length +#define kLFHExtraLen 28 // offset to extra length + +#define kCDESignature 0x02014b50 +#define kCDELen 46 // excluding variable-len fields +#define kCDEMethod 10 // offset to compression method +#define kCDEModWhen 12 // offset to modification timestamp +#define kCDECRC 16 // offset to entry CRC +#define kCDECompLen 20 // offset to compressed length +#define kCDEUncompLen 24 // offset to uncompressed length +#define kCDENameLen 28 // offset to filename length +#define kCDEExtraLen 30 // offset to extra length +#define kCDECommentLen 32 // offset to comment length +#define kCDELocalOffset 42 // offset to local hdr + +/* + * The values we return for ZipEntryRO use 0 as an invalid value, so we + * want to adjust the hash table index by a fixed amount. Using a large + * value helps insure that people don't mix & match arguments, e.g. to + * findEntryByIndex(). + */ +#define kZipEntryAdj 10000 + +ZipFileRO::~ZipFileRO() { + free(mHashTable); + if (mDirectoryMap) + mDirectoryMap->release(); + if (mFd >= 0) + TEMP_FAILURE_RETRY(close(mFd)); + if (mFileName) + free(mFileName); +} + +/* + * Convert a ZipEntryRO to a hash table index, verifying that it's in a + * valid range. + */ +int ZipFileRO::entryToIndex(const ZipEntryRO entry) const +{ + long ent = ((long) entry) - kZipEntryAdj; + if (ent < 0 || ent >= mHashTableSize || mHashTable[ent].name == NULL) { + ALOGW("Invalid ZipEntryRO %p (%ld)\n", entry, ent); + return -1; + } + return ent; +} + + +/* + * Open the specified file read-only. We memory-map the entire thing and + * close the file before returning. + */ +status_t ZipFileRO::open(const char* zipFileName) +{ + int fd = -1; + + assert(mDirectoryMap == NULL); + + /* + * Open and map the specified file. + */ + fd = ::open(zipFileName, O_RDONLY | O_BINARY); + if (fd < 0) { + ALOGW("Unable to open zip '%s': %s\n", zipFileName, strerror(errno)); + return NAME_NOT_FOUND; + } + + mFileLength = lseek64(fd, 0, SEEK_END); + if (mFileLength < kEOCDLen) { + TEMP_FAILURE_RETRY(close(fd)); + return UNKNOWN_ERROR; + } + + if (mFileName != NULL) { + free(mFileName); + } + mFileName = strdup(zipFileName); + + mFd = fd; + + /* + * Find the Central Directory and store its size and number of entries. + */ + if (!mapCentralDirectory()) { + goto bail; + } + + /* + * Verify Central Directory and create data structures for fast access. + */ + if (!parseZipArchive()) { + goto bail; + } + + return OK; + +bail: + free(mFileName); + mFileName = NULL; + TEMP_FAILURE_RETRY(close(fd)); + return UNKNOWN_ERROR; +} + +/* + * Parse the Zip archive, verifying its contents and initializing internal + * data structures. + */ +bool ZipFileRO::mapCentralDirectory(void) +{ + ssize_t readAmount = kMaxEOCDSearch; + if (readAmount > (ssize_t) mFileLength) + readAmount = mFileLength; + + unsigned char* scanBuf = (unsigned char*) malloc(readAmount); + if (scanBuf == NULL) { + ALOGW("couldn't allocate scanBuf: %s", strerror(errno)); + free(scanBuf); + return false; + } + + /* + * Make sure this is a Zip archive. + */ + if (lseek64(mFd, 0, SEEK_SET) != 0) { + ALOGW("seek to start failed: %s", strerror(errno)); + free(scanBuf); + return false; + } + + ssize_t actual = TEMP_FAILURE_RETRY(read(mFd, scanBuf, sizeof(int32_t))); + if (actual != (ssize_t) sizeof(int32_t)) { + ALOGI("couldn't read first signature from zip archive: %s", strerror(errno)); + free(scanBuf); + return false; + } + + { + unsigned int header = get4LE(scanBuf); + if (header == kEOCDSignature) { + ALOGI("Found Zip archive, but it looks empty\n"); + free(scanBuf); + return false; + } else if (header != kLFHSignature) { + ALOGV("Not a Zip archive (found 0x%08x)\n", header); + free(scanBuf); + return false; + } + } + + /* + * Perform the traditional EOCD snipe hunt. + * + * We're searching for the End of Central Directory magic number, + * which appears at the start of the EOCD block. It's followed by + * 18 bytes of EOCD stuff and up to 64KB of archive comment. We + * need to read the last part of the file into a buffer, dig through + * it to find the magic number, parse some values out, and use those + * to determine the extent of the CD. + * + * We start by pulling in the last part of the file. + */ + off64_t searchStart = mFileLength - readAmount; + + if (lseek64(mFd, searchStart, SEEK_SET) != searchStart) { + ALOGW("seek %ld failed: %s\n", (long) searchStart, strerror(errno)); + free(scanBuf); + return false; + } + actual = TEMP_FAILURE_RETRY(read(mFd, scanBuf, readAmount)); + if (actual != (ssize_t) readAmount) { + ALOGW("Zip: read " ZD ", expected " ZD ". Failed: %s\n", + (ZD_TYPE) actual, (ZD_TYPE) readAmount, strerror(errno)); + free(scanBuf); + return false; + } + + /* + * Scan backward for the EOCD magic. In an archive without a trailing + * comment, we'll find it on the first try. (We may want to consider + * doing an initial minimal read; if we don't find it, retry with a + * second read as above.) + */ + int i; + for (i = readAmount - kEOCDLen; i >= 0; i--) { + if (scanBuf[i] == 0x50 && get4LE(&scanBuf[i]) == kEOCDSignature) { + ALOGV("+++ Found EOCD at buf+%d\n", i); + break; + } + } + if (i < 0) { + ALOGD("Zip: EOCD not found, %s is not zip\n", mFileName); + free(scanBuf); + return false; + } + + off64_t eocdOffset = searchStart + i; + const unsigned char* eocdPtr = scanBuf + i; + + assert(eocdOffset < mFileLength); + + /* + * Grab the CD offset and size, and the number of entries in the + * archive. After that, we can release our EOCD hunt buffer. + */ + unsigned int numEntries = get2LE(eocdPtr + kEOCDNumEntries); + unsigned int dirSize = get4LE(eocdPtr + kEOCDSize); + unsigned int dirOffset = get4LE(eocdPtr + kEOCDFileOffset); + free(scanBuf); + + // Verify that they look reasonable. + if ((long long) dirOffset + (long long) dirSize > (long long) eocdOffset) { + ALOGW("bad offsets (dir %ld, size %u, eocd %ld)\n", + (long) dirOffset, dirSize, (long) eocdOffset); + return false; + } + if (numEntries == 0) { + ALOGW("empty archive?\n"); + return false; + } + + ALOGV("+++ numEntries=%d dirSize=%d dirOffset=%d\n", + numEntries, dirSize, dirOffset); + + mDirectoryMap = new FileMap(); + if (mDirectoryMap == NULL) { + ALOGW("Unable to create directory map: %s", strerror(errno)); + return false; + } + + if (!mDirectoryMap->create(mFileName, mFd, dirOffset, dirSize, true)) { + ALOGW("Unable to map '%s' (" ZD " to " ZD "): %s\n", mFileName, + (ZD_TYPE) dirOffset, (ZD_TYPE) (dirOffset + dirSize), strerror(errno)); + return false; + } + + mNumEntries = numEntries; + mDirectoryOffset = dirOffset; + + return true; +} + +bool ZipFileRO::parseZipArchive(void) +{ + bool result = false; + const unsigned char* cdPtr = (const unsigned char*) mDirectoryMap->getDataPtr(); + size_t cdLength = mDirectoryMap->getDataLength(); + int numEntries = mNumEntries; + + /* + * Create hash table. We have a minimum 75% load factor, possibly as + * low as 50% after we round off to a power of 2. + */ + mHashTableSize = roundUpPower2(1 + (numEntries * 4) / 3); + mHashTable = (HashEntry*) calloc(mHashTableSize, sizeof(HashEntry)); + + /* + * Walk through the central directory, adding entries to the hash + * table. + */ + const unsigned char* ptr = cdPtr; + for (int i = 0; i < numEntries; i++) { + if (get4LE(ptr) != kCDESignature) { + ALOGW("Missed a central dir sig (at %d)\n", i); + goto bail; + } + if (ptr + kCDELen > cdPtr + cdLength) { + ALOGW("Ran off the end (at %d)\n", i); + goto bail; + } + + long localHdrOffset = (long) get4LE(ptr + kCDELocalOffset); + if (localHdrOffset >= mDirectoryOffset) { + ALOGW("bad LFH offset %ld at entry %d\n", localHdrOffset, i); + goto bail; + } + + unsigned int fileNameLen, extraLen, commentLen, hash; + + fileNameLen = get2LE(ptr + kCDENameLen); + extraLen = get2LE(ptr + kCDEExtraLen); + commentLen = get2LE(ptr + kCDECommentLen); + + /* add the CDE filename to the hash table */ + hash = computeHash((const char*)ptr + kCDELen, fileNameLen); + addToHash((const char*)ptr + kCDELen, fileNameLen, hash); + + ptr += kCDELen + fileNameLen + extraLen + commentLen; + if ((size_t)(ptr - cdPtr) > cdLength) { + ALOGW("bad CD advance (%d vs " ZD ") at entry %d\n", + (int) (ptr - cdPtr), (ZD_TYPE) cdLength, i); + goto bail; + } + } + ALOGV("+++ zip good scan %d entries\n", numEntries); + result = true; + +bail: + return result; +} + +/* + * Simple string hash function for non-null-terminated strings. + */ +/*static*/ unsigned int ZipFileRO::computeHash(const char* str, int len) +{ + unsigned int hash = 0; + + while (len--) + hash = hash * 31 + *str++; + + return hash; +} + +/* + * Add a new entry to the hash table. + */ +void ZipFileRO::addToHash(const char* str, int strLen, unsigned int hash) +{ + int ent = hash & (mHashTableSize-1); + + /* + * We over-allocate the table, so we're guaranteed to find an empty slot. + */ + while (mHashTable[ent].name != NULL) + ent = (ent + 1) & (mHashTableSize-1); + + mHashTable[ent].name = str; + mHashTable[ent].nameLen = strLen; +} + +/* + * Find a matching entry. + * + * Returns NULL if not found. + */ +ZipEntryRO ZipFileRO::findEntryByName(const char* fileName) const +{ + /* + * If the ZipFileRO instance is not initialized, the entry number will + * end up being garbage since mHashTableSize is -1. + */ + if (mHashTableSize <= 0) { + return NULL; + } + + int nameLen = strlen(fileName); + unsigned int hash = computeHash(fileName, nameLen); + int ent = hash & (mHashTableSize-1); + + while (mHashTable[ent].name != NULL) { + if (mHashTable[ent].nameLen == nameLen && + memcmp(mHashTable[ent].name, fileName, nameLen) == 0) + { + /* match */ + return (ZipEntryRO)(long)(ent + kZipEntryAdj); + } + + ent = (ent + 1) & (mHashTableSize-1); + } + + return NULL; +} + +/* + * Find the Nth entry. + * + * This currently involves walking through the sparse hash table, counting + * non-empty entries. If we need to speed this up we can either allocate + * a parallel lookup table or (perhaps better) provide an iterator interface. + */ +ZipEntryRO ZipFileRO::findEntryByIndex(int idx) const +{ + if (idx < 0 || idx >= mNumEntries) { + ALOGW("Invalid index %d\n", idx); + return NULL; + } + + for (int ent = 0; ent < mHashTableSize; ent++) { + if (mHashTable[ent].name != NULL) { + if (idx-- == 0) + return (ZipEntryRO) (ent + kZipEntryAdj); + } + } + + return NULL; +} + +/* + * Get the useful fields from the zip entry. + * + * Returns "false" if the offsets to the fields or the contents of the fields + * appear to be bogus. + */ +bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, size_t* pUncompLen, + size_t* pCompLen, off64_t* pOffset, long* pModWhen, long* pCrc32) const +{ + bool ret = false; + + const int ent = entryToIndex(entry); + if (ent < 0) + return false; + + HashEntry hashEntry = mHashTable[ent]; + + /* + * Recover the start of the central directory entry from the filename + * pointer. The filename is the first entry past the fixed-size data, + * so we can just subtract back from that. + */ + const unsigned char* ptr = (const unsigned char*) hashEntry.name; + off64_t cdOffset = mDirectoryOffset; + + ptr -= kCDELen; + + int method = get2LE(ptr + kCDEMethod); + if (pMethod != NULL) + *pMethod = method; + + if (pModWhen != NULL) + *pModWhen = get4LE(ptr + kCDEModWhen); + if (pCrc32 != NULL) + *pCrc32 = get4LE(ptr + kCDECRC); + + size_t compLen = get4LE(ptr + kCDECompLen); + if (pCompLen != NULL) + *pCompLen = compLen; + size_t uncompLen = get4LE(ptr + kCDEUncompLen); + if (pUncompLen != NULL) + *pUncompLen = uncompLen; + + /* + * If requested, determine the offset of the start of the data. All we + * have is the offset to the Local File Header, which is variable size, + * so we have to read the contents of the struct to figure out where + * the actual data starts. + * + * We also need to make sure that the lengths are not so large that + * somebody trying to map the compressed or uncompressed data runs + * off the end of the mapped region. + * + * Note we don't verify compLen/uncompLen if they don't request the + * dataOffset, because dataOffset is expensive to determine. However, + * if they don't have the file offset, they're not likely to be doing + * anything with the contents. + */ + if (pOffset != NULL) { + long localHdrOffset = get4LE(ptr + kCDELocalOffset); + if (localHdrOffset + kLFHLen >= cdOffset) { + ALOGE("ERROR: bad local hdr offset in zip\n"); + return false; + } + + unsigned char lfhBuf[kLFHLen]; + +#ifdef HAVE_PREAD + /* + * This file descriptor might be from zygote's preloaded assets, + * so we need to do an pread64() instead of a lseek64() + read() to + * guarantee atomicity across the processes with the shared file + * descriptors. + */ + ssize_t actual = + TEMP_FAILURE_RETRY(pread64(mFd, lfhBuf, sizeof(lfhBuf), localHdrOffset)); + + if (actual != sizeof(lfhBuf)) { + ALOGW("failed reading lfh from offset %ld\n", localHdrOffset); + return false; + } + + if (get4LE(lfhBuf) != kLFHSignature) { + ALOGW("didn't find signature at start of lfh; wanted: offset=%ld data=0x%08x; " + "got: data=0x%08lx\n", + localHdrOffset, kLFHSignature, get4LE(lfhBuf)); + return false; + } +#else /* HAVE_PREAD */ + /* + * For hosts don't have pread64() we cannot guarantee atomic reads from + * an offset in a file. Android should never run on those platforms. + * File descriptors inherited from a fork() share file offsets and + * there would be nothing to protect from two different processes + * calling lseek64() concurrently. + */ + + { + AutoMutex _l(mFdLock); + + if (lseek64(mFd, localHdrOffset, SEEK_SET) != localHdrOffset) { + ALOGW("failed seeking to lfh at offset %ld\n", localHdrOffset); + return false; + } + + ssize_t actual = + TEMP_FAILURE_RETRY(read(mFd, lfhBuf, sizeof(lfhBuf))); + if (actual != sizeof(lfhBuf)) { + ALOGW("failed reading lfh from offset %ld\n", localHdrOffset); + return false; + } + + if (get4LE(lfhBuf) != kLFHSignature) { + off64_t actualOffset = lseek64(mFd, 0, SEEK_CUR); + ALOGW("didn't find signature at start of lfh; wanted: offset=%ld data=0x%08x; " + "got: offset=" ZD " data=0x%08lx\n", + localHdrOffset, kLFHSignature, (ZD_TYPE) actualOffset, get4LE(lfhBuf)); + return false; + } + } +#endif /* HAVE_PREAD */ + + off64_t dataOffset = localHdrOffset + kLFHLen + + get2LE(lfhBuf + kLFHNameLen) + get2LE(lfhBuf + kLFHExtraLen); + if (dataOffset >= cdOffset) { + ALOGW("bad data offset %ld in zip\n", (long) dataOffset); + return false; + } + + /* check lengths */ + if ((off64_t)(dataOffset + compLen) > cdOffset) { + ALOGW("bad compressed length in zip (%ld + " ZD " > %ld)\n", + (long) dataOffset, (ZD_TYPE) compLen, (long) cdOffset); + return false; + } + + if (method == kCompressStored && + (off64_t)(dataOffset + uncompLen) > cdOffset) + { + ALOGE("ERROR: bad uncompressed length in zip (%ld + " ZD " > %ld)\n", + (long) dataOffset, (ZD_TYPE) uncompLen, (long) cdOffset); + return false; + } + + *pOffset = dataOffset; + } + + return true; +} + +/* + * Copy the entry's filename to the buffer. + */ +int ZipFileRO::getEntryFileName(ZipEntryRO entry, char* buffer, int bufLen) + const +{ + int ent = entryToIndex(entry); + if (ent < 0) + return -1; + + int nameLen = mHashTable[ent].nameLen; + if (bufLen < nameLen+1) + return nameLen+1; + + memcpy(buffer, mHashTable[ent].name, nameLen); + buffer[nameLen] = '\0'; + return 0; +} + +/* + * Create a new FileMap object that spans the data in "entry". + */ +FileMap* ZipFileRO::createEntryFileMap(ZipEntryRO entry) const +{ + /* + * TODO: the efficient way to do this is to modify FileMap to allow + * sub-regions of a file to be mapped. A reference-counting scheme + * can manage the base memory mapping. For now, we just create a brand + * new mapping off of the Zip archive file descriptor. + */ + + FileMap* newMap; + size_t compLen; + off64_t offset; + + if (!getEntryInfo(entry, NULL, NULL, &compLen, &offset, NULL, NULL)) + return NULL; + + newMap = new FileMap(); + if (!newMap->create(mFileName, mFd, offset, compLen, true)) { + newMap->release(); + return NULL; + } + + return newMap; +} + +/* + * Uncompress an entry, in its entirety, into the provided output buffer. + * + * This doesn't verify the data's CRC, which might be useful for + * uncompressed data. The caller should be able to manage it. + */ +bool ZipFileRO::uncompressEntry(ZipEntryRO entry, void* buffer) const +{ + const size_t kSequentialMin = 32768; + bool result = false; + int ent = entryToIndex(entry); + if (ent < 0) + return -1; + + int method; + size_t uncompLen, compLen; + off64_t offset; + const unsigned char* ptr; + + getEntryInfo(entry, &method, &uncompLen, &compLen, &offset, NULL, NULL); + + FileMap* file = createEntryFileMap(entry); + if (file == NULL) { + goto bail; + } + + ptr = (const unsigned char*) file->getDataPtr(); + + /* + * Experiment with madvise hint. When we want to uncompress a file, + * we pull some stuff out of the central dir entry and then hit a + * bunch of compressed or uncompressed data sequentially. The CDE + * visit will cause a limited amount of read-ahead because it's at + * the end of the file. We could end up doing lots of extra disk + * access if the file we're prying open is small. Bottom line is we + * probably don't want to turn MADV_SEQUENTIAL on and leave it on. + * + * So, if the compressed size of the file is above a certain minimum + * size, temporarily boost the read-ahead in the hope that the extra + * pair of system calls are negated by a reduction in page faults. + */ + if (compLen > kSequentialMin) + file->advise(FileMap::SEQUENTIAL); + + if (method == kCompressStored) { + memcpy(buffer, ptr, uncompLen); + } else { + if (!inflateBuffer(buffer, ptr, uncompLen, compLen)) + goto unmap; + } + + if (compLen > kSequentialMin) + file->advise(FileMap::NORMAL); + + result = true; + +unmap: + file->release(); +bail: + return result; +} + +/* + * Uncompress an entry, in its entirety, to an open file descriptor. + * + * This doesn't verify the data's CRC, but probably should. + */ +bool ZipFileRO::uncompressEntry(ZipEntryRO entry, int fd) const +{ + bool result = false; + int ent = entryToIndex(entry); + if (ent < 0) + return -1; + + int method; + size_t uncompLen, compLen; + off64_t offset; + const unsigned char* ptr; + + getEntryInfo(entry, &method, &uncompLen, &compLen, &offset, NULL, NULL); + + FileMap* file = createEntryFileMap(entry); + if (file == NULL) { + goto bail; + } + + ptr = (const unsigned char*) file->getDataPtr(); + + if (method == kCompressStored) { + ssize_t actual = write(fd, ptr, uncompLen); + if (actual < 0) { + ALOGE("Write failed: %s\n", strerror(errno)); + goto unmap; + } else if ((size_t) actual != uncompLen) { + ALOGE("Partial write during uncompress (" ZD " of " ZD ")\n", + (ZD_TYPE) actual, (ZD_TYPE) uncompLen); + goto unmap; + } else { + ALOGI("+++ successful write\n"); + } + } else { + if (!inflateBuffer(fd, ptr, uncompLen, compLen)) + goto unmap; + } + + result = true; + +unmap: + file->release(); +bail: + return result; +} + +/* + * Uncompress "deflate" data from one buffer to another. + */ +/*static*/ bool ZipFileRO::inflateBuffer(void* outBuf, const void* inBuf, + size_t uncompLen, size_t compLen) +{ + bool result = false; + z_stream zstream; + int zerr; + + /* + * Initialize the zlib stream struct. + */ + memset(&zstream, 0, sizeof(zstream)); + zstream.zalloc = Z_NULL; + zstream.zfree = Z_NULL; + zstream.opaque = Z_NULL; + zstream.next_in = (Bytef*)inBuf; + zstream.avail_in = compLen; + zstream.next_out = (Bytef*) outBuf; + zstream.avail_out = uncompLen; + zstream.data_type = Z_UNKNOWN; + + /* + * Use the undocumented "negative window bits" feature to tell zlib + * that there's no zlib header waiting for it. + */ + zerr = inflateInit2(&zstream, -MAX_WBITS); + if (zerr != Z_OK) { + if (zerr == Z_VERSION_ERROR) { + ALOGE("Installed zlib is not compatible with linked version (%s)\n", + ZLIB_VERSION); + } else { + ALOGE("Call to inflateInit2 failed (zerr=%d)\n", zerr); + } + goto bail; + } + + /* + * Expand data. + */ + zerr = inflate(&zstream, Z_FINISH); + if (zerr != Z_STREAM_END) { + ALOGW("Zip inflate failed, zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)\n", + zerr, zstream.next_in, zstream.avail_in, + zstream.next_out, zstream.avail_out); + goto z_bail; + } + + /* paranoia */ + if (zstream.total_out != uncompLen) { + ALOGW("Size mismatch on inflated file (%ld vs " ZD ")\n", + zstream.total_out, (ZD_TYPE) uncompLen); + goto z_bail; + } + + result = true; + +z_bail: + inflateEnd(&zstream); /* free up any allocated structures */ + +bail: + return result; +} + +/* + * Uncompress "deflate" data from one buffer to an open file descriptor. + */ +/*static*/ bool ZipFileRO::inflateBuffer(int fd, const void* inBuf, + size_t uncompLen, size_t compLen) +{ + bool result = false; + const size_t kWriteBufSize = 32768; + unsigned char writeBuf[kWriteBufSize]; + z_stream zstream; + int zerr; + + /* + * Initialize the zlib stream struct. + */ + memset(&zstream, 0, sizeof(zstream)); + zstream.zalloc = Z_NULL; + zstream.zfree = Z_NULL; + zstream.opaque = Z_NULL; + zstream.next_in = (Bytef*)inBuf; + zstream.avail_in = compLen; + zstream.next_out = (Bytef*) writeBuf; + zstream.avail_out = sizeof(writeBuf); + zstream.data_type = Z_UNKNOWN; + + /* + * Use the undocumented "negative window bits" feature to tell zlib + * that there's no zlib header waiting for it. + */ + zerr = inflateInit2(&zstream, -MAX_WBITS); + if (zerr != Z_OK) { + if (zerr == Z_VERSION_ERROR) { + ALOGE("Installed zlib is not compatible with linked version (%s)\n", + ZLIB_VERSION); + } else { + ALOGE("Call to inflateInit2 failed (zerr=%d)\n", zerr); + } + goto bail; + } + + /* + * Loop while we have more to do. + */ + do { + /* + * Expand data. + */ + zerr = inflate(&zstream, Z_NO_FLUSH); + if (zerr != Z_OK && zerr != Z_STREAM_END) { + ALOGW("zlib inflate: zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)\n", + zerr, zstream.next_in, zstream.avail_in, + zstream.next_out, zstream.avail_out); + goto z_bail; + } + + /* write when we're full or when we're done */ + if (zstream.avail_out == 0 || + (zerr == Z_STREAM_END && zstream.avail_out != sizeof(writeBuf))) + { + long writeSize = zstream.next_out - writeBuf; + int cc = write(fd, writeBuf, writeSize); + if (cc != (int) writeSize) { + ALOGW("write failed in inflate (%d vs %ld)\n", cc, writeSize); + goto z_bail; + } + + zstream.next_out = writeBuf; + zstream.avail_out = sizeof(writeBuf); + } + } while (zerr == Z_OK); + + assert(zerr == Z_STREAM_END); /* other errors should've been caught */ + + /* paranoia */ + if (zstream.total_out != uncompLen) { + ALOGW("Size mismatch on inflated file (%ld vs " ZD ")\n", + zstream.total_out, (ZD_TYPE) uncompLen); + goto z_bail; + } + + result = true; + +z_bail: + inflateEnd(&zstream); /* free up any allocated structures */ + +bail: + return result; +} diff --git a/libs/utils/ZipUtils.cpp b/libs/utils/ZipUtils.cpp new file mode 100644 index 000000000..cf5467ba5 --- /dev/null +++ b/libs/utils/ZipUtils.cpp @@ -0,0 +1,343 @@ +/* + * 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. + */ + +// +// Misc zip/gzip utility functions. +// + +#define LOG_TAG "ziputil" + +#include +#include +#include + +#include +#include +#include + +#include + +using namespace android; + +/* + * Utility function that expands zip/gzip "deflate" compressed data + * into a buffer. + * + * "fd" is an open file positioned at the start of the "deflate" data + * "buf" must hold at least "uncompressedLen" bytes. + */ +/*static*/ bool ZipUtils::inflateToBuffer(int fd, void* buf, + long uncompressedLen, long compressedLen) +{ + bool result = false; + const unsigned long kReadBufSize = 32768; + unsigned char* readBuf = NULL; + z_stream zstream; + int zerr; + unsigned long compRemaining; + + assert(uncompressedLen >= 0); + assert(compressedLen >= 0); + + readBuf = new unsigned char[kReadBufSize]; + if (readBuf == NULL) + goto bail; + compRemaining = compressedLen; + + /* + * Initialize the zlib stream. + */ + memset(&zstream, 0, sizeof(zstream)); + zstream.zalloc = Z_NULL; + zstream.zfree = Z_NULL; + zstream.opaque = Z_NULL; + zstream.next_in = NULL; + zstream.avail_in = 0; + zstream.next_out = (Bytef*) buf; + zstream.avail_out = uncompressedLen; + zstream.data_type = Z_UNKNOWN; + + /* + * Use the undocumented "negative window bits" feature to tell zlib + * that there's no zlib header waiting for it. + */ + zerr = inflateInit2(&zstream, -MAX_WBITS); + if (zerr != Z_OK) { + if (zerr == Z_VERSION_ERROR) { + ALOGE("Installed zlib is not compatible with linked version (%s)\n", + ZLIB_VERSION); + } else { + ALOGE("Call to inflateInit2 failed (zerr=%d)\n", zerr); + } + goto bail; + } + + /* + * Loop while we have data. + */ + do { + unsigned long getSize; + + /* read as much as we can */ + if (zstream.avail_in == 0) { + getSize = (compRemaining > kReadBufSize) ? + kReadBufSize : compRemaining; + ALOGV("+++ reading %ld bytes (%ld left)\n", + getSize, compRemaining); + + int cc = read(fd, readBuf, getSize); + if (cc != (int) getSize) { + ALOGD("inflate read failed (%d vs %ld)\n", + cc, getSize); + goto z_bail; + } + + compRemaining -= getSize; + + zstream.next_in = readBuf; + zstream.avail_in = getSize; + } + + /* uncompress the data */ + zerr = inflate(&zstream, Z_NO_FLUSH); + if (zerr != Z_OK && zerr != Z_STREAM_END) { + ALOGD("zlib inflate call failed (zerr=%d)\n", zerr); + goto z_bail; + } + + /* output buffer holds all, so no need to write the output */ + } while (zerr == Z_OK); + + assert(zerr == Z_STREAM_END); /* other errors should've been caught */ + + if ((long) zstream.total_out != uncompressedLen) { + ALOGW("Size mismatch on inflated file (%ld vs %ld)\n", + zstream.total_out, uncompressedLen); + goto z_bail; + } + + // success! + result = true; + +z_bail: + inflateEnd(&zstream); /* free up any allocated structures */ + +bail: + delete[] readBuf; + return result; +} + +/* + * Utility function that expands zip/gzip "deflate" compressed data + * into a buffer. + * + * (This is a clone of the previous function, but it takes a FILE* instead + * of an fd. We could pass fileno(fd) to the above, but we can run into + * trouble when "fp" has a different notion of what fd's file position is.) + * + * "fp" is an open file positioned at the start of the "deflate" data + * "buf" must hold at least "uncompressedLen" bytes. + */ +/*static*/ bool ZipUtils::inflateToBuffer(FILE* fp, void* buf, + long uncompressedLen, long compressedLen) +{ + bool result = false; + const unsigned long kReadBufSize = 32768; + unsigned char* readBuf = NULL; + z_stream zstream; + int zerr; + unsigned long compRemaining; + + assert(uncompressedLen >= 0); + assert(compressedLen >= 0); + + readBuf = new unsigned char[kReadBufSize]; + if (readBuf == NULL) + goto bail; + compRemaining = compressedLen; + + /* + * Initialize the zlib stream. + */ + memset(&zstream, 0, sizeof(zstream)); + zstream.zalloc = Z_NULL; + zstream.zfree = Z_NULL; + zstream.opaque = Z_NULL; + zstream.next_in = NULL; + zstream.avail_in = 0; + zstream.next_out = (Bytef*) buf; + zstream.avail_out = uncompressedLen; + zstream.data_type = Z_UNKNOWN; + + /* + * Use the undocumented "negative window bits" feature to tell zlib + * that there's no zlib header waiting for it. + */ + zerr = inflateInit2(&zstream, -MAX_WBITS); + if (zerr != Z_OK) { + if (zerr == Z_VERSION_ERROR) { + ALOGE("Installed zlib is not compatible with linked version (%s)\n", + ZLIB_VERSION); + } else { + ALOGE("Call to inflateInit2 failed (zerr=%d)\n", zerr); + } + goto bail; + } + + /* + * Loop while we have data. + */ + do { + unsigned long getSize; + + /* read as much as we can */ + if (zstream.avail_in == 0) { + getSize = (compRemaining > kReadBufSize) ? + kReadBufSize : compRemaining; + ALOGV("+++ reading %ld bytes (%ld left)\n", + getSize, compRemaining); + + int cc = fread(readBuf, 1, getSize, fp); + if (cc != (int) getSize) { + ALOGD("inflate read failed (%d vs %ld)\n", + cc, getSize); + goto z_bail; + } + + compRemaining -= getSize; + + zstream.next_in = readBuf; + zstream.avail_in = getSize; + } + + /* uncompress the data */ + zerr = inflate(&zstream, Z_NO_FLUSH); + if (zerr != Z_OK && zerr != Z_STREAM_END) { + ALOGD("zlib inflate call failed (zerr=%d)\n", zerr); + goto z_bail; + } + + /* output buffer holds all, so no need to write the output */ + } while (zerr == Z_OK); + + assert(zerr == Z_STREAM_END); /* other errors should've been caught */ + + if ((long) zstream.total_out != uncompressedLen) { + ALOGW("Size mismatch on inflated file (%ld vs %ld)\n", + zstream.total_out, uncompressedLen); + goto z_bail; + } + + // success! + result = true; + +z_bail: + inflateEnd(&zstream); /* free up any allocated structures */ + +bail: + delete[] readBuf; + return result; +} + +/* + * Look at the contents of a gzip archive. We want to know where the + * data starts, and how long it will be after it is uncompressed. + * + * We expect to find the CRC and length as the last 8 bytes on the file. + * This is a pretty reasonable thing to expect for locally-compressed + * files, but there's a small chance that some extra padding got thrown + * on (the man page talks about compressed data written to tape). We + * don't currently deal with that here. If "gzip -l" whines, we're going + * to fail too. + * + * On exit, "fp" is pointing at the start of the compressed data. + */ +/*static*/ bool ZipUtils::examineGzip(FILE* fp, int* pCompressionMethod, + long* pUncompressedLen, long* pCompressedLen, unsigned long* pCRC32) +{ + enum { // flags + FTEXT = 0x01, + FHCRC = 0x02, + FEXTRA = 0x04, + FNAME = 0x08, + FCOMMENT = 0x10, + }; + int ic; + int method, flags; + int i; + + ic = getc(fp); + if (ic != 0x1f || getc(fp) != 0x8b) + return false; // not gzip + method = getc(fp); + flags = getc(fp); + + /* quick sanity checks */ + if (method == EOF || flags == EOF) + return false; + if (method != ZipFileRO::kCompressDeflated) + return false; + + /* skip over 4 bytes of mod time, 1 byte XFL, 1 byte OS */ + for (i = 0; i < 6; i++) + (void) getc(fp); + /* consume "extra" field, if present */ + if ((flags & FEXTRA) != 0) { + int len; + + len = getc(fp); + len |= getc(fp) << 8; + while (len-- && getc(fp) != EOF) + ; + } + /* consume filename, if present */ + if ((flags & FNAME) != 0) { + do { + ic = getc(fp); + } while (ic != 0 && ic != EOF); + } + /* consume comment, if present */ + if ((flags & FCOMMENT) != 0) { + do { + ic = getc(fp); + } while (ic != 0 && ic != EOF); + } + /* consume 16-bit header CRC, if present */ + if ((flags & FHCRC) != 0) { + (void) getc(fp); + (void) getc(fp); + } + + if (feof(fp) || ferror(fp)) + return false; + + /* seek to the end; CRC and length are in the last 8 bytes */ + long curPosn = ftell(fp); + unsigned char buf[8]; + fseek(fp, -8, SEEK_END); + *pCompressedLen = ftell(fp) - curPosn; + + if (fread(buf, 1, 8, fp) != 8) + return false; + /* seek back to start of compressed data */ + fseek(fp, curPosn, SEEK_SET); + + *pCompressionMethod = method; + *pCRC32 = ZipFileRO::get4LE(&buf[0]); + *pUncompressedLen = ZipFileRO::get4LE(&buf[4]); + + return true; +} diff --git a/libs/utils/tests/Android.mk b/libs/utils/tests/Android.mk index a6811fcf8..98e4936e8 100644 --- a/libs/utils/tests/Android.mk +++ b/libs/utils/tests/Android.mk @@ -9,6 +9,7 @@ test_src_files := \ Looper_test.cpp \ String8_test.cpp \ Unicode_test.cpp \ + ZipFileRO_test.cpp shared_libraries := \ libz \ diff --git a/libs/utils/tests/ZipFileRO_test.cpp b/libs/utils/tests/ZipFileRO_test.cpp new file mode 100644 index 000000000..7a1d0bd95 --- /dev/null +++ b/libs/utils/tests/ZipFileRO_test.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2011 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 "ZipFileRO_test" +#include +#include + +#include + +#include +#include + +namespace android { + +class ZipFileROTest : public testing::Test { +protected: + virtual void SetUp() { + } + + virtual void TearDown() { + } +}; + +TEST_F(ZipFileROTest, ZipTimeConvertSuccess) { + struct tm t; + + // 2011-06-29 14:40:40 + long when = 0x3EDD7514; + + ZipFileRO::zipTimeToTimespec(when, &t); + + EXPECT_EQ(2011, t.tm_year + 1900) + << "Year was improperly converted."; + + EXPECT_EQ(6, t.tm_mon) + << "Month was improperly converted."; + + EXPECT_EQ(29, t.tm_mday) + << "Day was improperly converted."; + + EXPECT_EQ(14, t.tm_hour) + << "Hour was improperly converted."; + + EXPECT_EQ(40, t.tm_min) + << "Minute was improperly converted."; + + EXPECT_EQ(40, t.tm_sec) + << "Second was improperly converted."; +} + +} From e29b5c384b0dc7658ba320d1fba38a4a772e1bf1 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Fri, 23 Mar 2012 10:14:56 -0700 Subject: [PATCH 409/541] frameworks/native: link host libutils against external/zlib Fix the SDK build by statically linking host libutils against libz from external/zlib. Change-Id: Id6805d3c9071e6fa0559024336642b5386cf3c52 --- libs/utils/Android.mk | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk index a5a59d8e3..5df630df7 100644 --- a/libs/utils/Android.mk +++ b/libs/utils/Android.mk @@ -68,6 +68,9 @@ endif include $(CLEAR_VARS) LOCAL_SRC_FILES:= $(commonSources) LOCAL_MODULE:= libutils +LOCAL_STATIC_LIBRARIES := libz +LOCAL_C_INCLUDES := \ + external/zlib LOCAL_CFLAGS += $(host_commonCflags) LOCAL_LDLIBS += $(host_commonLdlibs) include $(BUILD_HOST_STATIC_LIBRARY) @@ -78,6 +81,9 @@ include $(BUILD_HOST_STATIC_LIBRARY) include $(CLEAR_VARS) LOCAL_SRC_FILES:= $(commonSources) LOCAL_MODULE:= lib64utils +LOCAL_STATIC_LIBRARIES := libz +LOCAL_C_INCLUDES := \ + external/zlib LOCAL_CFLAGS += $(host_commonCflags) -m64 LOCAL_LDLIBS += $(host_commonLdlibs) include $(BUILD_HOST_STATIC_LIBRARY) From 4e371cec7fa13273f81b9266c88ca846ece87096 Mon Sep 17 00:00:00 2001 From: Keun young Park Date: Mon, 26 Mar 2012 14:44:24 -0700 Subject: [PATCH 410/541] add Looper.cpp to build for linux host to use in CTS audio Change-Id: I59cfe30fd48fcba0bb949033f4d2aef5ce9916e4 --- libs/utils/Android.mk | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk index 5df630df7..ddfb83e12 100644 --- a/libs/utils/Android.mk +++ b/libs/utils/Android.mk @@ -67,6 +67,9 @@ endif # ===================================================== include $(CLEAR_VARS) LOCAL_SRC_FILES:= $(commonSources) +ifeq ($(HOST_OS), linux) +LOCAL_SRC_FILES += Looper.cpp +endif LOCAL_MODULE:= libutils LOCAL_STATIC_LIBRARIES := libz LOCAL_C_INCLUDES := \ @@ -80,6 +83,9 @@ include $(BUILD_HOST_STATIC_LIBRARY) # ===================================================== include $(CLEAR_VARS) LOCAL_SRC_FILES:= $(commonSources) +ifeq ($(HOST_OS), linux) +LOCAL_SRC_FILES += Looper.cpp +endif LOCAL_MODULE:= lib64utils LOCAL_STATIC_LIBRARIES := libz LOCAL_C_INCLUDES := \ From ec2e1323a019e26f0eef73a263b536e8da73c4c6 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Thu, 5 Apr 2012 13:41:56 -0700 Subject: [PATCH 411/541] Add a log tag Change-Id: If30aa8536130cf6ff9918ce97c5e4e8651ae2fef --- libs/utils/Trace.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libs/utils/Trace.cpp b/libs/utils/Trace.cpp index d5322967a..f7d8abef5 100644 --- a/libs/utils/Trace.cpp +++ b/libs/utils/Trace.cpp @@ -14,6 +14,8 @@ * limitations under the License. */ +#define LOG_TAG "Trace" + #include #include #include From 52d59e2c48a1563ea4f7e9fef6dc3611a5d74aaa Mon Sep 17 00:00:00 2001 From: Chris Craik Date: Mon, 16 Apr 2012 16:09:16 -0700 Subject: [PATCH 412/541] Add webview tracing bit Change-Id: I82e54f5eeb2666a255e372fd3bfcc54c1b3d76a0 --- include/utils/Trace.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/utils/Trace.h b/include/utils/Trace.h index a2429c0f6..637c14625 100644 --- a/include/utils/Trace.h +++ b/include/utils/Trace.h @@ -45,7 +45,8 @@ #define ATRACE_TAG_GRAPHICS (1<<1) #define ATRACE_TAG_INPUT (1<<2) #define ATRACE_TAG_VIEW (1<<3) -#define ATRACE_TAG_LAST ATRACE_TAG_VIEW +#define ATRACE_TAG_WEBVIEW (1<<4) +#define ATRACE_TAG_LAST ATRACE_TAG_WEBVIEW #define ATRACE_TAG_VALID_MASK ((ATRACE_TAG_LAST - 1) | ATRACE_TAG_LAST) From 2c1627dc49994f83a636efd1970825b519bd93cb Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Tue, 17 Apr 2012 18:19:50 -0700 Subject: [PATCH 413/541] Support tokenizing arbitrary content. Bug: 6110399 Change-Id: I37be63b68934fd451e6dffbf7d6079553619c0a3 --- include/utils/Tokenizer.h | 13 ++++++++++++- libs/utils/Tokenizer.cpp | 20 ++++++++++++++++---- 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/include/utils/Tokenizer.h b/include/utils/Tokenizer.h index c7db5fb9e..bb25f374c 100644 --- a/include/utils/Tokenizer.h +++ b/include/utils/Tokenizer.h @@ -28,7 +28,8 @@ namespace android { * A simple tokenizer for loading and parsing ASCII text files line by line. */ class Tokenizer { - Tokenizer(const String8& filename, FileMap* fileMap, char* buffer, size_t length); + Tokenizer(const String8& filename, FileMap* fileMap, char* buffer, + bool ownBuffer, size_t length); public: ~Tokenizer(); @@ -41,6 +42,15 @@ public: */ static status_t open(const String8& filename, Tokenizer** outTokenizer); + /** + * Prepares to tokenize the contents of a string. + * + * Returns NO_ERROR and a tokenizer for the string, if successful. + * Otherwise returns an error and sets outTokenizer to NULL. + */ + static status_t fromContents(const String8& filename, + const char* contents, Tokenizer** outTokenizer); + /** * Returns true if at the end of the file. */ @@ -111,6 +121,7 @@ private: String8 mFilename; FileMap* mFileMap; char* mBuffer; + bool mOwnBuffer; size_t mLength; const char* mCurrent; diff --git a/libs/utils/Tokenizer.cpp b/libs/utils/Tokenizer.cpp index efda2bfff..7067533b1 100644 --- a/libs/utils/Tokenizer.cpp +++ b/libs/utils/Tokenizer.cpp @@ -35,15 +35,18 @@ static inline bool isDelimiter(char ch, const char* delimiters) { return strchr(delimiters, ch) != NULL; } -Tokenizer::Tokenizer(const String8& filename, FileMap* fileMap, char* buffer, size_t length) : +Tokenizer::Tokenizer(const String8& filename, FileMap* fileMap, char* buffer, + bool ownBuffer, size_t length) : mFilename(filename), mFileMap(fileMap), - mBuffer(buffer), mLength(length), mCurrent(buffer), mLineNumber(1) { + mBuffer(buffer), mOwnBuffer(ownBuffer), mLength(length), + mCurrent(buffer), mLineNumber(1) { } Tokenizer::~Tokenizer() { if (mFileMap) { mFileMap->release(); - } else { + } + if (mOwnBuffer) { delete[] mBuffer; } } @@ -65,6 +68,7 @@ status_t Tokenizer::open(const String8& filename, Tokenizer** outTokenizer) { size_t length = size_t(stat.st_size); FileMap* fileMap = new FileMap(); + bool ownBuffer = false; char* buffer; if (fileMap->create(NULL, fd, 0, length, true)) { fileMap->advise(FileMap::SEQUENTIAL); @@ -77,6 +81,7 @@ status_t Tokenizer::open(const String8& filename, Tokenizer** outTokenizer) { // The length we obtained from stat is wrong too (it will always be 4096) // so we must trust that read will read the entire file. buffer = new char[length]; + ownBuffer = true; ssize_t nrd = read(fd, buffer, length); if (nrd < 0) { result = -errno; @@ -89,7 +94,7 @@ status_t Tokenizer::open(const String8& filename, Tokenizer** outTokenizer) { } if (!result) { - *outTokenizer = new Tokenizer(filename, fileMap, buffer, length); + *outTokenizer = new Tokenizer(filename, fileMap, buffer, ownBuffer, length); } } close(fd); @@ -97,6 +102,13 @@ status_t Tokenizer::open(const String8& filename, Tokenizer** outTokenizer) { return result; } +status_t Tokenizer::fromContents(const String8& filename, + const char* contents, Tokenizer** outTokenizer) { + *outTokenizer = new Tokenizer(filename, NULL, + const_cast(contents), false, strlen(contents)); + return OK; +} + String8 Tokenizer::getLocation() const { String8 result; result.appendFormat("%s:%d", mFilename.string(), mLineNumber); From 77ed15a31ad314a1e6052299108362388c1169fe Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Fri, 16 Mar 2012 07:15:23 -0700 Subject: [PATCH 414/541] Scheduling group cleanup Remove C++ APIs androidSetThreadSchedulingGroup and androidGetThreadSchedulingGroup, and the ANDROID_TGROUP_* constants. Former callers of these should now use the C APIs set_sched_policy and get_sched_policy, and the SP_* constants. Note: debug.sys.noschedgroups is not supported by the C APIs, this needs to be discussed. Change-Id: I32bbfc539ef4090faf9ef0320380e8cca9eae07c --- include/utils/AndroidThreads.h | 13 -------- include/utils/ThreadDefs.h | 7 ----- libs/utils/Threads.cpp | 57 ---------------------------------- 3 files changed, 77 deletions(-) diff --git a/include/utils/AndroidThreads.h b/include/utils/AndroidThreads.h index 5bda0fd63..f67648fd6 100644 --- a/include/utils/AndroidThreads.h +++ b/include/utils/AndroidThreads.h @@ -74,12 +74,6 @@ extern void androidSetCreateThreadFunc(android_create_thread_fn func); extern pid_t androidGetTid(); #ifdef HAVE_ANDROID_OS -// Change the scheduling group of a particular thread. The group -// should be one of the ANDROID_TGROUP constants. Returns BAD_VALUE if -// grp is out of range, else another non-zero value with errno set if -// the operation failed. Thread ID zero means current thread. -extern int androidSetThreadSchedulingGroup(pid_t tid, int grp); - // Change the priority AND scheduling group of a particular thread. The priority // should be one of the ANDROID_PRIORITY constants. Returns INVALID_OPERATION // if the priority set failed, else another value if just the group set failed; @@ -89,13 +83,6 @@ extern int androidSetThreadPriority(pid_t tid, int prio); // Get the current priority of a particular thread. Returns one of the // ANDROID_PRIORITY constants or a negative result in case of error. extern int androidGetThreadPriority(pid_t tid); - -// Get the current scheduling group of a particular thread. Normally returns -// one of the ANDROID_TGROUP constants other than ANDROID_TGROUP_DEFAULT. -// Returns ANDROID_TGROUP_DEFAULT if no pthread support (e.g. on host) or if -// scheduling groups are disabled. Returns INVALID_OPERATION if unexpected error. -// Thread ID zero means current thread. -extern int androidGetThreadSchedulingGroup(pid_t tid); #endif #ifdef __cplusplus diff --git a/include/utils/ThreadDefs.h b/include/utils/ThreadDefs.h index 3e563734e..a8f8eb3d9 100644 --- a/include/utils/ThreadDefs.h +++ b/include/utils/ThreadDefs.h @@ -79,13 +79,6 @@ enum { ANDROID_PRIORITY_LESS_FAVORABLE = +1, }; -enum { - ANDROID_TGROUP_DEFAULT = 0, - ANDROID_TGROUP_BG_NONINTERACT = 1, - ANDROID_TGROUP_FG_BOOST = 2, - ANDROID_TGROUP_MAX = ANDROID_TGROUP_FG_BOOST, -}; - #ifdef __cplusplus } // extern "C" #endif diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp index f9277de73..bc1c285f3 100644 --- a/libs/utils/Threads.cpp +++ b/libs/utils/Threads.cpp @@ -324,29 +324,6 @@ pid_t androidGetTid() } #ifdef HAVE_ANDROID_OS -int androidSetThreadSchedulingGroup(pid_t tid, int grp) -{ - if (grp > ANDROID_TGROUP_MAX || grp < 0) { - return BAD_VALUE; - } - -#if defined(HAVE_PTHREADS) - pthread_once(&gDoSchedulingGroupOnce, checkDoSchedulingGroup); - if (gDoSchedulingGroup) { - // set_sched_policy does not support tid == 0 - if (tid == 0) { - tid = androidGetTid(); - } - if (set_sched_policy(tid, (grp == ANDROID_TGROUP_BG_NONINTERACT) ? - SP_BACKGROUND : SP_FOREGROUND)) { - return PERMISSION_DENIED; - } - } -#endif - - return NO_ERROR; -} - int androidSetThreadPriority(pid_t tid, int pri) { int rc = 0; @@ -392,40 +369,6 @@ int androidGetThreadPriority(pid_t tid) { #endif } -int androidGetThreadSchedulingGroup(pid_t tid) -{ - int ret = ANDROID_TGROUP_DEFAULT; - -#if defined(HAVE_PTHREADS) - // convention is to not call get/set_sched_policy methods if disabled by property - pthread_once(&gDoSchedulingGroupOnce, checkDoSchedulingGroup); - if (gDoSchedulingGroup) { - SchedPolicy policy; - // get_sched_policy does not support tid == 0 - if (tid == 0) { - tid = androidGetTid(); - } - if (get_sched_policy(tid, &policy) < 0) { - ret = INVALID_OPERATION; - } else { - switch (policy) { - case SP_BACKGROUND: - ret = ANDROID_TGROUP_BG_NONINTERACT; - break; - case SP_FOREGROUND: - ret = ANDROID_TGROUP_FG_BOOST; - break; - default: - // should not happen, as enum SchedPolicy does not have any other values - ret = INVALID_OPERATION; - break; - } - } - } -#endif - - return ret; -} #endif namespace android { From 265743abde39f6411ed46e1e38d08c3bb7ad4f53 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Thu, 26 Apr 2012 14:15:10 -0700 Subject: [PATCH 415/541] Add traces for window manager and activity manager. Change-Id: I6677ca64164f234efc7856ddd173ad6989b4f59e --- include/utils/Trace.h | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/include/utils/Trace.h b/include/utils/Trace.h index 637c14625..6e2e2845b 100644 --- a/include/utils/Trace.h +++ b/include/utils/Trace.h @@ -40,13 +40,15 @@ // in the tracing always being disabled. // // These tags must be kept in sync with frameworks/base/core/java/android/os/Trace.java. -#define ATRACE_TAG_NEVER 0 // The "never" tag is never enabled. -#define ATRACE_TAG_ALWAYS (1<<0) // The "always" tag is always enabled. -#define ATRACE_TAG_GRAPHICS (1<<1) -#define ATRACE_TAG_INPUT (1<<2) -#define ATRACE_TAG_VIEW (1<<3) -#define ATRACE_TAG_WEBVIEW (1<<4) -#define ATRACE_TAG_LAST ATRACE_TAG_WEBVIEW +#define ATRACE_TAG_NEVER 0 // The "never" tag is never enabled. +#define ATRACE_TAG_ALWAYS (1<<0) // The "always" tag is always enabled. +#define ATRACE_TAG_GRAPHICS (1<<1) +#define ATRACE_TAG_INPUT (1<<2) +#define ATRACE_TAG_VIEW (1<<3) +#define ATRACE_TAG_WEBVIEW (1<<4) +#define ATRACE_TAG_WINDOW_MANAGER (1<<5) +#define ATRACE_TAG_ACTIVITY_MANAGER (1<<6) +#define ATRACE_TAG_LAST ATRACE_TAG_ACTIVITY_MANAGER #define ATRACE_TAG_VALID_MASK ((ATRACE_TAG_LAST - 1) | ATRACE_TAG_LAST) From e8e5dc5478a237ffcd1a4c7fae774ea7d5aeed02 Mon Sep 17 00:00:00 2001 From: Andrew Hsieh Date: Wed, 2 May 2012 12:30:00 +0800 Subject: [PATCH 416/541] Fixed 64-bit porting issues Fixed two issues about casting between int and pointer which cause compilation error in 64-bit mode (eg. for 64-bit emulator) Change-Id: I5ca9a933a239db73e56d6f27de05aa42bd06a650 --- libs/utils/ZipFileRO.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/utils/ZipFileRO.cpp b/libs/utils/ZipFileRO.cpp index cad772075..db17546ff 100644 --- a/libs/utils/ZipFileRO.cpp +++ b/libs/utils/ZipFileRO.cpp @@ -118,7 +118,7 @@ ZipFileRO::~ZipFileRO() { */ int ZipFileRO::entryToIndex(const ZipEntryRO entry) const { - long ent = ((long) entry) - kZipEntryAdj; + long ent = ((intptr_t) entry) - kZipEntryAdj; if (ent < 0 || ent >= mHashTableSize || mHashTable[ent].name == NULL) { ALOGW("Invalid ZipEntryRO %p (%ld)\n", entry, ent); return -1; @@ -459,7 +459,7 @@ ZipEntryRO ZipFileRO::findEntryByIndex(int idx) const for (int ent = 0; ent < mHashTableSize; ent++) { if (mHashTable[ent].name != NULL) { if (idx-- == 0) - return (ZipEntryRO) (ent + kZipEntryAdj); + return (ZipEntryRO) (intptr_t)(ent + kZipEntryAdj); } } From 90dd9e188e39b2272ae3ec5f16f2ad53ce79475a Mon Sep 17 00:00:00 2001 From: Andy Stadler Date: Thu, 3 May 2012 15:04:37 -0700 Subject: [PATCH 417/541] Add System Tracing capability to Sync Manager See also: Change-Id: Ia8bc01adbba345a568405b0ac8ade96e56b40cc1 Change-Id: I2678974cf85464fbc09072b06f647a0787e6e23f --- include/utils/Trace.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/utils/Trace.h b/include/utils/Trace.h index 6e2e2845b..032af1b07 100644 --- a/include/utils/Trace.h +++ b/include/utils/Trace.h @@ -48,7 +48,8 @@ #define ATRACE_TAG_WEBVIEW (1<<4) #define ATRACE_TAG_WINDOW_MANAGER (1<<5) #define ATRACE_TAG_ACTIVITY_MANAGER (1<<6) -#define ATRACE_TAG_LAST ATRACE_TAG_ACTIVITY_MANAGER +#define ATRACE_TAG_SYNC_MANAGER (1<<7) +#define ATRACE_TAG_LAST ATRACE_TAG_SYNC_MANAGER #define ATRACE_TAG_VALID_MASK ((ATRACE_TAG_LAST - 1) | ATRACE_TAG_LAST) From 7e6e7e0a5467d50ef2aff1e6a4f8c86f8b4338e6 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Mon, 7 May 2012 11:08:31 -0700 Subject: [PATCH 418/541] Add audio tag to systrace Change-Id: I4a558c43e4ccd81a4ed94684c5fad6b3c486bd14 --- include/utils/Trace.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/utils/Trace.h b/include/utils/Trace.h index 032af1b07..62f5d6b45 100644 --- a/include/utils/Trace.h +++ b/include/utils/Trace.h @@ -49,7 +49,8 @@ #define ATRACE_TAG_WINDOW_MANAGER (1<<5) #define ATRACE_TAG_ACTIVITY_MANAGER (1<<6) #define ATRACE_TAG_SYNC_MANAGER (1<<7) -#define ATRACE_TAG_LAST ATRACE_TAG_SYNC_MANAGER +#define ATRACE_TAG_AUDIO (1<<8) +#define ATRACE_TAG_LAST ATRACE_TAG_AUDIO #define ATRACE_TAG_VALID_MASK ((ATRACE_TAG_LAST - 1) | ATRACE_TAG_LAST) From 6c1cf1c8f29263a1374991195ffa98b883f181ed Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Tue, 8 May 2012 14:54:24 -0700 Subject: [PATCH 419/541] Change name of system property for traces. This allows the property to be changed from the settings UI. Change-Id: Ife7424b3549e5bbe51b6ad2fb8e5edde3a9fd608 --- libs/utils/Trace.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/utils/Trace.cpp b/libs/utils/Trace.cpp index f7d8abef5..f3ec4851a 100644 --- a/libs/utils/Trace.cpp +++ b/libs/utils/Trace.cpp @@ -39,7 +39,7 @@ void Tracer::init() { // sEnabledTags remains zero indicating that no tracing can occur } else { char value[PROPERTY_VALUE_MAX]; - property_get("atrace.tags.enableflags", value, "0"); + property_get("debug.atrace.tags.enableflags", value, "0"); sEnabledTags = (strtoll(value, NULL, 0) & ATRACE_TAG_VALID_MASK) | ATRACE_TAG_ALWAYS; } From c1309d74e8929f73e1b9cdb5dbf70aa8a2b09af3 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Tue, 8 May 2012 18:54:22 -0700 Subject: [PATCH 420/541] Add callback hack to find out when to reload system properties. Every IBinder object can accept a new transaction to tell it that it might want to reload system properties, and in the process anyone can register a callback to be executed when this happens. Use this to reload the trace property. This is very much ONLY for debugging. Change-Id: I55c67c46f8f3fa9073bef0dfaab4577ed1d47eb4 --- include/utils/Trace.h | 5 ++++ include/utils/misc.h | 4 +++ libs/utils/Trace.cpp | 22 +++++++++++++--- libs/utils/misc.cpp | 60 ++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 86 insertions(+), 5 deletions(-) diff --git a/include/utils/Trace.h b/include/utils/Trace.h index 62f5d6b45..984cd4617 100644 --- a/include/utils/Trace.h +++ b/include/utils/Trace.h @@ -121,11 +121,16 @@ private: } } + static void changeCallback(); + // init opens the trace marker file for writing and reads the // atrace.tags.enableflags system property. It does this only the first // time it is run, using sMutex for synchronization. static void init(); + // retrieve the current value of the system property. + static void loadSystemProperty(); + // sIsReady is a boolean value indicating whether a call to init() has // completed in this process. It is initialized to 0 and set to 1 when the // first init() call completes. It is set to 1 even if a failure occurred diff --git a/include/utils/misc.h b/include/utils/misc.h index 23f2a4c7c..d7d5bc196 100644 --- a/include/utils/misc.h +++ b/include/utils/misc.h @@ -88,6 +88,10 @@ void strreverse(char* begin, char* end); void k_itoa(int value, char* str, int base); char* itoa(int val, int base); +typedef void (*sysprop_change_callback)(void); +void add_sysprop_change_callback(sysprop_change_callback cb, int priority); +void report_sysprop_change(); + }; // namespace android #endif // _LIBS_UTILS_MISC_H diff --git a/libs/utils/Trace.cpp b/libs/utils/Trace.cpp index f3ec4851a..5cd5731d5 100644 --- a/libs/utils/Trace.cpp +++ b/libs/utils/Trace.cpp @@ -19,6 +19,7 @@ #include #include #include +#include namespace android { @@ -27,10 +28,19 @@ int Tracer::sTraceFD = -1; uint64_t Tracer::sEnabledTags = 0; Mutex Tracer::sMutex; +void Tracer::changeCallback() { + Mutex::Autolock lock(sMutex); + if (sIsReady && sTraceFD >= 0) { + loadSystemProperty(); + } +} + void Tracer::init() { Mutex::Autolock lock(sMutex); if (!sIsReady) { + add_sysprop_change_callback(changeCallback, 0); + const char* const traceFileName = "/sys/kernel/debug/tracing/trace_marker"; sTraceFD = open(traceFileName, O_WRONLY); @@ -38,14 +48,18 @@ void Tracer::init() { ALOGE("error opening trace file: %s (%d)", strerror(errno), errno); // sEnabledTags remains zero indicating that no tracing can occur } else { - char value[PROPERTY_VALUE_MAX]; - property_get("debug.atrace.tags.enableflags", value, "0"); - sEnabledTags = (strtoll(value, NULL, 0) & ATRACE_TAG_VALID_MASK) - | ATRACE_TAG_ALWAYS; + loadSystemProperty(); } android_atomic_release_store(1, &sIsReady); } } +void Tracer::loadSystemProperty() { + char value[PROPERTY_VALUE_MAX]; + property_get("debug.atrace.tags.enableflags", value, "0"); + sEnabledTags = (strtoll(value, NULL, 0) & ATRACE_TAG_VALID_MASK) + | ATRACE_TAG_ALWAYS; +} + } // namespace andoid diff --git a/libs/utils/misc.cpp b/libs/utils/misc.cpp index dc89d15b6..b3c99e6b4 100644 --- a/libs/utils/misc.cpp +++ b/libs/utils/misc.cpp @@ -14,10 +14,13 @@ * limitations under the License. */ +#define LOG_TAG "misc" + // // Miscellaneous utility functions. // #include +#include #include #include @@ -25,6 +28,12 @@ #include #include +#if defined(HAVE_PTHREADS) +# include +#endif + +#include + using namespace android; namespace android { @@ -181,5 +190,54 @@ unsigned int roundUpPower2(unsigned int val) return val; } -}; // namespace android +struct sysprop_change_callback_info { + sysprop_change_callback callback; + int priority; +}; +#if defined(HAVE_PTHREADS) +static pthread_mutex_t gSyspropMutex = PTHREAD_MUTEX_INITIALIZER; +static Vector* gSyspropList = NULL; +#endif + +void add_sysprop_change_callback(sysprop_change_callback cb, int priority) { +#if defined(HAVE_PTHREADS) + pthread_mutex_lock(&gSyspropMutex); + if (gSyspropList == NULL) { + gSyspropList = new Vector(); + } + sysprop_change_callback_info info; + info.callback = cb; + info.priority = priority; + bool added = false; + for (size_t i=0; isize(); i++) { + if (priority >= gSyspropList->itemAt(i).priority) { + gSyspropList->insertAt(info, i); + added = true; + break; + } + } + if (!added) { + gSyspropList->add(info); + } + pthread_mutex_unlock(&gSyspropMutex); +#endif +} + +void report_sysprop_change() { +#if defined(HAVE_PTHREADS) + pthread_mutex_lock(&gSyspropMutex); + Vector listeners; + if (gSyspropList != NULL) { + listeners = *gSyspropList; + } + pthread_mutex_unlock(&gSyspropMutex); + + //ALOGI("Reporting sysprop change to %d listeners", listeners.size()); + for (size_t i=0; i Date: Thu, 10 May 2012 15:50:19 -0700 Subject: [PATCH 421/541] Workaround for add_tid_to_cgroup failed to write Bug: 6467109 Change-Id: I6dff8e608d83c7a7c453c25c94ad100f113769b9 --- libs/utils/Threads.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp index bc1c285f3..a25a81fbe 100644 --- a/libs/utils/Threads.cpp +++ b/libs/utils/Threads.cpp @@ -101,8 +101,10 @@ struct thread_data_t { if (gDoSchedulingGroup) { if (prio >= ANDROID_PRIORITY_BACKGROUND) { set_sched_policy(androidGetTid(), SP_BACKGROUND); - } else { + } else if (prio > ANDROID_PRIORITY_AUDIO) { set_sched_policy(androidGetTid(), SP_FOREGROUND); + } else { + // defaults to that of parent, or as set by requestPriority() } } From 69fc3d4e1e73c47aec6c8e932b0f0239dd777dd3 Mon Sep 17 00:00:00 2001 From: Jamie Gennis Date: Fri, 11 May 2012 04:42:59 -0700 Subject: [PATCH 422/541] libutils: add a tracing tag for video Change-Id: Icbecc7184844e95ee81a558b0a54d5513d5cf59f --- include/utils/Trace.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/utils/Trace.h b/include/utils/Trace.h index 984cd4617..421920635 100644 --- a/include/utils/Trace.h +++ b/include/utils/Trace.h @@ -50,7 +50,8 @@ #define ATRACE_TAG_ACTIVITY_MANAGER (1<<6) #define ATRACE_TAG_SYNC_MANAGER (1<<7) #define ATRACE_TAG_AUDIO (1<<8) -#define ATRACE_TAG_LAST ATRACE_TAG_AUDIO +#define ATRACE_TAG_VIDEO (1<<9) +#define ATRACE_TAG_LAST ATRACE_TAG_VIDEO #define ATRACE_TAG_VALID_MASK ((ATRACE_TAG_LAST - 1) | ATRACE_TAG_LAST) From 03b168a69bd155be2675d7dffa342a30990259f7 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Thu, 17 May 2012 16:52:21 -0700 Subject: [PATCH 423/541] fix a corruption in Vector<> when adding new items would happen when vectors are copied and new items is added in both vectors. we didn't duplicate the underlying storage when adding items in vectors. Bug: 6515797 Change-Id: If544c07d96c05821e088d7f2c9b5736f7e306c31 --- libs/utils/VectorImpl.cpp | 2 +- libs/utils/tests/Android.mk | 1 + libs/utils/tests/Vector_test.cpp | 75 ++++++++++++++++++++++++++++++++ 3 files changed, 77 insertions(+), 1 deletion(-) create mode 100644 libs/utils/tests/Vector_test.cpp diff --git a/libs/utils/VectorImpl.cpp b/libs/utils/VectorImpl.cpp index 220ae3ea6..e78faa820 100644 --- a/libs/utils/VectorImpl.cpp +++ b/libs/utils/VectorImpl.cpp @@ -382,8 +382,8 @@ void* VectorImpl::_grow(size_t where, size_t amount) } } } else { + void* array = editArrayImpl(); if (where != mCount) { - void* array = editArrayImpl(); const void* from = reinterpret_cast(array) + where*mItemSize; void* to = reinterpret_cast(array) + (where+amount)*mItemSize; _do_move_forward(to, from, mCount - where); diff --git a/libs/utils/tests/Android.mk b/libs/utils/tests/Android.mk index 98e4936e8..0a5b3795a 100644 --- a/libs/utils/tests/Android.mk +++ b/libs/utils/tests/Android.mk @@ -9,6 +9,7 @@ test_src_files := \ Looper_test.cpp \ String8_test.cpp \ Unicode_test.cpp \ + Vector_test.cpp \ ZipFileRO_test.cpp shared_libraries := \ diff --git a/libs/utils/tests/Vector_test.cpp b/libs/utils/tests/Vector_test.cpp new file mode 100644 index 000000000..d29c05445 --- /dev/null +++ b/libs/utils/tests/Vector_test.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2012 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 "Vector_test" + +#include +#include +#include +#include + +namespace android { + +class VectorTest : public testing::Test { +protected: + virtual void SetUp() { + } + + virtual void TearDown() { + } + +public: +}; + + +TEST_F(VectorTest, CopyOnWrite_CopyAndAddElements) { + + Vector vector; + Vector other; + vector.setCapacity(8); + + vector.add(1); + vector.add(2); + vector.add(3); + + EXPECT_EQ(vector.size(), 3); + + // copy the vector + other = vector; + + EXPECT_EQ(other.size(), 3); + + // add an element to the first vector + vector.add(4); + + // make sure the sizes are correct + EXPECT_EQ(vector.size(), 4); + EXPECT_EQ(other.size(), 3); + + // add an element to the copy + other.add(5); + + // make sure the sizes are correct + EXPECT_EQ(vector.size(), 4); + EXPECT_EQ(other.size(), 4); + + // make sure the content of both vectors are correct + EXPECT_EQ(vector[3], 4); + EXPECT_EQ(other[3], 5); +} + + +} // namespace android From 55195d745c6a21f8a75f26716f2a9690e40a1331 Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Wed, 30 May 2012 19:17:47 -0700 Subject: [PATCH 424/541] Remove unused statistics code. Bug: 6559630 Change-Id: Iacdf4bb4c1c125c09305cbd8cb443c7c80cfc010 --- include/utils/Looper.h | 21 ------------- libs/utils/Looper.cpp | 68 ------------------------------------------ 2 files changed, 89 deletions(-) diff --git a/include/utils/Looper.h b/include/utils/Looper.h index 3c2905d59..96b971e97 100644 --- a/include/utils/Looper.h +++ b/include/utils/Looper.h @@ -27,9 +27,6 @@ // When defined, uses epoll_wait() for polling, otherwise uses poll(). #define LOOPER_USES_EPOLL -// When defined, logs performance statistics for tuning and debugging purposes. -//#define LOOPER_STATISTICS - #ifdef LOOPER_USES_EPOLL #include #else @@ -340,24 +337,6 @@ private: void wakeAndLock(); #endif -#ifdef LOOPER_STATISTICS - static const int SAMPLED_WAKE_CYCLES_TO_AGGREGATE = 100; - static const int SAMPLED_POLLS_TO_AGGREGATE = 1000; - - nsecs_t mPendingWakeTime; - int mPendingWakeCount; - - int mSampledWakeCycles; - int mSampledWakeCountSum; - nsecs_t mSampledWakeLatencySum; - - int mSampledPolls; - int mSampledZeroPollCount; - int mSampledZeroPollLatencySum; - int mSampledTimeoutPollCount; - int mSampledTimeoutPollLatencySum; -#endif - // This state is only used privately by pollOnce and does not require a lock since // it runs on a single thread. Vector mResponses; diff --git a/libs/utils/Looper.cpp b/libs/utils/Looper.cpp index d1aa664bc..95db28db0 100644 --- a/libs/utils/Looper.cpp +++ b/libs/utils/Looper.cpp @@ -98,20 +98,6 @@ Looper::Looper(bool allowNonCallbacks) : mPolling = false; mWaiters = 0; #endif - -#ifdef LOOPER_STATISTICS - mPendingWakeTime = -1; - mPendingWakeCount = 0; - mSampledWakeCycles = 0; - mSampledWakeCountSum = 0; - mSampledWakeLatencySum = 0; - - mSampledPolls = 0; - mSampledZeroPollCount = 0; - mSampledZeroPollLatencySum = 0; - mSampledTimeoutPollCount = 0; - mSampledTimeoutPollLatencySum = 0; -#endif } Looper::~Looper() { @@ -234,10 +220,6 @@ int Looper::pollInner(int timeoutMillis) { mResponses.clear(); mResponseIndex = 0; -#ifdef LOOPER_STATISTICS - nsecs_t pollStartTime = systemTime(SYSTEM_TIME_MONOTONIC); -#endif - #ifdef LOOPER_USES_EPOLL struct epoll_event eventItems[EPOLL_MAX_EVENTS]; int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis); @@ -341,29 +323,6 @@ Done: } #endif -#ifdef LOOPER_STATISTICS - nsecs_t pollEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - mSampledPolls += 1; - if (timeoutMillis == 0) { - mSampledZeroPollCount += 1; - mSampledZeroPollLatencySum += pollEndTime - pollStartTime; - } else if (timeoutMillis > 0 && result == ALOOPER_POLL_TIMEOUT) { - mSampledTimeoutPollCount += 1; - mSampledTimeoutPollLatencySum += pollEndTime - pollStartTime - - milliseconds_to_nanoseconds(timeoutMillis); - } - if (mSampledPolls == SAMPLED_POLLS_TO_AGGREGATE) { - ALOGD("%p ~ poll latency statistics: %0.3fms zero timeout, %0.3fms non-zero timeout", this, - 0.000001f * float(mSampledZeroPollLatencySum) / mSampledZeroPollCount, - 0.000001f * float(mSampledTimeoutPollLatencySum) / mSampledTimeoutPollCount); - mSampledPolls = 0; - mSampledZeroPollCount = 0; - mSampledZeroPollLatencySum = 0; - mSampledTimeoutPollCount = 0; - mSampledTimeoutPollLatencySum = 0; - } -#endif - // Invoke pending message callbacks. mNextMessageUptime = LLONG_MAX; while (mMessageEnvelopes.size() != 0) { @@ -454,13 +413,6 @@ void Looper::wake() { ALOGD("%p ~ wake", this); #endif -#ifdef LOOPER_STATISTICS - // FIXME: Possible race with awoken() but this code is for testing only and is rarely enabled. - if (mPendingWakeCount++ == 0) { - mPendingWakeTime = systemTime(SYSTEM_TIME_MONOTONIC); - } -#endif - ssize_t nWrite; do { nWrite = write(mWakeWritePipeFd, "W", 1); @@ -478,26 +430,6 @@ void Looper::awoken() { ALOGD("%p ~ awoken", this); #endif -#ifdef LOOPER_STATISTICS - if (mPendingWakeCount == 0) { - ALOGD("%p ~ awoken: spurious!", this); - } else { - mSampledWakeCycles += 1; - mSampledWakeCountSum += mPendingWakeCount; - mSampledWakeLatencySum += systemTime(SYSTEM_TIME_MONOTONIC) - mPendingWakeTime; - mPendingWakeCount = 0; - mPendingWakeTime = -1; - if (mSampledWakeCycles == SAMPLED_WAKE_CYCLES_TO_AGGREGATE) { - ALOGD("%p ~ wake statistics: %0.3fms wake latency, %0.3f wakes per cycle", this, - 0.000001f * float(mSampledWakeLatencySum) / mSampledWakeCycles, - float(mSampledWakeCountSum) / mSampledWakeCycles); - mSampledWakeCycles = 0; - mSampledWakeCountSum = 0; - mSampledWakeLatencySum = 0; - } - } -#endif - char buffer[16]; ssize_t nRead; do { From 5705e69825a94e9d02bff3d8c3dec5800b73f6a5 Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Wed, 30 May 2012 19:21:12 -0700 Subject: [PATCH 425/541] Delete unused poll() code. We don't need this code anymore and it is just in the way. Bug: 6559630 Change-Id: I1dc9decf85d5ea1feab159c2985da6c20baffdd5 --- include/utils/Looper.h | 29 --------- libs/utils/Looper.cpp | 139 ----------------------------------------- 2 files changed, 168 deletions(-) diff --git a/include/utils/Looper.h b/include/utils/Looper.h index 96b971e97..84e3864a6 100644 --- a/include/utils/Looper.h +++ b/include/utils/Looper.h @@ -24,14 +24,7 @@ #include -// When defined, uses epoll_wait() for polling, otherwise uses poll(). -#define LOOPER_USES_EPOLL - -#ifdef LOOPER_USES_EPOLL #include -#else -#include -#endif /* * Declare a concrete type for the NDK's looper forward declaration. @@ -310,32 +303,10 @@ private: Vector mMessageEnvelopes; // guarded by mLock bool mSendingMessage; // guarded by mLock -#ifdef LOOPER_USES_EPOLL int mEpollFd; // immutable // Locked list of file descriptor monitoring requests. KeyedVector mRequests; // guarded by mLock -#else - // The lock guards state used to track whether there is a poll() in progress and whether - // there are any other threads waiting in wakeAndLock(). The condition variables - // are used to transfer control among these threads such that all waiters are - // serviced before a new poll can begin. - // The wakeAndLock() method increments mWaiters, wakes the poll, blocks on mAwake - // until mPolling becomes false, then decrements mWaiters again. - // The poll() method blocks on mResume until mWaiters becomes 0, then sets - // mPolling to true, blocks until the poll completes, then resets mPolling to false - // and signals mResume if there are waiters. - bool mPolling; // guarded by mLock - uint32_t mWaiters; // guarded by mLock - Condition mAwake; // guarded by mLock - Condition mResume; // guarded by mLock - - Vector mRequestedFds; // must hold mLock and mPolling must be false to modify - Vector mRequests; // must hold mLock and mPolling must be false to modify - - ssize_t getRequestIndexLocked(int fd); - void wakeAndLock(); -#endif // This state is only used privately by pollOnce and does not require a lock since // it runs on a single thread. diff --git a/libs/utils/Looper.cpp b/libs/utils/Looper.cpp index 95db28db0..989499362 100644 --- a/libs/utils/Looper.cpp +++ b/libs/utils/Looper.cpp @@ -40,13 +40,11 @@ void WeakMessageHandler::handleMessage(const Message& message) { // --- Looper --- -#ifdef LOOPER_USES_EPOLL // Hint for number of file descriptors to be associated with the epoll instance. static const int EPOLL_SIZE_HINT = 8; // Maximum number of file descriptors for which to retrieve poll events each iteration. static const int EPOLL_MAX_EVENTS = 16; -#endif static pthread_once_t gTLSOnce = PTHREAD_ONCE_INIT; static pthread_key_t gTLSKey = 0; @@ -69,7 +67,6 @@ Looper::Looper(bool allowNonCallbacks) : LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake write pipe non-blocking. errno=%d", errno); -#ifdef LOOPER_USES_EPOLL // Allocate the epoll instance and register the wake pipe. mEpollFd = epoll_create(EPOLL_SIZE_HINT); LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance. errno=%d", errno); @@ -81,31 +78,12 @@ Looper::Looper(bool allowNonCallbacks) : result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, & eventItem); LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake read pipe to epoll instance. errno=%d", errno); -#else - // Add the wake pipe to the head of the request list with a null callback. - struct pollfd requestedFd; - requestedFd.fd = mWakeReadPipeFd; - requestedFd.events = POLLIN; - mRequestedFds.push(requestedFd); - - Request request; - request.fd = mWakeReadPipeFd; - request.callback = NULL; - request.ident = 0; - request.data = NULL; - mRequests.push(request); - - mPolling = false; - mWaiters = 0; -#endif } Looper::~Looper() { close(mWakeReadPipeFd); close(mWakeWritePipeFd); -#ifdef LOOPER_USES_EPOLL close(mEpollFd); -#endif } void Looper::initTLSKey() { @@ -220,21 +198,8 @@ int Looper::pollInner(int timeoutMillis) { mResponses.clear(); mResponseIndex = 0; -#ifdef LOOPER_USES_EPOLL struct epoll_event eventItems[EPOLL_MAX_EVENTS]; int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis); -#else - // Wait for wakeAndLock() waiters to run then set mPolling to true. - mLock.lock(); - while (mWaiters != 0) { - mResume.wait(mLock); - } - mPolling = true; - mLock.unlock(); - - size_t requestedCount = mRequestedFds.size(); - int eventCount = poll(mRequestedFds.editArray(), requestedCount, timeoutMillis); -#endif // Acquire lock. mLock.lock(); @@ -263,7 +228,6 @@ int Looper::pollInner(int timeoutMillis) { ALOGD("%p ~ pollOnce - handling events from %d fds", this, eventCount); #endif -#ifdef LOOPER_USES_EPOLL for (int i = 0; i < eventCount; i++) { int fd = eventItems[i].data.fd; uint32_t epollEvents = eventItems[i].events; @@ -289,39 +253,6 @@ int Looper::pollInner(int timeoutMillis) { } } Done: ; -#else - for (size_t i = 0; i < requestedCount; i++) { - const struct pollfd& requestedFd = mRequestedFds.itemAt(i); - - short pollEvents = requestedFd.revents; - if (pollEvents) { - if (requestedFd.fd == mWakeReadPipeFd) { - if (pollEvents & POLLIN) { - awoken(); - } else { - ALOGW("Ignoring unexpected poll events 0x%x on wake read pipe.", pollEvents); - } - } else { - int events = 0; - if (pollEvents & POLLIN) events |= ALOOPER_EVENT_INPUT; - if (pollEvents & POLLOUT) events |= ALOOPER_EVENT_OUTPUT; - if (pollEvents & POLLERR) events |= ALOOPER_EVENT_ERROR; - if (pollEvents & POLLHUP) events |= ALOOPER_EVENT_HANGUP; - if (pollEvents & POLLNVAL) events |= ALOOPER_EVENT_INVALID; - pushResponse(events, mRequests.itemAt(i)); - } - if (--eventCount == 0) { - break; - } - } - } -Done: - // Set mPolling to false and wake up the wakeAndLock() waiters. - mPolling = false; - if (mWaiters != 0) { - mAwake.broadcast(); - } -#endif // Invoke pending message callbacks. mNextMessageUptime = LLONG_MAX; @@ -462,7 +393,6 @@ int Looper::addFd(int fd, int ident, int events, ALooper_callbackFunc callback, } } -#ifdef LOOPER_USES_EPOLL int epollEvents = 0; if (events & ALOOPER_EVENT_INPUT) epollEvents |= EPOLLIN; if (events & ALOOPER_EVENT_OUTPUT) epollEvents |= EPOLLOUT; @@ -498,33 +428,6 @@ int Looper::addFd(int fd, int ident, int events, ALooper_callbackFunc callback, mRequests.replaceValueAt(requestIndex, request); } } // release lock -#else - int pollEvents = 0; - if (events & ALOOPER_EVENT_INPUT) pollEvents |= POLLIN; - if (events & ALOOPER_EVENT_OUTPUT) pollEvents |= POLLOUT; - - wakeAndLock(); // acquire lock - - struct pollfd requestedFd; - requestedFd.fd = fd; - requestedFd.events = pollEvents; - - Request request; - request.fd = fd; - request.ident = ident; - request.callback = callback; - request.data = data; - ssize_t index = getRequestIndexLocked(fd); - if (index < 0) { - mRequestedFds.push(requestedFd); - mRequests.push(request); - } else { - mRequestedFds.replaceAt(requestedFd, size_t(index)); - mRequests.replaceAt(request, size_t(index)); - } - - mLock.unlock(); // release lock -#endif return 1; } @@ -533,7 +436,6 @@ int Looper::removeFd(int fd) { ALOGD("%p ~ removeFd - fd=%d", this, fd); #endif -#ifdef LOOPER_USES_EPOLL { // acquire lock AutoMutex _l(mLock); ssize_t requestIndex = mRequests.indexOfKey(fd); @@ -550,49 +452,8 @@ int Looper::removeFd(int fd) { mRequests.removeItemsAt(requestIndex); } // release lock return 1; -#else - wakeAndLock(); // acquire lock - - ssize_t index = getRequestIndexLocked(fd); - if (index >= 0) { - mRequestedFds.removeAt(size_t(index)); - mRequests.removeAt(size_t(index)); - } - - mLock.unlock(); // release lock - return index >= 0; -#endif } -#ifndef LOOPER_USES_EPOLL -ssize_t Looper::getRequestIndexLocked(int fd) { - size_t requestCount = mRequestedFds.size(); - - for (size_t i = 0; i < requestCount; i++) { - if (mRequestedFds.itemAt(i).fd == fd) { - return i; - } - } - - return -1; -} - -void Looper::wakeAndLock() { - mLock.lock(); - - mWaiters += 1; - while (mPolling) { - wake(); - mAwake.wait(mLock); - } - - mWaiters -= 1; - if (mWaiters == 0) { - mResume.signal(); - } -} -#endif - void Looper::sendMessage(const sp& handler, const Message& message) { nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); sendMessageAtTime(now, handler, message); From 970701fbd264d9b11d0937d0ac34e119cdb061df Mon Sep 17 00:00:00 2001 From: Eino-Ville Talvala Date: Thu, 31 May 2012 15:49:43 -0700 Subject: [PATCH 426/541] Add camera as an ATRACE tag Change-Id: Ib4f22d593836c4b973d122efa114d359f1529914 --- include/utils/Trace.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/utils/Trace.h b/include/utils/Trace.h index 421920635..e5cc7ec30 100644 --- a/include/utils/Trace.h +++ b/include/utils/Trace.h @@ -51,7 +51,8 @@ #define ATRACE_TAG_SYNC_MANAGER (1<<7) #define ATRACE_TAG_AUDIO (1<<8) #define ATRACE_TAG_VIDEO (1<<9) -#define ATRACE_TAG_LAST ATRACE_TAG_VIDEO +#define ATRACE_TAG_CAMERA (1<<10) +#define ATRACE_TAG_LAST ATRACE_TAG_CAMERA #define ATRACE_TAG_VALID_MASK ((ATRACE_TAG_LAST - 1) | ATRACE_TAG_LAST) From dd1b0378cec939e7c90f8e8d8216b481f9f2035a Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Thu, 31 May 2012 16:15:35 -0700 Subject: [PATCH 427/541] Support looper callbacks based on smart pointers. Bug: 6559630 Change-Id: I5a667f219f431838638acefbc9fa6afa610971bd --- include/utils/Looper.h | 54 ++++++++++++++++++++++++++++++++++++++++-- libs/utils/Looper.cpp | 48 +++++++++++++++++++++++++++---------- 2 files changed, 88 insertions(+), 14 deletions(-) diff --git a/include/utils/Looper.h b/include/utils/Looper.h index 84e3864a6..d4a0067fd 100644 --- a/include/utils/Looper.h +++ b/include/utils/Looper.h @@ -70,6 +70,9 @@ public: * A simple proxy that holds a weak reference to a message handler. */ class WeakMessageHandler : public MessageHandler { +protected: + virtual ~WeakMessageHandler(); + public: WeakMessageHandler(const wp& handler); virtual void handleMessage(const Message& message); @@ -79,6 +82,43 @@ private: }; +/** + * A looper callback. + */ +class LooperCallback : public virtual RefBase { +protected: + virtual ~LooperCallback() { } + +public: + /** + * Handles a poll event for the given file descriptor. + * It is given the file descriptor it is associated with, + * a bitmask of the poll events that were triggered (typically ALOOPER_EVENT_INPUT), + * and the data pointer that was originally supplied. + * + * Implementations should return 1 to continue receiving callbacks, or 0 + * to have this file descriptor and callback unregistered from the looper. + */ + virtual int handleEvent(int fd, int events, void* data) = 0; +}; + + +/** + * Wraps a ALooper_callbackFunc function pointer. + */ +class SimpleLooperCallback : public LooperCallback { +protected: + virtual ~SimpleLooperCallback(); + +public: + SimpleLooperCallback(ALooper_callbackFunc callback); + virtual int handleEvent(int fd, int events, void* data); + +private: + ALooper_callbackFunc mCallback; +}; + + /** * A polling loop that supports monitoring file descriptor events, optionally * using callbacks. The implementation uses epoll() internally. @@ -159,7 +199,7 @@ public: * If the same file descriptor was previously added, it is replaced. * * "fd" is the file descriptor to be added. - * "ident" is an identifier for this event, which is returned from ALooper_pollOnce(). + * "ident" is an identifier for this event, which is returned from pollOnce(). * The identifier must be >= 0, or ALOOPER_POLL_CALLBACK if providing a non-NULL callback. * "events" are the poll events to wake up on. Typically this is ALOOPER_EVENT_INPUT. * "callback" is the function to call when there is an event on the file descriptor. @@ -179,8 +219,14 @@ public: * * This method can be called on any thread. * This method may block briefly if it needs to wake the poll. + * + * The callback may either be specified as a bare function pointer or as a smart + * pointer callback object. The smart pointer should be preferred because it is + * easier to avoid races when the callback is removed from a different thread. + * See removeFd() for details. */ int addFd(int fd, int ident, int events, ALooper_callbackFunc callback, void* data); + int addFd(int fd, int ident, int events, const sp& callback, void* data); /** * Removes a previously added file descriptor from the looper. @@ -193,6 +239,10 @@ public: * by returning 0 or by calling this method, then it can be guaranteed to not be invoked * again at any later time unless registered anew. * + * A simple way to avoid this problem is to use the version of addFd() that takes + * a sp instead of a bare function pointer. The LooperCallback will + * be released at the appropriate time by the Looper. + * * Returns 1 if the file descriptor was removed, 0 if none was previously registered. * * This method can be called on any thread. @@ -273,7 +323,7 @@ private: struct Request { int fd; int ident; - ALooper_callbackFunc callback; + sp callback; void* data; }; diff --git a/libs/utils/Looper.cpp b/libs/utils/Looper.cpp index 989499362..a5e66458c 100644 --- a/libs/utils/Looper.cpp +++ b/libs/utils/Looper.cpp @@ -30,6 +30,9 @@ WeakMessageHandler::WeakMessageHandler(const wp& handler) : mHandler(handler) { } +WeakMessageHandler::~WeakMessageHandler() { +} + void WeakMessageHandler::handleMessage(const Message& message) { sp handler = mHandler.promote(); if (handler != NULL) { @@ -38,6 +41,20 @@ void WeakMessageHandler::handleMessage(const Message& message) { } +// --- SimpleLooperCallback --- + +SimpleLooperCallback::SimpleLooperCallback(ALooper_callbackFunc callback) : + mCallback(callback) { +} + +SimpleLooperCallback::~SimpleLooperCallback() { +} + +int SimpleLooperCallback::handleEvent(int fd, int events, void* data) { + return mCallback(fd, events, data); +} + + // --- Looper --- // Hint for number of file descriptors to be associated with the epoll instance. @@ -142,9 +159,8 @@ int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outDa for (;;) { while (mResponseIndex < mResponses.size()) { const Response& response = mResponses.itemAt(mResponseIndex++); - ALooper_callbackFunc callback = response.request.callback; - if (!callback) { - int ident = response.request.ident; + int ident = response.request.ident; + if (ident >= 0) { int fd = response.request.fd; int events = response.events; void* data = response.request.data; @@ -165,7 +181,7 @@ int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outDa ALOGD("%p ~ pollOnce - returning result %d", this, result); #endif if (outFd != NULL) *outFd = 0; - if (outEvents != NULL) *outEvents = NULL; + if (outEvents != NULL) *outEvents = 0; if (outData != NULL) *outData = NULL; return result; } @@ -293,20 +309,22 @@ Done: ; // Invoke all response callbacks. for (size_t i = 0; i < mResponses.size(); i++) { - const Response& response = mResponses.itemAt(i); - ALooper_callbackFunc callback = response.request.callback; - if (callback) { + Response& response = mResponses.editItemAt(i); + if (response.request.ident == ALOOPER_POLL_CALLBACK) { int fd = response.request.fd; int events = response.events; void* data = response.request.data; #if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS ALOGD("%p ~ pollOnce - invoking fd event callback %p: fd=%d, events=0x%x, data=%p", - this, callback, fd, events, data); + this, response.request.callback.get(), fd, events, data); #endif - int callbackResult = callback(fd, events, data); + int callbackResult = response.request.callback->handleEvent(fd, events, data); if (callbackResult == 0) { removeFd(fd); } + // Clear the callback reference in the response structure promptly because we + // will not clear the response vector itself until the next poll. + response.request.callback.clear(); result = ALOOPER_POLL_CALLBACK; } } @@ -376,21 +394,27 @@ void Looper::pushResponse(int events, const Request& request) { } int Looper::addFd(int fd, int ident, int events, ALooper_callbackFunc callback, void* data) { + return addFd(fd, ident, events, callback ? new SimpleLooperCallback(callback) : NULL, data); +} + +int Looper::addFd(int fd, int ident, int events, const sp& callback, void* data) { #if DEBUG_CALLBACKS ALOGD("%p ~ addFd - fd=%d, ident=%d, events=0x%x, callback=%p, data=%p", this, fd, ident, - events, callback, data); + events, callback.get(), data); #endif - if (! callback) { + if (!callback.get()) { if (! mAllowNonCallbacks) { ALOGE("Invalid attempt to set NULL callback but not allowed for this looper."); return -1; } if (ident < 0) { - ALOGE("Invalid attempt to set NULL callback with ident <= 0."); + ALOGE("Invalid attempt to set NULL callback with ident < 0."); return -1; } + } else { + ident = ALOOPER_POLL_CALLBACK; } int epollEvents = 0; From 885a2fe3b5e79918d4e4fe9d5c675b3d1d724e02 Mon Sep 17 00:00:00 2001 From: John Grossman Date: Wed, 27 Jun 2012 15:34:43 -0700 Subject: [PATCH 428/541] Utils: Fix a bug in the linear transformation code. Hand merge from ics-aah > Utils: Fix a bug in the linear transformation code. > > Fix a bug where an incorrect result would be computed if you used the > linear transformation code to do a reverse transformation (from B's > domain into A's domain) when the scaler fraction was negative. > > Change-Id: I8e5f109314d235a177ab41f65d3c4cd08cff78be > Signed-off-by: John Grossman Change-Id: Id90e18f685c61c1a89fd91c32adcf01363b3e8f3 Signed-off-by: John Grossman --- libs/utils/LinearTransform.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/libs/utils/LinearTransform.cpp b/libs/utils/LinearTransform.cpp index d752415cf..b7d28d4b3 100644 --- a/libs/utils/LinearTransform.cpp +++ b/libs/utils/LinearTransform.cpp @@ -114,6 +114,7 @@ static bool linear_transform_s64_to_s64( int64_t basis1, int32_t N, uint32_t D, + bool invert_frac, int64_t basis2, int64_t* out) { uint64_t scaled, res; @@ -137,8 +138,8 @@ static bool linear_transform_s64_to_s64( is_neg = !is_neg; if (!scale_u64_to_u64(abs_val, - ABS(N), - D, + invert_frac ? D : ABS(N), + invert_frac ? ABS(N) : D, &scaled, is_neg)) return false; // overflow/undeflow @@ -191,6 +192,7 @@ bool LinearTransform::doForwardTransform(int64_t a_in, int64_t* b_out) const { a_zero, a_to_b_numer, a_to_b_denom, + false, b_zero, b_out); } @@ -201,8 +203,9 @@ bool LinearTransform::doReverseTransform(int64_t b_in, int64_t* a_out) const { return linear_transform_s64_to_s64(b_in, b_zero, - a_to_b_denom, a_to_b_numer, + a_to_b_denom, + true, a_zero, a_out); } From e6bee12f943a5b6e7c596220dd02d5837322d09f Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Fri, 29 Jun 2012 14:12:52 -0700 Subject: [PATCH 429/541] one more step towards multiple display support - remove dependency on cached state in validateVisibility - get rid of mVertices and mTransformedBounds - get rid of validateVisibility - get rid of unlockPageFlip - handleTransaction now returns a dirty region - computevisibileregion now uses window-manager space --- include/utils/Vector.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/utils/Vector.h b/include/utils/Vector.h index e39a5b759..a89393ff2 100644 --- a/include/utils/Vector.h +++ b/include/utils/Vector.h @@ -72,11 +72,11 @@ public: //! returns number of items in the vector inline size_t size() const { return VectorImpl::size(); } - //! returns wether or not the vector is empty + //! returns whether or not the vector is empty inline bool isEmpty() const { return VectorImpl::isEmpty(); } //! returns how many items can be stored without reallocating the backing store inline size_t capacity() const { return VectorImpl::capacity(); } - //! setst the capacity. capacity can never be reduced less than size() + //! sets the capacity. capacity can never be reduced less than size() inline ssize_t setCapacity(size_t size) { return VectorImpl::setCapacity(size); } /*! @@ -102,12 +102,12 @@ public: const TYPE& mirrorItemAt(ssize_t index) const; /*! - * modifing the array + * modifying the array */ //! copy-on write support, grants write access to an item TYPE& editItemAt(size_t index); - //! grants right acces to the top of the stack (last element) + //! grants right access to the top of the stack (last element) TYPE& editTop(); /*! From af1e7b77217430cc2d423e9f552dcfb4e004539b Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Thu, 19 Jul 2012 09:17:24 -0700 Subject: [PATCH 430/541] Add elapsedRealtimeNano(), and use CLOCK_BOOTTIME where possible. Change-Id: I4231c8ca32663e6e2cb5b7c126d091f837373807 --- include/utils/SystemClock.h | 1 + include/utils/Timers.h | 5 +++-- libs/utils/SystemClock.cpp | 38 +++++++++++++++++++++++-------------- libs/utils/Timers.cpp | 3 ++- 4 files changed, 30 insertions(+), 17 deletions(-) diff --git a/include/utils/SystemClock.h b/include/utils/SystemClock.h index 7c319be13..d75264cd3 100644 --- a/include/utils/SystemClock.h +++ b/include/utils/SystemClock.h @@ -25,6 +25,7 @@ namespace android { int setCurrentTimeMillis(int64_t millis); int64_t uptimeMillis(); int64_t elapsedRealtime(); +int64_t elapsedRealtimeNano(); }; // namespace android diff --git a/include/utils/Timers.h b/include/utils/Timers.h index 8b4d32287..92f66c94c 100644 --- a/include/utils/Timers.h +++ b/include/utils/Timers.h @@ -78,9 +78,10 @@ enum { SYSTEM_TIME_REALTIME = 0, // system-wide realtime clock SYSTEM_TIME_MONOTONIC = 1, // monotonic time since unspecified starting point SYSTEM_TIME_PROCESS = 2, // high-resolution per-process clock - SYSTEM_TIME_THREAD = 3 // high-resolution per-thread clock + SYSTEM_TIME_THREAD = 3, // high-resolution per-thread clock + SYSTEM_TIME_BOOTTIME = 4 // same as SYSTEM_TIME_MONOTONIC, but including CPU suspend time }; - + // return the system-time according to the specified clock #ifdef __cplusplus nsecs_t systemTime(int clock = SYSTEM_TIME_MONOTONIC); diff --git a/libs/utils/SystemClock.cpp b/libs/utils/SystemClock.cpp index 8b8ac1031..53e06264a 100644 --- a/libs/utils/SystemClock.cpp +++ b/libs/utils/SystemClock.cpp @@ -105,8 +105,23 @@ int64_t uptimeMillis() * native public static long elapsedRealtime(); */ int64_t elapsedRealtime() +{ + return nanoseconds_to_milliseconds(elapsedRealtimeNano()); +} + +/* + * native public static long elapsedRealtimeNano(); + */ +int64_t elapsedRealtimeNano() { #ifdef HAVE_ANDROID_OS + struct timespec ts; + int result = clock_gettime(CLOCK_BOOTTIME, &ts); + if (result == 0) { + return seconds_to_nanoseconds(ts.tv_sec) + ts.tv_nsec; + } + + // CLOCK_BOOTTIME doesn't exist, fallback to /dev/alarm static int s_fd = -1; if (s_fd == -1) { @@ -114,25 +129,20 @@ int64_t elapsedRealtime() if (android_atomic_cmpxchg(-1, fd, &s_fd)) { close(fd); } + result = ioctl(s_fd, + ANDROID_ALARM_GET_TIME(ANDROID_ALARM_ELAPSED_REALTIME), &ts); } - struct timespec ts; - int result = ioctl(s_fd, - ANDROID_ALARM_GET_TIME(ANDROID_ALARM_ELAPSED_REALTIME), &ts); - if (result == 0) { - int64_t when = seconds_to_nanoseconds(ts.tv_sec) + ts.tv_nsec; - return (int64_t) nanoseconds_to_milliseconds(when); - } else { - // XXX: there was an error, probably because the driver didn't - // exist ... this should return - // a real error, like an exception! - int64_t when = systemTime(SYSTEM_TIME_MONOTONIC); - return (int64_t) nanoseconds_to_milliseconds(when); + return seconds_to_nanoseconds(ts.tv_sec) + ts.tv_nsec; } + + // XXX: there was an error, probably because the driver didn't + // exist ... this should return + // a real error, like an exception! + return systemTime(SYSTEM_TIME_MONOTONIC); #else - int64_t when = systemTime(SYSTEM_TIME_MONOTONIC); - return (int64_t) nanoseconds_to_milliseconds(when); + return systemTime(SYSTEM_TIME_MONOTONIC); #endif } diff --git a/libs/utils/Timers.cpp b/libs/utils/Timers.cpp index 64b470181..d4f85164a 100644 --- a/libs/utils/Timers.cpp +++ b/libs/utils/Timers.cpp @@ -39,7 +39,8 @@ nsecs_t systemTime(int clock) CLOCK_REALTIME, CLOCK_MONOTONIC, CLOCK_PROCESS_CPUTIME_ID, - CLOCK_THREAD_CPUTIME_ID + CLOCK_THREAD_CPUTIME_ID, + CLOCK_BOOTTIME }; struct timespec t; t.tv_sec = t.tv_nsec = 0; From 67b585149111b68afc250fec978620687892bb67 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Thu, 2 Aug 2012 18:32:23 -0700 Subject: [PATCH 431/541] We now have a real list of displays. displays can be dynamically added or removed, and the list is part of the SF's transaction. Change-Id: I4186ea39f1317c0e7c044f869004017738968fab --- include/utils/KeyedVector.h | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/include/utils/KeyedVector.h b/include/utils/KeyedVector.h index 20575ee3e..47c2c56de 100644 --- a/include/utils/KeyedVector.h +++ b/include/utils/KeyedVector.h @@ -56,7 +56,10 @@ public: inline size_t capacity() const { return mVector.capacity(); } //! setst the capacity. capacity can never be reduced less than size() inline ssize_t setCapacity(size_t size) { return mVector.setCapacity(size); } - + + // returns true if the arguments is known to be identical to this vector + inline bool isIdenticalTo(const KeyedVector& rhs) const; + /*! * accessors */ @@ -64,6 +67,7 @@ public: const VALUE& valueAt(size_t index) const; const KEY& keyAt(size_t index) const; ssize_t indexOfKey(const KEY& key) const; + const VALUE& operator[] (size_t index) const; /*! * modifying the array @@ -122,6 +126,11 @@ KeyedVector::KeyedVector() { } +template inline +bool KeyedVector::isIdenticalTo(const KeyedVector& rhs) const { + return mVector.array() == rhs.mVector.array(); +} + template inline ssize_t KeyedVector::indexOfKey(const KEY& key) const { return mVector.indexOf( key_value_pair_t(key) ); @@ -139,6 +148,11 @@ const VALUE& KeyedVector::valueAt(size_t index) const { return mVector.itemAt(index).value; } +template inline +const VALUE& KeyedVector::operator[] (size_t index) const { + return valueAt(index); +} + template inline const KEY& KeyedVector::keyAt(size_t index) const { return mVector.itemAt(index).key; From 5363183d2a2acdf9163eac57203645e841c0ca0d Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Sun, 5 Aug 2012 12:41:16 -0700 Subject: [PATCH 432/541] libutils cleanup: remove unused or seldom used code from misc.{cpp|h} Change-Id: I72206f16619d81887e3b85603494563faab3b445 --- include/utils/misc.h | 40 +-------------- libs/utils/ZipFileRO.cpp | 19 ++++++++ libs/utils/misc.cpp | 102 --------------------------------------- 3 files changed, 20 insertions(+), 141 deletions(-) diff --git a/include/utils/misc.h b/include/utils/misc.h index d7d5bc196..f1aa4325a 100644 --- a/include/utils/misc.h +++ b/include/utils/misc.h @@ -23,41 +23,12 @@ #include #include -namespace android { - /* get #of elements in a static array */ #ifndef NELEM # define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0]))) #endif -/* - * Make a copy of the string, using "new[]" instead of "malloc". Free the - * string with delete[]. - * - * Returns NULL if "str" is NULL. - */ -char* strdupNew(const char* str); - -/* - * Concatenate an argument vector into a single string. If argc is >= 0 - * it will be used; if it's < 0 then the last element in the arg vector - * must be NULL. - * - * This inserts a space between each argument. - * - * This does not automatically add double quotes around arguments with - * spaces in them. This practice is necessary for Win32, because Win32's - * CreateProcess call is stupid. - * - * The caller should delete[] the returned string. - */ -char* concatArgv(int argc, const char* const argv[]); - -/* - * Count up the number of arguments in "argv". The count does not include - * the final NULL entry. - */ -int countArgv(const char* const argv[]); +namespace android { /* * Some utility functions for working with files. These could be made @@ -79,15 +50,6 @@ FileType getFileType(const char* fileName); /* get the file's modification date; returns -1 w/errno set on failure */ time_t getFileModDate(const char* fileName); -/* - * Round up to the nearest power of 2. Handy for hash tables. - */ -unsigned int roundUpPower2(unsigned int val); - -void strreverse(char* begin, char* end); -void k_itoa(int value, char* str, int base); -char* itoa(int val, int base); - typedef void (*sysprop_change_callback)(void); void add_sysprop_change_callback(sysprop_change_callback cb, int priority); void report_sysprop_change(); diff --git a/libs/utils/ZipFileRO.cpp b/libs/utils/ZipFileRO.cpp index db17546ff..cc6221318 100644 --- a/libs/utils/ZipFileRO.cpp +++ b/libs/utils/ZipFileRO.cpp @@ -320,6 +320,25 @@ bool ZipFileRO::mapCentralDirectory(void) return true; } + +/* + * Round up to the next highest power of 2. + * + * Found on http://graphics.stanford.edu/~seander/bithacks.html. + */ +static unsigned int roundUpPower2(unsigned int val) +{ + val--; + val |= val >> 1; + val |= val >> 2; + val |= val >> 4; + val |= val >> 8; + val |= val >> 16; + val++; + + return val; +} + bool ZipFileRO::parseZipArchive(void) { bool result = false; diff --git a/libs/utils/misc.cpp b/libs/utils/misc.cpp index b3c99e6b4..445a23a01 100644 --- a/libs/utils/misc.cpp +++ b/libs/utils/misc.cpp @@ -38,90 +38,6 @@ using namespace android; namespace android { -/* - * Like strdup(), but uses C++ "new" operator instead of malloc. - */ -char* strdupNew(const char* str) -{ - char* newStr; - int len; - - if (str == NULL) - return NULL; - - len = strlen(str); - newStr = new char[len+1]; - memcpy(newStr, str, len+1); - - return newStr; -} - -/* - * Concatenate an argument vector. - */ -char* concatArgv(int argc, const char* const argv[]) -{ - char* newStr = NULL; - int len, totalLen, posn, idx; - - /* - * First, figure out the total length. - */ - totalLen = idx = 0; - while (1) { - if (idx == argc || argv[idx] == NULL) - break; - if (idx) - totalLen++; // leave a space between args - totalLen += strlen(argv[idx]); - idx++; - } - - /* - * Alloc the string. - */ - newStr = new char[totalLen +1]; - if (newStr == NULL) - return NULL; - - /* - * Finally, allocate the string and copy data over. - */ - idx = posn = 0; - while (1) { - if (idx == argc || argv[idx] == NULL) - break; - if (idx) - newStr[posn++] = ' '; - - len = strlen(argv[idx]); - memcpy(&newStr[posn], argv[idx], len); - posn += len; - - idx++; - } - - assert(posn == totalLen); - newStr[posn] = '\0'; - - return newStr; -} - -/* - * Count the #of args in an argument vector. Don't count the final NULL. - */ -int countArgv(const char* const argv[]) -{ - int count = 0; - - while (argv[count] != NULL) - count++; - - return count; -} - - -#include /* * Get a file's type. */ @@ -172,24 +88,6 @@ time_t getFileModDate(const char* fileName) return sb.st_mtime; } -/* - * Round up to the next highest power of 2. - * - * Found on http://graphics.stanford.edu/~seander/bithacks.html. - */ -unsigned int roundUpPower2(unsigned int val) -{ - val--; - val |= val >> 1; - val |= val >> 2; - val |= val >> 4; - val |= val >> 8; - val |= val >> 16; - val++; - - return val; -} - struct sysprop_change_callback_info { sysprop_change_callback callback; int priority; From bdf73c7efcfdefe990f55541b71e7869ac378090 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Thu, 9 Aug 2012 19:39:15 -0700 Subject: [PATCH 433/541] improve Vector<> safety checks - make errors that will always cause a memory corruption always fatal (for eg: KeyedVector<>::editValue{For|At}() failure) - make other errors fatal in debug mode, those that can be caught by the caller. - fix typos Change-Id: I65cc7d81035c37ce2906fc4500c50e5d5b5c49eb --- include/utils/KeyedVector.h | 10 ++++--- include/utils/SortedVector.h | 21 +++++++++----- include/utils/Vector.h | 17 +++++++----- include/utils/VectorImpl.h | 20 -------------- libs/utils/VectorImpl.cpp | 53 +++++++++++++++--------------------- 5 files changed, 52 insertions(+), 69 deletions(-) diff --git a/include/utils/KeyedVector.h b/include/utils/KeyedVector.h index 47c2c56de..c4faae0b7 100644 --- a/include/utils/KeyedVector.h +++ b/include/utils/KeyedVector.h @@ -21,6 +21,8 @@ #include #include +#include + #include #include #include @@ -50,11 +52,11 @@ public: //! returns number of items in the vector inline size_t size() const { return mVector.size(); } - //! returns wether or not the vector is empty + //! returns whether or not the vector is empty inline bool isEmpty() const { return mVector.isEmpty(); } //! returns how many items can be stored without reallocating the backing store inline size_t capacity() const { return mVector.capacity(); } - //! setst the capacity. capacity can never be reduced less than size() + //! sets the capacity. capacity can never be reduced less than size() inline ssize_t setCapacity(size_t size) { return mVector.setCapacity(size); } // returns true if the arguments is known to be identical to this vector @@ -139,7 +141,7 @@ ssize_t KeyedVector::indexOfKey(const KEY& key) const { template inline const VALUE& KeyedVector::valueFor(const KEY& key) const { ssize_t i = this->indexOfKey(key); - assert(i>=0); + LOG_ALWAYS_FATAL_IF(i<0, "%s: key not found", __PRETTY_FUNCTION__); return mVector.itemAt(i).value; } @@ -161,7 +163,7 @@ const KEY& KeyedVector::keyAt(size_t index) const { template inline VALUE& KeyedVector::editValueFor(const KEY& key) { ssize_t i = this->indexOfKey(key); - assert(i>=0); + LOG_ALWAYS_FATAL_IF(i<0, "%s: key not found", __PRETTY_FUNCTION__); return mVector.editItemAt(i).value; } diff --git a/include/utils/SortedVector.h b/include/utils/SortedVector.h index 24455253c..fd1cb8274 100644 --- a/include/utils/SortedVector.h +++ b/include/utils/SortedVector.h @@ -21,6 +21,8 @@ #include #include +#include + #include #include #include @@ -61,11 +63,11 @@ public: //! returns number of items in the vector inline size_t size() const { return VectorImpl::size(); } - //! returns wether or not the vector is empty + //! returns whether or not the vector is empty inline bool isEmpty() const { return VectorImpl::isEmpty(); } //! returns how many items can be stored without reallocating the backing store inline size_t capacity() const { return VectorImpl::capacity(); } - //! setst the capacity. capacity can never be reduced less than size() + //! sets the capacity. capacity can never be reduced less than size() inline ssize_t setCapacity(size_t size) { return VectorImpl::setCapacity(size); } /*! @@ -76,7 +78,7 @@ public: inline const TYPE* array() const; //! read-write C-style access. BE VERY CAREFUL when modifying the array - //! you ust keep it sorted! You usually don't use this function. + //! you must keep it sorted! You usually don't use this function. TYPE* editArray(); //! finds the index of an item @@ -100,7 +102,7 @@ public: const TYPE& mirrorItemAt(ssize_t index) const; /*! - * modifing the array + * modifying the array */ //! add an item in the right place (and replace the one that is there) @@ -186,7 +188,9 @@ TYPE* SortedVector::editArray() { template inline const TYPE& SortedVector::operator[](size_t index) const { - assert( index=size(), + "%s: index=%u out of range (%u)", __PRETTY_FUNCTION__, + int(index), int(size())); return *(array() + index); } @@ -197,8 +201,11 @@ const TYPE& SortedVector::itemAt(size_t index) const { template inline const TYPE& SortedVector::mirrorItemAt(ssize_t index) const { - assert( (index>0 ? index : -index)0 ? index : -index; + LOG_FATAL_IF(index>=size(), + "%s: index=%u out of range (%u)", __PRETTY_FUNCTION__, + int(index), int(size())); + return *(array() + i); } template inline diff --git a/include/utils/Vector.h b/include/utils/Vector.h index a89393ff2..506acae14 100644 --- a/include/utils/Vector.h +++ b/include/utils/Vector.h @@ -21,7 +21,8 @@ #include #include -#include +#include + #include #include @@ -271,8 +272,9 @@ TYPE* Vector::editArray() { template inline const TYPE& Vector::operator[](size_t index) const { - LOG_FATAL_IF( index>=size(), - "itemAt: index %d is past size %d", (int)index, (int)size() ); + LOG_FATAL_IF(index>=size(), + "%s: index=%u out of range (%u)", __PRETTY_FUNCTION__, + int(index), int(size())); return *(array() + index); } @@ -283,10 +285,11 @@ const TYPE& Vector::itemAt(size_t index) const { template inline const TYPE& Vector::mirrorItemAt(ssize_t index) const { - LOG_FATAL_IF( (index>0 ? index : -index)>=size(), - "mirrorItemAt: index %d is past size %d", - (int)index, (int)size() ); - return *(array() + ((index<0) ? (size()-index) : index)); + const size_t i = index>0 ? index : -index; + LOG_FATAL_IF(index>=size(), + "%s: index=%u out of range (%u)", __PRETTY_FUNCTION__, + int(index), int(size())); + return *(array() + i); } template inline diff --git a/include/utils/VectorImpl.h b/include/utils/VectorImpl.h index c4ec2ff97..b1224c65d 100644 --- a/include/utils/VectorImpl.h +++ b/include/utils/VectorImpl.h @@ -104,16 +104,6 @@ protected: virtual void do_splat(void* dest, const void* item, size_t num) const = 0; virtual void do_move_forward(void* dest, const void* from, size_t num) const = 0; virtual void do_move_backward(void* dest, const void* from, size_t num) const = 0; - - // take care of FBC... - virtual void reservedVectorImpl1(); - virtual void reservedVectorImpl2(); - virtual void reservedVectorImpl3(); - virtual void reservedVectorImpl4(); - virtual void reservedVectorImpl5(); - virtual void reservedVectorImpl6(); - virtual void reservedVectorImpl7(); - virtual void reservedVectorImpl8(); private: void* _grow(size_t where, size_t amount); @@ -165,16 +155,6 @@ public: protected: virtual int do_compare(const void* lhs, const void* rhs) const = 0; - // take care of FBC... - virtual void reservedSortedVectorImpl1(); - virtual void reservedSortedVectorImpl2(); - virtual void reservedSortedVectorImpl3(); - virtual void reservedSortedVectorImpl4(); - virtual void reservedSortedVectorImpl5(); - virtual void reservedSortedVectorImpl6(); - virtual void reservedSortedVectorImpl7(); - virtual void reservedSortedVectorImpl8(); - private: ssize_t _indexOrderOf(const void* item, size_t* order = 0) const; diff --git a/libs/utils/VectorImpl.cpp b/libs/utils/VectorImpl.cpp index e78faa820..020ec15c9 100644 --- a/libs/utils/VectorImpl.cpp +++ b/libs/utils/VectorImpl.cpp @@ -20,7 +20,8 @@ #include #include -#include +#include + #include #include #include @@ -56,9 +57,8 @@ VectorImpl::VectorImpl(const VectorImpl& rhs) VectorImpl::~VectorImpl() { - ALOG_ASSERT(!mCount, - "[%p] " - "subclasses of VectorImpl must call finish_vector()" + ALOGW_IF(mCount, + "[%p] subclasses of VectorImpl must call finish_vector()" " in their destructor. Leaking %d bytes.", this, (int)(mCount*mItemSize)); // We can't call _do_destroy() here because the vtable is already gone. @@ -66,7 +66,7 @@ VectorImpl::~VectorImpl() VectorImpl& VectorImpl::operator = (const VectorImpl& rhs) { - ALOG_ASSERT(mItemSize == rhs.mItemSize, + LOG_ALWAYS_FATAL_IF(mItemSize != rhs.mItemSize, "Vector<> have different types (this=%p, rhs=%p)", this, &rhs); if (this != &rhs) { release_storage(); @@ -251,6 +251,10 @@ ssize_t VectorImpl::replaceAt(const void* prototype, size_t index) ALOG_ASSERT(index= size()) { + return BAD_INDEX; + } + void* item = editItemLocation(index); if (item != prototype) { if (item == 0) @@ -294,10 +298,13 @@ void* VectorImpl::editItemLocation(size_t index) ALOG_ASSERT(index(buffer) + index*mItemSize; + + if (index < capacity()) { + void* buffer = editArrayImpl(); + if (buffer) { + return reinterpret_cast(buffer) + index*mItemSize; + } + } return 0; } @@ -307,9 +314,12 @@ const void* VectorImpl::itemLocation(size_t index) const "[%p] itemLocation: index=%d, capacity=%d, count=%d", this, (int)index, (int)capacity(), (int)mCount); - const void* buffer = arrayImpl(); - if (buffer) - return reinterpret_cast(buffer) + index*mItemSize; + if (index < capacity()) { + const void* buffer = arrayImpl(); + if (buffer) { + return reinterpret_cast(buffer) + index*mItemSize; + } + } return 0; } @@ -484,15 +494,6 @@ void VectorImpl::_do_move_backward(void* dest, const void* from, size_t num) con do_move_backward(dest, from, num); } -void VectorImpl::reservedVectorImpl1() { } -void VectorImpl::reservedVectorImpl2() { } -void VectorImpl::reservedVectorImpl3() { } -void VectorImpl::reservedVectorImpl4() { } -void VectorImpl::reservedVectorImpl5() { } -void VectorImpl::reservedVectorImpl6() { } -void VectorImpl::reservedVectorImpl7() { } -void VectorImpl::reservedVectorImpl8() { } - /*****************************************************************************/ SortedVectorImpl::SortedVectorImpl(size_t itemSize, uint32_t flags) @@ -608,16 +609,6 @@ ssize_t SortedVectorImpl::remove(const void* item) return i; } -void SortedVectorImpl::reservedSortedVectorImpl1() { }; -void SortedVectorImpl::reservedSortedVectorImpl2() { }; -void SortedVectorImpl::reservedSortedVectorImpl3() { }; -void SortedVectorImpl::reservedSortedVectorImpl4() { }; -void SortedVectorImpl::reservedSortedVectorImpl5() { }; -void SortedVectorImpl::reservedSortedVectorImpl6() { }; -void SortedVectorImpl::reservedSortedVectorImpl7() { }; -void SortedVectorImpl::reservedSortedVectorImpl8() { }; - - /*****************************************************************************/ }; // namespace android From 6454f461665a44b1e47d277d8b92415b5bbce2d8 Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Fri, 10 Aug 2012 20:44:39 -0700 Subject: [PATCH 434/541] Add a very simple helper function to log slow functions. Change-Id: I2e2e072206d02b572a330dd25857c161b5b563bf --- include/utils/Log.h | 38 ++++++++++++++++++++++++++++++++++++++ libs/utils/Android.mk | 1 + libs/utils/Log.cpp | 37 +++++++++++++++++++++++++++++++++++++ 3 files changed, 76 insertions(+) create mode 100644 libs/utils/Log.cpp diff --git a/include/utils/Log.h b/include/utils/Log.h index 3c6cc8bdc..98c441c80 100644 --- a/include/utils/Log.h +++ b/include/utils/Log.h @@ -29,5 +29,43 @@ #define _LIBS_UTILS_LOG_H #include +#include + +#ifdef __cplusplus + +namespace android { + +/* + * A very simple utility that yells in the log when an operation takes too long. + */ +class LogIfSlow { +public: + LogIfSlow(const char* tag, android_LogPriority priority, + int timeoutMillis, const char* message); + ~LogIfSlow(); + +private: + const char* const mTag; + const android_LogPriority mPriority; + const int mTimeoutMillis; + const char* const mMessage; + const int64_t mStart; +}; + +/* + * Writes the specified debug log message if this block takes longer than the + * specified number of milliseconds to run. Includes the time actually taken. + * + * { + * ALOGD_IF_SLOW(50, "Excessive delay doing something."); + * doSomething(); + * } + */ +#define ALOGD_IF_SLOW(timeoutMillis, message) \ + LogIfSlow _logIfSlow(LOG_TAG, ANDROID_LOG_DEBUG, timeoutMillis, message); + +} // namespace android + +#endif // __cplusplus #endif // _LIBS_UTILS_LOG_H diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk index ddfb83e12..c9f8fd4b9 100644 --- a/libs/utils/Android.mk +++ b/libs/utils/Android.mk @@ -26,6 +26,7 @@ commonSources:= \ FileMap.cpp \ Flattenable.cpp \ LinearTransform.cpp \ + Log.cpp \ PropertyMap.cpp \ RefBase.cpp \ SharedBuffer.cpp \ diff --git a/libs/utils/Log.cpp b/libs/utils/Log.cpp new file mode 100644 index 000000000..bffb56eda --- /dev/null +++ b/libs/utils/Log.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2012 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 "Log" + +#include +#include + +namespace android { + +LogIfSlow::LogIfSlow(const char* tag, android_LogPriority priority, + int timeoutMillis, const char* message) : + mTag(tag), mPriority(priority), mTimeoutMillis(timeoutMillis), mMessage(message), + mStart(systemTime(SYSTEM_TIME_BOOTTIME)) { +} + +LogIfSlow::~LogIfSlow() { + int durationMillis = nanoseconds_to_milliseconds(systemTime(SYSTEM_TIME_BOOTTIME) - mStart); + if (durationMillis > mTimeoutMillis) { + LOG_PRI(mPriority, mTag, "%s: %dms", mMessage, durationMillis); + } +} + +} // namespace android From 2497a1524dd909d0eb933544c94d2c2e9e2c3394 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Sun, 12 Aug 2012 19:37:16 -0700 Subject: [PATCH 435/541] improve [un]marshalling of non-binder objects this change introduces a new class LightFlattenable<> which is a protocol to flatten simple objects that don't require binders or file descriptors; the benefit of this protocol is that it doesn't require the objects to have a virtual table and give us a consitant way of doing this. we also introduce an implementation of this protocol for POD structures, LightFlattenablePod<>. Parcel has been update to handle this protocol automatically. Sensor, Rect, Point and Region now use this new protocol. Change-Id: Icb3ce7fa1d785249eb666f39c2129f2fc143ea4a --- include/utils/Flattenable.h | 72 +++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/include/utils/Flattenable.h b/include/utils/Flattenable.h index 852be3b6a..e40d289de 100644 --- a/include/utils/Flattenable.h +++ b/include/utils/Flattenable.h @@ -24,6 +24,11 @@ namespace android { +/* + * The Flattenable interface allows an object to serialize itself out + * to a byte-buffer and an array of file descriptors. + */ + class Flattenable { public: @@ -56,6 +61,73 @@ protected: }; +/* + * LightFlattenable is a protocol allowing object to serialize themselves out + * to a byte-buffer. + * + * LightFlattenable objects must implement this protocol. + * + * LightFlattenable doesn't require the object to be virtual. + */ +template +class LightFlattenable { +public: + // returns whether this object always flatten into the same size. + // for efficiency, this should always be inline. + inline bool isFixedSize() const; + + // returns size in bytes of the flattened object. must be a constant. + inline size_t getSize() const; + + // flattens the object into buffer. + inline status_t flatten(void* buffer) const; + + // unflattens the object from buffer of given size. + inline status_t unflatten(void const* buffer, size_t size); +}; + +template +inline bool LightFlattenable::isFixedSize() const { + return static_cast(this)->T::isFixedSize(); +} +template +inline size_t LightFlattenable::getSize() const { + return static_cast(this)->T::getSize(); +} +template +inline status_t LightFlattenable::flatten(void* buffer) const { + return static_cast(this)->T::flatten(buffer); +} +template +inline status_t LightFlattenable::unflatten(void const* buffer, size_t size) { + return static_cast(this)->T::unflatten(buffer, size); +} + +/* + * LightFlattenablePod is an implementation of the LightFlattenable protocol + * for POD (plain-old-data) objects. + */ +template +class LightFlattenablePod : public LightFlattenable { +public: + inline bool isFixedSize() const { + return true; + } + + inline size_t getSize() const { + return sizeof(T); + } + inline status_t flatten(void* buffer) const { + *reinterpret_cast(buffer) = *static_cast(this); + return NO_ERROR; + } + inline status_t unflatten(void const* buffer, size_t) { + *static_cast(this) = *reinterpret_cast(buffer); + return NO_ERROR; + } +}; + + }; // namespace android From 156eb980918355dfc8a88ae4abf4df969acfbdef Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Mon, 20 Aug 2012 14:21:29 -0700 Subject: [PATCH 436/541] get rid of mirrorItemAt() which isn't used anywhere Change-Id: Id6f2dbeed21cabc127d39538c0ff464077ada83f --- include/utils/SortedVector.h | 11 ----------- include/utils/Vector.h | 11 ----------- 2 files changed, 22 deletions(-) diff --git a/include/utils/SortedVector.h b/include/utils/SortedVector.h index fd1cb8274..2d3e82a7c 100644 --- a/include/utils/SortedVector.h +++ b/include/utils/SortedVector.h @@ -98,8 +98,6 @@ public: inline const TYPE& itemAt(size_t index) const; //! stack-usage of the vector. returns the top of the stack (last element) const TYPE& top() const; - //! same as operator [], but allows to access the vector backward (from the end) with a negative index - const TYPE& mirrorItemAt(ssize_t index) const; /*! * modifying the array @@ -199,15 +197,6 @@ const TYPE& SortedVector::itemAt(size_t index) const { return operator[](index); } -template inline -const TYPE& SortedVector::mirrorItemAt(ssize_t index) const { - const size_t i = index>0 ? index : -index; - LOG_FATAL_IF(index>=size(), - "%s: index=%u out of range (%u)", __PRETTY_FUNCTION__, - int(index), int(size())); - return *(array() + i); -} - template inline const TYPE& SortedVector::top() const { return *(array() + size() - 1); diff --git a/include/utils/Vector.h b/include/utils/Vector.h index 506acae14..7927328e8 100644 --- a/include/utils/Vector.h +++ b/include/utils/Vector.h @@ -99,8 +99,6 @@ public: inline const TYPE& itemAt(size_t index) const; //! stack-usage of the vector. returns the top of the stack (last element) const TYPE& top() const; - //! same as operator [], but allows to access the vector backward (from the end) with a negative index - const TYPE& mirrorItemAt(ssize_t index) const; /*! * modifying the array @@ -283,15 +281,6 @@ const TYPE& Vector::itemAt(size_t index) const { return operator[](index); } -template inline -const TYPE& Vector::mirrorItemAt(ssize_t index) const { - const size_t i = index>0 ? index : -index; - LOG_FATAL_IF(index>=size(), - "%s: index=%u out of range (%u)", __PRETTY_FUNCTION__, - int(index), int(size())); - return *(array() + i); -} - template inline const TYPE& Vector::top() const { return *(array() + size() - 1); From 8d0c1a0316f583bc3835dbd7241bcffdb7fef7df Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Mon, 20 Aug 2012 17:03:29 -0700 Subject: [PATCH 437/541] put back the unused virtuals in Vector<> some binaries are using these private APIs and broke (as they should!) with this change. Temporarily restore the virtuals to work around this. Bug: 6977550 Change-Id: I7c37f24b16e4d586b89205c493db5169cf87e024 --- include/utils/VectorImpl.h | 20 ++++++++++++++++++++ libs/utils/VectorImpl.cpp | 19 +++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/include/utils/VectorImpl.h b/include/utils/VectorImpl.h index b1224c65d..c4ec2ff97 100644 --- a/include/utils/VectorImpl.h +++ b/include/utils/VectorImpl.h @@ -104,6 +104,16 @@ protected: virtual void do_splat(void* dest, const void* item, size_t num) const = 0; virtual void do_move_forward(void* dest, const void* from, size_t num) const = 0; virtual void do_move_backward(void* dest, const void* from, size_t num) const = 0; + + // take care of FBC... + virtual void reservedVectorImpl1(); + virtual void reservedVectorImpl2(); + virtual void reservedVectorImpl3(); + virtual void reservedVectorImpl4(); + virtual void reservedVectorImpl5(); + virtual void reservedVectorImpl6(); + virtual void reservedVectorImpl7(); + virtual void reservedVectorImpl8(); private: void* _grow(size_t where, size_t amount); @@ -155,6 +165,16 @@ public: protected: virtual int do_compare(const void* lhs, const void* rhs) const = 0; + // take care of FBC... + virtual void reservedSortedVectorImpl1(); + virtual void reservedSortedVectorImpl2(); + virtual void reservedSortedVectorImpl3(); + virtual void reservedSortedVectorImpl4(); + virtual void reservedSortedVectorImpl5(); + virtual void reservedSortedVectorImpl6(); + virtual void reservedSortedVectorImpl7(); + virtual void reservedSortedVectorImpl8(); + private: ssize_t _indexOrderOf(const void* item, size_t* order = 0) const; diff --git a/libs/utils/VectorImpl.cpp b/libs/utils/VectorImpl.cpp index 020ec15c9..385530519 100644 --- a/libs/utils/VectorImpl.cpp +++ b/libs/utils/VectorImpl.cpp @@ -494,6 +494,15 @@ void VectorImpl::_do_move_backward(void* dest, const void* from, size_t num) con do_move_backward(dest, from, num); } +void VectorImpl::reservedVectorImpl1() { } +void VectorImpl::reservedVectorImpl2() { } +void VectorImpl::reservedVectorImpl3() { } +void VectorImpl::reservedVectorImpl4() { } +void VectorImpl::reservedVectorImpl5() { } +void VectorImpl::reservedVectorImpl6() { } +void VectorImpl::reservedVectorImpl7() { } +void VectorImpl::reservedVectorImpl8() { } + /*****************************************************************************/ SortedVectorImpl::SortedVectorImpl(size_t itemSize, uint32_t flags) @@ -609,6 +618,16 @@ ssize_t SortedVectorImpl::remove(const void* item) return i; } +void SortedVectorImpl::reservedSortedVectorImpl1() { }; +void SortedVectorImpl::reservedSortedVectorImpl2() { }; +void SortedVectorImpl::reservedSortedVectorImpl3() { }; +void SortedVectorImpl::reservedSortedVectorImpl4() { }; +void SortedVectorImpl::reservedSortedVectorImpl5() { }; +void SortedVectorImpl::reservedSortedVectorImpl6() { }; +void SortedVectorImpl::reservedSortedVectorImpl7() { }; +void SortedVectorImpl::reservedSortedVectorImpl8() { }; + + /*****************************************************************************/ }; // namespace android From cacf429b56522fe66862dd541860175f1bed739f Mon Sep 17 00:00:00 2001 From: "Tareq A. Siraj" Date: Thu, 26 Jul 2012 16:04:24 -0400 Subject: [PATCH 438/541] Fixed clang build error for libgui Fixed the order of the statements in ANDROID_SINGLETON_STATIC_INSTANCE macro so that the templated static member variable initialization comes before the instantiation of the Singleton class. This fixes the clang compile error. Change-Id: Ic47d17e152b657f2dff3191ccc3770753fdf002b Author: Tareq A. Siraj Reviewed-by: Edwin Vane --- include/utils/Singleton.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/utils/Singleton.h b/include/utils/Singleton.h index a42ce210d..c60680eab 100644 --- a/include/utils/Singleton.h +++ b/include/utils/Singleton.h @@ -65,9 +65,9 @@ private: */ #define ANDROID_SINGLETON_STATIC_INSTANCE(TYPE) \ - template class Singleton< TYPE >; \ template<> Mutex Singleton< TYPE >::sLock(Mutex::PRIVATE); \ - template<> TYPE* Singleton< TYPE >::sInstance(0); + template<> TYPE* Singleton< TYPE >::sInstance(0); \ + template class Singleton< TYPE >; // --------------------------------------------------------------------------- From 11b9acf7651bc73dc72df950cd074fee743ce5c9 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Fri, 24 Aug 2012 16:55:29 -0700 Subject: [PATCH 439/541] Revert "put back the unused virtuals in Vector<>" This reverts commit 1648d4c13ba2eff3ea14cd87ee94028458a39f97. Bug: 6977192 Change-Id: Idbb6b239aaed4fb1c054ce943f6ba06ede3492bb --- include/utils/VectorImpl.h | 20 -------------------- libs/utils/VectorImpl.cpp | 19 ------------------- 2 files changed, 39 deletions(-) diff --git a/include/utils/VectorImpl.h b/include/utils/VectorImpl.h index c4ec2ff97..b1224c65d 100644 --- a/include/utils/VectorImpl.h +++ b/include/utils/VectorImpl.h @@ -104,16 +104,6 @@ protected: virtual void do_splat(void* dest, const void* item, size_t num) const = 0; virtual void do_move_forward(void* dest, const void* from, size_t num) const = 0; virtual void do_move_backward(void* dest, const void* from, size_t num) const = 0; - - // take care of FBC... - virtual void reservedVectorImpl1(); - virtual void reservedVectorImpl2(); - virtual void reservedVectorImpl3(); - virtual void reservedVectorImpl4(); - virtual void reservedVectorImpl5(); - virtual void reservedVectorImpl6(); - virtual void reservedVectorImpl7(); - virtual void reservedVectorImpl8(); private: void* _grow(size_t where, size_t amount); @@ -165,16 +155,6 @@ public: protected: virtual int do_compare(const void* lhs, const void* rhs) const = 0; - // take care of FBC... - virtual void reservedSortedVectorImpl1(); - virtual void reservedSortedVectorImpl2(); - virtual void reservedSortedVectorImpl3(); - virtual void reservedSortedVectorImpl4(); - virtual void reservedSortedVectorImpl5(); - virtual void reservedSortedVectorImpl6(); - virtual void reservedSortedVectorImpl7(); - virtual void reservedSortedVectorImpl8(); - private: ssize_t _indexOrderOf(const void* item, size_t* order = 0) const; diff --git a/libs/utils/VectorImpl.cpp b/libs/utils/VectorImpl.cpp index 385530519..020ec15c9 100644 --- a/libs/utils/VectorImpl.cpp +++ b/libs/utils/VectorImpl.cpp @@ -494,15 +494,6 @@ void VectorImpl::_do_move_backward(void* dest, const void* from, size_t num) con do_move_backward(dest, from, num); } -void VectorImpl::reservedVectorImpl1() { } -void VectorImpl::reservedVectorImpl2() { } -void VectorImpl::reservedVectorImpl3() { } -void VectorImpl::reservedVectorImpl4() { } -void VectorImpl::reservedVectorImpl5() { } -void VectorImpl::reservedVectorImpl6() { } -void VectorImpl::reservedVectorImpl7() { } -void VectorImpl::reservedVectorImpl8() { } - /*****************************************************************************/ SortedVectorImpl::SortedVectorImpl(size_t itemSize, uint32_t flags) @@ -618,16 +609,6 @@ ssize_t SortedVectorImpl::remove(const void* item) return i; } -void SortedVectorImpl::reservedSortedVectorImpl1() { }; -void SortedVectorImpl::reservedSortedVectorImpl2() { }; -void SortedVectorImpl::reservedSortedVectorImpl3() { }; -void SortedVectorImpl::reservedSortedVectorImpl4() { }; -void SortedVectorImpl::reservedSortedVectorImpl5() { }; -void SortedVectorImpl::reservedSortedVectorImpl6() { }; -void SortedVectorImpl::reservedSortedVectorImpl7() { }; -void SortedVectorImpl::reservedSortedVectorImpl8() { }; - - /*****************************************************************************/ }; // namespace android From 919fa297fe54d16e202752bd91a0a99ff59bef0d Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Fri, 31 Aug 2012 15:41:24 -0700 Subject: [PATCH 440/541] Add a way to retrieve a Region as a SharedBuffer Change-Id: Ia53cb905fbc88f899521658545f990fb9217b1e1 --- include/utils/SharedBuffer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/utils/SharedBuffer.h b/include/utils/SharedBuffer.h index 24508b0f7..1d6d497fc 100644 --- a/include/utils/SharedBuffer.h +++ b/include/utils/SharedBuffer.h @@ -96,7 +96,7 @@ private: inline ~SharedBuffer() { } inline SharedBuffer(const SharedBuffer&); - // 16 bytes. must be sized to preserve correct alingment. + // 16 bytes. must be sized to preserve correct alignment. mutable int32_t mRefs; size_t mSize; uint32_t mReserved[2]; From e79aadd143a6467d70969e3cd4cb31a1623870f3 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Fri, 31 Aug 2012 16:20:23 -0700 Subject: [PATCH 441/541] minor SharedBuffer clean-up Change-Id: If38b7ce85806ae628c00f2c938de4e3f75142543 --- include/utils/SharedBuffer.h | 25 ++++++++----------------- libs/utils/VectorImpl.cpp | 14 +++++++------- 2 files changed, 15 insertions(+), 24 deletions(-) diff --git a/include/utils/SharedBuffer.h b/include/utils/SharedBuffer.h index 1d6d497fc..b6709537e 100644 --- a/include/utils/SharedBuffer.h +++ b/include/utils/SharedBuffer.h @@ -44,9 +44,6 @@ public: * users. */ static ssize_t dealloc(const SharedBuffer* released); - - //! get the SharedBuffer from the data pointer - static inline const SharedBuffer* sharedBuffer(const void* data); //! access the data for read inline const void* data() const; @@ -94,7 +91,8 @@ public: private: inline SharedBuffer() { } inline ~SharedBuffer() { } - inline SharedBuffer(const SharedBuffer&); + SharedBuffer(const SharedBuffer&); + SharedBuffer& operator = (const SharedBuffer&); // 16 bytes. must be sized to preserve correct alignment. mutable int32_t mRefs; @@ -104,10 +102,6 @@ private: // --------------------------------------------------------------------------- -const SharedBuffer* SharedBuffer::sharedBuffer(const void* data) { - return data ? reinterpret_cast(data)-1 : 0; -} - const void* SharedBuffer::data() const { return this + 1; } @@ -120,19 +114,16 @@ size_t SharedBuffer::size() const { return mSize; } -SharedBuffer* SharedBuffer::bufferFromData(void* data) -{ - return ((SharedBuffer*)data)-1; +SharedBuffer* SharedBuffer::bufferFromData(void* data) { + return data ? static_cast(data)-1 : 0; } -const SharedBuffer* SharedBuffer::bufferFromData(const void* data) -{ - return ((const SharedBuffer*)data)-1; +const SharedBuffer* SharedBuffer::bufferFromData(const void* data) { + return data ? static_cast(data)-1 : 0; } -size_t SharedBuffer::sizeFromData(const void* data) -{ - return (((const SharedBuffer*)data)-1)->mSize; +size_t SharedBuffer::sizeFromData(const void* data) { + return data ? bufferFromData(data)->mSize : 0; } bool SharedBuffer::onlyOwner() const { diff --git a/libs/utils/VectorImpl.cpp b/libs/utils/VectorImpl.cpp index 020ec15c9..8083bba0e 100644 --- a/libs/utils/VectorImpl.cpp +++ b/libs/utils/VectorImpl.cpp @@ -51,7 +51,7 @@ VectorImpl::VectorImpl(const VectorImpl& rhs) mFlags(rhs.mFlags), mItemSize(rhs.mItemSize) { if (mStorage) { - SharedBuffer::sharedBuffer(mStorage)->acquire(); + SharedBuffer::bufferFromData(mStorage)->acquire(); } } @@ -73,7 +73,7 @@ VectorImpl& VectorImpl::operator = (const VectorImpl& rhs) if (rhs.mCount) { mStorage = rhs.mStorage; mCount = rhs.mCount; - SharedBuffer::sharedBuffer(mStorage)->acquire(); + SharedBuffer::bufferFromData(mStorage)->acquire(); } else { mStorage = 0; mCount = 0; @@ -85,7 +85,7 @@ VectorImpl& VectorImpl::operator = (const VectorImpl& rhs) void* VectorImpl::editArrayImpl() { if (mStorage) { - SharedBuffer* sb = SharedBuffer::sharedBuffer(mStorage)->attemptEdit(); + SharedBuffer* sb = SharedBuffer::bufferFromData(mStorage)->attemptEdit(); if (sb == 0) { sb = SharedBuffer::alloc(capacity() * mItemSize); if (sb) { @@ -101,7 +101,7 @@ void* VectorImpl::editArrayImpl() size_t VectorImpl::capacity() const { if (mStorage) { - return SharedBuffer::sharedBuffer(mStorage)->size() / mItemSize; + return SharedBuffer::bufferFromData(mStorage)->size() / mItemSize; } return 0; } @@ -346,7 +346,7 @@ ssize_t VectorImpl::setCapacity(size_t new_capacity) void VectorImpl::release_storage() { if (mStorage) { - const SharedBuffer* sb = SharedBuffer::sharedBuffer(mStorage); + const SharedBuffer* sb = SharedBuffer::bufferFromData(mStorage); if (sb->release(SharedBuffer::eKeepStorage) == 1) { _do_destroy(mStorage, mCount); SharedBuffer::dealloc(sb); @@ -372,7 +372,7 @@ void* VectorImpl::_grow(size_t where, size_t amount) (mFlags & HAS_TRIVIAL_COPY) && (mFlags & HAS_TRIVIAL_DTOR)) { - const SharedBuffer* cur_sb = SharedBuffer::sharedBuffer(mStorage); + const SharedBuffer* cur_sb = SharedBuffer::bufferFromData(mStorage); SharedBuffer* sb = cur_sb->editResize(new_capacity * mItemSize); mStorage = sb->data(); } else { @@ -424,7 +424,7 @@ void VectorImpl::_shrink(size_t where, size_t amount) (mFlags & HAS_TRIVIAL_COPY) && (mFlags & HAS_TRIVIAL_DTOR)) { - const SharedBuffer* cur_sb = SharedBuffer::sharedBuffer(mStorage); + const SharedBuffer* cur_sb = SharedBuffer::bufferFromData(mStorage); SharedBuffer* sb = cur_sb->editResize(new_capacity * mItemSize); mStorage = sb->data(); } else { From ad0a959aeec262971c64e68ca1b78f4c69ab9139 Mon Sep 17 00:00:00 2001 From: Ben Cheng Date: Fri, 14 Sep 2012 14:45:34 -0700 Subject: [PATCH 442/541] Print warnings when backwards timestamps are detected. Bug: 7100774 Change-Id: I752fd1680b32ce33d17d6042d6c82e27d7ba9dd2 --- libs/utils/SystemClock.cpp | 60 ++++++++++++++++++++++++++++++++++---- 1 file changed, 55 insertions(+), 5 deletions(-) diff --git a/libs/utils/SystemClock.cpp b/libs/utils/SystemClock.cpp index 53e06264a..fe4ae917f 100644 --- a/libs/utils/SystemClock.cpp +++ b/libs/utils/SystemClock.cpp @@ -109,6 +109,43 @@ int64_t elapsedRealtime() return nanoseconds_to_milliseconds(elapsedRealtimeNano()); } +#define METHOD_CLOCK_GETTIME 0 +#define METHOD_IOCTL 1 +#define METHOD_SYSTEMTIME 2 + +static const char *gettime_method_names[] = { + "clock_gettime", + "ioctl", + "systemTime", +}; + +static inline void checkTimeStamps(int64_t timestamp, + int64_t volatile *prevTimestampPtr, + int volatile *prevMethodPtr, + int curMethod) +{ + /* + * Disable the check for SDK since the prebuilt toolchain doesn't contain + * gettid, and int64_t is different on the ARM platform + * (ie long vs long long). + */ +#ifdef ARCH_ARM + int64_t prevTimestamp = *prevTimestampPtr; + int prevMethod = *prevMethodPtr; + + if (timestamp < prevTimestamp) { + ALOGW("time going backwards: prev %lld(%s) vs now %lld(%s), tid=%d", + prevTimestamp, gettime_method_names[prevMethod], + timestamp, gettime_method_names[curMethod], + gettid()); + } + // NOTE - not atomic and may generate spurious warnings if the 64-bit + // write is interrupted or not observed as a whole. + *prevTimestampPtr = timestamp; + *prevMethodPtr = curMethod; +#endif +} + /* * native public static long elapsedRealtimeNano(); */ @@ -117,8 +154,15 @@ int64_t elapsedRealtimeNano() #ifdef HAVE_ANDROID_OS struct timespec ts; int result = clock_gettime(CLOCK_BOOTTIME, &ts); + int64_t timestamp; + static volatile int64_t prevTimestamp; + static volatile int prevMethod; + if (result == 0) { - return seconds_to_nanoseconds(ts.tv_sec) + ts.tv_nsec; + timestamp = seconds_to_nanoseconds(ts.tv_sec) + ts.tv_nsec; + checkTimeStamps(timestamp, &prevTimestamp, &prevMethod, + METHOD_CLOCK_GETTIME); + return timestamp; } // CLOCK_BOOTTIME doesn't exist, fallback to /dev/alarm @@ -129,18 +173,24 @@ int64_t elapsedRealtimeNano() if (android_atomic_cmpxchg(-1, fd, &s_fd)) { close(fd); } - result = ioctl(s_fd, - ANDROID_ALARM_GET_TIME(ANDROID_ALARM_ELAPSED_REALTIME), &ts); } + result = ioctl(s_fd, + ANDROID_ALARM_GET_TIME(ANDROID_ALARM_ELAPSED_REALTIME), &ts); + if (result == 0) { - return seconds_to_nanoseconds(ts.tv_sec) + ts.tv_nsec; + timestamp = seconds_to_nanoseconds(ts.tv_sec) + ts.tv_nsec; + checkTimeStamps(timestamp, &prevTimestamp, &prevMethod, METHOD_IOCTL); + return timestamp; } // XXX: there was an error, probably because the driver didn't // exist ... this should return // a real error, like an exception! - return systemTime(SYSTEM_TIME_MONOTONIC); + timestamp = systemTime(SYSTEM_TIME_MONOTONIC); + checkTimeStamps(timestamp, &prevTimestamp, &prevMethod, + METHOD_SYSTEMTIME); + return timestamp; #else return systemTime(SYSTEM_TIME_MONOTONIC); #endif From f4722b5910eb4e1394763da636b72a8431baea5b Mon Sep 17 00:00:00 2001 From: Ben Cheng Date: Wed, 19 Sep 2012 14:53:10 -0700 Subject: [PATCH 443/541] Disable the use of clock_gettime for now. Bug: 7100774 Change-Id: I6ede2a37a5d485134fe419b5dc766f70ae4af9d4 --- libs/utils/SystemClock.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/libs/utils/SystemClock.cpp b/libs/utils/SystemClock.cpp index fe4ae917f..ec2d82e01 100644 --- a/libs/utils/SystemClock.cpp +++ b/libs/utils/SystemClock.cpp @@ -153,17 +153,25 @@ int64_t elapsedRealtimeNano() { #ifdef HAVE_ANDROID_OS struct timespec ts; - int result = clock_gettime(CLOCK_BOOTTIME, &ts); + int result; int64_t timestamp; static volatile int64_t prevTimestamp; static volatile int prevMethod; +#if 0 + /* + * b/7100774 + * clock_gettime appears to have clock skews and can sometimes return + * backwards values. Disable its use until we find out what's wrong. + */ + result = clock_gettime(CLOCK_BOOTTIME, &ts); if (result == 0) { timestamp = seconds_to_nanoseconds(ts.tv_sec) + ts.tv_nsec; checkTimeStamps(timestamp, &prevTimestamp, &prevMethod, METHOD_CLOCK_GETTIME); return timestamp; } +#endif // CLOCK_BOOTTIME doesn't exist, fallback to /dev/alarm static int s_fd = -1; From 2fe13f185de6e94fb88f17a832a6b3fcc38587c7 Mon Sep 17 00:00:00 2001 From: Kenny Root Date: Tue, 2 Oct 2012 09:09:10 -0700 Subject: [PATCH 444/541] Update tests for new build target Change-Id: Ia1740d1b2c0b611c4559c5c846b12fb5c3e81b07 --- libs/utils/tests/Android.mk | 42 +++++++++++++------------------------ 1 file changed, 15 insertions(+), 27 deletions(-) diff --git a/libs/utils/tests/Android.mk b/libs/utils/tests/Android.mk index 0a5b3795a..5b2b5b1c1 100644 --- a/libs/utils/tests/Android.mk +++ b/libs/utils/tests/Android.mk @@ -4,42 +4,30 @@ include $(CLEAR_VARS) # Build the unit tests. test_src_files := \ - BasicHashtable_test.cpp \ - BlobCache_test.cpp \ - Looper_test.cpp \ - String8_test.cpp \ - Unicode_test.cpp \ - Vector_test.cpp \ - ZipFileRO_test.cpp + BasicHashtable_test.cpp \ + BlobCache_test.cpp \ + Looper_test.cpp \ + String8_test.cpp \ + Unicode_test.cpp \ + Vector_test.cpp \ + ZipFileRO_test.cpp shared_libraries := \ - libz \ - liblog \ - libcutils \ - libutils \ - libstlport + libz \ + liblog \ + libcutils \ + libutils \ + libstlport static_libraries := \ - libgtest \ - libgtest_main - -c_includes := \ - external/zlib \ - external/icu4c/common \ - bionic \ - bionic/libstdc++/include \ - external/gtest/include \ - external/stlport/stlport - -module_tags := eng tests + libgtest \ + libgtest_main $(foreach file,$(test_src_files), \ $(eval include $(CLEAR_VARS)) \ $(eval LOCAL_SHARED_LIBRARIES := $(shared_libraries)) \ $(eval LOCAL_STATIC_LIBRARIES := $(static_libraries)) \ - $(eval LOCAL_C_INCLUDES := $(c_includes)) \ $(eval LOCAL_SRC_FILES := $(file)) \ $(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \ - $(eval LOCAL_MODULE_TAGS := $(module_tags)) \ - $(eval include $(BUILD_EXECUTABLE)) \ + $(eval include $(BUILD_NATIVE_TEST)) \ ) From 5625087e6e56493ad26d4171b78687d17c7136f4 Mon Sep 17 00:00:00 2001 From: Kenny Root Date: Thu, 11 Oct 2012 23:42:05 -0700 Subject: [PATCH 445/541] Add TEMP_FAILURE_RETRY around open and write calls Bug: 7330849 Change-Id: I9aef3c3d3a248c3eea7ca060124ad6decaa6b4da --- libs/utils/ZipFileRO.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/libs/utils/ZipFileRO.cpp b/libs/utils/ZipFileRO.cpp index cc6221318..ef49c0f2c 100644 --- a/libs/utils/ZipFileRO.cpp +++ b/libs/utils/ZipFileRO.cpp @@ -140,7 +140,7 @@ status_t ZipFileRO::open(const char* zipFileName) /* * Open and map the specified file. */ - fd = ::open(zipFileName, O_RDONLY | O_BINARY); + fd = TEMP_FAILURE_RETRY(::open(zipFileName, O_RDONLY | O_BINARY)); if (fd < 0) { ALOGW("Unable to open zip '%s': %s\n", zipFileName, strerror(errno)); return NAME_NOT_FOUND; @@ -771,7 +771,7 @@ bool ZipFileRO::uncompressEntry(ZipEntryRO entry, int fd) const ptr = (const unsigned char*) file->getDataPtr(); if (method == kCompressStored) { - ssize_t actual = write(fd, ptr, uncompLen); + ssize_t actual = TEMP_FAILURE_RETRY(write(fd, ptr, uncompLen)); if (actual < 0) { ALOGE("Write failed: %s\n", strerror(errno)); goto unmap; @@ -920,9 +920,12 @@ bail: (zerr == Z_STREAM_END && zstream.avail_out != sizeof(writeBuf))) { long writeSize = zstream.next_out - writeBuf; - int cc = write(fd, writeBuf, writeSize); - if (cc != (int) writeSize) { - ALOGW("write failed in inflate (%d vs %ld)\n", cc, writeSize); + int cc = TEMP_FAILURE_RETRY(write(fd, writeBuf, writeSize)); + if (cc < 0) { + ALOGW("write failed in inflate: %s", strerror(errno)); + goto z_bail; + } else if (cc != (int) writeSize) { + ALOGW("write failed in inflate (%d vs %ld)", cc, writeSize); goto z_bail; } From b9fd6f986492d74125274722b8ea1a3819cd216a Mon Sep 17 00:00:00 2001 From: Kenny Root Date: Tue, 16 Oct 2012 11:34:02 -0700 Subject: [PATCH 446/541] Add TEMP_FAILURE_RETRY to ZipUtils Change-Id: I275c415f14eeffaf9a58d45f3ea014d766441ec3 --- include/utils/Compat.h | 23 +++++++++++++++++++++++ libs/utils/ZipFileRO.cpp | 24 +----------------------- libs/utils/ZipUtils.cpp | 10 ++++++---- 3 files changed, 30 insertions(+), 27 deletions(-) diff --git a/include/utils/Compat.h b/include/utils/Compat.h index 18192661c..fb7748eaa 100644 --- a/include/utils/Compat.h +++ b/include/utils/Compat.h @@ -39,4 +39,27 @@ static inline ssize_t pread64(int fd, void* buf, size_t nbytes, off64_t offset) #endif /* !HAVE_OFF64_T */ +#if HAVE_PRINTF_ZD +# define ZD "%zd" +# define ZD_TYPE ssize_t +#else +# define ZD "%ld" +# define ZD_TYPE long +#endif + +/* + * TEMP_FAILURE_RETRY is defined by some, but not all, versions of + * . (Alas, it is not as standard as we'd hoped!) So, if it's + * not already defined, then define it here. + */ +#ifndef TEMP_FAILURE_RETRY +/* Used to retry syscalls that can return EINTR. */ +#define TEMP_FAILURE_RETRY(exp) ({ \ + typeof (exp) _rc; \ + do { \ + _rc = (exp); \ + } while (_rc == -1 && errno == EINTR); \ + _rc; }) +#endif + #endif /* __LIB_UTILS_COMPAT_H */ diff --git a/libs/utils/ZipFileRO.cpp b/libs/utils/ZipFileRO.cpp index ef49c0f2c..a1bfedb37 100644 --- a/libs/utils/ZipFileRO.cpp +++ b/libs/utils/ZipFileRO.cpp @@ -20,6 +20,7 @@ #define LOG_TAG "zipro" //#define LOG_NDEBUG 0 #include +#include #include #include #include @@ -32,14 +33,6 @@ #include #include -#if HAVE_PRINTF_ZD -# define ZD "%zd" -# define ZD_TYPE ssize_t -#else -# define ZD "%ld" -# define ZD_TYPE long -#endif - /* * We must open binary files using open(path, ... | O_BINARY) under Windows. * Otherwise strange read errors will happen. @@ -48,21 +41,6 @@ # define O_BINARY 0 #endif -/* - * TEMP_FAILURE_RETRY is defined by some, but not all, versions of - * . (Alas, it is not as standard as we'd hoped!) So, if it's - * not already defined, then define it here. - */ -#ifndef TEMP_FAILURE_RETRY -/* Used to retry syscalls that can return EINTR. */ -#define TEMP_FAILURE_RETRY(exp) ({ \ - typeof (exp) _rc; \ - do { \ - _rc = (exp); \ - } while (_rc == -1 && errno == EINTR); \ - _rc; }) -#endif - using namespace android; /* diff --git a/libs/utils/ZipUtils.cpp b/libs/utils/ZipUtils.cpp index cf5467ba5..a43bbb0ca 100644 --- a/libs/utils/ZipUtils.cpp +++ b/libs/utils/ZipUtils.cpp @@ -21,6 +21,7 @@ #define LOG_TAG "ziputil" #include +#include #include #include @@ -98,10 +99,11 @@ using namespace android; ALOGV("+++ reading %ld bytes (%ld left)\n", getSize, compRemaining); - int cc = read(fd, readBuf, getSize); - if (cc != (int) getSize) { - ALOGD("inflate read failed (%d vs %ld)\n", - cc, getSize); + int cc = TEMP_FAILURE_RETRY(read(fd, readBuf, getSize)); + if (cc < 0) { + ALOGW("inflate read failed: %s", strerror(errno)); + } else if (cc != (int) getSize) { + ALOGW("inflate read failed (%d vs %ld)", cc, getSize); goto z_bail; } From 3fc49adfd2887d01e54c4ad6a62eae5383101f62 Mon Sep 17 00:00:00 2001 From: Romain Guy Date: Thu, 18 Oct 2012 16:16:10 -0700 Subject: [PATCH 447/541] Add runtime debugging capabilities to OpenGL The shell property debug.egl.trace can now be set to: 0 disables tracing 1 logs all GL calls error checks glGetError after every GL call, logs a stack trace on error systrace logs each GL call to systrace Change-Id: I34a2a2d4e19c373fd9eaa1b0cd93e67c87378996 --- include/utils/Trace.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/utils/Trace.h b/include/utils/Trace.h index e5cc7ec30..93e228546 100644 --- a/include/utils/Trace.h +++ b/include/utils/Trace.h @@ -67,6 +67,11 @@ // function body. #define ATRACE_CALL() android::ScopedTrace ___tracer(ATRACE_TAG, __FUNCTION__) +// ATRACE_NAME traces the beginning and end of the current function. To trace +// the correct start and end times this macro should be the first line of the +// function body. +#define ATRACE_NAME(name) android::ScopedTrace ___tracer(ATRACE_TAG, name) + // ATRACE_INT traces a named integer value. This can be used to track how the // value changes over time in a trace. #define ATRACE_INT(name, value) android::Tracer::traceCounter(ATRACE_TAG, name, value) From d49555291a356fae02430a5f8fa33b37306bec42 Mon Sep 17 00:00:00 2001 From: Dave Burke Date: Wed, 24 Oct 2012 17:30:31 -0700 Subject: [PATCH 448/541] Revert "Revert "put back the unused virtuals in Vector<>"" This reverts commit 225c66a48cdc3acef21ee380dc134449749d3cb3 Change-Id: If31a04b81052cbc7dd7bf237c07107c33066d03d --- include/utils/VectorImpl.h | 20 ++++++++++++++++++++ libs/utils/VectorImpl.cpp | 19 +++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/include/utils/VectorImpl.h b/include/utils/VectorImpl.h index b1224c65d..c4ec2ff97 100644 --- a/include/utils/VectorImpl.h +++ b/include/utils/VectorImpl.h @@ -104,6 +104,16 @@ protected: virtual void do_splat(void* dest, const void* item, size_t num) const = 0; virtual void do_move_forward(void* dest, const void* from, size_t num) const = 0; virtual void do_move_backward(void* dest, const void* from, size_t num) const = 0; + + // take care of FBC... + virtual void reservedVectorImpl1(); + virtual void reservedVectorImpl2(); + virtual void reservedVectorImpl3(); + virtual void reservedVectorImpl4(); + virtual void reservedVectorImpl5(); + virtual void reservedVectorImpl6(); + virtual void reservedVectorImpl7(); + virtual void reservedVectorImpl8(); private: void* _grow(size_t where, size_t amount); @@ -155,6 +165,16 @@ public: protected: virtual int do_compare(const void* lhs, const void* rhs) const = 0; + // take care of FBC... + virtual void reservedSortedVectorImpl1(); + virtual void reservedSortedVectorImpl2(); + virtual void reservedSortedVectorImpl3(); + virtual void reservedSortedVectorImpl4(); + virtual void reservedSortedVectorImpl5(); + virtual void reservedSortedVectorImpl6(); + virtual void reservedSortedVectorImpl7(); + virtual void reservedSortedVectorImpl8(); + private: ssize_t _indexOrderOf(const void* item, size_t* order = 0) const; diff --git a/libs/utils/VectorImpl.cpp b/libs/utils/VectorImpl.cpp index 8083bba0e..c3257bb3e 100644 --- a/libs/utils/VectorImpl.cpp +++ b/libs/utils/VectorImpl.cpp @@ -494,6 +494,15 @@ void VectorImpl::_do_move_backward(void* dest, const void* from, size_t num) con do_move_backward(dest, from, num); } +void VectorImpl::reservedVectorImpl1() { } +void VectorImpl::reservedVectorImpl2() { } +void VectorImpl::reservedVectorImpl3() { } +void VectorImpl::reservedVectorImpl4() { } +void VectorImpl::reservedVectorImpl5() { } +void VectorImpl::reservedVectorImpl6() { } +void VectorImpl::reservedVectorImpl7() { } +void VectorImpl::reservedVectorImpl8() { } + /*****************************************************************************/ SortedVectorImpl::SortedVectorImpl(size_t itemSize, uint32_t flags) @@ -609,6 +618,16 @@ ssize_t SortedVectorImpl::remove(const void* item) return i; } +void SortedVectorImpl::reservedSortedVectorImpl1() { }; +void SortedVectorImpl::reservedSortedVectorImpl2() { }; +void SortedVectorImpl::reservedSortedVectorImpl3() { }; +void SortedVectorImpl::reservedSortedVectorImpl4() { }; +void SortedVectorImpl::reservedSortedVectorImpl5() { }; +void SortedVectorImpl::reservedSortedVectorImpl6() { }; +void SortedVectorImpl::reservedSortedVectorImpl7() { }; +void SortedVectorImpl::reservedSortedVectorImpl8() { }; + + /*****************************************************************************/ }; // namespace android From 61db1669f491b221411269f62bd1cb458a543181 Mon Sep 17 00:00:00 2001 From: Jason Simmons Date: Thu, 25 Oct 2012 15:58:43 -0700 Subject: [PATCH 449/541] Ensure that Vector::erase() returns a valid iterator Vector::erase may reallocate the Vector's storage while removing an element. However, erase() calls begin() before calling removeItemsAt(), thus caching a pointer the the Vector's old storage. If the storage is reallocated, the iterator returned by erase() will be based on the old storage pointer and will thus be invalid. Change-Id: I2450c55fd418e6b1c558a4ca7c024573abbaa098 --- include/utils/Vector.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/utils/Vector.h b/include/utils/Vector.h index 7927328e8..f3020d63d 100644 --- a/include/utils/Vector.h +++ b/include/utils/Vector.h @@ -188,7 +188,8 @@ public: inline void push_back(const TYPE& item) { insertAt(item, size(), 1); } inline void push_front(const TYPE& item) { insertAt(item, 0, 1); } inline iterator erase(iterator pos) { - return begin() + removeItemsAt(pos-array()); + ssize_t index = removeItemsAt(pos-array()); + return begin() + index; } protected: From fe34e45c217e67e32fa56b7e01fd4163a621c647 Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Mon, 30 Apr 2012 16:03:30 -0700 Subject: [PATCH 450/541] Continue removing property debug.sys.noschedgroups Also 0 means gettid() for get_sched_policy() and set_sched_policy(). Change-Id: Ic12edc3df6c9b3e99eae5cffaf9f6fe56cf14043 --- libs/utils/Threads.cpp | 45 ++++++++---------------------------------- 1 file changed, 8 insertions(+), 37 deletions(-) diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp index a25a81fbe..f201fc744 100644 --- a/libs/utils/Threads.cpp +++ b/libs/utils/Threads.cpp @@ -68,20 +68,6 @@ using namespace android; typedef void* (*android_pthread_entry)(void*); -static pthread_once_t gDoSchedulingGroupOnce = PTHREAD_ONCE_INIT; -static bool gDoSchedulingGroup = true; - -static void checkDoSchedulingGroup(void) { - char buf[PROPERTY_VALUE_MAX]; - int len = property_get("debug.sys.noschedgroups", buf, ""); - if (len > 0) { - int temp; - if (sscanf(buf, "%d", &temp) == 1) { - gDoSchedulingGroup = temp == 0; - } - } -} - struct thread_data_t { thread_func_t entryFunction; void* userData; @@ -97,15 +83,10 @@ struct thread_data_t { char * name = t->threadName; delete t; setpriority(PRIO_PROCESS, 0, prio); - pthread_once(&gDoSchedulingGroupOnce, checkDoSchedulingGroup); - if (gDoSchedulingGroup) { - if (prio >= ANDROID_PRIORITY_BACKGROUND) { - set_sched_policy(androidGetTid(), SP_BACKGROUND); - } else if (prio > ANDROID_PRIORITY_AUDIO) { - set_sched_policy(androidGetTid(), SP_FOREGROUND); - } else { - // defaults to that of parent, or as set by requestPriority() - } + if (prio >= ANDROID_PRIORITY_BACKGROUND) { + set_sched_policy(0, SP_BACKGROUND); + } else { + set_sched_policy(0, SP_FOREGROUND); } if (name) { @@ -333,20 +314,10 @@ int androidSetThreadPriority(pid_t tid, int pri) #if defined(HAVE_PTHREADS) int lasterr = 0; - pthread_once(&gDoSchedulingGroupOnce, checkDoSchedulingGroup); - if (gDoSchedulingGroup) { - // set_sched_policy does not support tid == 0 - int policy_tid; - if (tid == 0) { - policy_tid = androidGetTid(); - } else { - policy_tid = tid; - } - if (pri >= ANDROID_PRIORITY_BACKGROUND) { - rc = set_sched_policy(policy_tid, SP_BACKGROUND); - } else if (getpriority(PRIO_PROCESS, tid) >= ANDROID_PRIORITY_BACKGROUND) { - rc = set_sched_policy(policy_tid, SP_FOREGROUND); - } + if (pri >= ANDROID_PRIORITY_BACKGROUND) { + rc = set_sched_policy(tid, SP_BACKGROUND); + } else if (getpriority(PRIO_PROCESS, tid) >= ANDROID_PRIORITY_BACKGROUND) { + rc = set_sched_policy(tid, SP_FOREGROUND); } if (rc) { From b6ea175b6b4d0aaac85ed6cd8ccac01ab896486b Mon Sep 17 00:00:00 2001 From: Raph Levien Date: Thu, 25 Oct 2012 23:11:13 -0700 Subject: [PATCH 451/541] Add an LRU cache plus hashing primitives This patch adds a hashtable-based LRU cache. This should be significantly higher performance than the GenerationCache it is intended to replace. It is a large part of the fix for bug 7271109 TextLayoutCache low-level performance issues. We added a new method to BasicHashtable to detect when rehashing is needed, because the internal linked list pointers would get invalidated by that rehashing. Also, the hash_type specialized to pointers had a small flaw. Change-Id: I950c2083f96519777b851dbe157100e0a334caec --- include/utils/BasicHashtable.h | 8 + include/utils/JenkinsHash.h | 44 +++++ include/utils/LruCache.h | 199 ++++++++++++++++++++ include/utils/TypeHelpers.h | 2 +- libs/utils/Android.mk | 1 + libs/utils/BasicHashtable.cpp | 2 +- libs/utils/JenkinsHash.cpp | 64 +++++++ libs/utils/tests/Android.mk | 1 + libs/utils/tests/LruCache_test.cpp | 291 +++++++++++++++++++++++++++++ 9 files changed, 610 insertions(+), 2 deletions(-) create mode 100644 include/utils/JenkinsHash.h create mode 100644 include/utils/LruCache.h create mode 100644 libs/utils/JenkinsHash.cpp create mode 100644 libs/utils/tests/LruCache_test.cpp diff --git a/include/utils/BasicHashtable.h b/include/utils/BasicHashtable.h index fdf97385f..7a6c96cef 100644 --- a/include/utils/BasicHashtable.h +++ b/include/utils/BasicHashtable.h @@ -328,6 +328,14 @@ public: BasicHashtableImpl::rehash(minimumCapacity, loadFactor); } + /* Determines whether there is room to add another entry without rehashing. + * When this returns true, a subsequent add() operation is guaranteed to + * complete without performing a rehash. + */ + inline bool hasMoreRoom() const { + return mCapacity > mFilledBuckets; + } + protected: static inline const TEntry& entryFor(const Bucket& bucket) { return reinterpret_cast(bucket.entry); diff --git a/include/utils/JenkinsHash.h b/include/utils/JenkinsHash.h new file mode 100644 index 000000000..e964e6f92 --- /dev/null +++ b/include/utils/JenkinsHash.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2012 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. + */ + +/* Implementation of Jenkins one-at-a-time hash function. These choices are + * optimized for code size and portability, rather than raw speed. But speed + * should still be quite good. + **/ + +#include + +namespace android { + +/* The Jenkins hash of a sequence of 32 bit words A, B, C is: + * Whiten(Mix(Mix(Mix(0, A), B), C)) */ + +inline uint32_t JenkinsHashMix(uint32_t hash, uint32_t data) { + hash += data; + hash += (hash << 10); + hash ^= (hash >> 6); + return hash; +} + +hash_t JenkinsHashWhiten(uint32_t hash); + +/* Helpful utility functions for hashing data in 32 bit chunks */ +uint32_t JenkinsHashMixBytes(uint32_t hash, const uint8_t* bytes, size_t size); + +uint32_t JenkinsHashMixShorts(uint32_t hash, const uint16_t* shorts, size_t size); + +} + diff --git a/include/utils/LruCache.h b/include/utils/LruCache.h new file mode 100644 index 000000000..af3931596 --- /dev/null +++ b/include/utils/LruCache.h @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2012 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 +#include +#include + +namespace android { + +// OnEntryRemoved is defined in GenerationCache.h, but maybe should move here. + +template +class LruCache { +public: + explicit LruCache(uint32_t maxCapacity); + + enum Capacity { + kUnlimitedCapacity, + }; + + void setOnEntryRemovedListener(OnEntryRemoved* listener); + size_t size() const; + const TValue& get(const TKey& key); + bool put(const TKey& key, const TValue& value); + bool remove(const TKey& key); + bool removeOldest(); + void clear(); + +private: + LruCache(const LruCache& that); // disallow copy constructor + + struct Entry { + TKey key; + TValue value; + Entry* parent; + Entry* child; + + Entry(TKey key_, TValue value_) : key(key_), value(value_), parent(NULL), child(NULL) { + } + const TKey& getKey() const { return key; } + }; + + void attachToCache(Entry& entry); + void detachFromCache(Entry& entry); + void rehash(size_t newCapacity); + + UniquePtr > mTable; + OnEntryRemoved* mListener; + Entry* mOldest; + Entry* mYoungest; + uint32_t mMaxCapacity; + TValue mNullValue; +}; + +// Implementation is here, because it's fully templated +template +LruCache::LruCache(uint32_t maxCapacity): mMaxCapacity(maxCapacity), + mNullValue(NULL), mTable(new BasicHashtable), mYoungest(NULL), mOldest(NULL), + mListener(NULL) { +}; + +template +void LruCache::setOnEntryRemovedListener(OnEntryRemoved* listener) { + mListener = listener; +} + +template +size_t LruCache::size() const { + return mTable->size(); +} + +template +const TValue& LruCache::get(const TKey& key) { + hash_t hash = hash_type(key); + ssize_t index = mTable->find(-1, hash, key); + if (index == -1) { + return mNullValue; + } + Entry& entry = mTable->editEntryAt(index); + detachFromCache(entry); + attachToCache(entry); + return entry.value; +} + +template +bool LruCache::put(const TKey& key, const TValue& value) { + if (mMaxCapacity != kUnlimitedCapacity && size() >= mMaxCapacity) { + removeOldest(); + } + + hash_t hash = hash_type(key); + ssize_t index = mTable->find(-1, hash, key); + if (index >= 0) { + return false; + } + if (!mTable->hasMoreRoom()) { + rehash(mTable->capacity() * 2); + } + + // Would it be better to initialize a blank entry and assign key, value? + Entry initEntry(key, value); + index = mTable->add(hash, initEntry); + Entry& entry = mTable->editEntryAt(index); + attachToCache(entry); + return true; +} + +template +bool LruCache::remove(const TKey& key) { + hash_t hash = hash_type(key); + ssize_t index = mTable->find(-1, hash, key); + if (index < 0) { + return false; + } + Entry& entry = mTable->editEntryAt(index); + if (mListener) { + (*mListener)(entry.key, entry.value); + } + detachFromCache(entry); + mTable->removeAt(index); + return true; +} + +template +bool LruCache::removeOldest() { + if (mOldest != NULL) { + return remove(mOldest->key); + // TODO: should probably abort if false + } + return false; +} + +template +void LruCache::clear() { + if (mListener) { + for (Entry* p = mOldest; p != NULL; p = p->child) { + (*mListener)(p->key, p->value); + } + } + mYoungest = NULL; + mOldest = NULL; + mTable->clear(); +} + +template +void LruCache::attachToCache(Entry& entry) { + if (mYoungest == NULL) { + mYoungest = mOldest = &entry; + } else { + entry.parent = mYoungest; + mYoungest->child = &entry; + mYoungest = &entry; + } +} + +template +void LruCache::detachFromCache(Entry& entry) { + if (entry.parent != NULL) { + entry.parent->child = entry.child; + } else { + mOldest = entry.child; + } + if (entry.child != NULL) { + entry.child->parent = entry.parent; + } else { + mYoungest = entry.parent; + } + + entry.parent = NULL; + entry.child = NULL; +} + +template +void LruCache::rehash(size_t newCapacity) { + UniquePtr > oldTable(mTable.release()); + Entry* oldest = mOldest; + + mOldest = NULL; + mYoungest = NULL; + mTable.reset(new BasicHashtable(newCapacity)); + for (Entry* p = oldest; p != NULL; p = p->child) { + put(p->key, p->value); + } +} + +} diff --git a/include/utils/TypeHelpers.h b/include/utils/TypeHelpers.h index 2bf33c332..13c908159 100644 --- a/include/utils/TypeHelpers.h +++ b/include/utils/TypeHelpers.h @@ -291,7 +291,7 @@ ANDROID_INT64_HASH(uint64_t) ANDROID_REINTERPRET_HASH(float, uint32_t) ANDROID_REINTERPRET_HASH(double, uint64_t) -template inline hash_t hash_type(const T*& value) { +template inline hash_t hash_type(T* const & value) { return hash_type(uintptr_t(value)); } diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk index c9f8fd4b9..73a98d5f8 100644 --- a/libs/utils/Android.mk +++ b/libs/utils/Android.mk @@ -25,6 +25,7 @@ commonSources:= \ Debug.cpp \ FileMap.cpp \ Flattenable.cpp \ + JenkinsHash.cpp \ LinearTransform.cpp \ Log.cpp \ PropertyMap.cpp \ diff --git a/libs/utils/BasicHashtable.cpp b/libs/utils/BasicHashtable.cpp index fb8ec9f83..fd51b7b2e 100644 --- a/libs/utils/BasicHashtable.cpp +++ b/libs/utils/BasicHashtable.cpp @@ -80,7 +80,7 @@ void BasicHashtableImpl::clear() { SharedBuffer* sb = SharedBuffer::bufferFromData(mBuckets); if (sb->onlyOwner()) { destroyBuckets(mBuckets, mBucketCount); - for (size_t i = 0; i < mSize; i++) { + for (size_t i = 0; i < mBucketCount; i++) { Bucket& bucket = bucketAt(mBuckets, i); bucket.cookie = 0; } diff --git a/libs/utils/JenkinsHash.cpp b/libs/utils/JenkinsHash.cpp new file mode 100644 index 000000000..52c9bb7df --- /dev/null +++ b/libs/utils/JenkinsHash.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2012 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. + */ + +/* Implementation of Jenkins one-at-a-time hash function. These choices are + * optimized for code size and portability, rather than raw speed. But speed + * should still be quite good. + **/ + +#include + +namespace android { + +hash_t JenkinsHashWhiten(uint32_t hash) { + hash += (hash << 3); + hash ^= (hash >> 11); + hash += (hash << 15); + return hash; +} + +uint32_t JenkinsHashMixBytes(uint32_t hash, const uint8_t* bytes, size_t size) { + hash = JenkinsHashMix(hash, (uint32_t)size); + size_t i; + for (i = 0; i < (size & -4); i += 4) { + uint32_t data = bytes[i] | (bytes[i+1] << 8) | (bytes[i+2] << 16) | (bytes[i+3] << 24); + hash = JenkinsHashMix(hash, data); + } + if (size & 3) { + uint32_t data = bytes[i]; + data |= ((size & 3) > 1) ? (bytes[i+1] << 8) : 0; + data |= ((size & 3) > 2) ? (bytes[i+2] << 16) : 0; + hash = JenkinsHashMix(hash, data); + } + return hash; +} + +uint32_t JenkinsHashMixShorts(uint32_t hash, const uint16_t* shorts, size_t size) { + hash = JenkinsHashMix(hash, (uint32_t)size); + size_t i; + for (i = 0; i < (size & -2); i += 2) { + uint32_t data = shorts[i] | (shorts[i+1] << 16); + hash = JenkinsHashMix(hash, data); + } + if (size & 1) { + uint32_t data = shorts[i]; + hash = JenkinsHashMix(hash, data); + } + return hash; +} + +} + diff --git a/libs/utils/tests/Android.mk b/libs/utils/tests/Android.mk index 5b2b5b1c1..a2ca9c877 100644 --- a/libs/utils/tests/Android.mk +++ b/libs/utils/tests/Android.mk @@ -7,6 +7,7 @@ test_src_files := \ BasicHashtable_test.cpp \ BlobCache_test.cpp \ Looper_test.cpp \ + LruCache_test.cpp \ String8_test.cpp \ Unicode_test.cpp \ Vector_test.cpp \ diff --git a/libs/utils/tests/LruCache_test.cpp b/libs/utils/tests/LruCache_test.cpp new file mode 100644 index 000000000..e573952c0 --- /dev/null +++ b/libs/utils/tests/LruCache_test.cpp @@ -0,0 +1,291 @@ +/* + * Copyright (C) 2012 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 +#include +#include +#include +#include + +namespace android { + +typedef int SimpleKey; +typedef const char* StringValue; + +struct ComplexKey { + int k; + + explicit ComplexKey(int k) : k(k) { + instanceCount += 1; + } + + ComplexKey(const ComplexKey& other) : k(other.k) { + instanceCount += 1; + } + + ~ComplexKey() { + instanceCount -= 1; + } + + bool operator ==(const ComplexKey& other) const { + return k == other.k; + } + + bool operator !=(const ComplexKey& other) const { + return k != other.k; + } + + static ssize_t instanceCount; +}; + +ssize_t ComplexKey::instanceCount = 0; + +template<> inline hash_t hash_type(const ComplexKey& value) { + return hash_type(value.k); +} + +struct ComplexValue { + int v; + + explicit ComplexValue(int v) : v(v) { + instanceCount += 1; + } + + ComplexValue(const ComplexValue& other) : v(other.v) { + instanceCount += 1; + } + + ~ComplexValue() { + instanceCount -= 1; + } + + static ssize_t instanceCount; +}; + +ssize_t ComplexValue::instanceCount = 0; + +typedef LruCache ComplexCache; + +class EntryRemovedCallback : public OnEntryRemoved { +public: + EntryRemovedCallback() : callbackCount(0), lastKey(-1), lastValue(NULL) { } + ~EntryRemovedCallback() {} + void operator()(SimpleKey& k, StringValue& v) { + callbackCount += 1; + lastKey = k; + lastValue = v; + } + ssize_t callbackCount; + SimpleKey lastKey; + StringValue lastValue; +}; + +class LruCacheTest : public testing::Test { +protected: + virtual void SetUp() { + ComplexKey::instanceCount = 0; + ComplexValue::instanceCount = 0; + } + + virtual void TearDown() { + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(0, 0)); + } + + void assertInstanceCount(ssize_t keys, ssize_t values) { + if (keys != ComplexKey::instanceCount || values != ComplexValue::instanceCount) { + FAIL() << "Expected " << keys << " keys and " << values << " values " + "but there were actually " << ComplexKey::instanceCount << " keys and " + << ComplexValue::instanceCount << " values"; + } + } +}; + +TEST_F(LruCacheTest, Empty) { + LruCache cache(100); + + EXPECT_EQ(NULL, cache.get(0)); + EXPECT_EQ(0u, cache.size()); +} + +TEST_F(LruCacheTest, Simple) { + LruCache cache(100); + + cache.put(1, "one"); + cache.put(2, "two"); + cache.put(3, "three"); + EXPECT_STREQ("one", cache.get(1)); + EXPECT_STREQ("two", cache.get(2)); + EXPECT_STREQ("three", cache.get(3)); + EXPECT_EQ(3u, cache.size()); +} + +TEST_F(LruCacheTest, MaxCapacity) { + LruCache cache(2); + + cache.put(1, "one"); + cache.put(2, "two"); + cache.put(3, "three"); + EXPECT_EQ(NULL, cache.get(1)); + EXPECT_STREQ("two", cache.get(2)); + EXPECT_STREQ("three", cache.get(3)); + EXPECT_EQ(2u, cache.size()); +} + +TEST_F(LruCacheTest, RemoveLru) { + LruCache cache(100); + + cache.put(1, "one"); + cache.put(2, "two"); + cache.put(3, "three"); + cache.removeOldest(); + EXPECT_EQ(NULL, cache.get(1)); + EXPECT_STREQ("two", cache.get(2)); + EXPECT_STREQ("three", cache.get(3)); + EXPECT_EQ(2u, cache.size()); +} + +TEST_F(LruCacheTest, GetUpdatesLru) { + LruCache cache(100); + + cache.put(1, "one"); + cache.put(2, "two"); + cache.put(3, "three"); + EXPECT_STREQ("one", cache.get(1)); + cache.removeOldest(); + EXPECT_STREQ("one", cache.get(1)); + EXPECT_EQ(NULL, cache.get(2)); + EXPECT_STREQ("three", cache.get(3)); + EXPECT_EQ(2u, cache.size()); +} + +uint32_t hash_int(int x) { + return JenkinsHashWhiten(JenkinsHashMix(0, x)); +} + +TEST_F(LruCacheTest, StressTest) { + const size_t kCacheSize = 512; + LruCache cache(512); + const size_t kNumKeys = 16 * 1024; + const size_t kNumIters = 100000; + char* strings[kNumKeys]; + + for (size_t i = 0; i < kNumKeys; i++) { + strings[i] = (char *)malloc(16); + sprintf(strings[i], "%d", i); + } + + srandom(12345); + int hitCount = 0; + for (size_t i = 0; i < kNumIters; i++) { + int index = random() % kNumKeys; + uint32_t key = hash_int(index); + const char *val = cache.get(key); + if (val != NULL) { + EXPECT_EQ(strings[index], val); + hitCount++; + } else { + cache.put(key, strings[index]); + } + } + size_t expectedHitCount = kNumIters * kCacheSize / kNumKeys; + EXPECT_LT(int(expectedHitCount * 0.9), hitCount); + EXPECT_GT(int(expectedHitCount * 1.1), hitCount); + EXPECT_EQ(kCacheSize, cache.size()); + + for (size_t i = 0; i < kNumKeys; i++) { + free((void *)strings[i]); + } +} + +TEST_F(LruCacheTest, NoLeak) { + ComplexCache cache(100); + + cache.put(ComplexKey(0), ComplexValue(0)); + cache.put(ComplexKey(1), ComplexValue(1)); + EXPECT_EQ(2, cache.size()); + assertInstanceCount(2, 3); // the null value counts as an instance +} + +TEST_F(LruCacheTest, Clear) { + ComplexCache cache(100); + + cache.put(ComplexKey(0), ComplexValue(0)); + cache.put(ComplexKey(1), ComplexValue(1)); + EXPECT_EQ(2, cache.size()); + assertInstanceCount(2, 3); + cache.clear(); + assertInstanceCount(0, 1); +} + +TEST_F(LruCacheTest, ClearNoDoubleFree) { + { + ComplexCache cache(100); + + cache.put(ComplexKey(0), ComplexValue(0)); + cache.put(ComplexKey(1), ComplexValue(1)); + EXPECT_EQ(2, cache.size()); + assertInstanceCount(2, 3); + cache.removeOldest(); + cache.clear(); + assertInstanceCount(0, 1); + } + assertInstanceCount(0, 0); +} + +TEST_F(LruCacheTest, ClearReuseOk) { + ComplexCache cache(100); + + cache.put(ComplexKey(0), ComplexValue(0)); + cache.put(ComplexKey(1), ComplexValue(1)); + EXPECT_EQ(2, cache.size()); + assertInstanceCount(2, 3); + cache.clear(); + assertInstanceCount(0, 1); + cache.put(ComplexKey(0), ComplexValue(0)); + cache.put(ComplexKey(1), ComplexValue(1)); + EXPECT_EQ(2, cache.size()); + assertInstanceCount(2, 3); +} + +TEST_F(LruCacheTest, Callback) { + LruCache cache(100); + EntryRemovedCallback callback; + cache.setOnEntryRemovedListener(&callback); + + cache.put(1, "one"); + cache.put(2, "two"); + cache.put(3, "three"); + EXPECT_EQ(3, cache.size()); + cache.removeOldest(); + EXPECT_EQ(1, callback.callbackCount); + EXPECT_EQ(1, callback.lastKey); + EXPECT_STREQ("one", callback.lastValue); +} + +TEST_F(LruCacheTest, CallbackOnClear) { + LruCache cache(100); + EntryRemovedCallback callback; + cache.setOnEntryRemovedListener(&callback); + + cache.put(1, "one"); + cache.put(2, "two"); + cache.put(3, "three"); + EXPECT_EQ(3, cache.size()); + cache.clear(); + EXPECT_EQ(3, callback.callbackCount); +} + +} From 45ad8f44d0cb60b6c930a4afd1ecd05e9501d381 Mon Sep 17 00:00:00 2001 From: Andy McFadden Date: Mon, 29 Oct 2012 16:49:44 -0700 Subject: [PATCH 452/541] Reduce emulator logspam The emulator doesn't support systrace, but we should point that out at most once per process. Bug 7436352 Change-Id: I06b2c1ea0df6c02c11cd2496423c337f8d7c62a1 --- include/utils/Trace.h | 2 ++ libs/utils/Trace.cpp | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/include/utils/Trace.h b/include/utils/Trace.h index 93e228546..41bce0095 100644 --- a/include/utils/Trace.h +++ b/include/utils/Trace.h @@ -54,6 +54,8 @@ #define ATRACE_TAG_CAMERA (1<<10) #define ATRACE_TAG_LAST ATRACE_TAG_CAMERA +#define ATRACE_TAG_NOT_READY (1LL<<63) // Reserved for use during init + #define ATRACE_TAG_VALID_MASK ((ATRACE_TAG_LAST - 1) | ATRACE_TAG_LAST) #ifndef ATRACE_TAG diff --git a/libs/utils/Trace.cpp b/libs/utils/Trace.cpp index 5cd5731d5..f5aaea322 100644 --- a/libs/utils/Trace.cpp +++ b/libs/utils/Trace.cpp @@ -25,7 +25,7 @@ namespace android { volatile int32_t Tracer::sIsReady = 0; int Tracer::sTraceFD = -1; -uint64_t Tracer::sEnabledTags = 0; +uint64_t Tracer::sEnabledTags = ATRACE_TAG_NOT_READY; Mutex Tracer::sMutex; void Tracer::changeCallback() { @@ -46,7 +46,7 @@ void Tracer::init() { sTraceFD = open(traceFileName, O_WRONLY); if (sTraceFD == -1) { ALOGE("error opening trace file: %s (%d)", strerror(errno), errno); - // sEnabledTags remains zero indicating that no tracing can occur + sEnabledTags = 0; // no tracing can occur } else { loadSystemProperty(); } From b3176acd5fa7a35cee7b6722cd5869f13542afdd Mon Sep 17 00:00:00 2001 From: Romain Guy Date: Wed, 28 Nov 2012 12:59:40 -0800 Subject: [PATCH 453/541] Add #ifndef to prevent multiple definitions Change-Id: Ib861eee0f333fe29290437b7e67623622d8dabd0 --- include/utils/LruCache.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/utils/LruCache.h b/include/utils/LruCache.h index af3931596..2a70d760a 100644 --- a/include/utils/LruCache.h +++ b/include/utils/LruCache.h @@ -14,6 +14,9 @@ * limitations under the License. */ +#ifndef ANDROID_UTILS_LRU_CACHE_H +#define ANDROID_UTILS_LRU_CACHE_H + #include #include #include @@ -197,3 +200,5 @@ void LruCache::rehash(size_t newCapacity) { } } + +#endif // ANDROID_UTILS_LRU_CACHE_H From bdce9baa880990f521061bc7d1498cb07d7efc01 Mon Sep 17 00:00:00 2001 From: Romain Guy Date: Wed, 28 Nov 2012 17:37:03 -0800 Subject: [PATCH 454/541] Add another ifndef and a couple of methods to LruCache The new methods on LruCache are needed by libhwui to manage the cache of paths. Change-Id: If54fa325c54e2b04e7fe5dfe6dad66066c40127c --- include/utils/JenkinsHash.h | 4 ++++ include/utils/LruCache.h | 24 ++++++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/include/utils/JenkinsHash.h b/include/utils/JenkinsHash.h index e964e6f92..7da5dbd6a 100644 --- a/include/utils/JenkinsHash.h +++ b/include/utils/JenkinsHash.h @@ -19,6 +19,9 @@ * should still be quite good. **/ +#ifndef ANDROID_JENKINS_HASH_H +#define ANDROID_JENKINS_HASH_H + #include namespace android { @@ -42,3 +45,4 @@ uint32_t JenkinsHashMixShorts(uint32_t hash, const uint16_t* shorts, size_t size } +#endif // ANDROID_JENKINS_HASH_H diff --git a/include/utils/LruCache.h b/include/utils/LruCache.h index 2a70d760a..937fe1e01 100644 --- a/include/utils/LruCache.h +++ b/include/utils/LruCache.h @@ -36,6 +36,9 @@ public: void setOnEntryRemovedListener(OnEntryRemoved* listener); size_t size() const; + const TKey& keyAt(size_t index) const; + const TValue& valueAt(size_t index) const; + void removeAt(size_t index); const TValue& get(const TKey& key); bool put(const TKey& key, const TValue& value); bool remove(const TKey& key); @@ -85,6 +88,27 @@ size_t LruCache::size() const { return mTable->size(); } +template +const TKey& LruCache::keyAt(size_t index) const { + const Entry& entry = mTable->entryAt(index); + return entry.key; +} + +template +const TValue& LruCache::valueAt(size_t index) const { + const Entry& entry = mTable->entryAt(index); + return entry.value; +} + +template +void LruCache::removeAt(size_t index) { + if (index < 0) { + return; + } + + mTable->removeAt(index); +} + template const TValue& LruCache::get(const TKey& key) { hash_t hash = hash_type(key); From 5ca402a4e2537fa694de120264c5a5e2a4e777bf Mon Sep 17 00:00:00 2001 From: Romain Guy Date: Wed, 28 Nov 2012 18:26:54 -0800 Subject: [PATCH 455/541] Add LruCache::Iterator Required by libhwui Change-Id: I164b9a4a82d89d132da01a56535c0df084de86f7 --- include/utils/LruCache.h | 50 +++++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/include/utils/LruCache.h b/include/utils/LruCache.h index 937fe1e01..302b929c7 100644 --- a/include/utils/LruCache.h +++ b/include/utils/LruCache.h @@ -36,15 +36,38 @@ public: void setOnEntryRemovedListener(OnEntryRemoved* listener); size_t size() const; - const TKey& keyAt(size_t index) const; - const TValue& valueAt(size_t index) const; - void removeAt(size_t index); const TValue& get(const TKey& key); bool put(const TKey& key, const TValue& value); bool remove(const TKey& key); bool removeOldest(); void clear(); + class Iterator { + public: + Iterator(const LruCache& cache): mCache(cache), mIndex(-1) { + } + + bool next() { + mIndex = mCache.mTable->next(mIndex); + return mIndex != -1; + } + + size_t index() const { + return mIndex; + } + + const TValue& value() const { + return mCache.mTable->entryAt(mIndex).value; + } + + const TKey& key() const { + return mCache.mTable->entryAt(mIndex).key; + } + private: + const LruCache& mCache; + size_t mIndex; + }; + private: LruCache(const LruCache& that); // disallow copy constructor @@ -88,27 +111,6 @@ size_t LruCache::size() const { return mTable->size(); } -template -const TKey& LruCache::keyAt(size_t index) const { - const Entry& entry = mTable->entryAt(index); - return entry.key; -} - -template -const TValue& LruCache::valueAt(size_t index) const { - const Entry& entry = mTable->entryAt(index); - return entry.value; -} - -template -void LruCache::removeAt(size_t index) { - if (index < 0) { - return; - } - - mTable->removeAt(index); -} - template const TValue& LruCache::get(const TKey& key) { hash_t hash = hash_type(key); From 5b2d36e38adcc09e72f81b06c324bf3c5c92e043 Mon Sep 17 00:00:00 2001 From: Alex Ray Date: Wed, 14 Nov 2012 17:28:10 -0800 Subject: [PATCH 456/541] utils: Use cutils tracing functionality. Tracing functionality has moved to cutils. Change-Id: Ie78ccc1d59dd5178f5058fbc3858a37f9adce552 --- include/utils/Trace.h | 150 +++--------------------------------------- libs/utils/Trace.cpp | 50 ++------------ 2 files changed, 14 insertions(+), 186 deletions(-) diff --git a/include/utils/Trace.h b/include/utils/Trace.h index 41bce0095..940aec1ce 100644 --- a/include/utils/Trace.h +++ b/include/utils/Trace.h @@ -27,42 +27,9 @@ #include #include +#include -// The ATRACE_TAG macro can be defined before including this header to trace -// using one of the tags defined below. It must be defined to one of the -// following ATRACE_TAG_* macros. The trace tag is used to filter tracing in -// userland to avoid some of the runtime cost of tracing when it is not desired. -// -// Defining ATRACE_TAG to be ATRACE_TAG_ALWAYS will result in the tracing always -// being enabled - this should ONLY be done for debug code, as userland tracing -// has a performance cost even when the trace is not being recorded. Defining -// ATRACE_TAG to be ATRACE_TAG_NEVER or leaving ATRACE_TAG undefined will result -// in the tracing always being disabled. -// -// These tags must be kept in sync with frameworks/base/core/java/android/os/Trace.java. -#define ATRACE_TAG_NEVER 0 // The "never" tag is never enabled. -#define ATRACE_TAG_ALWAYS (1<<0) // The "always" tag is always enabled. -#define ATRACE_TAG_GRAPHICS (1<<1) -#define ATRACE_TAG_INPUT (1<<2) -#define ATRACE_TAG_VIEW (1<<3) -#define ATRACE_TAG_WEBVIEW (1<<4) -#define ATRACE_TAG_WINDOW_MANAGER (1<<5) -#define ATRACE_TAG_ACTIVITY_MANAGER (1<<6) -#define ATRACE_TAG_SYNC_MANAGER (1<<7) -#define ATRACE_TAG_AUDIO (1<<8) -#define ATRACE_TAG_VIDEO (1<<9) -#define ATRACE_TAG_CAMERA (1<<10) -#define ATRACE_TAG_LAST ATRACE_TAG_CAMERA - -#define ATRACE_TAG_NOT_READY (1LL<<63) // Reserved for use during init - -#define ATRACE_TAG_VALID_MASK ((ATRACE_TAG_LAST - 1) | ATRACE_TAG_LAST) - -#ifndef ATRACE_TAG -#define ATRACE_TAG ATRACE_TAG_NEVER -#elif ATRACE_TAG > ATRACE_TAG_LAST -#error ATRACE_TAG must be defined to be one of the tags defined in utils/Trace.h -#endif +// See for more ATRACE_* macros. // ATRACE_CALL traces the beginning and end of the current function. To trace // the correct start and end times this macro should be the first line of the @@ -74,119 +41,20 @@ // function body. #define ATRACE_NAME(name) android::ScopedTrace ___tracer(ATRACE_TAG, name) -// ATRACE_INT traces a named integer value. This can be used to track how the -// value changes over time in a trace. -#define ATRACE_INT(name, value) android::Tracer::traceCounter(ATRACE_TAG, name, value) - -// ATRACE_ENABLED returns true if the trace tag is enabled. It can be used as a -// guard condition around more expensive trace calculations. -#define ATRACE_ENABLED() android::Tracer::isTagEnabled(ATRACE_TAG) - namespace android { -class Tracer { - -public: - - static uint64_t getEnabledTags() { - initIfNeeded(); - return sEnabledTags; - } - - static inline bool isTagEnabled(uint64_t tag) { - initIfNeeded(); - return sEnabledTags & tag; - } - - static inline void traceCounter(uint64_t tag, const char* name, - int32_t value) { - if (CC_UNLIKELY(isTagEnabled(tag))) { - char buf[1024]; - snprintf(buf, 1024, "C|%d|%s|%d", getpid(), name, value); - write(sTraceFD, buf, strlen(buf)); - } - } - - static inline void traceBegin(uint64_t tag, const char* name) { - if (CC_UNLIKELY(isTagEnabled(tag))) { - char buf[1024]; - size_t len = snprintf(buf, 1024, "B|%d|%s", getpid(), name); - write(sTraceFD, buf, len); - } - } - - static inline void traceEnd(uint64_t tag) { - if (CC_UNLIKELY(isTagEnabled(tag))) { - char buf = 'E'; - write(sTraceFD, &buf, 1); - } - } - -private: - - static inline void initIfNeeded() { - if (!android_atomic_acquire_load(&sIsReady)) { - init(); - } - } - - static void changeCallback(); - - // init opens the trace marker file for writing and reads the - // atrace.tags.enableflags system property. It does this only the first - // time it is run, using sMutex for synchronization. - static void init(); - - // retrieve the current value of the system property. - static void loadSystemProperty(); - - // sIsReady is a boolean value indicating whether a call to init() has - // completed in this process. It is initialized to 0 and set to 1 when the - // first init() call completes. It is set to 1 even if a failure occurred - // in init (e.g. the trace marker file couldn't be opened). - // - // This should be checked by all tracing functions using an atomic acquire - // load operation before calling init(). This check avoids the need to lock - // a mutex each time a trace function gets called. - static volatile int32_t sIsReady; - - // sTraceFD is the file descriptor used to write to the kernel's trace - // buffer. It is initialized to -1 and set to an open file descriptor in - // init() while a lock on sMutex is held. - // - // This should only be used by a trace function after init() has - // successfully completed. - static int sTraceFD; - - // sEnabledTags is the set of tag bits for which tracing is currently - // enabled. It is initialized to 0 and set based on the - // atrace.tags.enableflags system property in init() while a lock on sMutex - // is held. - // - // This should only be used by a trace function after init() has - // successfully completed. - // - // This value is only ever non-zero when tracing is initialized and sTraceFD is not -1. - static uint64_t sEnabledTags; - - // sMutex is used to protect the execution of init(). - static Mutex sMutex; -}; - class ScopedTrace { - public: - inline ScopedTrace(uint64_t tag, const char* name) : - mTag(tag) { - Tracer::traceBegin(mTag, name); - } +inline ScopedTrace(uint64_t tag, const char* name) + : mTag(tag) { + atrace_begin(mTag,name); +} - inline ~ScopedTrace() { - Tracer::traceEnd(mTag); - } +inline ~ScopedTrace() { + atrace_end(mTag); +} private: - uint64_t mTag; }; diff --git a/libs/utils/Trace.cpp b/libs/utils/Trace.cpp index f5aaea322..d4f02187f 100644 --- a/libs/utils/Trace.cpp +++ b/libs/utils/Trace.cpp @@ -14,52 +14,12 @@ * limitations under the License. */ -#define LOG_TAG "Trace" - -#include -#include -#include #include +#include -namespace android { +static void traceInit() __attribute__((constructor)); -volatile int32_t Tracer::sIsReady = 0; -int Tracer::sTraceFD = -1; -uint64_t Tracer::sEnabledTags = ATRACE_TAG_NOT_READY; -Mutex Tracer::sMutex; - -void Tracer::changeCallback() { - Mutex::Autolock lock(sMutex); - if (sIsReady && sTraceFD >= 0) { - loadSystemProperty(); - } +static void traceInit() +{ + android::add_sysprop_change_callback(atrace_update_tags, 0); } - -void Tracer::init() { - Mutex::Autolock lock(sMutex); - - if (!sIsReady) { - add_sysprop_change_callback(changeCallback, 0); - - const char* const traceFileName = - "/sys/kernel/debug/tracing/trace_marker"; - sTraceFD = open(traceFileName, O_WRONLY); - if (sTraceFD == -1) { - ALOGE("error opening trace file: %s (%d)", strerror(errno), errno); - sEnabledTags = 0; // no tracing can occur - } else { - loadSystemProperty(); - } - - android_atomic_release_store(1, &sIsReady); - } -} - -void Tracer::loadSystemProperty() { - char value[PROPERTY_VALUE_MAX]; - property_get("debug.atrace.tags.enableflags", value, "0"); - sEnabledTags = (strtoll(value, NULL, 0) & ATRACE_TAG_VALID_MASK) - | ATRACE_TAG_ALWAYS; -} - -} // namespace andoid From 0a19c906f20fe7ded0c2ab5b1c8cf29137fb0fa9 Mon Sep 17 00:00:00 2001 From: Chad Jones Date: Thu, 29 Nov 2012 15:13:36 -0800 Subject: [PATCH 457/541] Remove copy of atrace imported to wrong location --- atrace/Android.mk | 12 -- atrace/MODULE_LICENSE_APACHE2 | 0 atrace/NOTICE | 190 --------------------- atrace/atrace.c | 304 ---------------------------------- 4 files changed, 506 deletions(-) delete mode 100644 atrace/Android.mk delete mode 100644 atrace/MODULE_LICENSE_APACHE2 delete mode 100644 atrace/NOTICE delete mode 100644 atrace/atrace.c diff --git a/atrace/Android.mk b/atrace/Android.mk deleted file mode 100644 index b6c55d4fb..000000000 --- a/atrace/Android.mk +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright 2012 The Android Open Source Project - -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_SRC_FILES:= atrace.c - -LOCAL_MODULE:= atrace - -LOCAL_MODULE_TAGS:= optional - -include $(BUILD_EXECUTABLE) diff --git a/atrace/MODULE_LICENSE_APACHE2 b/atrace/MODULE_LICENSE_APACHE2 deleted file mode 100644 index e69de29bb..000000000 diff --git a/atrace/NOTICE b/atrace/NOTICE deleted file mode 100644 index c77f135e7..000000000 --- a/atrace/NOTICE +++ /dev/null @@ -1,190 +0,0 @@ - - Copyright (c) 2012, 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. - - 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. - - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - diff --git a/atrace/atrace.c b/atrace/atrace.c deleted file mode 100644 index 3d2a52e55..000000000 --- a/atrace/atrace.c +++ /dev/null @@ -1,304 +0,0 @@ -/* - * Copyright (C) 2012 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 -#include -#include -#include -#include -#include -#include -#include -#include - -/* Command line options */ -static int g_traceDurationSeconds = 5; -static bool g_traceSchedSwitch = false; -static bool g_traceWorkqueue = false; -static bool g_traceOverwrite = false; - -/* Global state */ -static bool g_traceAborted = false; - -/* Sys file paths */ -static const char* k_traceClockPath = - "/sys/kernel/debug/tracing/trace_clock"; - -static const char* k_tracingOverwriteEnablePath = - "/sys/kernel/debug/tracing/options/overwrite"; - -static const char* k_schedSwitchEnablePath = - "/sys/kernel/debug/tracing/events/sched/sched_switch/enable"; - -static const char* k_workqueueEnablePath = - "/sys/kernel/debug/tracing/events/workqueue/enable"; - -static const char* k_tracingOnPath = - "/sys/kernel/debug/tracing/tracing_on"; - -static const char* k_tracePath = - "/sys/kernel/debug/tracing/trace"; - -static const char* k_traceMarkerPath = - "/sys/kernel/debug/tracing/trace_marker"; - -// Write a string to a file, returning true if the write was successful. -bool writeStr(const char* filename, const char* str) -{ - int fd = open(filename, O_WRONLY); - if (fd == -1) { - fprintf(stderr, "error opening %s: %s (%d)\n", filename, - strerror(errno), errno); - return false; - } - - bool ok = true; - ssize_t len = strlen(str); - if (write(fd, str, len) != len) { - fprintf(stderr, "error writing to %s: %s (%d)\n", filename, - strerror(errno), errno); - ok = false; - } - - close(fd); - - return ok; -} - -// Enable or disable a kernel option by writing a "1" or a "0" into a /sys file. -static bool setKernelOptionEnable(const char* filename, bool enable) -{ - return writeStr(filename, enable ? "1" : "0"); -} - -// Enable or disable overwriting of the kernel trace buffers. Disabling this -// will cause tracing to stop once the trace buffers have filled up. -static bool setTraceOverwriteEnable(bool enable) -{ - return setKernelOptionEnable(k_tracingOverwriteEnablePath, enable); -} - -// Enable or disable tracing of the kernel scheduler switching. -static bool setSchedSwitchTracingEnable(bool enable) -{ - return setKernelOptionEnable(k_schedSwitchEnablePath, enable); -} - -// Enable or disable tracing of the kernel workqueues. -static bool setWorkqueueTracingEnabled(bool enable) -{ - return setKernelOptionEnable(k_workqueueEnablePath, enable); -} - -// Enable or disable kernel tracing. -static bool setTracingEnabled(bool enable) -{ - return setKernelOptionEnable(k_tracingOnPath, enable); -} - -// Clear the contents of the kernel trace. -static bool clearTrace() -{ - int traceFD = creat(k_tracePath, 0); - if (traceFD == -1) { - fprintf(stderr, "error truncating %s: %s (%d)\n", k_tracePath, - strerror(errno), errno); - return false; - } - - close(traceFD); - - return true; -} - -// Enable or disable the kernel's use of the global clock. Disabling the global -// clock will result in the kernel using a per-CPU local clock. -static bool setGlobalClockEnable(bool enable) -{ - return writeStr(k_traceClockPath, enable ? "global" : "local"); -} - -// Enable tracing in the kernel. -static bool startTrace() -{ - bool ok = true; - - // Set up the tracing options. - ok &= setTraceOverwriteEnable(g_traceOverwrite); - ok &= setSchedSwitchTracingEnable(g_traceSchedSwitch); - ok &= setWorkqueueTracingEnabled(g_traceWorkqueue); - ok &= setGlobalClockEnable(true); - - // Enable tracing. - ok &= setTracingEnabled(true); - - if (!ok) { - fprintf(stderr, "error: unable to start trace\n"); - } - - return ok; -} - -// Disable tracing in the kernel. -static void stopTrace() -{ - // Disable tracing. - setTracingEnabled(false); - - // Set the options back to their defaults. - setTraceOverwriteEnable(true); - setSchedSwitchTracingEnable(false); - setWorkqueueTracingEnabled(false); - setGlobalClockEnable(false); -} - -// Read the current kernel trace and write it to stdout. -static void dumpTrace() -{ - int traceFD = open(k_tracePath, O_RDWR); - if (traceFD == -1) { - fprintf(stderr, "error opening %s: %s (%d)\n", k_tracePath, - strerror(errno), errno); - return; - } - - ssize_t sent = 0; - while ((sent = sendfile(STDOUT_FILENO, traceFD, NULL, 64*1024*1024)) > 0); - if (sent == -1) { - fprintf(stderr, "error dumping trace: %s (%d)\n", strerror(errno), - errno); - } - - close(traceFD); -} - -// Print the command usage help to stderr. -static void showHelp(const char *cmd) -{ - fprintf(stderr, "usage: %s [options]\n", cmd); - fprintf(stderr, "options include:\n" - " -c trace into a circular buffer\n" - " -s trace the kernel scheduler switches\n" - " -t N trace for N seconds [defualt 5]\n" - " -w trace the kernel workqueue\n"); -} - -static void handleSignal(int signo) { - g_traceAborted = true; -} - -static void registerSigHandler() { - struct sigaction sa; - sigemptyset(&sa.sa_mask); - sa.sa_flags = 0; - sa.sa_handler = handleSignal; - sigaction(SIGHUP, &sa, NULL); - sigaction(SIGINT, &sa, NULL); - sigaction(SIGQUIT, &sa, NULL); - sigaction(SIGTERM, &sa, NULL); -} - -int main(int argc, char **argv) -{ - if (argc == 2 && 0 == strcmp(argv[1], "--help")) { - showHelp(argv[0]); - exit(0); - } - - if (getuid() != 0) { - fprintf(stderr, "error: %s must be run as root.", argv[0]); - } - - for (;;) { - int ret; - - ret = getopt(argc, argv, "cst:w"); - - if (ret < 0) { - break; - } - - switch(ret) { - case 'c': - g_traceOverwrite = true; - break; - - case 's': - g_traceSchedSwitch = true; - break; - - case 't': - g_traceDurationSeconds = atoi(optarg); - break; - - case 'w': - g_traceWorkqueue = true; - break; - - default: - showHelp(argv[0]); - exit(-1); - break; - } - } - - registerSigHandler(); - - bool ok = startTrace(); - - if (ok) { - printf("capturing trace..."); - fflush(stdout); - - // We clear the trace after starting it because tracing gets enabled for - // each CPU individually in the kernel. Having the beginning of the trace - // contain entries from only one CPU can cause "begin" entries without a - // matching "end" entry to show up if a task gets migrated from one CPU to - // another. - ok = clearTrace(); - - if (ok) { - // Sleep to allow the trace to be captured. - struct timespec timeLeft; - timeLeft.tv_sec = g_traceDurationSeconds; - timeLeft.tv_nsec = 0; - do { - if (g_traceAborted) { - break; - } - } while (nanosleep(&timeLeft, &timeLeft) == -1 && errno == EINTR); - } - } - - // Stop the trace and restore the default settings. - stopTrace(); - - if (ok) { - if (!g_traceAborted) { - printf(" done\nTRACE:\n"); - fflush(stdout); - dumpTrace(); - } else { - printf("\ntrace aborted.\n"); - fflush(stdout); - } - clearTrace(); - } else { - fprintf(stderr, "unable to start tracing\n"); - } - - return g_traceAborted ? 1 : 0; -} From fcb349ff75272c802519a20abc0e083c35bbe8cf Mon Sep 17 00:00:00 2001 From: Alex Ray Date: Fri, 30 Nov 2012 19:32:12 -0800 Subject: [PATCH 458/541] utils: clarify scoped tracing functionality ScopedTrace objects were being used in place of ATRACE_NAME because of a misunderstanding of it's function. Cleared up documentation for usage. Also explicitly use global namespace for sysprop callback. Change-Id: I7c248b486b614ccdb841659ca0dcfc644fda670a --- include/utils/Trace.h | 13 +++++-------- libs/utils/Trace.cpp | 2 +- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/include/utils/Trace.h b/include/utils/Trace.h index 940aec1ce..49578c47d 100644 --- a/include/utils/Trace.h +++ b/include/utils/Trace.h @@ -31,15 +31,12 @@ // See for more ATRACE_* macros. -// ATRACE_CALL traces the beginning and end of the current function. To trace -// the correct start and end times this macro should be the first line of the -// function body. -#define ATRACE_CALL() android::ScopedTrace ___tracer(ATRACE_TAG, __FUNCTION__) - -// ATRACE_NAME traces the beginning and end of the current function. To trace -// the correct start and end times this macro should be the first line of the -// function body. +// ATRACE_NAME traces the beginning and end of the current scope. To trace +// the correct start and end times this macro should be declared first in the +// scope body. #define ATRACE_NAME(name) android::ScopedTrace ___tracer(ATRACE_TAG, name) +// ATRACE_CALL is an ATRACE_NAME that uses the current function name. +#define ATRACE_CALL() ATRACE_NAME(__FUNCTION__) namespace android { diff --git a/libs/utils/Trace.cpp b/libs/utils/Trace.cpp index d4f02187f..36fd80214 100644 --- a/libs/utils/Trace.cpp +++ b/libs/utils/Trace.cpp @@ -21,5 +21,5 @@ static void traceInit() __attribute__((constructor)); static void traceInit() { - android::add_sysprop_change_callback(atrace_update_tags, 0); + ::android::add_sysprop_change_callback(atrace_update_tags, 0); } From a27c1e08396aa2969511dad1f661d10c2b835ac1 Mon Sep 17 00:00:00 2001 From: Igor Murashkin Date: Wed, 5 Dec 2012 16:10:26 -0800 Subject: [PATCH 459/541] utils: fix warnings for unused parameters Change-Id: Ibfb755a30ba2923669060fe0aed019beecbe38a1 --- include/utils/Mutex.h | 4 ++-- include/utils/RWLock.h | 4 ++-- include/utils/RefBase.h | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/utils/Mutex.h b/include/utils/Mutex.h index de6fb394a..dd201c898 100644 --- a/include/utils/Mutex.h +++ b/include/utils/Mutex.h @@ -91,10 +91,10 @@ private: inline Mutex::Mutex() { pthread_mutex_init(&mMutex, NULL); } -inline Mutex::Mutex(const char* name) { +inline Mutex::Mutex(__attribute__((unused)) const char* name) { pthread_mutex_init(&mMutex, NULL); } -inline Mutex::Mutex(int type, const char* name) { +inline Mutex::Mutex(int type, __attribute__((unused)) const char* name) { if (type == SHARED) { pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); diff --git a/include/utils/RWLock.h b/include/utils/RWLock.h index a5abea2b8..90beb5f4a 100644 --- a/include/utils/RWLock.h +++ b/include/utils/RWLock.h @@ -84,10 +84,10 @@ private: inline RWLock::RWLock() { pthread_rwlock_init(&mRWLock, NULL); } -inline RWLock::RWLock(const char* name) { +inline RWLock::RWLock(__attribute__((unused)) const char* name) { pthread_rwlock_init(&mRWLock, NULL); } -inline RWLock::RWLock(int type, const char* name) { +inline RWLock::RWLock(int type, __attribute__((unused)) const char* name) { if (type == SHARED) { pthread_rwlockattr_t attr; pthread_rwlockattr_init(&attr); diff --git a/include/utils/RefBase.h b/include/utils/RefBase.h index aa91f6023..8e304e75d 100644 --- a/include/utils/RefBase.h +++ b/include/utils/RefBase.h @@ -165,10 +165,10 @@ class LightRefBase { public: inline LightRefBase() : mCount(0) { } - inline void incStrong(const void* id) const { + inline void incStrong(__attribute__((unused)) const void* id) const { android_atomic_inc(&mCount); } - inline void decStrong(const void* id) const { + inline void decStrong(__attribute__((unused)) const void* id) const { if (android_atomic_dec(&mCount) == 1) { delete static_cast(this); } From 551fcf4fe39e68fab17d56cc6c4b8a61164891ff Mon Sep 17 00:00:00 2001 From: Chris Craik Date: Wed, 5 Dec 2012 10:36:45 -0800 Subject: [PATCH 460/541] Add LinearAllocator Moving from external/webkit/Source/WebCore/platform/graphics/android/utils/ Change-Id: If91830aa9b207dbc8692b2ca7c4a0b76778addd5 --- include/utils/LinearAllocator.h | 97 ++++++++++++++ libs/utils/Android.mk | 5 + libs/utils/LinearAllocator.cpp | 227 ++++++++++++++++++++++++++++++++ 3 files changed, 329 insertions(+) create mode 100644 include/utils/LinearAllocator.h create mode 100644 libs/utils/LinearAllocator.cpp diff --git a/include/utils/LinearAllocator.h b/include/utils/LinearAllocator.h new file mode 100644 index 000000000..4772bc8f7 --- /dev/null +++ b/include/utils/LinearAllocator.h @@ -0,0 +1,97 @@ +/* + * Copyright 2012, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ANDROID_LINEARALLOCATOR_H +#define ANDROID_LINEARALLOCATOR_H + +#include + +namespace android { + +/** + * A memory manager that internally allocates multi-kbyte buffers for placing objects in. It avoids + * the overhead of malloc when many objects are allocated. It is most useful when creating many + * small objects with a similar lifetime, and doesn't add significant overhead for large + * allocations. + */ +class LinearAllocator { +public: + LinearAllocator(); + ~LinearAllocator(); + + /** + * Reserves and returns a region of memory of at least size 'size', aligning as needed. + * Typically this is used in an object's overridden new() method or as a replacement for malloc. + * + * The lifetime of the returned buffers is tied to that of the LinearAllocator. If calling + * delete() on an object stored in a buffer is needed, it should be overridden to use + * rewindIfLastAlloc() + */ + void* alloc(size_t size); + + /** + * Attempt to deallocate the given buffer, with the LinearAllocator attempting to rewind its + * state if possible. No destructors are called. + */ + void rewindIfLastAlloc(void* ptr, size_t allocSize); + + /** + * Dump memory usage statistics to the log (allocated and wasted space) + */ + void dumpMemoryStats(const char* prefix = ""); + + /** + * The number of bytes used for buffers allocated in the LinearAllocator (does not count space + * wasted) + */ + size_t usedSize() const { return mTotalAllocated - mWastedSpace; } + +private: + LinearAllocator(const LinearAllocator& other); + + class Page; + + Page* newPage(size_t pageSize); + bool fitsInCurrentPage(size_t size); + void ensureNext(size_t size); + void* start(Page *p); + void* end(Page* p); + + size_t mPageSize; + size_t mMaxAllocSize; + void* mNext; + Page* mCurrentPage; + Page* mPages; + + // Memory usage tracking + size_t mTotalAllocated; + size_t mWastedSpace; + size_t mPageCount; + size_t mDedicatedPageCount; +}; + +}; // namespace android + +#endif // ANDROID_LINEARALLOCATOR_H diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk index 73a98d5f8..cbfe7bdce 100644 --- a/libs/utils/Android.mk +++ b/libs/utils/Android.mk @@ -26,6 +26,7 @@ commonSources:= \ FileMap.cpp \ Flattenable.cpp \ JenkinsHash.cpp \ + LinearAllocator.cpp \ LinearTransform.cpp \ Log.cpp \ PropertyMap.cpp \ @@ -112,6 +113,10 @@ ifeq ($(TARGET_OS),linux) LOCAL_LDLIBS += -lrt -ldl endif +ifeq ($(TARGET_ARCH),mips) +LOCAL_CFLAGS += -DALIGN_DOUBLE +endif + LOCAL_C_INCLUDES += \ bionic/libc/private \ external/zlib diff --git a/libs/utils/LinearAllocator.cpp b/libs/utils/LinearAllocator.cpp new file mode 100644 index 000000000..a07a2916e --- /dev/null +++ b/libs/utils/LinearAllocator.cpp @@ -0,0 +1,227 @@ +/* + * Copyright 2012, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define LOG_TAG "LinearAllocator" +#define LOG_NDEBUG 1 + +#include +#include +#include + + +// The ideal size of a page allocation (these need to be multiples of 8) +#define INITIAL_PAGE_SIZE ((size_t)4096) // 4kb +#define MAX_PAGE_SIZE ((size_t)131072) // 128kb + +// The maximum amount of wasted space we can have per page +// Allocations exceeding this will have their own dedicated page +// If this is too low, we will malloc too much +// Too high, and we may waste too much space +// Must be smaller than INITIAL_PAGE_SIZE +#define MAX_WASTE_SIZE ((size_t)1024) + +#if ALIGN_DOUBLE +#define ALIGN_SZ (sizeof(double)) +#else +#define ALIGN_SZ (sizeof(int)) +#endif + +#define ALIGN(x) ((x + ALIGN_SZ - 1 ) & ~(ALIGN_SZ - 1)) +#define ALIGN_PTR(p) ((void*)(ALIGN((size_t)p))) + +#if LOG_NDEBUG +#define ADD_ALLOCATION(size) +#define RM_ALLOCATION(size) +#else +#include +#include +static size_t s_totalAllocations = 0; +static nsecs_t s_nextLog = 0; +static android::Mutex s_mutex; + +static void _logUsageLocked() { + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + if (now > s_nextLog) { + s_nextLog = now + milliseconds_to_nanoseconds(10); + ALOGV("Total memory usage: %zu kb", s_totalAllocations / 1024); + } +} + +static void _addAllocation(size_t size) { + android::AutoMutex lock(s_mutex); + s_totalAllocations += size; + _logUsageLocked(); +} + +#define ADD_ALLOCATION(size) _addAllocation(size); +#define RM_ALLOCATION(size) _addAllocation(-size); +#endif + +#define min(x,y) (((x) < (y)) ? (x) : (y)) + +namespace android { + +class LinearAllocator::Page { +public: + Page* next() { return mNextPage; } + void setNext(Page* next) { mNextPage = next; } + + Page() + : mNextPage(0) + {} + + void* operator new(size_t size, void* buf) { return buf; } + + void* start() { + return (void*) (((size_t)this) + sizeof(Page)); + } + + void* end(int pageSize) { + return (void*) (((size_t)start()) + pageSize); + } + +private: + Page(const Page& other) {} + Page* mNextPage; +}; + +LinearAllocator::LinearAllocator() + : mPageSize(INITIAL_PAGE_SIZE) + , mMaxAllocSize(MAX_WASTE_SIZE) + , mNext(0) + , mCurrentPage(0) + , mPages(0) + , mTotalAllocated(0) + , mWastedSpace(0) + , mPageCount(0) + , mDedicatedPageCount(0) {} + +LinearAllocator::~LinearAllocator(void) { + Page* p = mPages; + while (p) { + Page* next = p->next(); + p->~Page(); + free(p); + RM_ALLOCATION(mPageSize); + p = next; + } +} + +void* LinearAllocator::start(Page* p) { + return ALIGN_PTR(((size_t*)p) + sizeof(Page)); +} + +void* LinearAllocator::end(Page* p) { + return ((char*)p) + mPageSize; +} + +bool LinearAllocator::fitsInCurrentPage(size_t size) { + return mNext && ((char*)mNext + size) <= end(mCurrentPage); +} + +void LinearAllocator::ensureNext(size_t size) { + if (fitsInCurrentPage(size)) return; + + if (mCurrentPage && mPageSize < MAX_PAGE_SIZE) { + mPageSize = min(MAX_PAGE_SIZE, mPageSize * 2); + mPageSize = ALIGN(mPageSize); + } + mWastedSpace += mPageSize; + Page* p = newPage(mPageSize); + if (mCurrentPage) { + mCurrentPage->setNext(p); + } + mCurrentPage = p; + if (!mPages) { + mPages = mCurrentPage; + } + mNext = start(mCurrentPage); +} + +void* LinearAllocator::alloc(size_t size) { + size = ALIGN(size); + if (size > mMaxAllocSize && !fitsInCurrentPage(size)) { + ALOGV("Exceeded max size %zu > %zu", size, mMaxAllocSize); + // Allocation is too large, create a dedicated page for the allocation + Page* page = newPage(size); + mDedicatedPageCount++; + page->setNext(mPages); + mPages = page; + if (!mCurrentPage) + mCurrentPage = mPages; + return start(page); + } + ensureNext(size); + void* ptr = mNext; + mNext = ((char*)mNext) + size; + mWastedSpace -= size; + return ptr; +} + +void LinearAllocator::rewindIfLastAlloc(void* ptr, size_t allocSize) { + // Don't bother rewinding across pages + allocSize = ALIGN(allocSize); + if (ptr >= start(mCurrentPage) && ptr < end(mCurrentPage) + && ptr == ((char*)mNext - allocSize)) { + mTotalAllocated -= allocSize; + mWastedSpace += allocSize; + mNext = ptr; + } +} + +LinearAllocator::Page* LinearAllocator::newPage(size_t pageSize) { + pageSize = ALIGN(pageSize + sizeof(LinearAllocator::Page)); + ADD_ALLOCATION(pageSize); + mTotalAllocated += pageSize; + mPageCount++; + void* buf = malloc(pageSize); + return new (buf) Page(); +} + +static const char* toSize(size_t value, float& result) { + if (value < 2000) { + result = value; + return "B"; + } + if (value < 2000000) { + result = value / 1024.0f; + return "KB"; + } + result = value / 1048576.0f; + return "MB"; +} + +void LinearAllocator::dumpMemoryStats(const char* prefix) { + float prettySize; + const char* prettySuffix; + prettySuffix = toSize(mTotalAllocated, prettySize); + ALOGD("%sTotal allocated: %.2f%s", prefix, prettySize, prettySuffix); + prettySuffix = toSize(mWastedSpace, prettySize); + ALOGD("%sWasted space: %.2f%s (%.1f%%)", prefix, prettySize, prettySuffix, + (float) mWastedSpace / (float) mTotalAllocated * 100.0f); + ALOGD("%sPages %zu (dedicated %zu)", prefix, mPageCount, mDedicatedPageCount); +} + +}; // namespace android From 99ec303e459ea4ded9981337e10651aee08016b0 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Mon, 17 Dec 2012 10:28:20 -0800 Subject: [PATCH 461/541] Add NULL check to CallStack::toString CallStack::toString() has a 0 default argument, which ends up getting passed to strlen(), resulting in a crash. Change-Id: If706aff8c400983670f49cdbb66e11191ac76e0e --- libs/utils/CallStack.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libs/utils/CallStack.cpp b/libs/utils/CallStack.cpp index 18fd84f25..66fabebc4 100644 --- a/libs/utils/CallStack.cpp +++ b/libs/utils/CallStack.cpp @@ -118,7 +118,9 @@ String8 CallStack::toString(const char* prefix) const { char line[MAX_BACKTRACE_LINE_LENGTH]; format_backtrace_line(i, &mStack[i], &symbols[i], line, MAX_BACKTRACE_LINE_LENGTH); - str.append(prefix); + if (prefix) { + str.append(prefix); + } str.append(line); str.append("\n"); } From 0656c9764a77b919d707624404546b10ec7fa9f8 Mon Sep 17 00:00:00 2001 From: Nick Kralevich Date: Fri, 1 Feb 2013 16:09:23 -0800 Subject: [PATCH 462/541] try to fix win_sdk build. Change-Id: I55e78e339874a6d6a3381c2550556b65c7ec1ca0 --- libs/utils/Threads.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp index f201fc744..608d674c1 100644 --- a/libs/utils/Threads.cpp +++ b/libs/utils/Threads.cpp @@ -21,7 +21,6 @@ #include #include -#include #include #include @@ -31,6 +30,7 @@ #include #if defined(HAVE_PTHREADS) +# include # include # include # include From d30884aa0cb6e8286aca78fc73d9b239f727627e Mon Sep 17 00:00:00 2001 From: Nick Kralevich Date: Sat, 2 Feb 2013 18:09:15 -0800 Subject: [PATCH 463/541] Revert "try to fix win_sdk build." A better change was checked into system/core. See commit 941daef629bd571032851edf7ae1dce24266640e This reverts commit fa99d30ec773c5f9653d96570fb8961426bfe966. --- libs/utils/Threads.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp index 608d674c1..f201fc744 100644 --- a/libs/utils/Threads.cpp +++ b/libs/utils/Threads.cpp @@ -21,6 +21,7 @@ #include #include +#include #include #include @@ -30,7 +31,6 @@ #include #if defined(HAVE_PTHREADS) -# include # include # include # include From ca3e2a276afa03cfffb2b989e28adfc44ec50d6f Mon Sep 17 00:00:00 2001 From: Naseer Ahmed Date: Wed, 13 Feb 2013 11:53:10 -0500 Subject: [PATCH 464/541] utils: Allow non android namespaces to use ALOGD_IF_SLOW Change-Id: I9207b64954fae2f56cc5f6aa5796a2e737400623 --- include/utils/Log.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/utils/Log.h b/include/utils/Log.h index 98c441c80..4259c86d1 100644 --- a/include/utils/Log.h +++ b/include/utils/Log.h @@ -62,7 +62,7 @@ private: * } */ #define ALOGD_IF_SLOW(timeoutMillis, message) \ - LogIfSlow _logIfSlow(LOG_TAG, ANDROID_LOG_DEBUG, timeoutMillis, message); + android::LogIfSlow _logIfSlow(LOG_TAG, ANDROID_LOG_DEBUG, timeoutMillis, message); } // namespace android From 769828d2d44fca3829e628bb424aa426aa468ee9 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Wed, 6 Mar 2013 17:51:15 -0800 Subject: [PATCH 465/541] Fix RefBase debugging. O_CREAT must specify the mode. Change-Id: I51c6df3cfd59b20ca73c3edee86bc2f74dbde1b1 --- libs/utils/RefBase.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/utils/RefBase.cpp b/libs/utils/RefBase.cpp index e80a795b7..2b39bce2e 100644 --- a/libs/utils/RefBase.cpp +++ b/libs/utils/RefBase.cpp @@ -199,7 +199,7 @@ public: { char name[100]; snprintf(name, 100, "/data/%p.stack", this); - int rc = open(name, O_RDWR | O_CREAT | O_APPEND); + int rc = open(name, O_RDWR | O_CREAT | O_APPEND, 644); if (rc >= 0) { write(rc, text.string(), text.length()); close(rc); From 6090df85a8a227db0bf407b7877b2777937e6427 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Thu, 7 Mar 2013 15:34:28 -0800 Subject: [PATCH 466/541] rename binder services main thread to Binder_* When a binder service's main thread joins the thread pool it retains its name (whatever the exec name was), which is very confusing in systrace. we now rename that thread just like its friends in the thread pool. Change-Id: Ibb3b6ff07304b247cfc6fb1694e72350c579513e --- include/utils/AndroidThreads.h | 3 +++ libs/utils/Threads.cpp | 40 +++++++++++++++++++--------------- 2 files changed, 25 insertions(+), 18 deletions(-) diff --git a/include/utils/AndroidThreads.h b/include/utils/AndroidThreads.h index f67648fd6..4eee14d7e 100644 --- a/include/utils/AndroidThreads.h +++ b/include/utils/AndroidThreads.h @@ -56,6 +56,9 @@ extern int androidCreateRawThreadEtc(android_thread_func_t entryFunction, size_t threadStackSize, android_thread_id_t *threadId); +// set the same of the running thread +extern void androidSetThreadName(const char* name); + // Used by the Java Runtime to control how threads are created, so that // they can be proper and lovely Java threads. typedef int (*android_create_thread_fn)(android_thread_func_t entryFunction, diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp index f201fc744..cbf4ef628 100644 --- a/libs/utils/Threads.cpp +++ b/libs/utils/Threads.cpp @@ -90,30 +90,34 @@ struct thread_data_t { } if (name) { -#if defined(HAVE_PRCTL) - // Mac OS doesn't have this, and we build libutil for the host too - int hasAt = 0; - int hasDot = 0; - char *s = name; - while (*s) { - if (*s == '.') hasDot = 1; - else if (*s == '@') hasAt = 1; - s++; - } - int len = s - name; - if (len < 15 || hasAt || !hasDot) { - s = name; - } else { - s = name + len - 15; - } - prctl(PR_SET_NAME, (unsigned long) s, 0, 0, 0); -#endif + androidSetThreadName(name); free(name); } return f(u); } }; +void androidSetThreadName(const char* name) { +#if defined(HAVE_PRCTL) + // Mac OS doesn't have this, and we build libutil for the host too + int hasAt = 0; + int hasDot = 0; + const char *s = name; + while (*s) { + if (*s == '.') hasDot = 1; + else if (*s == '@') hasAt = 1; + s++; + } + int len = s - name; + if (len < 15 || hasAt || !hasDot) { + s = name; + } else { + s = name + len - 15; + } + prctl(PR_SET_NAME, (unsigned long) s, 0, 0, 0); +#endif +} + int androidCreateRawThreadEtc(android_thread_func_t entryFunction, void *userData, const char* threadName, From 31ba37f1c80801ee128749d4772c47f23323a3be Mon Sep 17 00:00:00 2001 From: Romain Guy Date: Mon, 11 Mar 2013 14:34:56 -0700 Subject: [PATCH 467/541] Add Thread::isRunning and Condition::signal(WakeUpType) The signal() method is useful to choose whether to wake up one or all threads. Change-Id: I062ab6d3ddd306a9fb735549ea140e2a76eed75a --- include/utils/Condition.h | 13 +++++++++++++ include/utils/Thread.h | 3 +++ libs/utils/Threads.cpp | 5 +++++ 3 files changed, 21 insertions(+) diff --git a/include/utils/Condition.h b/include/utils/Condition.h index 8852d5333..e63ba7e69 100644 --- a/include/utils/Condition.h +++ b/include/utils/Condition.h @@ -48,6 +48,11 @@ public: SHARED = 1 }; + enum WakeUpType { + WAKE_UP_ONE = 0, + WAKE_UP_ALL = 1 + }; + Condition(); Condition(int type); ~Condition(); @@ -57,6 +62,14 @@ public: status_t waitRelative(Mutex& mutex, nsecs_t reltime); // Signal the condition variable, allowing one thread to continue. void signal(); + // Signal the condition variable, allowing one or all threads to continue. + void signal(WakeUpType type) { + if (type == WAKE_UP_ONE) { + signal(); + } else { + broadcast(); + } + } // Signal the condition variable, allowing all threads to continue. void broadcast(); diff --git a/include/utils/Thread.h b/include/utils/Thread.h index 4a34abd78..df3061135 100644 --- a/include/utils/Thread.h +++ b/include/utils/Thread.h @@ -67,6 +67,9 @@ public: // Do not call from this object's thread; will return WOULD_BLOCK in that case. status_t join(); + // Indicates whether this thread is running or not. + bool isRunning() const; + #ifdef HAVE_ANDROID_OS // Return the thread's kernel ID, same as the thread itself calling gettid() or // androidGetTid(), or -1 if the thread is not running. diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp index cbf4ef628..e0f12dc02 100644 --- a/libs/utils/Threads.cpp +++ b/libs/utils/Threads.cpp @@ -846,6 +846,11 @@ status_t Thread::join() return mStatus; } +bool Thread::isRunning() const { + Mutex::Autolock _l(mLock); + return mRunning; +} + #ifdef HAVE_ANDROID_OS pid_t Thread::getTid() const { From 8a4cdbcdb935554a660c78e20686afdad8e8ca75 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Mon, 11 Mar 2013 21:27:16 -0700 Subject: [PATCH 468/541] temporary: enable ASSERTs in RefBase this is in an attempt to get more data on bug 8328715. Change-Id: I9333a67c2d7f67f4d9b2fc5eb1ad8a7b2d1c6dcb --- libs/utils/RefBase.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libs/utils/RefBase.cpp b/libs/utils/RefBase.cpp index 2b39bce2e..3dac89eb6 100644 --- a/libs/utils/RefBase.cpp +++ b/libs/utils/RefBase.cpp @@ -15,6 +15,7 @@ */ #define LOG_TAG "RefBase" +#define LOG_NDEBUG 0 #include From a729ab1e3be17ed20073f7c035df0433888164d6 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Thu, 14 Mar 2013 15:26:30 -0700 Subject: [PATCH 469/541] fix a couple race-conditions in RefBase::promote() Bug: 8390295 Change-Id: I7a48e3bf5b213cc1da2b8e844c6bb37ee24cb047 --- libs/utils/RefBase.cpp | 93 ++++++++++++++++++++++++++++++------------ 1 file changed, 67 insertions(+), 26 deletions(-) diff --git a/libs/utils/RefBase.cpp b/libs/utils/RefBase.cpp index 3dac89eb6..0623f46a5 100644 --- a/libs/utils/RefBase.cpp +++ b/libs/utils/RefBase.cpp @@ -441,39 +441,68 @@ bool RefBase::weakref_type::attemptIncStrong(const void* id) incWeak(id); weakref_impl* const impl = static_cast(this); - int32_t curCount = impl->mStrong; - ALOG_ASSERT(curCount >= 0, "attemptIncStrong called on %p after underflow", - this); + + ALOG_ASSERT(curCount >= 0, + "attemptIncStrong called on %p after underflow", this); + while (curCount > 0 && curCount != INITIAL_STRONG_VALUE) { + // we're in the easy/common case of promoting a weak-reference + // from an existing strong reference. if (android_atomic_cmpxchg(curCount, curCount+1, &impl->mStrong) == 0) { break; } + // the strong count has changed on us, we need to re-assert our + // situation. curCount = impl->mStrong; } if (curCount <= 0 || curCount == INITIAL_STRONG_VALUE) { - bool allow; - if (curCount == INITIAL_STRONG_VALUE) { - // Attempting to acquire first strong reference... this is allowed - // if the object does NOT have a longer lifetime (meaning the - // implementation doesn't need to see this), or if the implementation - // allows it to happen. - allow = (impl->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK - || impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id); + // we're now in the harder case of either: + // - there never was a strong reference on us + // - or, all strong references have been released + if ((impl->mFlags&OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_STRONG) { + // this object has a "normal" life-time, i.e.: it gets destroyed + // when the last strong reference goes away + if (curCount <= 0) { + // the last strong-reference got released, the object cannot + // be revived. + decWeak(id); + return false; + } + + // here, curCount == INITIAL_STRONG_VALUE, which means + // there never was a strong-reference, so we can try to + // promote this object; we need to do that atomically. + while (curCount > 0) { + if (android_atomic_cmpxchg(curCount, curCount + 1, + &impl->mStrong) == 0) { + break; + } + // the strong count has changed on us, we need to re-assert our + // situation (e.g.: another thread has inc/decStrong'ed us) + curCount = impl->mStrong; + } + + if (curCount <= 0) { + // promote() failed, some other thread destroyed us in the + // meantime (i.e.: strong count reached zero). + decWeak(id); + return false; + } } else { - // Attempting to revive the object... this is allowed - // if the object DOES have a longer lifetime (so we can safely - // call the object with only a weak ref) and the implementation - // allows it to happen. - allow = (impl->mFlags&OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_WEAK - && impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id); + // this object has an "extended" life-time, i.e.: it can be + // revived from a weak-reference only. + // Ask the object's implementation if it agrees to be revived + if (!impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id)) { + // it didn't so give-up. + decWeak(id); + return false; + } + // grab a strong-reference, which is always safe due to the + // extended life-time. + curCount = android_atomic_inc(&impl->mStrong); } - if (!allow) { - decWeak(id); - return false; - } - curCount = android_atomic_inc(&impl->mStrong); // If the strong reference count has already been incremented by // someone else, the implementor of onIncStrongAttempted() is holding @@ -491,11 +520,23 @@ bool RefBase::weakref_type::attemptIncStrong(const void* id) ALOGD("attemptIncStrong of %p from %p: cnt=%d\n", this, id, curCount); #endif - if (curCount == INITIAL_STRONG_VALUE) { - android_atomic_add(-INITIAL_STRONG_VALUE, &impl->mStrong); - impl->mBase->onFirstRef(); + // now we need to fix-up the count if it was INITIAL_STRONG_VALUE + // this must be done safely, i.e.: handle the case where several threads + // were here in attemptIncStrong(). + curCount = impl->mStrong; + while (curCount >= INITIAL_STRONG_VALUE) { + ALOG_ASSERT(curCount > INITIAL_STRONG_VALUE, + "attemptIncStrong in %p underflowed to INITIAL_STRONG_VALUE", + this); + if (android_atomic_cmpxchg(curCount, curCount-INITIAL_STRONG_VALUE, + &impl->mStrong) == 0) { + break; + } + // the strong-count changed on us, we need to re-assert the situation, + // for e.g.: it's possible the fix-up happened in another thread. + curCount = impl->mStrong; } - + return true; } From b73559d86c9017166da28e1d59a884f13417426d Mon Sep 17 00:00:00 2001 From: Jesse Hall Date: Mon, 11 Mar 2013 10:16:48 -0700 Subject: [PATCH 470/541] Add Vector::resize() Bug: 8384764 Change-Id: Icee83d389f3e555eba7d419b64c8d52a9aa21b8b --- include/utils/Vector.h | 8 +++++++- include/utils/VectorImpl.h | 1 + libs/utils/VectorImpl.cpp | 10 ++++++++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/include/utils/Vector.h b/include/utils/Vector.h index f3020d63d..ed7b72521 100644 --- a/include/utils/Vector.h +++ b/include/utils/Vector.h @@ -80,7 +80,13 @@ public: //! sets the capacity. capacity can never be reduced less than size() inline ssize_t setCapacity(size_t size) { return VectorImpl::setCapacity(size); } - /*! + /*! + * set the size of the vector. items are appended with the default + * constructor, or removed from the end as needed. + */ + inline ssize_t resize(size_t size) { return VectorImpl::resize(size); } + + /*! * C-style array access */ diff --git a/include/utils/VectorImpl.h b/include/utils/VectorImpl.h index c4ec2ff97..9bc50e6c2 100644 --- a/include/utils/VectorImpl.h +++ b/include/utils/VectorImpl.h @@ -64,6 +64,7 @@ public: inline bool isEmpty() const { return mCount == 0; } size_t capacity() const; ssize_t setCapacity(size_t size); + ssize_t resize(size_t size); /*! append/insert another vector or array */ ssize_t insertVectorAt(const VectorImpl& vector, size_t index); diff --git a/libs/utils/VectorImpl.cpp b/libs/utils/VectorImpl.cpp index c3257bb3e..70f49deee 100644 --- a/libs/utils/VectorImpl.cpp +++ b/libs/utils/VectorImpl.cpp @@ -343,6 +343,16 @@ ssize_t VectorImpl::setCapacity(size_t new_capacity) return new_capacity; } +ssize_t VectorImpl::resize(size_t size) { + ssize_t result = NO_ERROR; + if (size > mCount) { + result = insertAt(mCount, size - mCount); + } else if (size < mCount) { + result = removeItemsAt(size, mCount - size); + } + return result < 0 ? result : size; +} + void VectorImpl::release_storage() { if (mStorage) { From 6d4419d9b130719dd2355861907dc8f87368a51c Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Mon, 18 Mar 2013 20:31:18 -0700 Subject: [PATCH 471/541] A few tweaks to RefBase debugging - stacks are now saved in /data/debug which must be created and writable by the user. - removed "always fatal" DEBUG_REFS option, it wasn't really needed. - DEBUG_REFS_ENABLED_BY_DEFAULT is not the default anymore (usually people want to target which refs they're tracking) Change-Id: I37fae72e9dacde6ce1fa8f7dbe2bc01b1a1b95e5 --- libs/utils/RefBase.cpp | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/libs/utils/RefBase.cpp b/libs/utils/RefBase.cpp index 0623f46a5..ef87131ad 100644 --- a/libs/utils/RefBase.cpp +++ b/libs/utils/RefBase.cpp @@ -35,10 +35,18 @@ // compile with refcounting debugging enabled #define DEBUG_REFS 0 -#define DEBUG_REFS_FATAL_SANITY_CHECKS 0 -#define DEBUG_REFS_ENABLED_BY_DEFAULT 1 + +// whether ref-tracking is enabled by default, if not, trackMe(true, false) +// needs to be called explicitly +#define DEBUG_REFS_ENABLED_BY_DEFAULT 0 + +// whether callstack are collected (significantly slows things down) #define DEBUG_REFS_CALLSTACK_ENABLED 1 +// folder where stack traces are saved when DEBUG_REFS is enabled +// this folder needs to exist and be writable +#define DEBUG_REFS_CALLSTACK_PATH "/data/debug" + // log all reference counting operations #define PRINT_REFS 0 @@ -96,11 +104,7 @@ public: bool dumpStack = false; if (!mRetain && mStrongRefs != NULL) { dumpStack = true; -#if DEBUG_REFS_FATAL_SANITY_CHECKS - LOG_ALWAYS_FATAL("Strong references remain!"); -#else ALOGE("Strong references remain:"); -#endif ref_entry* refs = mStrongRefs; while (refs) { char inc = refs->ref >= 0 ? '+' : '-'; @@ -114,11 +118,7 @@ public: if (!mRetain && mWeakRefs != NULL) { dumpStack = true; -#if DEBUG_REFS_FATAL_SANITY_CHECKS - LOG_ALWAYS_FATAL("Weak references remain:"); -#else ALOGE("Weak references remain!"); -#endif ref_entry* refs = mWeakRefs; while (refs) { char inc = refs->ref >= 0 ? '+' : '-'; @@ -199,7 +199,7 @@ public: { char name[100]; - snprintf(name, 100, "/data/%p.stack", this); + snprintf(name, 100, DEBUG_REFS_CALLSTACK_PATH "/%p.stack", this); int rc = open(name, O_RDWR | O_CREAT | O_APPEND, 644); if (rc >= 0) { write(rc, text.string(), text.length()); @@ -258,12 +258,6 @@ private: ref = *refs; } -#if DEBUG_REFS_FATAL_SANITY_CHECKS - LOG_ALWAYS_FATAL("RefBase: removing id %p on RefBase %p" - "(weakref_type %p) that doesn't exist!", - id, mBase, this); -#endif - ALOGE("RefBase: removing id %p on RefBase %p" "(weakref_type %p) that doesn't exist!", id, mBase, this); From 6cd548c7154c1633a0ed318c31dd22c50a5f5d02 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Mon, 18 Mar 2013 22:27:41 -0700 Subject: [PATCH 472/541] Fix a crasher with RefBase debugging and vectors of wp<> background: we have some code to fix-up the IDs of references when using RefBase's DEBUG_REFS when those refs are managed by arrays wp<> or sp<> (this is because wp<> / sp<> don't have a trivial ctor when DEBUG_REFS is enabled, and Vector treats them as trivial for obvious performance reasons) this is complicated by the fact that we don't want to have to recompile everything when enabling DEBUG_REFs (i.e.: the Vector code cannot know wheter it's enabled or not for its template stuff). problem: there was a bug in the fix-up code for wp<> which was trying to access the weakref_impl from the RefBase* however, this was moronic since RefBase could have been destroyed if there wasn't any more strong refs -- and this happned. Instead we need to get the weakref_impl directly from the wp<> Change-Id: Ie16e334204205fdbff142acb9faff8479a78450b --- include/utils/RefBase.h | 95 ++++++++++++++++++++++++----------------- libs/utils/RefBase.cpp | 24 +++++++---- 2 files changed, 71 insertions(+), 48 deletions(-) diff --git a/include/utils/RefBase.h b/include/utils/RefBase.h index 8e304e75d..cbfe13a72 100644 --- a/include/utils/RefBase.h +++ b/include/utils/RefBase.h @@ -52,12 +52,16 @@ inline bool operator _op_ (const U* o) const { \ } // --------------------------------------------------------------------------- -class ReferenceMover; -class ReferenceConverterBase { + +class ReferenceRenamer { +protected: + // destructor is purposedly not virtual so we avoid code overhead from + // subclasses; we have to make it protected to guarantee that it + // cannot be called from this base class (and to make strict compilers + // happy). + ~ReferenceRenamer() { } public: - virtual size_t getReferenceTypeSize() const = 0; - virtual void* getReferenceBase(void const*) const = 0; - inline virtual ~ReferenceConverterBase() { } + virtual void operator()(size_t i) const = 0; }; // --------------------------------------------------------------------------- @@ -143,11 +147,6 @@ protected: virtual bool onIncStrongAttempted(uint32_t flags, const void* id); virtual void onLastWeakRef(const void* id); -private: - friend class ReferenceMover; - static void moveReferences(void* d, void const* s, size_t n, - const ReferenceConverterBase& caster); - private: friend class weakref_type; class weakref_impl; @@ -155,6 +154,17 @@ private: RefBase(const RefBase& o); RefBase& operator=(const RefBase& o); +private: + friend class ReferenceMover; + + static void renameRefs(size_t n, const ReferenceRenamer& renamer); + + static void renameRefId(weakref_type* ref, + const void* old_id, const void* new_id); + + static void renameRefId(RefBase* ref, + const void* old_id, const void* new_id); + weakref_impl* const mRefs; }; @@ -185,8 +195,9 @@ protected: private: friend class ReferenceMover; - inline static void moveReferences(void* d, void const* s, size_t n, - const ReferenceConverterBase& caster) { } + inline static void renameRefs(size_t n, const ReferenceRenamer& renamer) { } + inline static void renameRefId(T* ref, + const void* old_id, const void* new_id) { } private: mutable volatile int32_t mCount; @@ -455,42 +466,48 @@ inline TextOutput& operator<<(TextOutput& to, const wp& val) // this class just serves as a namespace so TYPE::moveReferences can stay // private. - class ReferenceMover { - // StrongReferenceCast and WeakReferenceCast do the impedance matching - // between the generic (void*) implementation in Refbase and the strongly typed - // template specializations below. - - template - struct StrongReferenceCast : public ReferenceConverterBase { - virtual size_t getReferenceTypeSize() const { return sizeof( sp ); } - virtual void* getReferenceBase(void const* p) const { - sp const* sptr(reinterpret_cast const*>(p)); - return static_cast(sptr->get()); - } - }; - - template - struct WeakReferenceCast : public ReferenceConverterBase { - virtual size_t getReferenceTypeSize() const { return sizeof( wp ); } - virtual void* getReferenceBase(void const* p) const { - wp const* sptr(reinterpret_cast const*>(p)); - return static_cast(sptr->unsafe_get()); - } - }; - public: + // it would be nice if we could make sure no extra code is generated + // for sp or wp when TYPE is a descendant of RefBase: + // Using a sp override doesn't work; it's a bit like we wanted + // a template template... + template static inline void move_references(sp* d, sp const* s, size_t n) { + + class Renamer : public ReferenceRenamer { + sp* d; + sp const* s; + virtual void operator()(size_t i) const { + // The id are known to be the sp<>'s this pointer + TYPE::renameRefId(d[i].get(), &s[i], &d[i]); + } + public: + Renamer(sp* d, sp const* s) : s(s), d(d) { } + }; + memmove(d, s, n*sizeof(sp)); - StrongReferenceCast caster; - TYPE::moveReferences(d, s, n, caster); + TYPE::renameRefs(n, Renamer(d, s)); } + + template static inline void move_references(wp* d, wp const* s, size_t n) { + + class Renamer : public ReferenceRenamer { + wp* d; + wp const* s; + virtual void operator()(size_t i) const { + // The id are known to be the wp<>'s this pointer + TYPE::renameRefId(d[i].get_refs(), &s[i], &d[i]); + } + public: + Renamer(wp* d, wp const* s) : s(s), d(d) { } + }; + memmove(d, s, n*sizeof(wp)); - WeakReferenceCast caster; - TYPE::moveReferences(d, s, n, caster); + TYPE::renameRefs(n, Renamer(d, s)); } }; diff --git a/libs/utils/RefBase.cpp b/libs/utils/RefBase.cpp index ef87131ad..abaf3c0ca 100644 --- a/libs/utils/RefBase.cpp +++ b/libs/utils/RefBase.cpp @@ -631,21 +631,27 @@ void RefBase::onLastWeakRef(const void* /*id*/) // --------------------------------------------------------------------------- -void RefBase::moveReferences(void* dst, void const* src, size_t n, - const ReferenceConverterBase& caster) -{ +void RefBase::renameRefs(size_t n, const ReferenceRenamer& renamer) { #if DEBUG_REFS - const size_t itemSize = caster.getReferenceTypeSize(); for (size_t i=0 ; i(intptr_t(dst) + i*itemSize); - void const* s = reinterpret_cast(intptr_t(src) + i*itemSize); - RefBase* ref(reinterpret_cast(caster.getReferenceBase(d))); - ref->mRefs->renameStrongRefId(s, d); - ref->mRefs->renameWeakRefId(s, d); + renamer(i); } #endif } +void RefBase::renameRefId(weakref_type* ref, + const void* old_id, const void* new_id) { + weakref_impl* const impl = static_cast(ref); + impl->renameStrongRefId(old_id, new_id); + impl->renameWeakRefId(old_id, new_id); +} + +void RefBase::renameRefId(RefBase* ref, + const void* old_id, const void* new_id) { + ref->mRefs->renameStrongRefId(old_id, new_id); + ref->mRefs->renameWeakRefId(old_id, new_id); +} + // --------------------------------------------------------------------------- TextOutput& printStrongPointer(TextOutput& to, const void* val) From da8ec4b634ffc165fc12f8ae2671d61e684bd248 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Tue, 19 Mar 2013 17:36:57 -0700 Subject: [PATCH 473/541] disable RefBase consistency checks (NDEBUG) Bug: 8328715 Change-Id: Ib57646ff909fd8744610f37f3b50d90d884dff31 --- libs/utils/RefBase.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/utils/RefBase.cpp b/libs/utils/RefBase.cpp index abaf3c0ca..3d3a59595 100644 --- a/libs/utils/RefBase.cpp +++ b/libs/utils/RefBase.cpp @@ -15,7 +15,7 @@ */ #define LOG_TAG "RefBase" -#define LOG_NDEBUG 0 +// #define LOG_NDEBUG 0 #include From d34a8cad1efa1c8da1c7b5ad81226b822064cf73 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Thu, 21 Mar 2013 17:12:40 -0700 Subject: [PATCH 474/541] improved CallStack a bit - added a ctor that updates and dumps the stack immediately - added a "logtag" parameter to dump() Change-Id: Ie51c256071d282591752243bdb4f68cf9ff8829d --- include/utils/CallStack.h | 6 ++++-- libs/utils/CallStack.cpp | 11 +++++++++-- libs/utils/RefBase.cpp | 12 ++++-------- 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/include/utils/CallStack.h b/include/utils/CallStack.h index 079e20c69..61dc832ea 100644 --- a/include/utils/CallStack.h +++ b/include/utils/CallStack.h @@ -35,6 +35,8 @@ public: }; CallStack(); + CallStack(const char* logtag, int32_t ignoreDepth=1, + int32_t maxDepth=MAX_DEPTH); CallStack(const CallStack& rhs); ~CallStack(); @@ -53,8 +55,8 @@ public: void update(int32_t ignoreDepth=1, int32_t maxDepth=MAX_DEPTH); - // Dump a stack trace to the log - void dump(const char* prefix = 0) const; + // Dump a stack trace to the log using the supplied logtag + void dump(const char* logtag, const char* prefix = 0) const; // Return a string (possibly very long) containing the complete stack trace String8 toString(const char* prefix = 0) const; diff --git a/libs/utils/CallStack.cpp b/libs/utils/CallStack.cpp index 66fabebc4..e60f5d8e7 100644 --- a/libs/utils/CallStack.cpp +++ b/libs/utils/CallStack.cpp @@ -30,6 +30,11 @@ CallStack::CallStack() : mCount(0) { } +CallStack::CallStack(const char* logtag, int32_t ignoreDepth, int32_t maxDepth) { + this->update(ignoreDepth+1, maxDepth); + this->dump(logtag); +} + CallStack::CallStack(const CallStack& rhs) : mCount(rhs.mCount) { if (mCount) { @@ -96,7 +101,7 @@ void CallStack::update(int32_t ignoreDepth, int32_t maxDepth) { mCount = count > 0 ? count : 0; } -void CallStack::dump(const char* prefix) const { +void CallStack::dump(const char* logtag, const char* prefix) const { backtrace_symbol_t symbols[mCount]; get_backtrace_symbols(mStack, mCount, symbols); @@ -104,7 +109,9 @@ void CallStack::dump(const char* prefix) const { char line[MAX_BACKTRACE_LINE_LENGTH]; format_backtrace_line(i, &mStack[i], &symbols[i], line, MAX_BACKTRACE_LINE_LENGTH); - ALOGD("%s%s", prefix, line); + ALOG(LOG_DEBUG, logtag, "%s%s", + prefix ? prefix : "", + line); } free_backtrace_symbols(symbols, mCount); } diff --git a/libs/utils/RefBase.cpp b/libs/utils/RefBase.cpp index 3d3a59595..e538f6868 100644 --- a/libs/utils/RefBase.cpp +++ b/libs/utils/RefBase.cpp @@ -110,7 +110,7 @@ public: char inc = refs->ref >= 0 ? '+' : '-'; ALOGD("\t%c ID %p (ref %d):", inc, refs->id, refs->ref); #if DEBUG_REFS_CALLSTACK_ENABLED - refs->stack.dump(); + refs->stack.dump(LOG_TAG); #endif refs = refs->next; } @@ -124,16 +124,14 @@ public: char inc = refs->ref >= 0 ? '+' : '-'; ALOGD("\t%c ID %p (ref %d):", inc, refs->id, refs->ref); #if DEBUG_REFS_CALLSTACK_ENABLED - refs->stack.dump(); + refs->stack.dump(LOG_TAG); #endif refs = refs->next; } } if (dumpStack) { ALOGE("above errors at:"); - CallStack stack; - stack.update(); - stack.dump(); + CallStack stack(LOG_TAG); } } @@ -269,9 +267,7 @@ private: ref = ref->next; } - CallStack stack; - stack.update(); - stack.dump(); + CallStack stack(LOG_TAG); } } From 00c14b4be46bf08cc0a351663e247690f59acfa8 Mon Sep 17 00:00:00 2001 From: Bjorn Bringert Date: Fri, 22 Mar 2013 14:26:33 +0000 Subject: [PATCH 475/541] Build a static device libutils This is needed to make tools such as aapt more compatible when built for the device. Change-Id: I1261d47b6e24595f1be547b9202892863f66a1de --- libs/utils/Android.mk | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk index cbfe7bdce..ce256053b 100644 --- a/libs/utils/Android.mk +++ b/libs/utils/Android.mk @@ -98,7 +98,7 @@ LOCAL_LDLIBS += $(host_commonLdlibs) include $(BUILD_HOST_STATIC_LIBRARY) -# For the device +# For the device, static # ===================================================== include $(CLEAR_VARS) @@ -123,14 +123,29 @@ LOCAL_C_INCLUDES += \ LOCAL_LDLIBS += -lpthread -LOCAL_SHARED_LIBRARIES := \ - liblog \ +LOCAL_STATIC_LIBRARIES := \ libcutils \ - libdl \ - libcorkscrew \ libz +LOCAL_SHARED_LIBRARIES := \ + libcorkscrew \ + liblog \ + libdl + LOCAL_MODULE:= libutils +include $(BUILD_STATIC_LIBRARY) + +# For the device, shared +# ===================================================== +include $(CLEAR_VARS) +LOCAL_MODULE:= libutils +LOCAL_WHOLE_STATIC_LIBRARIES := libutils +LOCAL_SHARED_LIBRARIES := \ + liblog \ + libcutils \ + libdl \ + libcorkscrew \ + libz include $(BUILD_SHARED_LIBRARY) # Include subdirectory makefiles From d9ad7d8d73918d44d41811e9b4d0cc722dae338d Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Thu, 2 May 2013 15:41:35 -0700 Subject: [PATCH 476/541] String8: ensure static init done prior to empty string reference Avoid NULL deref on static initialization of empty String8 objects prior to libutils static init. Change-Id: I3d420041ba62b97ed8c2dfd2532a2dcd72b84ff1 --- libs/utils/String8.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libs/utils/String8.cpp b/libs/utils/String8.cpp index 562f026f6..75daee9c2 100644 --- a/libs/utils/String8.cpp +++ b/libs/utils/String8.cpp @@ -48,12 +48,16 @@ int gDarwinIsReallyAnnoying; static inline char* getEmptyString() { + if (!gEmptyStringBuf) initialize_string8(); + gEmptyStringBuf->acquire(); return gEmptyString; } void initialize_string8() { + if (gEmptyStringBuf) return; + // HACK: This dummy dependency forces linking libutils Static.cpp, // which is needed to initialize String8/String16 classes. // These variables are named for Darwin, but are needed elsewhere too, From 19159f90020c04ac2f4dcb39424d740f765ed9a3 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Mon, 6 May 2013 14:25:20 -0700 Subject: [PATCH 477/541] Add new Looper API to check whether the looper is idle. This is just to support the watchdog to give it a faster way to determine if a thread is deadlocked without having to post a message to it. Change-Id: I068dc8b9387caf94fe5811fb4aeb0f9b57b1a080 --- include/utils/Looper.h | 11 +++++++++++ libs/utils/Looper.cpp | 12 ++++++++++++ 2 files changed, 23 insertions(+) diff --git a/include/utils/Looper.h b/include/utils/Looper.h index d4a0067fd..2e0651a3c 100644 --- a/include/utils/Looper.h +++ b/include/utils/Looper.h @@ -296,6 +296,13 @@ public: */ void removeMessages(const sp& handler, int what); + /** + * Return whether this looper's thread is currently idling -- that is, whether it + * stopped waiting for more work to do. Note that this is intrinsically racy, since + * its state can change before you get the result back. + */ + bool isIdling() const; + /** * Prepares a looper associated with the calling thread, and returns it. * If the thread already has a looper, it is returned. Otherwise, a new @@ -353,6 +360,10 @@ private: Vector mMessageEnvelopes; // guarded by mLock bool mSendingMessage; // guarded by mLock + // Whether we are currently waiting for work. Not protected by a lock, + // any use of it is racy anyway. + volatile bool mIdling; + int mEpollFd; // immutable // Locked list of file descriptor monitoring requests. diff --git a/libs/utils/Looper.cpp b/libs/utils/Looper.cpp index a5e66458c..c51df2d1a 100644 --- a/libs/utils/Looper.cpp +++ b/libs/utils/Looper.cpp @@ -84,6 +84,8 @@ Looper::Looper(bool allowNonCallbacks) : LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake write pipe non-blocking. errno=%d", errno); + mIdling = false; + // Allocate the epoll instance and register the wake pipe. mEpollFd = epoll_create(EPOLL_SIZE_HINT); LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance. errno=%d", errno); @@ -214,9 +216,15 @@ int Looper::pollInner(int timeoutMillis) { mResponses.clear(); mResponseIndex = 0; + // We are about to idle. + mIdling = true; + struct epoll_event eventItems[EPOLL_MAX_EVENTS]; int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis); + // No longer idling. + mIdling = false; + // Acquire lock. mLock.lock(); @@ -558,4 +566,8 @@ void Looper::removeMessages(const sp& handler, int what) { } // release lock } +bool Looper::isIdling() const { + return mIdling; +} + } // namespace android From 9eb2a3b1c0cc1ff3082a9283e24c8babc112f56b Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Mon, 6 May 2013 20:20:50 -0700 Subject: [PATCH 478/541] libutils clean-up Change-Id: I6ff4cfc736751de2912c697f954e45e275f2d386 --- include/utils/BufferedTextOutput.h | 67 -- include/utils/Debug.h | 28 +- include/utils/GenerationCache.h | 247 -------- include/utils/LruCache.h | 11 +- include/utils/String16.h | 2 - include/utils/String8.h | 2 - include/utils/StringArray.h | 83 --- include/utils/StrongPointer.h | 6 - include/utils/SystemClock.h | 1 - include/utils/TextOutput.h | 190 ------ include/utils/Timers.h | 39 -- include/utils/WorkQueue.h | 119 ---- include/utils/ZipFileCRO.h | 61 -- include/utils/ZipFileRO.h | 262 -------- include/utils/ZipUtils.h | 67 -- include/utils/misc.h | 21 - libs/utils/Android.mk | 21 +- libs/utils/BufferedTextOutput.cpp | 279 --------- libs/utils/Debug.cpp | 318 ---------- libs/utils/RefBase.cpp | 16 - libs/utils/Static.cpp | 58 +- libs/utils/String16.cpp | 9 - libs/utils/String8.cpp | 11 +- libs/utils/StringArray.cpp | 113 ---- libs/utils/SystemClock.cpp | 54 +- libs/utils/TextOutput.cpp | 154 ----- libs/utils/Timers.cpp | 62 -- libs/utils/WorkQueue.cpp | 171 ----- libs/utils/ZipFileCRO.cpp | 54 -- libs/utils/ZipFileRO.cpp | 931 ---------------------------- libs/utils/ZipUtils.cpp | 345 ----------- libs/utils/misc.cpp | 51 -- libs/utils/tests/Android.mk | 3 +- libs/utils/tests/ZipFileRO_test.cpp | 64 -- 34 files changed, 27 insertions(+), 3893 deletions(-) delete mode 100644 include/utils/BufferedTextOutput.h delete mode 100644 include/utils/GenerationCache.h delete mode 100644 include/utils/StringArray.h delete mode 100644 include/utils/TextOutput.h delete mode 100644 include/utils/WorkQueue.h delete mode 100644 include/utils/ZipFileCRO.h delete mode 100644 include/utils/ZipFileRO.h delete mode 100644 include/utils/ZipUtils.h delete mode 100644 libs/utils/BufferedTextOutput.cpp delete mode 100644 libs/utils/Debug.cpp delete mode 100644 libs/utils/StringArray.cpp delete mode 100644 libs/utils/TextOutput.cpp delete mode 100644 libs/utils/WorkQueue.cpp delete mode 100644 libs/utils/ZipFileCRO.cpp delete mode 100644 libs/utils/ZipFileRO.cpp delete mode 100644 libs/utils/ZipUtils.cpp delete mode 100644 libs/utils/tests/ZipFileRO_test.cpp diff --git a/include/utils/BufferedTextOutput.h b/include/utils/BufferedTextOutput.h deleted file mode 100644 index 69c624037..000000000 --- a/include/utils/BufferedTextOutput.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2006 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_BUFFEREDTEXTOUTPUT_H -#define ANDROID_BUFFEREDTEXTOUTPUT_H - -#include -#include -#include - -// --------------------------------------------------------------------------- -namespace android { - -class BufferedTextOutput : public TextOutput -{ -public: - //** Flags for constructor */ - enum { - MULTITHREADED = 0x0001 - }; - - BufferedTextOutput(uint32_t flags = 0); - virtual ~BufferedTextOutput(); - - virtual status_t print(const char* txt, size_t len); - virtual void moveIndent(int delta); - - virtual void pushBundle(); - virtual void popBundle(); - -protected: - virtual status_t writeLines(const struct iovec& vec, size_t N) = 0; - -private: - struct BufferState; - struct ThreadState; - - static ThreadState*getThreadState(); - static void threadDestructor(void *st); - - BufferState*getBuffer() const; - - uint32_t mFlags; - const int32_t mSeq; - const int32_t mIndex; - - Mutex mLock; - BufferState* mGlobalState; -}; - -// --------------------------------------------------------------------------- -}; // namespace android - -#endif // ANDROID_BUFFEREDTEXTOUTPUT_H diff --git a/include/utils/Debug.h b/include/utils/Debug.h index d9ed32d7d..08893bdaa 100644 --- a/include/utils/Debug.h +++ b/include/utils/Debug.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ANDROID_DEBUG_H -#define ANDROID_DEBUG_H +#ifndef ANDROID_UTILS_DEBUG_H +#define ANDROID_UTILS_DEBUG_H #include #include @@ -42,29 +42,7 @@ template struct CompileTimeIfElse { typedef RHS TYPE; }; #endif -// --------------------------------------------------------------------------- - -#ifdef __cplusplus -extern "C" { -#endif - -const char* stringForIndent(int32_t indentLevel); - -typedef void (*debugPrintFunc)(void* cookie, const char* txt); - -void printTypeCode(uint32_t typeCode, - debugPrintFunc func = 0, void* cookie = 0); - -void printHexData(int32_t indent, const void *buf, size_t length, - size_t bytesPerLine=16, int32_t singleLineBytesCutoff=16, - size_t alignment=0, bool cArrayStyle=false, - debugPrintFunc func = 0, void* cookie = 0); - -#ifdef __cplusplus -} -#endif - // --------------------------------------------------------------------------- }; // namespace android -#endif // ANDROID_DEBUG_H +#endif // ANDROID_UTILS_DEBUG_H diff --git a/include/utils/GenerationCache.h b/include/utils/GenerationCache.h deleted file mode 100644 index 40722d11e..000000000 --- a/include/utils/GenerationCache.h +++ /dev/null @@ -1,247 +0,0 @@ -/* - * Copyright (C) 2010 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_UTILS_GENERATION_CACHE_H -#define ANDROID_UTILS_GENERATION_CACHE_H - -#include -#include - -namespace android { - -/** - * GenerationCache callback used when an item is removed - */ -template -class OnEntryRemoved { -public: - virtual ~OnEntryRemoved() { }; - virtual void operator()(EntryKey& key, EntryValue& value) = 0; -}; // class OnEntryRemoved - -template -struct Entry: public LightRefBase > { - Entry(const Entry& e) : - key(e.key), value(e.value), - parent(e.parent), child(e.child) { } - Entry(const EntryKey& key, const EntryValue& value) : - key(key), value(value) { } - - EntryKey key; - EntryValue value; - - sp > parent; // next older entry - sp > child; // next younger entry -}; // struct Entry - -/** - * A LRU type cache - */ -template -class GenerationCache { -public: - GenerationCache(uint32_t maxCapacity); - virtual ~GenerationCache(); - - enum Capacity { - kUnlimitedCapacity, - }; - - void setOnEntryRemovedListener(OnEntryRemoved* listener); - - size_t size() const; - - void clear(); - - bool contains(const K& key) const; - const K& getKeyAt(size_t index) const; - const V& getValueAt(size_t index) const; - - const V& get(const K& key); - bool put(const K& key, const V& value); - - void removeAt(ssize_t index); - bool remove(const K& key); - bool removeOldest(); - -private: - KeyedVector > > mCache; - uint32_t mMaxCapacity; - - OnEntryRemoved* mListener; - - sp > mOldest; - sp > mYoungest; - - void attachToCache(const sp >& entry); - void detachFromCache(const sp >& entry); - - const V mNullValue; -}; // class GenerationCache - -template -GenerationCache::GenerationCache(uint32_t maxCapacity): mMaxCapacity(maxCapacity), - mListener(NULL), mNullValue(NULL) { -}; - -template -GenerationCache::~GenerationCache() { - clear(); -}; - -template -uint32_t GenerationCache::size() const { - return mCache.size(); -} - -/** - * Should be set by the user of the Cache so that the callback is called whenever an item is - * removed from the cache - */ -template -void GenerationCache::setOnEntryRemovedListener(OnEntryRemoved* listener) { - mListener = listener; -} - -template -void GenerationCache::clear() { - if (mListener) { - for (uint32_t i = 0; i < mCache.size(); i++) { - sp > entry = mCache.valueAt(i); - if (mListener) { - (*mListener)(entry->key, entry->value); - } - } - } - mCache.clear(); - mYoungest.clear(); - mOldest.clear(); -} - -template -bool GenerationCache::contains(const K& key) const { - return mCache.indexOfKey(key) >= 0; -} - -template -const K& GenerationCache::getKeyAt(size_t index) const { - return mCache.keyAt(index); -} - -template -const V& GenerationCache::getValueAt(size_t index) const { - return mCache.valueAt(index)->value; -} - -template -const V& GenerationCache::get(const K& key) { - ssize_t index = mCache.indexOfKey(key); - if (index >= 0) { - const sp >& entry = mCache.valueAt(index); - detachFromCache(entry); - attachToCache(entry); - return entry->value; - } - - return mNullValue; -} - -template -bool GenerationCache::put(const K& key, const V& value) { - if (mMaxCapacity != kUnlimitedCapacity && mCache.size() >= mMaxCapacity) { - removeOldest(); - } - - ssize_t index = mCache.indexOfKey(key); - if (index < 0) { - sp > entry = new Entry(key, value); - mCache.add(key, entry); - attachToCache(entry); - return true; - } - - return false; -} - -template -bool GenerationCache::remove(const K& key) { - ssize_t index = mCache.indexOfKey(key); - if (index >= 0) { - removeAt(index); - return true; - } - - return false; -} - -template -void GenerationCache::removeAt(ssize_t index) { - sp > entry = mCache.valueAt(index); - if (mListener) { - (*mListener)(entry->key, entry->value); - } - mCache.removeItemsAt(index, 1); - detachFromCache(entry); -} - -template -bool GenerationCache::removeOldest() { - if (mOldest.get()) { - ssize_t index = mCache.indexOfKey(mOldest->key); - if (index >= 0) { - removeAt(index); - return true; - } - ALOGE("GenerationCache: removeOldest failed to find the item in the cache " - "with the given key, but we know it must be in there. " - "Is the key comparator kaput?"); - } - - return false; -} - -template -void GenerationCache::attachToCache(const sp >& entry) { - if (!mYoungest.get()) { - mYoungest = mOldest = entry; - } else { - entry->parent = mYoungest; - mYoungest->child = entry; - mYoungest = entry; - } -} - -template -void GenerationCache::detachFromCache(const sp >& entry) { - if (entry->parent.get()) { - entry->parent->child = entry->child; - } else { - mOldest = entry->child; - } - - if (entry->child.get()) { - entry->child->parent = entry->parent; - } else { - mYoungest = entry->parent; - } - - entry->parent.clear(); - entry->child.clear(); -} - -}; // namespace android - -#endif // ANDROID_UTILS_GENERATION_CACHE_H diff --git a/include/utils/LruCache.h b/include/utils/LruCache.h index 302b929c7..053bfaf97 100644 --- a/include/utils/LruCache.h +++ b/include/utils/LruCache.h @@ -18,12 +18,19 @@ #define ANDROID_UTILS_LRU_CACHE_H #include -#include #include namespace android { -// OnEntryRemoved is defined in GenerationCache.h, but maybe should move here. +/** + * GenerationCache callback used when an item is removed + */ +template +class OnEntryRemoved { +public: + virtual ~OnEntryRemoved() { }; + virtual void operator()(EntryKey& key, EntryValue& value) = 0; +}; // class OnEntryRemoved template class LruCache { diff --git a/include/utils/String16.h b/include/utils/String16.h index fe06c576f..6d4253d24 100644 --- a/include/utils/String16.h +++ b/include/utils/String16.h @@ -117,8 +117,6 @@ private: // require any change to the underlying SharedBuffer contents or reference count. ANDROID_TRIVIAL_MOVE_TRAIT(String16) -TextOutput& operator<<(TextOutput& to, const String16& val); - // --------------------------------------------------------------------------- // No user servicable parts below. diff --git a/include/utils/String8.h b/include/utils/String8.h index 335e7f1bf..9426fcfc8 100644 --- a/include/utils/String8.h +++ b/include/utils/String8.h @@ -224,8 +224,6 @@ private: // require any change to the underlying SharedBuffer contents or reference count. ANDROID_TRIVIAL_MOVE_TRAIT(String8) -TextOutput& operator<<(TextOutput& to, const String16& val); - // --------------------------------------------------------------------------- // No user servicable parts below. diff --git a/include/utils/StringArray.h b/include/utils/StringArray.h deleted file mode 100644 index c24458718..000000000 --- a/include/utils/StringArray.h +++ /dev/null @@ -1,83 +0,0 @@ -/* - * 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. - */ - -// -// Sortable array of strings. STL-ish, but STL-free. -// -#ifndef _LIBS_UTILS_STRING_ARRAY_H -#define _LIBS_UTILS_STRING_ARRAY_H - -#include -#include - -namespace android { - -// -// An expanding array of strings. Add, get, sort, delete. -// -class StringArray { -public: - StringArray(); - virtual ~StringArray(); - - // - // Add a string. A copy of the string is made. - // - bool push_back(const char* str); - - // - // Delete an entry. - // - void erase(int idx); - - // - // Sort the array. - // - void sort(int (*compare)(const void*, const void*)); - - // - // Pass this to the sort routine to do an ascending alphabetical sort. - // - static int cmpAscendingAlpha(const void* pstr1, const void* pstr2); - - // - // Get the #of items in the array. - // - inline int size(void) const { return mCurrent; } - - // - // Return entry N. - // [should use operator[] here] - // - const char* getEntry(int idx) const { - return (unsigned(idx) >= unsigned(mCurrent)) ? NULL : mArray[idx]; - } - - // - // Set entry N to specified string. - // [should use operator[] here] - // - void setEntry(int idx, const char* str); - -private: - int mMax; - int mCurrent; - char** mArray; -}; - -}; // namespace android - -#endif // _LIBS_UTILS_STRING_ARRAY_H diff --git a/include/utils/StrongPointer.h b/include/utils/StrongPointer.h index 49fa3a8d6..40aebfd3c 100644 --- a/include/utils/StrongPointer.h +++ b/include/utils/StrongPointer.h @@ -207,12 +207,6 @@ void sp::set_pointer(T* ptr) { m_ptr = ptr; } -template -inline TextOutput& operator<<(TextOutput& to, const sp& val) -{ - return printStrongPointer(to, val.get()); -} - }; // namespace android // --------------------------------------------------------------------------- diff --git a/include/utils/SystemClock.h b/include/utils/SystemClock.h index d75264cd3..01db34078 100644 --- a/include/utils/SystemClock.h +++ b/include/utils/SystemClock.h @@ -22,7 +22,6 @@ namespace android { -int setCurrentTimeMillis(int64_t millis); int64_t uptimeMillis(); int64_t elapsedRealtime(); int64_t elapsedRealtimeNano(); diff --git a/include/utils/TextOutput.h b/include/utils/TextOutput.h deleted file mode 100644 index de2fbbee1..000000000 --- a/include/utils/TextOutput.h +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Copyright (C) 2006 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_TEXTOUTPUT_H -#define ANDROID_TEXTOUTPUT_H - -#include - -#include -#include - -// --------------------------------------------------------------------------- -namespace android { - -class TextOutput -{ -public: - TextOutput(); - virtual ~TextOutput(); - - virtual status_t print(const char* txt, size_t len) = 0; - virtual void moveIndent(int delta) = 0; - - class Bundle { - public: - inline Bundle(TextOutput& to) : mTO(to) { to.pushBundle(); } - inline ~Bundle() { mTO.popBundle(); } - private: - TextOutput& mTO; - }; - - virtual void pushBundle() = 0; - virtual void popBundle() = 0; -}; - -// --------------------------------------------------------------------------- - -// Text output stream for printing to the log (via utils/Log.h). -extern TextOutput& alog; - -// Text output stream for printing to stdout. -extern TextOutput& aout; - -// Text output stream for printing to stderr. -extern TextOutput& aerr; - -typedef TextOutput& (*TextOutputManipFunc)(TextOutput&); - -TextOutput& endl(TextOutput& to); -TextOutput& indent(TextOutput& to); -TextOutput& dedent(TextOutput& to); - -TextOutput& operator<<(TextOutput& to, const char* str); -TextOutput& operator<<(TextOutput& to, char); // writes raw character -TextOutput& operator<<(TextOutput& to, bool); -TextOutput& operator<<(TextOutput& to, int); -TextOutput& operator<<(TextOutput& to, long); -TextOutput& operator<<(TextOutput& to, unsigned int); -TextOutput& operator<<(TextOutput& to, unsigned long); -TextOutput& operator<<(TextOutput& to, long long); -TextOutput& operator<<(TextOutput& to, unsigned long long); -TextOutput& operator<<(TextOutput& to, float); -TextOutput& operator<<(TextOutput& to, double); -TextOutput& operator<<(TextOutput& to, TextOutputManipFunc func); -TextOutput& operator<<(TextOutput& to, const void*); - -class TypeCode -{ -public: - inline TypeCode(uint32_t code); - inline ~TypeCode(); - - inline uint32_t typeCode() const; - -private: - uint32_t mCode; -}; - -TextOutput& operator<<(TextOutput& to, const TypeCode& val); - -class HexDump -{ -public: - HexDump(const void *buf, size_t size, size_t bytesPerLine=16); - inline ~HexDump(); - - inline HexDump& setBytesPerLine(size_t bytesPerLine); - inline HexDump& setSingleLineCutoff(int32_t bytes); - inline HexDump& setAlignment(size_t alignment); - inline HexDump& setCArrayStyle(bool enabled); - - inline const void* buffer() const; - inline size_t size() const; - inline size_t bytesPerLine() const; - inline int32_t singleLineCutoff() const; - inline size_t alignment() const; - inline bool carrayStyle() const; - -private: - const void* mBuffer; - size_t mSize; - size_t mBytesPerLine; - int32_t mSingleLineCutoff; - size_t mAlignment; - bool mCArrayStyle; -}; - -TextOutput& operator<<(TextOutput& to, const HexDump& val); - -// --------------------------------------------------------------------------- -// No user servicable parts below. - -inline TextOutput& endl(TextOutput& to) -{ - to.print("\n", 1); - return to; -} - -inline TextOutput& indent(TextOutput& to) -{ - to.moveIndent(1); - return to; -} - -inline TextOutput& dedent(TextOutput& to) -{ - to.moveIndent(-1); - return to; -} - -inline TextOutput& operator<<(TextOutput& to, const char* str) -{ - to.print(str, strlen(str)); - return to; -} - -inline TextOutput& operator<<(TextOutput& to, char c) -{ - to.print(&c, 1); - return to; -} - -inline TextOutput& operator<<(TextOutput& to, TextOutputManipFunc func) -{ - return (*func)(to); -} - -inline TypeCode::TypeCode(uint32_t code) : mCode(code) { } -inline TypeCode::~TypeCode() { } -inline uint32_t TypeCode::typeCode() const { return mCode; } - -inline HexDump::~HexDump() { } - -inline HexDump& HexDump::setBytesPerLine(size_t bytesPerLine) { - mBytesPerLine = bytesPerLine; return *this; -} -inline HexDump& HexDump::setSingleLineCutoff(int32_t bytes) { - mSingleLineCutoff = bytes; return *this; -} -inline HexDump& HexDump::setAlignment(size_t alignment) { - mAlignment = alignment; return *this; -} -inline HexDump& HexDump::setCArrayStyle(bool enabled) { - mCArrayStyle = enabled; return *this; -} - -inline const void* HexDump::buffer() const { return mBuffer; } -inline size_t HexDump::size() const { return mSize; } -inline size_t HexDump::bytesPerLine() const { return mBytesPerLine; } -inline int32_t HexDump::singleLineCutoff() const { return mSingleLineCutoff; } -inline size_t HexDump::alignment() const { return mAlignment; } -inline bool HexDump::carrayStyle() const { return mCArrayStyle; } - -// --------------------------------------------------------------------------- -}; // namespace android - -#endif // ANDROID_TEXTOUTPUT_H diff --git a/include/utils/Timers.h b/include/utils/Timers.h index 92f66c94c..d01542169 100644 --- a/include/utils/Timers.h +++ b/include/utils/Timers.h @@ -103,43 +103,4 @@ int toMillisecondTimeoutDelay(nsecs_t referenceTime, nsecs_t timeoutTime); } // extern "C" #endif -// ------------------------------------------------------------------ -// C++ API - -#ifdef __cplusplus - -namespace android { -/* - * Time the duration of something. - * - * Includes some timeval manipulation functions. - */ -class DurationTimer { -public: - DurationTimer() {} - ~DurationTimer() {} - - // Start the timer. - void start(); - // Stop the timer. - void stop(); - // Get the duration in microseconds. - long long durationUsecs() const; - - // Subtract two timevals. Returns the difference (ptv1-ptv2) in - // microseconds. - static long long subtractTimevals(const struct timeval* ptv1, - const struct timeval* ptv2); - - // Add the specified amount of time to the timeval. - static void addToTimeval(struct timeval* ptv, long usec); - -private: - struct timeval mStartWhen; - struct timeval mStopWhen; -}; - -}; // android -#endif // def __cplusplus - #endif // _LIBS_UTILS_TIMERS_H diff --git a/include/utils/WorkQueue.h b/include/utils/WorkQueue.h deleted file mode 100644 index e3c75b219..000000000 --- a/include/utils/WorkQueue.h +++ /dev/null @@ -1,119 +0,0 @@ -/*] - * Copyright (C) 2012 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 _LIBS_UTILS_WORK_QUEUE_H -#define _LIBS_UTILS_WORK_QUEUE_H - -#include -#include -#include - -namespace android { - -/* - * A threaded work queue. - * - * This class is designed to make it easy to run a bunch of isolated work - * units in parallel, using up to the specified number of threads. - * To use it, write a loop to post work units to the work queue, then synchronize - * on the queue at the end. - */ -class WorkQueue { -public: - class WorkUnit { - public: - WorkUnit() { } - virtual ~WorkUnit() { } - - /* - * Runs the work unit. - * If the result is 'true' then the work queue continues scheduling work as usual. - * If the result is 'false' then the work queue is canceled. - */ - virtual bool run() = 0; - }; - - /* Creates a work queue with the specified maximum number of work threads. */ - WorkQueue(size_t maxThreads, bool canCallJava = true); - - /* Destroys the work queue. - * Cancels pending work and waits for all remaining threads to complete. - */ - ~WorkQueue(); - - /* Posts a work unit to run later. - * If the work queue has been canceled or is already finished, returns INVALID_OPERATION - * and does not take ownership of the work unit (caller must destroy it itself). - * Otherwise, returns OK and takes ownership of the work unit (the work queue will - * destroy it automatically). - * - * For flow control, this method blocks when the size of the pending work queue is more - * 'backlog' times the number of threads. This condition reduces the rate of entry into - * the pending work queue and prevents it from growing much more rapidly than the - * work threads can actually handle. - * - * If 'backlog' is 0, then no throttle is applied. - */ - status_t schedule(WorkUnit* workUnit, size_t backlog = 2); - - /* Cancels all pending work. - * If the work queue is already finished, returns INVALID_OPERATION. - * If the work queue is already canceled, returns OK and does nothing else. - * Otherwise, returns OK, discards all pending work units and prevents additional - * work units from being scheduled. - * - * Call finish() after cancel() to wait for all remaining work to complete. - */ - status_t cancel(); - - /* Waits for all work to complete. - * If the work queue is already finished, returns INVALID_OPERATION. - * Otherwise, waits for all work to complete and returns OK. - */ - status_t finish(); - -private: - class WorkThread : public Thread { - public: - WorkThread(WorkQueue* workQueue, bool canCallJava); - virtual ~WorkThread(); - - private: - virtual bool threadLoop(); - - WorkQueue* const mWorkQueue; - }; - - status_t cancelLocked(); - bool threadLoop(); // called from each work thread - - const size_t mMaxThreads; - const bool mCanCallJava; - - Mutex mLock; - Condition mWorkChangedCondition; - Condition mWorkDequeuedCondition; - - bool mCanceled; - bool mFinished; - size_t mIdleThreads; - Vector > mWorkThreads; - Vector mWorkUnits; -}; - -}; // namespace android - -#endif // _LIBS_UTILS_WORK_QUEUE_H diff --git a/include/utils/ZipFileCRO.h b/include/utils/ZipFileCRO.h deleted file mode 100644 index 3e42a958b..000000000 --- a/include/utils/ZipFileCRO.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// -// C API for ead-only access to Zip archives, with minimal heap allocation. -// -#ifndef __LIBS_ZIPFILECRO_H -#define __LIBS_ZIPFILECRO_H - -#include -#include -#include - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/* - * Trivial typedef to ensure that ZipFileCRO is not treated as a simple integer. - */ -typedef void* ZipFileCRO; - -/* - * Trivial typedef to ensure that ZipEntryCRO is not treated as a simple - * integer. We use NULL to indicate an invalid value. - */ -typedef void* ZipEntryCRO; - -extern ZipFileCRO ZipFileXRO_open(const char* path); - -extern void ZipFileCRO_destroy(ZipFileCRO zip); - -extern ZipEntryCRO ZipFileCRO_findEntryByName(ZipFileCRO zip, - const char* fileName); - -extern bool ZipFileCRO_getEntryInfo(ZipFileCRO zip, ZipEntryCRO entry, - int* pMethod, size_t* pUncompLen, - size_t* pCompLen, off64_t* pOffset, long* pModWhen, long* pCrc32); - -extern bool ZipFileCRO_uncompressEntry(ZipFileCRO zip, ZipEntryCRO entry, int fd); - -#ifdef __cplusplus -} -#endif - -#endif /*__LIBS_ZIPFILECRO_H*/ diff --git a/include/utils/ZipFileRO.h b/include/utils/ZipFileRO.h deleted file mode 100644 index 547e36a09..000000000 --- a/include/utils/ZipFileRO.h +++ /dev/null @@ -1,262 +0,0 @@ -/* - * 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. - */ - -/* - * Read-only access to Zip archives, with minimal heap allocation. - * - * This is similar to the more-complete ZipFile class, but no attempt - * has been made to make them interchangeable. This class operates under - * a very different set of assumptions and constraints. - * - * One such assumption is that if you're getting file descriptors for - * use with this class as a child of a fork() operation, you must be on - * a pread() to guarantee correct operation. This is because pread() can - * atomically read at a file offset without worrying about a lock around an - * lseek() + read() pair. - */ -#ifndef __LIBS_ZIPFILERO_H -#define __LIBS_ZIPFILERO_H - -#include -#include -#include -#include - -#include -#include -#include -#include - -namespace android { - -/* - * Trivial typedef to ensure that ZipEntryRO is not treated as a simple - * integer. We use NULL to indicate an invalid value. - */ -typedef void* ZipEntryRO; - -/* - * Open a Zip archive for reading. - * - * We want "open" and "find entry by name" to be fast operations, and we - * want to use as little memory as possible. We memory-map the file, - * and load a hash table with pointers to the filenames (which aren't - * null-terminated). The other fields are at a fixed offset from the - * filename, so we don't need to extract those (but we do need to byte-read - * and endian-swap them every time we want them). - * - * To speed comparisons when doing a lookup by name, we could make the mapping - * "private" (copy-on-write) and null-terminate the filenames after verifying - * the record structure. However, this requires a private mapping of - * every page that the Central Directory touches. Easier to tuck a copy - * of the string length into the hash table entry. - * - * NOTE: If this is used on file descriptors inherited from a fork() operation, - * you must be on a platform that implements pread() to guarantee correctness - * on the shared file descriptors. - */ -class ZipFileRO { -public: - ZipFileRO() - : mFd(-1), mFileName(NULL), mFileLength(-1), - mDirectoryMap(NULL), - mNumEntries(-1), mDirectoryOffset(-1), - mHashTableSize(-1), mHashTable(NULL) - {} - - ~ZipFileRO(); - - /* - * Open an archive. - */ - status_t open(const char* zipFileName); - - /* - * Find an entry, by name. Returns the entry identifier, or NULL if - * not found. - * - * If two entries have the same name, one will be chosen at semi-random. - */ - ZipEntryRO findEntryByName(const char* fileName) const; - - /* - * Return the #of entries in the Zip archive. - */ - int getNumEntries(void) const { - return mNumEntries; - } - - /* - * Return the Nth entry. Zip file entries are not stored in sorted - * order, and updated entries may appear at the end, so anyone walking - * the archive needs to avoid making ordering assumptions. We take - * that further by returning the Nth non-empty entry in the hash table - * rather than the Nth entry in the archive. - * - * Valid values are [0..numEntries). - * - * [This is currently O(n). If it needs to be fast we can allocate an - * additional data structure or provide an iterator interface.] - */ - ZipEntryRO findEntryByIndex(int idx) const; - - /* - * Copy the filename into the supplied buffer. Returns 0 on success, - * -1 if "entry" is invalid, or the filename length if it didn't fit. The - * length, and the returned string, include the null-termination. - */ - int getEntryFileName(ZipEntryRO entry, char* buffer, int bufLen) const; - - /* - * Get the vital stats for an entry. Pass in NULL pointers for anything - * you don't need. - * - * "*pOffset" holds the Zip file offset of the entry's data. - * - * Returns "false" if "entry" is bogus or if the data in the Zip file - * appears to be bad. - */ - bool getEntryInfo(ZipEntryRO entry, int* pMethod, size_t* pUncompLen, - size_t* pCompLen, off64_t* pOffset, long* pModWhen, long* pCrc32) const; - - /* - * Create a new FileMap object that maps a subset of the archive. For - * an uncompressed entry this effectively provides a pointer to the - * actual data, for a compressed entry this provides the input buffer - * for inflate(). - */ - FileMap* createEntryFileMap(ZipEntryRO entry) const; - - /* - * Uncompress the data into a buffer. Depending on the compression - * format, this is either an "inflate" operation or a memcpy. - * - * Use "uncompLen" from getEntryInfo() to determine the required - * buffer size. - * - * Returns "true" on success. - */ - bool uncompressEntry(ZipEntryRO entry, void* buffer) const; - - /* - * Uncompress the data to an open file descriptor. - */ - bool uncompressEntry(ZipEntryRO entry, int fd) const; - - /* Zip compression methods we support */ - enum { - kCompressStored = 0, // no compression - kCompressDeflated = 8, // standard deflate - }; - - /* - * Utility function: uncompress deflated data, buffer to buffer. - */ - static bool inflateBuffer(void* outBuf, const void* inBuf, - size_t uncompLen, size_t compLen); - - /* - * Utility function: uncompress deflated data, buffer to fd. - */ - static bool inflateBuffer(int fd, const void* inBuf, - size_t uncompLen, size_t compLen); - - /* - * Utility function to convert ZIP's time format to a timespec struct. - */ - static inline void zipTimeToTimespec(long when, struct tm* timespec) { - const long date = when >> 16; - timespec->tm_year = ((date >> 9) & 0x7F) + 80; // Zip is years since 1980 - timespec->tm_mon = (date >> 5) & 0x0F; - timespec->tm_mday = date & 0x1F; - - timespec->tm_hour = (when >> 11) & 0x1F; - timespec->tm_min = (when >> 5) & 0x3F; - timespec->tm_sec = (when & 0x1F) << 1; - } - - /* - * Some basic functions for raw data manipulation. "LE" means - * Little Endian. - */ - static inline unsigned short get2LE(const unsigned char* buf) { - return buf[0] | (buf[1] << 8); - } - static inline unsigned long get4LE(const unsigned char* buf) { - return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); - } - -private: - /* these are private and not defined */ - ZipFileRO(const ZipFileRO& src); - ZipFileRO& operator=(const ZipFileRO& src); - - /* locate and parse the central directory */ - bool mapCentralDirectory(void); - - /* parse the archive, prepping internal structures */ - bool parseZipArchive(void); - - /* add a new entry to the hash table */ - void addToHash(const char* str, int strLen, unsigned int hash); - - /* compute string hash code */ - static unsigned int computeHash(const char* str, int len); - - /* convert a ZipEntryRO back to a hash table index */ - int entryToIndex(const ZipEntryRO entry) const; - - /* - * One entry in the hash table. - */ - typedef struct HashEntry { - const char* name; - unsigned short nameLen; - //unsigned int hash; - } HashEntry; - - /* open Zip archive */ - int mFd; - - /* Lock for handling the file descriptor (seeks, etc) */ - mutable Mutex mFdLock; - - /* zip file name */ - char* mFileName; - - /* length of file */ - size_t mFileLength; - - /* mapped file */ - FileMap* mDirectoryMap; - - /* number of entries in the Zip archive */ - int mNumEntries; - - /* CD directory offset in the Zip archive */ - off64_t mDirectoryOffset; - - /* - * We know how many entries are in the Zip archive, so we have a - * fixed-size hash table. We probe for an empty slot. - */ - int mHashTableSize; - HashEntry* mHashTable; -}; - -}; // namespace android - -#endif /*__LIBS_ZIPFILERO_H*/ diff --git a/include/utils/ZipUtils.h b/include/utils/ZipUtils.h deleted file mode 100644 index 42c42b6c0..000000000 --- a/include/utils/ZipUtils.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * 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. - */ - -// -// Miscellaneous zip/gzip utility functions. -// -#ifndef __LIBS_ZIPUTILS_H -#define __LIBS_ZIPUTILS_H - -#include - -namespace android { - -/* - * Container class for utility functions, primarily for namespace reasons. - */ -class ZipUtils { -public: - /* - * General utility function for uncompressing "deflate" data from a file - * to a buffer. - */ - static bool inflateToBuffer(int fd, void* buf, long uncompressedLen, - long compressedLen); - static bool inflateToBuffer(FILE* fp, void* buf, long uncompressedLen, - long compressedLen); - - /* - * Someday we might want to make this generic and handle bzip2 ".bz2" - * files too. - * - * We could declare gzip to be a sub-class of zip that has exactly - * one always-compressed entry, but we currently want to treat Zip - * and gzip as distinct, so there's no value. - * - * The zlib library has some gzip utilities, but it has no interface - * for extracting the uncompressed length of the file (you do *not* - * want to gzseek to the end). - * - * Pass in a seeked file pointer for the gzip file. If this is a gzip - * file, we set our return values appropriately and return "true" with - * the file seeked to the start of the compressed data. - */ - static bool examineGzip(FILE* fp, int* pCompressionMethod, - long* pUncompressedLen, long* pCompressedLen, unsigned long* pCRC32); - -private: - ZipUtils() {} - ~ZipUtils() {} -}; - -}; // namespace android - -#endif /*__LIBS_ZIPUTILS_H*/ diff --git a/include/utils/misc.h b/include/utils/misc.h index f1aa4325a..6cccec387 100644 --- a/include/utils/misc.h +++ b/include/utils/misc.h @@ -20,7 +20,6 @@ #ifndef _LIBS_UTILS_MISC_H #define _LIBS_UTILS_MISC_H -#include #include /* get #of elements in a static array */ @@ -30,26 +29,6 @@ namespace android { -/* - * Some utility functions for working with files. These could be made - * part of a "File" class. - */ -typedef enum FileType { - kFileTypeUnknown = 0, - kFileTypeNonexistent, // i.e. ENOENT - kFileTypeRegular, - kFileTypeDirectory, - kFileTypeCharDev, - kFileTypeBlockDev, - kFileTypeFifo, - kFileTypeSymlink, - kFileTypeSocket, -} FileType; -/* get the file's type; follows symlinks */ -FileType getFileType(const char* fileName); -/* get the file's modification date; returns -1 w/errno set on failure */ -time_t getFileModDate(const char* fileName); - typedef void (*sysprop_change_callback)(void); void add_sysprop_change_callback(sysprop_change_callback cb, int priority); void report_sysprop_change(); diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk index ce256053b..42e1c7e68 100644 --- a/libs/utils/Android.mk +++ b/libs/utils/Android.mk @@ -20,9 +20,7 @@ LOCAL_PATH:= $(call my-dir) commonSources:= \ BasicHashtable.cpp \ BlobCache.cpp \ - BufferedTextOutput.cpp \ CallStack.cpp \ - Debug.cpp \ FileMap.cpp \ Flattenable.cpp \ JenkinsHash.cpp \ @@ -36,18 +34,12 @@ commonSources:= \ StopWatch.cpp \ String8.cpp \ String16.cpp \ - StringArray.cpp \ SystemClock.cpp \ - TextOutput.cpp \ Threads.cpp \ Timers.cpp \ Tokenizer.cpp \ Unicode.cpp \ VectorImpl.cpp \ - WorkQueue.cpp \ - ZipFileCRO.cpp \ - ZipFileRO.cpp \ - ZipUtils.cpp \ misc.cpp host_commonCflags := -DLIBUTILS_NATIVE=1 $(TOOL_CFLAGS) @@ -74,9 +66,6 @@ ifeq ($(HOST_OS), linux) LOCAL_SRC_FILES += Looper.cpp endif LOCAL_MODULE:= libutils -LOCAL_STATIC_LIBRARIES := libz -LOCAL_C_INCLUDES := \ - external/zlib LOCAL_CFLAGS += $(host_commonCflags) LOCAL_LDLIBS += $(host_commonLdlibs) include $(BUILD_HOST_STATIC_LIBRARY) @@ -90,9 +79,6 @@ ifeq ($(HOST_OS), linux) LOCAL_SRC_FILES += Looper.cpp endif LOCAL_MODULE:= lib64utils -LOCAL_STATIC_LIBRARIES := libz -LOCAL_C_INCLUDES := \ - external/zlib LOCAL_CFLAGS += $(host_commonCflags) -m64 LOCAL_LDLIBS += $(host_commonLdlibs) include $(BUILD_HOST_STATIC_LIBRARY) @@ -124,8 +110,7 @@ LOCAL_C_INCLUDES += \ LOCAL_LDLIBS += -lpthread LOCAL_STATIC_LIBRARIES := \ - libcutils \ - libz + libcutils LOCAL_SHARED_LIBRARIES := \ libcorkscrew \ @@ -144,8 +129,8 @@ LOCAL_SHARED_LIBRARIES := \ liblog \ libcutils \ libdl \ - libcorkscrew \ - libz + libcorkscrew + include $(BUILD_SHARED_LIBRARY) # Include subdirectory makefiles diff --git a/libs/utils/BufferedTextOutput.cpp b/libs/utils/BufferedTextOutput.cpp deleted file mode 100644 index 989662e84..000000000 --- a/libs/utils/BufferedTextOutput.cpp +++ /dev/null @@ -1,279 +0,0 @@ -/* - * Copyright (C) 2006 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 - -#include -#include -#include -#include -#include -#include - -#include - -#include -#include - -// --------------------------------------------------------------------------- - -namespace android { - -struct BufferedTextOutput::BufferState : public RefBase -{ - BufferState(int32_t _seq) - : seq(_seq) - , buffer(NULL) - , bufferPos(0) - , bufferSize(0) - , atFront(true) - , indent(0) - , bundle(0) { - } - ~BufferState() { - free(buffer); - } - - status_t append(const char* txt, size_t len) { - if ((len+bufferPos) > bufferSize) { - void* b = realloc(buffer, ((len+bufferPos)*3)/2); - if (!b) return NO_MEMORY; - buffer = (char*)b; - } - memcpy(buffer+bufferPos, txt, len); - bufferPos += len; - return NO_ERROR; - } - - void restart() { - bufferPos = 0; - atFront = true; - if (bufferSize > 256) { - void* b = realloc(buffer, 256); - if (b) { - buffer = (char*)b; - bufferSize = 256; - } - } - } - - const int32_t seq; - char* buffer; - size_t bufferPos; - size_t bufferSize; - bool atFront; - int32_t indent; - int32_t bundle; -}; - -struct BufferedTextOutput::ThreadState -{ - Vector > states; -}; - -static mutex_t gMutex; - -static thread_store_t tls; - -BufferedTextOutput::ThreadState* BufferedTextOutput::getThreadState() -{ - ThreadState* ts = (ThreadState*) thread_store_get( &tls ); - if (ts) return ts; - ts = new ThreadState; - thread_store_set( &tls, ts, threadDestructor ); - return ts; -} - -void BufferedTextOutput::threadDestructor(void *st) -{ - delete ((ThreadState*)st); -} - -static volatile int32_t gSequence = 0; - -static volatile int32_t gFreeBufferIndex = -1; - -static int32_t allocBufferIndex() -{ - int32_t res = -1; - - mutex_lock(&gMutex); - - if (gFreeBufferIndex >= 0) { - res = gFreeBufferIndex; - gFreeBufferIndex = gTextBuffers[res]; - gTextBuffers.editItemAt(res) = -1; - - } else { - res = gTextBuffers.size(); - gTextBuffers.add(-1); - } - - mutex_unlock(&gMutex); - - return res; -} - -static void freeBufferIndex(int32_t idx) -{ - mutex_lock(&gMutex); - gTextBuffers.editItemAt(idx) = gFreeBufferIndex; - gFreeBufferIndex = idx; - mutex_unlock(&gMutex); -} - -// --------------------------------------------------------------------------- - -BufferedTextOutput::BufferedTextOutput(uint32_t flags) - : mFlags(flags) - , mSeq(android_atomic_inc(&gSequence)) - , mIndex(allocBufferIndex()) -{ - mGlobalState = new BufferState(mSeq); - if (mGlobalState) mGlobalState->incStrong(this); -} - -BufferedTextOutput::~BufferedTextOutput() -{ - if (mGlobalState) mGlobalState->decStrong(this); - freeBufferIndex(mIndex); -} - -status_t BufferedTextOutput::print(const char* txt, size_t len) -{ - //printf("BufferedTextOutput: printing %d\n", len); - - AutoMutex _l(mLock); - BufferState* b = getBuffer(); - - const char* const end = txt+len; - - status_t err; - - while (txt < end) { - // Find the next line. - const char* first = txt; - while (txt < end && *txt != '\n') txt++; - - // Include this and all following empty lines. - while (txt < end && *txt == '\n') txt++; - - // Special cases for first data on a line. - if (b->atFront) { - if (b->indent > 0) { - // If this is the start of a line, add the indent. - const char* prefix = stringForIndent(b->indent); - err = b->append(prefix, strlen(prefix)); - if (err != NO_ERROR) return err; - - } else if (*(txt-1) == '\n' && !b->bundle) { - // Fast path: if we are not indenting or bundling, and - // have been given one or more complete lines, just write - // them out without going through the buffer. - - // Slurp up all of the lines. - const char* lastLine = txt+1; - while (txt < end) { - if (*txt++ == '\n') lastLine = txt; - } - struct iovec vec; - vec.iov_base = (void*)first; - vec.iov_len = lastLine-first; - //printf("Writing %d bytes of data!\n", vec.iov_len); - writeLines(vec, 1); - txt = lastLine; - continue; - } - } - - // Append the new text to the buffer. - err = b->append(first, txt-first); - if (err != NO_ERROR) return err; - b->atFront = *(txt-1) == '\n'; - - // If we have finished a line and are not bundling, write - // it out. - //printf("Buffer is now %d bytes\n", b->bufferPos); - if (b->atFront && !b->bundle) { - struct iovec vec; - vec.iov_base = b->buffer; - vec.iov_len = b->bufferPos; - //printf("Writing %d bytes of data!\n", vec.iov_len); - writeLines(vec, 1); - b->restart(); - } - } - - return NO_ERROR; -} - -void BufferedTextOutput::moveIndent(int delta) -{ - AutoMutex _l(mLock); - BufferState* b = getBuffer(); - b->indent += delta; - if (b->indent < 0) b->indent = 0; -} - -void BufferedTextOutput::pushBundle() -{ - AutoMutex _l(mLock); - BufferState* b = getBuffer(); - b->bundle++; -} - -void BufferedTextOutput::popBundle() -{ - AutoMutex _l(mLock); - BufferState* b = getBuffer(); - b->bundle--; - LOG_FATAL_IF(b->bundle < 0, - "TextOutput::popBundle() called more times than pushBundle()"); - if (b->bundle < 0) b->bundle = 0; - - if (b->bundle == 0) { - // Last bundle, write out data if it is complete. If it is not - // complete, don't write until the last line is done... this may - // or may not be the write thing to do, but it's the easiest. - if (b->bufferPos > 0 && b->atFront) { - struct iovec vec; - vec.iov_base = b->buffer; - vec.iov_len = b->bufferPos; - writeLines(vec, 1); - b->restart(); - } - } -} - -BufferedTextOutput::BufferState* BufferedTextOutput::getBuffer() const -{ - if ((mFlags&MULTITHREADED) != 0) { - ThreadState* ts = getThreadState(); - if (ts) { - while (ts->states.size() <= (size_t)mIndex) ts->states.add(NULL); - BufferState* bs = ts->states[mIndex].get(); - if (bs != NULL && bs->seq == mSeq) return bs; - - ts->states.editItemAt(mIndex) = new BufferState(mIndex); - bs = ts->states[mIndex].get(); - if (bs != NULL) return bs; - } - } - - return mGlobalState; -} - -}; // namespace android diff --git a/libs/utils/Debug.cpp b/libs/utils/Debug.cpp deleted file mode 100644 index e8ac983ea..000000000 --- a/libs/utils/Debug.cpp +++ /dev/null @@ -1,318 +0,0 @@ -/* - * Copyright (C) 2005 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 - -#include - -#include -#include -#include - -namespace android { - -// --------------------------------------------------------------------- - -static const char indentStr[] = -" " -" "; - -const char* stringForIndent(int32_t indentLevel) -{ - ssize_t off = sizeof(indentStr)-1-(indentLevel*2); - return indentStr + (off < 0 ? 0 : off); -} - -// --------------------------------------------------------------------- - -static void defaultPrintFunc(void* cookie, const char* txt) -{ - printf("%s", txt); -} - -// --------------------------------------------------------------------- - -static inline int isident(int c) -{ - return isalnum(c) || c == '_'; -} - -static inline bool isasciitype(char c) -{ - if( c >= ' ' && c < 127 && c != '\'' && c != '\\' ) return true; - return false; -} - -static inline char makehexdigit(uint32_t val) -{ - return "0123456789abcdef"[val&0xF]; -} - -static char* appendhexnum(uint32_t val, char* out) -{ - for( int32_t i=28; i>=0; i-=4 ) { - *out++ = makehexdigit( val>>i ); - } - *out = 0; - return out; -} - -static inline char makeupperhexdigit(uint32_t val) -{ - return "0123456789ABCDEF"[val&0xF]; -} - -static char* appendupperhexnum(uint32_t val, char* out) -{ - for( int32_t i=28; i>=0; i-=4 ) { - *out++ = makeupperhexdigit( val>>i ); - } - *out = 0; - return out; -} - -static char* appendcharornum(char c, char* out, bool skipzero = true) -{ - if (skipzero && c == 0) return out; - - if (isasciitype(c)) { - *out++ = c; - return out; - } - - *out++ = '\\'; - *out++ = 'x'; - *out++ = makehexdigit(c>>4); - *out++ = makehexdigit(c); - return out; -} - -static char* typetostring(uint32_t type, char* out, - bool fullContext = true, - bool strict = false) -{ - char* pos = out; - char c[4]; - c[0] = (char)((type>>24)&0xFF); - c[1] = (char)((type>>16)&0xFF); - c[2] = (char)((type>>8)&0xFF); - c[3] = (char)(type&0xFF); - bool valid; - if( !strict ) { - // now even less strict! - // valid = isasciitype(c[3]); - valid = true; - int32_t i = 0; - bool zero = true; - while (valid && i<3) { - if (c[i] == 0) { - if (!zero) valid = false; - } else { - zero = false; - //if (!isasciitype(c[i])) valid = false; - } - i++; - } - // if all zeros, not a valid type code. - if (zero) valid = false; - } else { - valid = isident(c[3]) ? true : false; - int32_t i = 0; - bool zero = true; - while (valid && i<3) { - if (c[i] == 0) { - if (!zero) valid = false; - } else { - zero = false; - if (!isident(c[i])) valid = false; - } - i++; - } - } - if( valid && (!fullContext || c[0] != '0' || c[1] != 'x') ) { - if( fullContext ) *pos++ = '\''; - pos = appendcharornum(c[0], pos); - pos = appendcharornum(c[1], pos); - pos = appendcharornum(c[2], pos); - pos = appendcharornum(c[3], pos); - if( fullContext ) *pos++ = '\''; - *pos = 0; - return pos; - } - - if( fullContext ) { - *pos++ = '0'; - *pos++ = 'x'; - } - return appendhexnum(type, pos); -} - -void printTypeCode(uint32_t typeCode, debugPrintFunc func, void* cookie) -{ - char buffer[32]; - char* end = typetostring(typeCode, buffer); - *end = 0; - func ? (*func)(cookie, buffer) : defaultPrintFunc(cookie, buffer); -} - -void printHexData(int32_t indent, const void *buf, size_t length, - size_t bytesPerLine, int32_t singleLineBytesCutoff, - size_t alignment, bool cStyle, - debugPrintFunc func, void* cookie) -{ - if (alignment == 0) { - if (bytesPerLine >= 16) alignment = 4; - else if (bytesPerLine >= 8) alignment = 2; - else alignment = 1; - } - if (func == NULL) func = defaultPrintFunc; - - size_t offset; - - unsigned char *pos = (unsigned char *)buf; - - if (pos == NULL) { - if (singleLineBytesCutoff < 0) func(cookie, "\n"); - func(cookie, "(NULL)"); - return; - } - - if (length == 0) { - if (singleLineBytesCutoff < 0) func(cookie, "\n"); - func(cookie, "(empty)"); - return; - } - - if ((int32_t)length < 0) { - if (singleLineBytesCutoff < 0) func(cookie, "\n"); - char buf[64]; - sprintf(buf, "(bad length: %zu)", length); - func(cookie, buf); - return; - } - - char buffer[256]; - static const size_t maxBytesPerLine = (sizeof(buffer)-1-11-4)/(3+1); - - if (bytesPerLine > maxBytesPerLine) bytesPerLine = maxBytesPerLine; - - const bool oneLine = (int32_t)length <= singleLineBytesCutoff; - bool newLine = false; - if (cStyle) { - indent++; - func(cookie, "{\n"); - newLine = true; - } else if (!oneLine) { - func(cookie, "\n"); - newLine = true; - } - - for (offset = 0; ; offset += bytesPerLine, pos += bytesPerLine) { - long remain = length; - - char* c = buffer; - if (!oneLine && !cStyle) { - sprintf(c, "0x%08x: ", (int)offset); - c += 12; - } - - size_t index; - size_t word; - - for (word = 0; word < bytesPerLine; ) { - -#ifdef HAVE_LITTLE_ENDIAN - const size_t startIndex = word+(alignment-(alignment?1:0)); - const ssize_t dir = -1; -#else - const size_t startIndex = word; - const ssize_t dir = 1; -#endif - - for (index = 0; index < alignment || (alignment == 0 && index < bytesPerLine); index++) { - - if (!cStyle) { - if (index == 0 && word > 0 && alignment > 0) { - *c++ = ' '; - } - - if (remain-- > 0) { - const unsigned char val = *(pos+startIndex+(index*dir)); - *c++ = makehexdigit(val>>4); - *c++ = makehexdigit(val); - } else if (!oneLine) { - *c++ = ' '; - *c++ = ' '; - } - } else { - if (remain > 0) { - if (index == 0 && word > 0) { - *c++ = ','; - *c++ = ' '; - } - if (index == 0) { - *c++ = '0'; - *c++ = 'x'; - } - const unsigned char val = *(pos+startIndex+(index*dir)); - *c++ = makehexdigit(val>>4); - *c++ = makehexdigit(val); - remain--; - } - } - } - - word += index; - } - - if (!cStyle) { - remain = length; - *c++ = ' '; - *c++ = '\''; - for (index = 0; index < bytesPerLine; index++) { - - if (remain-- > 0) { - const unsigned char val = pos[index]; - *c++ = (val >= ' ' && val < 127) ? val : '.'; - } else if (!oneLine) { - *c++ = ' '; - } - } - - *c++ = '\''; - if (length > bytesPerLine) *c++ = '\n'; - } else { - if (remain > 0) *c++ = ','; - *c++ = '\n'; - } - - if (newLine && indent) func(cookie, stringForIndent(indent)); - *c = 0; - func(cookie, buffer); - newLine = true; - - if (length <= bytesPerLine) break; - length -= bytesPerLine; - } - - if (cStyle) { - if (indent > 0) func(cookie, stringForIndent(indent-1)); - func(cookie, "};"); - } -} - -}; // namespace android - diff --git a/libs/utils/RefBase.cpp b/libs/utils/RefBase.cpp index e538f6868..f398a82ce 100644 --- a/libs/utils/RefBase.cpp +++ b/libs/utils/RefBase.cpp @@ -23,7 +23,6 @@ #include #include #include -#include #include #include @@ -648,19 +647,4 @@ void RefBase::renameRefId(RefBase* ref, ref->mRefs->renameWeakRefId(old_id, new_id); } -// --------------------------------------------------------------------------- - -TextOutput& printStrongPointer(TextOutput& to, const void* val) -{ - to << "sp<>(" << val << ")"; - return to; -} - -TextOutput& printWeakPointer(TextOutput& to, const void* val) -{ - to << "wp<>(" << val << ")"; - return to; -} - - }; // namespace android diff --git a/libs/utils/Static.cpp b/libs/utils/Static.cpp index 624e917ae..3ed07a10c 100644 --- a/libs/utils/Static.cpp +++ b/libs/utils/Static.cpp @@ -17,13 +17,16 @@ // All static variables go here, to control initialization and // destruction order in the library. -#include - -#include -#include - namespace android { +// For String8.cpp +extern void initialize_string8(); +extern void terminate_string8(); + +// For String16.cpp +extern void initialize_string16(); +extern void terminate_string16(); + class LibUtilsFirstStatics { public: @@ -43,49 +46,4 @@ public: static LibUtilsFirstStatics gFirstStatics; int gDarwinCantLoadAllObjects = 1; -// ------------ Text output streams - -Vector gTextBuffers; - -class LogTextOutput : public BufferedTextOutput -{ -public: - LogTextOutput() : BufferedTextOutput(MULTITHREADED) { } - virtual ~LogTextOutput() { }; - -protected: - virtual status_t writeLines(const struct iovec& vec, size_t N) - { - //android_writevLog(&vec, N); <-- this is now a no-op - if (N != 1) ALOGI("WARNING: writeLines N=%zu\n", N); - ALOGI("%.*s", (int)vec.iov_len, (const char*) vec.iov_base); - return NO_ERROR; - } -}; - -class FdTextOutput : public BufferedTextOutput -{ -public: - FdTextOutput(int fd) : BufferedTextOutput(MULTITHREADED), mFD(fd) { } - virtual ~FdTextOutput() { }; - -protected: - virtual status_t writeLines(const struct iovec& vec, size_t N) - { - writev(mFD, &vec, N); - return NO_ERROR; - } - -private: - int mFD; -}; - -static LogTextOutput gLogTextOutput; -static FdTextOutput gStdoutTextOutput(STDOUT_FILENO); -static FdTextOutput gStderrTextOutput(STDERR_FILENO); - -TextOutput& alog(gLogTextOutput); -TextOutput& aout(gStdoutTextOutput); -TextOutput& aerr(gStderrTextOutput); - } // namespace android diff --git a/libs/utils/String16.cpp b/libs/utils/String16.cpp index 94e072fce..c856ceb9b 100644 --- a/libs/utils/String16.cpp +++ b/libs/utils/String16.cpp @@ -20,11 +20,8 @@ #include #include #include -#include #include -#include - #include #include #include @@ -409,10 +406,4 @@ status_t String16::remove(size_t len, size_t begin) return NO_MEMORY; } -TextOutput& operator<<(TextOutput& to, const String16& val) -{ - to << String8(val).string(); - return to; -} - }; // namespace android diff --git a/libs/utils/String8.cpp b/libs/utils/String8.cpp index 75daee9c2..413928abb 100644 --- a/libs/utils/String8.cpp +++ b/libs/utils/String8.cpp @@ -20,11 +20,8 @@ #include #include #include -#include #include -#include - #include /* @@ -46,6 +43,8 @@ static char* gEmptyString = NULL; extern int gDarwinCantLoadAllObjects; int gDarwinIsReallyAnnoying; +void initialize_string8(); + static inline char* getEmptyString() { if (!gEmptyStringBuf) initialize_string8(); @@ -454,12 +453,6 @@ void String8::getUtf32(char32_t* dst) const utf8_to_utf32(mString, length(), dst); } -TextOutput& operator<<(TextOutput& to, const String8& val) -{ - to << val.string(); - return to; -} - // --------------------------------------------------------------------------- // Path functions diff --git a/libs/utils/StringArray.cpp b/libs/utils/StringArray.cpp deleted file mode 100644 index aa42d6837..000000000 --- a/libs/utils/StringArray.cpp +++ /dev/null @@ -1,113 +0,0 @@ -/* - * 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. - */ - -// -// Sortable array of strings. STL-ish, but STL-free. -// - -#include -#include - -#include - -namespace android { - -// -// An expanding array of strings. Add, get, sort, delete. -// -StringArray::StringArray() - : mMax(0), mCurrent(0), mArray(NULL) -{ -} - -StringArray:: ~StringArray() { - for (int i = 0; i < mCurrent; i++) - delete[] mArray[i]; - delete[] mArray; -} - -// -// Add a string. A copy of the string is made. -// -bool StringArray::push_back(const char* str) { - if (mCurrent >= mMax) { - char** tmp; - - if (mMax == 0) - mMax = 16; // initial storage - else - mMax *= 2; - - tmp = new char*[mMax]; - if (tmp == NULL) - return false; - - memcpy(tmp, mArray, mCurrent * sizeof(char*)); - delete[] mArray; - mArray = tmp; - } - - int len = strlen(str); - mArray[mCurrent] = new char[len+1]; - memcpy(mArray[mCurrent], str, len+1); - mCurrent++; - - return true; -} - -// -// Delete an entry. -// -void StringArray::erase(int idx) { - if (idx < 0 || idx >= mCurrent) - return; - delete[] mArray[idx]; - if (idx < mCurrent-1) { - memmove(&mArray[idx], &mArray[idx+1], - (mCurrent-1 - idx) * sizeof(char*)); - } - mCurrent--; -} - -// -// Sort the array. -// -void StringArray::sort(int (*compare)(const void*, const void*)) { - qsort(mArray, mCurrent, sizeof(char*), compare); -} - -// -// Pass this to the sort routine to do an ascending alphabetical sort. -// -int StringArray::cmpAscendingAlpha(const void* pstr1, const void* pstr2) { - return strcmp(*(const char**)pstr1, *(const char**)pstr2); -} - -// -// Set entry N to specified string. -// [should use operator[] here] -// -void StringArray::setEntry(int idx, const char* str) { - if (idx < 0 || idx >= mCurrent) - return; - delete[] mArray[idx]; - int len = strlen(str); - mArray[idx] = new char[len+1]; - memcpy(mArray[idx], str, len+1); -} - - -}; // namespace android diff --git a/libs/utils/SystemClock.cpp b/libs/utils/SystemClock.cpp index ec2d82e01..4b7488941 100644 --- a/libs/utils/SystemClock.cpp +++ b/libs/utils/SystemClock.cpp @@ -36,62 +36,10 @@ #include #define LOG_TAG "SystemClock" -#include "utils/Log.h" +#include namespace android { -/* - * Set the current time. This only works when running as root. - */ -int setCurrentTimeMillis(int64_t millis) -{ -#if WIN32 - // not implemented - return -1; -#else - struct timeval tv; -#ifdef HAVE_ANDROID_OS - struct timespec ts; - int fd; - int res; -#endif - int ret = 0; - - if (millis <= 0 || millis / 1000LL >= INT_MAX) { - return -1; - } - - tv.tv_sec = (time_t) (millis / 1000LL); - tv.tv_usec = (suseconds_t) ((millis % 1000LL) * 1000LL); - - ALOGD("Setting time of day to sec=%d\n", (int) tv.tv_sec); - -#ifdef HAVE_ANDROID_OS - fd = open("/dev/alarm", O_RDWR); - if(fd < 0) { - ALOGW("Unable to open alarm driver: %s\n", strerror(errno)); - return -1; - } - ts.tv_sec = tv.tv_sec; - ts.tv_nsec = tv.tv_usec * 1000; - res = ioctl(fd, ANDROID_ALARM_SET_RTC, &ts); - if(res < 0) { - ALOGW("Unable to set rtc to %ld: %s\n", tv.tv_sec, strerror(errno)); - ret = -1; - } - close(fd); -#else - if (settimeofday(&tv, NULL) != 0) { - ALOGW("Unable to set clock to %d.%d: %s\n", - (int) tv.tv_sec, (int) tv.tv_usec, strerror(errno)); - ret = -1; - } -#endif - - return ret; -#endif // WIN32 -} - /* * native public static long uptimeMillis(); */ diff --git a/libs/utils/TextOutput.cpp b/libs/utils/TextOutput.cpp deleted file mode 100644 index e04823d2b..000000000 --- a/libs/utils/TextOutput.cpp +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright (C) 2005 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 - -#include - -#include -#include -#include - -namespace android { - -// --------------------------------------------------------------------------- - -TextOutput::TextOutput() { -} - -TextOutput::~TextOutput() { -} - -// --------------------------------------------------------------------------- - -TextOutput& operator<<(TextOutput& to, bool val) -{ - if (val) to.print("true", 4); - else to.print("false", 5); - return to; -} - -TextOutput& operator<<(TextOutput& to, int val) -{ - char buf[16]; - sprintf(buf, "%d", val); - to.print(buf, strlen(buf)); - return to; -} - -TextOutput& operator<<(TextOutput& to, long val) -{ - char buf[16]; - sprintf(buf, "%ld", val); - to.print(buf, strlen(buf)); - return to; -} - -TextOutput& operator<<(TextOutput& to, unsigned int val) -{ - char buf[16]; - sprintf(buf, "%u", val); - to.print(buf, strlen(buf)); - return to; -} - -TextOutput& operator<<(TextOutput& to, unsigned long val) -{ - char buf[16]; - sprintf(buf, "%lu", val); - to.print(buf, strlen(buf)); - return to; -} - -TextOutput& operator<<(TextOutput& to, long long val) -{ - char buf[32]; - sprintf(buf, "%Ld", val); - to.print(buf, strlen(buf)); - return to; -} - -TextOutput& operator<<(TextOutput& to, unsigned long long val) -{ - char buf[32]; - sprintf(buf, "%Lu", val); - to.print(buf, strlen(buf)); - return to; -} - -static TextOutput& print_float(TextOutput& to, double value) -{ - char buf[64]; - sprintf(buf, "%g", value); - if( !strchr(buf, '.') && !strchr(buf, 'e') && - !strchr(buf, 'E') ) { - strncat(buf, ".0", sizeof(buf)-1); - } - to.print(buf, strlen(buf)); - return to; -} - -TextOutput& operator<<(TextOutput& to, float val) -{ - return print_float(to,val); -} - -TextOutput& operator<<(TextOutput& to, double val) -{ - return print_float(to,val); -} - -TextOutput& operator<<(TextOutput& to, const void* val) -{ - char buf[16]; - sprintf(buf, "%p", val); - to.print(buf, strlen(buf)); - return to; -} - -static void textOutputPrinter(void* cookie, const char* txt) -{ - ((TextOutput*)cookie)->print(txt, strlen(txt)); -} - -TextOutput& operator<<(TextOutput& to, const TypeCode& val) -{ - printTypeCode(val.typeCode(), textOutputPrinter, (void*)&to); - return to; -} - -HexDump::HexDump(const void *buf, size_t size, size_t bytesPerLine) - : mBuffer(buf) - , mSize(size) - , mBytesPerLine(bytesPerLine) - , mSingleLineCutoff(16) - , mAlignment(4) - , mCArrayStyle(false) -{ - if (bytesPerLine >= 16) mAlignment = 4; - else if (bytesPerLine >= 8) mAlignment = 2; - else mAlignment = 1; -} - -TextOutput& operator<<(TextOutput& to, const HexDump& val) -{ - printHexData(0, val.buffer(), val.size(), val.bytesPerLine(), - val.singleLineCutoff(), val.alignment(), val.carrayStyle(), - textOutputPrinter, (void*)&to); - return to; -} - -}; // namespace android diff --git a/libs/utils/Timers.cpp b/libs/utils/Timers.cpp index d4f85164a..5293cd2a8 100644 --- a/libs/utils/Timers.cpp +++ b/libs/utils/Timers.cpp @@ -70,65 +70,3 @@ int toMillisecondTimeoutDelay(nsecs_t referenceTime, nsecs_t timeoutTime) } return timeoutDelayMillis; } - - -/* - * =========================================================================== - * DurationTimer - * =========================================================================== - */ - -using namespace android; - -// Start the timer. -void DurationTimer::start(void) -{ - gettimeofday(&mStartWhen, NULL); -} - -// Stop the timer. -void DurationTimer::stop(void) -{ - gettimeofday(&mStopWhen, NULL); -} - -// Get the duration in microseconds. -long long DurationTimer::durationUsecs(void) const -{ - return (long) subtractTimevals(&mStopWhen, &mStartWhen); -} - -// Subtract two timevals. Returns the difference (ptv1-ptv2) in -// microseconds. -/*static*/ long long DurationTimer::subtractTimevals(const struct timeval* ptv1, - const struct timeval* ptv2) -{ - long long stop = ((long long) ptv1->tv_sec) * 1000000LL + - ((long long) ptv1->tv_usec); - long long start = ((long long) ptv2->tv_sec) * 1000000LL + - ((long long) ptv2->tv_usec); - return stop - start; -} - -// Add the specified amount of time to the timeval. -/*static*/ void DurationTimer::addToTimeval(struct timeval* ptv, long usec) -{ - if (usec < 0) { - ALOG(LOG_WARN, "", "Negative values not supported in addToTimeval\n"); - return; - } - - // normalize tv_usec if necessary - if (ptv->tv_usec >= 1000000) { - ptv->tv_sec += ptv->tv_usec / 1000000; - ptv->tv_usec %= 1000000; - } - - ptv->tv_usec += usec % 1000000; - if (ptv->tv_usec >= 1000000) { - ptv->tv_usec -= 1000000; - ptv->tv_sec++; - } - ptv->tv_sec += usec / 1000000; -} - diff --git a/libs/utils/WorkQueue.cpp b/libs/utils/WorkQueue.cpp deleted file mode 100644 index 3bb99a1be..000000000 --- a/libs/utils/WorkQueue.cpp +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright (C) 2012 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_NDEBUG 0 -#define LOG_TAG "WorkQueue" - -#include -#include - -namespace android { - -// --- WorkQueue --- - -WorkQueue::WorkQueue(size_t maxThreads, bool canCallJava) : - mMaxThreads(maxThreads), mCanCallJava(canCallJava), - mCanceled(false), mFinished(false), mIdleThreads(0) { -} - -WorkQueue::~WorkQueue() { - if (!cancel()) { - finish(); - } -} - -status_t WorkQueue::schedule(WorkUnit* workUnit, size_t backlog) { - AutoMutex _l(mLock); - - if (mFinished || mCanceled) { - return INVALID_OPERATION; - } - - if (mWorkThreads.size() < mMaxThreads - && mIdleThreads < mWorkUnits.size() + 1) { - sp workThread = new WorkThread(this, mCanCallJava); - status_t status = workThread->run("WorkQueue::WorkThread"); - if (status) { - return status; - } - mWorkThreads.add(workThread); - mIdleThreads += 1; - } else if (backlog) { - while (mWorkUnits.size() >= mMaxThreads * backlog) { - mWorkDequeuedCondition.wait(mLock); - if (mFinished || mCanceled) { - return INVALID_OPERATION; - } - } - } - - mWorkUnits.add(workUnit); - mWorkChangedCondition.broadcast(); - return OK; -} - -status_t WorkQueue::cancel() { - AutoMutex _l(mLock); - - return cancelLocked(); -} - -status_t WorkQueue::cancelLocked() { - if (mFinished) { - return INVALID_OPERATION; - } - - if (!mCanceled) { - mCanceled = true; - - size_t count = mWorkUnits.size(); - for (size_t i = 0; i < count; i++) { - delete mWorkUnits.itemAt(i); - } - mWorkUnits.clear(); - mWorkChangedCondition.broadcast(); - mWorkDequeuedCondition.broadcast(); - } - return OK; -} - -status_t WorkQueue::finish() { - { // acquire lock - AutoMutex _l(mLock); - - if (mFinished) { - return INVALID_OPERATION; - } - - mFinished = true; - mWorkChangedCondition.broadcast(); - } // release lock - - // It is not possible for the list of work threads to change once the mFinished - // flag has been set, so we can access mWorkThreads outside of the lock here. - size_t count = mWorkThreads.size(); - for (size_t i = 0; i < count; i++) { - mWorkThreads.itemAt(i)->join(); - } - mWorkThreads.clear(); - return OK; -} - -bool WorkQueue::threadLoop() { - WorkUnit* workUnit; - { // acquire lock - AutoMutex _l(mLock); - - for (;;) { - if (mCanceled) { - return false; - } - - if (!mWorkUnits.isEmpty()) { - workUnit = mWorkUnits.itemAt(0); - mWorkUnits.removeAt(0); - mIdleThreads -= 1; - mWorkDequeuedCondition.broadcast(); - break; - } - - if (mFinished) { - return false; - } - - mWorkChangedCondition.wait(mLock); - } - } // release lock - - bool shouldContinue = workUnit->run(); - delete workUnit; - - { // acquire lock - AutoMutex _l(mLock); - - mIdleThreads += 1; - - if (!shouldContinue) { - cancelLocked(); - return false; - } - } // release lock - - return true; -} - -// --- WorkQueue::WorkThread --- - -WorkQueue::WorkThread::WorkThread(WorkQueue* workQueue, bool canCallJava) : - Thread(canCallJava), mWorkQueue(workQueue) { -} - -WorkQueue::WorkThread::~WorkThread() { -} - -bool WorkQueue::WorkThread::threadLoop() { - return mWorkQueue->threadLoop(); -} - -}; // namespace android diff --git a/libs/utils/ZipFileCRO.cpp b/libs/utils/ZipFileCRO.cpp deleted file mode 100644 index 55dfd9f87..000000000 --- a/libs/utils/ZipFileCRO.cpp +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include - -using namespace android; - -ZipFileCRO ZipFileXRO_open(const char* path) { - ZipFileRO* zip = new ZipFileRO(); - if (zip->open(path) == NO_ERROR) { - return (ZipFileCRO)zip; - } - return NULL; -} - -void ZipFileCRO_destroy(ZipFileCRO zipToken) { - ZipFileRO* zip = (ZipFileRO*)zipToken; - delete zip; -} - -ZipEntryCRO ZipFileCRO_findEntryByName(ZipFileCRO zipToken, - const char* fileName) { - ZipFileRO* zip = (ZipFileRO*)zipToken; - return (ZipEntryCRO)zip->findEntryByName(fileName); -} - -bool ZipFileCRO_getEntryInfo(ZipFileCRO zipToken, ZipEntryRO entryToken, - int* pMethod, size_t* pUncompLen, - size_t* pCompLen, off64_t* pOffset, long* pModWhen, long* pCrc32) { - ZipFileRO* zip = (ZipFileRO*)zipToken; - ZipEntryRO entry = (ZipEntryRO)entryToken; - return zip->getEntryInfo(entry, pMethod, pUncompLen, pCompLen, pOffset, - pModWhen, pCrc32); -} - -bool ZipFileCRO_uncompressEntry(ZipFileCRO zipToken, ZipEntryRO entryToken, int fd) { - ZipFileRO* zip = (ZipFileRO*)zipToken; - ZipEntryRO entry = (ZipEntryRO)entryToken; - return zip->uncompressEntry(entry, fd); -} diff --git a/libs/utils/ZipFileRO.cpp b/libs/utils/ZipFileRO.cpp deleted file mode 100644 index a1bfedb37..000000000 --- a/libs/utils/ZipFileRO.cpp +++ /dev/null @@ -1,931 +0,0 @@ -/* - * 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. - */ - -// -// Read-only access to Zip archives, with minimal heap allocation. -// -#define LOG_TAG "zipro" -//#define LOG_NDEBUG 0 -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include - -/* - * We must open binary files using open(path, ... | O_BINARY) under Windows. - * Otherwise strange read errors will happen. - */ -#ifndef O_BINARY -# define O_BINARY 0 -#endif - -using namespace android; - -/* - * Zip file constants. - */ -#define kEOCDSignature 0x06054b50 -#define kEOCDLen 22 -#define kEOCDNumEntries 8 // offset to #of entries in file -#define kEOCDSize 12 // size of the central directory -#define kEOCDFileOffset 16 // offset to central directory - -#define kMaxCommentLen 65535 // longest possible in ushort -#define kMaxEOCDSearch (kMaxCommentLen + kEOCDLen) - -#define kLFHSignature 0x04034b50 -#define kLFHLen 30 // excluding variable-len fields -#define kLFHNameLen 26 // offset to filename length -#define kLFHExtraLen 28 // offset to extra length - -#define kCDESignature 0x02014b50 -#define kCDELen 46 // excluding variable-len fields -#define kCDEMethod 10 // offset to compression method -#define kCDEModWhen 12 // offset to modification timestamp -#define kCDECRC 16 // offset to entry CRC -#define kCDECompLen 20 // offset to compressed length -#define kCDEUncompLen 24 // offset to uncompressed length -#define kCDENameLen 28 // offset to filename length -#define kCDEExtraLen 30 // offset to extra length -#define kCDECommentLen 32 // offset to comment length -#define kCDELocalOffset 42 // offset to local hdr - -/* - * The values we return for ZipEntryRO use 0 as an invalid value, so we - * want to adjust the hash table index by a fixed amount. Using a large - * value helps insure that people don't mix & match arguments, e.g. to - * findEntryByIndex(). - */ -#define kZipEntryAdj 10000 - -ZipFileRO::~ZipFileRO() { - free(mHashTable); - if (mDirectoryMap) - mDirectoryMap->release(); - if (mFd >= 0) - TEMP_FAILURE_RETRY(close(mFd)); - if (mFileName) - free(mFileName); -} - -/* - * Convert a ZipEntryRO to a hash table index, verifying that it's in a - * valid range. - */ -int ZipFileRO::entryToIndex(const ZipEntryRO entry) const -{ - long ent = ((intptr_t) entry) - kZipEntryAdj; - if (ent < 0 || ent >= mHashTableSize || mHashTable[ent].name == NULL) { - ALOGW("Invalid ZipEntryRO %p (%ld)\n", entry, ent); - return -1; - } - return ent; -} - - -/* - * Open the specified file read-only. We memory-map the entire thing and - * close the file before returning. - */ -status_t ZipFileRO::open(const char* zipFileName) -{ - int fd = -1; - - assert(mDirectoryMap == NULL); - - /* - * Open and map the specified file. - */ - fd = TEMP_FAILURE_RETRY(::open(zipFileName, O_RDONLY | O_BINARY)); - if (fd < 0) { - ALOGW("Unable to open zip '%s': %s\n", zipFileName, strerror(errno)); - return NAME_NOT_FOUND; - } - - mFileLength = lseek64(fd, 0, SEEK_END); - if (mFileLength < kEOCDLen) { - TEMP_FAILURE_RETRY(close(fd)); - return UNKNOWN_ERROR; - } - - if (mFileName != NULL) { - free(mFileName); - } - mFileName = strdup(zipFileName); - - mFd = fd; - - /* - * Find the Central Directory and store its size and number of entries. - */ - if (!mapCentralDirectory()) { - goto bail; - } - - /* - * Verify Central Directory and create data structures for fast access. - */ - if (!parseZipArchive()) { - goto bail; - } - - return OK; - -bail: - free(mFileName); - mFileName = NULL; - TEMP_FAILURE_RETRY(close(fd)); - return UNKNOWN_ERROR; -} - -/* - * Parse the Zip archive, verifying its contents and initializing internal - * data structures. - */ -bool ZipFileRO::mapCentralDirectory(void) -{ - ssize_t readAmount = kMaxEOCDSearch; - if (readAmount > (ssize_t) mFileLength) - readAmount = mFileLength; - - unsigned char* scanBuf = (unsigned char*) malloc(readAmount); - if (scanBuf == NULL) { - ALOGW("couldn't allocate scanBuf: %s", strerror(errno)); - free(scanBuf); - return false; - } - - /* - * Make sure this is a Zip archive. - */ - if (lseek64(mFd, 0, SEEK_SET) != 0) { - ALOGW("seek to start failed: %s", strerror(errno)); - free(scanBuf); - return false; - } - - ssize_t actual = TEMP_FAILURE_RETRY(read(mFd, scanBuf, sizeof(int32_t))); - if (actual != (ssize_t) sizeof(int32_t)) { - ALOGI("couldn't read first signature from zip archive: %s", strerror(errno)); - free(scanBuf); - return false; - } - - { - unsigned int header = get4LE(scanBuf); - if (header == kEOCDSignature) { - ALOGI("Found Zip archive, but it looks empty\n"); - free(scanBuf); - return false; - } else if (header != kLFHSignature) { - ALOGV("Not a Zip archive (found 0x%08x)\n", header); - free(scanBuf); - return false; - } - } - - /* - * Perform the traditional EOCD snipe hunt. - * - * We're searching for the End of Central Directory magic number, - * which appears at the start of the EOCD block. It's followed by - * 18 bytes of EOCD stuff and up to 64KB of archive comment. We - * need to read the last part of the file into a buffer, dig through - * it to find the magic number, parse some values out, and use those - * to determine the extent of the CD. - * - * We start by pulling in the last part of the file. - */ - off64_t searchStart = mFileLength - readAmount; - - if (lseek64(mFd, searchStart, SEEK_SET) != searchStart) { - ALOGW("seek %ld failed: %s\n", (long) searchStart, strerror(errno)); - free(scanBuf); - return false; - } - actual = TEMP_FAILURE_RETRY(read(mFd, scanBuf, readAmount)); - if (actual != (ssize_t) readAmount) { - ALOGW("Zip: read " ZD ", expected " ZD ". Failed: %s\n", - (ZD_TYPE) actual, (ZD_TYPE) readAmount, strerror(errno)); - free(scanBuf); - return false; - } - - /* - * Scan backward for the EOCD magic. In an archive without a trailing - * comment, we'll find it on the first try. (We may want to consider - * doing an initial minimal read; if we don't find it, retry with a - * second read as above.) - */ - int i; - for (i = readAmount - kEOCDLen; i >= 0; i--) { - if (scanBuf[i] == 0x50 && get4LE(&scanBuf[i]) == kEOCDSignature) { - ALOGV("+++ Found EOCD at buf+%d\n", i); - break; - } - } - if (i < 0) { - ALOGD("Zip: EOCD not found, %s is not zip\n", mFileName); - free(scanBuf); - return false; - } - - off64_t eocdOffset = searchStart + i; - const unsigned char* eocdPtr = scanBuf + i; - - assert(eocdOffset < mFileLength); - - /* - * Grab the CD offset and size, and the number of entries in the - * archive. After that, we can release our EOCD hunt buffer. - */ - unsigned int numEntries = get2LE(eocdPtr + kEOCDNumEntries); - unsigned int dirSize = get4LE(eocdPtr + kEOCDSize); - unsigned int dirOffset = get4LE(eocdPtr + kEOCDFileOffset); - free(scanBuf); - - // Verify that they look reasonable. - if ((long long) dirOffset + (long long) dirSize > (long long) eocdOffset) { - ALOGW("bad offsets (dir %ld, size %u, eocd %ld)\n", - (long) dirOffset, dirSize, (long) eocdOffset); - return false; - } - if (numEntries == 0) { - ALOGW("empty archive?\n"); - return false; - } - - ALOGV("+++ numEntries=%d dirSize=%d dirOffset=%d\n", - numEntries, dirSize, dirOffset); - - mDirectoryMap = new FileMap(); - if (mDirectoryMap == NULL) { - ALOGW("Unable to create directory map: %s", strerror(errno)); - return false; - } - - if (!mDirectoryMap->create(mFileName, mFd, dirOffset, dirSize, true)) { - ALOGW("Unable to map '%s' (" ZD " to " ZD "): %s\n", mFileName, - (ZD_TYPE) dirOffset, (ZD_TYPE) (dirOffset + dirSize), strerror(errno)); - return false; - } - - mNumEntries = numEntries; - mDirectoryOffset = dirOffset; - - return true; -} - - -/* - * Round up to the next highest power of 2. - * - * Found on http://graphics.stanford.edu/~seander/bithacks.html. - */ -static unsigned int roundUpPower2(unsigned int val) -{ - val--; - val |= val >> 1; - val |= val >> 2; - val |= val >> 4; - val |= val >> 8; - val |= val >> 16; - val++; - - return val; -} - -bool ZipFileRO::parseZipArchive(void) -{ - bool result = false; - const unsigned char* cdPtr = (const unsigned char*) mDirectoryMap->getDataPtr(); - size_t cdLength = mDirectoryMap->getDataLength(); - int numEntries = mNumEntries; - - /* - * Create hash table. We have a minimum 75% load factor, possibly as - * low as 50% after we round off to a power of 2. - */ - mHashTableSize = roundUpPower2(1 + (numEntries * 4) / 3); - mHashTable = (HashEntry*) calloc(mHashTableSize, sizeof(HashEntry)); - - /* - * Walk through the central directory, adding entries to the hash - * table. - */ - const unsigned char* ptr = cdPtr; - for (int i = 0; i < numEntries; i++) { - if (get4LE(ptr) != kCDESignature) { - ALOGW("Missed a central dir sig (at %d)\n", i); - goto bail; - } - if (ptr + kCDELen > cdPtr + cdLength) { - ALOGW("Ran off the end (at %d)\n", i); - goto bail; - } - - long localHdrOffset = (long) get4LE(ptr + kCDELocalOffset); - if (localHdrOffset >= mDirectoryOffset) { - ALOGW("bad LFH offset %ld at entry %d\n", localHdrOffset, i); - goto bail; - } - - unsigned int fileNameLen, extraLen, commentLen, hash; - - fileNameLen = get2LE(ptr + kCDENameLen); - extraLen = get2LE(ptr + kCDEExtraLen); - commentLen = get2LE(ptr + kCDECommentLen); - - /* add the CDE filename to the hash table */ - hash = computeHash((const char*)ptr + kCDELen, fileNameLen); - addToHash((const char*)ptr + kCDELen, fileNameLen, hash); - - ptr += kCDELen + fileNameLen + extraLen + commentLen; - if ((size_t)(ptr - cdPtr) > cdLength) { - ALOGW("bad CD advance (%d vs " ZD ") at entry %d\n", - (int) (ptr - cdPtr), (ZD_TYPE) cdLength, i); - goto bail; - } - } - ALOGV("+++ zip good scan %d entries\n", numEntries); - result = true; - -bail: - return result; -} - -/* - * Simple string hash function for non-null-terminated strings. - */ -/*static*/ unsigned int ZipFileRO::computeHash(const char* str, int len) -{ - unsigned int hash = 0; - - while (len--) - hash = hash * 31 + *str++; - - return hash; -} - -/* - * Add a new entry to the hash table. - */ -void ZipFileRO::addToHash(const char* str, int strLen, unsigned int hash) -{ - int ent = hash & (mHashTableSize-1); - - /* - * We over-allocate the table, so we're guaranteed to find an empty slot. - */ - while (mHashTable[ent].name != NULL) - ent = (ent + 1) & (mHashTableSize-1); - - mHashTable[ent].name = str; - mHashTable[ent].nameLen = strLen; -} - -/* - * Find a matching entry. - * - * Returns NULL if not found. - */ -ZipEntryRO ZipFileRO::findEntryByName(const char* fileName) const -{ - /* - * If the ZipFileRO instance is not initialized, the entry number will - * end up being garbage since mHashTableSize is -1. - */ - if (mHashTableSize <= 0) { - return NULL; - } - - int nameLen = strlen(fileName); - unsigned int hash = computeHash(fileName, nameLen); - int ent = hash & (mHashTableSize-1); - - while (mHashTable[ent].name != NULL) { - if (mHashTable[ent].nameLen == nameLen && - memcmp(mHashTable[ent].name, fileName, nameLen) == 0) - { - /* match */ - return (ZipEntryRO)(long)(ent + kZipEntryAdj); - } - - ent = (ent + 1) & (mHashTableSize-1); - } - - return NULL; -} - -/* - * Find the Nth entry. - * - * This currently involves walking through the sparse hash table, counting - * non-empty entries. If we need to speed this up we can either allocate - * a parallel lookup table or (perhaps better) provide an iterator interface. - */ -ZipEntryRO ZipFileRO::findEntryByIndex(int idx) const -{ - if (idx < 0 || idx >= mNumEntries) { - ALOGW("Invalid index %d\n", idx); - return NULL; - } - - for (int ent = 0; ent < mHashTableSize; ent++) { - if (mHashTable[ent].name != NULL) { - if (idx-- == 0) - return (ZipEntryRO) (intptr_t)(ent + kZipEntryAdj); - } - } - - return NULL; -} - -/* - * Get the useful fields from the zip entry. - * - * Returns "false" if the offsets to the fields or the contents of the fields - * appear to be bogus. - */ -bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, size_t* pUncompLen, - size_t* pCompLen, off64_t* pOffset, long* pModWhen, long* pCrc32) const -{ - bool ret = false; - - const int ent = entryToIndex(entry); - if (ent < 0) - return false; - - HashEntry hashEntry = mHashTable[ent]; - - /* - * Recover the start of the central directory entry from the filename - * pointer. The filename is the first entry past the fixed-size data, - * so we can just subtract back from that. - */ - const unsigned char* ptr = (const unsigned char*) hashEntry.name; - off64_t cdOffset = mDirectoryOffset; - - ptr -= kCDELen; - - int method = get2LE(ptr + kCDEMethod); - if (pMethod != NULL) - *pMethod = method; - - if (pModWhen != NULL) - *pModWhen = get4LE(ptr + kCDEModWhen); - if (pCrc32 != NULL) - *pCrc32 = get4LE(ptr + kCDECRC); - - size_t compLen = get4LE(ptr + kCDECompLen); - if (pCompLen != NULL) - *pCompLen = compLen; - size_t uncompLen = get4LE(ptr + kCDEUncompLen); - if (pUncompLen != NULL) - *pUncompLen = uncompLen; - - /* - * If requested, determine the offset of the start of the data. All we - * have is the offset to the Local File Header, which is variable size, - * so we have to read the contents of the struct to figure out where - * the actual data starts. - * - * We also need to make sure that the lengths are not so large that - * somebody trying to map the compressed or uncompressed data runs - * off the end of the mapped region. - * - * Note we don't verify compLen/uncompLen if they don't request the - * dataOffset, because dataOffset is expensive to determine. However, - * if they don't have the file offset, they're not likely to be doing - * anything with the contents. - */ - if (pOffset != NULL) { - long localHdrOffset = get4LE(ptr + kCDELocalOffset); - if (localHdrOffset + kLFHLen >= cdOffset) { - ALOGE("ERROR: bad local hdr offset in zip\n"); - return false; - } - - unsigned char lfhBuf[kLFHLen]; - -#ifdef HAVE_PREAD - /* - * This file descriptor might be from zygote's preloaded assets, - * so we need to do an pread64() instead of a lseek64() + read() to - * guarantee atomicity across the processes with the shared file - * descriptors. - */ - ssize_t actual = - TEMP_FAILURE_RETRY(pread64(mFd, lfhBuf, sizeof(lfhBuf), localHdrOffset)); - - if (actual != sizeof(lfhBuf)) { - ALOGW("failed reading lfh from offset %ld\n", localHdrOffset); - return false; - } - - if (get4LE(lfhBuf) != kLFHSignature) { - ALOGW("didn't find signature at start of lfh; wanted: offset=%ld data=0x%08x; " - "got: data=0x%08lx\n", - localHdrOffset, kLFHSignature, get4LE(lfhBuf)); - return false; - } -#else /* HAVE_PREAD */ - /* - * For hosts don't have pread64() we cannot guarantee atomic reads from - * an offset in a file. Android should never run on those platforms. - * File descriptors inherited from a fork() share file offsets and - * there would be nothing to protect from two different processes - * calling lseek64() concurrently. - */ - - { - AutoMutex _l(mFdLock); - - if (lseek64(mFd, localHdrOffset, SEEK_SET) != localHdrOffset) { - ALOGW("failed seeking to lfh at offset %ld\n", localHdrOffset); - return false; - } - - ssize_t actual = - TEMP_FAILURE_RETRY(read(mFd, lfhBuf, sizeof(lfhBuf))); - if (actual != sizeof(lfhBuf)) { - ALOGW("failed reading lfh from offset %ld\n", localHdrOffset); - return false; - } - - if (get4LE(lfhBuf) != kLFHSignature) { - off64_t actualOffset = lseek64(mFd, 0, SEEK_CUR); - ALOGW("didn't find signature at start of lfh; wanted: offset=%ld data=0x%08x; " - "got: offset=" ZD " data=0x%08lx\n", - localHdrOffset, kLFHSignature, (ZD_TYPE) actualOffset, get4LE(lfhBuf)); - return false; - } - } -#endif /* HAVE_PREAD */ - - off64_t dataOffset = localHdrOffset + kLFHLen - + get2LE(lfhBuf + kLFHNameLen) + get2LE(lfhBuf + kLFHExtraLen); - if (dataOffset >= cdOffset) { - ALOGW("bad data offset %ld in zip\n", (long) dataOffset); - return false; - } - - /* check lengths */ - if ((off64_t)(dataOffset + compLen) > cdOffset) { - ALOGW("bad compressed length in zip (%ld + " ZD " > %ld)\n", - (long) dataOffset, (ZD_TYPE) compLen, (long) cdOffset); - return false; - } - - if (method == kCompressStored && - (off64_t)(dataOffset + uncompLen) > cdOffset) - { - ALOGE("ERROR: bad uncompressed length in zip (%ld + " ZD " > %ld)\n", - (long) dataOffset, (ZD_TYPE) uncompLen, (long) cdOffset); - return false; - } - - *pOffset = dataOffset; - } - - return true; -} - -/* - * Copy the entry's filename to the buffer. - */ -int ZipFileRO::getEntryFileName(ZipEntryRO entry, char* buffer, int bufLen) - const -{ - int ent = entryToIndex(entry); - if (ent < 0) - return -1; - - int nameLen = mHashTable[ent].nameLen; - if (bufLen < nameLen+1) - return nameLen+1; - - memcpy(buffer, mHashTable[ent].name, nameLen); - buffer[nameLen] = '\0'; - return 0; -} - -/* - * Create a new FileMap object that spans the data in "entry". - */ -FileMap* ZipFileRO::createEntryFileMap(ZipEntryRO entry) const -{ - /* - * TODO: the efficient way to do this is to modify FileMap to allow - * sub-regions of a file to be mapped. A reference-counting scheme - * can manage the base memory mapping. For now, we just create a brand - * new mapping off of the Zip archive file descriptor. - */ - - FileMap* newMap; - size_t compLen; - off64_t offset; - - if (!getEntryInfo(entry, NULL, NULL, &compLen, &offset, NULL, NULL)) - return NULL; - - newMap = new FileMap(); - if (!newMap->create(mFileName, mFd, offset, compLen, true)) { - newMap->release(); - return NULL; - } - - return newMap; -} - -/* - * Uncompress an entry, in its entirety, into the provided output buffer. - * - * This doesn't verify the data's CRC, which might be useful for - * uncompressed data. The caller should be able to manage it. - */ -bool ZipFileRO::uncompressEntry(ZipEntryRO entry, void* buffer) const -{ - const size_t kSequentialMin = 32768; - bool result = false; - int ent = entryToIndex(entry); - if (ent < 0) - return -1; - - int method; - size_t uncompLen, compLen; - off64_t offset; - const unsigned char* ptr; - - getEntryInfo(entry, &method, &uncompLen, &compLen, &offset, NULL, NULL); - - FileMap* file = createEntryFileMap(entry); - if (file == NULL) { - goto bail; - } - - ptr = (const unsigned char*) file->getDataPtr(); - - /* - * Experiment with madvise hint. When we want to uncompress a file, - * we pull some stuff out of the central dir entry and then hit a - * bunch of compressed or uncompressed data sequentially. The CDE - * visit will cause a limited amount of read-ahead because it's at - * the end of the file. We could end up doing lots of extra disk - * access if the file we're prying open is small. Bottom line is we - * probably don't want to turn MADV_SEQUENTIAL on and leave it on. - * - * So, if the compressed size of the file is above a certain minimum - * size, temporarily boost the read-ahead in the hope that the extra - * pair of system calls are negated by a reduction in page faults. - */ - if (compLen > kSequentialMin) - file->advise(FileMap::SEQUENTIAL); - - if (method == kCompressStored) { - memcpy(buffer, ptr, uncompLen); - } else { - if (!inflateBuffer(buffer, ptr, uncompLen, compLen)) - goto unmap; - } - - if (compLen > kSequentialMin) - file->advise(FileMap::NORMAL); - - result = true; - -unmap: - file->release(); -bail: - return result; -} - -/* - * Uncompress an entry, in its entirety, to an open file descriptor. - * - * This doesn't verify the data's CRC, but probably should. - */ -bool ZipFileRO::uncompressEntry(ZipEntryRO entry, int fd) const -{ - bool result = false; - int ent = entryToIndex(entry); - if (ent < 0) - return -1; - - int method; - size_t uncompLen, compLen; - off64_t offset; - const unsigned char* ptr; - - getEntryInfo(entry, &method, &uncompLen, &compLen, &offset, NULL, NULL); - - FileMap* file = createEntryFileMap(entry); - if (file == NULL) { - goto bail; - } - - ptr = (const unsigned char*) file->getDataPtr(); - - if (method == kCompressStored) { - ssize_t actual = TEMP_FAILURE_RETRY(write(fd, ptr, uncompLen)); - if (actual < 0) { - ALOGE("Write failed: %s\n", strerror(errno)); - goto unmap; - } else if ((size_t) actual != uncompLen) { - ALOGE("Partial write during uncompress (" ZD " of " ZD ")\n", - (ZD_TYPE) actual, (ZD_TYPE) uncompLen); - goto unmap; - } else { - ALOGI("+++ successful write\n"); - } - } else { - if (!inflateBuffer(fd, ptr, uncompLen, compLen)) - goto unmap; - } - - result = true; - -unmap: - file->release(); -bail: - return result; -} - -/* - * Uncompress "deflate" data from one buffer to another. - */ -/*static*/ bool ZipFileRO::inflateBuffer(void* outBuf, const void* inBuf, - size_t uncompLen, size_t compLen) -{ - bool result = false; - z_stream zstream; - int zerr; - - /* - * Initialize the zlib stream struct. - */ - memset(&zstream, 0, sizeof(zstream)); - zstream.zalloc = Z_NULL; - zstream.zfree = Z_NULL; - zstream.opaque = Z_NULL; - zstream.next_in = (Bytef*)inBuf; - zstream.avail_in = compLen; - zstream.next_out = (Bytef*) outBuf; - zstream.avail_out = uncompLen; - zstream.data_type = Z_UNKNOWN; - - /* - * Use the undocumented "negative window bits" feature to tell zlib - * that there's no zlib header waiting for it. - */ - zerr = inflateInit2(&zstream, -MAX_WBITS); - if (zerr != Z_OK) { - if (zerr == Z_VERSION_ERROR) { - ALOGE("Installed zlib is not compatible with linked version (%s)\n", - ZLIB_VERSION); - } else { - ALOGE("Call to inflateInit2 failed (zerr=%d)\n", zerr); - } - goto bail; - } - - /* - * Expand data. - */ - zerr = inflate(&zstream, Z_FINISH); - if (zerr != Z_STREAM_END) { - ALOGW("Zip inflate failed, zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)\n", - zerr, zstream.next_in, zstream.avail_in, - zstream.next_out, zstream.avail_out); - goto z_bail; - } - - /* paranoia */ - if (zstream.total_out != uncompLen) { - ALOGW("Size mismatch on inflated file (%ld vs " ZD ")\n", - zstream.total_out, (ZD_TYPE) uncompLen); - goto z_bail; - } - - result = true; - -z_bail: - inflateEnd(&zstream); /* free up any allocated structures */ - -bail: - return result; -} - -/* - * Uncompress "deflate" data from one buffer to an open file descriptor. - */ -/*static*/ bool ZipFileRO::inflateBuffer(int fd, const void* inBuf, - size_t uncompLen, size_t compLen) -{ - bool result = false; - const size_t kWriteBufSize = 32768; - unsigned char writeBuf[kWriteBufSize]; - z_stream zstream; - int zerr; - - /* - * Initialize the zlib stream struct. - */ - memset(&zstream, 0, sizeof(zstream)); - zstream.zalloc = Z_NULL; - zstream.zfree = Z_NULL; - zstream.opaque = Z_NULL; - zstream.next_in = (Bytef*)inBuf; - zstream.avail_in = compLen; - zstream.next_out = (Bytef*) writeBuf; - zstream.avail_out = sizeof(writeBuf); - zstream.data_type = Z_UNKNOWN; - - /* - * Use the undocumented "negative window bits" feature to tell zlib - * that there's no zlib header waiting for it. - */ - zerr = inflateInit2(&zstream, -MAX_WBITS); - if (zerr != Z_OK) { - if (zerr == Z_VERSION_ERROR) { - ALOGE("Installed zlib is not compatible with linked version (%s)\n", - ZLIB_VERSION); - } else { - ALOGE("Call to inflateInit2 failed (zerr=%d)\n", zerr); - } - goto bail; - } - - /* - * Loop while we have more to do. - */ - do { - /* - * Expand data. - */ - zerr = inflate(&zstream, Z_NO_FLUSH); - if (zerr != Z_OK && zerr != Z_STREAM_END) { - ALOGW("zlib inflate: zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)\n", - zerr, zstream.next_in, zstream.avail_in, - zstream.next_out, zstream.avail_out); - goto z_bail; - } - - /* write when we're full or when we're done */ - if (zstream.avail_out == 0 || - (zerr == Z_STREAM_END && zstream.avail_out != sizeof(writeBuf))) - { - long writeSize = zstream.next_out - writeBuf; - int cc = TEMP_FAILURE_RETRY(write(fd, writeBuf, writeSize)); - if (cc < 0) { - ALOGW("write failed in inflate: %s", strerror(errno)); - goto z_bail; - } else if (cc != (int) writeSize) { - ALOGW("write failed in inflate (%d vs %ld)", cc, writeSize); - goto z_bail; - } - - zstream.next_out = writeBuf; - zstream.avail_out = sizeof(writeBuf); - } - } while (zerr == Z_OK); - - assert(zerr == Z_STREAM_END); /* other errors should've been caught */ - - /* paranoia */ - if (zstream.total_out != uncompLen) { - ALOGW("Size mismatch on inflated file (%ld vs " ZD ")\n", - zstream.total_out, (ZD_TYPE) uncompLen); - goto z_bail; - } - - result = true; - -z_bail: - inflateEnd(&zstream); /* free up any allocated structures */ - -bail: - return result; -} diff --git a/libs/utils/ZipUtils.cpp b/libs/utils/ZipUtils.cpp deleted file mode 100644 index a43bbb0ca..000000000 --- a/libs/utils/ZipUtils.cpp +++ /dev/null @@ -1,345 +0,0 @@ -/* - * 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. - */ - -// -// Misc zip/gzip utility functions. -// - -#define LOG_TAG "ziputil" - -#include -#include -#include -#include - -#include -#include -#include - -#include - -using namespace android; - -/* - * Utility function that expands zip/gzip "deflate" compressed data - * into a buffer. - * - * "fd" is an open file positioned at the start of the "deflate" data - * "buf" must hold at least "uncompressedLen" bytes. - */ -/*static*/ bool ZipUtils::inflateToBuffer(int fd, void* buf, - long uncompressedLen, long compressedLen) -{ - bool result = false; - const unsigned long kReadBufSize = 32768; - unsigned char* readBuf = NULL; - z_stream zstream; - int zerr; - unsigned long compRemaining; - - assert(uncompressedLen >= 0); - assert(compressedLen >= 0); - - readBuf = new unsigned char[kReadBufSize]; - if (readBuf == NULL) - goto bail; - compRemaining = compressedLen; - - /* - * Initialize the zlib stream. - */ - memset(&zstream, 0, sizeof(zstream)); - zstream.zalloc = Z_NULL; - zstream.zfree = Z_NULL; - zstream.opaque = Z_NULL; - zstream.next_in = NULL; - zstream.avail_in = 0; - zstream.next_out = (Bytef*) buf; - zstream.avail_out = uncompressedLen; - zstream.data_type = Z_UNKNOWN; - - /* - * Use the undocumented "negative window bits" feature to tell zlib - * that there's no zlib header waiting for it. - */ - zerr = inflateInit2(&zstream, -MAX_WBITS); - if (zerr != Z_OK) { - if (zerr == Z_VERSION_ERROR) { - ALOGE("Installed zlib is not compatible with linked version (%s)\n", - ZLIB_VERSION); - } else { - ALOGE("Call to inflateInit2 failed (zerr=%d)\n", zerr); - } - goto bail; - } - - /* - * Loop while we have data. - */ - do { - unsigned long getSize; - - /* read as much as we can */ - if (zstream.avail_in == 0) { - getSize = (compRemaining > kReadBufSize) ? - kReadBufSize : compRemaining; - ALOGV("+++ reading %ld bytes (%ld left)\n", - getSize, compRemaining); - - int cc = TEMP_FAILURE_RETRY(read(fd, readBuf, getSize)); - if (cc < 0) { - ALOGW("inflate read failed: %s", strerror(errno)); - } else if (cc != (int) getSize) { - ALOGW("inflate read failed (%d vs %ld)", cc, getSize); - goto z_bail; - } - - compRemaining -= getSize; - - zstream.next_in = readBuf; - zstream.avail_in = getSize; - } - - /* uncompress the data */ - zerr = inflate(&zstream, Z_NO_FLUSH); - if (zerr != Z_OK && zerr != Z_STREAM_END) { - ALOGD("zlib inflate call failed (zerr=%d)\n", zerr); - goto z_bail; - } - - /* output buffer holds all, so no need to write the output */ - } while (zerr == Z_OK); - - assert(zerr == Z_STREAM_END); /* other errors should've been caught */ - - if ((long) zstream.total_out != uncompressedLen) { - ALOGW("Size mismatch on inflated file (%ld vs %ld)\n", - zstream.total_out, uncompressedLen); - goto z_bail; - } - - // success! - result = true; - -z_bail: - inflateEnd(&zstream); /* free up any allocated structures */ - -bail: - delete[] readBuf; - return result; -} - -/* - * Utility function that expands zip/gzip "deflate" compressed data - * into a buffer. - * - * (This is a clone of the previous function, but it takes a FILE* instead - * of an fd. We could pass fileno(fd) to the above, but we can run into - * trouble when "fp" has a different notion of what fd's file position is.) - * - * "fp" is an open file positioned at the start of the "deflate" data - * "buf" must hold at least "uncompressedLen" bytes. - */ -/*static*/ bool ZipUtils::inflateToBuffer(FILE* fp, void* buf, - long uncompressedLen, long compressedLen) -{ - bool result = false; - const unsigned long kReadBufSize = 32768; - unsigned char* readBuf = NULL; - z_stream zstream; - int zerr; - unsigned long compRemaining; - - assert(uncompressedLen >= 0); - assert(compressedLen >= 0); - - readBuf = new unsigned char[kReadBufSize]; - if (readBuf == NULL) - goto bail; - compRemaining = compressedLen; - - /* - * Initialize the zlib stream. - */ - memset(&zstream, 0, sizeof(zstream)); - zstream.zalloc = Z_NULL; - zstream.zfree = Z_NULL; - zstream.opaque = Z_NULL; - zstream.next_in = NULL; - zstream.avail_in = 0; - zstream.next_out = (Bytef*) buf; - zstream.avail_out = uncompressedLen; - zstream.data_type = Z_UNKNOWN; - - /* - * Use the undocumented "negative window bits" feature to tell zlib - * that there's no zlib header waiting for it. - */ - zerr = inflateInit2(&zstream, -MAX_WBITS); - if (zerr != Z_OK) { - if (zerr == Z_VERSION_ERROR) { - ALOGE("Installed zlib is not compatible with linked version (%s)\n", - ZLIB_VERSION); - } else { - ALOGE("Call to inflateInit2 failed (zerr=%d)\n", zerr); - } - goto bail; - } - - /* - * Loop while we have data. - */ - do { - unsigned long getSize; - - /* read as much as we can */ - if (zstream.avail_in == 0) { - getSize = (compRemaining > kReadBufSize) ? - kReadBufSize : compRemaining; - ALOGV("+++ reading %ld bytes (%ld left)\n", - getSize, compRemaining); - - int cc = fread(readBuf, 1, getSize, fp); - if (cc != (int) getSize) { - ALOGD("inflate read failed (%d vs %ld)\n", - cc, getSize); - goto z_bail; - } - - compRemaining -= getSize; - - zstream.next_in = readBuf; - zstream.avail_in = getSize; - } - - /* uncompress the data */ - zerr = inflate(&zstream, Z_NO_FLUSH); - if (zerr != Z_OK && zerr != Z_STREAM_END) { - ALOGD("zlib inflate call failed (zerr=%d)\n", zerr); - goto z_bail; - } - - /* output buffer holds all, so no need to write the output */ - } while (zerr == Z_OK); - - assert(zerr == Z_STREAM_END); /* other errors should've been caught */ - - if ((long) zstream.total_out != uncompressedLen) { - ALOGW("Size mismatch on inflated file (%ld vs %ld)\n", - zstream.total_out, uncompressedLen); - goto z_bail; - } - - // success! - result = true; - -z_bail: - inflateEnd(&zstream); /* free up any allocated structures */ - -bail: - delete[] readBuf; - return result; -} - -/* - * Look at the contents of a gzip archive. We want to know where the - * data starts, and how long it will be after it is uncompressed. - * - * We expect to find the CRC and length as the last 8 bytes on the file. - * This is a pretty reasonable thing to expect for locally-compressed - * files, but there's a small chance that some extra padding got thrown - * on (the man page talks about compressed data written to tape). We - * don't currently deal with that here. If "gzip -l" whines, we're going - * to fail too. - * - * On exit, "fp" is pointing at the start of the compressed data. - */ -/*static*/ bool ZipUtils::examineGzip(FILE* fp, int* pCompressionMethod, - long* pUncompressedLen, long* pCompressedLen, unsigned long* pCRC32) -{ - enum { // flags - FTEXT = 0x01, - FHCRC = 0x02, - FEXTRA = 0x04, - FNAME = 0x08, - FCOMMENT = 0x10, - }; - int ic; - int method, flags; - int i; - - ic = getc(fp); - if (ic != 0x1f || getc(fp) != 0x8b) - return false; // not gzip - method = getc(fp); - flags = getc(fp); - - /* quick sanity checks */ - if (method == EOF || flags == EOF) - return false; - if (method != ZipFileRO::kCompressDeflated) - return false; - - /* skip over 4 bytes of mod time, 1 byte XFL, 1 byte OS */ - for (i = 0; i < 6; i++) - (void) getc(fp); - /* consume "extra" field, if present */ - if ((flags & FEXTRA) != 0) { - int len; - - len = getc(fp); - len |= getc(fp) << 8; - while (len-- && getc(fp) != EOF) - ; - } - /* consume filename, if present */ - if ((flags & FNAME) != 0) { - do { - ic = getc(fp); - } while (ic != 0 && ic != EOF); - } - /* consume comment, if present */ - if ((flags & FCOMMENT) != 0) { - do { - ic = getc(fp); - } while (ic != 0 && ic != EOF); - } - /* consume 16-bit header CRC, if present */ - if ((flags & FHCRC) != 0) { - (void) getc(fp); - (void) getc(fp); - } - - if (feof(fp) || ferror(fp)) - return false; - - /* seek to the end; CRC and length are in the last 8 bytes */ - long curPosn = ftell(fp); - unsigned char buf[8]; - fseek(fp, -8, SEEK_END); - *pCompressedLen = ftell(fp) - curPosn; - - if (fread(buf, 1, 8, fp) != 8) - return false; - /* seek back to start of compressed data */ - fseek(fp, curPosn, SEEK_SET); - - *pCompressionMethod = method; - *pCRC32 = ZipFileRO::get4LE(&buf[0]); - *pUncompressedLen = ZipFileRO::get4LE(&buf[4]); - - return true; -} diff --git a/libs/utils/misc.cpp b/libs/utils/misc.cpp index 445a23a01..58eb49901 100644 --- a/libs/utils/misc.cpp +++ b/libs/utils/misc.cpp @@ -25,7 +25,6 @@ #include #include #include -#include #include #if defined(HAVE_PTHREADS) @@ -38,56 +37,6 @@ using namespace android; namespace android { -/* - * Get a file's type. - */ -FileType getFileType(const char* fileName) -{ - struct stat sb; - - if (stat(fileName, &sb) < 0) { - if (errno == ENOENT || errno == ENOTDIR) - return kFileTypeNonexistent; - else { - fprintf(stderr, "getFileType got errno=%d on '%s'\n", - errno, fileName); - return kFileTypeUnknown; - } - } else { - if (S_ISREG(sb.st_mode)) - return kFileTypeRegular; - else if (S_ISDIR(sb.st_mode)) - return kFileTypeDirectory; - else if (S_ISCHR(sb.st_mode)) - return kFileTypeCharDev; - else if (S_ISBLK(sb.st_mode)) - return kFileTypeBlockDev; - else if (S_ISFIFO(sb.st_mode)) - return kFileTypeFifo; -#ifdef HAVE_SYMLINKS - else if (S_ISLNK(sb.st_mode)) - return kFileTypeSymlink; - else if (S_ISSOCK(sb.st_mode)) - return kFileTypeSocket; -#endif - else - return kFileTypeUnknown; - } -} - -/* - * Get a file's modification date. - */ -time_t getFileModDate(const char* fileName) -{ - struct stat sb; - - if (stat(fileName, &sb) < 0) - return (time_t) -1; - - return sb.st_mtime; -} - struct sysprop_change_callback_info { sysprop_change_callback callback; int priority; diff --git a/libs/utils/tests/Android.mk b/libs/utils/tests/Android.mk index a2ca9c877..a6d9cbe7b 100644 --- a/libs/utils/tests/Android.mk +++ b/libs/utils/tests/Android.mk @@ -10,8 +10,7 @@ test_src_files := \ LruCache_test.cpp \ String8_test.cpp \ Unicode_test.cpp \ - Vector_test.cpp \ - ZipFileRO_test.cpp + Vector_test.cpp shared_libraries := \ libz \ diff --git a/libs/utils/tests/ZipFileRO_test.cpp b/libs/utils/tests/ZipFileRO_test.cpp deleted file mode 100644 index 7a1d0bd95..000000000 --- a/libs/utils/tests/ZipFileRO_test.cpp +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) 2011 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 "ZipFileRO_test" -#include -#include - -#include - -#include -#include - -namespace android { - -class ZipFileROTest : public testing::Test { -protected: - virtual void SetUp() { - } - - virtual void TearDown() { - } -}; - -TEST_F(ZipFileROTest, ZipTimeConvertSuccess) { - struct tm t; - - // 2011-06-29 14:40:40 - long when = 0x3EDD7514; - - ZipFileRO::zipTimeToTimespec(when, &t); - - EXPECT_EQ(2011, t.tm_year + 1900) - << "Year was improperly converted."; - - EXPECT_EQ(6, t.tm_mon) - << "Month was improperly converted."; - - EXPECT_EQ(29, t.tm_mday) - << "Day was improperly converted."; - - EXPECT_EQ(14, t.tm_hour) - << "Hour was improperly converted."; - - EXPECT_EQ(40, t.tm_min) - << "Minute was improperly converted."; - - EXPECT_EQ(40, t.tm_sec) - << "Second was improperly converted."; -} - -} From 4485d0d966d062d1b45b635e2447a2d2f96c3f38 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Wed, 8 May 2013 16:04:13 -0700 Subject: [PATCH 479/541] new String8, String16 ctors to initialize empty static strings with static linkage when libutils is statically linked, the ordering of the static initializer is not guaranteed and therefore it's unsafe to use empty static strings: e.g.: static String8 sThisStaticStringIsNotSafe; instead, this new constructor can be used: static String8 sThisStaticStringIsSafe(kEmptyString); Change-Id: Ia3daf1cab1c97d021c0ee9c2b394b5e27e8d6c0d --- include/utils/String16.h | 9 +++++++++ include/utils/String8.h | 9 +++++++++ libs/utils/String16.cpp | 13 +++++++++++++ libs/utils/String8.cpp | 17 +++++++++++++---- 4 files changed, 44 insertions(+), 4 deletions(-) diff --git a/include/utils/String16.h b/include/utils/String16.h index 6d4253d24..d131bfc6a 100644 --- a/include/utils/String16.h +++ b/include/utils/String16.h @@ -41,7 +41,16 @@ class TextOutput; class String16 { public: + /* use String16(StaticLinkage) if you're statically linking against + * libutils and declaring an empty static String16, e.g.: + * + * static String16 sAStaticEmptyString(String16::kEmptyString); + * static String16 sAnotherStaticEmptyString(sAStaticEmptyString); + */ + enum StaticLinkage { kEmptyString }; + String16(); + explicit String16(StaticLinkage); String16(const String16& o); String16(const String16& o, size_t len, diff --git a/include/utils/String8.h b/include/utils/String8.h index 9426fcfc8..ef5947047 100644 --- a/include/utils/String8.h +++ b/include/utils/String8.h @@ -37,7 +37,16 @@ class TextOutput; class String8 { public: + /* use String8(StaticLinkage) if you're statically linking against + * libutils and declaring an empty static String8, e.g.: + * + * static String8 sAStaticEmptyString(String8::kEmptyString); + * static String8 sAnotherStaticEmptyString(sAStaticEmptyString); + */ + enum StaticLinkage { kEmptyString }; + String8(); + explicit String8(StaticLinkage); String8(const String8& o); explicit String8(const char* o); explicit String8(const char* o, size_t numChars); diff --git a/libs/utils/String16.cpp b/libs/utils/String16.cpp index c856ceb9b..b09b728ad 100644 --- a/libs/utils/String16.cpp +++ b/libs/utils/String16.cpp @@ -93,6 +93,19 @@ String16::String16() { } +String16::String16(StaticLinkage) + : mString(0) +{ + // this constructor is used when we can't rely on the static-initializers + // having run. In this case we always allocate an empty string. It's less + // efficient than using getEmptyString(), but we assume it's uncommon. + + char16_t* data = static_cast( + SharedBuffer::alloc(sizeof(char16_t))->data()); + data[0] = 0; + mString = data; +} + String16::String16(const String16& o) : mString(o.mString) { diff --git a/libs/utils/String8.cpp b/libs/utils/String8.cpp index 413928abb..e852d77b7 100644 --- a/libs/utils/String8.cpp +++ b/libs/utils/String8.cpp @@ -47,16 +47,12 @@ void initialize_string8(); static inline char* getEmptyString() { - if (!gEmptyStringBuf) initialize_string8(); - gEmptyStringBuf->acquire(); return gEmptyString; } void initialize_string8() { - if (gEmptyStringBuf) return; - // HACK: This dummy dependency forces linking libutils Static.cpp, // which is needed to initialize String8/String16 classes. // These variables are named for Darwin, but are needed elsewhere too, @@ -146,6 +142,19 @@ String8::String8() { } +String8::String8(StaticLinkage) + : mString(0) +{ + // this constructor is used when we can't rely on the static-initializers + // having run. In this case we always allocate an empty string. It's less + // efficient than using getEmptyString(), but we assume it's uncommon. + + char* data = static_cast( + SharedBuffer::alloc(sizeof(char))->data()); + data[0] = 0; + mString = data; +} + String8::String8(const String8& o) : mString(o.mString) { From d3d3fceb7c00f09fd8a02bf81145017e8af37d2e Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Thu, 9 May 2013 13:03:05 -0700 Subject: [PATCH 480/541] remove reserved virtual slots these consume small amount of space (plt) in every library that links against libutils. Change-Id: I1b6b3dd9098aa5a051243f7a5dbf91cd7dcb8b2c --- include/utils/VectorImpl.h | 20 -------------------- libs/utils/VectorImpl.cpp | 19 ------------------- 2 files changed, 39 deletions(-) diff --git a/include/utils/VectorImpl.h b/include/utils/VectorImpl.h index 9bc50e6c2..21ad71ce6 100644 --- a/include/utils/VectorImpl.h +++ b/include/utils/VectorImpl.h @@ -105,16 +105,6 @@ protected: virtual void do_splat(void* dest, const void* item, size_t num) const = 0; virtual void do_move_forward(void* dest, const void* from, size_t num) const = 0; virtual void do_move_backward(void* dest, const void* from, size_t num) const = 0; - - // take care of FBC... - virtual void reservedVectorImpl1(); - virtual void reservedVectorImpl2(); - virtual void reservedVectorImpl3(); - virtual void reservedVectorImpl4(); - virtual void reservedVectorImpl5(); - virtual void reservedVectorImpl6(); - virtual void reservedVectorImpl7(); - virtual void reservedVectorImpl8(); private: void* _grow(size_t where, size_t amount); @@ -166,16 +156,6 @@ public: protected: virtual int do_compare(const void* lhs, const void* rhs) const = 0; - // take care of FBC... - virtual void reservedSortedVectorImpl1(); - virtual void reservedSortedVectorImpl2(); - virtual void reservedSortedVectorImpl3(); - virtual void reservedSortedVectorImpl4(); - virtual void reservedSortedVectorImpl5(); - virtual void reservedSortedVectorImpl6(); - virtual void reservedSortedVectorImpl7(); - virtual void reservedSortedVectorImpl8(); - private: ssize_t _indexOrderOf(const void* item, size_t* order = 0) const; diff --git a/libs/utils/VectorImpl.cpp b/libs/utils/VectorImpl.cpp index 70f49deee..5a79647cb 100644 --- a/libs/utils/VectorImpl.cpp +++ b/libs/utils/VectorImpl.cpp @@ -504,15 +504,6 @@ void VectorImpl::_do_move_backward(void* dest, const void* from, size_t num) con do_move_backward(dest, from, num); } -void VectorImpl::reservedVectorImpl1() { } -void VectorImpl::reservedVectorImpl2() { } -void VectorImpl::reservedVectorImpl3() { } -void VectorImpl::reservedVectorImpl4() { } -void VectorImpl::reservedVectorImpl5() { } -void VectorImpl::reservedVectorImpl6() { } -void VectorImpl::reservedVectorImpl7() { } -void VectorImpl::reservedVectorImpl8() { } - /*****************************************************************************/ SortedVectorImpl::SortedVectorImpl(size_t itemSize, uint32_t flags) @@ -628,16 +619,6 @@ ssize_t SortedVectorImpl::remove(const void* item) return i; } -void SortedVectorImpl::reservedSortedVectorImpl1() { }; -void SortedVectorImpl::reservedSortedVectorImpl2() { }; -void SortedVectorImpl::reservedSortedVectorImpl3() { }; -void SortedVectorImpl::reservedSortedVectorImpl4() { }; -void SortedVectorImpl::reservedSortedVectorImpl5() { }; -void SortedVectorImpl::reservedSortedVectorImpl6() { }; -void SortedVectorImpl::reservedSortedVectorImpl7() { }; -void SortedVectorImpl::reservedSortedVectorImpl8() { }; - - /*****************************************************************************/ }; // namespace android From 8db925f7777834eae5af386f7f92a22478c88346 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Thu, 9 May 2013 13:04:32 -0700 Subject: [PATCH 481/541] remove unused declarations and reformat Change-Id: I4e168fb62c275e02621c4b6e0c6000d0f006c327 --- include/utils/StrongPointer.h | 95 +++++++++++++++++------------------ 1 file changed, 46 insertions(+), 49 deletions(-) diff --git a/include/utils/StrongPointer.h b/include/utils/StrongPointer.h index 40aebfd3c..aba9577da 100644 --- a/include/utils/StrongPointer.h +++ b/include/utils/StrongPointer.h @@ -26,9 +26,6 @@ // --------------------------------------------------------------------------- namespace android { -class TextOutput; -TextOutput& printStrongPointer(TextOutput& to, const void* val); - template class wp; // --------------------------------------------------------------------------- @@ -58,9 +55,8 @@ inline bool operator _op_ (const wp& o) const { \ // --------------------------------------------------------------------------- -template -class sp -{ +template +class sp { public: inline sp() : m_ptr(0) { } @@ -110,92 +106,93 @@ private: #undef COMPARE -template -TextOutput& operator<<(TextOutput& to, const sp& val); - // --------------------------------------------------------------------------- // No user serviceable parts below here. template sp::sp(T* other) -: m_ptr(other) - { - if (other) other->incStrong(this); - } + : m_ptr(other) { + if (other) + other->incStrong(this); +} template sp::sp(const sp& other) -: m_ptr(other.m_ptr) - { - if (m_ptr) m_ptr->incStrong(this); - } + : m_ptr(other.m_ptr) { + if (m_ptr) + m_ptr->incStrong(this); +} template template -sp::sp(U* other) : m_ptr(other) -{ - if (other) ((T*)other)->incStrong(this); +sp::sp(U* other) + : m_ptr(other) { + if (other) + ((T*) other)->incStrong(this); } template template sp::sp(const sp& other) -: m_ptr(other.m_ptr) - { - if (m_ptr) m_ptr->incStrong(this); - } - -template -sp::~sp() -{ - if (m_ptr) m_ptr->decStrong(this); + : m_ptr(other.m_ptr) { + if (m_ptr) + m_ptr->incStrong(this); } template -sp& sp::operator = (const sp& other) { +sp::~sp() { + if (m_ptr) + m_ptr->decStrong(this); +} + +template +sp& sp::operator =(const sp& other) { T* otherPtr(other.m_ptr); - if (otherPtr) otherPtr->incStrong(this); - if (m_ptr) m_ptr->decStrong(this); + if (otherPtr) + otherPtr->incStrong(this); + if (m_ptr) + m_ptr->decStrong(this); m_ptr = otherPtr; return *this; } template -sp& sp::operator = (T* other) -{ - if (other) other->incStrong(this); - if (m_ptr) m_ptr->decStrong(this); +sp& sp::operator =(T* other) { + if (other) + other->incStrong(this); + if (m_ptr) + m_ptr->decStrong(this); m_ptr = other; return *this; } template template -sp& sp::operator = (const sp& other) -{ +sp& sp::operator =(const sp& other) { T* otherPtr(other.m_ptr); - if (otherPtr) otherPtr->incStrong(this); - if (m_ptr) m_ptr->decStrong(this); + if (otherPtr) + otherPtr->incStrong(this); + if (m_ptr) + m_ptr->decStrong(this); m_ptr = otherPtr; return *this; } template template -sp& sp::operator = (U* other) -{ - if (other) ((T*)other)->incStrong(this); - if (m_ptr) m_ptr->decStrong(this); +sp& sp::operator =(U* other) { + if (other) + ((T*) other)->incStrong(this); + if (m_ptr) + m_ptr->decStrong(this); m_ptr = other; return *this; } -template -void sp::force_set(T* other) -{ +template +void sp::force_set(T* other) { other->forceIncStrong(this); m_ptr = other; } template -void sp::clear() -{ +void sp::clear() { if (m_ptr) { m_ptr->decStrong(this); m_ptr = 0; From e45a9acd6390d68e902b1f0a25f90b96b8849a3c Mon Sep 17 00:00:00 2001 From: Nick Kralevich Date: Fri, 17 May 2013 11:06:39 -0700 Subject: [PATCH 482/541] Remove unnecessary header file. In b2cac4ff37ed2a3521efb307b807b12f649634b8, the last reference to property_get was removed, but the include file remained. Remove it. This is causing me unrelated problems. Change-Id: I87bbfbfa5871f6da4a1b00037a225f67bbd75881 --- libs/utils/Threads.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp index e0f12dc02..ff74914cb 100644 --- a/libs/utils/Threads.cpp +++ b/libs/utils/Threads.cpp @@ -21,7 +21,6 @@ #include #include -#include #include #include From 214c701ec6de8bd4b57f6aec8874fb343c79d874 Mon Sep 17 00:00:00 2001 From: Tim Murray Date: Thu, 23 May 2013 13:44:08 -0700 Subject: [PATCH 483/541] Make trace macros slightly more robust Change-Id: I9544eb7b27fc1a971cabadd8d5b4b4b80678febf --- include/utils/Trace.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/include/utils/Trace.h b/include/utils/Trace.h index 49578c47d..6ee343d79 100644 --- a/include/utils/Trace.h +++ b/include/utils/Trace.h @@ -17,6 +17,8 @@ #ifndef ANDROID_TRACE_H #define ANDROID_TRACE_H +#ifdef HAVE_ANDROID_OS + #include #include #include @@ -57,4 +59,11 @@ private: }; // namespace android +#else // HAVE_ANDROID_OS + +#define ATRACE_NAME(...) +#define ATRACE_CALL() + +#endif // HAVE_ANDROID_OS + #endif // ANDROID_TRACE_H From d614ee455705047fd27db0ad7f3013e6ea64204d Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Tue, 21 May 2013 14:11:34 -0700 Subject: [PATCH 484/541] Added bitwise-or and bitwise-and to BitSet Change-Id: I9bbf41f9d2d4a2593b0e6d7d8be7e283f985bade --- include/utils/BitSet.h | 14 +++++ libs/utils/tests/Android.mk | 1 + libs/utils/tests/BitSet_test.cpp | 87 ++++++++++++++++++++++++++++++++ 3 files changed, 102 insertions(+) create mode 100644 libs/utils/tests/BitSet_test.cpp diff --git a/include/utils/BitSet.h b/include/utils/BitSet.h index e189d0c73..19c03d120 100644 --- a/include/utils/BitSet.h +++ b/include/utils/BitSet.h @@ -101,6 +101,20 @@ struct BitSet32 { inline bool operator== (const BitSet32& other) const { return value == other.value; } inline bool operator!= (const BitSet32& other) const { return value != other.value; } + inline BitSet32 operator& (const BitSet32& other) const { + return BitSet32(value & other.value); + } + inline BitSet32& operator&= (const BitSet32& other) { + value &= other.value; + return *this; + } + inline BitSet32 operator| (const BitSet32& other) const { + return BitSet32(value | other.value); + } + inline BitSet32& operator|= (const BitSet32& other) { + value |= other.value; + return *this; + } }; ANDROID_BASIC_TYPES_TRAITS(BitSet32) diff --git a/libs/utils/tests/Android.mk b/libs/utils/tests/Android.mk index a6d9cbe7b..caedaffc0 100644 --- a/libs/utils/tests/Android.mk +++ b/libs/utils/tests/Android.mk @@ -6,6 +6,7 @@ include $(CLEAR_VARS) test_src_files := \ BasicHashtable_test.cpp \ BlobCache_test.cpp \ + BitSet_test.cpp \ Looper_test.cpp \ LruCache_test.cpp \ String8_test.cpp \ diff --git a/libs/utils/tests/BitSet_test.cpp b/libs/utils/tests/BitSet_test.cpp new file mode 100644 index 000000000..752e56db4 --- /dev/null +++ b/libs/utils/tests/BitSet_test.cpp @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2013 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 "BitSet_test" + +#include +#include +#include +#include + +namespace android { + +class BitSetTest : public testing::Test { +protected: + BitSet32 b1; + BitSet32 b2; + virtual void TearDown() { + b1.clear(); + b2.clear(); + } +}; + + +TEST_F(BitSetTest, BitWiseOr) { + b1.markBit(2); + b2.markBit(4); + + BitSet32 tmp = b1 | b2; + EXPECT_EQ(tmp.count(), 2u); + EXPECT_TRUE(tmp.hasBit(2) && tmp.hasBit(4)); + // Check that the operator is symmetric + EXPECT_TRUE((b2 | b1) == (b1 | b2)); + + b1 |= b2; + EXPECT_EQ(b1.count(), 2u); + EXPECT_TRUE(b1.hasBit(2) && b1.hasBit(4)); + EXPECT_TRUE(b2.hasBit(4) && b2.count() == 1u); +} +TEST_F(BitSetTest, BitWiseAnd_Disjoint) { + b1.markBit(2); + b1.markBit(4); + b1.markBit(6); + + BitSet32 tmp = b1 & b2; + EXPECT_TRUE(tmp.isEmpty()); + // Check that the operator is symmetric + EXPECT_TRUE((b2 & b1) == (b1 & b2)); + + b2 &= b1; + EXPECT_TRUE(b2.isEmpty()); + EXPECT_EQ(b1.count(), 3u); + EXPECT_TRUE(b1.hasBit(2) && b1.hasBit(4) && b1.hasBit(6)); +} + +TEST_F(BitSetTest, BitWiseAnd_NonDisjoint) { + b1.markBit(2); + b1.markBit(4); + b1.markBit(6); + b2.markBit(3); + b2.markBit(6); + b2.markBit(9); + + BitSet32 tmp = b1 & b2; + EXPECT_EQ(tmp.count(), 1u); + EXPECT_TRUE(tmp.hasBit(6)); + // Check that the operator is symmetric + EXPECT_TRUE((b2 & b1) == (b1 & b2)); + + b1 &= b2; + EXPECT_EQ(b1.count(), 1u); + EXPECT_EQ(b2.count(), 3u); + EXPECT_TRUE(b2.hasBit(3) && b2.hasBit(6) && b2.hasBit(9)); +} +} // namespace android From 567ea471ffdee67150da4d0aaebb4fcbe84b6a0c Mon Sep 17 00:00:00 2001 From: Alex Ray Date: Tue, 23 Jul 2013 17:06:55 -0700 Subject: [PATCH 485/541] utils: move thread enums to system/core Change-Id: Ia97963cc9acf8661d01355786f5e7e7bf54581c2 --- include/utils/ThreadDefs.h | 48 +------------------------------------- 1 file changed, 1 insertion(+), 47 deletions(-) diff --git a/include/utils/ThreadDefs.h b/include/utils/ThreadDefs.h index a8f8eb3d9..9711c1379 100644 --- a/include/utils/ThreadDefs.h +++ b/include/utils/ThreadDefs.h @@ -20,6 +20,7 @@ #include #include #include +#include // --------------------------------------------------------------------------- // C API @@ -32,53 +33,6 @@ typedef void* android_thread_id_t; typedef int (*android_thread_func_t)(void*); -enum { - /* - * *********************************************** - * ** Keep in sync with android.os.Process.java ** - * *********************************************** - * - * This maps directly to the "nice" priorities we use in Android. - * A thread priority should be chosen inverse-proportionally to - * the amount of work the thread is expected to do. The more work - * a thread will do, the less favorable priority it should get so that - * it doesn't starve the system. Threads not behaving properly might - * be "punished" by the kernel. - * Use the levels below when appropriate. Intermediate values are - * acceptable, preferably use the {MORE|LESS}_FAVORABLE constants below. - */ - ANDROID_PRIORITY_LOWEST = 19, - - /* use for background tasks */ - ANDROID_PRIORITY_BACKGROUND = 10, - - /* most threads run at normal priority */ - ANDROID_PRIORITY_NORMAL = 0, - - /* threads currently running a UI that the user is interacting with */ - ANDROID_PRIORITY_FOREGROUND = -2, - - /* the main UI thread has a slightly more favorable priority */ - ANDROID_PRIORITY_DISPLAY = -4, - - /* ui service treads might want to run at a urgent display (uncommon) */ - ANDROID_PRIORITY_URGENT_DISPLAY = HAL_PRIORITY_URGENT_DISPLAY, - - /* all normal audio threads */ - ANDROID_PRIORITY_AUDIO = -16, - - /* service audio threads (uncommon) */ - ANDROID_PRIORITY_URGENT_AUDIO = -19, - - /* should never be used in practice. regular process might not - * be allowed to use this level */ - ANDROID_PRIORITY_HIGHEST = -20, - - ANDROID_PRIORITY_DEFAULT = ANDROID_PRIORITY_NORMAL, - ANDROID_PRIORITY_MORE_FAVORABLE = -1, - ANDROID_PRIORITY_LESS_FAVORABLE = +1, -}; - #ifdef __cplusplus } // extern "C" #endif From 946003ae6472ab4661c10d11c2b6ded42fe701ca Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Tue, 23 Jul 2013 17:39:35 -0700 Subject: [PATCH 486/541] add static dependency on liblog Change-Id: Ibf8733142d52e4ffac2e3b26932d7196fb79c5a7 --- libs/utils/Android.mk | 2 ++ libs/utils/CleanSpec.mk | 51 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 libs/utils/CleanSpec.mk diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk index 42e1c7e68..abf4b2e33 100644 --- a/libs/utils/Android.mk +++ b/libs/utils/Android.mk @@ -66,6 +66,7 @@ ifeq ($(HOST_OS), linux) LOCAL_SRC_FILES += Looper.cpp endif LOCAL_MODULE:= libutils +LOCAL_STATIC_LIBRARIES := liblog LOCAL_CFLAGS += $(host_commonCflags) LOCAL_LDLIBS += $(host_commonLdlibs) include $(BUILD_HOST_STATIC_LIBRARY) @@ -79,6 +80,7 @@ ifeq ($(HOST_OS), linux) LOCAL_SRC_FILES += Looper.cpp endif LOCAL_MODULE:= lib64utils +LOCAL_STATIC_LIBRARIES := liblog LOCAL_CFLAGS += $(host_commonCflags) -m64 LOCAL_LDLIBS += $(host_commonLdlibs) include $(BUILD_HOST_STATIC_LIBRARY) diff --git a/libs/utils/CleanSpec.mk b/libs/utils/CleanSpec.mk new file mode 100644 index 000000000..c3c5651b2 --- /dev/null +++ b/libs/utils/CleanSpec.mk @@ -0,0 +1,51 @@ +# Copyright (C) 2012 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. +# + +# If you don't need to do a full clean build but would like to touch +# a file or delete some intermediate files, add a clean step to the end +# of the list. These steps will only be run once, if they haven't been +# run before. +# +# E.g.: +# $(call add-clean-step, touch -c external/sqlite/sqlite3.h) +# $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates) +# +# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with +# files that are missing or have been moved. +# +# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory. +# Use $(OUT_DIR) to refer to the "out" directory. +# +# If you need to re-do something that's already mentioned, just copy +# the command and add it to the bottom of the list. E.g., if a change +# that you made last week required touching a file and a change you +# made today requires touching the same file, just copy the old +# touch step and add it to the end of the list. +# +# ************************************************ +# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST +# ************************************************ + +# For example: +#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates) +#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates) +#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f) +#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*) + +# ************************************************ +# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST +# ************************************************ +$(call add-clean-step, rm -rf $(HOST_OUT)/obj/STATIC_LIBRARIES/libutils_intermediates/import_includes) +$(call add-clean-step, rm -rf $(HOST_OUT)/obj/STATIC_LIBRARIES/lib64utils_intermediates/import_includes) From 0d8f3d6c452883ab7295573c4ff7437d68d1d936 Mon Sep 17 00:00:00 2001 From: Alex Ray Date: Wed, 17 Jul 2013 16:57:21 -0700 Subject: [PATCH 487/541] libutils: give BasicHashtableImpl a virtual destructor Change-Id: I4ec590b060d732af5fe525670becbe778684247b --- include/utils/BasicHashtable.h | 1 + libs/utils/BasicHashtable.cpp | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/include/utils/BasicHashtable.h b/include/utils/BasicHashtable.h index 7a6c96cef..c235d6252 100644 --- a/include/utils/BasicHashtable.h +++ b/include/utils/BasicHashtable.h @@ -52,6 +52,7 @@ protected: BasicHashtableImpl(size_t entrySize, bool hasTrivialDestructor, size_t minimumInitialCapacity, float loadFactor); BasicHashtableImpl(const BasicHashtableImpl& other); + virtual ~BasicHashtableImpl(); void dispose(); diff --git a/libs/utils/BasicHashtable.cpp b/libs/utils/BasicHashtable.cpp index fd51b7b2e..491d9e98b 100644 --- a/libs/utils/BasicHashtable.cpp +++ b/libs/utils/BasicHashtable.cpp @@ -42,6 +42,10 @@ BasicHashtableImpl::BasicHashtableImpl(const BasicHashtableImpl& other) : } } +BasicHashtableImpl::~BasicHashtableImpl() +{ +} + void BasicHashtableImpl::dispose() { if (mBuckets) { releaseBuckets(mBuckets, mBucketCount); From f88a5b4f887bddd478e9fb35d609b684c54d5204 Mon Sep 17 00:00:00 2001 From: Eino-Ville Talvala Date: Tue, 30 Jul 2013 14:07:08 -0700 Subject: [PATCH 488/541] Window: Add query for consumer usage bits. Bug: 9592202 Change-Id: Ied2fdb985dc59dd9c0f26c515353abdc37f0eb77 --- include/system/window.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/include/system/window.h b/include/system/window.h index b8a19c80f..7ce59e02f 100644 --- a/include/system/window.h +++ b/include/system/window.h @@ -230,7 +230,13 @@ enum { * Boolean that indicates whether the consumer is running more than * one buffer behind the producer. */ - NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND = 9 + NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND = 9, + + /* + * The consumer gralloc usage bits currently set by the consumer. + * The values are defined in hardware/libhardware/include/gralloc.h. + */ + NATIVE_WINDOW_CONSUMER_USAGE_BITS = 10 }; /* Valid operations for the (*perform)() hook. From 6d611a891d0c818bf3a34a7cad036f3f0064bc4a Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Mon, 29 Jul 2013 21:24:40 -0700 Subject: [PATCH 489/541] Make Flattenable not virtual (libutils) Making an object Flattenable doesn't force it to become virtual anymore. For instance, Fence and GraphicBuffer are now non-virtual classes. Also change Flatennable protocol a bit so that it updates its parameters (pointers, sizes) to make it easier to implement a flattenable in terms of other flattenables. Change-Id: Ie81dc7637180b3c2cfcbaf644f8987ca804eb891 --- include/utils/BlobCache.h | 20 ++----- include/utils/Flattenable.h | 111 ++++++++++++++++++++++++++++-------- libs/utils/Android.mk | 1 - libs/utils/BlobCache.cpp | 20 +------ libs/utils/Flattenable.cpp | 24 -------- 5 files changed, 94 insertions(+), 82 deletions(-) delete mode 100644 libs/utils/Flattenable.cpp diff --git a/include/utils/BlobCache.h b/include/utils/BlobCache.h index 4f342a2ad..7d621e438 100644 --- a/include/utils/BlobCache.h +++ b/include/utils/BlobCache.h @@ -33,7 +33,8 @@ namespace android { // and then reloaded in a subsequent execution of the program. This // serialization is non-portable and the data should only be used by the device // that generated it. -class BlobCache : public RefBase, public Flattenable { +class BlobCache : public RefBase { + public: // Create an empty blob cache. The blob cache will cache key/value pairs @@ -78,14 +79,10 @@ public: // 0 <= valueSize size_t get(const void* key, size_t keySize, void* value, size_t valueSize); + // getFlattenedSize returns the number of bytes needed to store the entire // serialized cache. - virtual size_t getFlattenedSize() const; - - // getFdCount returns the number of file descriptors that will result from - // flattening the cache. This will always return 0 so as to allow the - // flattened cache to be saved to disk and then later restored. - virtual size_t getFdCount() const; + size_t getFlattenedSize() const; // flatten serializes the current contents of the cache into the memory // pointed to by 'buffer'. The serialized cache contents can later be @@ -94,9 +91,7 @@ public: // // Preconditions: // size >= this.getFlattenedSize() - // count == 0 - virtual status_t flatten(void* buffer, size_t size, int fds[], - size_t count) const; + status_t flatten(void* buffer, size_t size) const; // unflatten replaces the contents of the cache with the serialized cache // contents in the memory pointed to by 'buffer'. The previous contents of @@ -104,10 +99,7 @@ public: // unflattening the serialized cache contents then the BlobCache will be // left in an empty state. // - // Preconditions: - // count == 0 - virtual status_t unflatten(void const* buffer, size_t size, int fds[], - size_t count); + status_t unflatten(void const* buffer, size_t size); private: // Copying is disallowed. diff --git a/include/utils/Flattenable.h b/include/utils/Flattenable.h index e40d289de..c283ad766 100644 --- a/include/utils/Flattenable.h +++ b/include/utils/Flattenable.h @@ -21,30 +21,75 @@ #include #include #include +#include namespace android { + +class FlattenableUtils { +public: + template + static size_t align(size_t size) { + COMPILE_TIME_ASSERT_FUNCTION_SCOPE( !(N & (N-1)) ); + return (size + (N-1)) & ~(N-1); + } + + template + static size_t align(void*& buffer) { + COMPILE_TIME_ASSERT_FUNCTION_SCOPE( !(N & (N-1)) ); + intptr_t b = intptr_t(buffer); + buffer = (void*)((intptr_t(buffer) + (N-1)) & ~(N-1)); + return size_t(intptr_t(buffer) - b); + } + + static void advance(void*& buffer, size_t& size, size_t offset) { + buffer = reinterpret_cast( intptr_t(buffer) + offset ); + size -= offset; + } + + static void advance(void const*& buffer, size_t& size, size_t offset) { + buffer = reinterpret_cast( intptr_t(buffer) + offset ); + size -= offset; + } + + // write a POD structure + template + static void write(void*& buffer, size_t& size, const T& value) { + *static_cast(buffer) = value; + advance(buffer, size, sizeof(T)); + } + + // read a POD structure + template + static void read(void const*& buffer, size_t& size, T& value) { + value = *static_cast(buffer); + advance(buffer, size, sizeof(T)); + } +}; + + /* - * The Flattenable interface allows an object to serialize itself out + * The Flattenable protocol allows an object to serialize itself out * to a byte-buffer and an array of file descriptors. + * Flattenable objects must implement this protocol. */ -class Flattenable -{ +template +class Flattenable { public: // size in bytes of the flattened object - virtual size_t getFlattenedSize() const = 0; + inline size_t getFlattenedSize() const; // number of file descriptors to flatten - virtual size_t getFdCount() const = 0; + inline size_t getFdCount() const; // flattens the object into buffer. // size should be at least of getFlattenedSize() // file descriptors are written in the fds[] array but ownership is // not transfered (ie: they must be dupped by the caller of // flatten() if needed). - virtual status_t flatten(void* buffer, size_t size, - int fds[], size_t count) const = 0; + inline status_t flatten(void*& buffer, size_t& size, + int*& fds, size_t& count) const; // unflattens the object from buffer. // size should be equal to the value of getFlattenedSize() when the @@ -53,21 +98,34 @@ public: // don't need to be dupped(). ie: the caller of unflatten doesn't // keep ownership. If a fd is not retained by unflatten() it must be // explicitly closed. - virtual status_t unflatten(void const* buffer, size_t size, - int fds[], size_t count) = 0; - -protected: - virtual ~Flattenable() = 0; - + inline status_t unflatten(void const*& buffer, size_t& size, + int const*& fds, size_t& count); }; +template +inline size_t Flattenable::getFlattenedSize() const { + return static_cast(this)->T::getFlattenedSize(); +} +template +inline size_t Flattenable::getFdCount() const { + return static_cast(this)->T::getFdCount(); +} +template +inline status_t Flattenable::flatten( + void*& buffer, size_t& size, int*& fds, size_t& count) const { + return static_cast(this)->T::flatten(buffer, size, fds, count); +} +template +inline status_t Flattenable::unflatten( + void const*& buffer, size_t& size, int const*& fds, size_t& count) { + return static_cast(this)->T::unflatten(buffer, size, fds, count); +} + /* * LightFlattenable is a protocol allowing object to serialize themselves out - * to a byte-buffer. - * + * to a byte-buffer. Because it doesn't handle file-descriptors, + * LightFlattenable is usually more size efficient than Flattenable. * LightFlattenable objects must implement this protocol. - * - * LightFlattenable doesn't require the object to be virtual. */ template class LightFlattenable { @@ -77,10 +135,10 @@ public: inline bool isFixedSize() const; // returns size in bytes of the flattened object. must be a constant. - inline size_t getSize() const; + inline size_t getFlattenedSize() const; // flattens the object into buffer. - inline status_t flatten(void* buffer) const; + inline status_t flatten(void* buffer, size_t size) const; // unflattens the object from buffer of given size. inline status_t unflatten(void const* buffer, size_t size); @@ -91,12 +149,12 @@ inline bool LightFlattenable::isFixedSize() const { return static_cast(this)->T::isFixedSize(); } template -inline size_t LightFlattenable::getSize() const { - return static_cast(this)->T::getSize(); +inline size_t LightFlattenable::getFlattenedSize() const { + return static_cast(this)->T::getFlattenedSize(); } template -inline status_t LightFlattenable::flatten(void* buffer) const { - return static_cast(this)->T::flatten(buffer); +inline status_t LightFlattenable::flatten(void* buffer, size_t size) const { + return static_cast(this)->T::flatten(buffer, size); } template inline status_t LightFlattenable::unflatten(void const* buffer, size_t size) { @@ -106,6 +164,8 @@ inline status_t LightFlattenable::unflatten(void const* buffer, size_t size) /* * LightFlattenablePod is an implementation of the LightFlattenable protocol * for POD (plain-old-data) objects. + * Simply derive from LightFlattenablePod to make Foo flattenable; no + * need to implement any methods; obviously Foo must be a POD structure. */ template class LightFlattenablePod : public LightFlattenable { @@ -114,10 +174,11 @@ public: return true; } - inline size_t getSize() const { + inline size_t getFlattenedSize() const { return sizeof(T); } - inline status_t flatten(void* buffer) const { + inline status_t flatten(void* buffer, size_t size) const { + if (size < sizeof(T)) return NO_MEMORY; *reinterpret_cast(buffer) = *static_cast(this); return NO_ERROR; } diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk index abf4b2e33..7e6b1be42 100644 --- a/libs/utils/Android.mk +++ b/libs/utils/Android.mk @@ -22,7 +22,6 @@ commonSources:= \ BlobCache.cpp \ CallStack.cpp \ FileMap.cpp \ - Flattenable.cpp \ JenkinsHash.cpp \ LinearAllocator.cpp \ LinearTransform.cpp \ diff --git a/libs/utils/BlobCache.cpp b/libs/utils/BlobCache.cpp index be398ee4e..0fb1d8e99 100644 --- a/libs/utils/BlobCache.cpp +++ b/libs/utils/BlobCache.cpp @@ -176,17 +176,7 @@ size_t BlobCache::getFlattenedSize() const { return size; } -size_t BlobCache::getFdCount() const { - return 0; -} - -status_t BlobCache::flatten(void* buffer, size_t size, int fds[], size_t count) - const { - if (count != 0) { - ALOGE("flatten: nonzero fd count: %zu", count); - return BAD_VALUE; - } - +status_t BlobCache::flatten(void* buffer, size_t size) const { // Write the cache header if (size < sizeof(Header)) { ALOGE("flatten: not enough room for cache header"); @@ -228,16 +218,10 @@ status_t BlobCache::flatten(void* buffer, size_t size, int fds[], size_t count) return OK; } -status_t BlobCache::unflatten(void const* buffer, size_t size, int fds[], - size_t count) { +status_t BlobCache::unflatten(void const* buffer, size_t size) { // All errors should result in the BlobCache being in an empty state. mCacheEntries.clear(); - if (count != 0) { - ALOGE("unflatten: nonzero fd count: %zu", count); - return BAD_VALUE; - } - // Read the cache header if (size < sizeof(Header)) { ALOGE("unflatten: not enough room for cache header"); diff --git a/libs/utils/Flattenable.cpp b/libs/utils/Flattenable.cpp deleted file mode 100644 index 1f2ffaa28..000000000 --- a/libs/utils/Flattenable.cpp +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (C) 2006 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 - -namespace android { - -Flattenable::~Flattenable() { -} - -}; // namespace android From 5189f9a263c6732329c79f38c6ca9019cfaa6476 Mon Sep 17 00:00:00 2001 From: Brian Carlstrom Date: Wed, 31 Jul 2013 01:08:36 -0700 Subject: [PATCH 490/541] Fix BlobCache_test Change-Id: I5b20aeb4dcff80f1e18e1165b8461d20034dc1ac --- libs/utils/tests/BlobCache_test.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/libs/utils/tests/BlobCache_test.cpp b/libs/utils/tests/BlobCache_test.cpp index b64cc3956..7202123eb 100644 --- a/libs/utils/tests/BlobCache_test.cpp +++ b/libs/utils/tests/BlobCache_test.cpp @@ -273,8 +273,8 @@ protected: void roundTrip() { size_t size = mBC->getFlattenedSize(); uint8_t* flat = new uint8_t[size]; - ASSERT_EQ(OK, mBC->flatten(flat, size, NULL, 0)); - ASSERT_EQ(OK, mBC2->unflatten(flat, size, NULL, 0)); + ASSERT_EQ(OK, mBC->flatten(flat, size)); + ASSERT_EQ(OK, mBC2->unflatten(flat, size)); delete[] flat; } @@ -321,7 +321,7 @@ TEST_F(BlobCacheFlattenTest, FlattenDoesntChangeCache) { size_t size = mBC->getFlattenedSize(); uint8_t* flat = new uint8_t[size]; - ASSERT_EQ(OK, mBC->flatten(flat, size, NULL, 0)); + ASSERT_EQ(OK, mBC->flatten(flat, size)); delete[] flat; // Verify the cache that we just serialized @@ -343,7 +343,7 @@ TEST_F(BlobCacheFlattenTest, FlattenCatchesBufferTooSmall) { size_t size = mBC->getFlattenedSize() - 1; uint8_t* flat = new uint8_t[size]; - ASSERT_EQ(BAD_VALUE, mBC->flatten(flat, size, NULL, 0)); + ASSERT_EQ(BAD_VALUE, mBC->flatten(flat, size)); delete[] flat; } @@ -353,11 +353,11 @@ TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadMagic) { size_t size = mBC->getFlattenedSize(); uint8_t* flat = new uint8_t[size]; - ASSERT_EQ(OK, mBC->flatten(flat, size, NULL, 0)); + ASSERT_EQ(OK, mBC->flatten(flat, size)); flat[1] = ~flat[1]; // Bad magic should cause an error. - ASSERT_EQ(BAD_VALUE, mBC2->unflatten(flat, size, NULL, 0)); + ASSERT_EQ(BAD_VALUE, mBC2->unflatten(flat, size)); delete[] flat; // The error should cause the unflatten to result in an empty cache @@ -370,12 +370,12 @@ TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadBlobCacheVersion) { size_t size = mBC->getFlattenedSize(); uint8_t* flat = new uint8_t[size]; - ASSERT_EQ(OK, mBC->flatten(flat, size, NULL, 0)); + ASSERT_EQ(OK, mBC->flatten(flat, size)); flat[5] = ~flat[5]; // Version mismatches shouldn't cause errors, but should not use the // serialized entries - ASSERT_EQ(OK, mBC2->unflatten(flat, size, NULL, 0)); + ASSERT_EQ(OK, mBC2->unflatten(flat, size)); delete[] flat; // The version mismatch should cause the unflatten to result in an empty @@ -389,12 +389,12 @@ TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadBlobCacheDeviceVersion) { size_t size = mBC->getFlattenedSize(); uint8_t* flat = new uint8_t[size]; - ASSERT_EQ(OK, mBC->flatten(flat, size, NULL, 0)); + ASSERT_EQ(OK, mBC->flatten(flat, size)); flat[10] = ~flat[10]; // Version mismatches shouldn't cause errors, but should not use the // serialized entries - ASSERT_EQ(OK, mBC2->unflatten(flat, size, NULL, 0)); + ASSERT_EQ(OK, mBC2->unflatten(flat, size)); delete[] flat; // The version mismatch should cause the unflatten to result in an empty @@ -408,10 +408,10 @@ TEST_F(BlobCacheFlattenTest, UnflattenCatchesBufferTooSmall) { size_t size = mBC->getFlattenedSize(); uint8_t* flat = new uint8_t[size]; - ASSERT_EQ(OK, mBC->flatten(flat, size, NULL, 0)); + ASSERT_EQ(OK, mBC->flatten(flat, size)); // A buffer truncation shouldt cause an error - ASSERT_EQ(BAD_VALUE, mBC2->unflatten(flat, size-1, NULL, 0)); + ASSERT_EQ(BAD_VALUE, mBC2->unflatten(flat, size-1)); delete[] flat; // The error should cause the unflatten to result in an empty cache From 0f10d0abf3e6f6b5631c091256f8b4e7a20a33d0 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Wed, 31 Jul 2013 16:04:39 -0700 Subject: [PATCH 491/541] Add a new utf8-to-utf16 conversion function. Change-Id: I957c22fb219596ca4239db7a169473d3894b09eb --- include/utils/Unicode.h | 7 +++++++ libs/utils/Unicode.cpp | 30 ++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/include/utils/Unicode.h b/include/utils/Unicode.h index 927353377..c8c87c326 100644 --- a/include/utils/Unicode.h +++ b/include/utils/Unicode.h @@ -163,6 +163,13 @@ char16_t* utf8_to_utf16_no_null_terminator(const uint8_t* src, size_t srcLen, ch */ void utf8_to_utf16(const uint8_t* src, size_t srcLen, char16_t* dst); +/** + * Like utf8_to_utf16_no_null_terminator, but you can supply a maximum length of the + * decoded string. The decoded string will fill up to that length; if it is longer + * the returned pointer will be to the character after dstLen. + */ +char16_t* utf8_to_utf16_n(const uint8_t* src, size_t srcLen, char16_t* dst, size_t dstLen); + } #endif diff --git a/libs/utils/Unicode.cpp b/libs/utils/Unicode.cpp index 41cbf035e..a66e3bbbb 100644 --- a/libs/utils/Unicode.cpp +++ b/libs/utils/Unicode.cpp @@ -573,4 +573,34 @@ void utf8_to_utf16(const uint8_t* u8str, size_t u8len, char16_t* u16str) { *end = 0; } +char16_t* utf8_to_utf16_n(const uint8_t* src, size_t srcLen, char16_t* dst, size_t dstLen) { + const uint8_t* const u8end = src + srcLen; + const uint8_t* u8cur = src; + const uint16_t* const u16end = dst + dstLen; + char16_t* u16cur = dst; + + while (u8cur < u8end && u16cur < u16end) { + size_t u8len = utf8_codepoint_len(*u8cur); + uint32_t codepoint = utf8_to_utf32_codepoint(u8cur, u8len); + + // Convert the UTF32 codepoint to one or more UTF16 codepoints + if (codepoint <= 0xFFFF) { + // Single UTF16 character + *u16cur++ = (char16_t) codepoint; + } else { + // Multiple UTF16 characters with surrogates + codepoint = codepoint - 0x10000; + *u16cur++ = (char16_t) ((codepoint >> 10) + 0xD800); + if (u16cur >= u16end) { + // Ooops... not enough room for this surrogate pair. + return u16cur-1; + } + *u16cur++ = (char16_t) ((codepoint & 0x3FF) + 0xDC00); + } + + u8cur += u8len; + } + return u16cur; +} + } From ddff6230495b66312ad93f652d0c79069a64dbbd Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Thu, 1 Aug 2013 12:47:58 -0700 Subject: [PATCH 492/541] minor tweaks to FlattenableUtils Change-Id: Ibfceec36434baac92c8815a18d902375d3fa1a6d --- include/utils/Flattenable.h | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/include/utils/Flattenable.h b/include/utils/Flattenable.h index c283ad766..882a8b249 100644 --- a/include/utils/Flattenable.h +++ b/include/utils/Flattenable.h @@ -35,13 +35,18 @@ public: } template - static size_t align(void*& buffer) { + static size_t align(void const*& buffer) { COMPILE_TIME_ASSERT_FUNCTION_SCOPE( !(N & (N-1)) ); intptr_t b = intptr_t(buffer); buffer = (void*)((intptr_t(buffer) + (N-1)) & ~(N-1)); return size_t(intptr_t(buffer) - b); } + template + static size_t align(void*& buffer) { + return align( const_cast(buffer) ); + } + static void advance(void*& buffer, size_t& size, size_t offset) { buffer = reinterpret_cast( intptr_t(buffer) + offset ); size -= offset; @@ -88,8 +93,7 @@ public: // file descriptors are written in the fds[] array but ownership is // not transfered (ie: they must be dupped by the caller of // flatten() if needed). - inline status_t flatten(void*& buffer, size_t& size, - int*& fds, size_t& count) const; + inline status_t flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const; // unflattens the object from buffer. // size should be equal to the value of getFlattenedSize() when the @@ -98,8 +102,7 @@ public: // don't need to be dupped(). ie: the caller of unflatten doesn't // keep ownership. If a fd is not retained by unflatten() it must be // explicitly closed. - inline status_t unflatten(void const*& buffer, size_t& size, - int const*& fds, size_t& count); + inline status_t unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count); }; template From c051ffd7c24bbe989c9e39c793c6bbbac3ee3eff Mon Sep 17 00:00:00 2001 From: Glenn Kasten Date: Thu, 1 Aug 2013 07:35:33 -0700 Subject: [PATCH 493/541] Add audio_input_flags_t Change-Id: Ie65398cc67218cc63be4baa8a2a61d0ea591a2a3 --- include/system/audio.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/include/system/audio.h b/include/system/audio.h index 6c260dd7f..abd2990aa 100644 --- a/include/system/audio.h +++ b/include/system/audio.h @@ -389,6 +389,16 @@ typedef enum { AUDIO_OUTPUT_FLAG_NON_BLOCKING = 0x20 // use non-blocking write } audio_output_flags_t; +/* The audio input flags are analogous to audio output flags. + * Currently they are used only when an AudioRecord is created, + * to indicate a preference to be connected to an input stream with + * attributes corresponding to the specified flags. + */ +typedef enum { + AUDIO_INPUT_FLAG_NONE = 0x0, // no attributes + AUDIO_INPUT_FLAG_FAST = 0x1, // prefer an input that supports "fast tracks" +} audio_input_flags_t; + /* Additional information about compressed streams offloaded to * hardware playback * The version and size fields must be initialized by the caller by using From be06210c508d5878dcc7d185e5613f4c7e38dfe8 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Wed, 24 Jul 2013 17:37:05 -0700 Subject: [PATCH 494/541] libnetutils: Get mtu together with dhcp session results Change-Id: I517fd1f62d451b07466a21a2b770d9ce389dac19 Signed-off-by: Dmitry Shmidt --- include/netutils/dhcp.h | 8 ++++++-- libnetutils/dhcp_utils.c | 17 ++++++++++++----- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/include/netutils/dhcp.h b/include/netutils/dhcp.h index bd2c9571a..de6bc82f4 100644 --- a/include/netutils/dhcp.h +++ b/include/netutils/dhcp.h @@ -30,7 +30,9 @@ extern int dhcp_do_request(const char *ifname, char *dns[], char *server, uint32_t *lease, - char *vendorInfo); + char *vendorInfo, + char *domain, + char *mtu); extern int dhcp_do_request_renew(const char *ifname, char *ipaddr, char *gateway, @@ -38,7 +40,9 @@ extern int dhcp_do_request_renew(const char *ifname, char *dns[], char *server, uint32_t *lease, - char *vendorInfo); + char *vendorInfo, + char *domain, + char *mtu); extern int dhcp_stop(const char *ifname); extern int dhcp_release_lease(const char *ifname); extern char *dhcp_get_errmsg(); diff --git a/libnetutils/dhcp_utils.c b/libnetutils/dhcp_utils.c index da00f7436..e1df87481 100644 --- a/libnetutils/dhcp_utils.c +++ b/libnetutils/dhcp_utils.c @@ -92,7 +92,8 @@ static int fill_ip_info(const char *interface, char *server, uint32_t *lease, char *vendorInfo, - char *domain) + char *domain, + char *mtu) { char prop_name[PROPERTY_KEY_MAX]; char prop_value[PROPERTY_VALUE_MAX]; @@ -158,6 +159,10 @@ static int fill_ip_info(const char *interface, p2p_interface); property_get(prop_name, domain, NULL); + snprintf(prop_name, sizeof(prop_name), "%s.%s.mtu", DHCP_PROP_NAME_PREFIX, + p2p_interface); + property_get(prop_name, mtu, NULL); + return 0; } @@ -186,7 +191,8 @@ int dhcp_do_request(const char *interface, char *server, uint32_t *lease, char *vendorInfo, - char *domain) + char *domain, + char *mtu) { char result_prop_name[PROPERTY_KEY_MAX]; char daemon_prop_name[PROPERTY_KEY_MAX]; @@ -238,7 +244,7 @@ int dhcp_do_request(const char *interface, if (strcmp(prop_value, "ok") == 0) { char dns_prop_name[PROPERTY_KEY_MAX]; if (fill_ip_info(interface, ipaddr, gateway, prefixLength, dns, - server, lease, vendorInfo, domain) == -1) { + server, lease, vendorInfo, domain, mtu) == -1) { return -1; } return 0; @@ -329,7 +335,8 @@ int dhcp_do_request_renew(const char *interface, char *server, uint32_t *lease, char *vendorInfo, - char *domain) + char *domain, + char *mtu) { char result_prop_name[PROPERTY_KEY_MAX]; char prop_value[PROPERTY_VALUE_MAX] = {'\0'}; @@ -366,7 +373,7 @@ int dhcp_do_request_renew(const char *interface, } if (strcmp(prop_value, "ok") == 0) { return fill_ip_info(interface, ipaddr, gateway, prefixLength, dns, - server, lease, vendorInfo, domain); + server, lease, vendorInfo, domain, mtu); } else { snprintf(errmsg, sizeof(errmsg), "DHCP Renew result was %s", prop_value); return -1; From d98e07fdf9c338589f263c47ce5c844ed43efad5 Mon Sep 17 00:00:00 2001 From: Alex Ray Date: Fri, 2 Aug 2013 14:40:08 -0700 Subject: [PATCH 495/541] move libs/utils to libutils Change-Id: I6cf4268599460791414882f91eeb88a992fbd29d --- {libs/utils => libutils}/Android.mk | 0 {libs/utils => libutils}/BasicHashtable.cpp | 0 {libs/utils => libutils}/BlobCache.cpp | 0 {libs/utils => libutils}/CallStack.cpp | 0 {libs/utils => libutils}/CleanSpec.mk | 0 {libs/utils => libutils}/FileMap.cpp | 0 {libs/utils => libutils}/JenkinsHash.cpp | 0 {libs/utils => libutils}/LinearAllocator.cpp | 0 {libs/utils => libutils}/LinearTransform.cpp | 0 {libs/utils => libutils}/Log.cpp | 0 {libs/utils => libutils}/Looper.cpp | 0 {libs/utils => libutils}/MODULE_LICENSE_APACHE2 | 0 {libs/utils => libutils}/NOTICE | 0 {libs/utils => libutils}/PropertyMap.cpp | 0 {libs/utils => libutils}/README | 0 {libs/utils => libutils}/RefBase.cpp | 0 {libs/utils => libutils}/SharedBuffer.cpp | 0 {libs/utils => libutils}/Static.cpp | 0 {libs/utils => libutils}/StopWatch.cpp | 0 {libs/utils => libutils}/String16.cpp | 0 {libs/utils => libutils}/String8.cpp | 0 {libs/utils => libutils}/SystemClock.cpp | 0 {libs/utils => libutils}/Threads.cpp | 0 {libs/utils => libutils}/Timers.cpp | 0 {libs/utils => libutils}/Tokenizer.cpp | 0 {libs/utils => libutils}/Trace.cpp | 0 {libs/utils => libutils}/Unicode.cpp | 0 {libs/utils => libutils}/VectorImpl.cpp | 0 {libs/utils => libutils}/misc.cpp | 0 {libs/utils => libutils}/primes.py | 0 {libs/utils => libutils}/tests/Android.mk | 0 {libs/utils => libutils}/tests/BasicHashtable_test.cpp | 0 {libs/utils => libutils}/tests/BitSet_test.cpp | 0 {libs/utils => libutils}/tests/BlobCache_test.cpp | 0 {libs/utils => libutils}/tests/Looper_test.cpp | 0 {libs/utils => libutils}/tests/LruCache_test.cpp | 0 {libs/utils => libutils}/tests/String8_test.cpp | 0 {libs/utils => libutils}/tests/TestHelpers.h | 0 {libs/utils => libutils}/tests/Unicode_test.cpp | 0 {libs/utils => libutils}/tests/Vector_test.cpp | 0 40 files changed, 0 insertions(+), 0 deletions(-) rename {libs/utils => libutils}/Android.mk (100%) rename {libs/utils => libutils}/BasicHashtable.cpp (100%) rename {libs/utils => libutils}/BlobCache.cpp (100%) rename {libs/utils => libutils}/CallStack.cpp (100%) rename {libs/utils => libutils}/CleanSpec.mk (100%) rename {libs/utils => libutils}/FileMap.cpp (100%) rename {libs/utils => libutils}/JenkinsHash.cpp (100%) rename {libs/utils => libutils}/LinearAllocator.cpp (100%) rename {libs/utils => libutils}/LinearTransform.cpp (100%) rename {libs/utils => libutils}/Log.cpp (100%) rename {libs/utils => libutils}/Looper.cpp (100%) rename {libs/utils => libutils}/MODULE_LICENSE_APACHE2 (100%) rename {libs/utils => libutils}/NOTICE (100%) rename {libs/utils => libutils}/PropertyMap.cpp (100%) rename {libs/utils => libutils}/README (100%) rename {libs/utils => libutils}/RefBase.cpp (100%) rename {libs/utils => libutils}/SharedBuffer.cpp (100%) rename {libs/utils => libutils}/Static.cpp (100%) rename {libs/utils => libutils}/StopWatch.cpp (100%) rename {libs/utils => libutils}/String16.cpp (100%) rename {libs/utils => libutils}/String8.cpp (100%) rename {libs/utils => libutils}/SystemClock.cpp (100%) rename {libs/utils => libutils}/Threads.cpp (100%) rename {libs/utils => libutils}/Timers.cpp (100%) rename {libs/utils => libutils}/Tokenizer.cpp (100%) rename {libs/utils => libutils}/Trace.cpp (100%) rename {libs/utils => libutils}/Unicode.cpp (100%) rename {libs/utils => libutils}/VectorImpl.cpp (100%) rename {libs/utils => libutils}/misc.cpp (100%) rename {libs/utils => libutils}/primes.py (100%) rename {libs/utils => libutils}/tests/Android.mk (100%) rename {libs/utils => libutils}/tests/BasicHashtable_test.cpp (100%) rename {libs/utils => libutils}/tests/BitSet_test.cpp (100%) rename {libs/utils => libutils}/tests/BlobCache_test.cpp (100%) rename {libs/utils => libutils}/tests/Looper_test.cpp (100%) rename {libs/utils => libutils}/tests/LruCache_test.cpp (100%) rename {libs/utils => libutils}/tests/String8_test.cpp (100%) rename {libs/utils => libutils}/tests/TestHelpers.h (100%) rename {libs/utils => libutils}/tests/Unicode_test.cpp (100%) rename {libs/utils => libutils}/tests/Vector_test.cpp (100%) diff --git a/libs/utils/Android.mk b/libutils/Android.mk similarity index 100% rename from libs/utils/Android.mk rename to libutils/Android.mk diff --git a/libs/utils/BasicHashtable.cpp b/libutils/BasicHashtable.cpp similarity index 100% rename from libs/utils/BasicHashtable.cpp rename to libutils/BasicHashtable.cpp diff --git a/libs/utils/BlobCache.cpp b/libutils/BlobCache.cpp similarity index 100% rename from libs/utils/BlobCache.cpp rename to libutils/BlobCache.cpp diff --git a/libs/utils/CallStack.cpp b/libutils/CallStack.cpp similarity index 100% rename from libs/utils/CallStack.cpp rename to libutils/CallStack.cpp diff --git a/libs/utils/CleanSpec.mk b/libutils/CleanSpec.mk similarity index 100% rename from libs/utils/CleanSpec.mk rename to libutils/CleanSpec.mk diff --git a/libs/utils/FileMap.cpp b/libutils/FileMap.cpp similarity index 100% rename from libs/utils/FileMap.cpp rename to libutils/FileMap.cpp diff --git a/libs/utils/JenkinsHash.cpp b/libutils/JenkinsHash.cpp similarity index 100% rename from libs/utils/JenkinsHash.cpp rename to libutils/JenkinsHash.cpp diff --git a/libs/utils/LinearAllocator.cpp b/libutils/LinearAllocator.cpp similarity index 100% rename from libs/utils/LinearAllocator.cpp rename to libutils/LinearAllocator.cpp diff --git a/libs/utils/LinearTransform.cpp b/libutils/LinearTransform.cpp similarity index 100% rename from libs/utils/LinearTransform.cpp rename to libutils/LinearTransform.cpp diff --git a/libs/utils/Log.cpp b/libutils/Log.cpp similarity index 100% rename from libs/utils/Log.cpp rename to libutils/Log.cpp diff --git a/libs/utils/Looper.cpp b/libutils/Looper.cpp similarity index 100% rename from libs/utils/Looper.cpp rename to libutils/Looper.cpp diff --git a/libs/utils/MODULE_LICENSE_APACHE2 b/libutils/MODULE_LICENSE_APACHE2 similarity index 100% rename from libs/utils/MODULE_LICENSE_APACHE2 rename to libutils/MODULE_LICENSE_APACHE2 diff --git a/libs/utils/NOTICE b/libutils/NOTICE similarity index 100% rename from libs/utils/NOTICE rename to libutils/NOTICE diff --git a/libs/utils/PropertyMap.cpp b/libutils/PropertyMap.cpp similarity index 100% rename from libs/utils/PropertyMap.cpp rename to libutils/PropertyMap.cpp diff --git a/libs/utils/README b/libutils/README similarity index 100% rename from libs/utils/README rename to libutils/README diff --git a/libs/utils/RefBase.cpp b/libutils/RefBase.cpp similarity index 100% rename from libs/utils/RefBase.cpp rename to libutils/RefBase.cpp diff --git a/libs/utils/SharedBuffer.cpp b/libutils/SharedBuffer.cpp similarity index 100% rename from libs/utils/SharedBuffer.cpp rename to libutils/SharedBuffer.cpp diff --git a/libs/utils/Static.cpp b/libutils/Static.cpp similarity index 100% rename from libs/utils/Static.cpp rename to libutils/Static.cpp diff --git a/libs/utils/StopWatch.cpp b/libutils/StopWatch.cpp similarity index 100% rename from libs/utils/StopWatch.cpp rename to libutils/StopWatch.cpp diff --git a/libs/utils/String16.cpp b/libutils/String16.cpp similarity index 100% rename from libs/utils/String16.cpp rename to libutils/String16.cpp diff --git a/libs/utils/String8.cpp b/libutils/String8.cpp similarity index 100% rename from libs/utils/String8.cpp rename to libutils/String8.cpp diff --git a/libs/utils/SystemClock.cpp b/libutils/SystemClock.cpp similarity index 100% rename from libs/utils/SystemClock.cpp rename to libutils/SystemClock.cpp diff --git a/libs/utils/Threads.cpp b/libutils/Threads.cpp similarity index 100% rename from libs/utils/Threads.cpp rename to libutils/Threads.cpp diff --git a/libs/utils/Timers.cpp b/libutils/Timers.cpp similarity index 100% rename from libs/utils/Timers.cpp rename to libutils/Timers.cpp diff --git a/libs/utils/Tokenizer.cpp b/libutils/Tokenizer.cpp similarity index 100% rename from libs/utils/Tokenizer.cpp rename to libutils/Tokenizer.cpp diff --git a/libs/utils/Trace.cpp b/libutils/Trace.cpp similarity index 100% rename from libs/utils/Trace.cpp rename to libutils/Trace.cpp diff --git a/libs/utils/Unicode.cpp b/libutils/Unicode.cpp similarity index 100% rename from libs/utils/Unicode.cpp rename to libutils/Unicode.cpp diff --git a/libs/utils/VectorImpl.cpp b/libutils/VectorImpl.cpp similarity index 100% rename from libs/utils/VectorImpl.cpp rename to libutils/VectorImpl.cpp diff --git a/libs/utils/misc.cpp b/libutils/misc.cpp similarity index 100% rename from libs/utils/misc.cpp rename to libutils/misc.cpp diff --git a/libs/utils/primes.py b/libutils/primes.py similarity index 100% rename from libs/utils/primes.py rename to libutils/primes.py diff --git a/libs/utils/tests/Android.mk b/libutils/tests/Android.mk similarity index 100% rename from libs/utils/tests/Android.mk rename to libutils/tests/Android.mk diff --git a/libs/utils/tests/BasicHashtable_test.cpp b/libutils/tests/BasicHashtable_test.cpp similarity index 100% rename from libs/utils/tests/BasicHashtable_test.cpp rename to libutils/tests/BasicHashtable_test.cpp diff --git a/libs/utils/tests/BitSet_test.cpp b/libutils/tests/BitSet_test.cpp similarity index 100% rename from libs/utils/tests/BitSet_test.cpp rename to libutils/tests/BitSet_test.cpp diff --git a/libs/utils/tests/BlobCache_test.cpp b/libutils/tests/BlobCache_test.cpp similarity index 100% rename from libs/utils/tests/BlobCache_test.cpp rename to libutils/tests/BlobCache_test.cpp diff --git a/libs/utils/tests/Looper_test.cpp b/libutils/tests/Looper_test.cpp similarity index 100% rename from libs/utils/tests/Looper_test.cpp rename to libutils/tests/Looper_test.cpp diff --git a/libs/utils/tests/LruCache_test.cpp b/libutils/tests/LruCache_test.cpp similarity index 100% rename from libs/utils/tests/LruCache_test.cpp rename to libutils/tests/LruCache_test.cpp diff --git a/libs/utils/tests/String8_test.cpp b/libutils/tests/String8_test.cpp similarity index 100% rename from libs/utils/tests/String8_test.cpp rename to libutils/tests/String8_test.cpp diff --git a/libs/utils/tests/TestHelpers.h b/libutils/tests/TestHelpers.h similarity index 100% rename from libs/utils/tests/TestHelpers.h rename to libutils/tests/TestHelpers.h diff --git a/libs/utils/tests/Unicode_test.cpp b/libutils/tests/Unicode_test.cpp similarity index 100% rename from libs/utils/tests/Unicode_test.cpp rename to libutils/tests/Unicode_test.cpp diff --git a/libs/utils/tests/Vector_test.cpp b/libutils/tests/Vector_test.cpp similarity index 100% rename from libs/utils/tests/Vector_test.cpp rename to libutils/tests/Vector_test.cpp From 3ad3d1c4b5856d4e314febc5671c74e78a76db00 Mon Sep 17 00:00:00 2001 From: Geremy Condra Date: Fri, 22 Feb 2013 18:11:41 -0800 Subject: [PATCH 496/541] Add basic verity support to fs_mgr. This change adds a "verify" fs_mgr flag specifying that the device in question should be verified. Devices marked with this flag are expected to have a footer immediately after their data containing all the information needed to set up a verity instance. Change-Id: I10101f2c3240228ee0932e3767fe35e673d2e720 --- fs_mgr/Android.mk | 7 +- fs_mgr/fs_mgr.c | 27 ++- fs_mgr/fs_mgr_priv.h | 3 + fs_mgr/fs_mgr_priv_verity.h | 17 ++ fs_mgr/fs_mgr_verity.c | 410 ++++++++++++++++++++++++++++++++++++ fs_mgr/include/fs_mgr.h | 4 + init/Android.mk | 4 +- 7 files changed, 465 insertions(+), 7 deletions(-) create mode 100644 fs_mgr/fs_mgr_priv_verity.h create mode 100644 fs_mgr/fs_mgr_verity.c diff --git a/fs_mgr/Android.mk b/fs_mgr/Android.mk index 782ae9986..790598a91 100644 --- a/fs_mgr/Android.mk +++ b/fs_mgr/Android.mk @@ -3,12 +3,13 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) -LOCAL_SRC_FILES:= fs_mgr.c +LOCAL_SRC_FILES:= fs_mgr.c fs_mgr_verity.c LOCAL_C_INCLUDES := $(LOCAL_PATH)/include LOCAL_MODULE:= libfs_mgr -LOCAL_STATIC_LIBRARIES := liblogwrap +LOCAL_STATIC_LIBRARIES := liblogwrap libmincrypt libext4_utils_static +LOCAL_C_INCLUDES += system/extras/ext4_utils LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include include $(BUILD_STATIC_LIBRARY) @@ -28,7 +29,7 @@ LOCAL_FORCE_STATIC_EXECUTABLE := true LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)/sbin LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_UNSTRIPPED) -LOCAL_STATIC_LIBRARIES := libfs_mgr liblogwrap libcutils liblog libc +LOCAL_STATIC_LIBRARIES := libfs_mgr liblogwrap libcutils liblog libc libmincrypt libext4_utils_static include $(BUILD_EXECUTABLE) diff --git a/fs_mgr/fs_mgr.c b/fs_mgr/fs_mgr.c index 2761545bb..82c579821 100644 --- a/fs_mgr/fs_mgr.c +++ b/fs_mgr/fs_mgr.c @@ -34,12 +34,18 @@ #define SWAP_FLAG_PRIO_SHIFT 0 #define SWAP_FLAG_DISCARD 0x10000 +#include #include #include #include #include +#include "mincrypt/rsa.h" +#include "mincrypt/sha.h" +#include "mincrypt/sha256.h" + #include "fs_mgr_priv.h" +#include "fs_mgr_priv_verity.h" #define KEY_LOC_PROP "ro.crypto.keyfile.userdata" #define KEY_IN_FOOTER "footer" @@ -85,6 +91,7 @@ static struct flag_list fs_mgr_flags[] = { { "recoveryonly",MF_RECOVERYONLY }, { "swapprio=", MF_SWAPPRIO }, { "zramsize=", MF_ZRAMSIZE }, + { "verify", MF_VERIFY }, { "defaults", 0 }, { 0, 0 }, }; @@ -588,9 +595,17 @@ int fs_mgr_mount_all(struct fstab *fstab) fstab->recs[i].mount_point); } + if (fstab->recs[i].fs_mgr_flags & MF_VERIFY) { + if (fs_mgr_setup_verity(&fstab->recs[i]) < 0) { + ERROR("Could not set up verified partition, skipping!"); + continue; + } + } + mret = __mount(fstab->recs[i].blk_device, fstab->recs[i].mount_point, - fstab->recs[i].fs_type, fstab->recs[i].flags, - fstab->recs[i].fs_options); + fstab->recs[i].fs_type, fstab->recs[i].flags, + fstab->recs[i].fs_options); + if (!mret) { /* Success! Go get the next one */ continue; @@ -665,6 +680,13 @@ int fs_mgr_do_mount(struct fstab *fstab, char *n_name, char *n_blk_device, fstab->recs[i].mount_point); } + if (fstab->recs[i].fs_mgr_flags & MF_VERIFY) { + if (fs_mgr_setup_verity(&fstab->recs[i]) < 0) { + ERROR("Could not set up verified partition, skipping!"); + continue; + } + } + /* Now mount it where requested */ if (tmp_mount_point) { m = tmp_mount_point; @@ -909,4 +931,3 @@ int fs_mgr_is_encryptable(struct fstab_rec *fstab) { return fstab->fs_mgr_flags & MF_CRYPT; } - diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h index 465f51ef0..f284ca69e 100644 --- a/fs_mgr/fs_mgr_priv.h +++ b/fs_mgr/fs_mgr_priv.h @@ -71,6 +71,9 @@ #define MF_RECOVERYONLY 0x40 #define MF_SWAPPRIO 0x80 #define MF_ZRAMSIZE 0x100 +#define MF_VERIFY 0x200 + +#define DM_BUF_SIZE 4096 #endif /* __CORE_FS_MGR_PRIV_H */ diff --git a/fs_mgr/fs_mgr_priv_verity.h b/fs_mgr/fs_mgr_priv_verity.h new file mode 100644 index 000000000..61937849a --- /dev/null +++ b/fs_mgr/fs_mgr_priv_verity.h @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2013 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. + */ + +int fs_mgr_setup_verity(struct fstab_rec *fstab); \ No newline at end of file diff --git a/fs_mgr/fs_mgr_verity.c b/fs_mgr/fs_mgr_verity.c new file mode 100644 index 000000000..969eab2a0 --- /dev/null +++ b/fs_mgr/fs_mgr_verity.c @@ -0,0 +1,410 @@ +/* + * Copyright (C) 2013 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "mincrypt/rsa.h" +#include "mincrypt/sha.h" +#include "mincrypt/sha256.h" + +#include "ext4_utils.h" +#include "ext4.h" + +#include "fs_mgr_priv.h" +#include "fs_mgr_priv_verity.h" + +#define VERITY_METADATA_SIZE 32768 +#define VERITY_METADATA_MAGIC_NUMBER 0xb001b001 +#define VERITY_TABLE_RSA_KEY "/verity_key" + +extern struct fs_info info; + +static RSAPublicKey *load_key(char *path) +{ + FILE *f; + RSAPublicKey *key; + + key = malloc(sizeof(RSAPublicKey)); + if (!key) { + ERROR("Can't malloc key\n"); + return NULL; + } + + f = fopen(path, "r"); + if (!f) { + ERROR("Can't open '%s'\n", path); + free(key); + return NULL; + } + + if (!fread(key, sizeof(*key), 1, f)) { + ERROR("Could not read key!"); + fclose(f); + free(key); + return NULL; + } + + if (key->len != RSANUMWORDS) { + ERROR("Invalid key length %d\n", key->len); + fclose(f); + free(key); + return NULL; + } + + fclose(f); + return key; +} + +static int verify_table(char *signature, char *table, int table_length) +{ + int fd; + RSAPublicKey *key; + uint8_t hash_buf[SHA_DIGEST_SIZE]; + int retval = -1; + + // Hash the table + SHA_hash((uint8_t*)table, table_length, hash_buf); + + // Now get the public key from the keyfile + key = load_key(VERITY_TABLE_RSA_KEY); + if (!key) { + ERROR("Couldn't load verity keys"); + goto out; + } + + // verify the result + if (!RSA_verify(key, + (uint8_t*) signature, + RSANUMBYTES, + (uint8_t*) hash_buf, + SHA_DIGEST_SIZE)) { + ERROR("Couldn't verify table."); + goto out; + } + + retval = 0; + +out: + free(key); + return retval; +} + +static int get_target_device_size(char *blk_device, uint64_t *device_size) +{ + int data_device; + struct ext4_super_block sb; + + data_device = open(blk_device, O_RDONLY); + if (data_device < 0) { + ERROR("Error opening block device (%s)", strerror(errno)); + return -1; + } + + if (lseek64(data_device, 1024, SEEK_SET) < 0) { + ERROR("Error seeking to superblock"); + close(data_device); + return -1; + } + + if (read(data_device, &sb, sizeof(sb)) != sizeof(sb)) { + ERROR("Error reading superblock"); + close(data_device); + return -1; + } + + ext4_parse_sb(&sb); + *device_size = info.len; + + close(data_device); + return 0; +} + +static int read_verity_metadata(char *block_device, char **signature, char **table) +{ + unsigned magic_number; + unsigned table_length; + uint64_t device_length; + int protocol_version; + FILE *device; + int retval = -1; + + device = fopen(block_device, "r"); + if (!device) { + ERROR("Could not open block device %s (%s).\n", block_device, strerror(errno)); + goto out; + } + + // find the start of the verity metadata + if (get_target_device_size(block_device, &device_length) < 0) { + ERROR("Could not get target device size.\n"); + goto out; + } + if (fseek(device, device_length, SEEK_SET) < 0) { + ERROR("Could not seek to start of verity metadata block.\n"); + goto out; + } + + // check the magic number + if (!fread(&magic_number, sizeof(int), 1, device)) { + ERROR("Couldn't read magic number!\n"); + goto out; + } + if (magic_number != VERITY_METADATA_MAGIC_NUMBER) { + ERROR("Couldn't find verity metadata at offset %llu!\n", device_length); + goto out; + } + + // check the protocol version + if (!fread(&protocol_version, sizeof(int), 1, device)) { + ERROR("Couldn't read verity metadata protocol version!\n"); + goto out; + } + if (protocol_version != 0) { + ERROR("Got unknown verity metadata protocol version %d!\n", protocol_version); + goto out; + } + + // get the signature + *signature = (char*) malloc(RSANUMBYTES * sizeof(char)); + if (!*signature) { + ERROR("Couldn't allocate memory for signature!\n"); + goto out; + } + if (!fread(*signature, RSANUMBYTES, 1, device)) { + ERROR("Couldn't read signature from verity metadata!\n"); + free(*signature); + goto out; + } + + // get the size of the table + if (!fread(&table_length, sizeof(int), 1, device)) { + ERROR("Couldn't get the size of the verity table from metadata!\n"); + free(*signature); + goto out; + } + + // get the table + null terminator + table_length += 1; + *table = malloc(table_length); + if(!*table) { + ERROR("Couldn't allocate memory for verity table!\n"); + goto out; + } + if (!fgets(*table, table_length, device)) { + ERROR("Couldn't read the verity table from metadata!\n"); + free(*table); + free(*signature); + goto out; + } + + retval = 0; + +out: + if (device) + fclose(device); + return retval; +} + +static void verity_ioctl_init(struct dm_ioctl *io, char *name, unsigned flags) +{ + memset(io, 0, DM_BUF_SIZE); + io->data_size = DM_BUF_SIZE; + io->data_start = sizeof(struct dm_ioctl); + io->version[0] = 4; + io->version[1] = 0; + io->version[2] = 0; + io->flags = flags | DM_READONLY_FLAG; + if (name) { + strlcpy(io->name, name, sizeof(io->name)); + } +} + +static int create_verity_device(struct dm_ioctl *io, char *name, int fd) +{ + verity_ioctl_init(io, name, 1); + if (ioctl(fd, DM_DEV_CREATE, io)) { + ERROR("Error creating device mapping (%s)", strerror(errno)); + return -1; + } + return 0; +} + +static int get_verity_device_name(struct dm_ioctl *io, char *name, int fd, char **dev_name) +{ + verity_ioctl_init(io, name, 0); + if (ioctl(fd, DM_DEV_STATUS, io)) { + ERROR("Error fetching verity device number (%s)", strerror(errno)); + return -1; + } + int dev_num = (io->dev & 0xff) | ((io->dev >> 12) & 0xfff00); + if (asprintf(dev_name, "/dev/block/dm-%u", dev_num) < 0) { + ERROR("Error getting verity block device name (%s)", strerror(errno)); + return -1; + } + return 0; +} + +static int load_verity_table(struct dm_ioctl *io, char *name, char *blockdev, int fd, char *table) +{ + char *verity_params; + char *buffer = (char*) io; + uint64_t device_size = 0; + + if (get_target_device_size(blockdev, &device_size) < 0) { + return -1; + } + + verity_ioctl_init(io, name, DM_STATUS_TABLE_FLAG); + + struct dm_target_spec *tgt = (struct dm_target_spec *) &buffer[sizeof(struct dm_ioctl)]; + + // set tgt arguments here + io->target_count = 1; + tgt->status=0; + tgt->sector_start=0; + tgt->length=device_size/512; + strcpy(tgt->target_type, "verity"); + + // build the verity params here + verity_params = buffer + sizeof(struct dm_ioctl) + sizeof(struct dm_target_spec); + if (sprintf(verity_params, "%s", table) < 0) { + return -1; + } + + // set next target boundary + verity_params += strlen(verity_params) + 1; + verity_params = (char*) (((unsigned long)verity_params + 7) & ~8); + tgt->next = verity_params - buffer; + + // send the ioctl to load the verity table + if (ioctl(fd, DM_TABLE_LOAD, io)) { + ERROR("Error loading verity table (%s)", strerror(errno)); + return -1; + } + + return 0; +} + +static int resume_verity_table(struct dm_ioctl *io, char *name, int fd) +{ + verity_ioctl_init(io, name, 0); + if (ioctl(fd, DM_DEV_SUSPEND, io)) { + ERROR("Error activating verity device (%s)", strerror(errno)); + return -1; + } + return 0; +} + +static int test_access(char *device) { + int tries = 25; + while (tries--) { + if (!access(device, F_OK) || errno != ENOENT) { + return 0; + } + usleep(40 * 1000); + } + return -1; +} + +int fs_mgr_setup_verity(struct fstab_rec *fstab) { + + int retval = -1; + + char *verity_blk_name; + char *verity_table; + char *verity_table_signature; + + char buffer[DM_BUF_SIZE]; + struct dm_ioctl *io = (struct dm_ioctl *) buffer; + char *mount_point = basename(fstab->mount_point); + + // set the dm_ioctl flags + io->flags |= 1; + io->target_count = 1; + + // get the device mapper fd + int fd; + if ((fd = open("/dev/device-mapper", O_RDWR)) < 0) { + ERROR("Error opening device mapper (%s)", strerror(errno)); + return retval; + } + + // create the device + if (create_verity_device(io, mount_point, fd) < 0) { + ERROR("Couldn't create verity device!"); + goto out; + } + + // get the name of the device file + if (get_verity_device_name(io, mount_point, fd, &verity_blk_name) < 0) { + ERROR("Couldn't get verity device number!"); + goto out; + } + + // read the verity block at the end of the block device + if (read_verity_metadata(fstab->blk_device, + &verity_table_signature, + &verity_table) < 0) { + goto out; + } + + // verify the signature on the table + if (verify_table(verity_table_signature, + verity_table, + strlen(verity_table)) < 0) { + goto out; + } + + // load the verity mapping table + if (load_verity_table(io, mount_point, fstab->blk_device, fd, verity_table) < 0) { + goto out; + } + + // activate the device + if (resume_verity_table(io, mount_point, fd) < 0) { + goto out; + } + + // assign the new verity block device as the block device + free(fstab->blk_device); + fstab->blk_device = verity_blk_name; + + // make sure we've set everything up properly + if (test_access(fstab->blk_device) < 0) { + goto out; + } + + retval = 0; + +out: + close(fd); + return retval; +} diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h index 110e73830..384d19594 100644 --- a/fs_mgr/include/fs_mgr.h +++ b/fs_mgr/include/fs_mgr.h @@ -17,6 +17,9 @@ #ifndef __CORE_FS_MGR_H #define __CORE_FS_MGR_H +#include +#include + #ifdef __cplusplus extern "C" { #endif @@ -35,6 +38,7 @@ struct fstab_rec { char *fs_options; int fs_mgr_flags; char *key_loc; + char *verity_loc; long long length; char *label; int partnum; diff --git a/init/Android.mk b/init/Android.mk index ef62bce5c..abfc68a0f 100644 --- a/init/Android.mk +++ b/init/Android.mk @@ -39,7 +39,9 @@ LOCAL_STATIC_LIBRARIES := \ libcutils \ liblog \ libc \ - libselinux + libselinux \ + libmincrypt \ + libext4_utils_static include $(BUILD_EXECUTABLE) From b45f1f5bd00c9a8380960d7c439eb14b3a334f50 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Tue, 30 Jul 2013 18:57:16 -0700 Subject: [PATCH 497/541] healthd: add optional current_now and charge_counter to tracked state uA and uAh units are converted to mA and mAh. If current_now is present, add it to the heartbeat log (c=nnn). Change-Id: I2b5fe7b4505c98ca2d11c3f94564c1c38493c8b9 --- healthd/BatteryMonitor.cpp | 42 ++++++++++++++++++++++++++++++++------ healthd/BatteryMonitor.h | 2 ++ 2 files changed, 38 insertions(+), 6 deletions(-) diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp index 22f77136f..882d514bd 100644 --- a/healthd/BatteryMonitor.cpp +++ b/healthd/BatteryMonitor.cpp @@ -176,6 +176,8 @@ bool BatteryMonitor::update(void) { props.chargerWirelessOnline = false; props.batteryStatus = BATTERY_STATUS_UNKNOWN; props.batteryHealth = BATTERY_HEALTH_UNKNOWN; + props.batteryCurrentNow = INT_MIN; + props.batteryChargeCounter = INT_MIN; if (!mBatteryPresentPath.isEmpty()) props.batteryPresent = getBooleanField(mBatteryPresentPath); @@ -184,6 +186,13 @@ bool BatteryMonitor::update(void) { props.batteryLevel = getIntField(mBatteryCapacityPath); props.batteryVoltage = getIntField(mBatteryVoltagePath) / 1000; + + if (!mBatteryCurrentNowPath.isEmpty()) + props.batteryCurrentNow = getIntField(mBatteryCurrentNowPath) / 1000; + + if (!mBatteryChargeCounterPath.isEmpty()) + props.batteryChargeCounter = getIntField(mBatteryChargeCounterPath) / 1000; + props.batteryTemperature = getIntField(mBatteryTemperaturePath); const int SIZE = 128; @@ -229,12 +238,23 @@ bool BatteryMonitor::update(void) { } } - KLOG_INFO(LOG_TAG, "battery l=%d v=%d t=%s%d.%d h=%d st=%d chg=%s%s%s\n", - props.batteryLevel, props.batteryVoltage, - props.batteryTemperature < 0 ? "-" : "", - abs(props.batteryTemperature / 10), - abs(props.batteryTemperature % 10), props.batteryHealth, - props.batteryStatus, + char dmesgline[256]; + snprintf(dmesgline, sizeof(dmesgline), + "battery l=%d v=%d t=%s%d.%d h=%d st=%d", + props.batteryLevel, props.batteryVoltage, + props.batteryTemperature < 0 ? "-" : "", + abs(props.batteryTemperature / 10), + abs(props.batteryTemperature % 10), props.batteryHealth, + props.batteryStatus); + + if (!mBatteryCurrentNowPath.isEmpty()) { + char b[20]; + + snprintf(b, sizeof(b), " c=%d", props.batteryCurrentNow); + strlcat(dmesgline, b, sizeof(dmesgline)); + } + + KLOG_INFO(LOG_TAG, "%s chg=%s%s%s\n", dmesgline, props.chargerAcOnline ? "a" : "", props.chargerUsbOnline ? "u" : "", props.chargerWirelessOnline ? "w" : ""); @@ -304,6 +324,16 @@ void BatteryMonitor::init(bool nosvcmgr) { mBatteryVoltagePath = path; } + path.clear(); + path.appendFormat("%s/%s/current_now", POWER_SUPPLY_SYSFS_PATH, name); + if (access(path, R_OK) == 0) + mBatteryCurrentNowPath = path; + + path.clear(); + path.appendFormat("%s/%s/charge_counter", POWER_SUPPLY_SYSFS_PATH, name); + if (access(path, R_OK) == 0) + mBatteryChargeCounterPath = path; + path.clear(); path.appendFormat("%s/%s/temp", POWER_SUPPLY_SYSFS_PATH, name); if (access(path, R_OK) == 0) { diff --git a/healthd/BatteryMonitor.h b/healthd/BatteryMonitor.h index df0920b88..ff1ea1ed3 100644 --- a/healthd/BatteryMonitor.h +++ b/healthd/BatteryMonitor.h @@ -49,6 +49,8 @@ class BatteryMonitor { String8 mBatteryVoltagePath; String8 mBatteryTemperaturePath; String8 mBatteryTechnologyPath; + String8 mBatteryCurrentNowPath; + String8 mBatteryChargeCounterPath; Vector mChargerNames; From 10b235e7436256da9adc827125645f141bd8fac9 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Wed, 7 Aug 2013 15:25:14 -0700 Subject: [PATCH 498/541] healthd: add health HAL support Adds board-specific battery monitoring capabilities: * processing of battery property values and additional charging logic. * adjusted (or removed) polling intervals. * replaced (or removed) battery status heartbeat in kernel log. Change-Id: Ia77bca8dc92c6c2a51afa65d516cacca08da73ac --- healthd/Android.mk | 6 ++++ healthd/BatteryMonitor.cpp | 40 ++++++++++++---------- healthd/healthd.cpp | 31 ++++++++++++----- healthd/healthd.h | 56 +++++++++++++++++++++++++++++++ healthd/healthd_board_default.cpp | 29 ++++++++++++++++ 5 files changed, 137 insertions(+), 25 deletions(-) create mode 100644 healthd/healthd.h create mode 100644 healthd/healthd_board_default.cpp diff --git a/healthd/Android.mk b/healthd/Android.mk index 910afb2ec..cff7a3b98 100644 --- a/healthd/Android.mk +++ b/healthd/Android.mk @@ -18,6 +18,12 @@ LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_SBIN_UNSTRIPPED) LOCAL_STATIC_LIBRARIES := libbatteryservice libbinder libz libutils libstdc++ libcutils liblog libm libc +ifdef BOARD_LIB_HEALTHD +LOCAL_STATIC_LIBRARIES += $(BOARD_LIB_HEALTHD) +else +LOCAL_SRC_FILES += healthd_board_default.cpp +endif + include $(BUILD_EXECUTABLE) endif diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp index 882d514bd..8492dce59 100644 --- a/healthd/BatteryMonitor.cpp +++ b/healthd/BatteryMonitor.cpp @@ -16,6 +16,7 @@ #define LOG_TAG "healthd" +#include "healthd.h" #include "BatteryMonitor.h" #include "BatteryPropertiesRegistrar.h" @@ -170,6 +171,7 @@ int BatteryMonitor::getIntField(const String8& path) { bool BatteryMonitor::update(void) { struct BatteryProperties props; + bool logthis; props.chargerAcOnline = false; props.chargerUsbOnline = false; @@ -238,27 +240,31 @@ bool BatteryMonitor::update(void) { } } - char dmesgline[256]; - snprintf(dmesgline, sizeof(dmesgline), - "battery l=%d v=%d t=%s%d.%d h=%d st=%d", - props.batteryLevel, props.batteryVoltage, - props.batteryTemperature < 0 ? "-" : "", - abs(props.batteryTemperature / 10), - abs(props.batteryTemperature % 10), props.batteryHealth, - props.batteryStatus); + logthis = !healthd_board_battery_update(&props); - if (!mBatteryCurrentNowPath.isEmpty()) { - char b[20]; + if (logthis) { + char dmesgline[256]; + snprintf(dmesgline, sizeof(dmesgline), + "battery l=%d v=%d t=%s%d.%d h=%d st=%d", + props.batteryLevel, props.batteryVoltage, + props.batteryTemperature < 0 ? "-" : "", + abs(props.batteryTemperature / 10), + abs(props.batteryTemperature % 10), props.batteryHealth, + props.batteryStatus); - snprintf(b, sizeof(b), " c=%d", props.batteryCurrentNow); - strlcat(dmesgline, b, sizeof(dmesgline)); + if (!mBatteryCurrentNowPath.isEmpty()) { + char b[20]; + + snprintf(b, sizeof(b), " c=%d", props.batteryCurrentNow); + strlcat(dmesgline, b, sizeof(dmesgline)); + } + + KLOG_INFO(LOG_TAG, "%s chg=%s%s%s\n", dmesgline, + props.chargerAcOnline ? "a" : "", + props.chargerUsbOnline ? "u" : "", + props.chargerWirelessOnline ? "w" : ""); } - KLOG_INFO(LOG_TAG, "%s chg=%s%s%s\n", dmesgline, - props.chargerAcOnline ? "a" : "", - props.chargerUsbOnline ? "u" : "", - props.chargerWirelessOnline ? "w" : ""); - if (mBatteryPropertiesRegistrar != NULL) mBatteryPropertiesRegistrar->notifyListeners(props); diff --git a/healthd/healthd.cpp b/healthd/healthd.cpp index 1d79097a7..89178fa71 100644 --- a/healthd/healthd.cpp +++ b/healthd/healthd.cpp @@ -17,6 +17,7 @@ #define LOG_TAG "healthd" #define KLOG_LEVEL 6 +#include "healthd.h" #include "BatteryMonitor.h" #include @@ -34,8 +35,12 @@ using namespace android; // Periodic chores intervals in seconds -#define PERIODIC_CHORES_INTERVAL_FAST (60 * 1) -#define PERIODIC_CHORES_INTERVAL_SLOW (60 * 10) +#define DEFAULT_PERIODIC_CHORES_INTERVAL_FAST (60 * 1) +#define DEFAULT_PERIODIC_CHORES_INTERVAL_SLOW (60 * 10) +static int periodic_chores_interval_fast = + DEFAULT_PERIODIC_CHORES_INTERVAL_FAST; +static int periodic_chores_interval_slow = + DEFAULT_PERIODIC_CHORES_INTERVAL_SLOW; #define POWER_SUPPLY_SUBSYSTEM "power_supply" @@ -48,7 +53,7 @@ static int binder_fd; // -1 for no epoll timeout static int awake_poll_interval = -1; -static int wakealarm_wake_interval = PERIODIC_CHORES_INTERVAL_FAST; +static int wakealarm_wake_interval = DEFAULT_PERIODIC_CHORES_INTERVAL_FAST; static BatteryMonitor* gBatteryMonitor; @@ -61,6 +66,10 @@ static void wakealarm_set_interval(int interval) { return; wakealarm_wake_interval = interval; + + if (interval == -1) + interval = 0; + itval.it_interval.tv_sec = interval; itval.it_interval.tv_nsec = 0; itval.it_value.tv_sec = interval; @@ -75,7 +84,7 @@ static void battery_update(void) { // slow wake interval when on battery (watch for drained battery). int new_wake_interval = gBatteryMonitor->update() ? - PERIODIC_CHORES_INTERVAL_FAST : PERIODIC_CHORES_INTERVAL_SLOW; + periodic_chores_interval_fast : periodic_chores_interval_slow; if (new_wake_interval != wakealarm_wake_interval) wakealarm_set_interval(new_wake_interval); @@ -85,9 +94,12 @@ static void battery_update(void) { // poll at fast rate while awake and let alarm wake up at slow rate when // asleep. - awake_poll_interval = - new_wake_interval == PERIODIC_CHORES_INTERVAL_FAST ? - -1 : PERIODIC_CHORES_INTERVAL_FAST * 1000; + if (periodic_chores_interval_fast == -1) + awake_poll_interval = -1; + else + awake_poll_interval = + new_wake_interval == periodic_chores_interval_fast ? + -1 : periodic_chores_interval_fast * 1000; } static void periodic_chores() { @@ -138,7 +150,10 @@ static void wakealarm_init(void) { return; } - wakealarm_set_interval(PERIODIC_CHORES_INTERVAL_FAST); + healthd_board_poll_intervals(&periodic_chores_interval_fast, + &periodic_chores_interval_slow); + + wakealarm_set_interval(periodic_chores_interval_fast); } static void wakealarm_event(void) { diff --git a/healthd/healthd.h b/healthd/healthd.h new file mode 100644 index 000000000..18ad03af5 --- /dev/null +++ b/healthd/healthd.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2013 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 _HEALTHD_H_ +#define _HEALTHD_H_ + +#include + +// The following are implemented in libhealthd_board to handle board-specific +// behavior. +// +// Set periodic poll intervals in seconds. +// +// fast_interval is used while the device is not in suspend, or in suspend and +// connected to a charger (to watch for battery overheat due to charging). +// The default value is 60 (1 minute). Value -1 turns off fast_interval +// polling. +// +// slow_interval is used when the device is in suspend and not connected to a +// charger (to watch for a battery drained to zero remaining capacity). The +// default value is 600 (10 minutes). Value -1 tuns off slow interval +// polling. +// +// To use the default values, this function can simply return without +// modifying the parameters. + +void healthd_board_poll_intervals(int *fast_interval, int *slow_interval); + +// Process updated battery property values. This function is called when +// the kernel sends updated battery status via a uevent from the power_supply +// subsystem, or when updated values are polled by healthd, as for periodic +// poll of battery state. +// +// props are the battery properties read from the kernel. These values may +// be modified in this call, prior to sending the modified values to the +// Android runtime. +// +// Return 0 to indicate the usual kernel log battery status heartbeat message +// is to be logged, or non-zero to prevent logging this information. + +int healthd_board_battery_update(struct android::BatteryProperties *props); + +#endif /* _HEALTHD_H_ */ diff --git a/healthd/healthd_board_default.cpp b/healthd/healthd_board_default.cpp new file mode 100644 index 000000000..82f37fe82 --- /dev/null +++ b/healthd/healthd_board_default.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2013 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 + +void healthd_board_poll_intervals(int *fast_interval, int *slow_interval) +{ + // use defaults +} + + +int healthd_board_battery_update(struct android::BatteryProperties *props) +{ + // return 0 to log periodic polled battery status to kernel log + return 0; +} From 9face5cad5b4ffb883b23e1c45aafac73c712fd6 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Thu, 8 Aug 2013 12:24:53 -0700 Subject: [PATCH 499/541] healthd: create healthd_board_init() Subsume healthd_board_poll_intervals, make clear the call is for init-time actions. Change-Id: I9267e4ce7c62b78d2997a43822f20bfa13b54cd8 --- healthd/healthd.cpp | 24 +++++++++++------------ healthd/healthd.h | 32 +++++++++++++++++++------------ healthd/healthd_board_default.cpp | 2 +- 3 files changed, 33 insertions(+), 25 deletions(-) diff --git a/healthd/healthd.cpp b/healthd/healthd.cpp index 89178fa71..158274c20 100644 --- a/healthd/healthd.cpp +++ b/healthd/healthd.cpp @@ -37,10 +37,11 @@ using namespace android; // Periodic chores intervals in seconds #define DEFAULT_PERIODIC_CHORES_INTERVAL_FAST (60 * 1) #define DEFAULT_PERIODIC_CHORES_INTERVAL_SLOW (60 * 10) -static int periodic_chores_interval_fast = - DEFAULT_PERIODIC_CHORES_INTERVAL_FAST; -static int periodic_chores_interval_slow = - DEFAULT_PERIODIC_CHORES_INTERVAL_SLOW; + +static struct healthd_config healthd_config = { + .periodic_chores_interval_fast = DEFAULT_PERIODIC_CHORES_INTERVAL_FAST, + .periodic_chores_interval_slow = DEFAULT_PERIODIC_CHORES_INTERVAL_SLOW, +}; #define POWER_SUPPLY_SUBSYSTEM "power_supply" @@ -84,7 +85,8 @@ static void battery_update(void) { // slow wake interval when on battery (watch for drained battery). int new_wake_interval = gBatteryMonitor->update() ? - periodic_chores_interval_fast : periodic_chores_interval_slow; + healthd_config.periodic_chores_interval_fast : + healthd_config.periodic_chores_interval_slow; if (new_wake_interval != wakealarm_wake_interval) wakealarm_set_interval(new_wake_interval); @@ -94,12 +96,12 @@ static void battery_update(void) { // poll at fast rate while awake and let alarm wake up at slow rate when // asleep. - if (periodic_chores_interval_fast == -1) + if (healthd_config.periodic_chores_interval_fast == -1) awake_poll_interval = -1; else awake_poll_interval = - new_wake_interval == periodic_chores_interval_fast ? - -1 : periodic_chores_interval_fast * 1000; + new_wake_interval == healthd_config.periodic_chores_interval_fast ? + -1 : healthd_config.periodic_chores_interval_fast * 1000; } static void periodic_chores() { @@ -150,10 +152,7 @@ static void wakealarm_init(void) { return; } - healthd_board_poll_intervals(&periodic_chores_interval_fast, - &periodic_chores_interval_slow); - - wakealarm_set_interval(periodic_chores_interval_fast); + wakealarm_set_interval(healthd_config.periodic_chores_interval_fast); } static void wakealarm_event(void) { @@ -262,6 +261,7 @@ int main(int argc, char **argv) { } } + healthd_board_init(&healthd_config); wakealarm_init(); uevent_init(); binder_init(); diff --git a/healthd/healthd.h b/healthd/healthd.h index 18ad03af5..895be40dc 100644 --- a/healthd/healthd.h +++ b/healthd/healthd.h @@ -19,25 +19,33 @@ #include +// periodic_chores_interval_fast, periodic_chores_interval_slow: intervals at +// which healthd wakes up to poll health state and perform periodic chores, +// in units of seconds: +// +// periodic_chores_interval_fast is used while the device is not in +// suspend, or in suspend and connected to a charger (to watch for battery +// overheat due to charging). The default value is 60 (1 minute). Value +// -1 turns off periodic chores (and wakeups) in these conditions. +// +// periodic_chores_interval_slow is used when the device is in suspend and +// not connected to a charger (to watch for a battery drained to zero +// remaining capacity). The default value is 600 (10 minutes). Value -1 +// tuns off periodic chores (and wakeups) in these conditions. + +struct healthd_config { + int periodic_chores_interval_fast; + int periodic_chores_interval_slow; +}; + // The following are implemented in libhealthd_board to handle board-specific // behavior. // -// Set periodic poll intervals in seconds. -// -// fast_interval is used while the device is not in suspend, or in suspend and -// connected to a charger (to watch for battery overheat due to charging). -// The default value is 60 (1 minute). Value -1 turns off fast_interval -// polling. -// -// slow_interval is used when the device is in suspend and not connected to a -// charger (to watch for a battery drained to zero remaining capacity). The -// default value is 600 (10 minutes). Value -1 tuns off slow interval -// polling. // // To use the default values, this function can simply return without // modifying the parameters. -void healthd_board_poll_intervals(int *fast_interval, int *slow_interval); +void healthd_board_init(struct healthd_config *config); // Process updated battery property values. This function is called when // the kernel sends updated battery status via a uevent from the power_supply diff --git a/healthd/healthd_board_default.cpp b/healthd/healthd_board_default.cpp index 82f37fe82..b2bb51655 100644 --- a/healthd/healthd_board_default.cpp +++ b/healthd/healthd_board_default.cpp @@ -16,7 +16,7 @@ #include -void healthd_board_poll_intervals(int *fast_interval, int *slow_interval) +void healthd_board_init(struct healthd_config *config) { // use defaults } From dfe0cbab3f9039f34af1dc9e31faf8155737ec2d Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Wed, 3 Jul 2013 17:08:29 -0700 Subject: [PATCH 500/541] Richer SD card permissions through FUSE. Changes the FUSE daemon to synthesize an Android-specific set of filesystem permissions, even when the underlying media storage is permissionless. This is designed to support several features: First, apps can access their own files in /Android/data/com.example/ without requiring any external storage permissions. This is enabled by allowing o+x on parent directories, and assigning the UID owner based on the directory name (package name). The mapping from package to appId is parsed from packages.list, which is updated when apps are added/removed. Changes are observed through inotify. It creates missing package name directories when requested and valid. Second, support for separate permissions for photos and audio/video content on the device through new GIDs which are assigned based on top-level directory names. Finally, support for multi-user separation on the same physical media through new /Android/user/ directory, which will be bind-mounted into place. It recursively applies the above rules to each secondary user. rwxrwx--x root:sdcard_rw / rwxrwx--- root:sdcard_pics /Pictures rwxrwx--- root:sdcard_av /Music rwxrwx--x root:sdcard_rw /Android rwxrwx--x root:sdcard_rw /Android/data rwxrwx--- u0_a12:sdcard_rw /Android/data/com.example rwxrwx--x root:sdcard_rw /Android/obb/ rwxrwx--- u0_a12:sdcard_rw /Android/obb/com.example rwxrwx--- root:sdcard_all /Android/user rwxrwx--x root:sdcard_rw /Android/user/10 rwxrwx--- u10_a12:sdcard_rw /Android/user/10/Android/data/com.example These derived permissions are disabled by default. Switched option parsing to getopt(). Change-Id: I21bf5d79d13f0f07a6a116122b16395f4f97505b --- include/private/android_filesystem_config.h | 107 ++--- sdcard/Android.mk | 2 +- sdcard/sdcard.c | 424 +++++++++++++++++--- 3 files changed, 431 insertions(+), 102 deletions(-) diff --git a/include/private/android_filesystem_config.h b/include/private/android_filesystem_config.h index 4a7d37707..0ed0d78e6 100644 --- a/include/private/android_filesystem_config.h +++ b/include/private/android_filesystem_config.h @@ -34,8 +34,8 @@ #endif /* This is the master Users and Groups config for the platform. -** DO NOT EVER RENUMBER. -*/ + * DO NOT EVER RENUMBER + */ #define AID_ROOT 0 /* traditional unix root user */ @@ -72,6 +72,10 @@ #define AID_CLAT 1029 /* clat part of nat464 */ #define AID_LOOP_RADIO 1030 /* loop radio devices */ #define AID_MEDIA_DRM 1031 /* MediaDrm plugins */ +#define AID_PACKAGE_INFO 1032 /* access to installed package details */ +#define AID_SDCARD_PICS 1033 /* external storage photos access */ +#define AID_SDCARD_AV 1034 /* external storage audio/video access */ +#define AID_SDCARD_ALL 1035 /* access all users external storage */ #define AID_SHELL 2000 /* adb and debug shell user */ #define AID_CACHE 2001 /* cache access */ @@ -108,50 +112,61 @@ struct android_id_info { }; static const struct android_id_info android_ids[] = { - { "root", AID_ROOT, }, - { "system", AID_SYSTEM, }, - { "radio", AID_RADIO, }, - { "bluetooth", AID_BLUETOOTH, }, - { "graphics", AID_GRAPHICS, }, - { "input", AID_INPUT, }, - { "audio", AID_AUDIO, }, - { "camera", AID_CAMERA, }, - { "log", AID_LOG, }, - { "compass", AID_COMPASS, }, - { "mount", AID_MOUNT, }, - { "wifi", AID_WIFI, }, - { "dhcp", AID_DHCP, }, - { "adb", AID_ADB, }, - { "install", AID_INSTALL, }, - { "media", AID_MEDIA, }, - { "drm", AID_DRM, }, - { "mdnsr", AID_MDNSR, }, - { "nfc", AID_NFC, }, - { "drmrpc", AID_DRMRPC, }, - { "shell", AID_SHELL, }, - { "cache", AID_CACHE, }, - { "diag", AID_DIAG, }, - { "net_bt_admin", AID_NET_BT_ADMIN, }, - { "net_bt", AID_NET_BT, }, - { "net_bt_stack", AID_NET_BT_STACK, }, - { "sdcard_r", AID_SDCARD_R, }, - { "sdcard_rw", AID_SDCARD_RW, }, - { "media_rw", AID_MEDIA_RW, }, - { "vpn", AID_VPN, }, - { "keystore", AID_KEYSTORE, }, - { "usb", AID_USB, }, - { "mtp", AID_MTP, }, - { "gps", AID_GPS, }, - { "inet", AID_INET, }, - { "net_raw", AID_NET_RAW, }, - { "net_admin", AID_NET_ADMIN, }, - { "net_bw_stats", AID_NET_BW_STATS, }, - { "net_bw_acct", AID_NET_BW_ACCT, }, - { "loop_radio", AID_LOOP_RADIO, }, - { "misc", AID_MISC, }, - { "nobody", AID_NOBODY, }, - { "clat", AID_CLAT, }, - { "mediadrm", AID_MEDIA_DRM, }, + { "root", AID_ROOT, }, + + { "system", AID_SYSTEM, }, + + { "radio", AID_RADIO, }, + { "bluetooth", AID_BLUETOOTH, }, + { "graphics", AID_GRAPHICS, }, + { "input", AID_INPUT, }, + { "audio", AID_AUDIO, }, + { "camera", AID_CAMERA, }, + { "log", AID_LOG, }, + { "compass", AID_COMPASS, }, + { "mount", AID_MOUNT, }, + { "wifi", AID_WIFI, }, + { "adb", AID_ADB, }, + { "install", AID_INSTALL, }, + { "media", AID_MEDIA, }, + { "dhcp", AID_DHCP, }, + { "sdcard_rw", AID_SDCARD_RW, }, + { "vpn", AID_VPN, }, + { "keystore", AID_KEYSTORE, }, + { "usb", AID_USB, }, + { "drm", AID_DRM, }, + { "mdnsr", AID_MDNSR, }, + { "gps", AID_GPS, }, + // AID_UNUSED1 + { "media_rw", AID_MEDIA_RW, }, + { "mtp", AID_MTP, }, + // AID_UNUSED2 + { "drmrpc", AID_DRMRPC, }, + { "nfc", AID_NFC, }, + { "sdcard_r", AID_SDCARD_R, }, + { "clat", AID_CLAT, }, + { "loop_radio", AID_LOOP_RADIO, }, + { "mediadrm", AID_MEDIA_DRM, }, + { "package_info", AID_PACKAGE_INFO, }, + { "sdcard_pics", AID_SDCARD_PICS, }, + { "sdcard_av", AID_SDCARD_AV, }, + { "sdcard_all", AID_SDCARD_ALL, }, + + { "shell", AID_SHELL, }, + { "cache", AID_CACHE, }, + { "diag", AID_DIAG, }, + + { "net_bt_admin", AID_NET_BT_ADMIN, }, + { "net_bt", AID_NET_BT, }, + { "inet", AID_INET, }, + { "net_raw", AID_NET_RAW, }, + { "net_admin", AID_NET_ADMIN, }, + { "net_bw_stats", AID_NET_BW_STATS, }, + { "net_bw_acct", AID_NET_BW_ACCT, }, + { "net_bt_stack", AID_NET_BT_STACK, }, + + { "misc", AID_MISC, }, + { "nobody", AID_NOBODY, }, }; #define android_id_count \ diff --git a/sdcard/Android.mk b/sdcard/Android.mk index fb04d6d35..4630db97c 100644 --- a/sdcard/Android.mk +++ b/sdcard/Android.mk @@ -6,6 +6,6 @@ LOCAL_SRC_FILES:= sdcard.c LOCAL_MODULE:= sdcard LOCAL_CFLAGS := -Wall -Wno-unused-parameter -LOCAL_SHARED_LIBRARIES := libc +LOCAL_SHARED_LIBRARIES := libc libcutils include $(BUILD_EXECUTABLE) diff --git a/sdcard/sdcard.c b/sdcard/sdcard.c index bff6e67d5..377c008df 100644 --- a/sdcard/sdcard.c +++ b/sdcard/sdcard.c @@ -30,6 +30,10 @@ #include #include #include +#include + +#include +#include #include @@ -57,6 +61,30 @@ * - if an op that returns a fuse_entry fails writing the reply to the * kernel, you must rollback the refcount to reflect the reference the * kernel did not actually acquire + * + * This daemon can also derive custom filesystem permissions based on directory + * structure when requested. These custom permissions support several features: + * + * - Apps can access their own files in /Android/data/com.example/ without + * requiring any additional GIDs. + * - Separate permissions for protecting directories like Pictures and Music. + * - Multi-user separation on the same physical device. + * + * The derived permissions look like this: + * + * rwxrwx--x root:sdcard_rw / + * rwxrwx--- root:sdcard_pics /Pictures + * rwxrwx--- root:sdcard_av /Music + * + * rwxrwx--x root:sdcard_rw /Android + * rwxrwx--x root:sdcard_rw /Android/data + * rwxrwx--- u0_a12:sdcard_rw /Android/data/com.example + * rwxrwx--x root:sdcard_rw /Android/obb/ + * rwxrwx--- u0_a12:sdcard_rw /Android/obb/com.example + * + * rwxrwx--- root:sdcard_all /Android/user + * rwxrwx--x root:sdcard_rw /Android/user/10 + * rwxrwx--- u10_a12:sdcard_rw /Android/user/10/Android/data/com.example */ #define FUSE_TRACE 0 @@ -89,6 +117,23 @@ * or that a reply has already been written. */ #define NO_STATUS 1 +/* Path to system-provided mapping of package name to appIds */ +static const char* const kPackagesListFile = "/data/system/packages.list"; + +/* Supplementary groups to execute with */ +static const gid_t kGroups[1] = { AID_PACKAGE_INFO }; + +/* Permission mode for a specific node. Controls how file permissions + * are derived for children nodes. */ +typedef enum { + PERM_INHERIT, + PERM_ROOT, + PERM_ANDROID, + PERM_ANDROID_DATA, + PERM_ANDROID_OBB, + PERM_ANDROID_USER, +} perm_t; + struct handle { int fd; }; @@ -97,11 +142,22 @@ struct dirhandle { DIR *d; }; +struct package { + appid_t appid; +}; + struct node { __u32 refcount; __u64 nid; __u64 gen; + /* State derived based on current position in hierarchy. */ + perm_t perm; + userid_t userid; + uid_t uid; + gid_t gid; + mode_t mode; + struct node *next; /* per-dir sibling list */ struct node *child; /* first contained file by this dir */ struct node *parent; /* containing directory */ @@ -116,14 +172,25 @@ struct node { char *actual_name; }; +static int str_hash(void *key) { + return hashmapHash(key, strlen(key)); +} + +static bool str_equals(void *keyA, void *keyB) { + return strcmp(keyA, keyB) == 0; +} + /* Global data structure shared by all fuse handlers. */ struct fuse { pthread_mutex_t lock; __u64 next_generation; int fd; + bool derive_perms; struct node root; char rootpath[PATH_MAX]; + + Hashmap *package_to_appid; }; /* Private data used by a single fuse handler. */ @@ -258,7 +325,7 @@ static char* find_file_within(const char* path, const char* name, struct dirent* entry; DIR* dir = opendir(path); if (!dir) { - ERROR("opendir %s failed: %s", path, strerror(errno)); + ERROR("opendir %s failed: %s\n", path, strerror(errno)); return actual; } while ((entry = readdir(dir))) { @@ -273,9 +340,9 @@ static char* find_file_within(const char* path, const char* name, return actual; } -static void attr_from_stat(struct fuse_attr *attr, const struct stat *s, __u64 nid) +static void attr_from_stat(struct fuse_attr *attr, const struct stat *s, const struct node* node) { - attr->ino = nid; + attr->ino = node->nid; attr->size = s->st_size; attr->blocks = s->st_blocks; attr->atime = s->st_atime; @@ -287,19 +354,89 @@ static void attr_from_stat(struct fuse_attr *attr, const struct stat *s, __u64 n attr->mode = s->st_mode; attr->nlink = s->st_nlink; - /* force permissions to something reasonable: - * world readable - * writable by the sdcard group - */ - if (attr->mode & 0100) { - attr->mode = (attr->mode & (~0777)) | 0775; - } else { - attr->mode = (attr->mode & (~0777)) | 0664; + attr->uid = node->uid; + attr->gid = node->gid; + + /* Filter requested mode based on underlying file, and + * pass through file type. */ + int owner_mode = s->st_mode & 0700; + int filtered_mode = node->mode & (owner_mode | (owner_mode >> 3) | (owner_mode >> 6)); + attr->mode = (attr->mode & S_IFMT) | filtered_mode; +} + +static void derive_permissions_locked(struct fuse* fuse, struct node *parent, + struct node *node) { + struct package* package; + + /* By default, each node inherits from its parent */ + node->perm = PERM_INHERIT; + node->userid = parent->userid; + node->uid = parent->uid; + node->gid = parent->gid; + node->mode = parent->mode; + + if (!fuse->derive_perms) { + return; } - /* all files owned by root.sdcard */ - attr->uid = 0; - attr->gid = AID_SDCARD_RW; + /* Derive custom permissions based on parent and current node */ + switch (parent->perm) { + case PERM_INHERIT: + /* Already inherited above */ + break; + case PERM_ROOT: + if (!strcmp(node->name, "Android")) { + /* App-specific directories inside; let anyone traverse */ + node->perm = PERM_ANDROID; + node->mode = 0771; + } else if (!strcmp(node->name, "DCIM") + || !strcmp(node->name, "Pictures")) { + node->gid = AID_SDCARD_PICS; + node->mode = 0770; + } else if (!strcmp(node->name, "Alarms") + || !strcmp(node->name, "Movies") + || !strcmp(node->name, "Music") + || !strcmp(node->name, "Notifications") + || !strcmp(node->name, "Podcasts") + || !strcmp(node->name, "Ringtones")) { + node->gid = AID_SDCARD_AV; + node->mode = 0770; + } + break; + case PERM_ANDROID: + if (!strcmp(node->name, "data")) { + /* App-specific directories inside; let anyone traverse */ + node->perm = PERM_ANDROID_DATA; + node->mode = 0771; + } else if (!strcmp(node->name, "obb")) { + /* App-specific directories inside; let anyone traverse */ + node->perm = PERM_ANDROID_OBB; + node->mode = 0771; + } else if (!strcmp(node->name, "user")) { + /* User directories must only be accessible to system, protected + * by sdcard_all. Zygote will bind mount the appropriate user- + * specific path. */ + node->perm = PERM_ANDROID_USER; + node->gid = AID_SDCARD_ALL; + node->mode = 0770; + } + break; + case PERM_ANDROID_DATA: + case PERM_ANDROID_OBB: + package = hashmapGet(fuse->package_to_appid, node->name); + if (package != NULL) { + node->uid = multiuser_get_uid(parent->userid, package->appid); + } + node->mode = 0770; + break; + case PERM_ANDROID_USER: + /* Root of a secondary user */ + node->perm = PERM_ROOT; + node->userid = atoi(node->name); + node->gid = AID_SDCARD_RW; + node->mode = 0771; + break; + } } struct node *create_node_locked(struct fuse* fuse, @@ -330,6 +467,8 @@ struct node *create_node_locked(struct fuse* fuse, node->namelen = namelen; node->nid = ptr_to_id(node); node->gen = fuse->next_generation++; + + derive_permissions_locked(fuse, parent, node); acquire_node_locked(node); add_node_to_parent_locked(node, parent); return node; @@ -422,18 +561,33 @@ static struct node* acquire_or_create_child_locked( return child; } -static void fuse_init(struct fuse *fuse, int fd, const char *source_path) +static void fuse_init(struct fuse *fuse, int fd, const char *source_path, bool derive_perms) { pthread_mutex_init(&fuse->lock, NULL); fuse->fd = fd; fuse->next_generation = 0; + fuse->derive_perms = derive_perms; memset(&fuse->root, 0, sizeof(fuse->root)); fuse->root.nid = FUSE_ROOT_ID; /* 1 */ fuse->root.refcount = 2; fuse->root.namelen = strlen(source_path); fuse->root.name = strdup(source_path); + + fuse->root.perm = PERM_ROOT; + fuse->root.userid = 0; + fuse->root.uid = AID_ROOT; + fuse->root.gid = AID_SDCARD_RW; + if (derive_perms) { + fuse->root.mode = 0771; + } else { + fuse->root.mode = 0775; + } + + if (derive_perms) { + fuse->package_to_appid = hashmapCreate(256, str_hash, str_equals); + } } static void fuse_status(struct fuse *fuse, __u64 unique, int err) @@ -475,7 +629,36 @@ static int fuse_reply_entry(struct fuse* fuse, __u64 unique, struct stat s; if (lstat(path, &s) < 0) { - return -errno; + /* But wait! We'll automatically create a directory if its + * a valid package name under data or obb, since apps may not + * have enough permissions to create for themselves. */ + if (errno == ENOENT && (parent->perm == PERM_ANDROID_DATA + || parent->perm == PERM_ANDROID_OBB)) { + TRACE("automatically creating %s\n", path); + + pthread_mutex_lock(&fuse->lock); + bool validPackage = hashmapContainsKey(fuse->package_to_appid, name); + pthread_mutex_unlock(&fuse->lock); + + if (!validPackage) { + return -ENOENT; + } + if (mkdir(path, 0775) == -1) { + /* We might have raced with ourselves and already created */ + if (errno != EEXIST) { + ERROR("failed to mkdir(%s): %s\n", name, strerror(errno)); + return -ENOENT; + } + } + + /* It should exist this time around! */ + if (lstat(path, &s) < 0) { + ERROR("failed to lstat(%s): %s\n", name, strerror(errno)); + return -errno; + } + } else { + return -errno; + } } pthread_mutex_lock(&fuse->lock); @@ -485,7 +668,7 @@ static int fuse_reply_entry(struct fuse* fuse, __u64 unique, return -ENOMEM; } memset(&out, 0, sizeof(out)); - attr_from_stat(&out.attr, &s, node->nid); + attr_from_stat(&out.attr, &s, node); out.attr_valid = 10; out.entry_valid = 10; out.nodeid = node->nid; @@ -495,7 +678,7 @@ static int fuse_reply_entry(struct fuse* fuse, __u64 unique, return NO_STATUS; } -static int fuse_reply_attr(struct fuse* fuse, __u64 unique, __u64 nid, +static int fuse_reply_attr(struct fuse* fuse, __u64 unique, const struct node* node, const char* path) { struct fuse_attr_out out; @@ -505,7 +688,7 @@ static int fuse_reply_attr(struct fuse* fuse, __u64 unique, __u64 nid, return -errno; } memset(&out, 0, sizeof(out)); - attr_from_stat(&out.attr, &s, nid); + attr_from_stat(&out.attr, &s, node); out.attr_valid = 10; fuse_reply(fuse, unique, &out, sizeof(out)); return NO_STATUS; @@ -567,7 +750,7 @@ static int handle_getattr(struct fuse* fuse, struct fuse_handler* handler, if (!node) { return -ENOENT; } - return fuse_reply_attr(fuse, hdr->unique, hdr->nodeid, path); + return fuse_reply_attr(fuse, hdr->unique, node, path); } static int handle_setattr(struct fuse* fuse, struct fuse_handler* handler, @@ -625,7 +808,7 @@ static int handle_setattr(struct fuse* fuse, struct fuse_handler* handler, return -errno; } } - return fuse_reply_attr(fuse, hdr->unique, hdr->nodeid, path); + return fuse_reply_attr(fuse, hdr->unique, node, path); } static int handle_mknod(struct fuse* fuse, struct fuse_handler* handler, @@ -1206,6 +1389,111 @@ static void* start_handler(void* data) return NULL; } +static bool hashmap_remove(void *key, void *value, void *context) { + Hashmap* map = context; + hashmapRemove(map, key); + free(key); + free(value); + return true; +} + +static int read_package_list(struct fuse *fuse) { + pthread_mutex_lock(&fuse->lock); + + hashmapForEach(fuse->package_to_appid, hashmap_remove, fuse->package_to_appid); + + FILE* file = fopen(kPackagesListFile, "r"); + if (!file) { + ERROR("failed to open package list: %s\n", strerror(errno)); + pthread_mutex_unlock(&fuse->lock); + return -1; + } + + char buf[512]; + while (fgets(buf, sizeof(buf), file) != NULL) { + char package_name[512]; + int appid; + if (sscanf(buf, "%s %d", package_name, &appid) == 2) { + char* package_name_dup = strdup(package_name); + struct package* package = malloc(sizeof(struct package)); + if (!package_name_dup || !package) { + ERROR("cannot allocate package details\n"); + return -ENOMEM; + } + + package->appid = appid; + hashmapPut(fuse->package_to_appid, package_name_dup, package); + } + } + + TRACE("read_package_list: found %d packages\n", hashmapSize(fuse->package_to_appid)); + fclose(file); + pthread_mutex_unlock(&fuse->lock); + return 0; +} + +static void watch_package_list(struct fuse* fuse) { + struct inotify_event *event; + char event_buf[512]; + + int nfd = inotify_init(); + if (nfd < 0) { + ERROR("inotify_init failed: %s\n", strerror(errno)); + return; + } + + bool active = false; + while (1) { + if (!active) { + int res = inotify_add_watch(nfd, kPackagesListFile, IN_DELETE_SELF); + if (res == -1) { + if (errno == ENOENT || errno == EACCES) { + /* Framework may not have created yet, sleep and retry */ + ERROR("missing packages.list; retrying\n"); + sleep(3); + continue; + } else { + ERROR("inotify_add_watch failed: %s\n", strerror(errno)); + return; + } + } + + /* Watch above will tell us about any future changes, so + * read the current state. */ + if (read_package_list(fuse) == -1) { + ERROR("read_package_list failed: %s\n", strerror(errno)); + return; + } + active = true; + } + + int event_pos = 0; + int res = read(nfd, event_buf, sizeof(event_buf)); + if (res < (int) sizeof(*event)) { + if (errno == EINTR) + continue; + ERROR("failed to read inotify event: %s\n", strerror(errno)); + return; + } + + while (res >= (int) sizeof(*event)) { + int event_size; + event = (struct inotify_event *) (event_buf + event_pos); + + TRACE("inotify event: %08x\n", event->mask); + if ((event->mask & IN_IGNORED) == IN_IGNORED) { + /* Previously watched file was deleted, probably due to move + * that swapped in new data; re-arm the watch and read. */ + active = false; + } + + event_size = sizeof(*event) + event->len; + res -= event_size; + event_pos += event_size; + } + } +} + static int ignite_fuse(struct fuse* fuse, int num_threads) { struct fuse_handler* handlers; @@ -1213,7 +1501,7 @@ static int ignite_fuse(struct fuse* fuse, int num_threads) handlers = malloc(num_threads * sizeof(struct fuse_handler)); if (!handlers) { - ERROR("cannot allocate storage for threads"); + ERROR("cannot allocate storage for threads\n"); return -ENOMEM; } @@ -1222,16 +1510,25 @@ static int ignite_fuse(struct fuse* fuse, int num_threads) handlers[i].token = i; } - for (i = 1; i < num_threads; i++) { + /* When deriving permissions, this thread is used to process inotify events, + * otherwise it becomes one of the FUSE handlers. */ + i = fuse->derive_perms ? 0 : 1; + for (; i < num_threads; i++) { pthread_t thread; int res = pthread_create(&thread, NULL, start_handler, &handlers[i]); if (res) { - ERROR("failed to start thread #%d, error=%d", i, res); + ERROR("failed to start thread #%d, error=%d\n", i, res); goto quit; } } - handle_fuse_requests(&handlers[0]); - ERROR("terminated prematurely"); + + if (fuse->derive_perms) { + watch_package_list(fuse); + } else { + handle_fuse_requests(&handlers[0]); + } + + ERROR("terminated prematurely\n"); /* don't bother killing all of the other threads or freeing anything, * should never get here anyhow */ @@ -1241,14 +1538,17 @@ quit: static int usage() { - ERROR("usage: sdcard [-t] \n" - " -t: specify number of threads to use, default -t%d\n" + ERROR("usage: sdcard [OPTIONS] \n" + " -u: specify UID to run as\n" + " -g: specify GID to run as\n" + " -t: specify number of threads to use (default %d)\n" + " -d: derive file permissions based on path\n" "\n", DEFAULT_NUM_THREADS); return 1; } static int run(const char* source_path, const char* dest_path, uid_t uid, gid_t gid, - int num_threads) { + int num_threads, bool derive_perms) { int fd; char opts[256]; int res; @@ -1259,7 +1559,7 @@ static int run(const char* source_path, const char* dest_path, uid_t uid, gid_t fd = open("/dev/fuse", O_RDWR); if (fd < 0){ - ERROR("cannot open fuse device (error %d)\n", errno); + ERROR("cannot open fuse device: %s\n", strerror(errno)); return -1; } @@ -1269,23 +1569,29 @@ static int run(const char* source_path, const char* dest_path, uid_t uid, gid_t res = mount("/dev/fuse", dest_path, "fuse", MS_NOSUID | MS_NODEV, opts); if (res < 0) { - ERROR("cannot mount fuse filesystem (error %d)\n", errno); + ERROR("cannot mount fuse filesystem: %s\n", strerror(errno)); + goto error; + } + + res = setgroups(sizeof(kGroups) / sizeof(kGroups[0]), kGroups); + if (res < 0) { + ERROR("cannot setgroups: %s\n", strerror(errno)); goto error; } res = setgid(gid); if (res < 0) { - ERROR("cannot setgid (error %d)\n", errno); + ERROR("cannot setgid: %s\n", strerror(errno)); goto error; } res = setuid(uid); if (res < 0) { - ERROR("cannot setuid (error %d)\n", errno); + ERROR("cannot setuid: %s\n", strerror(errno)); goto error; } - fuse_init(&fuse, fd, source_path); + fuse_init(&fuse, fd, source_path, derive_perms); umask(0); res = ignite_fuse(&fuse, num_threads); @@ -1306,33 +1612,41 @@ int main(int argc, char **argv) uid_t uid = 0; gid_t gid = 0; int num_threads = DEFAULT_NUM_THREADS; + bool derive_perms = false; int i; struct rlimit rlim; - for (i = 1; i < argc; i++) { + int opt; + while ((opt = getopt(argc, argv, "u:g:t:d")) != -1) { + switch (opt) { + case 'u': + uid = strtoul(optarg, NULL, 10); + break; + case 'g': + gid = strtoul(optarg, NULL, 10); + break; + case 't': + num_threads = strtoul(optarg, NULL, 10); + break; + case 'd': + derive_perms = true; + break; + case '?': + default: + return usage(); + } + } + + for (i = optind; i < argc; i++) { char* arg = argv[i]; - if (!strncmp(arg, "-t", 2)) - num_threads = strtoul(arg + 2, 0, 10); - else if (!source_path) + if (!source_path) { source_path = arg; - else if (!dest_path) + } else if (!dest_path) { dest_path = arg; - else if (!uid) { - char* endptr = NULL; - errno = 0; - uid = strtoul(arg, &endptr, 10); - if (*endptr != '\0' || errno != 0) { - ERROR("Invalid uid"); - return usage(); - } + } else if (!uid) { + uid = strtoul(arg, NULL, 10); } else if (!gid) { - char* endptr = NULL; - errno = 0; - gid = strtoul(arg, &endptr, 10); - if (*endptr != '\0' || errno != 0) { - ERROR("Invalid gid"); - return usage(); - } + gid = strtoul(arg, NULL, 10); } else { ERROR("too many arguments\n"); return usage(); @@ -1362,6 +1676,6 @@ int main(int argc, char **argv) ERROR("Error setting RLIMIT_NOFILE, errno = %d\n", errno); } - res = run(source_path, dest_path, uid, gid, num_threads); + res = run(source_path, dest_path, uid, gid, num_threads, derive_perms); return res < 0 ? 1 : 0; } From 57fd0989cbb59f305514378e511e36c752b5aa20 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Fri, 9 Aug 2013 12:22:07 -0700 Subject: [PATCH 501/541] healthd: preserve uA and uAh units for current_now and charge_counter Change-Id: I20b3f47c541c4321b0593e504090ad6573b9cb01 --- healthd/BatteryMonitor.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp index 8492dce59..8ff53575b 100644 --- a/healthd/BatteryMonitor.cpp +++ b/healthd/BatteryMonitor.cpp @@ -190,10 +190,10 @@ bool BatteryMonitor::update(void) { props.batteryVoltage = getIntField(mBatteryVoltagePath) / 1000; if (!mBatteryCurrentNowPath.isEmpty()) - props.batteryCurrentNow = getIntField(mBatteryCurrentNowPath) / 1000; + props.batteryCurrentNow = getIntField(mBatteryCurrentNowPath); if (!mBatteryChargeCounterPath.isEmpty()) - props.batteryChargeCounter = getIntField(mBatteryChargeCounterPath) / 1000; + props.batteryChargeCounter = getIntField(mBatteryChargeCounterPath); props.batteryTemperature = getIntField(mBatteryTemperaturePath); @@ -255,7 +255,7 @@ bool BatteryMonitor::update(void) { if (!mBatteryCurrentNowPath.isEmpty()) { char b[20]; - snprintf(b, sizeof(b), " c=%d", props.batteryCurrentNow); + snprintf(b, sizeof(b), " c=%d", props.batteryCurrentNow / 1000); strlcat(dmesgline, b, sizeof(dmesgline)); } From a41611f638f82cb8066f83b32f2610f0d00dafc6 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Mon, 12 Aug 2013 12:17:32 -0700 Subject: [PATCH 502/541] healthd: fix botched logging call Change-Id: Ibf5e28c657e0d9d193b266acd7b2912bebc3e71f --- healthd/BatteryMonitor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp index 8ff53575b..74fb6a8ce 100644 --- a/healthd/BatteryMonitor.cpp +++ b/healthd/BatteryMonitor.cpp @@ -369,7 +369,7 @@ void BatteryMonitor::init(bool nosvcmgr) { if (mBatteryStatusPath.isEmpty()) KLOG_WARNING(LOG_TAG, "BatteryStatusPath not found\n"); if (mBatteryHealthPath.isEmpty()) - KLOG_WARNING("BatteryHealthPath not found\n"); + KLOG_WARNING(LOG_TAG, "BatteryHealthPath not found\n"); if (mBatteryPresentPath.isEmpty()) KLOG_WARNING(LOG_TAG, "BatteryPresentPath not found\n"); if (mBatteryCapacityPath.isEmpty()) From d65104ca31766922537c79ec2d7647584723407c Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Tue, 13 Aug 2013 15:50:42 -0700 Subject: [PATCH 503/541] healthd: switch to HAL static libraries Change-Id: I5a802f9b9a8ad4782c4d0c6376a068aef32a70cb --- healthd/Android.mk | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/healthd/Android.mk b/healthd/Android.mk index cff7a3b98..473d37531 100644 --- a/healthd/Android.mk +++ b/healthd/Android.mk @@ -3,6 +3,12 @@ ifneq ($(BUILD_TINY_ANDROID),true) LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_SRC_FILES := healthd_board_default.cpp +LOCAL_MODULE := libhealthd.default +include $(BUILD_STATIC_LIBRARY) + include $(CLEAR_VARS) LOCAL_SRC_FILES := \ @@ -17,12 +23,7 @@ LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT_SBIN) LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_SBIN_UNSTRIPPED) LOCAL_STATIC_LIBRARIES := libbatteryservice libbinder libz libutils libstdc++ libcutils liblog libm libc - -ifdef BOARD_LIB_HEALTHD -LOCAL_STATIC_LIBRARIES += $(BOARD_LIB_HEALTHD) -else -LOCAL_SRC_FILES += healthd_board_default.cpp -endif +LOCAL_HAL_STATIC_LIBRARIES := libhealthd include $(BUILD_EXECUTABLE) From f5d3012d2c6ac343a0b8bcc95b9cd5137d9ef02f Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Mon, 12 Aug 2013 17:03:35 -0700 Subject: [PATCH 504/541] healthd: Move power_supply attribute paths to healthd_config Allow health HAL to select specific paths to be used, overriding default search for arbitrary power supplies with the named paths. Change-Id: I5f724739f58ef56087ab592b7403fc083db8f173 --- healthd/BatteryMonitor.cpp | 166 ++++++++++++++++++++++--------------- healthd/BatteryMonitor.h | 14 +--- healthd/healthd.cpp | 11 ++- healthd/healthd.h | 34 +++++++- 4 files changed, 144 insertions(+), 81 deletions(-) diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp index 74fb6a8ce..688c7ff54 100644 --- a/healthd/BatteryMonitor.cpp +++ b/healthd/BatteryMonitor.cpp @@ -181,33 +181,33 @@ bool BatteryMonitor::update(void) { props.batteryCurrentNow = INT_MIN; props.batteryChargeCounter = INT_MIN; - if (!mBatteryPresentPath.isEmpty()) - props.batteryPresent = getBooleanField(mBatteryPresentPath); + if (!mHealthdConfig->batteryPresentPath.isEmpty()) + props.batteryPresent = getBooleanField(mHealthdConfig->batteryPresentPath); else props.batteryPresent = true; - props.batteryLevel = getIntField(mBatteryCapacityPath); - props.batteryVoltage = getIntField(mBatteryVoltagePath) / 1000; + props.batteryLevel = getIntField(mHealthdConfig->batteryCapacityPath); + props.batteryVoltage = getIntField(mHealthdConfig->batteryVoltagePath) / 1000; - if (!mBatteryCurrentNowPath.isEmpty()) - props.batteryCurrentNow = getIntField(mBatteryCurrentNowPath); + if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) + props.batteryCurrentNow = getIntField(mHealthdConfig->batteryCurrentNowPath); - if (!mBatteryChargeCounterPath.isEmpty()) - props.batteryChargeCounter = getIntField(mBatteryChargeCounterPath); + if (!mHealthdConfig->batteryChargeCounterPath.isEmpty()) + props.batteryChargeCounter = getIntField(mHealthdConfig->batteryChargeCounterPath); - props.batteryTemperature = getIntField(mBatteryTemperaturePath); + props.batteryTemperature = getIntField(mHealthdConfig->batteryTemperaturePath); const int SIZE = 128; char buf[SIZE]; String8 btech; - if (readFromFile(mBatteryStatusPath, buf, SIZE) > 0) + if (readFromFile(mHealthdConfig->batteryStatusPath, buf, SIZE) > 0) props.batteryStatus = getBatteryStatus(buf); - if (readFromFile(mBatteryHealthPath, buf, SIZE) > 0) + if (readFromFile(mHealthdConfig->batteryHealthPath, buf, SIZE) > 0) props.batteryHealth = getBatteryHealth(buf); - if (readFromFile(mBatteryTechnologyPath, buf, SIZE) > 0) + if (readFromFile(mHealthdConfig->batteryTechnologyPath, buf, SIZE) > 0) props.batteryTechnology = String8(buf); unsigned int i; @@ -252,7 +252,7 @@ bool BatteryMonitor::update(void) { abs(props.batteryTemperature % 10), props.batteryHealth, props.batteryStatus); - if (!mBatteryCurrentNowPath.isEmpty()) { + if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) { char b[20]; snprintf(b, sizeof(b), " c=%d", props.batteryCurrentNow / 1000); @@ -272,9 +272,10 @@ bool BatteryMonitor::update(void) { props.chargerWirelessOnline; } -void BatteryMonitor::init(bool nosvcmgr) { +void BatteryMonitor::init(struct healthd_config *hc, bool nosvcmgr) { String8 path; + mHealthdConfig = hc; DIR* dir = opendir(POWER_SUPPLY_SYSFS_PATH); if (dir == NULL) { KLOG_ERROR(LOG_TAG, "Could not open %s\n", POWER_SUPPLY_SYSFS_PATH); @@ -302,59 +303,92 @@ void BatteryMonitor::init(bool nosvcmgr) { break; case ANDROID_POWER_SUPPLY_TYPE_BATTERY: - path.clear(); - path.appendFormat("%s/%s/status", POWER_SUPPLY_SYSFS_PATH, name); - if (access(path, R_OK) == 0) - mBatteryStatusPath = path; - path.clear(); - path.appendFormat("%s/%s/health", POWER_SUPPLY_SYSFS_PATH, name); - if (access(path, R_OK) == 0) - mBatteryHealthPath = path; - path.clear(); - path.appendFormat("%s/%s/present", POWER_SUPPLY_SYSFS_PATH, name); - if (access(path, R_OK) == 0) - mBatteryPresentPath = path; - path.clear(); - path.appendFormat("%s/%s/capacity", POWER_SUPPLY_SYSFS_PATH, name); - if (access(path, R_OK) == 0) - mBatteryCapacityPath = path; - - path.clear(); - path.appendFormat("%s/%s/voltage_now", POWER_SUPPLY_SYSFS_PATH, name); - if (access(path, R_OK) == 0) { - mBatteryVoltagePath = path; - } else { + if (mHealthdConfig->batteryStatusPath.isEmpty()) { path.clear(); - path.appendFormat("%s/%s/batt_vol", POWER_SUPPLY_SYSFS_PATH, name); + path.appendFormat("%s/%s/status", POWER_SUPPLY_SYSFS_PATH, + name); if (access(path, R_OK) == 0) - mBatteryVoltagePath = path; + mHealthdConfig->batteryStatusPath = path; } - path.clear(); - path.appendFormat("%s/%s/current_now", POWER_SUPPLY_SYSFS_PATH, name); - if (access(path, R_OK) == 0) - mBatteryCurrentNowPath = path; - - path.clear(); - path.appendFormat("%s/%s/charge_counter", POWER_SUPPLY_SYSFS_PATH, name); - if (access(path, R_OK) == 0) - mBatteryChargeCounterPath = path; - - path.clear(); - path.appendFormat("%s/%s/temp", POWER_SUPPLY_SYSFS_PATH, name); - if (access(path, R_OK) == 0) { - mBatteryTemperaturePath = path; - } else { + if (mHealthdConfig->batteryHealthPath.isEmpty()) { path.clear(); - path.appendFormat("%s/%s/batt_temp", POWER_SUPPLY_SYSFS_PATH, name); + path.appendFormat("%s/%s/health", POWER_SUPPLY_SYSFS_PATH, + name); if (access(path, R_OK) == 0) - mBatteryTemperaturePath = path; + mHealthdConfig->batteryHealthPath = path; + } + + if (mHealthdConfig->batteryPresentPath.isEmpty()) { + path.clear(); + path.appendFormat("%s/%s/present", POWER_SUPPLY_SYSFS_PATH, + name); + if (access(path, R_OK) == 0) + mHealthdConfig->batteryPresentPath = path; + } + + if (mHealthdConfig->batteryCapacityPath.isEmpty()) { + path.clear(); + path.appendFormat("%s/%s/capacity", POWER_SUPPLY_SYSFS_PATH, + name); + if (access(path, R_OK) == 0) + mHealthdConfig->batteryCapacityPath = path; + } + + if (mHealthdConfig->batteryVoltagePath.isEmpty()) { + path.clear(); + path.appendFormat("%s/%s/voltage_now", + POWER_SUPPLY_SYSFS_PATH, name); + if (access(path, R_OK) == 0) { + mHealthdConfig->batteryVoltagePath = path; + } else { + path.clear(); + path.appendFormat("%s/%s/batt_vol", + POWER_SUPPLY_SYSFS_PATH, name); + if (access(path, R_OK) == 0) + mHealthdConfig->batteryVoltagePath = path; + } + } + + if (mHealthdConfig->batteryCurrentNowPath.isEmpty()) { + path.clear(); + path.appendFormat("%s/%s/current_now", + POWER_SUPPLY_SYSFS_PATH, name); + if (access(path, R_OK) == 0) + mHealthdConfig->batteryCurrentNowPath = path; + } + + if (mHealthdConfig->batteryChargeCounterPath.isEmpty()) { + path.clear(); + path.appendFormat("%s/%s/charge_counter", + POWER_SUPPLY_SYSFS_PATH, name); + if (access(path, R_OK) == 0) + mHealthdConfig->batteryChargeCounterPath = path; + } + + if (mHealthdConfig->batteryTemperaturePath.isEmpty()) { + path.clear(); + path.appendFormat("%s/%s/temp", POWER_SUPPLY_SYSFS_PATH, + name); + if (access(path, R_OK) == 0) { + mHealthdConfig->batteryTemperaturePath = path; + } else { + path.clear(); + path.appendFormat("%s/%s/batt_temp", + POWER_SUPPLY_SYSFS_PATH, name); + if (access(path, R_OK) == 0) + mHealthdConfig->batteryTemperaturePath = path; + } + } + + if (mHealthdConfig->batteryTechnologyPath.isEmpty()) { + path.clear(); + path.appendFormat("%s/%s/technology", + POWER_SUPPLY_SYSFS_PATH, name); + if (access(path, R_OK) == 0) + mHealthdConfig->batteryTechnologyPath = path; } - path.clear(); - path.appendFormat("%s/%s/technology", POWER_SUPPLY_SYSFS_PATH, name); - if (access(path, R_OK) == 0) - mBatteryTechnologyPath = path; break; case ANDROID_POWER_SUPPLY_TYPE_UNKNOWN: @@ -366,19 +400,19 @@ void BatteryMonitor::init(bool nosvcmgr) { if (!mChargerNames.size()) KLOG_ERROR(LOG_TAG, "No charger supplies found\n"); - if (mBatteryStatusPath.isEmpty()) + if (mHealthdConfig->batteryStatusPath.isEmpty()) KLOG_WARNING(LOG_TAG, "BatteryStatusPath not found\n"); - if (mBatteryHealthPath.isEmpty()) + if (mHealthdConfig->batteryHealthPath.isEmpty()) KLOG_WARNING(LOG_TAG, "BatteryHealthPath not found\n"); - if (mBatteryPresentPath.isEmpty()) + if (mHealthdConfig->batteryPresentPath.isEmpty()) KLOG_WARNING(LOG_TAG, "BatteryPresentPath not found\n"); - if (mBatteryCapacityPath.isEmpty()) + if (mHealthdConfig->batteryCapacityPath.isEmpty()) KLOG_WARNING(LOG_TAG, "BatteryCapacityPath not found\n"); - if (mBatteryVoltagePath.isEmpty()) + if (mHealthdConfig->batteryVoltagePath.isEmpty()) KLOG_WARNING(LOG_TAG, "BatteryVoltagePath not found\n"); - if (mBatteryTemperaturePath.isEmpty()) + if (mHealthdConfig->batteryTemperaturePath.isEmpty()) KLOG_WARNING(LOG_TAG, "BatteryTemperaturePath not found\n"); - if (mBatteryTechnologyPath.isEmpty()) + if (mHealthdConfig->batteryTechnologyPath.isEmpty()) KLOG_WARNING(LOG_TAG, "BatteryTechnologyPath not found\n"); if (nosvcmgr == false) { diff --git a/healthd/BatteryMonitor.h b/healthd/BatteryMonitor.h index ff1ea1ed3..ba291af1e 100644 --- a/healthd/BatteryMonitor.h +++ b/healthd/BatteryMonitor.h @@ -21,6 +21,7 @@ #include #include +#include "healthd.h" #include "BatteryPropertiesRegistrar.h" namespace android { @@ -38,20 +39,11 @@ class BatteryMonitor { ANDROID_POWER_SUPPLY_TYPE_BATTERY }; - void init(bool nosvcmgr); + void init(struct healthd_config *hc, bool nosvcmgr); bool update(void); private: - String8 mBatteryStatusPath; - String8 mBatteryHealthPath; - String8 mBatteryPresentPath; - String8 mBatteryCapacityPath; - String8 mBatteryVoltagePath; - String8 mBatteryTemperaturePath; - String8 mBatteryTechnologyPath; - String8 mBatteryCurrentNowPath; - String8 mBatteryChargeCounterPath; - + struct healthd_config *mHealthdConfig; Vector mChargerNames; sp mBatteryPropertiesRegistrar; diff --git a/healthd/healthd.cpp b/healthd/healthd.cpp index 158274c20..1719c22f6 100644 --- a/healthd/healthd.cpp +++ b/healthd/healthd.cpp @@ -41,6 +41,15 @@ using namespace android; static struct healthd_config healthd_config = { .periodic_chores_interval_fast = DEFAULT_PERIODIC_CHORES_INTERVAL_FAST, .periodic_chores_interval_slow = DEFAULT_PERIODIC_CHORES_INTERVAL_SLOW, + .batteryStatusPath = String8(String8::kEmptyString), + .batteryHealthPath = String8(String8::kEmptyString), + .batteryPresentPath = String8(String8::kEmptyString), + .batteryCapacityPath = String8(String8::kEmptyString), + .batteryVoltagePath = String8(String8::kEmptyString), + .batteryTemperaturePath = String8(String8::kEmptyString), + .batteryTechnologyPath = String8(String8::kEmptyString), + .batteryCurrentNowPath = String8(String8::kEmptyString), + .batteryChargeCounterPath = String8(String8::kEmptyString), }; #define POWER_SUPPLY_SUBSYSTEM "power_supply" @@ -266,7 +275,7 @@ int main(int argc, char **argv) { uevent_init(); binder_init(); gBatteryMonitor = new BatteryMonitor(); - gBatteryMonitor->init(nosvcmgr); + gBatteryMonitor->init(&healthd_config, nosvcmgr); healthd_mainloop(); return 0; diff --git a/healthd/healthd.h b/healthd/healthd.h index 895be40dc..5374fb15a 100644 --- a/healthd/healthd.h +++ b/healthd/healthd.h @@ -18,6 +18,7 @@ #define _HEALTHD_H_ #include +#include // periodic_chores_interval_fast, periodic_chores_interval_slow: intervals at // which healthd wakes up to poll health state and perform periodic chores, @@ -32,18 +33,45 @@ // not connected to a charger (to watch for a battery drained to zero // remaining capacity). The default value is 600 (10 minutes). Value -1 // tuns off periodic chores (and wakeups) in these conditions. +// +// power_supply sysfs attribute file paths. Set these to specific paths +// to use for the associated battery parameters. healthd will search for +// appropriate power_supply attribute files to use for any paths left empty: +// +// batteryStatusPath: charging status (POWER_SUPPLY_PROP_STATUS) +// batteryHealthPath: battery health (POWER_SUPPLY_PROP_HEALTH) +// batteryPresentPath: battery present (POWER_SUPPLY_PROP_PRESENT) +// batteryCapacityPath: remaining capacity (POWER_SUPPLY_PROP_CAPACITY) +// batteryVoltagePath: battery voltage (POWER_SUPPLY_PROP_VOLTAGE_NOW) +// batteryTemperaturePath: battery temperature (POWER_SUPPLY_PROP_TEMP) +// batteryTechnologyPath: battery technology (POWER_SUPPLY_PROP_TECHNOLOGY) +// batteryCurrentNowPath: battery current (POWER_SUPPLY_PROP_CURRENT_NOW) +// batteryChargeCounterPath: battery accumulated charge +// (POWER_SUPPLY_PROP_CHARGE_COUNTER) struct healthd_config { int periodic_chores_interval_fast; int periodic_chores_interval_slow; + + android::String8 batteryStatusPath; + android::String8 batteryHealthPath; + android::String8 batteryPresentPath; + android::String8 batteryCapacityPath; + android::String8 batteryVoltagePath; + android::String8 batteryTemperaturePath; + android::String8 batteryTechnologyPath; + android::String8 batteryCurrentNowPath; + android::String8 batteryChargeCounterPath; }; // The following are implemented in libhealthd_board to handle board-specific // behavior. // -// -// To use the default values, this function can simply return without -// modifying the parameters. +// healthd_board_init() is called at startup time to modify healthd's +// configuration according to board-specific requirements. config +// points to the healthd configuration values described above. To use default +// values, this function can simply return without modifying the fields of the +// config parameter. void healthd_board_init(struct healthd_config *config); From a4b4e9ad8e35ab424e61d76ebe6654445fc61e63 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Fri, 2 Aug 2013 05:58:37 +0900 Subject: [PATCH 505/541] Parse IP address changes in NetlinkEvent. This adds support for parsing RTM_NEWADDR and RTM_DELADDR netlink messages received on netd's netlink socket and formatting them them so NetlinkHandler can process them. Address changes are notified in subsystem "address". Interface changes, which used to be notified in subsystem "net", are now notified in subsystem "interface" to avoid confusion. Bug: 10232006 Change-Id: I93a50e8de17014e118a42f5cc0eb90a0cbfa73cc --- include/sysutils/NetlinkEvent.h | 1 + libsysutils/src/NetlinkEvent.cpp | 117 ++++++++++++++++++++++++++++++- 2 files changed, 117 insertions(+), 1 deletion(-) diff --git a/include/sysutils/NetlinkEvent.h b/include/sysutils/NetlinkEvent.h index 25a56f771..2a734cb89 100644 --- a/include/sysutils/NetlinkEvent.h +++ b/include/sysutils/NetlinkEvent.h @@ -47,6 +47,7 @@ public: void dump(); protected: + bool parseIfAddrMessage(int type, struct ifaddrmsg *ifaddr, int rtasize); bool parseBinaryNetlinkMessage(char *buffer, int size); bool parseAsciiNetlinkMessage(char *buffer, int size); }; diff --git a/libsysutils/src/NetlinkEvent.cpp b/libsysutils/src/NetlinkEvent.cpp index 4beebb73d..b6da72f4c 100644 --- a/libsysutils/src/NetlinkEvent.cpp +++ b/libsysutils/src/NetlinkEvent.cpp @@ -23,6 +23,10 @@ #include #include +#include +#include +#include + #include #include #include @@ -69,6 +73,102 @@ void NetlinkEvent::dump() { } } +/* + * Decode a RTM_NEWADDR or RTM_DELADDR message. + */ +bool NetlinkEvent::parseIfAddrMessage(int type, struct ifaddrmsg *ifaddr, + int rtasize) { + struct rtattr *rta = IFA_RTA(ifaddr); + struct ifa_cacheinfo *cacheinfo = NULL; + char addrstr[INET6_ADDRSTRLEN] = ""; + + // Sanity check. + if (type != RTM_NEWADDR && type != RTM_DELADDR) { + SLOGE("parseIfAddrMessage on incorrect message type 0x%x\n", type); + return false; + } + + // For log messages. + const char *msgtype = (type == RTM_NEWADDR) ? "RTM_NEWADDR" : "RTM_DELADDR"; + + while(RTA_OK(rta, rtasize)) { + if (rta->rta_type == IFA_ADDRESS) { + // Only look at the first address, because we only support notifying + // one change at a time. + if (*addrstr != '\0') { + SLOGE("Multiple IFA_ADDRESSes in %s, ignoring\n", msgtype); + continue; + } + + // Convert the IP address to a string. + if (ifaddr->ifa_family == AF_INET) { + struct in_addr *addr4 = (struct in_addr *) RTA_DATA(rta); + if (RTA_PAYLOAD(rta) < sizeof(*addr4)) { + SLOGE("Short IPv4 address (%d bytes) in %s", + RTA_PAYLOAD(rta), msgtype); + continue; + } + inet_ntop(AF_INET, addr4, addrstr, sizeof(addrstr)); + } else if (ifaddr->ifa_family == AF_INET6) { + struct in6_addr *addr6 = (struct in6_addr *) RTA_DATA(rta); + if (RTA_PAYLOAD(rta) < sizeof(*addr6)) { + SLOGE("Short IPv6 address (%d bytes) in %s", + RTA_PAYLOAD(rta), msgtype); + continue; + } + inet_ntop(AF_INET6, addr6, addrstr, sizeof(addrstr)); + } else { + SLOGE("Unknown address family %d\n", ifaddr->ifa_family); + continue; + } + + // Find the interface name. + char ifname[IFNAMSIZ + 1]; + if (!if_indextoname(ifaddr->ifa_index, ifname)) { + SLOGE("Unknown ifindex %d in %s", ifaddr->ifa_index, msgtype); + return false; + } + + // Fill in interface information. + mAction = (type == RTM_NEWADDR) ? NlActionAdd : NlActionRemove; + mSubsystem = strdup("address"); + asprintf(&mParams[0], "ADDRESS=%s/%d", addrstr, + ifaddr->ifa_prefixlen); + asprintf(&mParams[1], "IFACE=%s", ifname); + asprintf(&mParams[2], "FLAGS=%u", ifaddr->ifa_flags); + asprintf(&mParams[3], "SCOPE=%u", ifaddr->ifa_scope); + } else if (rta->rta_type == IFA_CACHEINFO) { + // Address lifetime information. + if (cacheinfo) { + // We only support one address. + SLOGE("Multiple IFA_CACHEINFOs in %s, ignoring\n", msgtype); + continue; + } + + if (RTA_PAYLOAD(rta) < sizeof(*cacheinfo)) { + SLOGE("Short IFA_CACHEINFO (%d vs. %d bytes) in %s", + RTA_PAYLOAD(rta), sizeof(cacheinfo), msgtype); + continue; + } + + cacheinfo = (struct ifa_cacheinfo *) RTA_DATA(rta); + asprintf(&mParams[4], "PREFERRED=%u", cacheinfo->ifa_prefered); + asprintf(&mParams[5], "VALID=%u", cacheinfo->ifa_valid); + asprintf(&mParams[6], "CSTAMP=%u", cacheinfo->cstamp); + asprintf(&mParams[7], "TSTAMP=%u", cacheinfo->tstamp); + } + + rta = RTA_NEXT(rta, rtasize); + } + + if (addrstr[0] == '\0') { + SLOGE("No IFA_ADDRESS in %s\n", msgtype); + return false; + } + + return true; +} + /* * Parse an binary message from a NETLINK_ROUTE netlink socket. */ @@ -105,7 +205,7 @@ bool NetlinkEvent::parseBinaryNetlinkMessage(char *buffer, int size) { mParams[0] = strdup(buffer); mAction = (ifi->ifi_flags & IFF_LOWER_UP) ? NlActionLinkUp : NlActionLinkDown; - mSubsystem = strdup("net"); + mSubsystem = strdup("interface"); break; } @@ -127,6 +227,21 @@ bool NetlinkEvent::parseBinaryNetlinkMessage(char *buffer, int size) { mSubsystem = strdup("qlog"); mAction = NlActionChange; + } else if (nh->nlmsg_type == RTM_NEWADDR || + nh->nlmsg_type == RTM_DELADDR) { + int len = nh->nlmsg_len - sizeof(*nh); + struct ifaddrmsg *ifa; + + if (sizeof(*ifa) > (size_t) len) { + SLOGE("Got a short RTM_xxxADDR message\n"); + continue; + } + + ifa = (ifaddrmsg *)NLMSG_DATA(nh); + size_t rtasize = IFA_PAYLOAD(nh); + if (!parseIfAddrMessage(nh->nlmsg_type, ifa, rtasize)) { + continue; + } } else { SLOGD("Unexpected netlink message. type=0x%x\n", nh->nlmsg_type); } From 977a9f3b1a05e6168e8245a1e2061225b68b2b41 Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Mon, 12 Aug 2013 20:23:49 -0700 Subject: [PATCH 506/541] Add legacy layout support to FUSE, enforce write. The legacy internal layout places users at the top-level of the filesystem, so handle with new PERM_LEGACY_PRE_ROOT when requested. Mirror single OBB directory between all users without requiring fancy bind mounts by letting a nodes graft in another part of the underlying tree. Move to everything having "sdcard_r" GID by default, and verify that calling apps hold "sdcard_rw" when performing mutations. Determines app group membership from new packages.list column. Flag to optionally enable sdcard_pics/sdcard_av permissions splitting. Flag to supply a default GID for all files. Ignore attempts to access security sensitive files. Fix run-as to check for new "package_info" GID. Change-Id: Id5f3680779109141c65fb8fa1daf56597f49ea0d --- run-as/package.c | 2 +- sdcard/sdcard.c | 332 +++++++++++++++++++++++++++++++++++++---------- 2 files changed, 265 insertions(+), 69 deletions(-) diff --git a/run-as/package.c b/run-as/package.c index 27fc1ebbb..4762c5f89 100644 --- a/run-as/package.c +++ b/run-as/package.c @@ -111,7 +111,7 @@ map_file(const char* filename, size_t* filesize) goto EXIT; /* Ensure that the file is owned by the system user */ - if ((st.st_uid != AID_SYSTEM) || (st.st_gid != AID_SYSTEM)) { + if ((st.st_uid != AID_SYSTEM) || (st.st_gid != AID_PACKAGE_INFO)) { goto EXIT; } diff --git a/sdcard/sdcard.c b/sdcard/sdcard.c index 377c008df..44274153c 100644 --- a/sdcard/sdcard.c +++ b/sdcard/sdcard.c @@ -126,14 +126,30 @@ static const gid_t kGroups[1] = { AID_PACKAGE_INFO }; /* Permission mode for a specific node. Controls how file permissions * are derived for children nodes. */ typedef enum { + /* Nothing special; this node should just inherit from its parent. */ PERM_INHERIT, + /* This node is one level above a normal root; used for legacy layouts + * which use the first level to represent user_id. */ + PERM_LEGACY_PRE_ROOT, + /* This node is "/" */ PERM_ROOT, + /* This node is "/Android" */ PERM_ANDROID, + /* This node is "/Android/data" */ PERM_ANDROID_DATA, + /* This node is "/Android/obb" */ PERM_ANDROID_OBB, + /* This node is "/Android/user" */ PERM_ANDROID_USER, } perm_t; +/* Permissions structure to derive */ +typedef enum { + DERIVE_NONE, + DERIVE_LEGACY, + DERIVE_UNIFIED, +} derive_t; + struct handle { int fd; }; @@ -142,10 +158,6 @@ struct dirhandle { DIR *d; }; -struct package { - appid_t appid; -}; - struct node { __u32 refcount; __u64 nid; @@ -170,6 +182,11 @@ struct node { * namelen for both fields. */ char *actual_name; + + /* If non-null, an exact underlying path that should be grafted into this + * position. Used to support things like OBB. */ + char* graft_path; + size_t graft_pathlen; }; static int str_hash(void *key) { @@ -180,17 +197,27 @@ static bool str_equals(void *keyA, void *keyB) { return strcmp(keyA, keyB) == 0; } +static int int_hash(void *key) { + return (int) key; +} + +static bool int_equals(void *keyA, void *keyB) { + return keyA == keyB; +} + /* Global data structure shared by all fuse handlers. */ struct fuse { pthread_mutex_t lock; __u64 next_generation; int fd; - bool derive_perms; + derive_t derive; + bool split_perms; struct node root; - char rootpath[PATH_MAX]; + char obbpath[PATH_MAX]; - Hashmap *package_to_appid; + Hashmap* package_to_appid; + Hashmap* appid_with_rw; }; /* Private data used by a single fuse handler. */ @@ -275,15 +302,26 @@ static void remove_node_from_parent_locked(struct node* node) * Populates 'buf' with the path and returns the length of the path on success, * or returns -1 if the path is too long for the provided buffer. */ -static ssize_t get_node_path_locked(struct node* node, char* buf, size_t bufsize) -{ - size_t namelen = node->namelen; +static ssize_t get_node_path_locked(struct node* node, char* buf, size_t bufsize) { + const char* name; + size_t namelen; + if (node->graft_path) { + name = node->graft_path; + namelen = node->graft_pathlen; + } else if (node->actual_name) { + name = node->actual_name; + namelen = node->namelen; + } else { + name = node->name; + namelen = node->namelen; + } + if (bufsize < namelen + 1) { return -1; } ssize_t pathlen = 0; - if (node->parent) { + if (node->parent && node->graft_path == NULL) { pathlen = get_node_path_locked(node->parent, buf, bufsize - namelen - 2); if (pathlen < 0) { return -1; @@ -291,7 +329,6 @@ static ssize_t get_node_path_locked(struct node* node, char* buf, size_t bufsize buf[pathlen++] = '/'; } - const char* name = node->actual_name ? node->actual_name : node->name; memcpy(buf + pathlen, name, namelen + 1); /* include trailing \0 */ return pathlen + namelen; } @@ -366,7 +403,7 @@ static void attr_from_stat(struct fuse_attr *attr, const struct stat *s, const s static void derive_permissions_locked(struct fuse* fuse, struct node *parent, struct node *node) { - struct package* package; + appid_t appid; /* By default, each node inherits from its parent */ node->perm = PERM_INHERIT; @@ -375,7 +412,7 @@ static void derive_permissions_locked(struct fuse* fuse, struct node *parent, node->gid = parent->gid; node->mode = parent->mode; - if (!fuse->derive_perms) { + if (fuse->derive == DERIVE_NONE) { return; } @@ -384,23 +421,30 @@ static void derive_permissions_locked(struct fuse* fuse, struct node *parent, case PERM_INHERIT: /* Already inherited above */ break; + case PERM_LEGACY_PRE_ROOT: + /* Legacy internal layout places users at top level */ + node->perm = PERM_ROOT; + node->userid = strtoul(node->name, NULL, 10); + break; case PERM_ROOT: + /* Assume masked off by default. */ + node->mode = 0770; if (!strcmp(node->name, "Android")) { /* App-specific directories inside; let anyone traverse */ node->perm = PERM_ANDROID; node->mode = 0771; - } else if (!strcmp(node->name, "DCIM") - || !strcmp(node->name, "Pictures")) { - node->gid = AID_SDCARD_PICS; - node->mode = 0770; - } else if (!strcmp(node->name, "Alarms") - || !strcmp(node->name, "Movies") - || !strcmp(node->name, "Music") - || !strcmp(node->name, "Notifications") - || !strcmp(node->name, "Podcasts") - || !strcmp(node->name, "Ringtones")) { - node->gid = AID_SDCARD_AV; - node->mode = 0770; + } else if (fuse->split_perms) { + if (!strcmp(node->name, "DCIM") + || !strcmp(node->name, "Pictures")) { + node->gid = AID_SDCARD_PICS; + } else if (!strcmp(node->name, "Alarms") + || !strcmp(node->name, "Movies") + || !strcmp(node->name, "Music") + || !strcmp(node->name, "Notifications") + || !strcmp(node->name, "Podcasts") + || !strcmp(node->name, "Ringtones")) { + node->gid = AID_SDCARD_AV; + } } break; case PERM_ANDROID: @@ -412,6 +456,9 @@ static void derive_permissions_locked(struct fuse* fuse, struct node *parent, /* App-specific directories inside; let anyone traverse */ node->perm = PERM_ANDROID_OBB; node->mode = 0771; + /* Single OBB directory is always shared */ + node->graft_path = fuse->obbpath; + node->graft_pathlen = strlen(fuse->obbpath); } else if (!strcmp(node->name, "user")) { /* User directories must only be accessible to system, protected * by sdcard_all. Zygote will bind mount the appropriate user- @@ -423,22 +470,72 @@ static void derive_permissions_locked(struct fuse* fuse, struct node *parent, break; case PERM_ANDROID_DATA: case PERM_ANDROID_OBB: - package = hashmapGet(fuse->package_to_appid, node->name); - if (package != NULL) { - node->uid = multiuser_get_uid(parent->userid, package->appid); + appid = (appid_t) hashmapGet(fuse->package_to_appid, node->name); + if (appid != 0) { + node->uid = multiuser_get_uid(parent->userid, appid); } node->mode = 0770; break; case PERM_ANDROID_USER: /* Root of a secondary user */ node->perm = PERM_ROOT; - node->userid = atoi(node->name); - node->gid = AID_SDCARD_RW; + node->userid = strtoul(node->name, NULL, 10); + node->gid = AID_SDCARD_R; node->mode = 0771; break; } } +/* Kernel has already enforced everything we returned through + * derive_permissions_locked(), so this is used to lock down access + * even further, such as enforcing that apps hold sdcard_rw. */ +static bool check_caller_access_to_name(struct fuse* fuse, + const struct fuse_in_header *hdr, const struct node* parent_node, + const char* name, int mode) { + /* Always block security-sensitive files at root */ + if (parent_node && parent_node->perm == PERM_ROOT) { + if (!strcmp(name, "autorun.inf") + || !strcmp(name, ".android_secure") + || !strcmp(name, "android_secure")) { + return false; + } + } + + /* No additional permissions enforcement */ + if (fuse->derive == DERIVE_NONE) { + return true; + } + + /* Root or shell always have access */ + if (hdr->uid == 0 || hdr->uid == AID_SHELL) { + return true; + } + + /* If asking to write, verify that caller either owns the + * parent or holds sdcard_rw. */ + if (mode & W_OK) { + if (parent_node && hdr->uid == parent_node->uid) { + return true; + } + + appid_t appid = multiuser_get_app_id(hdr->uid); + + pthread_mutex_lock(&fuse->lock); + bool hasRw = hashmapContainsKey(fuse->appid_with_rw, (void*) appid); + pthread_mutex_unlock(&fuse->lock); + + return hasRw; + } + + /* No extra permissions to enforce */ + return true; +} + +static bool check_caller_access_to_node(struct fuse* fuse, + const struct fuse_in_header *hdr, const struct node* node, int mode) { + return check_caller_access_to_name(fuse, hdr, node->parent, node->name, mode); +} + struct node *create_node_locked(struct fuse* fuse, struct node *parent, const char *name, const char* actual_name) { @@ -561,32 +658,53 @@ static struct node* acquire_or_create_child_locked( return child; } -static void fuse_init(struct fuse *fuse, int fd, const char *source_path, bool derive_perms) -{ +static void fuse_init(struct fuse *fuse, int fd, const char *source_path, + gid_t fs_gid, derive_t derive, bool split_perms) { pthread_mutex_init(&fuse->lock, NULL); fuse->fd = fd; fuse->next_generation = 0; - fuse->derive_perms = derive_perms; + fuse->derive = derive; + fuse->split_perms = split_perms; memset(&fuse->root, 0, sizeof(fuse->root)); fuse->root.nid = FUSE_ROOT_ID; /* 1 */ fuse->root.refcount = 2; fuse->root.namelen = strlen(source_path); fuse->root.name = strdup(source_path); - - fuse->root.perm = PERM_ROOT; fuse->root.userid = 0; fuse->root.uid = AID_ROOT; - fuse->root.gid = AID_SDCARD_RW; - if (derive_perms) { - fuse->root.mode = 0771; - } else { - fuse->root.mode = 0775; - } - if (derive_perms) { + /* Set up root node for various modes of operation */ + switch (derive) { + case DERIVE_NONE: + /* Traditional behavior that treats entire device as being accessible + * to sdcard_rw, and no permissions are derived. */ + fuse->root.perm = PERM_ROOT; + fuse->root.mode = 0775; + fuse->root.gid = AID_SDCARD_RW; + break; + case DERIVE_LEGACY: + /* Legacy behavior used to support internal multiuser layout which + * places user_id at the top directory level, with the actual roots + * just below that. Shared OBB path is also at top level. */ + fuse->root.perm = PERM_LEGACY_PRE_ROOT; + fuse->root.mode = 0771; + fuse->root.gid = fs_gid; fuse->package_to_appid = hashmapCreate(256, str_hash, str_equals); + fuse->appid_with_rw = hashmapCreate(128, int_hash, int_equals); + snprintf(fuse->obbpath, sizeof(fuse->obbpath), "%s/obb", source_path); + break; + case DERIVE_UNIFIED: + /* Unified multiuser layout which places secondary user_id under + * /Android/user and shared OBB path under /Android/obb. */ + fuse->root.perm = PERM_ROOT; + fuse->root.mode = 0771; + fuse->root.gid = fs_gid; + fuse->package_to_appid = hashmapCreate(256, str_hash, str_equals); + fuse->appid_with_rw = hashmapCreate(128, int_hash, int_equals); + snprintf(fuse->obbpath, sizeof(fuse->obbpath), "%s/Android/obb", source_path); + break; } } @@ -637,7 +755,7 @@ static int fuse_reply_entry(struct fuse* fuse, __u64 unique, TRACE("automatically creating %s\n", path); pthread_mutex_lock(&fuse->lock); - bool validPackage = hashmapContainsKey(fuse->package_to_appid, name); + bool validPackage = hashmapContainsKey(fuse->package_to_appid, (char*) name); pthread_mutex_unlock(&fuse->lock); if (!validPackage) { @@ -713,6 +831,10 @@ static int handle_lookup(struct fuse* fuse, struct fuse_handler* handler, child_path, sizeof(child_path), 1))) { return -ENOENT; } + if (!check_caller_access_to_name(fuse, hdr, parent_node, name, R_OK)) { + return -EACCES; + } + return fuse_reply_entry(fuse, hdr->unique, parent_node, name, actual_name, child_path); } @@ -750,6 +872,10 @@ static int handle_getattr(struct fuse* fuse, struct fuse_handler* handler, if (!node) { return -ENOENT; } + if (!check_caller_access_to_node(fuse, hdr, node, R_OK)) { + return -EACCES; + } + return fuse_reply_attr(fuse, hdr->unique, node, path); } @@ -769,6 +895,9 @@ static int handle_setattr(struct fuse* fuse, struct fuse_handler* handler, if (!node) { return -ENOENT; } + if (!check_caller_access_to_node(fuse, hdr, node, W_OK)) { + return -EACCES; + } /* XXX: incomplete implementation on purpose. * chmod/chown should NEVER be implemented.*/ @@ -830,6 +959,9 @@ static int handle_mknod(struct fuse* fuse, struct fuse_handler* handler, child_path, sizeof(child_path), 1))) { return -ENOENT; } + if (!check_caller_access_to_name(fuse, hdr, parent_node, name, W_OK)) { + return -EACCES; + } __u32 mode = (req->mode & (~0777)) | 0664; if (mknod(child_path, mode, req->rdev) < 0) { return -errno; @@ -856,6 +988,9 @@ static int handle_mkdir(struct fuse* fuse, struct fuse_handler* handler, child_path, sizeof(child_path), 1))) { return -ENOENT; } + if (!check_caller_access_to_name(fuse, hdr, parent_node, name, W_OK)) { + return -EACCES; + } __u32 mode = (req->mode & (~0777)) | 0775; if (mkdir(child_path, mode) < 0) { return -errno; @@ -881,6 +1016,9 @@ static int handle_unlink(struct fuse* fuse, struct fuse_handler* handler, child_path, sizeof(child_path), 1)) { return -ENOENT; } + if (!check_caller_access_to_name(fuse, hdr, parent_node, name, W_OK)) { + return -EACCES; + } if (unlink(child_path) < 0) { return -errno; } @@ -905,6 +1043,9 @@ static int handle_rmdir(struct fuse* fuse, struct fuse_handler* handler, child_path, sizeof(child_path), 1)) { return -ENOENT; } + if (!check_caller_access_to_name(fuse, hdr, parent_node, name, W_OK)) { + return -EACCES; + } if (rmdir(child_path) < 0) { return -errno; } @@ -938,6 +1079,14 @@ static int handle_rename(struct fuse* fuse, struct fuse_handler* handler, res = -ENOENT; goto lookup_error; } + if (!check_caller_access_to_name(fuse, hdr, old_parent_node, old_name, W_OK)) { + res = -EACCES; + goto lookup_error; + } + if (!check_caller_access_to_name(fuse, hdr, new_parent_node, new_name, W_OK)) { + res = -EACCES; + goto lookup_error; + } child_node = lookup_child_by_name_locked(old_parent_node, old_name); if (!child_node || get_node_path_locked(child_node, old_child_path, sizeof(old_child_path)) < 0) { @@ -983,6 +1132,17 @@ lookup_error: return res; } +static int open_flags_to_access_mode(int open_flags) { + if ((open_flags & O_ACCMODE) == O_RDONLY) { + return R_OK; + } else if ((open_flags & O_ACCMODE) == O_WRONLY) { + return W_OK; + } else { + /* Probably O_RDRW, but treat as default to be safe */ + return R_OK | W_OK; + } +} + static int handle_open(struct fuse* fuse, struct fuse_handler* handler, const struct fuse_in_header* hdr, const struct fuse_open_in* req) { @@ -1000,6 +1160,9 @@ static int handle_open(struct fuse* fuse, struct fuse_handler* handler, if (!node) { return -ENOENT; } + if (!check_caller_access_to_node(fuse, hdr, node, open_flags_to_access_mode(req->flags))) { + return -EACCES; + } h = malloc(sizeof(*h)); if (!h) { return -ENOMEM; @@ -1144,6 +1307,9 @@ static int handle_opendir(struct fuse* fuse, struct fuse_handler* handler, if (!node) { return -ENOENT; } + if (!check_caller_access_to_node(fuse, hdr, node, R_OK)) { + return -EACCES; + } h = malloc(sizeof(*h)); if (!h) { return -ENOMEM; @@ -1389,18 +1555,24 @@ static void* start_handler(void* data) return NULL; } -static bool hashmap_remove(void *key, void *value, void *context) { +static bool remove_str_to_int(void *key, void *value, void *context) { Hashmap* map = context; hashmapRemove(map, key); free(key); - free(value); + return true; +} + +static bool remove_int_to_null(void *key, void *value, void *context) { + Hashmap* map = context; + hashmapRemove(map, key); return true; } static int read_package_list(struct fuse *fuse) { pthread_mutex_lock(&fuse->lock); - hashmapForEach(fuse->package_to_appid, hashmap_remove, fuse->package_to_appid); + hashmapForEach(fuse->package_to_appid, remove_str_to_int, fuse->package_to_appid); + hashmapForEach(fuse->appid_with_rw, remove_int_to_null, fuse->appid_with_rw); FILE* file = fopen(kPackagesListFile, "r"); if (!file) { @@ -1413,20 +1585,26 @@ static int read_package_list(struct fuse *fuse) { while (fgets(buf, sizeof(buf), file) != NULL) { char package_name[512]; int appid; - if (sscanf(buf, "%s %d", package_name, &appid) == 2) { - char* package_name_dup = strdup(package_name); - struct package* package = malloc(sizeof(struct package)); - if (!package_name_dup || !package) { - ERROR("cannot allocate package details\n"); - return -ENOMEM; - } + char gids[512]; - package->appid = appid; - hashmapPut(fuse->package_to_appid, package_name_dup, package); + if (sscanf(buf, "%s %d %*d %*s %*s %s", package_name, &appid, gids) == 3) { + char* package_name_dup = strdup(package_name); + hashmapPut(fuse->package_to_appid, package_name_dup, (void*) appid); + + char* token = strtok(gids, ","); + while (token != NULL) { + if (strtoul(token, NULL, 10) == AID_SDCARD_RW) { + hashmapPut(fuse->appid_with_rw, (void*) appid, (void*) 1); + break; + } + token = strtok(NULL, ","); + } } } - TRACE("read_package_list: found %d packages\n", hashmapSize(fuse->package_to_appid)); + TRACE("read_package_list: found %d packages, %d with sdcard_rw\n", + hashmapSize(fuse->package_to_appid), + hashmapSize(fuse->appid_with_rw)); fclose(file); pthread_mutex_unlock(&fuse->lock); return 0; @@ -1512,7 +1690,7 @@ static int ignite_fuse(struct fuse* fuse, int num_threads) /* When deriving permissions, this thread is used to process inotify events, * otherwise it becomes one of the FUSE handlers. */ - i = fuse->derive_perms ? 0 : 1; + i = (fuse->derive == DERIVE_NONE) ? 1 : 0; for (; i < num_threads; i++) { pthread_t thread; int res = pthread_create(&thread, NULL, start_handler, &handlers[i]); @@ -1522,10 +1700,10 @@ static int ignite_fuse(struct fuse* fuse, int num_threads) } } - if (fuse->derive_perms) { - watch_package_list(fuse); - } else { + if (fuse->derive == DERIVE_NONE) { handle_fuse_requests(&handlers[0]); + } else { + watch_package_list(fuse); } ERROR("terminated prematurely\n"); @@ -1541,14 +1719,17 @@ static int usage() ERROR("usage: sdcard [OPTIONS] \n" " -u: specify UID to run as\n" " -g: specify GID to run as\n" + " -G: specify default GID for files (default sdcard_r, requires -d or -l)\n" " -t: specify number of threads to use (default %d)\n" " -d: derive file permissions based on path\n" + " -l: derive file permissions based on legacy internal layout\n" + " -s: split derived permissions for pics, av\n" "\n", DEFAULT_NUM_THREADS); return 1; } -static int run(const char* source_path, const char* dest_path, uid_t uid, gid_t gid, - int num_threads, bool derive_perms) { +static int run(const char* source_path, const char* dest_path, uid_t uid, + gid_t gid, gid_t fs_gid, int num_threads, derive_t derive, bool split_perms) { int fd; char opts[256]; int res; @@ -1591,7 +1772,7 @@ static int run(const char* source_path, const char* dest_path, uid_t uid, gid_t goto error; } - fuse_init(&fuse, fd, source_path, derive_perms); + fuse_init(&fuse, fd, source_path, fs_gid, derive, split_perms); umask(0); res = ignite_fuse(&fuse, num_threads); @@ -1611,13 +1792,15 @@ int main(int argc, char **argv) const char *dest_path = NULL; uid_t uid = 0; gid_t gid = 0; + gid_t fs_gid = AID_SDCARD_R; int num_threads = DEFAULT_NUM_THREADS; - bool derive_perms = false; + derive_t derive = DERIVE_NONE; + bool split_perms = false; int i; struct rlimit rlim; int opt; - while ((opt = getopt(argc, argv, "u:g:t:d")) != -1) { + while ((opt = getopt(argc, argv, "u:g:G:t:dls")) != -1) { switch (opt) { case 'u': uid = strtoul(optarg, NULL, 10); @@ -1625,11 +1808,20 @@ int main(int argc, char **argv) case 'g': gid = strtoul(optarg, NULL, 10); break; + case 'G': + fs_gid = strtoul(optarg, NULL, 10); + break; case 't': num_threads = strtoul(optarg, NULL, 10); break; case 'd': - derive_perms = true; + derive = DERIVE_UNIFIED; + break; + case 'l': + derive = DERIVE_LEGACY; + break; + case 's': + split_perms = true; break; case '?': default: @@ -1669,6 +1861,10 @@ int main(int argc, char **argv) ERROR("number of threads must be at least 1\n"); return usage(); } + if (split_perms && derive == DERIVE_NONE) { + ERROR("cannot split permissions without deriving\n"); + return usage(); + } rlim.rlim_cur = 8192; rlim.rlim_max = 8192; @@ -1676,6 +1872,6 @@ int main(int argc, char **argv) ERROR("Error setting RLIMIT_NOFILE, errno = %d\n", errno); } - res = run(source_path, dest_path, uid, gid, num_threads, derive_perms); + res = run(source_path, dest_path, uid, gid, fs_gid, num_threads, derive, split_perms); return res < 0 ? 1 : 0; } From 3a8768804ce4b4797359d5df03ec8897fe43de90 Mon Sep 17 00:00:00 2001 From: Ken Sumrall Date: Wed, 14 Aug 2013 20:02:13 -0700 Subject: [PATCH 507/541] Fix handle_opendir() in the sdcard daemon The fuse_open_out structure returned to the kernel by handle_opendir() was not properly initializing all the fields. The symptom was recursive ls (ls -R) failing on the emulated sdcard filesystem, because rewinddir(3) was failing with ESPIPE. Bug: 7168594 Change-Id: I56ddfd3453e6aac34fe6e001e88c4c46fb2eb271 --- sdcard/sdcard.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sdcard/sdcard.c b/sdcard/sdcard.c index bff6e67d5..7e65a68c4 100644 --- a/sdcard/sdcard.c +++ b/sdcard/sdcard.c @@ -972,6 +972,8 @@ static int handle_opendir(struct fuse* fuse, struct fuse_handler* handler, return -errno; } out.fh = ptr_to_id(h); + out.open_flags = 0; + out.padding = 0; fuse_reply(fuse, hdr->unique, &out, sizeof(out)); return NO_STATUS; } From 1e26356da14d78abd18eb66ddd980741e2207f1e Mon Sep 17 00:00:00 2001 From: Jesse Hall Date: Fri, 16 Aug 2013 08:40:52 -0700 Subject: [PATCH 508/541] Replace sRGB_888 with sRGB_X_8888 Bug: 10357459 Change-Id: I23a3eca77acd8b4b40b1a67e7c050a7245b1821a --- include/system/graphics.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/system/graphics.h b/include/system/graphics.h index 5d883be26..154f9cb4c 100644 --- a/include/system/graphics.h +++ b/include/system/graphics.h @@ -75,7 +75,7 @@ enum { * */ HAL_PIXEL_FORMAT_sRGB_A_8888 = 0xC, - HAL_PIXEL_FORMAT_sRGB_888 = 0xD, + HAL_PIXEL_FORMAT_sRGB_X_8888 = 0xD, /* * 0x100 - 0x1FF From 3984276ce47c965ad02a522280a139e0a0c7e5cf Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Sat, 17 Aug 2013 03:40:31 +0900 Subject: [PATCH 509/541] Get rid of an infinite loop in NetlinkEvent.cpp. Bug: 10358527 Bug: 10263310 Bug: 10232006 Change-Id: I750e4bdf2000040adf214d6a772591d7bd25b350 --- libsysutils/src/NetlinkEvent.cpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/libsysutils/src/NetlinkEvent.cpp b/libsysutils/src/NetlinkEvent.cpp index b6da72f4c..01bec7772 100644 --- a/libsysutils/src/NetlinkEvent.cpp +++ b/libsysutils/src/NetlinkEvent.cpp @@ -78,7 +78,7 @@ void NetlinkEvent::dump() { */ bool NetlinkEvent::parseIfAddrMessage(int type, struct ifaddrmsg *ifaddr, int rtasize) { - struct rtattr *rta = IFA_RTA(ifaddr); + struct rtattr *rta; struct ifa_cacheinfo *cacheinfo = NULL; char addrstr[INET6_ADDRSTRLEN] = ""; @@ -91,7 +91,8 @@ bool NetlinkEvent::parseIfAddrMessage(int type, struct ifaddrmsg *ifaddr, // For log messages. const char *msgtype = (type == RTM_NEWADDR) ? "RTM_NEWADDR" : "RTM_DELADDR"; - while(RTA_OK(rta, rtasize)) { + for (rta = IFA_RTA(ifaddr); RTA_OK(rta, rtasize); + rta = RTA_NEXT(rta, rtasize)) { if (rta->rta_type == IFA_ADDRESS) { // Only look at the first address, because we only support notifying // one change at a time. @@ -157,8 +158,6 @@ bool NetlinkEvent::parseIfAddrMessage(int type, struct ifaddrmsg *ifaddr, asprintf(&mParams[6], "CSTAMP=%u", cacheinfo->cstamp); asprintf(&mParams[7], "TSTAMP=%u", cacheinfo->tstamp); } - - rta = RTA_NEXT(rta, rtasize); } if (addrstr[0] == '\0') { @@ -173,10 +172,11 @@ bool NetlinkEvent::parseIfAddrMessage(int type, struct ifaddrmsg *ifaddr, * Parse an binary message from a NETLINK_ROUTE netlink socket. */ bool NetlinkEvent::parseBinaryNetlinkMessage(char *buffer, int size) { - size_t sz = size; - const struct nlmsghdr *nh = (struct nlmsghdr *) buffer; + const struct nlmsghdr *nh; - while (NLMSG_OK(nh, sz) && (nh->nlmsg_type != NLMSG_DONE)) { + for (nh = (struct nlmsghdr *) buffer; + NLMSG_OK(nh, size) && (nh->nlmsg_type != NLMSG_DONE); + nh = NLMSG_NEXT(nh, size)) { if (nh->nlmsg_type == RTM_NEWLINK) { int len = nh->nlmsg_len - sizeof(*nh); @@ -245,7 +245,6 @@ bool NetlinkEvent::parseBinaryNetlinkMessage(char *buffer, int size) { } else { SLOGD("Unexpected netlink message. type=0x%x\n", nh->nlmsg_type); } - nh = NLMSG_NEXT(nh, size); } return true; From 8ecc7afca42d0bb27aad25733790309f71f307d6 Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Fri, 16 Aug 2013 20:09:37 -0700 Subject: [PATCH 510/541] Add helpers for audio remote submix devices. Bug: 10265163 Change-Id: Iea7ecab4f2a655b719ff14b04c757d6dbd7a7ef3 --- include/system/audio.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/include/system/audio.h b/include/system/audio.h index abd2990aa..b90f4a90e 100644 --- a/include/system/audio.h +++ b/include/system/audio.h @@ -31,6 +31,9 @@ __BEGIN_DECLS * frameworks/base/include/media/AudioSystem.h */ +/* device address used to refer to the standard remote submix */ +#define AUDIO_REMOTE_SUBMIX_DEVICE_ADDRESS "0" + typedef int audio_io_handle_t; /* Audio stream types */ @@ -481,7 +484,8 @@ static inline bool audio_is_usb_device(audio_devices_t device) static inline bool audio_is_remote_submix_device(audio_devices_t device) { - if ((popcount(device) == 1) && (device & AUDIO_DEVICE_OUT_REMOTE_SUBMIX)) + if ((device & AUDIO_DEVICE_OUT_REMOTE_SUBMIX) == AUDIO_DEVICE_OUT_REMOTE_SUBMIX + || (device & AUDIO_DEVICE_IN_REMOTE_SUBMIX) == AUDIO_DEVICE_IN_REMOTE_SUBMIX) return true; else return false; From 9f50abdee00a9571393ce4a589080b4d6129aaf3 Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Mon, 19 Aug 2013 15:56:34 -0700 Subject: [PATCH 511/541] Add basic readlink command Change-Id: I693c1098e6c6d107a9e97285bf826ab153a1c8f0 --- toolbox/Android.mk | 3 ++- toolbox/readlink.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 toolbox/readlink.c diff --git a/toolbox/Android.mk b/toolbox/Android.mk index f60037dae..75ce53f0a 100644 --- a/toolbox/Android.mk +++ b/toolbox/Android.mk @@ -68,7 +68,8 @@ TOOLS := \ load_policy \ swapon \ swapoff \ - mkswap + mkswap \ + readlink ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT))) TOOLS += r diff --git a/toolbox/readlink.c b/toolbox/readlink.c new file mode 100644 index 000000000..9c1ab95aa --- /dev/null +++ b/toolbox/readlink.c @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2013, The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google, Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include + +static void usage(char* name) { + fprintf(stderr, "Usage: %s FILE\n", name); +} + +int readlink_main(int argc, char* argv[]) { + if (argc != 2) { + usage(argv[0]); + return EXIT_FAILURE; + } + + char name[PATH_MAX+1]; + ssize_t len = readlink(argv[1], name, PATH_MAX); + + if (len < 0) { + perror("readlink"); + return EXIT_FAILURE; + } + + name[len] = '\0'; + printf("%s\n", name); + + return EXIT_SUCCESS; +} From 0d872d8bb4f5371200601b7615ea48993383befb Mon Sep 17 00:00:00 2001 From: Alex Klyubin Date: Fri, 16 Aug 2013 13:19:24 -0700 Subject: [PATCH 512/541] Seed Linux RNG from Hardware RNG at boot during init. The Linux RNG may have little entropy during boot. As more and more devices have a Hardware RNG, we mix in 512 bytes from Hardware RNG (if present) into Linux RNG early during boot (after wait_for_coldboot_done and before property_service_init actions in init). To avoid having to trust the output of Hardware RNG, we do not mix it into the Linux RNG's primary pool or increase the Linux RNG's entropy estimates. Bug: 10362513 Change-Id: I80617f21710400747f5e7533e518d90ea74e2f11 --- init/init.c | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/init/init.c b/init/init.c index b7e34d04c..94a201196 100644 --- a/init/init.c +++ b/init/init.c @@ -42,6 +42,7 @@ #include #include #include +#include #include #include @@ -559,6 +560,84 @@ static int wait_for_coldboot_done_action(int nargs, char **args) return ret; } +/* + * Writes 512 bytes of output from Hardware RNG (/dev/hw_random, backed + * by Linux kernel's hw_random framework) into Linux RNG's via /dev/urandom. + * Does nothing if Hardware RNG is not present. + * + * Since we don't yet trust the quality of Hardware RNG, these bytes are not + * mixed into the primary pool of Linux RNG and the entropy estimate is left + * unmodified. + * + * If the HW RNG device /dev/hw_random is present, we require that at least + * 512 bytes read from it are written into Linux RNG. QA is expected to catch + * devices/configurations where these I/O operations are blocking for a long + * time. We do not reboot or halt on failures, as this is a best-effort + * attempt. + */ +static int mix_hwrng_into_linux_rng_action(int nargs, char **args) +{ + int result = -1; + int hwrandom_fd = -1; + int urandom_fd = -1; + char buf[512]; + ssize_t chunk_size; + size_t total_bytes_written = 0; + + hwrandom_fd = TEMP_FAILURE_RETRY( + open("/dev/hw_random", O_RDONLY | O_NOFOLLOW)); + if (hwrandom_fd == -1) { + if (errno == ENOENT) { + ERROR("/dev/hw_random not found\n"); + /* It's not an error to not have a Hardware RNG. */ + result = 0; + } else { + ERROR("Failed to open /dev/hw_random: %s\n", strerror(errno)); + } + goto ret; + } + + urandom_fd = TEMP_FAILURE_RETRY( + open("/dev/urandom", O_WRONLY | O_NOFOLLOW)); + if (urandom_fd == -1) { + ERROR("Failed to open /dev/urandom: %s\n", strerror(errno)); + goto ret; + } + + while (total_bytes_written < sizeof(buf)) { + chunk_size = TEMP_FAILURE_RETRY( + read(hwrandom_fd, buf, sizeof(buf) - total_bytes_written)); + if (chunk_size == -1) { + ERROR("Failed to read from /dev/hw_random: %s\n", strerror(errno)); + goto ret; + } else if (chunk_size == 0) { + ERROR("Failed to read from /dev/hw_random: EOF\n"); + goto ret; + } + + chunk_size = TEMP_FAILURE_RETRY(write(urandom_fd, buf, chunk_size)); + if (chunk_size == -1) { + ERROR("Failed to write to /dev/urandom: %s\n", strerror(errno)); + goto ret; + } + total_bytes_written += chunk_size; + } + + INFO("Mixed %d bytes from /dev/hw_random into /dev/urandom", + total_bytes_written); + result = 0; + +ret: + if (hwrandom_fd != -1) { + close(hwrandom_fd); + } + if (urandom_fd != -1) { + close(urandom_fd); + } + memset(buf, 0, sizeof(buf)); + return result; +} + static int keychord_init_action(int nargs, char **args) { keychord_init(); @@ -962,6 +1041,7 @@ int main(int argc, char **argv) action_for_each_trigger("early-init", action_add_queue_tail); queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done"); + queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng"); queue_builtin_action(keychord_init_action, "keychord_init"); queue_builtin_action(console_init_action, "console_init"); @@ -976,6 +1056,11 @@ int main(int argc, char **argv) action_for_each_trigger("post-fs-data", action_add_queue_tail); } + /* Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random + * wasn't ready immediately after wait_for_coldboot_done + */ + queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng"); + queue_builtin_action(property_service_init_action, "property_service_init"); queue_builtin_action(signal_init_action, "signal_init"); queue_builtin_action(check_startup_action, "check_startup"); From 18860c524915bc991a9015bdbab32e918f5298d7 Mon Sep 17 00:00:00 2001 From: Alex Klyubin Date: Tue, 20 Aug 2013 15:16:31 -0700 Subject: [PATCH 513/541] Enable run-as to read packages.list now owned by package_info. The group ownership of the package database /data/system/packages.list read by run-as was changed in 977a9f3b1a05e6168e8245a1e2061225b68b2b41 from "system" to "package_info". run-as currently changes its effective group to "system" and is thus unable to read the database. This CL fixes the issue by making run-as change its effective group to "package_info" for reading the package database. Bug: 10411916 Change-Id: Id23059bfb5b43264824917873a31c287f057ce4e --- run-as/package.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/run-as/package.c b/run-as/package.c index 4762c5f89..901e9e370 100644 --- a/run-as/package.c +++ b/run-as/package.c @@ -90,7 +90,7 @@ map_file(const char* filename, size_t* filesize) */ oldegid = getegid(); - if (setegid(AID_SYSTEM) < 0) { + if (setegid(AID_PACKAGE_INFO) < 0) { return NULL; } From a306ced1ac383557dabfb38c70e5f5e2a2ec0510 Mon Sep 17 00:00:00 2001 From: Ziv Hendel Date: Wed, 7 Aug 2013 19:29:17 +0300 Subject: [PATCH 514/541] libusbhost: It's no longer assumed that "bus/usb" exists once "bus" was created On some devices there is a slight delay between the creation of "/dev/bus" and "/dev/bus/usb". Previously, the code assumed that both are created in the same time which caused "watch_existing_subdirs" to fail and libusbhost to stop working until the device is rebooted. The fix will setup an inotify event on the creation of the "bus/usb" so it will not be missed once it's created. Change-Id: I17f06dd167e61573307425e48898e12ebc954093 --- libusbhost/usbhost.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/libusbhost/usbhost.c b/libusbhost/usbhost.c index b96734258..8be393ee7 100644 --- a/libusbhost/usbhost.c +++ b/libusbhost/usbhost.c @@ -51,7 +51,8 @@ #include "usbhost/usbhost.h" #define DEV_DIR "/dev" -#define USB_FS_DIR DEV_DIR "/bus/usb" +#define DEV_BUS_DIR DEV_DIR "/bus" +#define USB_FS_DIR DEV_BUS_DIR "/usb" #define USB_FS_ID_SCANNER USB_FS_DIR "/%d/%d" #define USB_FS_ID_FORMAT USB_FS_DIR "/%03d/%03d" @@ -68,6 +69,7 @@ struct usb_host_context { void *data; int wds[MAX_USBFS_WD_COUNT]; int wdd; + int wddbus; }; struct usb_device { @@ -195,6 +197,7 @@ int usb_host_load(struct usb_host_context *context, D("Created device discovery thread\n"); /* watch for files added and deleted within USB_FS_DIR */ + context->wddbus = -1; for (i = 0; i < MAX_USBFS_WD_COUNT; i++) context->wds[i] = -1; @@ -228,15 +231,25 @@ int usb_host_read_event(struct usb_host_context *context) ret = read(context->fd, event_buf, sizeof(event_buf)); if (ret >= (int)sizeof(struct inotify_event)) { - while (offset < ret) { + while (offset < ret && !done) { event = (struct inotify_event*)&event_buf[offset]; done = 0; wd = event->wd; if (wd == context->wdd) { if ((event->mask & IN_CREATE) && !strcmp(event->name, "bus")) { + context->wddbus = inotify_add_watch(context->fd, DEV_BUS_DIR, IN_CREATE | IN_DELETE); + if (context->wddbus < 0) { + done = 1; + } else { + watch_existing_subdirs(context, context->wds, MAX_USBFS_WD_COUNT); + done = find_existing_devices(context->cb_added, context->data); + } + } + } else if (wd == context->wddbus) { + if ((event->mask & IN_CREATE) && !strcmp(event->name, "usb")) { watch_existing_subdirs(context, context->wds, MAX_USBFS_WD_COUNT); done = find_existing_devices(context->cb_added, context->data); - } else if ((event->mask & IN_DELETE) && !strcmp(event->name, "bus")) { + } else if ((event->mask & IN_DELETE) && !strcmp(event->name, "usb")) { for (i = 0; i < MAX_USBFS_WD_COUNT; i++) { if (context->wds[i] >= 0) { inotify_rm_watch(context->fd, context->wds[i]); From b0739c662db6a19b49c0912b865edb2853156bda Mon Sep 17 00:00:00 2001 From: Alex Klyubin Date: Mon, 5 Aug 2013 13:03:58 -0700 Subject: [PATCH 515/541] Fix run-as which was broken in Android 4.3 In Android 4.3 the run-as binary no longer has the SUID/SGID bits set. Instead, it requires to be installed with setuid and setgid file-based capabilities. As a result of the above two changes, the binary no longer executes as root when invoked by the "shell" user but can still change its UID/GID to that of the target package. Unfortunately, run-as attempts to chdir into the target package's data directory before changing its effective UID/GID. As a result, when run-as is invoked by the "shell" user, the chdir operation fails. The fix is for run-as to chdir after changing the effective UID/GID to those of the target package. Bug: 10154652 (cherry picked from commit f2904a7b63c2005ab588a9ba2fb309e73200ec81) Change-Id: I0f6cb9efd49f5c2c491f7aa1d614d700a5ec2304 --- run-as/run-as.c | 43 ++++++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/run-as/run-as.c b/run-as/run-as.c index 3c0ecc4a9..cc05e6365 100644 --- a/run-as/run-as.c +++ b/run-as/run-as.c @@ -36,9 +36,9 @@ /* * WARNING WARNING WARNING WARNING * - * This program runs as set-uid root on Android production devices. - * Be very conservative when modifying it to avoid any serious - * security issue. Keep in mind the following: + * This program runs with CAP_SETUID and CAP_SETGID capabilities on Android + * production devices. Be very conservative when modifying it to avoid any + * serious security issue. Keep in mind the following: * * - This program should only run for the 'root' or 'shell' users * @@ -61,14 +61,19 @@ * * run-as * - * The 'run-as' binary is setuid, but will check the following: + * The 'run-as' binary is installed with CAP_SETUID and CAP_SETGID file + * capabilities, but will check the following: * * - that it is invoked from the 'shell' or 'root' user (abort otherwise) * - that '' is the name of an installed and debuggable package * - that the package's data directory is well-formed (see package.c) * - * If so, it will cd to the package's data directory, drop to the application's - * user id / group id then run the command there. + * If so, it will drop to the application's user id / group id, cd to the + * package's data directory, then run the command there. + * + * NOTE: In the future it might not be possible to cd to the package's data + * directory under that package's user id / group id, in which case this + * utility will need to be changed accordingly. * * This can be useful for a number of different things on production devices: * @@ -141,19 +146,6 @@ int main(int argc, char **argv) return 1; } - /* then move to it */ - { - int ret; - do { - ret = chdir(info.dataDir); - } while (ret < 0 && errno == EINTR); - - if (ret < 0) { - panic("Could not cd to package's data directory: %s\n", strerror(errno)); - return 1; - } - } - /* Ensure that we change all real/effective/saved IDs at the * same time to avoid nasty surprises. */ @@ -168,6 +160,19 @@ int main(int argc, char **argv) return 1; } + /* cd into the data directory */ + { + int ret; + do { + ret = chdir(info.dataDir); + } while (ret < 0 && errno == EINTR); + + if (ret < 0) { + panic("Could not cd to package's data directory: %s\n", strerror(errno)); + return 1; + } + } + /* User specified command for exec. */ if (argc >= 3 ) { if (execvp(argv[2], argv+2) < 0) { From 1e1d29133f12031bd432b6818143c309f18de417 Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Mon, 19 Aug 2013 20:08:04 -0700 Subject: [PATCH 516/541] Add standard options to readlink Change-Id: I4c2624d22ce879a3578d5b17440a9895b19e2f1f --- toolbox/readlink.c | 52 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 44 insertions(+), 8 deletions(-) diff --git a/toolbox/readlink.c b/toolbox/readlink.c index 9c1ab95aa..d114e2098 100644 --- a/toolbox/readlink.c +++ b/toolbox/readlink.c @@ -34,26 +34,62 @@ #include #include +static int skip_newline, quiet_errors, canonicalize; + static void usage(char* name) { - fprintf(stderr, "Usage: %s FILE\n", name); + fprintf(stderr, "Usage: %s [OPTION]... FILE\n", name); } int readlink_main(int argc, char* argv[]) { - if (argc != 2) { + int c; + while ((c = getopt(argc, argv, "nfqs")) != -1) { + switch (c) { + case 'n': + skip_newline = 1; + break; + case 'f': + canonicalize = 1; + break; + case 'q': + case 's': + quiet_errors = 1; + break; + case '?': + default: + usage(argv[0]); + return EXIT_FAILURE; + } + } + int index = optind; + if (argc - index != 1) { usage(argv[0]); return EXIT_FAILURE; } char name[PATH_MAX+1]; - ssize_t len = readlink(argv[1], name, PATH_MAX); + if (canonicalize) { + if(!realpath(argv[optind], name)) { + if (!quiet_errors) { + perror("readlink"); + } + return EXIT_FAILURE; + } + } else { + ssize_t len = readlink(argv[1], name, PATH_MAX); - if (len < 0) { - perror("readlink"); - return EXIT_FAILURE; + if (len < 0) { + if (!quiet_errors) { + perror("readlink"); + } + return EXIT_FAILURE; + } + name[len] = '\0'; } - name[len] = '\0'; - printf("%s\n", name); + fputs(name, stdout); + if (!skip_newline) { + fputs("\n", stdout); + } return EXIT_SUCCESS; } From 46a24db563f46736f6e02c4d80656ab51d94ff77 Mon Sep 17 00:00:00 2001 From: Jeff Brown Date: Thu, 22 Aug 2013 18:38:35 -0700 Subject: [PATCH 517/541] Remove input flinger stubs. (DO NOT MERGE) Bug: 10446930 Change-Id: I88b926380dbe1e866c0b87ec95b3489d87c5907b --- rootdir/init.rc | 7 ------- 1 file changed, 7 deletions(-) diff --git a/rootdir/init.rc b/rootdir/init.rc index 729879a75..6140ba1ab 100644 --- a/rootdir/init.rc +++ b/rootdir/init.rc @@ -446,7 +446,6 @@ service servicemanager /system/bin/servicemanager onrestart restart zygote onrestart restart media onrestart restart surfaceflinger - onrestart restart inputflinger onrestart restart drm service vold /system/bin/vold @@ -476,12 +475,6 @@ service surfaceflinger /system/bin/surfaceflinger group graphics drmrpc onrestart restart zygote -service inputflinger /system/bin/inputflinger - class main - user system - group input - onrestart restart zygote - service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server class main socket zygote stream 660 root system From aa04e818a4904b193e00d603785c93e888eab174 Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Fri, 30 Aug 2013 10:26:15 -0700 Subject: [PATCH 518/541] Fix recursive locking bug. handle_rename() would end up acquiring the lock twice. Change to always derive has_rw inside earlier locks (instead of acquiring a second time), and pass the value into check_caller_access_to_name(). Bug: 10547597 Change-Id: If5744d6d226a4785676c19d0f7fdf1c05060ed76 --- sdcard/sdcard.c | 57 +++++++++++++++++++++++++++++++------------------ 1 file changed, 36 insertions(+), 21 deletions(-) diff --git a/sdcard/sdcard.c b/sdcard/sdcard.c index 7349b2241..330d555b5 100644 --- a/sdcard/sdcard.c +++ b/sdcard/sdcard.c @@ -486,12 +486,18 @@ static void derive_permissions_locked(struct fuse* fuse, struct node *parent, } } +/* Return if the calling UID holds sdcard_rw. */ +static bool get_caller_has_rw_locked(struct fuse* fuse, const struct fuse_in_header *hdr) { + appid_t appid = multiuser_get_app_id(hdr->uid); + return hashmapContainsKey(fuse->appid_with_rw, (void*) appid); +} + /* Kernel has already enforced everything we returned through * derive_permissions_locked(), so this is used to lock down access * even further, such as enforcing that apps hold sdcard_rw. */ static bool check_caller_access_to_name(struct fuse* fuse, const struct fuse_in_header *hdr, const struct node* parent_node, - const char* name, int mode) { + const char* name, int mode, bool has_rw) { /* Always block security-sensitive files at root */ if (parent_node && parent_node->perm == PERM_ROOT) { if (!strcmp(name, "autorun.inf") @@ -518,13 +524,7 @@ static bool check_caller_access_to_name(struct fuse* fuse, return true; } - appid_t appid = multiuser_get_app_id(hdr->uid); - - pthread_mutex_lock(&fuse->lock); - bool hasRw = hashmapContainsKey(fuse->appid_with_rw, (void*) appid); - pthread_mutex_unlock(&fuse->lock); - - return hasRw; + return has_rw; } /* No extra permissions to enforce */ @@ -532,8 +532,8 @@ static bool check_caller_access_to_name(struct fuse* fuse, } static bool check_caller_access_to_node(struct fuse* fuse, - const struct fuse_in_header *hdr, const struct node* node, int mode) { - return check_caller_access_to_name(fuse, hdr, node->parent, node->name, mode); + const struct fuse_in_header *hdr, const struct node* node, int mode, bool has_rw) { + return check_caller_access_to_name(fuse, hdr, node->parent, node->name, mode, has_rw); } struct node *create_node_locked(struct fuse* fuse, @@ -831,7 +831,7 @@ static int handle_lookup(struct fuse* fuse, struct fuse_handler* handler, child_path, sizeof(child_path), 1))) { return -ENOENT; } - if (!check_caller_access_to_name(fuse, hdr, parent_node, name, R_OK)) { + if (!check_caller_access_to_name(fuse, hdr, parent_node, name, R_OK, false)) { return -EACCES; } @@ -872,7 +872,7 @@ static int handle_getattr(struct fuse* fuse, struct fuse_handler* handler, if (!node) { return -ENOENT; } - if (!check_caller_access_to_node(fuse, hdr, node, R_OK)) { + if (!check_caller_access_to_node(fuse, hdr, node, R_OK, false)) { return -EACCES; } @@ -882,11 +882,13 @@ static int handle_getattr(struct fuse* fuse, struct fuse_handler* handler, static int handle_setattr(struct fuse* fuse, struct fuse_handler* handler, const struct fuse_in_header *hdr, const struct fuse_setattr_in *req) { + bool has_rw; struct node* node; char path[PATH_MAX]; struct timespec times[2]; pthread_mutex_lock(&fuse->lock); + has_rw = get_caller_has_rw_locked(fuse, hdr); node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, path, sizeof(path)); TRACE("[%d] SETATTR fh=%llx valid=%x @ %llx (%s)\n", handler->token, req->fh, req->valid, hdr->nodeid, node ? node->name : "?"); @@ -895,7 +897,7 @@ static int handle_setattr(struct fuse* fuse, struct fuse_handler* handler, if (!node) { return -ENOENT; } - if (!check_caller_access_to_node(fuse, hdr, node, W_OK)) { + if (!check_caller_access_to_node(fuse, hdr, node, W_OK, has_rw)) { return -EACCES; } @@ -943,12 +945,14 @@ static int handle_setattr(struct fuse* fuse, struct fuse_handler* handler, static int handle_mknod(struct fuse* fuse, struct fuse_handler* handler, const struct fuse_in_header* hdr, const struct fuse_mknod_in* req, const char* name) { + bool has_rw; struct node* parent_node; char parent_path[PATH_MAX]; char child_path[PATH_MAX]; const char* actual_name; pthread_mutex_lock(&fuse->lock); + has_rw = get_caller_has_rw_locked(fuse, hdr); parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, parent_path, sizeof(parent_path)); TRACE("[%d] MKNOD %s 0%o @ %llx (%s)\n", handler->token, @@ -959,7 +963,7 @@ static int handle_mknod(struct fuse* fuse, struct fuse_handler* handler, child_path, sizeof(child_path), 1))) { return -ENOENT; } - if (!check_caller_access_to_name(fuse, hdr, parent_node, name, W_OK)) { + if (!check_caller_access_to_name(fuse, hdr, parent_node, name, W_OK, has_rw)) { return -EACCES; } __u32 mode = (req->mode & (~0777)) | 0664; @@ -972,12 +976,14 @@ static int handle_mknod(struct fuse* fuse, struct fuse_handler* handler, static int handle_mkdir(struct fuse* fuse, struct fuse_handler* handler, const struct fuse_in_header* hdr, const struct fuse_mkdir_in* req, const char* name) { + bool has_rw; struct node* parent_node; char parent_path[PATH_MAX]; char child_path[PATH_MAX]; const char* actual_name; pthread_mutex_lock(&fuse->lock); + has_rw = get_caller_has_rw_locked(fuse, hdr); parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, parent_path, sizeof(parent_path)); TRACE("[%d] MKDIR %s 0%o @ %llx (%s)\n", handler->token, @@ -988,7 +994,7 @@ static int handle_mkdir(struct fuse* fuse, struct fuse_handler* handler, child_path, sizeof(child_path), 1))) { return -ENOENT; } - if (!check_caller_access_to_name(fuse, hdr, parent_node, name, W_OK)) { + if (!check_caller_access_to_name(fuse, hdr, parent_node, name, W_OK, has_rw)) { return -EACCES; } __u32 mode = (req->mode & (~0777)) | 0775; @@ -1001,11 +1007,13 @@ static int handle_mkdir(struct fuse* fuse, struct fuse_handler* handler, static int handle_unlink(struct fuse* fuse, struct fuse_handler* handler, const struct fuse_in_header* hdr, const char* name) { + bool has_rw; struct node* parent_node; char parent_path[PATH_MAX]; char child_path[PATH_MAX]; pthread_mutex_lock(&fuse->lock); + has_rw = get_caller_has_rw_locked(fuse, hdr); parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, parent_path, sizeof(parent_path)); TRACE("[%d] UNLINK %s @ %llx (%s)\n", handler->token, @@ -1016,7 +1024,7 @@ static int handle_unlink(struct fuse* fuse, struct fuse_handler* handler, child_path, sizeof(child_path), 1)) { return -ENOENT; } - if (!check_caller_access_to_name(fuse, hdr, parent_node, name, W_OK)) { + if (!check_caller_access_to_name(fuse, hdr, parent_node, name, W_OK, has_rw)) { return -EACCES; } if (unlink(child_path) < 0) { @@ -1028,11 +1036,13 @@ static int handle_unlink(struct fuse* fuse, struct fuse_handler* handler, static int handle_rmdir(struct fuse* fuse, struct fuse_handler* handler, const struct fuse_in_header* hdr, const char* name) { + bool has_rw; struct node* parent_node; char parent_path[PATH_MAX]; char child_path[PATH_MAX]; pthread_mutex_lock(&fuse->lock); + has_rw = get_caller_has_rw_locked(fuse, hdr); parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, parent_path, sizeof(parent_path)); TRACE("[%d] RMDIR %s @ %llx (%s)\n", handler->token, @@ -1043,7 +1053,7 @@ static int handle_rmdir(struct fuse* fuse, struct fuse_handler* handler, child_path, sizeof(child_path), 1)) { return -ENOENT; } - if (!check_caller_access_to_name(fuse, hdr, parent_node, name, W_OK)) { + if (!check_caller_access_to_name(fuse, hdr, parent_node, name, W_OK, has_rw)) { return -EACCES; } if (rmdir(child_path) < 0) { @@ -1056,6 +1066,7 @@ static int handle_rename(struct fuse* fuse, struct fuse_handler* handler, const struct fuse_in_header* hdr, const struct fuse_rename_in* req, const char* old_name, const char* new_name) { + bool has_rw; struct node* old_parent_node; struct node* new_parent_node; struct node* child_node; @@ -1067,6 +1078,7 @@ static int handle_rename(struct fuse* fuse, struct fuse_handler* handler, int res; pthread_mutex_lock(&fuse->lock); + has_rw = get_caller_has_rw_locked(fuse, hdr); old_parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, old_parent_path, sizeof(old_parent_path)); new_parent_node = lookup_node_and_path_by_id_locked(fuse, req->newdir, @@ -1079,11 +1091,11 @@ static int handle_rename(struct fuse* fuse, struct fuse_handler* handler, res = -ENOENT; goto lookup_error; } - if (!check_caller_access_to_name(fuse, hdr, old_parent_node, old_name, W_OK)) { + if (!check_caller_access_to_name(fuse, hdr, old_parent_node, old_name, W_OK, has_rw)) { res = -EACCES; goto lookup_error; } - if (!check_caller_access_to_name(fuse, hdr, new_parent_node, new_name, W_OK)) { + if (!check_caller_access_to_name(fuse, hdr, new_parent_node, new_name, W_OK, has_rw)) { res = -EACCES; goto lookup_error; } @@ -1146,12 +1158,14 @@ static int open_flags_to_access_mode(int open_flags) { static int handle_open(struct fuse* fuse, struct fuse_handler* handler, const struct fuse_in_header* hdr, const struct fuse_open_in* req) { + bool has_rw; struct node* node; char path[PATH_MAX]; struct fuse_open_out out; struct handle *h; pthread_mutex_lock(&fuse->lock); + has_rw = get_caller_has_rw_locked(fuse, hdr); node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, path, sizeof(path)); TRACE("[%d] OPEN 0%o @ %llx (%s)\n", handler->token, req->flags, hdr->nodeid, node ? node->name : "?"); @@ -1160,7 +1174,8 @@ static int handle_open(struct fuse* fuse, struct fuse_handler* handler, if (!node) { return -ENOENT; } - if (!check_caller_access_to_node(fuse, hdr, node, open_flags_to_access_mode(req->flags))) { + if (!check_caller_access_to_node(fuse, hdr, node, + open_flags_to_access_mode(req->flags), has_rw)) { return -EACCES; } h = malloc(sizeof(*h)); @@ -1307,7 +1322,7 @@ static int handle_opendir(struct fuse* fuse, struct fuse_handler* handler, if (!node) { return -ENOENT; } - if (!check_caller_access_to_node(fuse, hdr, node, R_OK)) { + if (!check_caller_access_to_node(fuse, hdr, node, R_OK, false)) { return -EACCES; } h = malloc(sizeof(*h)); From 39ff0ae0f66d7bc1499b30c9f75e187d329382ec Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Fri, 30 Aug 2013 13:58:13 -0700 Subject: [PATCH 519/541] Only check caller when deriving permissions. Bug: 10547597 Change-Id: Ied909f9047c2567e93dde0f4658d6e4b9ff161ab --- sdcard/sdcard.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sdcard/sdcard.c b/sdcard/sdcard.c index 330d555b5..9a1dd17b7 100644 --- a/sdcard/sdcard.c +++ b/sdcard/sdcard.c @@ -488,6 +488,11 @@ static void derive_permissions_locked(struct fuse* fuse, struct node *parent, /* Return if the calling UID holds sdcard_rw. */ static bool get_caller_has_rw_locked(struct fuse* fuse, const struct fuse_in_header *hdr) { + /* No additional permissions enforcement */ + if (fuse->derive == DERIVE_NONE) { + return true; + } + appid_t appid = multiuser_get_app_id(hdr->uid); return hashmapContainsKey(fuse->appid_with_rw, (void*) appid); } From ec0639a58a8c018dd8742762f1c4899e013ffc65 Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Fri, 30 Aug 2013 10:26:15 -0700 Subject: [PATCH 520/541] Fix recursive locking bug. handle_rename() would end up acquiring the lock twice. Change to always derive has_rw inside earlier locks (instead of acquiring a second time), and pass the value into check_caller_access_to_name(). Bug: 10547597 Change-Id: If5744d6d226a4785676c19d0f7fdf1c05060ed76 --- sdcard/sdcard.c | 57 +++++++++++++++++++++++++++++++------------------ 1 file changed, 36 insertions(+), 21 deletions(-) diff --git a/sdcard/sdcard.c b/sdcard/sdcard.c index 7349b2241..330d555b5 100644 --- a/sdcard/sdcard.c +++ b/sdcard/sdcard.c @@ -486,12 +486,18 @@ static void derive_permissions_locked(struct fuse* fuse, struct node *parent, } } +/* Return if the calling UID holds sdcard_rw. */ +static bool get_caller_has_rw_locked(struct fuse* fuse, const struct fuse_in_header *hdr) { + appid_t appid = multiuser_get_app_id(hdr->uid); + return hashmapContainsKey(fuse->appid_with_rw, (void*) appid); +} + /* Kernel has already enforced everything we returned through * derive_permissions_locked(), so this is used to lock down access * even further, such as enforcing that apps hold sdcard_rw. */ static bool check_caller_access_to_name(struct fuse* fuse, const struct fuse_in_header *hdr, const struct node* parent_node, - const char* name, int mode) { + const char* name, int mode, bool has_rw) { /* Always block security-sensitive files at root */ if (parent_node && parent_node->perm == PERM_ROOT) { if (!strcmp(name, "autorun.inf") @@ -518,13 +524,7 @@ static bool check_caller_access_to_name(struct fuse* fuse, return true; } - appid_t appid = multiuser_get_app_id(hdr->uid); - - pthread_mutex_lock(&fuse->lock); - bool hasRw = hashmapContainsKey(fuse->appid_with_rw, (void*) appid); - pthread_mutex_unlock(&fuse->lock); - - return hasRw; + return has_rw; } /* No extra permissions to enforce */ @@ -532,8 +532,8 @@ static bool check_caller_access_to_name(struct fuse* fuse, } static bool check_caller_access_to_node(struct fuse* fuse, - const struct fuse_in_header *hdr, const struct node* node, int mode) { - return check_caller_access_to_name(fuse, hdr, node->parent, node->name, mode); + const struct fuse_in_header *hdr, const struct node* node, int mode, bool has_rw) { + return check_caller_access_to_name(fuse, hdr, node->parent, node->name, mode, has_rw); } struct node *create_node_locked(struct fuse* fuse, @@ -831,7 +831,7 @@ static int handle_lookup(struct fuse* fuse, struct fuse_handler* handler, child_path, sizeof(child_path), 1))) { return -ENOENT; } - if (!check_caller_access_to_name(fuse, hdr, parent_node, name, R_OK)) { + if (!check_caller_access_to_name(fuse, hdr, parent_node, name, R_OK, false)) { return -EACCES; } @@ -872,7 +872,7 @@ static int handle_getattr(struct fuse* fuse, struct fuse_handler* handler, if (!node) { return -ENOENT; } - if (!check_caller_access_to_node(fuse, hdr, node, R_OK)) { + if (!check_caller_access_to_node(fuse, hdr, node, R_OK, false)) { return -EACCES; } @@ -882,11 +882,13 @@ static int handle_getattr(struct fuse* fuse, struct fuse_handler* handler, static int handle_setattr(struct fuse* fuse, struct fuse_handler* handler, const struct fuse_in_header *hdr, const struct fuse_setattr_in *req) { + bool has_rw; struct node* node; char path[PATH_MAX]; struct timespec times[2]; pthread_mutex_lock(&fuse->lock); + has_rw = get_caller_has_rw_locked(fuse, hdr); node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, path, sizeof(path)); TRACE("[%d] SETATTR fh=%llx valid=%x @ %llx (%s)\n", handler->token, req->fh, req->valid, hdr->nodeid, node ? node->name : "?"); @@ -895,7 +897,7 @@ static int handle_setattr(struct fuse* fuse, struct fuse_handler* handler, if (!node) { return -ENOENT; } - if (!check_caller_access_to_node(fuse, hdr, node, W_OK)) { + if (!check_caller_access_to_node(fuse, hdr, node, W_OK, has_rw)) { return -EACCES; } @@ -943,12 +945,14 @@ static int handle_setattr(struct fuse* fuse, struct fuse_handler* handler, static int handle_mknod(struct fuse* fuse, struct fuse_handler* handler, const struct fuse_in_header* hdr, const struct fuse_mknod_in* req, const char* name) { + bool has_rw; struct node* parent_node; char parent_path[PATH_MAX]; char child_path[PATH_MAX]; const char* actual_name; pthread_mutex_lock(&fuse->lock); + has_rw = get_caller_has_rw_locked(fuse, hdr); parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, parent_path, sizeof(parent_path)); TRACE("[%d] MKNOD %s 0%o @ %llx (%s)\n", handler->token, @@ -959,7 +963,7 @@ static int handle_mknod(struct fuse* fuse, struct fuse_handler* handler, child_path, sizeof(child_path), 1))) { return -ENOENT; } - if (!check_caller_access_to_name(fuse, hdr, parent_node, name, W_OK)) { + if (!check_caller_access_to_name(fuse, hdr, parent_node, name, W_OK, has_rw)) { return -EACCES; } __u32 mode = (req->mode & (~0777)) | 0664; @@ -972,12 +976,14 @@ static int handle_mknod(struct fuse* fuse, struct fuse_handler* handler, static int handle_mkdir(struct fuse* fuse, struct fuse_handler* handler, const struct fuse_in_header* hdr, const struct fuse_mkdir_in* req, const char* name) { + bool has_rw; struct node* parent_node; char parent_path[PATH_MAX]; char child_path[PATH_MAX]; const char* actual_name; pthread_mutex_lock(&fuse->lock); + has_rw = get_caller_has_rw_locked(fuse, hdr); parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, parent_path, sizeof(parent_path)); TRACE("[%d] MKDIR %s 0%o @ %llx (%s)\n", handler->token, @@ -988,7 +994,7 @@ static int handle_mkdir(struct fuse* fuse, struct fuse_handler* handler, child_path, sizeof(child_path), 1))) { return -ENOENT; } - if (!check_caller_access_to_name(fuse, hdr, parent_node, name, W_OK)) { + if (!check_caller_access_to_name(fuse, hdr, parent_node, name, W_OK, has_rw)) { return -EACCES; } __u32 mode = (req->mode & (~0777)) | 0775; @@ -1001,11 +1007,13 @@ static int handle_mkdir(struct fuse* fuse, struct fuse_handler* handler, static int handle_unlink(struct fuse* fuse, struct fuse_handler* handler, const struct fuse_in_header* hdr, const char* name) { + bool has_rw; struct node* parent_node; char parent_path[PATH_MAX]; char child_path[PATH_MAX]; pthread_mutex_lock(&fuse->lock); + has_rw = get_caller_has_rw_locked(fuse, hdr); parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, parent_path, sizeof(parent_path)); TRACE("[%d] UNLINK %s @ %llx (%s)\n", handler->token, @@ -1016,7 +1024,7 @@ static int handle_unlink(struct fuse* fuse, struct fuse_handler* handler, child_path, sizeof(child_path), 1)) { return -ENOENT; } - if (!check_caller_access_to_name(fuse, hdr, parent_node, name, W_OK)) { + if (!check_caller_access_to_name(fuse, hdr, parent_node, name, W_OK, has_rw)) { return -EACCES; } if (unlink(child_path) < 0) { @@ -1028,11 +1036,13 @@ static int handle_unlink(struct fuse* fuse, struct fuse_handler* handler, static int handle_rmdir(struct fuse* fuse, struct fuse_handler* handler, const struct fuse_in_header* hdr, const char* name) { + bool has_rw; struct node* parent_node; char parent_path[PATH_MAX]; char child_path[PATH_MAX]; pthread_mutex_lock(&fuse->lock); + has_rw = get_caller_has_rw_locked(fuse, hdr); parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, parent_path, sizeof(parent_path)); TRACE("[%d] RMDIR %s @ %llx (%s)\n", handler->token, @@ -1043,7 +1053,7 @@ static int handle_rmdir(struct fuse* fuse, struct fuse_handler* handler, child_path, sizeof(child_path), 1)) { return -ENOENT; } - if (!check_caller_access_to_name(fuse, hdr, parent_node, name, W_OK)) { + if (!check_caller_access_to_name(fuse, hdr, parent_node, name, W_OK, has_rw)) { return -EACCES; } if (rmdir(child_path) < 0) { @@ -1056,6 +1066,7 @@ static int handle_rename(struct fuse* fuse, struct fuse_handler* handler, const struct fuse_in_header* hdr, const struct fuse_rename_in* req, const char* old_name, const char* new_name) { + bool has_rw; struct node* old_parent_node; struct node* new_parent_node; struct node* child_node; @@ -1067,6 +1078,7 @@ static int handle_rename(struct fuse* fuse, struct fuse_handler* handler, int res; pthread_mutex_lock(&fuse->lock); + has_rw = get_caller_has_rw_locked(fuse, hdr); old_parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, old_parent_path, sizeof(old_parent_path)); new_parent_node = lookup_node_and_path_by_id_locked(fuse, req->newdir, @@ -1079,11 +1091,11 @@ static int handle_rename(struct fuse* fuse, struct fuse_handler* handler, res = -ENOENT; goto lookup_error; } - if (!check_caller_access_to_name(fuse, hdr, old_parent_node, old_name, W_OK)) { + if (!check_caller_access_to_name(fuse, hdr, old_parent_node, old_name, W_OK, has_rw)) { res = -EACCES; goto lookup_error; } - if (!check_caller_access_to_name(fuse, hdr, new_parent_node, new_name, W_OK)) { + if (!check_caller_access_to_name(fuse, hdr, new_parent_node, new_name, W_OK, has_rw)) { res = -EACCES; goto lookup_error; } @@ -1146,12 +1158,14 @@ static int open_flags_to_access_mode(int open_flags) { static int handle_open(struct fuse* fuse, struct fuse_handler* handler, const struct fuse_in_header* hdr, const struct fuse_open_in* req) { + bool has_rw; struct node* node; char path[PATH_MAX]; struct fuse_open_out out; struct handle *h; pthread_mutex_lock(&fuse->lock); + has_rw = get_caller_has_rw_locked(fuse, hdr); node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, path, sizeof(path)); TRACE("[%d] OPEN 0%o @ %llx (%s)\n", handler->token, req->flags, hdr->nodeid, node ? node->name : "?"); @@ -1160,7 +1174,8 @@ static int handle_open(struct fuse* fuse, struct fuse_handler* handler, if (!node) { return -ENOENT; } - if (!check_caller_access_to_node(fuse, hdr, node, open_flags_to_access_mode(req->flags))) { + if (!check_caller_access_to_node(fuse, hdr, node, + open_flags_to_access_mode(req->flags), has_rw)) { return -EACCES; } h = malloc(sizeof(*h)); @@ -1307,7 +1322,7 @@ static int handle_opendir(struct fuse* fuse, struct fuse_handler* handler, if (!node) { return -ENOENT; } - if (!check_caller_access_to_node(fuse, hdr, node, R_OK)) { + if (!check_caller_access_to_node(fuse, hdr, node, R_OK, false)) { return -EACCES; } h = malloc(sizeof(*h)); From c6480909e5819e7e9eae4b3c9864cc5bef704d21 Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Fri, 30 Aug 2013 13:58:13 -0700 Subject: [PATCH 521/541] Only check caller when deriving permissions. Bug: 10547597 Change-Id: Ied909f9047c2567e93dde0f4658d6e4b9ff161ab --- sdcard/sdcard.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sdcard/sdcard.c b/sdcard/sdcard.c index 330d555b5..9a1dd17b7 100644 --- a/sdcard/sdcard.c +++ b/sdcard/sdcard.c @@ -488,6 +488,11 @@ static void derive_permissions_locked(struct fuse* fuse, struct node *parent, /* Return if the calling UID holds sdcard_rw. */ static bool get_caller_has_rw_locked(struct fuse* fuse, const struct fuse_in_header *hdr) { + /* No additional permissions enforcement */ + if (fuse->derive == DERIVE_NONE) { + return true; + } + appid_t appid = multiuser_get_app_id(hdr->uid); return hashmapContainsKey(fuse->appid_with_rw, (void*) appid); } From f34861346d5c207912075fba9874090e4c947869 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Tue, 3 Sep 2013 00:25:14 +0900 Subject: [PATCH 522/541] Switch back to subsystem "net" for netlink events. The change to enable address tracking via netlink incorrectly changed the subsystem of rtnetlink events from "net" to "interface". This broke interface add/delete notifications, which come from the kernel with subsystem "net". Switch back to "net" and deal with address tracking via new action codes instead of a new subsystem. Bug: 10433320 Change-Id: Ibf30efb426949dfd02304cc1d9adb1c005a539a6 --- include/sysutils/NetlinkEvent.h | 2 ++ libsysutils/src/NetlinkEvent.cpp | 11 +++++++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/include/sysutils/NetlinkEvent.h b/include/sysutils/NetlinkEvent.h index 2a734cb89..f3501cf75 100644 --- a/include/sysutils/NetlinkEvent.h +++ b/include/sysutils/NetlinkEvent.h @@ -34,6 +34,8 @@ public: const static int NlActionChange; const static int NlActionLinkDown; const static int NlActionLinkUp; + const static int NlActionAddressUpdated; + const static int NlActionAddressRemoved; NetlinkEvent(); virtual ~NetlinkEvent(); diff --git a/libsysutils/src/NetlinkEvent.cpp b/libsysutils/src/NetlinkEvent.cpp index 01bec7772..aae2ae731 100644 --- a/libsysutils/src/NetlinkEvent.cpp +++ b/libsysutils/src/NetlinkEvent.cpp @@ -42,6 +42,8 @@ const int NetlinkEvent::NlActionRemove = 2; const int NetlinkEvent::NlActionChange = 3; const int NetlinkEvent::NlActionLinkUp = 4; const int NetlinkEvent::NlActionLinkDown = 5; +const int NetlinkEvent::NlActionAddressUpdated = 6; +const int NetlinkEvent::NlActionAddressRemoved = 7; NetlinkEvent::NetlinkEvent() { mAction = NlActionUnknown; @@ -131,11 +133,12 @@ bool NetlinkEvent::parseIfAddrMessage(int type, struct ifaddrmsg *ifaddr, } // Fill in interface information. - mAction = (type == RTM_NEWADDR) ? NlActionAdd : NlActionRemove; - mSubsystem = strdup("address"); + mAction = (type == RTM_NEWADDR) ? NlActionAddressUpdated : + NlActionAddressRemoved; + mSubsystem = strdup("net"); asprintf(&mParams[0], "ADDRESS=%s/%d", addrstr, ifaddr->ifa_prefixlen); - asprintf(&mParams[1], "IFACE=%s", ifname); + asprintf(&mParams[1], "INTERFACE=%s", ifname); asprintf(&mParams[2], "FLAGS=%u", ifaddr->ifa_flags); asprintf(&mParams[3], "SCOPE=%u", ifaddr->ifa_scope); } else if (rta->rta_type == IFA_CACHEINFO) { @@ -205,7 +208,7 @@ bool NetlinkEvent::parseBinaryNetlinkMessage(char *buffer, int size) { mParams[0] = strdup(buffer); mAction = (ifi->ifi_flags & IFF_LOWER_UP) ? NlActionLinkUp : NlActionLinkDown; - mSubsystem = strdup("interface"); + mSubsystem = strdup("net"); break; } From fc600e49bf9e0977b8e91642480e065bd95f2670 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Fri, 6 Sep 2013 16:41:40 -0700 Subject: [PATCH 523/541] Initial libmemtrack libmemtrack is an interface to a device-specific Memory Tracker HAL to account for memory that may not show up in the normal memory usage tools that walk /proc/pid/maps. Bug: 10294768 Change-Id: I436f6799898df0bf8bf29747be3bc9dea5721185 --- libmemtrack/Android.mk | 20 ++++ libmemtrack/include/memtrack.h | 138 +++++++++++++++++++++++ libmemtrack/memtrack.c | 200 +++++++++++++++++++++++++++++++++ libmemtrack/memtrack_test.c | 145 ++++++++++++++++++++++++ 4 files changed, 503 insertions(+) create mode 100644 libmemtrack/Android.mk create mode 100644 libmemtrack/include/memtrack.h create mode 100644 libmemtrack/memtrack.c create mode 100644 libmemtrack/memtrack_test.c diff --git a/libmemtrack/Android.mk b/libmemtrack/Android.mk new file mode 100644 index 000000000..c23b6f410 --- /dev/null +++ b/libmemtrack/Android.mk @@ -0,0 +1,20 @@ +# Copyright 2013 The Android Open Source Project + +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include +LOCAL_SRC_FILES := memtrack.c +LOCAL_MODULE := libmemtrack +LOCAL_C_INCLUDES += $(LOCAL_PATH)/include hardware/libhardware/include +LOCAL_SHARED_LIBRARIES := libhardware liblog +LOCAL_CFLAGS := -Wall -Werror +include $(BUILD_SHARED_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_SRC_FILES := memtrack_test.c +LOCAL_MODULE := memtrack_test +LOCAL_C_INCLUDES := $(call include-path-for, libpagemap) +LOCAL_SHARED_LIBRARIES := libmemtrack libpagemap +LOCAL_CFLAGS := -Wall -Werror +include $(BUILD_EXECUTABLE) diff --git a/libmemtrack/include/memtrack.h b/libmemtrack/include/memtrack.h new file mode 100644 index 000000000..d6b370b22 --- /dev/null +++ b/libmemtrack/include/memtrack.h @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2013 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 _LIBMEMTRACK_MEMTRACK_H_ +#define _LIBMEMTRACK_MEMTRACK_H_ + +#include +#include + +/** + * struct memtrack_proc + * + * an opaque handle to the memory stats on a process. + * Created with memtrack_proc_new, destroyed by + * memtrack_proc_destroy. Can be reused multiple times with + * memtrack_proc_get. + */ +struct memtrack_proc; + +/** + * memtrack_init + * + * Must be called once before calling any other functions. After this function + * is called, everything else is thread-safe. + * + * Returns 0 on success, -errno on error. + */ +int memtrack_init(void); + +/** + * memtrack_proc_new + * + * Return a new handle to hold process memory stats. + * + * Returns NULL on error. + */ +struct memtrack_proc *memtrack_proc_new(void); + +/** + * memtrack_proc_destroy + * + * Free all memory associated with a process memory stats handle. + */ +void memtrack_proc_destroy(struct memtrack_proc *p); + +/** + * memtrack_proc_get + * + * Fill a process memory stats handle with data about the given pid. Can be + * called on a handle that was just allocated with memtrack_proc_new, + * or on a handle that has been previously passed to memtrack_proc_get + * to replace the data with new data on the same or another process. It is + * expected that the second call on the same handle should not require + * allocating any new memory. + * + * Returns 0 on success, -errno on error. + */ +int memtrack_proc_get(struct memtrack_proc *p, pid_t pid); + +/** + * memtrack_proc_graphics_total + * + * Return total amount of memory that has been allocated for use as window + * buffers. Does not differentiate between memory that has already been + * accounted for by reading /proc/pid/smaps and memory that has not been + * accounted for. + * + * Returns non-negative size in bytes on success, -errno on error. + */ +ssize_t memtrack_proc_graphics_total(struct memtrack_proc *p); + +/** + * memtrack_proc_graphics_pss + * + * Return total amount of memory that has been allocated for use as window + * buffers, but has not already been accounted for by reading /proc/pid/smaps. + * Memory that is shared across processes may already be divided by the + * number of processes that share it (preferred), or may be charged in full to + * every process that shares it, depending on the capabilities of the driver. + * + * Returns non-negative size in bytes on success, -errno on error. + */ +ssize_t memtrack_proc_graphics_pss(struct memtrack_proc *p); + +/** + * memtrack_proc_gl_total + * + * Same as memtrack_proc_graphics_total, but counts GL memory (which + * should not overlap with graphics memory) instead of graphics memory. + * + * Returns non-negative size in bytes on success, -errno on error. + */ +ssize_t memtrack_proc_gl_total(struct memtrack_proc *p); + +/** + * memtrack_proc_gl_pss + * + * Same as memtrack_proc_graphics_total, but counts GL memory (which + * should not overlap with graphics memory) instead of graphics memory. + * + * Returns non-negative size in bytes on success, -errno on error. + */ +ssize_t memtrack_proc_gl_pss(struct memtrack_proc *p); + +/** + * memtrack_proc_gl_total + * + * Same as memtrack_proc_graphics_total, but counts miscellaneous memory + * not tracked by gl or graphics calls above. + * + * Returns non-negative size in bytes on success, -errno on error. + */ +ssize_t memtrack_proc_other_total(struct memtrack_proc *p); + +/** + * memtrack_proc_gl_pss + * + * Same as memtrack_proc_graphics_total, but counts miscellaneous memory + * not tracked by gl or graphics calls above. + * + * Returns non-negative size in bytes on success, -errno on error. + */ +ssize_t memtrack_proc_other_pss(struct memtrack_proc *p); + +#endif diff --git a/libmemtrack/memtrack.c b/libmemtrack/memtrack.c new file mode 100644 index 000000000..2b2651a28 --- /dev/null +++ b/libmemtrack/memtrack.c @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2013 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 + +#define LOG_TAG "memtrack" + +#include + +#include + +#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) + +static const memtrack_module_t *module; + +struct memtrack_proc { + pid_t pid; + struct memtrack_proc_type { + enum memtrack_type type; + size_t num_records; + size_t allocated_records; + struct memtrack_record *records; + } types[MEMTRACK_NUM_TYPES]; +}; + +int memtrack_init(void) +{ + int err; + + if (module) { + return 0; + } + + err = hw_get_module(MEMTRACK_HARDWARE_MODULE_ID, + (hw_module_t const**)&module); + if (err) { + ALOGE("Couldn't load %s module (%s)", MEMTRACK_HARDWARE_MODULE_ID, + strerror(-err)); + return err; + } + + return module->init(module); +} + +struct memtrack_proc *memtrack_proc_new(void) +{ + if (!module) { + return NULL; + } + + return calloc(sizeof(struct memtrack_proc), 1); +} + +void memtrack_proc_destroy(struct memtrack_proc *p) +{ + enum memtrack_type i; + + if (p) { + for (i = 0; i < MEMTRACK_NUM_TYPES; i++) { + free(p->types[i].records); + } + } + free(p); +} + +static int memtrack_proc_get_type(struct memtrack_proc_type *t, + pid_t pid, enum memtrack_type type) +{ + size_t num_records = t->num_records; + int ret; + +retry: + ret = module->getMemory(module, pid, type, t->records, &num_records); + if (ret) { + t->num_records = 0; + return ret; + } + if (num_records > t->allocated_records) { + /* Need more records than allocated */ + free(t->records); + t->records = calloc(sizeof(*t->records), num_records); + if (!t->records) { + return -ENOMEM; + } + t->allocated_records = num_records; + goto retry; + } + t->num_records = num_records; + + return 0; +} + +/* TODO: sanity checks on return values from HALs: + * make sure no records have invalid flags set + * - unknown flags + * - too many flags of a single category + * - missing ACCOUNTED/UNACCOUNTED + * make sure there are not overlapping SHARED and SHARED_PSS records + */ +static int memtrack_proc_sanity_check(struct memtrack_proc *p) +{ + (void)p; + return 0; +} + +int memtrack_proc_get(struct memtrack_proc *p, pid_t pid) +{ + enum memtrack_type i; + + if (!module) { + return -EINVAL; + } + + if (!p) { + return -EINVAL; + } + + p->pid = pid; + for (i = 0; i < MEMTRACK_NUM_TYPES; i++) { + memtrack_proc_get_type(&p->types[i], pid, i); + } + + return memtrack_proc_sanity_check(p); +} + +static ssize_t memtrack_proc_sum(struct memtrack_proc *p, + enum memtrack_type types[], size_t num_types, + unsigned int flags) +{ + ssize_t sum = 0; + size_t i; + size_t j; + + for (i = 0; i < num_types; i++) { + enum memtrack_type type = types[i]; + for (j = 0; j < p->types[type].num_records; j++) { + if ((p->types[type].records[j].flags & flags) == flags) { + sum += p->types[type].records[j].size_in_bytes; + } + } + } + + return sum; +} + +ssize_t memtrack_proc_graphics_total(struct memtrack_proc *p) +{ + enum memtrack_type types[] = { MEMTRACK_TYPE_GRAPHICS }; + return memtrack_proc_sum(p, types, ARRAY_SIZE(types), 0); +} + +ssize_t memtrack_proc_graphics_pss(struct memtrack_proc *p) +{ + enum memtrack_type types[] = { MEMTRACK_TYPE_GRAPHICS }; + return memtrack_proc_sum(p, types, ARRAY_SIZE(types), + MEMTRACK_FLAG_SMAPS_UNACCOUNTED); +} + +ssize_t memtrack_proc_gl_total(struct memtrack_proc *p) +{ + enum memtrack_type types[] = { MEMTRACK_TYPE_GL }; + return memtrack_proc_sum(p, types, ARRAY_SIZE(types), 0); +} + +ssize_t memtrack_proc_gl_pss(struct memtrack_proc *p) +{ + enum memtrack_type types[] = { MEMTRACK_TYPE_GL }; + return memtrack_proc_sum(p, types, ARRAY_SIZE(types), + MEMTRACK_FLAG_SMAPS_UNACCOUNTED); +} + +ssize_t memtrack_proc_other_total(struct memtrack_proc *p) +{ + enum memtrack_type types[] = { MEMTRACK_TYPE_MULTIMEDIA, + MEMTRACK_TYPE_CAMERA, + MEMTRACK_TYPE_OTHER }; + return memtrack_proc_sum(p, types, ARRAY_SIZE(types), 0); +} + +ssize_t memtrack_proc_other_pss(struct memtrack_proc *p) +{ + enum memtrack_type types[] = { MEMTRACK_TYPE_MULTIMEDIA, + MEMTRACK_TYPE_CAMERA, + MEMTRACK_TYPE_OTHER }; + return memtrack_proc_sum(p, types, ARRAY_SIZE(types), + MEMTRACK_FLAG_SMAPS_UNACCOUNTED); +} diff --git a/libmemtrack/memtrack_test.c b/libmemtrack/memtrack_test.c new file mode 100644 index 000000000..f306f67f1 --- /dev/null +++ b/libmemtrack/memtrack_test.c @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2013 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 +#include +#include +#include + +#include + +#include + +#define DIV_ROUND_UP(x,y) (((x) + (y) - 1) / (y)) + +static int getprocname(pid_t pid, char *buf, int len) { + char *filename; + FILE *f; + int rc = 0; + static const char* unknown_cmdline = ""; + + if (len <= 0) { + return -1; + } + + if (asprintf(&filename, "/proc/%zd/cmdline", pid) < 0) { + rc = 1; + goto exit; + } + + f = fopen(filename, "r"); + if (f == NULL) { + rc = 2; + goto releasefilename; + } + + if (fgets(buf, len, f) == NULL) { + rc = 3; + goto closefile; + } + +closefile: + (void) fclose(f); +releasefilename: + free(filename); +exit: + if (rc != 0) { + /* + * The process went away before we could read its process name. Try + * to give the user "" here, but otherwise they get to look + * at a blank. + */ + if (strlcpy(buf, unknown_cmdline, (size_t)len) >= (size_t)len) { + rc = 4; + } + } + + return rc; +} + +int main(int argc, char *argv[]) +{ + int ret; + pm_kernel_t *ker; + size_t num_procs; + pid_t *pids; + struct memtrack_proc *p; + size_t i; + + (void)argc; + (void)argv; + + ret = memtrack_init(); + if (ret < 0) { + fprintf(stderr, "failed to initialize HAL: %s (%d)\n", strerror(-ret), ret); + exit(EXIT_FAILURE); + } + + ret = pm_kernel_create(&ker); + if (ret) { + fprintf(stderr, "Error creating kernel interface -- " + "does this kernel have pagemap?\n"); + exit(EXIT_FAILURE); + } + + ret = pm_kernel_pids(ker, &pids, &num_procs); + if (ret) { + fprintf(stderr, "Error listing processes.\n"); + exit(EXIT_FAILURE); + } + + p = memtrack_proc_new(); + if (ret) { + fprintf(stderr, "failed to create memtrack process handle\n"); + exit(EXIT_FAILURE); + } + + for (i = 0; i < num_procs; i++) { + pid_t pid = pids[i]; + char cmdline[256]; + size_t v1; + size_t v2; + size_t v3; + size_t v4; + size_t v5; + size_t v6; + + getprocname(pid, cmdline, (int)sizeof(cmdline)); + + ret = memtrack_proc_get(p, pid); + if (ret) { + fprintf(stderr, "failed to get memory info for pid %d: %s (%d)\n", + pid, strerror(-ret), ret); + continue; + } + + v1 = DIV_ROUND_UP(memtrack_proc_graphics_total(p), 1024); + v2 = DIV_ROUND_UP(memtrack_proc_graphics_pss(p), 1024); + v3 = DIV_ROUND_UP(memtrack_proc_gl_total(p), 1024); + v4 = DIV_ROUND_UP(memtrack_proc_gl_pss(p), 1024); + v5 = DIV_ROUND_UP(memtrack_proc_other_total(p), 1024); + v6 = DIV_ROUND_UP(memtrack_proc_other_pss(p), 1024); + + if (v1 | v2 | v3 | v4 | v5 | v6) { + printf("%5d %6zu %6zu %6zu %6zu %6zu %6zu %s\n", pid, + v1, v2, v3, v4, v5, v6, cmdline); + } + } + + memtrack_proc_destroy(p); + + return 0; +} From ff9ec2d9990fbf9a707df063f3d43e9169bdde16 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Mon, 9 Sep 2013 14:30:55 -0700 Subject: [PATCH 524/541] healthd: perform periodic chores when awake and on battery power Monitor battery/charging status at normal awake rate when on battery power. Bug: 10650797 Change-Id: I914d24af4963ab9d52b03c0a2615ad653ced9b12 --- healthd/healthd.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/healthd/healthd.cpp b/healthd/healthd.cpp index 1719c22f6..9b84c3e8f 100644 --- a/healthd/healthd.cpp +++ b/healthd/healthd.cpp @@ -249,6 +249,9 @@ static void healthd_mainloop(void) { if (events[n].data.ptr) (*(void (*)())events[n].data.ptr)(); } + + if (!nevents) + periodic_chores(); } return; From 04c12ca061cd4ffe79a8eb5d570c4830a0950c85 Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Mon, 9 Sep 2013 10:39:54 -0700 Subject: [PATCH 525/541] Add HOTWORD audio source to audio.h - This is a complimentary change to the one adding this new source to MediaRecorder.java Bug: 10640877. Change-Id: Ie213e82b5a60dad2a277cef367c2f5e4df70df06 --- include/system/audio.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/system/audio.h b/include/system/audio.h index b90f4a90e..aa7ac021a 100644 --- a/include/system/audio.h +++ b/include/system/audio.h @@ -72,6 +72,11 @@ typedef enum { /* play the mix captured by this audio source. */ AUDIO_SOURCE_CNT, AUDIO_SOURCE_MAX = AUDIO_SOURCE_CNT - 1, + AUDIO_SOURCE_HOTWORD = 1999, /* A low-priority, preemptible audio source for + for background software hotword detection. + Same tuning as AUDIO_SOURCE_VOICE_RECOGNITION. + Used only internally to the framework. Not exposed + at the audio HAL. */ } audio_source_t; /* special audio session values From 67b00d8b2d96e8133c249bcbc0fb63c49e10e022 Mon Sep 17 00:00:00 2001 From: Rom Lemarchand Date: Tue, 10 Sep 2013 17:39:30 -0700 Subject: [PATCH 526/541] init.rc: change mem cgroups permissions Changing mem cgroups permissions to only be accessible by root and system. Bug: 10210529 Bug: 10210900 Change-Id: Ib4fff6f49b33013b3629d40ae98a5e2464571b2d --- rootdir/init.rc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rootdir/init.rc b/rootdir/init.rc index 6140ba1ab..19ab6cc7e 100644 --- a/rootdir/init.rc +++ b/rootdir/init.rc @@ -42,13 +42,13 @@ loglevel 3 mkdir /acct/uid # Create cgroup mount point for memory - mount tmpfs none /sys/fs/cgroup - mkdir /sys/fs/cgroup/memory + mount tmpfs none /sys/fs/cgroup mode=0750,uid=0,gid=1000 + mkdir /sys/fs/cgroup/memory 0750 root system mount cgroup none /sys/fs/cgroup/memory memory write /sys/fs/cgroup/memory/memory.move_charge_at_immigrate 1 chown root system /sys/fs/cgroup/memory/tasks chmod 0660 /sys/fs/cgroup/memory/tasks - mkdir /sys/fs/cgroup/memory/sw + mkdir /sys/fs/cgroup/memory/sw 0750 root system write /sys/fs/cgroup/memory/sw/memory.swappiness 100 write /sys/fs/cgroup/memory/sw/memory.move_charge_at_immigrate 1 chown root system /sys/fs/cgroup/memory/sw/tasks From 694636142113d91c2b9585ad28e143d4ff001584 Mon Sep 17 00:00:00 2001 From: Nick Kralevich Date: Fri, 13 Sep 2013 17:21:28 -0700 Subject: [PATCH 527/541] property_service: better validate property names Don't allow unexpected characters in property names. Don't allow double dots in property names. Bug: 10733330 Change-Id: I8d69740d697efb791f2f201f90989576e13bac81 --- init/property_service.c | 41 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/init/property_service.c b/init/property_service.c index 9afc7569a..9ac278169 100644 --- a/init/property_service.c +++ b/init/property_service.c @@ -276,6 +276,34 @@ static void write_persistent_property(const char *name, const char *value) } } +static bool is_legal_property_name(const char* name, size_t namelen) +{ + size_t i; + bool previous_was_dot = false; + if (namelen >= PROP_NAME_MAX) return false; + if (namelen < 1) return false; + if (name[0] == '.') return false; + if (name[namelen - 1] == '.') return false; + + /* Only allow alphanumeric, plus '.', '-', or '_' */ + /* Don't allow ".." to appear in a property name */ + for (i = 0; i < namelen; i++) { + if (name[i] == '.') { + if (previous_was_dot == true) return false; + previous_was_dot = true; + continue; + } + previous_was_dot = false; + if (name[i] == '_' || name[i] == '-') continue; + if (name[i] >= 'a' && name[i] <= 'z') continue; + if (name[i] >= 'A' && name[i] <= 'Z') continue; + if (name[i] >= '0' && name[i] <= '9') continue; + return false; + } + + return true; +} + int property_set(const char *name, const char *value) { prop_info *pi; @@ -284,9 +312,8 @@ int property_set(const char *name, const char *value) size_t namelen = strlen(name); size_t valuelen = strlen(value); - if(namelen >= PROP_NAME_MAX) return -1; - if(valuelen >= PROP_VALUE_MAX) return -1; - if(namelen < 1) return -1; + if (!is_legal_property_name(name, namelen)) return -1; + if (valuelen >= PROP_VALUE_MAX) return -1; pi = (prop_info*) __system_property_find(name); @@ -298,7 +325,7 @@ int property_set(const char *name, const char *value) } else { ret = __system_property_add(name, namelen, value, valuelen); if (ret < 0) { - ERROR("Failed to set '%s'='%s'", name, value); + ERROR("Failed to set '%s'='%s'\n", name, value); return ret; } } @@ -364,6 +391,12 @@ void handle_property_set_fd() msg.name[PROP_NAME_MAX-1] = 0; msg.value[PROP_VALUE_MAX-1] = 0; + if (!is_legal_property_name(msg.name, strlen(msg.name))) { + ERROR("sys_prop: illegal property name. Got: \"%s\"\n", msg.name); + close(s); + return; + } + getpeercon(s, &source_ctx); if(memcmp(msg.name,"ctl.",4) == 0) { From 5535b05120fa3fd3d68a09e01284aba35cc6e058 Mon Sep 17 00:00:00 2001 From: Nick Kralevich Date: Tue, 17 Sep 2013 14:43:12 -0700 Subject: [PATCH 528/541] write_file: introduce O_NOFOLLOW, use sane mask Don't follow symlinks when writing to a file. Don't create world-writable files. Bug: 10802869 Change-Id: Ifb55600d574307a535df878acb3347e02028cd30 --- init/builtins.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/init/builtins.c b/init/builtins.c index bfc0ddb15..e8c8f9165 100644 --- a/init/builtins.c +++ b/init/builtins.c @@ -57,7 +57,7 @@ static int write_file(const char *path, const char *value) { int fd, ret, len; - fd = open(path, O_WRONLY|O_CREAT, 0622); + fd = open(path, O_WRONLY|O_CREAT|O_NOFOLLOW, 0600); if (fd < 0) return -errno; From 96675ed91ffe1ff95e7b10edf2b285f46b6f5213 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Tue, 17 Sep 2013 23:48:54 -0700 Subject: [PATCH 529/541] Fix rotation in camera2 API Bug: 10804238 Change-Id: I093945789d9c6d373392fc9dfd18ec2c6058d3b9 --- include/system/graphics.h | 2 ++ include/system/window.h | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/include/system/graphics.h b/include/system/graphics.h index 154f9cb4c..be86ae42b 100644 --- a/include/system/graphics.h +++ b/include/system/graphics.h @@ -293,6 +293,8 @@ enum { HAL_TRANSFORM_ROT_180 = 0x03, /* rotate source image 270 degrees clockwise */ HAL_TRANSFORM_ROT_270 = 0x07, + /* don't use. see system/window.h */ + HAL_TRANSFORM_RESERVED = 0x08, }; #ifdef __cplusplus diff --git a/include/system/window.h b/include/system/window.h index 7ce59e02f..649bd71fd 100644 --- a/include/system/window.h +++ b/include/system/window.h @@ -296,12 +296,15 @@ enum { NATIVE_WINDOW_TRANSFORM_FLIP_H = HAL_TRANSFORM_FLIP_H , /* flip source image vertically */ NATIVE_WINDOW_TRANSFORM_FLIP_V = HAL_TRANSFORM_FLIP_V, - /* rotate source image 90 degrees clock-wise */ + /* rotate source image 90 degrees clock-wise, and is applied after TRANSFORM_FLIP_{H|V} */ NATIVE_WINDOW_TRANSFORM_ROT_90 = HAL_TRANSFORM_ROT_90, /* rotate source image 180 degrees */ NATIVE_WINDOW_TRANSFORM_ROT_180 = HAL_TRANSFORM_ROT_180, /* rotate source image 270 degrees clock-wise */ NATIVE_WINDOW_TRANSFORM_ROT_270 = HAL_TRANSFORM_ROT_270, + /* transforms source by the inverse transform of the screen it is displayed onto. This + * transform is applied last */ + NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY = 0x08 }; /* parameter for NATIVE_WINDOW_SET_SCALING_MODE */ From b410eb19130cf1eca158a672bba8f515e7627f11 Mon Sep 17 00:00:00 2001 From: Nick Kralevich Date: Tue, 17 Sep 2013 16:18:23 -0700 Subject: [PATCH 530/541] Initialize /dev/urandom earlier in boot. It's a security best practice to carry entropy across reboots. (see "man 4 random"). Currently, entropy saving and mixing occur in the system_server, via the EntropyMixer code. Unfortunately, the EntropyMixer code runs fairly late in the boot process, which means early boot doesn't have high quality entropy. This has caused security problems in the past. Load entropy data as soon as we can in the early boot process, so that we can get /dev/random / /dev/urandom into a "random" state earlier. Bug: 9983133 Change-Id: Id4a6f39e9060f30fe7497bd8f8085a9bec851e80 --- rootdir/init.rc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/rootdir/init.rc b/rootdir/init.rc index 19ab6cc7e..be74f6fc2 100644 --- a/rootdir/init.rc +++ b/rootdir/init.rc @@ -177,6 +177,9 @@ on post-fs-data # We restorecon /data in case the userdata partition has been reset. restorecon /data + # Avoid predictable entropy pool. Carry over entropy from previous boot. + copy /data/system/entropy.dat /dev/urandom + # Create dump dir and collect dumps. # Do this before we mount cache so eventually we can use cache for # storing dumps on platforms which do not have a dedicated dump partition. From f1921c79498cb2c048caf4f1725e74c22cae3e8f Mon Sep 17 00:00:00 2001 From: Jamie Gennis Date: Wed, 18 Sep 2013 12:01:18 -0700 Subject: [PATCH 531/541] trace: Add support for tracing 64-bit ints. Bug: 10624956 Change-Id: Ie1d7c8ac16b5a5ec1e63e4fb1863f5b1ab2b59e8 --- include/cutils/trace.h | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/include/cutils/trace.h b/include/cutils/trace.h index a0dd1e034..1c8f10728 100644 --- a/include/cutils/trace.h +++ b/include/cutils/trace.h @@ -259,6 +259,23 @@ static inline void atrace_int(uint64_t tag, const char* name, int32_t value) } } +/** + * Traces a 64-bit integer counter value. name is used to identify the + * counter. This can be used to track how a value changes over time. + */ +#define ATRACE_INT64(name, value) atrace_int64(ATRACE_TAG, name, value) +static inline void atrace_int64(uint64_t tag, const char* name, int64_t value) +{ + if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) { + char buf[ATRACE_MESSAGE_LENGTH]; + size_t len; + + len = snprintf(buf, ATRACE_MESSAGE_LENGTH, "C|%d|%s|%lld", + getpid(), name, value); + write(atrace_marker_fd, buf, len); + } +} + #else // not HAVE_ANDROID_OS #define ATRACE_INIT() From 44d6342caa0db1f613809e9ba1ea8d9af0183b74 Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Thu, 12 Sep 2013 09:44:48 -0700 Subject: [PATCH 532/541] Remove mkdir() side effect, add .nomedia, utils. Before this change, FUSE lookup() would have the side effect of creating the directory on behalf of apps. This resulted in most directories being created just by Settings trying to measure disk space. Instead, we're switching to have vold do directory creation when an app doesn't have enough permissions. Create fs_mkdirs() utility to create all parent directories in a path as needed. Allow traversal (+x) into /storage directories. Fix FUSE derived permissions to be case insensitive. Mark well-known directories as .nomedia when created. Bug: 10577808, 10330221 Change-Id: I53114f2e63ffbe6de4ba6a72d94a232523231cad --- include/cutils/fs.h | 8 ++++ libcutils/fs.c | 90 ++++++++++++++++++++++++++++++++++++ rootdir/init.rc | 2 +- sdcard/sdcard.c | 110 ++++++++++++++++++++++++-------------------- 4 files changed, 158 insertions(+), 52 deletions(-) diff --git a/include/cutils/fs.h b/include/cutils/fs.h index fd5296bb9..d1d4cf28d 100644 --- a/include/cutils/fs.h +++ b/include/cutils/fs.h @@ -55,6 +55,14 @@ extern int fs_read_atomic_int(const char* path, int* value); */ extern int fs_write_atomic_int(const char* path, int value); +/* + * Ensure that all directories along given path exist, creating parent + * directories as needed. Validates that given path is absolute and that + * it contains no relative "." or ".." paths or symlinks. Last path segment + * is treated as filename and ignored, unless the path ends with "/". + */ +extern int fs_mkdirs(const char* path, mode_t mode); + #ifdef __cplusplus } #endif diff --git a/libcutils/fs.c b/libcutils/fs.c index 116526dca..8d1da21c8 100644 --- a/libcutils/fs.c +++ b/libcutils/fs.c @@ -16,6 +16,11 @@ #define LOG_TAG "cutils" +/* These defines are only needed because prebuilt headers are out of date */ +#define __USE_XOPEN2K8 1 +#define _ATFILE_SOURCE 1 +#define _GNU_SOURCE 1 + #include #include @@ -27,6 +32,7 @@ #include #include #include +#include #define ALL_PERMS (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO) #define BUF_SIZE 64 @@ -141,3 +147,87 @@ fail_closed: unlink(temp); return -1; } + +int fs_mkdirs(const char* path, mode_t mode) { + int res = 0; + int fd = 0; + struct stat sb; + char* buf = strdup(path); + + if (*buf != '/') { + ALOGE("Relative paths are not allowed: %s", buf); + res = -EINVAL; + goto done; + } + + if ((fd = open("/", 0)) == -1) { + ALOGE("Failed to open(/): %s", strerror(errno)); + res = -errno; + goto done; + } + + char* segment = buf + 1; + char* p = segment; + while (*p != '\0') { + if (*p == '/') { + *p = '\0'; + + if (!strcmp(segment, "..") || !strcmp(segment, ".") || !strcmp(segment, "")) { + ALOGE("Invalid path: %s", buf); + res = -EINVAL; + goto done_close; + } + + if (fstatat(fd, segment, &sb, AT_SYMLINK_NOFOLLOW) != 0) { + if (errno == ENOENT) { + /* Nothing there yet; let's create it! */ + if (mkdirat(fd, segment, mode) != 0) { + if (errno == EEXIST) { + /* We raced with someone; ignore */ + } else { + ALOGE("Failed to mkdirat(%s): %s", buf, strerror(errno)); + res = -errno; + goto done_close; + } + } + } else { + ALOGE("Failed to fstatat(%s): %s", buf, strerror(errno)); + res = -errno; + goto done_close; + } + } else { + if (S_ISLNK(sb.st_mode)) { + ALOGE("Symbolic links are not allowed: %s", buf); + res = -ELOOP; + goto done_close; + } + if (!S_ISDIR(sb.st_mode)) { + ALOGE("Existing segment not a directory: %s", buf); + res = -ENOTDIR; + goto done_close; + } + } + + /* Yay, segment is ready for us to step into */ + int next_fd; + if ((next_fd = openat(fd, segment, 0)) == -1) { + ALOGE("Failed to openat(%s): %s", buf, strerror(errno)); + res = -errno; + goto done_close; + } + + close(fd); + fd = next_fd; + + *p = '/'; + segment = p + 1; + } + p++; + } + +done_close: + close(fd); +done: + free(buf); + return res; +} diff --git a/rootdir/init.rc b/rootdir/init.rc index be74f6fc2..cd995742f 100644 --- a/rootdir/init.rc +++ b/rootdir/init.rc @@ -61,7 +61,7 @@ loglevel 3 # See storage config details at http://source.android.com/tech/storage/ mkdir /mnt/shell 0700 shell shell - mkdir /storage 0050 root sdcard_r + mkdir /storage 0751 root sdcard_r # Directory for putting things only root should see. mkdir /mnt/secure 0700 root root diff --git a/sdcard/sdcard.c b/sdcard/sdcard.c index 9a1dd17b7..3f1e268e6 100644 --- a/sdcard/sdcard.c +++ b/sdcard/sdcard.c @@ -32,6 +32,7 @@ #include #include +#include #include #include @@ -193,8 +194,9 @@ static int str_hash(void *key) { return hashmapHash(key, strlen(key)); } -static bool str_equals(void *keyA, void *keyB) { - return strcmp(keyA, keyB) == 0; +/** Test if two string keys are equal ignoring case */ +static bool str_icase_equals(void *keyA, void *keyB) { + return strcasecmp(keyA, keyB) == 0; } static int int_hash(void *key) { @@ -401,6 +403,20 @@ static void attr_from_stat(struct fuse_attr *attr, const struct stat *s, const s attr->mode = (attr->mode & S_IFMT) | filtered_mode; } +static int touch(char* path, mode_t mode) { + int fd = open(path, O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW, mode); + if (fd == -1) { + if (errno == EEXIST) { + return 0; + } else { + ERROR("Failed to open(%s): %s\n", path, strerror(errno)); + return -1; + } + } + close(fd); + return 0; +} + static void derive_permissions_locked(struct fuse* fuse, struct node *parent, struct node *node) { appid_t appid; @@ -429,37 +445,37 @@ static void derive_permissions_locked(struct fuse* fuse, struct node *parent, case PERM_ROOT: /* Assume masked off by default. */ node->mode = 0770; - if (!strcmp(node->name, "Android")) { + if (!strcasecmp(node->name, "Android")) { /* App-specific directories inside; let anyone traverse */ node->perm = PERM_ANDROID; node->mode = 0771; } else if (fuse->split_perms) { - if (!strcmp(node->name, "DCIM") - || !strcmp(node->name, "Pictures")) { + if (!strcasecmp(node->name, "DCIM") + || !strcasecmp(node->name, "Pictures")) { node->gid = AID_SDCARD_PICS; - } else if (!strcmp(node->name, "Alarms") - || !strcmp(node->name, "Movies") - || !strcmp(node->name, "Music") - || !strcmp(node->name, "Notifications") - || !strcmp(node->name, "Podcasts") - || !strcmp(node->name, "Ringtones")) { + } else if (!strcasecmp(node->name, "Alarms") + || !strcasecmp(node->name, "Movies") + || !strcasecmp(node->name, "Music") + || !strcasecmp(node->name, "Notifications") + || !strcasecmp(node->name, "Podcasts") + || !strcasecmp(node->name, "Ringtones")) { node->gid = AID_SDCARD_AV; } } break; case PERM_ANDROID: - if (!strcmp(node->name, "data")) { + if (!strcasecmp(node->name, "data")) { /* App-specific directories inside; let anyone traverse */ node->perm = PERM_ANDROID_DATA; node->mode = 0771; - } else if (!strcmp(node->name, "obb")) { + } else if (!strcasecmp(node->name, "obb")) { /* App-specific directories inside; let anyone traverse */ node->perm = PERM_ANDROID_OBB; node->mode = 0771; /* Single OBB directory is always shared */ node->graft_path = fuse->obbpath; node->graft_pathlen = strlen(fuse->obbpath); - } else if (!strcmp(node->name, "user")) { + } else if (!strcasecmp(node->name, "user")) { /* User directories must only be accessible to system, protected * by sdcard_all. Zygote will bind mount the appropriate user- * specific path. */ @@ -505,9 +521,9 @@ static bool check_caller_access_to_name(struct fuse* fuse, const char* name, int mode, bool has_rw) { /* Always block security-sensitive files at root */ if (parent_node && parent_node->perm == PERM_ROOT) { - if (!strcmp(name, "autorun.inf") - || !strcmp(name, ".android_secure") - || !strcmp(name, "android_secure")) { + if (!strcasecmp(name, "autorun.inf") + || !strcasecmp(name, ".android_secure") + || !strcasecmp(name, "android_secure")) { return false; } } @@ -517,8 +533,9 @@ static bool check_caller_access_to_name(struct fuse* fuse, return true; } - /* Root or shell always have access */ - if (hdr->uid == 0 || hdr->uid == AID_SHELL) { + /* Root always has access; access for any other UIDs should always + * be controlled through packages.list. */ + if (hdr->uid == 0) { return true; } @@ -696,9 +713,10 @@ static void fuse_init(struct fuse *fuse, int fd, const char *source_path, fuse->root.perm = PERM_LEGACY_PRE_ROOT; fuse->root.mode = 0771; fuse->root.gid = fs_gid; - fuse->package_to_appid = hashmapCreate(256, str_hash, str_equals); + fuse->package_to_appid = hashmapCreate(256, str_hash, str_icase_equals); fuse->appid_with_rw = hashmapCreate(128, int_hash, int_equals); snprintf(fuse->obbpath, sizeof(fuse->obbpath), "%s/obb", source_path); + fs_prepare_dir(fuse->obbpath, 0775, getuid(), getgid()); break; case DERIVE_UNIFIED: /* Unified multiuser layout which places secondary user_id under @@ -706,7 +724,7 @@ static void fuse_init(struct fuse *fuse, int fd, const char *source_path, fuse->root.perm = PERM_ROOT; fuse->root.mode = 0771; fuse->root.gid = fs_gid; - fuse->package_to_appid = hashmapCreate(256, str_hash, str_equals); + fuse->package_to_appid = hashmapCreate(256, str_hash, str_icase_equals); fuse->appid_with_rw = hashmapCreate(128, int_hash, int_equals); snprintf(fuse->obbpath, sizeof(fuse->obbpath), "%s/Android/obb", source_path); break; @@ -752,36 +770,7 @@ static int fuse_reply_entry(struct fuse* fuse, __u64 unique, struct stat s; if (lstat(path, &s) < 0) { - /* But wait! We'll automatically create a directory if its - * a valid package name under data or obb, since apps may not - * have enough permissions to create for themselves. */ - if (errno == ENOENT && (parent->perm == PERM_ANDROID_DATA - || parent->perm == PERM_ANDROID_OBB)) { - TRACE("automatically creating %s\n", path); - - pthread_mutex_lock(&fuse->lock); - bool validPackage = hashmapContainsKey(fuse->package_to_appid, (char*) name); - pthread_mutex_unlock(&fuse->lock); - - if (!validPackage) { - return -ENOENT; - } - if (mkdir(path, 0775) == -1) { - /* We might have raced with ourselves and already created */ - if (errno != EEXIST) { - ERROR("failed to mkdir(%s): %s\n", name, strerror(errno)); - return -ENOENT; - } - } - - /* It should exist this time around! */ - if (lstat(path, &s) < 0) { - ERROR("failed to lstat(%s): %s\n", name, strerror(errno)); - return -errno; - } - } else { - return -errno; - } + return -errno; } pthread_mutex_lock(&fuse->lock); @@ -1006,6 +995,25 @@ static int handle_mkdir(struct fuse* fuse, struct fuse_handler* handler, if (mkdir(child_path, mode) < 0) { return -errno; } + + /* When creating /Android/data and /Android/obb, mark them as .nomedia */ + if (parent_node->perm == PERM_ANDROID && !strcasecmp(name, "data")) { + char nomedia[PATH_MAX]; + snprintf(nomedia, PATH_MAX, "%s/.nomedia", child_path); + if (touch(nomedia, 0664) != 0) { + ERROR("Failed to touch(%s): %s\n", nomedia, strerror(errno)); + return -ENOENT; + } + } + if (parent_node->perm == PERM_ANDROID && !strcasecmp(name, "obb")) { + char nomedia[PATH_MAX]; + snprintf(nomedia, PATH_MAX, "%s/.nomedia", fuse->obbpath); + if (touch(nomedia, 0664) != 0) { + ERROR("Failed to touch(%s): %s\n", nomedia, strerror(errno)); + return -ENOENT; + } + } + return fuse_reply_entry(fuse, hdr->unique, parent_node, name, actual_name, child_path); } From 4481c325b16d4a42fd0ebf4c8a2bb5bcc09daf5e Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Fri, 20 Sep 2013 17:28:22 -0700 Subject: [PATCH 533/541] Fix Mac builds. Change-Id: I0eb029395a3afd2f93a7632d7d3d20d28c2ab189 --- libcutils/Android.mk | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/libcutils/Android.mk b/libcutils/Android.mk index 0fd5a57e0..528138514 100644 --- a/libcutils/Android.mk +++ b/libcutils/Android.mk @@ -65,12 +65,6 @@ ifneq ($(strip $(USE_MINGW)),) WINDOWS_HOST_ONLY := 1 endif -ifneq ($(WINDOWS_HOST_ONLY),1) - commonSources += \ - fs.c \ - multiuser.c -endif - # Static library for host # ======================================================== @@ -107,7 +101,9 @@ LOCAL_SRC_FILES := $(commonSources) \ android_reboot.c \ ashmem-dev.c \ debugger.c \ + fs.c \ klog.c \ + multiuser.c \ partition_utils.c \ properties.c \ qtaguid.c \ From 887f2892062c64fa688fc9525ebd09cc98ca70d8 Mon Sep 17 00:00:00 2001 From: Ken Sumrall Date: Wed, 11 Sep 2013 19:42:51 -0700 Subject: [PATCH 534/541] New fstab flags to support more expressive SD card permissions Bug: 10330128 Change-Id: I41fb178b839487b604762fbc1ccba097d25c7aa0 --- fs_mgr/fs_mgr.c | 6 ++++++ fs_mgr/fs_mgr_priv.h | 6 ++++++ fs_mgr/include/fs_mgr.h | 1 + 3 files changed, 13 insertions(+) diff --git a/fs_mgr/fs_mgr.c b/fs_mgr/fs_mgr.c index 82c579821..ea60cc899 100644 --- a/fs_mgr/fs_mgr.c +++ b/fs_mgr/fs_mgr.c @@ -92,6 +92,7 @@ static struct flag_list fs_mgr_flags[] = { { "swapprio=", MF_SWAPPRIO }, { "zramsize=", MF_ZRAMSIZE }, { "verify", MF_VERIFY }, + { "noemulatedsd", MF_NOEMULATEDSD }, { "defaults", 0 }, { 0, 0 }, }; @@ -931,3 +932,8 @@ int fs_mgr_is_encryptable(struct fstab_rec *fstab) { return fstab->fs_mgr_flags & MF_CRYPT; } + +int fs_mgr_is_noemulatedsd(struct fstab_rec *fstab) +{ + return fstab->fs_mgr_flags & MF_NOEMULATEDSD; +} diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h index f284ca69e..59ffd785c 100644 --- a/fs_mgr/fs_mgr_priv.h +++ b/fs_mgr/fs_mgr_priv.h @@ -72,6 +72,12 @@ #define MF_SWAPPRIO 0x80 #define MF_ZRAMSIZE 0x100 #define MF_VERIFY 0x200 +/* + * There is no emulated sdcard daemon running on /data/media on this device, + * so treat the physical SD card as the only external storage device, + * a la the Nexus One. + */ +#define MF_NOEMULATEDSD 0x400 #define DM_BUF_SIZE 4096 diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h index 384d19594..0f90c32f1 100644 --- a/fs_mgr/include/fs_mgr.h +++ b/fs_mgr/include/fs_mgr.h @@ -62,6 +62,7 @@ struct fstab_rec *fs_mgr_get_entry_for_mount_point(struct fstab *fstab, const ch int fs_mgr_is_voldmanaged(struct fstab_rec *fstab); int fs_mgr_is_nonremovable(struct fstab_rec *fstab); int fs_mgr_is_encryptable(struct fstab_rec *fstab); +int fs_mgr_is_noemulatedsd(struct fstab_rec *fstab); int fs_mgr_swapon_all(struct fstab *fstab); #ifdef __cplusplus } From 4eaf905858c6a1d7cf0639bed3e8de3bd7987646 Mon Sep 17 00:00:00 2001 From: Ken Sumrall Date: Wed, 18 Sep 2013 17:49:21 -0700 Subject: [PATCH 535/541] Create a separate copy of the fsck logs The log_target parameter of android_fork_execvp_ext() is now a bit field, and multiple targets can be set to log to multiple places at the same time. The new target LOG_FILE will log to a file specified by the new parameter file_path. Set LOG_FILE and log to a file in /dev (the only writable filesystem avilable when e2fsck runs) when invoking e2fsck in fs_mgr. Bug: 10021342 Change-Id: I63baf644cc8c3afccc8345df27a74203b44d0400 --- fs_mgr/fs_mgr.c | 7 +++-- logwrapper/include/logwrap/logwrap.h | 16 +++++++---- logwrapper/logwrap.c | 43 ++++++++++++++++++++++------ logwrapper/logwrapper.c | 2 +- rootdir/init.rc | 4 +++ 5 files changed, 55 insertions(+), 17 deletions(-) diff --git a/fs_mgr/fs_mgr.c b/fs_mgr/fs_mgr.c index ea60cc899..3500c911a 100644 --- a/fs_mgr/fs_mgr.c +++ b/fs_mgr/fs_mgr.c @@ -53,6 +53,8 @@ #define E2FSCK_BIN "/system/bin/e2fsck" #define MKSWAP_BIN "/system/bin/mkswap" +#define FSCK_LOG_FILE "/dev/fscklogs/log" + #define ZRAM_CONF_DEV "/sys/block/zram0/disksize" #define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a))) @@ -484,7 +486,8 @@ static void check_fs(char *blk_device, char *fs_type, char *target) INFO("Running %s on %s\n", E2FSCK_BIN, blk_device); ret = android_fork_execvp_ext(ARRAY_SIZE(e2fsck_argv), e2fsck_argv, - &status, true, LOG_KLOG, true); + &status, true, LOG_KLOG | LOG_FILE, + true, FSCK_LOG_FILE); if (ret < 0) { /* No need to check for error in fork, we can't really handle it now */ @@ -801,7 +804,7 @@ int fs_mgr_swapon_all(struct fstab *fstab) /* Initialize the swap area */ mkswap_argv[1] = fstab->recs[i].blk_device; err = android_fork_execvp_ext(ARRAY_SIZE(mkswap_argv), mkswap_argv, - &status, true, LOG_KLOG, false); + &status, true, LOG_KLOG, false, NULL); if (err) { ERROR("mkswap failed for %s\n", fstab->recs[i].blk_device); ret = -1; diff --git a/logwrapper/include/logwrap/logwrap.h b/logwrapper/include/logwrap/logwrap.h index 8087f0a4b..4307a3055 100644 --- a/logwrapper/include/logwrap/logwrap.h +++ b/logwrapper/include/logwrap/logwrap.h @@ -44,11 +44,15 @@ __BEGIN_DECLS * send a signal twice to signal the caller (once for the child, and * once for the caller) * log_target: Specify where to log the output of the child, either LOG_NONE, - * LOG_ALOG (for the Android system log) or LOG_KLOG (for the kernel - * log). + * LOG_ALOG (for the Android system log), LOG_KLOG (for the kernel + * log), or LOG_FILE (and you need to specify a pathname in the + * file_path argument, otherwise pass NULL). These are bit fields, + * and can be OR'ed together to log to multiple places. * abbreviated: If true, capture up to the first 100 lines and last 4K of * output from the child. The abbreviated output is not dumped to * the specified log until the child has exited. + * file_path: if log_target has the LOG_FILE bit set, then this parameter + * must be set to the pathname of the file to log to. * * Return value: * 0 when logwrap successfully run the child process and captured its status @@ -58,13 +62,14 @@ __BEGIN_DECLS * */ -/* Values for the log_target parameter android_fork_exec_ext() */ +/* Values for the log_target parameter android_fork_execvp_ext() */ #define LOG_NONE 0 #define LOG_ALOG 1 #define LOG_KLOG 2 +#define LOG_FILE 4 int android_fork_execvp_ext(int argc, char* argv[], int *status, bool ignore_int_quit, - int log_target, bool abbreviated); + int log_target, bool abbreviated, char *file_path); /* Similar to above, except abbreviated logging is not available, and if logwrap * is true, logging is to the Android system log, and if false, there is no @@ -74,10 +79,9 @@ static inline int android_fork_execvp(int argc, char* argv[], int *status, bool ignore_int_quit, bool logwrap) { return android_fork_execvp_ext(argc, argv, status, ignore_int_quit, - (logwrap ? LOG_ALOG : LOG_NONE), false); + (logwrap ? LOG_ALOG : LOG_NONE), false, NULL); } - __END_DECLS #endif /* __LIBS_LOGWRAP_H */ diff --git a/logwrapper/logwrap.c b/logwrapper/logwrap.c index 01cc9a189..4ca1db4c8 100644 --- a/logwrapper/logwrap.c +++ b/logwrapper/logwrap.c @@ -93,6 +93,7 @@ struct log_info { char klog_fmt[MAX_KLOG_TAG * 2]; char *btag; bool abbreviated; + FILE *fp; struct abbr_buf a_buf; }; @@ -158,11 +159,15 @@ static void add_line_to_circular_buf(struct ending_buf *e_buf, /* Log directly to the specified log */ static void do_log_line(struct log_info *log_info, char *line) { - if (log_info->log_target == LOG_KLOG) { + if (log_info->log_target & LOG_KLOG) { klog_write(6, log_info->klog_fmt, line); - } else if (log_info->log_target == LOG_ALOG) { + } + if (log_info->log_target & LOG_ALOG) { ALOG(LOG_INFO, log_info->btag, "%s", line); } + if (log_info->log_target & LOG_FILE) { + fprintf(log_info->fp, "%s\n", line); + } } /* Log to either the abbreviated buf, or directly to the specified log @@ -290,7 +295,7 @@ static void print_abbr_buf(struct log_info *log_info) { } static int parent(const char *tag, int parent_read, pid_t pid, - int *chld_sts, int log_target, bool abbreviated) { + int *chld_sts, int log_target, bool abbreviated, char *file_path) { int status = 0; char buffer[4096]; struct pollfd poll_fds[] = { @@ -300,6 +305,7 @@ static int parent(const char *tag, int parent_read, pid_t pid, }, }; int rc = 0; + int fd; struct log_info log_info; @@ -309,8 +315,6 @@ static int parent(const char *tag, int parent_read, pid_t pid, bool found_child = false; char tmpbuf[256]; - log_info.log_target = log_target; - log_info.abbreviated = abbreviated; log_info.btag = basename(tag); if (!log_info.btag) { log_info.btag = (char*) tag; @@ -323,11 +327,30 @@ static int parent(const char *tag, int parent_read, pid_t pid, init_abbr_buf(&log_info.a_buf); } - if (log_target == LOG_KLOG) { + if (log_target & LOG_KLOG) { snprintf(log_info.klog_fmt, sizeof(log_info.klog_fmt), "<6>%.*s: %%s", MAX_KLOG_TAG, log_info.btag); } + if ((log_target & LOG_FILE) && !file_path) { + /* No file_path specified, clear the LOG_FILE bit */ + log_target &= ~LOG_FILE; + } + + if (log_target & LOG_FILE) { + fd = open(file_path, O_WRONLY | O_CREAT, 0664); + if (fd < 0) { + ERROR("Cannot log to file %s\n", file_path); + log_target &= ~LOG_FILE; + } else { + lseek(fd, 0, SEEK_END); + log_info.fp = fdopen(fd, "a"); + } + } + + log_info.log_target = log_target; + log_info.abbreviated = abbreviated; + while (!found_child) { if (TEMP_FAILURE_RETRY(poll(poll_fds, ARRAY_SIZE(poll_fds), -1)) < 0) { ERROR("poll failed\n"); @@ -432,6 +455,9 @@ static int parent(const char *tag, int parent_read, pid_t pid, err_waitpid: err_poll: + if (log_target & LOG_FILE) { + fclose(log_info.fp); /* Also closes underlying fd */ + } if (abbreviated) { free_abbr_buf(&log_info.a_buf); } @@ -451,7 +477,7 @@ static void child(int argc, char* argv[]) { } int android_fork_execvp_ext(int argc, char* argv[], int *status, bool ignore_int_quit, - int log_target, bool abbreviated) { + int log_target, bool abbreviated, char *file_path) { pid_t pid; int parent_ptty; int child_ptty; @@ -523,7 +549,8 @@ int android_fork_execvp_ext(int argc, char* argv[], int *status, bool ignore_int sigaction(SIGQUIT, &ignact, &quitact); } - rc = parent(argv[0], parent_ptty, pid, status, log_target, abbreviated); + rc = parent(argv[0], parent_ptty, pid, status, log_target, + abbreviated, file_path); } if (ignore_int_quit) { diff --git a/logwrapper/logwrapper.c b/logwrapper/logwrapper.c index d1c62403a..d0d8d1471 100644 --- a/logwrapper/logwrapper.c +++ b/logwrapper/logwrapper.c @@ -80,7 +80,7 @@ int main(int argc, char* argv[]) { } rc = android_fork_execvp_ext(argc, &argv[0], &status, true, - log_target, abbreviated); + log_target, abbreviated, NULL); if (!rc) { if (WIFEXITED(status)) rc = WEXITSTATUS(status); diff --git a/rootdir/init.rc b/rootdir/init.rc index be74f6fc2..9eb78d810 100644 --- a/rootdir/init.rc +++ b/rootdir/init.rc @@ -133,6 +133,10 @@ loglevel 3 # This is needed by any process that uses socket tagging. chmod 0644 /dev/xt_qtaguid +# Create location for fs_mgr to store abbreviated output from filesystem +# checker programs. + mkdir /dev/fscklogs 0770 root system + on post-fs # once everything is setup, no need to modify / mount rootfs rootfs / ro remount From 0ee7d8c68b57c02d02f707d6f71c731234d56eec Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Fri, 20 Sep 2013 17:58:54 -0700 Subject: [PATCH 536/541] Fix Mac builds, for realz. Change-Id: I63e21b90e5b8a8f016dd04ceca4354793faa5e37 --- libcutils/Android.mk | 8 ++++++-- libcutils/fs.c | 4 ++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/libcutils/Android.mk b/libcutils/Android.mk index 528138514..0fd5a57e0 100644 --- a/libcutils/Android.mk +++ b/libcutils/Android.mk @@ -65,6 +65,12 @@ ifneq ($(strip $(USE_MINGW)),) WINDOWS_HOST_ONLY := 1 endif +ifneq ($(WINDOWS_HOST_ONLY),1) + commonSources += \ + fs.c \ + multiuser.c +endif + # Static library for host # ======================================================== @@ -101,9 +107,7 @@ LOCAL_SRC_FILES := $(commonSources) \ android_reboot.c \ ashmem-dev.c \ debugger.c \ - fs.c \ klog.c \ - multiuser.c \ partition_utils.c \ properties.c \ qtaguid.c \ diff --git a/libcutils/fs.c b/libcutils/fs.c index 8d1da21c8..286a8eb09 100644 --- a/libcutils/fs.c +++ b/libcutils/fs.c @@ -148,6 +148,8 @@ fail_closed: return -1; } +#ifndef __APPLE__ + int fs_mkdirs(const char* path, mode_t mode) { int res = 0; int fd = 0; @@ -231,3 +233,5 @@ done: free(buf); return res; } + +#endif From 481b947d687a752174f2030511f5338e0ea674b0 Mon Sep 17 00:00:00 2001 From: Adam Lesinski Date: Mon, 23 Sep 2013 18:42:41 -0700 Subject: [PATCH 537/541] Moves libmemtrack header to standard location Change-Id: If8c80003bc2f042c67ffdf38469407de3c2fda2c --- {libmemtrack/include => include/memtrack}/memtrack.h | 9 +++++++++ libmemtrack/Android.mk | 3 +-- libmemtrack/memtrack.c | 2 +- libmemtrack/memtrack_test.c | 2 +- 4 files changed, 12 insertions(+), 4 deletions(-) rename {libmemtrack/include => include/memtrack}/memtrack.h (97%) diff --git a/libmemtrack/include/memtrack.h b/include/memtrack/memtrack.h similarity index 97% rename from libmemtrack/include/memtrack.h rename to include/memtrack/memtrack.h index d6b370b22..0f1f85e22 100644 --- a/libmemtrack/include/memtrack.h +++ b/include/memtrack/memtrack.h @@ -19,6 +19,11 @@ #include #include +#include + +#ifdef __cplusplus +extern "C" { +#endif /** * struct memtrack_proc @@ -135,4 +140,8 @@ ssize_t memtrack_proc_other_total(struct memtrack_proc *p); */ ssize_t memtrack_proc_other_pss(struct memtrack_proc *p); +#ifdef __cplusplus +} +#endif + #endif diff --git a/libmemtrack/Android.mk b/libmemtrack/Android.mk index c23b6f410..a8fb3eb00 100644 --- a/libmemtrack/Android.mk +++ b/libmemtrack/Android.mk @@ -3,10 +3,9 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) -LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include LOCAL_SRC_FILES := memtrack.c LOCAL_MODULE := libmemtrack -LOCAL_C_INCLUDES += $(LOCAL_PATH)/include hardware/libhardware/include +LOCAL_C_INCLUDES += hardware/libhardware/include LOCAL_SHARED_LIBRARIES := libhardware liblog LOCAL_CFLAGS := -Wall -Werror include $(BUILD_SHARED_LIBRARY) diff --git a/libmemtrack/memtrack.c b/libmemtrack/memtrack.c index 2b2651a28..9a656dfd3 100644 --- a/libmemtrack/memtrack.c +++ b/libmemtrack/memtrack.c @@ -14,7 +14,7 @@ * limitations under the License. */ -#include +#include #define LOG_TAG "memtrack" diff --git a/libmemtrack/memtrack_test.c b/libmemtrack/memtrack_test.c index f306f67f1..cd94bc5e1 100644 --- a/libmemtrack/memtrack_test.c +++ b/libmemtrack/memtrack_test.c @@ -19,7 +19,7 @@ #include #include -#include +#include #include From 5cd118178e87c4a6590dc8d8446effa5d5193a9f Mon Sep 17 00:00:00 2001 From: Ben Cheng Date: Mon, 23 Sep 2013 22:39:15 -0700 Subject: [PATCH 538/541] Disable timestamp logging. The kernel problem has been fixed long time ago and the ad-hoc logging mechanism is not thread safe and can flood the log with spurious messages. BUG: 10899829 Change-Id: I63278db51295e744eed3e47dc8d4cfe621c0d8f7 --- libutils/SystemClock.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/libutils/SystemClock.cpp b/libutils/SystemClock.cpp index 4b7488941..ac8da8871 100644 --- a/libutils/SystemClock.cpp +++ b/libutils/SystemClock.cpp @@ -61,12 +61,20 @@ int64_t elapsedRealtime() #define METHOD_IOCTL 1 #define METHOD_SYSTEMTIME 2 +/* + * To debug/verify the timestamps returned by the kernel, change + * DEBUG_TIMESTAMP to 1 and call the timestamp routine from a single thread + * in the test program. b/10899829 + */ +#define DEBUG_TIMESTAMP 0 + static const char *gettime_method_names[] = { "clock_gettime", "ioctl", "systemTime", }; +#if DEBUG_TIMESTAMP static inline void checkTimeStamps(int64_t timestamp, int64_t volatile *prevTimestampPtr, int volatile *prevMethodPtr, @@ -93,6 +101,9 @@ static inline void checkTimeStamps(int64_t timestamp, *prevMethodPtr = curMethod; #endif } +#else +#define checkTimeStamps(timestamp, prevTimestampPtr, prevMethodPtr, curMethod) +#endif /* * native public static long elapsedRealtimeNano(); @@ -103,8 +114,10 @@ int64_t elapsedRealtimeNano() struct timespec ts; int result; int64_t timestamp; +#if DEBUG_TIMESTAMP static volatile int64_t prevTimestamp; static volatile int prevMethod; +#endif #if 0 /* From 397a3642145dbd0919f8148ff24f0cafe1714b55 Mon Sep 17 00:00:00 2001 From: Rom Lemarchand Date: Tue, 24 Sep 2013 10:49:21 -0700 Subject: [PATCH 539/541] fs_mgr: check that fstab is not NULL in fs_mgr_free_fstab Make sure fstab is not NULL before freeing it Bug: 10911605 Change-Id: I549c0a470dd183fb15a2f3c5cf4f3dd393b6e307 --- fs_mgr/fs_mgr.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fs_mgr/fs_mgr.c b/fs_mgr/fs_mgr.c index 3500c911a..f432f6a25 100644 --- a/fs_mgr/fs_mgr.c +++ b/fs_mgr/fs_mgr.c @@ -430,6 +430,10 @@ void fs_mgr_free_fstab(struct fstab *fstab) { int i; + if (!fstab) { + return; + } + for (i = 0; i < fstab->num_entries; i++) { /* Free the pointers return by strdup(3) */ free(fstab->recs[i].blk_device); From 53367585956dd5a4da37e388e5a32ce577d8174a Mon Sep 17 00:00:00 2001 From: William Luh Date: Wed, 4 Sep 2013 16:31:24 -0700 Subject: [PATCH 540/541] Add new cert_pin_failure tag that can be used in user-consent filtering. Bug: 10912373 Change-Id: If8a2f9829b3aac4abc33a4623c5f5b4966fd6870 --- logcat/event.logtags | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/logcat/event.logtags b/logcat/event.logtags index 3a1b28140..a325692e8 100644 --- a/logcat/event.logtags +++ b/logcat/event.logtags @@ -90,10 +90,6 @@ # Logged when the supplicant switches to a new state 50023 wifi_supplicant_state_changed (supplicant_state|1|5) -# Do not change these names without updating tag in: -#//device/dalvik/libcore/luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.c -51000 socket_stats (send|1|2),(recv|1|2),(ip|1|5),(port|1|5),(close|1|5) - # Database operation samples. # db: the filename of the database # sql: the executed query (without query args) @@ -136,7 +132,7 @@ 80310 bionic_event_resolver_wrong_query (uid|1) # libcore failure logging -90100 cert_pin_failure (certs|4) +90100 exp_det_cert_pin_failure (certs|4) # NOTE - the range 1000000-2000000 is reserved for partners and others who # want to define their own log tags without conflicting with the core platform. From e93a0517f4c88310066ac39c6b268ebfcceef44e Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Tue, 8 Oct 2013 10:14:24 -0700 Subject: [PATCH 541/541] Set GID required to write, media_rw mount point. Add sdcard FUSE daemon flag to specify the GID required for a package to have write access. Normally sdcard_rw, but it will be media_rw for secondary external storage devices, so DefaultContainerService can still clean up package directories after uninstall. Create /mnt/media_rw which is where vold will mount raw secondary external storage devices before wrapping them in a FUSE instance. Bug: 10330128, 10330229 Change-Id: I4385c36fd9035cdf56892aaf7b36ef4b81f4418a --- rootdir/init.rc | 1 + sdcard/sdcard.c | 29 ++++++++++++++++------------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/rootdir/init.rc b/rootdir/init.rc index 8150a73f1..86e124f15 100644 --- a/rootdir/init.rc +++ b/rootdir/init.rc @@ -61,6 +61,7 @@ loglevel 3 # See storage config details at http://source.android.com/tech/storage/ mkdir /mnt/shell 0700 shell shell + mkdir /mnt/media_rw 0700 media_rw media_rw mkdir /storage 0751 root sdcard_r # Directory for putting things only root should see. diff --git a/sdcard/sdcard.c b/sdcard/sdcard.c index 3f1e268e6..05fbfbad3 100644 --- a/sdcard/sdcard.c +++ b/sdcard/sdcard.c @@ -215,6 +215,7 @@ struct fuse { int fd; derive_t derive; bool split_perms; + gid_t write_gid; struct node root; char obbpath[PATH_MAX]; @@ -681,13 +682,14 @@ static struct node* acquire_or_create_child_locked( } static void fuse_init(struct fuse *fuse, int fd, const char *source_path, - gid_t fs_gid, derive_t derive, bool split_perms) { + gid_t write_gid, derive_t derive, bool split_perms) { pthread_mutex_init(&fuse->lock, NULL); fuse->fd = fd; fuse->next_generation = 0; fuse->derive = derive; fuse->split_perms = split_perms; + fuse->write_gid = write_gid; memset(&fuse->root, 0, sizeof(fuse->root)); fuse->root.nid = FUSE_ROOT_ID; /* 1 */ @@ -712,7 +714,7 @@ static void fuse_init(struct fuse *fuse, int fd, const char *source_path, * just below that. Shared OBB path is also at top level. */ fuse->root.perm = PERM_LEGACY_PRE_ROOT; fuse->root.mode = 0771; - fuse->root.gid = fs_gid; + fuse->root.gid = AID_SDCARD_R; fuse->package_to_appid = hashmapCreate(256, str_hash, str_icase_equals); fuse->appid_with_rw = hashmapCreate(128, int_hash, int_equals); snprintf(fuse->obbpath, sizeof(fuse->obbpath), "%s/obb", source_path); @@ -723,7 +725,7 @@ static void fuse_init(struct fuse *fuse, int fd, const char *source_path, * /Android/user and shared OBB path under /Android/obb. */ fuse->root.perm = PERM_ROOT; fuse->root.mode = 0771; - fuse->root.gid = fs_gid; + fuse->root.gid = AID_SDCARD_R; fuse->package_to_appid = hashmapCreate(256, str_hash, str_icase_equals); fuse->appid_with_rw = hashmapCreate(128, int_hash, int_equals); snprintf(fuse->obbpath, sizeof(fuse->obbpath), "%s/Android/obb", source_path); @@ -1623,7 +1625,7 @@ static int read_package_list(struct fuse *fuse) { char* token = strtok(gids, ","); while (token != NULL) { - if (strtoul(token, NULL, 10) == AID_SDCARD_RW) { + if (strtoul(token, NULL, 10) == fuse->write_gid) { hashmapPut(fuse->appid_with_rw, (void*) appid, (void*) 1); break; } @@ -1632,7 +1634,7 @@ static int read_package_list(struct fuse *fuse) { } } - TRACE("read_package_list: found %d packages, %d with sdcard_rw\n", + TRACE("read_package_list: found %d packages, %d with write_gid\n", hashmapSize(fuse->package_to_appid), hashmapSize(fuse->appid_with_rw)); fclose(file); @@ -1749,7 +1751,7 @@ static int usage() ERROR("usage: sdcard [OPTIONS] \n" " -u: specify UID to run as\n" " -g: specify GID to run as\n" - " -G: specify default GID for files (default sdcard_r, requires -d or -l)\n" + " -w: specify GID required to write (default sdcard_rw, requires -d or -l)\n" " -t: specify number of threads to use (default %d)\n" " -d: derive file permissions based on path\n" " -l: derive file permissions based on legacy internal layout\n" @@ -1759,7 +1761,8 @@ static int usage() } static int run(const char* source_path, const char* dest_path, uid_t uid, - gid_t gid, gid_t fs_gid, int num_threads, derive_t derive, bool split_perms) { + gid_t gid, gid_t write_gid, int num_threads, derive_t derive, + bool split_perms) { int fd; char opts[256]; int res; @@ -1802,7 +1805,7 @@ static int run(const char* source_path, const char* dest_path, uid_t uid, goto error; } - fuse_init(&fuse, fd, source_path, fs_gid, derive, split_perms); + fuse_init(&fuse, fd, source_path, write_gid, derive, split_perms); umask(0); res = ignite_fuse(&fuse, num_threads); @@ -1822,7 +1825,7 @@ int main(int argc, char **argv) const char *dest_path = NULL; uid_t uid = 0; gid_t gid = 0; - gid_t fs_gid = AID_SDCARD_R; + gid_t write_gid = AID_SDCARD_RW; int num_threads = DEFAULT_NUM_THREADS; derive_t derive = DERIVE_NONE; bool split_perms = false; @@ -1830,7 +1833,7 @@ int main(int argc, char **argv) struct rlimit rlim; int opt; - while ((opt = getopt(argc, argv, "u:g:G:t:dls")) != -1) { + while ((opt = getopt(argc, argv, "u:g:w:t:dls")) != -1) { switch (opt) { case 'u': uid = strtoul(optarg, NULL, 10); @@ -1838,8 +1841,8 @@ int main(int argc, char **argv) case 'g': gid = strtoul(optarg, NULL, 10); break; - case 'G': - fs_gid = strtoul(optarg, NULL, 10); + case 'w': + write_gid = strtoul(optarg, NULL, 10); break; case 't': num_threads = strtoul(optarg, NULL, 10); @@ -1902,6 +1905,6 @@ int main(int argc, char **argv) ERROR("Error setting RLIMIT_NOFILE, errno = %d\n", errno); } - res = run(source_path, dest_path, uid, gid, fs_gid, num_threads, derive, split_perms); + res = run(source_path, dest_path, uid, gid, write_gid, num_threads, derive, split_perms); return res < 0 ? 1 : 0; }